mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-12 11:34:05 +09:00
com.torvald → net.torvald
Former-commit-id: 375604da8a20a6ba7cd0a8d05a44add02b2d04f4 Former-commit-id: 287287c5920b07618174d7a7573f049d350ded66
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import net.torvald.random.HQRNG
|
||||
import com.sun.javaws.exceptions.InvalidArgumentException
|
||||
|
||||
import java.util.Random
|
||||
|
||||
object FloatingIslandsPreset {
|
||||
|
||||
val PRESETS = 5
|
||||
|
||||
internal fun generatePreset(random: HQRNG): Array<IntArray> {
|
||||
val index = random.nextInt(PRESETS)
|
||||
return generatePreset(index, random)
|
||||
}
|
||||
|
||||
internal fun generatePreset(index: Int, random: Random): Array<IntArray> {
|
||||
if (index == 0) {
|
||||
return processPreset(random, FloatingIslePreset01.data, FloatingIslePreset01.w, FloatingIslePreset01.h)
|
||||
}
|
||||
else if (index == 1) {
|
||||
return processPreset(random, FloatingIslePreset02.data, FloatingIslePreset02.w, FloatingIslePreset02.h)
|
||||
}
|
||||
else if (index == 2) {
|
||||
return processPreset(random, FloatingIslePreset03.data, FloatingIslePreset03.w, FloatingIslePreset03.h)
|
||||
}
|
||||
else if (index == 3) {
|
||||
return processPreset(random, FloatingIslePreset04.data, FloatingIslePreset04.w, FloatingIslePreset04.h)
|
||||
}
|
||||
else {
|
||||
return processPreset(random, FloatingIslePreset05.data, FloatingIslePreset05.w, FloatingIslePreset05.h)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processPreset(random: Random, preset: IntArray, w: Int, h: Int): Array<IntArray> {
|
||||
val temp = Array(h) { IntArray(w) }
|
||||
var counter = 0
|
||||
val mirrored = random.nextBoolean()
|
||||
|
||||
for (i in 0..h - 1) {
|
||||
for (j in 0..w - 1) {
|
||||
if (!mirrored) {
|
||||
if (counter < preset.size - 1) {
|
||||
temp[i][j] = preset[counter]
|
||||
counter++
|
||||
}
|
||||
else {
|
||||
temp[i][j] = 0
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (counter < preset.size - 1) {
|
||||
temp[i][w - 1 - j] = preset[counter]
|
||||
counter++
|
||||
}
|
||||
else {
|
||||
temp[i][w - 1 - j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
948
src/net/torvald/terrarum/mapgenerator/MapGenerator.kt
Normal file
948
src/net/torvald/terrarum/mapgenerator/MapGenerator.kt
Normal file
@@ -0,0 +1,948 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.terrarum.gamemap.GameMap
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
import com.jme3.math.FastMath
|
||||
import com.sudoplay.joise.Joise
|
||||
import com.sudoplay.joise.module.*
|
||||
import java.util.*
|
||||
|
||||
object MapGenerator {
|
||||
|
||||
private lateinit var map: GameMap
|
||||
private lateinit var random: Random
|
||||
//private static float[] noiseArray;
|
||||
var SEED: Long = 0
|
||||
var WIDTH: Int = 0
|
||||
var HEIGHT: Int = 0
|
||||
|
||||
//private lateinit var heightMap: IntArray
|
||||
private lateinit var terrainMap: Array<BitSet>
|
||||
|
||||
var DIRT_LAYER_DEPTH: Int = 0
|
||||
var TERRAIN_AVERAGE_HEIGHT: Int = 0
|
||||
private var minimumFloatingIsleHeight: Int = 0
|
||||
|
||||
private val NOISE_GRAD_START = 0.67f
|
||||
private val NOISE_GRAD_END = 0.56f
|
||||
|
||||
private val NOISE_SIMPLEX_ORE_START = 1.42f
|
||||
private val NOISE_SIMPLEX_ORE_END = 1.28f
|
||||
|
||||
private val HILL_WIDTH = 256 // power of two!
|
||||
//private val MAX_HILL_HEIGHT = 100
|
||||
private val TERRAIN_UNDULATION = 250
|
||||
|
||||
private val SIMPLEXGEN_LARGEST_FEATURE = 200
|
||||
|
||||
private var OCEAN_WIDTH = 400
|
||||
private var SHORE_WIDTH = 120
|
||||
private val MAX_OCEAN_DEPTH = 200
|
||||
|
||||
private var GLACIER_MOUNTAIN_WIDTH = 900
|
||||
private val GLACIER_MOUNTAIN_HEIGHT = 300
|
||||
|
||||
private val CAVEGEN_THRE_START = 0.95f
|
||||
private val CAVEGEN_THRE_END = 0.67f
|
||||
|
||||
|
||||
private var worldOceanPosition: Int = -1
|
||||
private val TYPE_OCEAN_LEFT = 0
|
||||
private val TYPE_OCEAN_RIGHT = 1
|
||||
|
||||
private val GRASSCUR_UP = 0
|
||||
private val GRASSCUR_RIGHT = 1
|
||||
private val GRASSCUR_DOWN = 2
|
||||
private val GRASSCUR_LEFT = 3
|
||||
|
||||
private val TILE_MACRO_ALL = -1
|
||||
|
||||
fun attachMap(map: GameMap) {
|
||||
this.map = map
|
||||
WIDTH = map.width
|
||||
HEIGHT = map.height
|
||||
|
||||
val widthMulFactor = WIDTH / 8192f
|
||||
|
||||
DIRT_LAYER_DEPTH = (100 * HEIGHT / 1024f).toInt()
|
||||
minimumFloatingIsleHeight = (25 * (HEIGHT / 1024f)).toInt()
|
||||
TERRAIN_AVERAGE_HEIGHT = HEIGHT / 4
|
||||
|
||||
OCEAN_WIDTH = Math.round(OCEAN_WIDTH * widthMulFactor)
|
||||
SHORE_WIDTH = Math.round(SHORE_WIDTH * widthMulFactor)
|
||||
GLACIER_MOUNTAIN_WIDTH = Math.round(GLACIER_MOUNTAIN_WIDTH * widthMulFactor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate terrain and override attached map
|
||||
*/
|
||||
fun generateMap() {
|
||||
random = HQRNG(SEED)
|
||||
println("[mapgenerator] Seed: " + SEED)
|
||||
|
||||
worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT
|
||||
|
||||
//heightMap = raise2(MAX_HILL_HEIGHT / 2)
|
||||
//generateOcean(heightMap)
|
||||
//placeGlacierMount(heightMap)
|
||||
//heightMapToObjectMap(heightMap)
|
||||
|
||||
|
||||
terrainMap = raise3()
|
||||
|
||||
|
||||
fillMapByNoiseMap()
|
||||
|
||||
/**
|
||||
* Done: more perturbed overworld (harder to supra-navigate)
|
||||
* Todo: veined ore distribution (metals) -- use veined simplex noise
|
||||
* Todo: clustered gem distribution (clusters: [Ruby, Sapphire], Amethyst, Yellow topaz, emerald, diamond) -- use regular simplex noise
|
||||
* Todo: Lakes! Aquifers! Lava chambers!
|
||||
* Todo: deserts (variants: SAND_DESERT, SAND_RED)
|
||||
* Todo: volcano(es?)
|
||||
* Done: variants of beach (SAND, SAND_BEACH, SAND_BLACK, SAND_GREEN)
|
||||
*/
|
||||
|
||||
val noiseArray = arrayOf(
|
||||
TaggedJoise("Carving caves", noiseRidged(1.7f, 1.4f), 1f, TILE_MACRO_ALL, TILE_MACRO_ALL, TileNameCode.AIR, NoiseFilterSqrt, CAVEGEN_THRE_START, CAVEGEN_THRE_END)
|
||||
, TaggedJoise("Collapsing caves", noiseBlobs(0.5f, 0.5f), 0.3f, TileNameCode.AIR, TileNameCode.STONE, TileNameCode.STONE, NoiseFilterUniform)
|
||||
|
||||
//, TaggedJoise("Putting stone patches on the ground", noiseBlobs(0.8f, 0.8f), 1.02f, TileNameCode.DIRT, TileNameCode.DIRT, TileNameCode.STONE, NoiseFilterQuadratic, noiseGradientEnd, noiseGradientStart)
|
||||
//, TaggedJoise("Placing dirt spots in the cave", noiseBlobs(0.5f, 0.5f), 0.98f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.DIRT, NoiseFilterQuadratic, noiseGradientEnd, noiseGradientStart)
|
||||
//, TaggedJoise("Quarrying some stone into gravels", noiseBlobs(0.5f, 0.5f), 0.98f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.GRAVEL, NoiseFilterQuadratic, noiseGradientEnd, noiseGradientStart)
|
||||
|
||||
//, TaggedJoise("Growing copper veins", noiseRidged(1.7f, 1.7f), 1.68f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_COPPER)
|
||||
//, TaggedJoise("Cutting copper veins", noiseBlobs(0.4f, 0.4f), 0.26f, TileNameCode.ORE_COPPER, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
//, TaggedJoise("Growing iron veins", noiseRidged(1.7f, 1.7f), 1.68f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_IRON)
|
||||
//, TaggedJoise("Cutting iron veins", noiseBlobs(0.7f, 0.7f), 0.26f, TileNameCode.ORE_IRON, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
//, TaggedJoise("Growing silver veins", noiseRidged(1.7f, 1.7f), 1.71f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_SILVER)
|
||||
//, TaggedJoise("Cutting silver veins", noiseBlobs(0.7f, 0.7f), 0.26f, TileNameCode.ORE_SILVER, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
//, TaggedJoise("Growing gold veins", noiseRidged(1.7f, 1.7f), 1.73f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_GOLD)
|
||||
//, TaggedJoise("Cutting gold veins", noiseBlobs(0.7f, 0.7f), 0.26f, TileNameCode.ORE_GOLD, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
////, TaggedJoise("Growing topaz clusters", noiseBlobs(0.9f, 0.9f), 2f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_TOPAZ)
|
||||
//, TaggedJoise("Growing aluminium oxide clusters", noiseBlobs(0.9f, 0.9f), 1.7f, TileNameCode.STONE, TileNameCode.STONE, intArrayOf(TileNameCode.RAW_RUBY, TileNameCode.RAW_SAPPHIRE))
|
||||
//, TaggedJoise("Growing emerald clusters", noiseBlobs(0.9f, 0.9f), 1,7f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_EMERALD)
|
||||
//, TaggedJoise("Growing hearts of white", noiseBlobs(0.9f, 0.9f), 1.83f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_DIAMOND)
|
||||
|
||||
//, TaggedJoise("Growing hearts of violet", noiseRidged(2.5f, 2.5f), 1.75f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_AMETHYST)
|
||||
//, TaggedJoise("Cutting over-grown hearts", noiseBlobs(0.7f, 0.7f), 0.17f, TileNameCode.RAW_AMETHYST, TileNameCode.STONE, TileNameCode.STONE)
|
||||
)
|
||||
|
||||
processNoiseLayers(noiseArray)
|
||||
|
||||
/** TODO Cobaltite, Ilmenite, Aurichalcum (and possibly pitchblende?) */
|
||||
|
||||
floodBottomLava()
|
||||
// freeze()
|
||||
// fillOcean()
|
||||
plantGrass()
|
||||
|
||||
//post-process
|
||||
generateFloatingIslands()
|
||||
|
||||
//wire layer
|
||||
for (i in 0..HEIGHT - 1) {
|
||||
for (j in 0..WIDTH - 1) {
|
||||
map.wireArray[i][j] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Free some memories
|
||||
System.gc()
|
||||
}
|
||||
|
||||
/* 1. Raise */
|
||||
|
||||
private fun noiseRidged(xStretch: Float, yStretch: Float): Joise {
|
||||
val ridged = ModuleFractal()
|
||||
ridged.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
||||
ridged.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
ridged.setNumOctaves(4)
|
||||
ridged.setFrequency(1.0)
|
||||
ridged.seed = SEED xor random.nextLong()
|
||||
|
||||
val ridged_autocorrect = ModuleAutoCorrect()
|
||||
ridged_autocorrect.setRange(0.0, 1.0)
|
||||
ridged_autocorrect.setSource(ridged)
|
||||
|
||||
val ridged_scale = ModuleScaleDomain()
|
||||
ridged_scale.setScaleX(xStretch.toDouble())
|
||||
ridged_scale.setScaleY(yStretch.toDouble())
|
||||
ridged_scale.setSource(ridged_autocorrect)
|
||||
|
||||
return Joise(ridged_scale)
|
||||
}
|
||||
|
||||
private fun noiseBlobs(xStretch: Float, yStretch: Float): Joise {
|
||||
val gradval = ModuleBasisFunction()
|
||||
gradval.seed = SEED xor random.nextLong()
|
||||
gradval.setType(ModuleBasisFunction.BasisType.GRADVAL)
|
||||
gradval.setInterpolation(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
|
||||
val gradval_scale = ModuleScaleDomain()
|
||||
gradval_scale.setScaleX(1.0 / xStretch)
|
||||
gradval_scale.setScaleY(1.0 / yStretch)
|
||||
gradval_scale.setSource(gradval)
|
||||
|
||||
return Joise(gradval_scale)
|
||||
}
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* * Threshold 1.4 for rarer gem clusters, 1.35 for ores
|
||||
*/
|
||||
private fun noiseSimplex(xStretch: Float, yStretch: Float): Joise {
|
||||
val simplex = ModuleFractal()
|
||||
simplex.seed = SEED
|
||||
simplex.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.SIMPLEX)
|
||||
simplex.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.LINEAR)
|
||||
simplex.setNumOctaves(2)
|
||||
simplex.setFrequency(1.0)
|
||||
|
||||
val simplex_scale = ModuleScaleDomain()
|
||||
simplex_scale.setScaleX(1.0 / xStretch)
|
||||
simplex_scale.setScaleY(1.0 / yStretch)
|
||||
simplex_scale.setSource(simplex)
|
||||
|
||||
return Joise(simplex_scale)
|
||||
}
|
||||
|
||||
private fun generateOcean(noiseArrayLocal: IntArray): IntArray {
|
||||
val oceanLeftP1 = noiseArrayLocal[OCEAN_WIDTH]
|
||||
val oceanRightP1 = noiseArrayLocal[noiseArrayLocal.size - OCEAN_WIDTH]
|
||||
|
||||
/**
|
||||
* Add ocean so that:
|
||||
|
||||
* +1| - -
|
||||
* 0| - -- ...
|
||||
* -1|______ -
|
||||
|
||||
* interpolated to
|
||||
|
||||
* +1| - -
|
||||
* 0| _--- -- ...
|
||||
* -1|__- -
|
||||
|
||||
* ↑-- Rough, white noise
|
||||
|
||||
* -1 means -MAX_HILL_HEIGHT
|
||||
*/
|
||||
for (i in 0..OCEAN_WIDTH - 1) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
noiseArrayLocal[i] = Math.round(
|
||||
interpolateCosine(
|
||||
i.toFloat() / OCEAN_WIDTH, (-MAX_OCEAN_DEPTH).toFloat(), oceanLeftP1.toFloat()))
|
||||
} else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
noiseArrayLocal[noiseArrayLocal.size - OCEAN_WIDTH + i] = Math.round(
|
||||
interpolateCosine(
|
||||
i.toFloat() / OCEAN_WIDTH, oceanRightP1.toFloat(), (-MAX_OCEAN_DEPTH).toFloat()))
|
||||
} else {
|
||||
throw RuntimeException("Ocean position were not set correctly.")
|
||||
}
|
||||
}
|
||||
|
||||
return noiseArrayLocal
|
||||
}
|
||||
|
||||
/**
|
||||
* http://accidentalnoise.sourceforge.net/minecraftworlds.html
|
||||
*/
|
||||
private fun raise3(): Array<BitSet> {
|
||||
val noiseMap = Array(HEIGHT, { BitSet(WIDTH) })
|
||||
|
||||
// Height = Terrain undulation times 2.
|
||||
val SCALE_X: Double = (TERRAIN_UNDULATION * 0.5).toDouble()
|
||||
val SCALE_Y: Double = (TERRAIN_UNDULATION * 0.25).toDouble()
|
||||
|
||||
val ground_gradient = ModuleGradient()
|
||||
ground_gradient.setGradient(0.0, 0.0, 0.0, 1.0)
|
||||
|
||||
/* Lowlands */
|
||||
|
||||
val lowland_shape_fractal = ModuleFractal()
|
||||
lowland_shape_fractal.setType(ModuleFractal.FractalType.FBM)
|
||||
lowland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
lowland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
lowland_shape_fractal.setNumOctaves(4)
|
||||
lowland_shape_fractal.setFrequency(0.6)
|
||||
lowland_shape_fractal.seed = SEED xor random.nextLong()
|
||||
//println(lowland_shape_fractal.seed)
|
||||
|
||||
val lowland_autocorrect = ModuleAutoCorrect()
|
||||
lowland_autocorrect.setRange(0.0, 1.0)
|
||||
lowland_autocorrect.setSource(lowland_shape_fractal)
|
||||
|
||||
val lowland_scale = ModuleScaleOffset()
|
||||
lowland_scale.setSource(lowland_autocorrect)
|
||||
lowland_scale.setScale(0.8)
|
||||
lowland_scale.setOffset(-2.75)
|
||||
|
||||
val lowland_y_scale = ModuleScaleDomain()
|
||||
lowland_y_scale.setSource(lowland_scale)
|
||||
lowland_y_scale.setScaleY(0.0)
|
||||
|
||||
val lowland_terrain = ModuleTranslateDomain()
|
||||
lowland_terrain.setSource(ground_gradient)
|
||||
lowland_terrain.setAxisYSource(lowland_y_scale)
|
||||
|
||||
/* highlands */
|
||||
|
||||
val highland_shape_fractal = ModuleFractal()
|
||||
highland_shape_fractal.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
||||
highland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
highland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
highland_shape_fractal.setNumOctaves(4)
|
||||
highland_shape_fractal.setFrequency(0.5) // horizontal size. Higher == narrower
|
||||
highland_shape_fractal.seed = SEED xor random.nextLong()
|
||||
//println(highland_shape_fractal.seed)
|
||||
|
||||
val highland_autocorrect = ModuleAutoCorrect()
|
||||
highland_autocorrect.setSource(highland_shape_fractal)
|
||||
highland_autocorrect.setRange(0.0, 1.0)
|
||||
|
||||
val highland_scale = ModuleScaleOffset()
|
||||
highland_scale.setSource(highland_autocorrect)
|
||||
highland_scale.setScale(1.4) // vertical size. Higher == taller
|
||||
highland_scale.setOffset(-2.25)
|
||||
|
||||
val highland_y_scale = ModuleScaleDomain()
|
||||
highland_y_scale.setSource(highland_scale)
|
||||
highland_y_scale.setScaleY(0.0)
|
||||
|
||||
val highland_terrain = ModuleTranslateDomain()
|
||||
highland_terrain.setSource(ground_gradient)
|
||||
highland_terrain.setAxisYSource(highland_y_scale)
|
||||
|
||||
/* mountains */
|
||||
|
||||
val mountain_shape_fractal = ModuleFractal()
|
||||
mountain_shape_fractal.setType(ModuleFractal.FractalType.BILLOW)
|
||||
mountain_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
mountain_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
mountain_shape_fractal.setNumOctaves(6)
|
||||
mountain_shape_fractal.setFrequency(0.55)
|
||||
mountain_shape_fractal.seed = SEED xor random.nextLong()
|
||||
//println(mountain_shape_fractal.seed)
|
||||
|
||||
val mountain_autocorrect = ModuleAutoCorrect()
|
||||
mountain_autocorrect.setSource(mountain_shape_fractal)
|
||||
mountain_autocorrect.setRange(0.0, 1.0)
|
||||
|
||||
val mountain_scale = ModuleScaleOffset()
|
||||
mountain_scale.setSource(mountain_autocorrect)
|
||||
mountain_scale.setScale(1.66)
|
||||
mountain_scale.setOffset(-1.25)
|
||||
|
||||
val mountain_y_scale = ModuleScaleDomain()
|
||||
mountain_y_scale.setSource(mountain_scale)
|
||||
mountain_y_scale.setScaleY(0.1)
|
||||
|
||||
val mountain_terrain = ModuleTranslateDomain()
|
||||
mountain_terrain.setSource(ground_gradient)
|
||||
mountain_terrain.setAxisYSource(mountain_y_scale)
|
||||
|
||||
/* selection */
|
||||
|
||||
val terrain_type_fractal = ModuleFractal()
|
||||
terrain_type_fractal.setType(ModuleFractal.FractalType.MULTI)
|
||||
terrain_type_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
terrain_type_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
terrain_type_fractal.setNumOctaves(5)
|
||||
terrain_type_fractal.setFrequency(0.4) // <= 0.33
|
||||
terrain_type_fractal.seed = SEED xor random.nextLong()
|
||||
//println(terrain_type_fractal.seed)
|
||||
|
||||
val terrain_autocorrect = ModuleAutoCorrect()
|
||||
terrain_autocorrect.setSource(terrain_type_fractal)
|
||||
terrain_autocorrect.setRange(0.0, 1.0)
|
||||
|
||||
val terrain_type_scale = ModuleScaleDomain()
|
||||
terrain_type_scale.setScaleY(0.33)
|
||||
terrain_type_scale.setSource(terrain_autocorrect)
|
||||
|
||||
val terrain_type_cache = ModuleCache()
|
||||
terrain_type_cache.setSource(terrain_type_scale)
|
||||
|
||||
val highland_mountain_select = ModuleSelect()
|
||||
highland_mountain_select.setLowSource(highland_terrain)
|
||||
highland_mountain_select.setHighSource(mountain_terrain)
|
||||
highland_mountain_select.setControlSource(terrain_type_cache)
|
||||
highland_mountain_select.setThreshold(0.55)
|
||||
highland_mountain_select.setFalloff(0.15)
|
||||
|
||||
val highland_lowland_select = ModuleSelect()
|
||||
highland_lowland_select.setLowSource(lowland_terrain)
|
||||
highland_lowland_select.setHighSource(highland_mountain_select)
|
||||
highland_lowland_select.setControlSource(terrain_type_cache)
|
||||
highland_lowland_select.setThreshold(0.25)
|
||||
highland_lowland_select.setFalloff(0.15)
|
||||
|
||||
|
||||
val ground_select = ModuleSelect()
|
||||
ground_select.setLowSource(0.0)
|
||||
ground_select.setHighSource(1.0)
|
||||
ground_select.setThreshold(0.5)
|
||||
ground_select.setControlSource(highland_lowland_select)
|
||||
|
||||
val joise = Joise(ground_select)
|
||||
|
||||
// fill the area as Joise map
|
||||
println("[mapgenerator] Raising and eroding terrain...")
|
||||
for (y in 0..(TERRAIN_UNDULATION - 1)) {
|
||||
for (x in 0..WIDTH) {
|
||||
val map: Boolean = (
|
||||
joise.get(
|
||||
x / SCALE_X,
|
||||
y / SCALE_Y
|
||||
) == 1.0)
|
||||
noiseMap[y + TERRAIN_AVERAGE_HEIGHT - (TERRAIN_UNDULATION / 2)].set(x, map)
|
||||
}
|
||||
}
|
||||
// fill the area bottom of the above map as 'filled'
|
||||
for (y in TERRAIN_AVERAGE_HEIGHT + (TERRAIN_UNDULATION / 2)..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH) {
|
||||
noiseMap[y].set(x, true)
|
||||
}
|
||||
}
|
||||
|
||||
return noiseMap
|
||||
}
|
||||
|
||||
/**
|
||||
* | ----
|
||||
* | ---
|
||||
* | ---
|
||||
* | --
|
||||
* | -
|
||||
* | --
|
||||
* | ---
|
||||
* | ---
|
||||
* - ----------------------------
|
||||
|
||||
* @param func_x
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun getGlacierMountedAmplitude(func_x: Int): Float {
|
||||
if (func_x > GLACIER_MOUNTAIN_WIDTH) {
|
||||
return 0f
|
||||
} else {
|
||||
val func_y = GLACIER_MOUNTAIN_HEIGHT / 2f * Math.cos((10 * func_x / (FastMath.PI * GLACIER_MOUNTAIN_WIDTH)).toDouble()).toFloat() + GLACIER_MOUNTAIN_HEIGHT / 2
|
||||
return func_y
|
||||
}
|
||||
}
|
||||
|
||||
private fun placeGlacierMount(heightMap: IntArray) {
|
||||
println("[mapgenerator] Putting glacier...")
|
||||
|
||||
// raise
|
||||
for (i in heightMap.indices) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
heightMap[i] += Math.round(getGlacierMountedAmplitude(i))
|
||||
} else {
|
||||
heightMap[i] += Math.round(getGlacierMountedAmplitude(heightMap.size - i - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cosine interpolation between point a and b.
|
||||
* @param x [0.0, 1.0] relative position between a and b
|
||||
* *
|
||||
* @param a leftmost point
|
||||
* *
|
||||
* @param b rightmost point
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun interpolateCosine(x: Float, a: Float, b: Float): Float {
|
||||
val ft = x * FastMath.PI
|
||||
val f = (1 - FastMath.cos(ft)) * 0.5f
|
||||
|
||||
return a * (1 - f) + b * f
|
||||
}
|
||||
|
||||
private fun heightMapToObjectMap(fs: IntArray) {
|
||||
println("[mapgenerator] Shaping world as processed...")
|
||||
|
||||
// iterate for heightmap
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val medianPosition = TERRAIN_AVERAGE_HEIGHT
|
||||
val pillarOffset = medianPosition - fs[x]
|
||||
|
||||
// for pillar length
|
||||
for (i in 0..HEIGHT - pillarOffset - 1) {
|
||||
|
||||
if (i < DIRT_LAYER_DEPTH) {
|
||||
map.setTileTerrain(x, i + pillarOffset, TileNameCode.DIRT)
|
||||
map.setTileWall(x, i + pillarOffset, TileNameCode.DIRT)
|
||||
} else {
|
||||
map.setTileTerrain(x, i + pillarOffset, TileNameCode.STONE)
|
||||
map.setTileWall(x, i + pillarOffset, TileNameCode.STONE)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillMapByNoiseMap() {
|
||||
println("[mapgenerator] Shaping world...")
|
||||
// generate dirt-stone transition line
|
||||
// use catmull spline
|
||||
val dirtStoneLine = IntArray(WIDTH)
|
||||
val POINTS_GAP = 64 // power of two!
|
||||
val splineControlPoints = Array((WIDTH / POINTS_GAP) + 1, { Pair(0, 0) })
|
||||
|
||||
// get spline points
|
||||
for (x in 0..(WIDTH / POINTS_GAP)) {
|
||||
for (y in 0..TERRAIN_AVERAGE_HEIGHT + TERRAIN_UNDULATION) {
|
||||
splineControlPoints[x] = Pair(x * POINTS_GAP, y)
|
||||
if (terrainMap[y].get(splineControlPoints[x].first)) break
|
||||
}
|
||||
// println("Spline[$x] x: ${splineControlPoints[x].first}, " +
|
||||
// "y: ${splineControlPoints[x].second}")
|
||||
}
|
||||
|
||||
// do interpolation
|
||||
for (x in 0..dirtStoneLine.size - 1) {
|
||||
val x_1 = x / POINTS_GAP
|
||||
|
||||
val splineX0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
|
||||
val splineX1 = splineControlPoints[x_1].first
|
||||
val splineX2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
|
||||
val splineX3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].first
|
||||
|
||||
val splineP0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
|
||||
val splineP1 = splineControlPoints[x_1].second.toFloat()
|
||||
val splineP2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
|
||||
val splineP3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
|
||||
|
||||
if (x in POINTS_GAP - 1..WIDTH - 2 * POINTS_GAP) {
|
||||
dirtStoneLine[x] = Math.round(FastMath.interpolateCatmullRom(
|
||||
(x - splineX1) / POINTS_GAP.toFloat(),
|
||||
-0.3f,//0.01f,
|
||||
splineP0,
|
||||
splineP1,
|
||||
splineP2,
|
||||
splineP3
|
||||
))
|
||||
}
|
||||
else {
|
||||
dirtStoneLine[x] = Math.round(FastMath.interpolateCatmullRom(
|
||||
(x - splineX1) / POINTS_GAP.toFloat(),
|
||||
-0.3f,//0.01f,
|
||||
splineP0,
|
||||
splineP1,
|
||||
splineP2,
|
||||
splineP3
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// scan vertically
|
||||
for (x in 0..WIDTH - 1) {
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
if (terrainMap[clamp(y + DIRT_LAYER_DEPTH, 0, HEIGHT - 1)].get(x)) {
|
||||
// map.setTileTerrain(x, y, TileNameCode.DIRT)
|
||||
// map.setTileWall(x, y, TileNameCode.DIRT)
|
||||
val tile =
|
||||
if (y < dirtStoneLine[x]) TileNameCode.DIRT
|
||||
else TileNameCode.STONE
|
||||
map.setTileTerrain(x, y, tile)
|
||||
map.setTileWall(x, y, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Carve */
|
||||
|
||||
/**
|
||||
* Carve (place specified block) by noisemap, inversed gradation filter applied.
|
||||
* @param map noisemap
|
||||
* *
|
||||
* @param scarcity higher == rarer
|
||||
* * 1.0 is a default value. This value works as a multiplier to the gradient filter.
|
||||
* @param tile
|
||||
* *
|
||||
* @param message
|
||||
*/
|
||||
private fun carveByMap(noisemap: Any, scarcity: Float, tile: Int, message: String,
|
||||
filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
filterStart: Float = NOISE_GRAD_START,
|
||||
filterEnd: Float = NOISE_GRAD_END) {
|
||||
println("[mapgenerator] " + message)
|
||||
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = when (noisemap) {
|
||||
is Joise ->
|
||||
noisemap.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
is TaggedSimplexNoise -> noisemap.noiseModule.getNoise(
|
||||
Math.round(x / noisemap.xStretch),
|
||||
Math.round(y / noisemap.yStretch)
|
||||
)
|
||||
|
||||
else -> throw(IllegalArgumentException("[mapgenerator] Unknown noise module type '${noisemap.javaClass.simpleName}': Only the 'Joise' or 'TaggedSimplexNoise' is valid."))
|
||||
}
|
||||
|
||||
if (noise > filter.getGrad(y, filterStart, filterEnd) * scarcity) {
|
||||
map.setTileTerrain(x, y, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillByMap(noisemap: Any, scarcity: Float, replaceFrom: Int, replaceTo: Int, message: String,
|
||||
filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
filterStart: Float = NOISE_GRAD_START,
|
||||
filterEnd: Float = NOISE_GRAD_END) {
|
||||
println("[mapgenerator] " + message)
|
||||
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = when (noisemap) {
|
||||
is Joise ->
|
||||
noisemap.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
is TaggedSimplexNoise -> noisemap.noiseModule.getNoise(
|
||||
Math.round(x / noisemap.xStretch),
|
||||
Math.round(y / noisemap.yStretch)
|
||||
)
|
||||
|
||||
else -> throw(IllegalArgumentException("[mapgenerator] Unknown noise module type '${noisemap.javaClass.simpleName}': Only the 'Joise' or 'TaggedSimplexNoise' is valid."))
|
||||
}
|
||||
|
||||
if (noise > filter.getGrad(y, filterStart, filterEnd) * scarcity
|
||||
&& map.getTileFromTerrain(x, y) == replaceFrom) {
|
||||
map.setTileTerrain(x, y, replaceTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillByMap(noisemap: Any, scarcity: Float, replaceFrom: Int, tile: IntArray, message: String,
|
||||
filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
filterStart: Float = NOISE_GRAD_START,
|
||||
filterEnd: Float = NOISE_GRAD_END) {
|
||||
println("[mapgenerator] " + message)
|
||||
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = when (noisemap) {
|
||||
is Joise ->
|
||||
noisemap.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
is TaggedSimplexNoise -> noisemap.noiseModule.getNoise(
|
||||
Math.round(x / noisemap.xStretch),
|
||||
Math.round(y / noisemap.yStretch)
|
||||
)
|
||||
|
||||
else -> throw(IllegalArgumentException("[mapgenerator] Unknown noise module type '${noisemap.javaClass.simpleName}': Only the 'Joise' or 'TaggedSimplexNoise' is valid."))
|
||||
}
|
||||
|
||||
if (noise > filter.getGrad(y, filterStart, filterEnd) * scarcity && map.getTileFromTerrain(x, y) == replaceFrom) {
|
||||
map.setTileTerrain(x, y, tile[random.nextInt(tile.size)])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processNoiseLayers(noiseRecords: Array<TaggedJoise>) {
|
||||
for (record in noiseRecords) {
|
||||
println("[mapgenerator] ${record.message}...")
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = record.noiseModule.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
val fromTerr = record.replaceFromTerrain
|
||||
val fromWall = record.replaceFromWall
|
||||
val to: Int = when(record.replaceTo) {
|
||||
is Int -> record.replaceTo as Int
|
||||
is IntArray -> (record.replaceTo as IntArray)[random.nextInt((record.replaceTo as IntArray).size)]
|
||||
else -> throw IllegalArgumentException("[mapgenerator] Unknown replaceTo tile type '${record.replaceTo.javaClass.canonicalName}': Only 'kotlin.Int' and 'kotlin.IntArray' is valid.")
|
||||
}
|
||||
if (to == TILE_MACRO_ALL) throw IllegalArgumentException("[mapgenerator] Invalid replaceTo: TILE_MACRO_ALL")
|
||||
val threshold = record.filter.getGrad(y, record.filterArg1, record.filterArg2)
|
||||
|
||||
if (noise > threshold * record.scarcity) {
|
||||
if ((map.getTileFromTerrain(x, y) == fromTerr || fromTerr == TILE_MACRO_ALL)
|
||||
&& (map.getTileFromWall(x, y) == fromWall || fromWall == TILE_MACRO_ALL)) {
|
||||
map.setTileTerrain(x, y, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateFloatingIslands() {
|
||||
println("[mapgenerator] Placing floating islands...")
|
||||
|
||||
val nIslandsMax = Math.round(map.width * 6f / 8192f)
|
||||
val nIslandsMin = Math.max(2, Math.round(map.width * 4f / 8192f))
|
||||
val nIslands = random.nextInt(nIslandsMax - nIslandsMin) + nIslandsMin
|
||||
val prevIndex = -1
|
||||
|
||||
val tiles = intArrayOf(TileNameCode.AIR, TileNameCode.STONE, TileNameCode.DIRT, TileNameCode.GRASS)
|
||||
|
||||
for (i in 0..nIslands - 1) {
|
||||
var currentIndex = random.nextInt(FloatingIslandsPreset.PRESETS)
|
||||
while (currentIndex == prevIndex) {
|
||||
currentIndex = random.nextInt(FloatingIslandsPreset.PRESETS)
|
||||
}
|
||||
val island = FloatingIslandsPreset.generatePreset(currentIndex, random)
|
||||
|
||||
val startingPosX = random.nextInt(map.width - 2048) + 1024
|
||||
val startingPosY = minimumFloatingIsleHeight + random.nextInt(minimumFloatingIsleHeight)
|
||||
|
||||
for (j in island.indices) {
|
||||
for (k in 0..island[0].size - 1) {
|
||||
if (island[j][k] > 0) {
|
||||
map.setTileTerrain(k + startingPosX, j + startingPosY, tiles[island[j][k]])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flood */
|
||||
|
||||
private fun floodBottomLava() {
|
||||
println("[mapgenerator] Flooding bottom lava...")
|
||||
for (i in HEIGHT * 14 / 15..HEIGHT - 1) {
|
||||
for (j in 0..WIDTH - 1) {
|
||||
if (map.terrainArray[i][j].toInt() == 0) {
|
||||
map.setTileTerrain(j, i, TileNameCode.LAVA)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Plant */
|
||||
|
||||
private fun plantGrass() {
|
||||
println("[mapgenerator] Planting grass...")
|
||||
|
||||
/* TODO composing dirt and stone
|
||||
* over certain level, use background dirt with stone 'peckles'
|
||||
* beetween levels, use background dirt with larger and denser stone peckles.
|
||||
* under another certain level, use background stone with dirt peckles.
|
||||
*/
|
||||
|
||||
for (y in TERRAIN_AVERAGE_HEIGHT - TERRAIN_UNDULATION..TERRAIN_AVERAGE_HEIGHT + TERRAIN_UNDULATION - 1) {
|
||||
for (x in 0..map.width - 1) {
|
||||
|
||||
val thisTile = map.getTileFromTerrain(x, y)
|
||||
|
||||
for (i in 0..8) {
|
||||
var nearbyWallTile: Int?
|
||||
nearbyWallTile = map.getTileFromWall(x + i % 3 - 1, y + i / 3 - 1)
|
||||
|
||||
if (nearbyWallTile == null) break;
|
||||
|
||||
if (i != 4 && thisTile == TileNameCode.DIRT && nearbyWallTile == TileNameCode.AIR) {
|
||||
map.setTileTerrain(x, y, TileNameCode.GRASS)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun isGrassOrDirt(x: Int, y: Int): Boolean {
|
||||
return map.getTileFromTerrain(x, y) == TileNameCode.GRASS || map.getTileFromTerrain(x, y) == TileNameCode.DIRT
|
||||
}
|
||||
|
||||
private fun replaceIfTerrain(ifTileRaw: Int, x: Int, y: Int, replaceTileRaw: Int) {
|
||||
if (map.getTileFromTerrain(x, y) == ifTileRaw) {
|
||||
map.setTileTerrain(x, y, replaceTileRaw)
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceIfWall(ifTileRaw: Int, x: Int, y: Int, replaceTileRaw: Int) {
|
||||
if (map.getTileFromWall(x, y) == ifTileRaw) {
|
||||
map.setTileWall(x, y, replaceTileRaw)
|
||||
}
|
||||
}
|
||||
|
||||
/* Post-process */
|
||||
|
||||
private fun fillOcean() {
|
||||
val thisSandList = intArrayOf(
|
||||
TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND,
|
||||
TileNameCode.SAND_WHITE, TileNameCode.SAND_WHITE, TileNameCode.SAND_WHITE,
|
||||
TileNameCode.SAND_BLACK, TileNameCode.SAND_BLACK, TileNameCode.SAND_GREEN
|
||||
)
|
||||
val thisRand = HQRNG(SEED xor random.nextLong())
|
||||
val thisSand = thisSandList[thisRand.nextInt(thisSandList.size)]
|
||||
// val thisSand = TileNameCode.SAND_GREEN
|
||||
|
||||
val thisSandStr = if (thisSand == TileNameCode.SAND_BLACK)
|
||||
"black"
|
||||
else if (thisSand == TileNameCode.SAND_GREEN)
|
||||
"green"
|
||||
else if (thisSand == TileNameCode.SAND)
|
||||
"yellow"
|
||||
else
|
||||
"white"
|
||||
println("[mapgenerator] Beach sand type: $thisSandStr")
|
||||
|
||||
var ix = 0
|
||||
while (ix < OCEAN_WIDTH * 1.5) {
|
||||
//flooding
|
||||
if (ix < OCEAN_WIDTH) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
for (y in getTerrainHeightFromHeightMap(OCEAN_WIDTH)..getTerrainHeightFromHeightMap(ix) - 1) {
|
||||
map.setTileTerrain(ix, y, TileNameCode.WATER)
|
||||
}
|
||||
} else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
for (y in getTerrainHeightFromHeightMap(map.width - 1 - OCEAN_WIDTH)..getTerrainHeightFromHeightMap(map.width - 1 - ix) - 1) {
|
||||
map.setTileTerrain(map.width - 1 - ix, y, TileNameCode.WATER)
|
||||
}
|
||||
}
|
||||
}
|
||||
//sand
|
||||
// linearly increase thickness of the sand sheet
|
||||
for (iy in 0..40 - ix * 40 / (OCEAN_WIDTH + SHORE_WIDTH) - 1) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
val terrainPoint = getTerrainHeightFromHeightMap(ix)
|
||||
|
||||
|
||||
map.setTileTerrain(ix, terrainPoint + iy, thisSand)
|
||||
// clear grass and make the sheet thicker
|
||||
map.setTileTerrain(ix, terrainPoint + iy - 1, thisSand)
|
||||
} else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
val terrainPoint = getTerrainHeightFromHeightMap(map.width - 1 - ix)
|
||||
|
||||
map.setTileTerrain(map.width - 1 - ix, terrainPoint + iy, thisSand)
|
||||
// clear grass and make the sheet thicker
|
||||
map.setTileTerrain(map.width - 1 - ix, terrainPoint + iy - 1, thisSand)
|
||||
}
|
||||
}
|
||||
ix++
|
||||
}
|
||||
}
|
||||
|
||||
private fun freeze() {
|
||||
for (y in 0..map.height - 1 - 1) {
|
||||
for (x in 0..getFrozenAreaWidth(y) - 1) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
replaceIfTerrain(TileNameCode.DIRT, x, y, TileNameCode.SNOW)
|
||||
replaceIfTerrain(TileNameCode.STONE, x, y, TileNameCode.ICE_NATURAL)
|
||||
|
||||
replaceIfWall(TileNameCode.DIRT, x, y, TileNameCode.SNOW)
|
||||
replaceIfWall(TileNameCode.STONE, x, y, TileNameCode.ICE_NATURAL)
|
||||
} else {
|
||||
replaceIfTerrain(TileNameCode.DIRT, map.width - 1 - x, y, TileNameCode.SNOW)
|
||||
replaceIfTerrain(TileNameCode.STONE, map.width - 1 - x, y, TileNameCode.ICE_NATURAL)
|
||||
|
||||
replaceIfWall(TileNameCode.DIRT, map.width - 1 - x, y, TileNameCode.SNOW)
|
||||
replaceIfWall(TileNameCode.STONE, map.width - 1 - x, y, TileNameCode.ICE_NATURAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @return width of the frozen area for mapgenerator.freeze
|
||||
*/
|
||||
private fun getFrozenAreaWidth(y: Int): Int {
|
||||
val randDeviation = 7
|
||||
// narrower that the actual width
|
||||
val width = Math.round(GLACIER_MOUNTAIN_WIDTH * 0.625f)
|
||||
val height: Int
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
height = getTerrainHeightFromHeightMap(width)
|
||||
} else {
|
||||
height = getTerrainHeightFromHeightMap(map.width - 1 - width)
|
||||
}
|
||||
val k = width / FastMath.sqrt(height.toFloat())
|
||||
|
||||
if (y < height) {
|
||||
// ground
|
||||
return width
|
||||
} else {
|
||||
// underground
|
||||
return Math.round(
|
||||
k * FastMath.sqrt(y.toFloat()) + (random.nextInt(3) - 1))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param x position of heightmap
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun getTerrainHeightFromHeightMap(x: Int): Int {
|
||||
TODO()
|
||||
}
|
||||
|
||||
|
||||
/* Utility */
|
||||
|
||||
private fun clampN(clampNumber: Int, num: Int): Int {
|
||||
return FastMath.floor((num / clampNumber).toFloat()) * clampNumber
|
||||
}
|
||||
|
||||
private fun outOfBound(w: Int, h: Int, x: Int, y: Int): Boolean {
|
||||
return !(x > 0 && y > 0 && x < w && y < h)
|
||||
}
|
||||
|
||||
private fun getDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
|
||||
return FastMath.sqrt(FastMath.pow(x1 - x2, 2f) + FastMath.pow(y2 - y1, 2f))
|
||||
}
|
||||
|
||||
private fun circularDig(i: Int, j: Int, brushSize: Int, fillFrom: Int, fill: Int) {
|
||||
val halfBrushSize = brushSize * 0.5f
|
||||
|
||||
for (pointerY in 0..brushSize - 1) {
|
||||
for (pointerX in 0..brushSize - 1) {
|
||||
if (getDistance(j.toFloat(), i.toFloat(), j + pointerX - halfBrushSize, i + pointerY - halfBrushSize) <= FastMath.floor((brushSize / 2).toFloat()) - 1) {
|
||||
if (Math.round(j + pointerX - halfBrushSize) > brushSize
|
||||
&& Math.round(j + pointerX - halfBrushSize) < WIDTH - brushSize
|
||||
&& Math.round(i + pointerY - halfBrushSize) > brushSize
|
||||
&& Math.round(i + pointerY - halfBrushSize) < HEIGHT - brushSize) {
|
||||
if (map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] == fillFrom.toByte()) {
|
||||
map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] = fill.toByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clamp(x: Int, min: Int, max: Int): Int = if (x < min) min else if (x > max) max else x
|
||||
|
||||
data class TaggedSimplexNoise(var noiseModule: SimplexNoise, var xStretch: Float, var yStretch: Float)
|
||||
|
||||
data class TaggedJoise(var message: String,
|
||||
var noiseModule: Joise, var scarcity: Float,
|
||||
var replaceFromTerrain: Int, var replaceFromWall: Int,
|
||||
var replaceTo: Any,
|
||||
var filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
var filterArg1: Float = NOISE_GRAD_START,
|
||||
var filterArg2: Float = NOISE_GRAD_END)
|
||||
}
|
||||
8
src/net/torvald/terrarum/mapgenerator/NoiseFilter.kt
Normal file
8
src/net/torvald/terrarum/mapgenerator/NoiseFilter.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-31.
|
||||
*/
|
||||
interface NoiseFilter {
|
||||
fun getGrad(func_argX: Int, start: Float, end: Float): Float
|
||||
}
|
||||
46
src/net/torvald/terrarum/mapgenerator/NoiseFilterCubic.kt
Normal file
46
src/net/torvald/terrarum/mapgenerator/NoiseFilterCubic.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
|
||||
/**
|
||||
* Double Quadratic polynomial
|
||||
* (16/9) * (start-end)/height^2 * (x-height)^2 + end
|
||||
* 16/9: terrain is formed from 1/4 of height.
|
||||
* 1 - (1/4) = 3/4, reverse it and square it.
|
||||
* That makes 16/9.
|
||||
|
||||
* Shape:
|
||||
|
||||
* cavity -
|
||||
* small
|
||||
* -
|
||||
* -
|
||||
* --
|
||||
* ----
|
||||
* cavity --------
|
||||
* large ----------------
|
||||
|
||||
* @param func_argX
|
||||
* *
|
||||
* @param start
|
||||
* *
|
||||
* @param end
|
||||
* *
|
||||
* @return
|
||||
* Created by minjaesong on 16-03-31.
|
||||
*/
|
||||
object NoiseFilterCubic : NoiseFilter {
|
||||
override fun getGrad(func_argX: Int, start: Float, end: Float): Float {
|
||||
val graph_gradient = -FastMath.pow(FastMath.pow((1 - MapGenerator.TERRAIN_AVERAGE_HEIGHT).toFloat(), 3f), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9
|
||||
(start - end) / FastMath.pow(MapGenerator.HEIGHT.toFloat(), 3f) *
|
||||
FastMath.pow((func_argX - MapGenerator.HEIGHT).toFloat(), 3f) + end
|
||||
|
||||
if (func_argX < MapGenerator.TERRAIN_AVERAGE_HEIGHT) {
|
||||
return start
|
||||
} else if (func_argX >= MapGenerator.HEIGHT) {
|
||||
return end
|
||||
} else {
|
||||
return graph_gradient
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
|
||||
/**
|
||||
* Quadratic polynomial
|
||||
* -(16/9) * (start-end)/height^2 * (x - 0.25 * height)^2 + start
|
||||
* 16/9: terrain is formed from 1/4 of height.
|
||||
* 1 - (1/4) = 3/4, reverse it and square it.
|
||||
* That makes 16/9.
|
||||
|
||||
* Shape:
|
||||
|
||||
* cavity _
|
||||
* small
|
||||
* _
|
||||
* _
|
||||
* __
|
||||
* ____
|
||||
* cavity ________
|
||||
* large ________________
|
||||
|
||||
* @param func_argX
|
||||
* *
|
||||
* @param start
|
||||
* *
|
||||
* @param end
|
||||
* *
|
||||
* @return
|
||||
* Created by minjaesong on 16-03-31.
|
||||
*/
|
||||
object NoiseFilterMinusQuadratic : NoiseFilter {
|
||||
override fun getGrad(func_argX: Int, start: Float, end: Float): Float {
|
||||
val graph_gradient = -FastMath.pow(FastMath.sqr((1 - MapGenerator.TERRAIN_AVERAGE_HEIGHT).toFloat()), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9
|
||||
(start - end) / FastMath.sqr(MapGenerator.HEIGHT.toFloat()) *
|
||||
FastMath.sqr((func_argX - MapGenerator.TERRAIN_AVERAGE_HEIGHT).toFloat()) + start
|
||||
|
||||
if (func_argX < MapGenerator.TERRAIN_AVERAGE_HEIGHT) {
|
||||
return start
|
||||
} else if (func_argX >= MapGenerator.HEIGHT) {
|
||||
return end
|
||||
} else {
|
||||
return graph_gradient
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
|
||||
/**
|
||||
* Quadratic polynomial
|
||||
* (16/9) * (start-end)/height^2 * (x-height)^2 + end
|
||||
* 16/9: terrain is formed from 1/4 of height.
|
||||
* 1 - (1/4) = 3/4, reverse it and square it.
|
||||
* That makes 16/9.
|
||||
|
||||
* Shape:
|
||||
|
||||
* cavity -
|
||||
* small
|
||||
* -
|
||||
* -
|
||||
* --
|
||||
* ----
|
||||
* cavity --------
|
||||
* large ----------------
|
||||
|
||||
* @param func_argX
|
||||
* *
|
||||
* @param start
|
||||
* *
|
||||
* @param end
|
||||
* *
|
||||
* @return
|
||||
*
|
||||
* Created by minjaesong on 16-03-31.
|
||||
*/
|
||||
object NoiseFilterQuadratic : NoiseFilter {
|
||||
override fun getGrad(func_argX: Int, start: Float, end: Float): Float {
|
||||
val graph_gradient = FastMath.pow(FastMath.sqr((1 - MapGenerator.TERRAIN_AVERAGE_HEIGHT).toFloat()), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9
|
||||
(start - end) / FastMath.sqr(MapGenerator.HEIGHT.toFloat()) *
|
||||
FastMath.sqr((func_argX - MapGenerator.HEIGHT).toFloat()) + end
|
||||
|
||||
if (func_argX < MapGenerator.TERRAIN_AVERAGE_HEIGHT) {
|
||||
return start
|
||||
} else if (func_argX >= MapGenerator.HEIGHT) {
|
||||
return end
|
||||
} else {
|
||||
return graph_gradient
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/net/torvald/terrarum/mapgenerator/NoiseFilterSqrt.kt
Normal file
20
src/net/torvald/terrarum/mapgenerator/NoiseFilterSqrt.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-31.
|
||||
*/
|
||||
object NoiseFilterSqrt : NoiseFilter {
|
||||
override fun getGrad(func_argX: Int, start: Float, end: Float): Float {
|
||||
val graph_gradient = (end - start) / FastMath.sqrt((MapGenerator.HEIGHT - MapGenerator.TERRAIN_AVERAGE_HEIGHT).toFloat()) * FastMath.sqrt((func_argX - MapGenerator.TERRAIN_AVERAGE_HEIGHT).toFloat()) + start
|
||||
|
||||
if (func_argX < MapGenerator.TERRAIN_AVERAGE_HEIGHT) {
|
||||
return start
|
||||
} else if (func_argX >= MapGenerator.HEIGHT) {
|
||||
return end
|
||||
} else {
|
||||
return graph_gradient
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/net/torvald/terrarum/mapgenerator/NoiseFilterUniform.kt
Normal file
10
src/net/torvald/terrarum/mapgenerator/NoiseFilterUniform.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-31.
|
||||
*/
|
||||
object NoiseFilterUniform : NoiseFilter {
|
||||
override fun getGrad(func_argX: Int, start: Float, end: Float): Float {
|
||||
return 1f
|
||||
}
|
||||
}
|
||||
161
src/net/torvald/terrarum/mapgenerator/RoguelikeRandomiser.kt
Normal file
161
src/net/torvald/terrarum/mapgenerator/RoguelikeRandomiser.kt
Normal file
@@ -0,0 +1,161 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import net.torvald.colourutil.Col4096
|
||||
import net.torvald.random.HQRNG
|
||||
import org.newdawn.slick.Color
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-23.
|
||||
*/
|
||||
object RoguelikeRandomiser {
|
||||
|
||||
val POTION_PRIMARY_COLSET = intArrayOf(15, 15, 7, 7, 0, 0)
|
||||
|
||||
var potionColours: HashMap<Int, Col4096> = HashMap()
|
||||
var coloursDiscovered: HashMap<Col4096, Boolean> = HashMap()
|
||||
|
||||
val coloursTaken: ArrayList<Col4096> = ArrayList()
|
||||
|
||||
var seed: Long = 0
|
||||
private val random: Random = HQRNG()
|
||||
|
||||
private val POTION_HEAL_TIER1 = 0x00
|
||||
private val POTION_HEAL_TIRE2 = 0x01
|
||||
|
||||
private val POTION_MAGIC_REGEN_TIER1 = 0x10
|
||||
|
||||
private val POTION_BERSERK_TIER1 = 0x20
|
||||
|
||||
|
||||
|
||||
fun setupColours() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* For external classes/objects, does not touch COLOUR SET
|
||||
* @param arr Array of Int(0-15)
|
||||
*/
|
||||
fun composeColourFrom(arr: IntArray): Color {
|
||||
val colourElements = arr.copyOf()
|
||||
shuffleArrayInt(colourElements, HQRNG())
|
||||
|
||||
val colourStack = IntArrayStack(colourElements)
|
||||
|
||||
return Col4096(colourStack.pop(),
|
||||
colourStack.pop(),
|
||||
colourStack.pop())
|
||||
.toSlickColour()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun shuffleArrayInt(ar: IntArray, rnd: Random) {
|
||||
for (i in ar.size - 1 downTo 0) {
|
||||
val index = rnd.nextInt(i + 1);
|
||||
// Simple swap
|
||||
val a = ar[index];
|
||||
ar[index] = ar[i];
|
||||
ar[i] = a;
|
||||
}
|
||||
}
|
||||
|
||||
class IntArrayStack {
|
||||
/**
|
||||
* Number of elements in the stack
|
||||
*/
|
||||
var depth: Int = 0
|
||||
private set
|
||||
|
||||
var size: Int
|
||||
get() = data.size
|
||||
set(newSize) {
|
||||
if (newSize > depth) inflate(newSize - data.size)
|
||||
else deflate(data.size - newSize)
|
||||
}
|
||||
|
||||
private lateinit var data: IntArray
|
||||
|
||||
constructor(stackSize: Int) {
|
||||
data = IntArray(stackSize)
|
||||
}
|
||||
|
||||
constructor(arr: IntArray) {
|
||||
data = arr.copyOf()
|
||||
depth = size
|
||||
}
|
||||
|
||||
fun push(v: Int) {
|
||||
if (depth >= data.size) throw StackOverflowError()
|
||||
data[depth++] = v
|
||||
}
|
||||
|
||||
fun pop(): Int {
|
||||
if (depth == 0) throw EmptyStackException()
|
||||
return data[--depth]
|
||||
}
|
||||
|
||||
fun peek(): Int? {
|
||||
if (depth == 0) return null
|
||||
return data[depth - 1]
|
||||
}
|
||||
|
||||
fun dup() {
|
||||
if (depth == 0) throw EmptyStackException()
|
||||
if (depth == data.size) throw StackOverflowError()
|
||||
push(peek()!!)
|
||||
}
|
||||
|
||||
fun swap() {
|
||||
if (depth < 2) throw UnsupportedOperationException("Stack is empty or has only one element.")
|
||||
val up = pop()
|
||||
val dn = pop()
|
||||
push(up)
|
||||
push(dn)
|
||||
}
|
||||
|
||||
fun drop() {
|
||||
if (depth == 0) throw EmptyStackException()
|
||||
--depth
|
||||
}
|
||||
|
||||
fun defineFromArray(arr: IntArray) { data = arr.copyOf() }
|
||||
|
||||
/**
|
||||
* Increase the stack size by a factor.
|
||||
*/
|
||||
fun inflate(sizeToAdd: Int) {
|
||||
if (sizeToAdd < 0) throw UnsupportedOperationException("$sizeToAdd: Cannot deflate the stack with this function. Use deflate(int) instead.")
|
||||
size += sizeToAdd
|
||||
val oldStack = this.asArray()
|
||||
data = IntArray(size, { if (it < oldStack.size) oldStack[it] else 0 })
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the stack size by a factor. Overflowing data will be removed.
|
||||
*/
|
||||
fun deflate(sizeToTake: Int) {
|
||||
if (size - sizeToTake < 1) throw UnsupportedOperationException("$sizeToTake: Cannot deflate the stack to the size of zero or negative.")
|
||||
size -= sizeToTake
|
||||
val oldStack = this.asArray()
|
||||
data = IntArray(size, { oldStack[it] })
|
||||
if (depth > data.size) depth = data.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert stack as array. Index zero is the bottommost element.
|
||||
* @return array of data, with array size equivalent to the stack depth.
|
||||
*/
|
||||
fun asArray() = data.copyOfRange(0, depth - 1)
|
||||
|
||||
fun equalTo(other: IntArrayStack) = (this.asArray() == other.asArray())
|
||||
|
||||
fun plus() { data[depth - 2] += pop() }
|
||||
fun minus() { data[depth - 2] -= pop() }
|
||||
fun times() { data[depth - 2] *= pop() }
|
||||
fun div() { data[depth - 2] /= pop() }
|
||||
fun mod() { data[depth - 2] %= pop() }
|
||||
}
|
||||
|
||||
}
|
||||
73
src/net/torvald/terrarum/mapgenerator/SimplexNoise.kt
Normal file
73
src/net/torvald/terrarum/mapgenerator/SimplexNoise.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
import net.torvald.random.HQRNG
|
||||
import com.jme3.math.FastMath
|
||||
|
||||
class SimplexNoise
|
||||
/**
|
||||
* @param largestFeature
|
||||
* *
|
||||
* @param persistence higher the value, rougher the output
|
||||
* *
|
||||
* @param seed
|
||||
*/
|
||||
(internal var largestFeature: Int, internal var persistence: Float, internal var seed: Long) {
|
||||
|
||||
internal var octaves: Array<SimplexNoise_octave>
|
||||
internal var frequencys: FloatArray
|
||||
internal var amplitudes: FloatArray
|
||||
|
||||
init {
|
||||
|
||||
//receives a number (e.g. 128) and calculates what power of 2 it is (e.g. 2^7)
|
||||
val numberOfOctaves = FastMath.intLog2(largestFeature)
|
||||
val rnd = HQRNG(seed)
|
||||
|
||||
octaves = Array<SimplexNoise_octave>(numberOfOctaves, {i -> SimplexNoise_octave(rnd.nextInt())})
|
||||
frequencys = FloatArray(numberOfOctaves)
|
||||
amplitudes = FloatArray(numberOfOctaves)
|
||||
|
||||
for (i in 0..numberOfOctaves - 1) {
|
||||
octaves[i] = SimplexNoise_octave(rnd.nextInt())
|
||||
|
||||
frequencys[i] = FastMath.pow(2f, i.toFloat())
|
||||
amplitudes[i] = FastMath.pow(persistence, (octaves.size - i).toFloat())
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun getNoise(x: Int, y: Int): Float {
|
||||
|
||||
var result = 0f
|
||||
|
||||
for (i in octaves.indices) {
|
||||
//float frequency = FastMath.pow(2,i);
|
||||
//float amplitude = FastMath.pow(persistence,octaves.length-i);
|
||||
|
||||
result += (octaves[i].noise((x / frequencys[i]).toDouble(), (y / frequencys[i]).toDouble()) * amplitudes[i]).toFloat()
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
fun getNoise(x: Int, y: Int, z: Int): Float {
|
||||
|
||||
var result = 0f
|
||||
|
||||
for (i in octaves.indices) {
|
||||
val frequency = FastMath.pow(2f, i.toFloat())
|
||||
val amplitude = FastMath.pow(persistence, (octaves.size - i).toFloat())
|
||||
|
||||
result += (octaves[i].noise((x / frequency).toDouble(), (y / frequency).toDouble(), (z / frequency).toDouble()) * amplitude).toFloat()
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
}
|
||||
457
src/net/torvald/terrarum/mapgenerator/SimplexNoise_octave.kt
Normal file
457
src/net/torvald/terrarum/mapgenerator/SimplexNoise_octave.kt
Normal file
@@ -0,0 +1,457 @@
|
||||
package net.torvald.terrarum.mapgenerator
|
||||
|
||||
/*
|
||||
* A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
|
||||
*
|
||||
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
|
||||
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
|
||||
* Better rank ordering method by Stefan Gustavson in 2012.
|
||||
*
|
||||
* This could be speeded up even further, but it's useful as it is.
|
||||
*
|
||||
* Version 2012-03-09
|
||||
*
|
||||
* This code was placed in the public domain by its original author,
|
||||
* Stefan Gustavson. You may use it as you see fit, but
|
||||
* attribution is appreciated.
|
||||
*
|
||||
*/
|
||||
|
||||
import net.torvald.random.HQRNG
|
||||
|
||||
class SimplexNoise_octave(seed: Int) { // Simplex noise in 2D, 3D and 4D
|
||||
|
||||
private var p = ShortArray(p_supply.size)
|
||||
|
||||
// To remove the need for index wrapping, double the permutation table length
|
||||
private val perm = ShortArray(512)
|
||||
private val permMod12 = ShortArray(512)
|
||||
|
||||
init {
|
||||
p = p_supply.clone()
|
||||
|
||||
if (seed == RANDOMSEED) {
|
||||
throw IllegalArgumentException("Seed cannot be zero.")
|
||||
}
|
||||
|
||||
//the random for the swaps
|
||||
val rand = HQRNG(seed.toLong())
|
||||
|
||||
//the seed determines the swaps that occur between the default order and the order we're actually going to use
|
||||
for (i in 0..NUMBEROFSWAPS - 1) {
|
||||
val swapFrom = rand.nextInt(p.size)
|
||||
val swapTo = rand.nextInt(p.size)
|
||||
|
||||
val temp = p[swapFrom]
|
||||
p[swapFrom] = p[swapTo]
|
||||
p[swapTo] = temp
|
||||
}
|
||||
|
||||
|
||||
for (i in 0..511) {
|
||||
perm[i] = p[i and 255]
|
||||
permMod12[i] = (perm[i] % 12).toShort()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2D simplex noise
|
||||
fun noise(xin: Double, yin: Double): Double {
|
||||
val n0: Double
|
||||
val n1: Double
|
||||
val n2: Double // Noise contributions from the three corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
val s = (xin + yin) * F2 // Hairy factor for 2D
|
||||
val i = fastfloor(xin + s)
|
||||
val j = fastfloor(yin + s)
|
||||
val t = (i + j) * G2
|
||||
val X0 = i - t // Unskew the cell origin back to (x,y) space
|
||||
val Y0 = j - t
|
||||
val x0 = xin - X0 // The x,y distances from the cell origin
|
||||
val y0 = yin - Y0
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
val i1: Int
|
||||
val j1: Int // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||
if (x0 > y0) {
|
||||
i1 = 1
|
||||
j1 = 0
|
||||
} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||
else {
|
||||
i1 = 0
|
||||
j1 = 1
|
||||
} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
val x1 = x0 - i1 + G2 // Offsets for middle corner in (x,y) unskewed coords
|
||||
val y1 = y0 - j1 + G2
|
||||
val x2 = x0 - 1.0 + 2.0 * G2 // Offsets for last corner in (x,y) unskewed coords
|
||||
val y2 = y0 - 1.0 + 2.0 * G2
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
val ii = i and 255
|
||||
val jj = j and 255
|
||||
val gi0 = permMod12[ii + perm[jj]].toInt()
|
||||
val gi1 = permMod12[ii + i1 + perm[jj + j1].toInt()].toInt()
|
||||
val gi2 = permMod12[ii + 1 + perm[jj + 1].toInt()].toInt()
|
||||
// Calculate the contribution from the three corners
|
||||
var t0 = 0.5 - x0 * x0 - y0 * y0
|
||||
if (t0 < 0)
|
||||
n0 = 0.0
|
||||
else {
|
||||
t0 *= t0
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0) // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
var t1 = 0.5 - x1 * x1 - y1 * y1
|
||||
if (t1 < 0)
|
||||
n1 = 0.0
|
||||
else {
|
||||
t1 *= t1
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1)
|
||||
}
|
||||
var t2 = 0.5 - x2 * x2 - y2 * y2
|
||||
if (t2 < 0)
|
||||
n2 = 0.0
|
||||
else {
|
||||
t2 *= t2
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2)
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 70.0 * (n0 + n1 + n2)
|
||||
}
|
||||
|
||||
|
||||
// 3D simplex noise
|
||||
fun noise(xin: Double, yin: Double, zin: Double): Double {
|
||||
val n0: Double
|
||||
val n1: Double
|
||||
val n2: Double
|
||||
val n3: Double // Noise contributions from the four corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
val s = (xin + yin + zin) * F3 // Very nice and simple skew factor for 3D
|
||||
val i = fastfloor(xin + s)
|
||||
val j = fastfloor(yin + s)
|
||||
val k = fastfloor(zin + s)
|
||||
val t = (i + j + k) * G3
|
||||
val X0 = i - t // Unskew the cell origin back to (x,y,z) space
|
||||
val Y0 = j - t
|
||||
val Z0 = k - t
|
||||
val x0 = xin - X0 // The x,y,z distances from the cell origin
|
||||
val y0 = yin - Y0
|
||||
val z0 = zin - Z0
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
// Determine which simplex we are in.
|
||||
val i1: Int
|
||||
val j1: Int
|
||||
val k1: Int // Offsets for second corner of simplex in (i,j,k) coords
|
||||
val i2: Int
|
||||
val j2: Int
|
||||
val k2: Int // Offsets for third corner of simplex in (i,j,k) coords
|
||||
if (x0 >= y0) {
|
||||
if (y0 >= z0) {
|
||||
i1 = 1
|
||||
j1 = 0
|
||||
k1 = 0
|
||||
i2 = 1
|
||||
j2 = 1
|
||||
k2 = 0
|
||||
} // X Y Z order
|
||||
else if (x0 >= z0) {
|
||||
i1 = 1
|
||||
j1 = 0
|
||||
k1 = 0
|
||||
i2 = 1
|
||||
j2 = 0
|
||||
k2 = 1
|
||||
} // X Z Y order
|
||||
else {
|
||||
i1 = 0
|
||||
j1 = 0
|
||||
k1 = 1
|
||||
i2 = 1
|
||||
j2 = 0
|
||||
k2 = 1
|
||||
} // Z X Y order
|
||||
}
|
||||
else {
|
||||
// x0<y0
|
||||
if (y0 < z0) {
|
||||
i1 = 0
|
||||
j1 = 0
|
||||
k1 = 1
|
||||
i2 = 0
|
||||
j2 = 1
|
||||
k2 = 1
|
||||
} // Z Y X order
|
||||
else if (x0 < z0) {
|
||||
i1 = 0
|
||||
j1 = 1
|
||||
k1 = 0
|
||||
i2 = 0
|
||||
j2 = 1
|
||||
k2 = 1
|
||||
} // Y Z X order
|
||||
else {
|
||||
i1 = 0
|
||||
j1 = 1
|
||||
k1 = 0
|
||||
i2 = 1
|
||||
j2 = 1
|
||||
k2 = 0
|
||||
} // Y X Z order
|
||||
}
|
||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||
// c = 1/6.
|
||||
val x1 = x0 - i1 + G3 // Offsets for second corner in (x,y,z) coords
|
||||
val y1 = y0 - j1 + G3
|
||||
val z1 = z0 - k1 + G3
|
||||
val x2 = x0 - i2 + 2.0 * G3 // Offsets for third corner in (x,y,z) coords
|
||||
val y2 = y0 - j2 + 2.0 * G3
|
||||
val z2 = z0 - k2 + 2.0 * G3
|
||||
val x3 = x0 - 1.0 + 3.0 * G3 // Offsets for last corner in (x,y,z) coords
|
||||
val y3 = y0 - 1.0 + 3.0 * G3
|
||||
val z3 = z0 - 1.0 + 3.0 * G3
|
||||
// Work out the hashed gradient indices of the four simplex corners
|
||||
val ii = i and 255
|
||||
val jj = j and 255
|
||||
val kk = k and 255
|
||||
val gi0 = permMod12[ii + perm[jj + perm[kk]]].toInt()
|
||||
val gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1].toInt()].toInt()].toInt()
|
||||
val gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2].toInt()].toInt()].toInt()
|
||||
val gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1].toInt()].toInt()].toInt()
|
||||
// Calculate the contribution from the four corners
|
||||
var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0
|
||||
if (t0 < 0)
|
||||
n0 = 0.0
|
||||
else {
|
||||
t0 *= t0
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0)
|
||||
}
|
||||
var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1
|
||||
if (t1 < 0)
|
||||
n1 = 0.0
|
||||
else {
|
||||
t1 *= t1
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1)
|
||||
}
|
||||
var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2
|
||||
if (t2 < 0)
|
||||
n2 = 0.0
|
||||
else {
|
||||
t2 *= t2
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2)
|
||||
}
|
||||
var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3
|
||||
if (t3 < 0)
|
||||
n3 = 0.0
|
||||
else {
|
||||
t3 *= t3
|
||||
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3)
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to stay just inside [-1,1]
|
||||
return 32.0 * (n0 + n1 + n2 + n3)
|
||||
}
|
||||
|
||||
|
||||
// 4D simplex noise, better simplex rank ordering method 2012-03-09
|
||||
fun noise(x: Double, y: Double, z: Double, w: Double): Double {
|
||||
|
||||
val n0: Double
|
||||
val n1: Double
|
||||
val n2: Double
|
||||
val n3: Double
|
||||
val n4: Double // Noise contributions from the five corners
|
||||
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
||||
val s = (x + y + z + w) * F4 // Factor for 4D skewing
|
||||
val i = fastfloor(x + s)
|
||||
val j = fastfloor(y + s)
|
||||
val k = fastfloor(z + s)
|
||||
val l = fastfloor(w + s)
|
||||
val t = (i + j + k + l) * G4 // Factor for 4D unskewing
|
||||
val X0 = i - t // Unskew the cell origin back to (x,y,z,w) space
|
||||
val Y0 = j - t
|
||||
val Z0 = k - t
|
||||
val W0 = l - t
|
||||
val x0 = x - X0 // The x,y,z,w distances from the cell origin
|
||||
val y0 = y - Y0
|
||||
val z0 = z - Z0
|
||||
val w0 = w - W0
|
||||
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
||||
// To find out which of the 24 possible simplices we're in, we need to
|
||||
// determine the magnitude ordering of x0, y0, z0 and w0.
|
||||
// Six pair-wise comparisons are performed between each possible pair
|
||||
// of the four coordinates, and the results are used to rank the numbers.
|
||||
var rankx = 0
|
||||
var ranky = 0
|
||||
var rankz = 0
|
||||
var rankw = 0
|
||||
if (x0 > y0) rankx++ else ranky++
|
||||
if (x0 > z0) rankx++ else rankz++
|
||||
if (x0 > w0) rankx++ else rankw++
|
||||
if (y0 > z0) ranky++ else rankz++
|
||||
if (y0 > w0) ranky++ else rankw++
|
||||
if (z0 > w0) rankz++ else rankw++
|
||||
val i1: Int
|
||||
val j1: Int
|
||||
val k1: Int
|
||||
val l1: Int // The integer offsets for the second simplex corner
|
||||
val i2: Int
|
||||
val j2: Int
|
||||
val k2: Int
|
||||
val l2: Int // The integer offsets for the third simplex corner
|
||||
val i3: Int
|
||||
val j3: Int
|
||||
val k3: Int
|
||||
val l3: Int // The integer offsets for the fourth simplex corner
|
||||
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
||||
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
||||
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
||||
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
||||
// Rank 3 denotes the largest coordinate.
|
||||
i1 = if (rankx >= 3) 1 else 0
|
||||
j1 = if (ranky >= 3) 1 else 0
|
||||
k1 = if (rankz >= 3) 1 else 0
|
||||
l1 = if (rankw >= 3) 1 else 0
|
||||
// Rank 2 denotes the second largest coordinate.
|
||||
i2 = if (rankx >= 2) 1 else 0
|
||||
j2 = if (ranky >= 2) 1 else 0
|
||||
k2 = if (rankz >= 2) 1 else 0
|
||||
l2 = if (rankw >= 2) 1 else 0
|
||||
// Rank 1 denotes the second smallest coordinate.
|
||||
i3 = if (rankx >= 1) 1 else 0
|
||||
j3 = if (ranky >= 1) 1 else 0
|
||||
k3 = if (rankz >= 1) 1 else 0
|
||||
l3 = if (rankw >= 1) 1 else 0
|
||||
// The fifth corner has all coordinate offsets = 1, so no need to compute that.
|
||||
val x1 = x0 - i1 + G4 // Offsets for second corner in (x,y,z,w) coords
|
||||
val y1 = y0 - j1 + G4
|
||||
val z1 = z0 - k1 + G4
|
||||
val w1 = w0 - l1 + G4
|
||||
val x2 = x0 - i2 + 2.0 * G4 // Offsets for third corner in (x,y,z,w) coords
|
||||
val y2 = y0 - j2 + 2.0 * G4
|
||||
val z2 = z0 - k2 + 2.0 * G4
|
||||
val w2 = w0 - l2 + 2.0 * G4
|
||||
val x3 = x0 - i3 + 3.0 * G4 // Offsets for fourth corner in (x,y,z,w) coords
|
||||
val y3 = y0 - j3 + 3.0 * G4
|
||||
val z3 = z0 - k3 + 3.0 * G4
|
||||
val w3 = w0 - l3 + 3.0 * G4
|
||||
val x4 = x0 - 1.0 + 4.0 * G4 // Offsets for last corner in (x,y,z,w) coords
|
||||
val y4 = y0 - 1.0 + 4.0 * G4
|
||||
val z4 = z0 - 1.0 + 4.0 * G4
|
||||
val w4 = w0 - 1.0 + 4.0 * G4
|
||||
// Work out the hashed gradient indices of the five simplex corners
|
||||
val ii = i and 255
|
||||
val jj = j and 255
|
||||
val kk = k and 255
|
||||
val ll = l and 255
|
||||
val gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32
|
||||
val gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1].toInt()].toInt()].toInt()] % 32
|
||||
val gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2].toInt()].toInt()].toInt()] % 32
|
||||
val gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3].toInt()].toInt()].toInt()] % 32
|
||||
val gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1].toInt()].toInt()].toInt()] % 32
|
||||
// Calculate the contribution from the five corners
|
||||
var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0
|
||||
if (t0 < 0)
|
||||
n0 = 0.0
|
||||
else {
|
||||
t0 *= t0
|
||||
n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0)
|
||||
}
|
||||
var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1
|
||||
if (t1 < 0)
|
||||
n1 = 0.0
|
||||
else {
|
||||
t1 *= t1
|
||||
n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1)
|
||||
}
|
||||
var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2
|
||||
if (t2 < 0)
|
||||
n2 = 0.0
|
||||
else {
|
||||
t2 *= t2
|
||||
n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2)
|
||||
}
|
||||
var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3
|
||||
if (t3 < 0)
|
||||
n3 = 0.0
|
||||
else {
|
||||
t3 *= t3
|
||||
n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3)
|
||||
}
|
||||
var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4
|
||||
if (t4 < 0)
|
||||
n4 = 0.0
|
||||
else {
|
||||
t4 *= t4
|
||||
n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4)
|
||||
}
|
||||
// Sum up and scale the result to cover the range [-1,1]
|
||||
return 27.0 * (n0 + n1 + n2 + n3 + n4)
|
||||
}
|
||||
|
||||
// Inner class to speed upp gradient computations
|
||||
// (array access is a lot slower than member access)
|
||||
private class Grad {
|
||||
internal var x: Double = 0.toDouble()
|
||||
internal var y: Double = 0.toDouble()
|
||||
internal var z: Double = 0.toDouble()
|
||||
internal var w: Double = 0.toDouble()
|
||||
|
||||
internal constructor(x: Double, y: Double, z: Double) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
}
|
||||
|
||||
internal constructor(x: Double, y: Double, z: Double, w: Double) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
this.w = w
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
var RANDOMSEED = 0
|
||||
private val NUMBEROFSWAPS = 400
|
||||
|
||||
private val grad3 = arrayOf(Grad(1.0, 1.0, 0.0), Grad(-1.0, 1.0, 0.0), Grad(1.0, -1.0, 0.0), Grad(-1.0, -1.0, 0.0), Grad(1.0, 0.0, 1.0), Grad(-1.0, 0.0, 1.0), Grad(1.0, 0.0, -1.0), Grad(-1.0, 0.0, -1.0), Grad(0.0, 1.0, 1.0), Grad(0.0, -1.0, 1.0), Grad(0.0, 1.0, -1.0), Grad(0.0, -1.0, -1.0))
|
||||
|
||||
private val grad4 = arrayOf(Grad(0.0, 1.0, 1.0, 1.0), Grad(0.0, 1.0, 1.0, -1.0), Grad(0.0, 1.0, -1.0, 1.0), Grad(0.0, 1.0, -1.0, -1.0), Grad(0.0, -1.0, 1.0, 1.0), Grad(0.0, -1.0, 1.0, -1.0), Grad(0.0, -1.0, -1.0, 1.0), Grad(0.0, -1.0, -1.0, -1.0), Grad(1.0, 0.0, 1.0, 1.0), Grad(1.0, 0.0, 1.0, -1.0), Grad(1.0, 0.0, -1.0, 1.0), Grad(1.0, 0.0, -1.0, -1.0), Grad(-1.0, 0.0, 1.0, 1.0), Grad(-1.0, 0.0, 1.0, -1.0), Grad(-1.0, 0.0, -1.0, 1.0), Grad(-1.0, 0.0, -1.0, -1.0), Grad(1.0, 1.0, 0.0, 1.0), Grad(1.0, 1.0, 0.0, -1.0), Grad(1.0, -1.0, 0.0, 1.0), Grad(1.0, -1.0, 0.0, -1.0), Grad(-1.0, 1.0, 0.0, 1.0), Grad(-1.0, 1.0, 0.0, -1.0), Grad(-1.0, -1.0, 0.0, 1.0), Grad(-1.0, -1.0, 0.0, -1.0), Grad(1.0, 1.0, 1.0, 0.0), Grad(1.0, 1.0, -1.0, 0.0), Grad(1.0, -1.0, 1.0, 0.0), Grad(1.0, -1.0, -1.0, 0.0), Grad(-1.0, 1.0, 1.0, 0.0), Grad(-1.0, 1.0, -1.0, 0.0), Grad(-1.0, -1.0, 1.0, 0.0), Grad(-1.0, -1.0, -1.0, 0.0))
|
||||
|
||||
private val p_supply = shortArrayOf(151, 160, 137, 91, 90, 15, //this contains all the numbers between 0 and 255, these are put in a random order depending upon the seed
|
||||
131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180)
|
||||
|
||||
// Skewing and unskewing factors for 2, 3, and 4 dimensions
|
||||
private val F2 = 0.5 * (Math.sqrt(3.0) - 1.0)
|
||||
private val G2 = (3.0 - Math.sqrt(3.0)) / 6.0
|
||||
private val F3 = 1.0 / 3.0
|
||||
private val G3 = 1.0 / 6.0
|
||||
private val F4 = (Math.sqrt(5.0) - 1.0) / 4.0
|
||||
private val G4 = (5.0 - Math.sqrt(5.0)) / 20.0
|
||||
|
||||
// This method is a *lot* faster than using (int)Math.floor(x)
|
||||
private fun fastfloor(x: Double): Int {
|
||||
val xi = x.toInt()
|
||||
return if (x < xi) xi - 1 else xi
|
||||
}
|
||||
|
||||
private fun dot(g: Grad, x: Double, y: Double): Double {
|
||||
return g.x * x + g.y * y
|
||||
}
|
||||
|
||||
private fun dot(g: Grad, x: Double, y: Double, z: Double): Double {
|
||||
return g.x * x + g.y * y + g.z * z
|
||||
}
|
||||
|
||||
private fun dot(g: Grad, x: Double, y: Double, z: Double, w: Double): Double {
|
||||
return g.x * x + g.y * y + g.z * z + g.w * w
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset01.png
Normal file
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset02.png
Normal file
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset03.png
Normal file
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset04.png
Normal file
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset04.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset05.png
Normal file
BIN
src/net/torvald/terrarum/mapgenerator/floatingIslePreset05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Reference in New Issue
Block a user