diff --git a/assets/mods/basegame/weathers/WeatherGeneric.json b/assets/mods/basegame/weathers/WeatherGeneric.json index 7c21b2b32..ea353bf7d 100644 --- a/assets/mods/basegame/weathers/WeatherGeneric.json +++ b/assets/mods/basegame/weathers/WeatherGeneric.json @@ -1,8 +1,9 @@ { + "identifier": "generic01", "skyboxGradColourMap": "generic_skybox.tga", "daylightClut": "clut_daylight.tga", "classification": "generic", - "cloudChance": 250, + "cloudChance": 125, "cloudGamma": [0.48, 2.4], "cloudGammaVariance": [0.1, 0.1], "windSpeed": 0.25, @@ -10,13 +11,13 @@ "clouds": { "cumulonimbus": { "filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.2, - "baseScale": 2.0, "scaleVariance": 0.3333333, - "altLow": 80, "altHigh": 120 + "baseScale": 4.0, "scaleVariance": 0.3333333, + "altLow": 1080, "altHigh": 1120 }, "cumulus": { "filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0, - "baseScale": 1.0, "scaleVariance": 0.6, - "altLow": 80, "altHigh": 800 + "baseScale": 2.0, "scaleVariance": 0.6, + "altLow": 1080, "altHigh": 1800 } }, "atmoTurbidity": 3.5 diff --git a/assets/mods/basegame/weathers/WeatherGeneric2.json b/assets/mods/basegame/weathers/WeatherGeneric2.json index 0c5f017fe..5ce3615dd 100644 --- a/assets/mods/basegame/weathers/WeatherGeneric2.json +++ b/assets/mods/basegame/weathers/WeatherGeneric2.json @@ -1,8 +1,9 @@ { + "identifier": "generic02", "skyboxGradColourMap": "generic_skybox.tga", "daylightClut": "clut_daylight.tga", "classification": "generic2", - "cloudChance": 1600, + "cloudChance": 800, "cloudGamma": [0.48, 2.4], "cloudGammaVariance": [0.1, 0.1], "windSpeed": 0.25, @@ -10,18 +11,18 @@ "clouds": { "cumulonimbus": { "filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.02, - "baseScale": 2.0, "scaleVariance": 0.3333333, - "altLow": 80, "altHigh": 120 + "baseScale": 4.0, "scaleVariance": 0.3333333, + "altLow": 1080, "altHigh": 1120 }, "cumulus": { "filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.05, - "baseScale": 1.0, "scaleVariance": 0.6, - "altLow": 80, "altHigh": 800 + "baseScale": 2.0, "scaleVariance": 0.6, + "altLow": 1080, "altHigh": 1800 }, "altocumulus": { "filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0, - "baseScale": 0.5, "scaleVariance": 0.1, - "altLow": 1600, "altHigh": 2400 + "baseScale": 1.0, "scaleVariance": 0.1, + "altLow": 2600, "altHigh": 3400 } }, "atmoTurbidity": 3.5, diff --git a/assets/mods/basegame/weathers/WeatherOvercast.json b/assets/mods/basegame/weathers/WeatherOvercast.json index 137ae761f..5cd86ed5c 100644 --- a/assets/mods/basegame/weathers/WeatherOvercast.json +++ b/assets/mods/basegame/weathers/WeatherOvercast.json @@ -1,4 +1,5 @@ { + "identifier": "overcast01", "skyboxGradColourMap": "generic_skybox.tga", "daylightClut": "clut_daylight.tga", "classification": "overcast", @@ -10,18 +11,18 @@ "clouds": { "cumulus": { "filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.1, - "baseScale": 0.8, "scaleVariance": 0.6, - "altLow": 80, "altHigh": 800 + "baseScale": 1.6, "scaleVariance": 0.6, + "altLow": 580, "altHigh": 1300 }, "cumulonimbus": { "filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.4, - "baseScale": 2.0, "scaleVariance": 0.3333333, - "altLow": 90, "altHigh": 120 + "baseScale": 4.0, "scaleVariance": 0.3333333, + "altLow": 590, "altHigh":60 }, "nimbostratus": { "filename": "cloud_wide.png", "tw": 4096, "th": 1024, "probability": 1.0, - "baseScale": 4.0, "scaleVariance": 0.1, - "altLow": 100, "altHigh": 100 + "baseScale": 8.0, "scaleVariance": 0.1, + "altLow": 600, "altHigh": 700 } }, "atmoTurbidity": 9.5 diff --git a/src/net/torvald/terrarum/serialise/Common.kt b/src/net/torvald/terrarum/serialise/Common.kt index dae8ecfd9..68bdfe259 100644 --- a/src/net/torvald/terrarum/serialise/Common.kt +++ b/src/net/torvald/terrarum/serialise/Common.kt @@ -14,6 +14,8 @@ import net.torvald.terrarum.savegame.ByteArray64GrowableOutputStream import net.torvald.terrarum.savegame.ByteArray64InputStream import net.torvald.terrarum.savegame.ByteArray64Reader import net.torvald.terrarum.utils.* +import net.torvald.terrarum.weather.BaseModularWeather +import net.torvald.terrarum.weather.WeatherMixer import net.torvald.terrarum.weather.WeatherStateBox import org.apache.commons.codec.digest.DigestUtils import java.io.File @@ -217,15 +219,25 @@ object Common { // WeatherStateBox jsoner.setSerializer(WeatherStateBox::class.java, object : Json.Serializer { override fun write(json: Json, obj: WeatherStateBox, knownType: Class<*>?) { - json.writeValue("${obj.x};${obj.p0};${obj.p1};${obj.p2};${obj.p3}") + json.writeValue("${obj.x};${obj.pM2};${obj.pM1};${obj.p0};${obj.p1};${obj.p2};${obj.p3}") } override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): WeatherStateBox { return jsonData.asString().split(';').map { it.toFloat() }.let { - WeatherStateBox(it[0], it[1], it[2], it[3], it[4]) + WeatherStateBox(it[0], it[1], it[2], it[3], it[4], it[5], it[6]) } } }) + // BaseModularWeather + jsoner.setSerializer(BaseModularWeather::class.java, object : Json.Serializer { + override fun write(json: Json, obj: BaseModularWeather, knownType: Class<*>?) { + json.writeValue(obj.identifier) + } + + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): BaseModularWeather { + return WeatherMixer.weatherDict[jsonData.asString()]!! + } + }) } diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index 0c4aaef7d..ded4838d3 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -112,6 +112,10 @@ class BasicDebugInfoWindow : UICanvas() { if (this == null) "null" else if (this.isNaN()) "NaN" else if (this.isInfinite()) "${if (this >= 0.0) '+' else '-'}Inf" else "${((if (this >= 0.0) "" else "-") + "${this.absoluteValue.toInt()}").padStart(intLen)}." + (10.0 pow fracLen.toDouble()).let { d -> (this.absoluteValue.times(d) % d).toInt().toString().padStart(fracLen, '0').padEnd(fracLen) } + private fun Float?.toIntAndFrac(intLen: Int, fracLen: Int = 4): String = + if (this == null) "null" else if (this.isNaN()) "NaN" else if (this.isInfinite()) "${if (this >= 0.0) '+' else '-'}Inf" else + "${((if (this >= 0.0) "" else "-") + "${this.absoluteValue.toInt()}").padStart(intLen)}." + + (10.0 pow fracLen.toDouble()).let { d -> (this.absoluteValue.times(d) % d).toInt().toString().padStart(fracLen, '0').padEnd(fracLen) } private val gap = 14f @@ -236,10 +240,10 @@ class BasicDebugInfoWindow : UICanvas() { try { world?.let { val valRaw = LightmapRenderer.getLight(mouseTileX, mouseTileY) - val rawR = valRaw?.r?.toDouble().toIntAndFrac(1,3) - val rawG = valRaw?.g?.toDouble().toIntAndFrac(1,3) - val rawB = valRaw?.b?.toDouble().toIntAndFrac(1,3) - val rawA = valRaw?.a?.toDouble().toIntAndFrac(1,3) + val rawR = valRaw?.r?.toIntAndFrac(1,3) + val rawG = valRaw?.g?.toIntAndFrac(1,3) + val rawB = valRaw?.b?.toIntAndFrac(1,3) + val rawA = valRaw?.a?.toIntAndFrac(1,3) val wallNum = it.getTileFromWall(mouseTileX, mouseTileY) val tileNum = it.getTileFromTerrain(mouseTileX, mouseTileY) @@ -249,7 +253,7 @@ class BasicDebugInfoWindow : UICanvas() { App.fontSmallNumbers.draw(batch, "$ccO$TERRAIN$ccG$tileNum", gap + 7f*(tileCursX + 3), line(tileCursY)) App.fontSmallNumbers.draw(batch, "$ccO$WALL$ccG$wallNum", gap + 7f*(tileCursX + 3), line(tileCursY + 1)) - App.fontSmallNumbers.draw(batch, "$ccO$LIQUID$ccG${fluid.type.value.toString().padEnd(3)}$ccO$BEAKER$ccG${fluid.amount}f", gap + 7f*(tileCursX + 3), line(tileCursY + 2)) + App.fontSmallNumbers.draw(batch, "$ccO$LIQUID$ccG${fluid.type.value.toString().padEnd(3)}$ccO$BEAKER$ccG${fluid.amount.toIntAndFrac(2)}", gap + 7f*(tileCursX + 3), line(tileCursY + 2)) App.fontSmallNumbers.draw(batch, "$ccO$WIRE$ccG$wireCount ${ccY}X$ccO$mouseTileX ${ccY}Y$ccO$mouseTileY", gap + 7f*(tileCursX + 3), line(tileCursY + 3)) App.fontSmallNumbers.draw(batch, "$ccR$rawR $ccG$rawG $ccB$rawB $ccW$rawA", gap + 7f*(tileCursX + 3), line(tileCursY + 4)) @@ -344,8 +348,19 @@ class BasicDebugInfoWindow : UICanvas() { private fun drawWeatherInfo(batch: SpriteBatch) { - drawWeatherStateBox(batch, WeatherMixer.weatherbox.windSpeed, "WindSpd", App.scr.width - 170, App.scr.height - 140 - 120, WeatherMixer.currentWeather.windSpeed * (1.0 + WeatherMixer.currentWeather.windSpeedVariance)) - drawWeatherStateBox(batch, WeatherMixer.weatherbox.windDir, "WindDir", App.scr.width - 170, App.scr.height - 140) + val drawX = App.scr.width - 170 + val drawXf = drawX.toFloat() + + drawWeatherStateBox(batch, WeatherMixer.weatherbox.windSpeed, "WindSpd", drawX, App.scr.height - 140 - 120, WeatherMixer.currentWeather.windSpeed * (1.0 + WeatherMixer.currentWeather.windSpeedVariance)) + drawWeatherStateBox(batch, WeatherMixer.weatherbox.windDir, "WindDir", drawX, App.scr.height - 140) + + // draw weather schedule + val schedYstart = App.scr.height - 140 - 120 - 13f * (WeatherMixer.weatherbox.weatherSchedule.size + 3) + App.fontSmallNumbers.draw(batch, "$ccY== WeatherSched [${WeatherMixer.weatherbox.weatherSchedule.size}] ==", drawXf, schedYstart) + WeatherMixer.weatherbox.weatherSchedule.forEachIndexed { index, weather -> + val sek = if (index == 0) WeatherMixer.weatherbox.updateAkku else 0 + App.fontSmallNumbers.draw(batch, "$ccY${weather.weather.identifier} $ccG${weather.duration - sek}", drawXf, schedYstart + 13 * (index + 1)) + } } private val colHairline = Color(0xf22100ff.toInt()) @@ -468,7 +483,7 @@ class BasicDebugInfoWindow : UICanvas() { // text batch.color = Color.WHITE - App.fontSmallNumbers.draw(batch, "$ccY$label $ccG${box.value().toDouble().toIntAndFrac(3)}", x.toFloat(), y - 16f) + App.fontSmallNumbers.draw(batch, "$ccY$label $ccG${box.value.toDouble().toIntAndFrac(3)}", x.toFloat(), y - 15f) } private val processorName = App.processor.replace(Regex(""" Processor|( CPU)? @ [0-9.]+GHz"""), "") + if (App.is32BitJVM) " (32-bit)" else "" diff --git a/src/net/torvald/terrarum/weather/BaseModularWeather.kt b/src/net/torvald/terrarum/weather/BaseModularWeather.kt index 489d2cda2..cf6c193e0 100644 --- a/src/net/torvald/terrarum/weather/BaseModularWeather.kt +++ b/src/net/torvald/terrarum/weather/BaseModularWeather.kt @@ -17,6 +17,7 @@ import kotlin.math.absoluteValue * Created by minjaesong on 2016-07-11. */ data class BaseModularWeather( + val identifier: String, val json: JsonValue, var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA) val daylightClut: GdxColorMap, @@ -30,9 +31,8 @@ data class BaseModularWeather( val mixFrom: String? = null, val mixPercentage: Double? = null, - - var forceWindVec: Vector3? = null ) { + /** * @param rnd random number between -1 and +1 */ diff --git a/src/net/torvald/terrarum/weather/WeatherMixer.kt b/src/net/torvald/terrarum/weather/WeatherMixer.kt index 54a4a9d58..713ec5b2e 100644 --- a/src/net/torvald/terrarum/weather/WeatherMixer.kt +++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt @@ -29,7 +29,6 @@ import java.io.File import java.io.FileFilter import java.lang.Double.doubleToLongBits import java.lang.Math.toDegrees -import java.lang.Math.toRadians import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.math.* @@ -52,16 +51,32 @@ import kotlin.math.* */ internal object WeatherMixer : RNGConsumer { + val DEFAULT_WEATHER = BaseModularWeather( + "default", + JsonValue(JsonValue.ValueType.`object`), + GdxColorMap(1, 3, Color(0x55aaffff), Color(0xaaffffff.toInt()), Color.WHITE), + GdxColorMap(2, 2, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE), + "default", + 0f, + 0f, + 0f, + Vector2(1f, 1f), + Vector2(0f, 0f), + listOf() + ) + override val RNG = HQRNG() var globalLightOverridden = false - var weatherList: HashMap> + val weatherDB: HashMap> // search by classification + val weatherDict: HashMap // search by identifier - var currentWeather: BaseModularWeather - var nextWeather: BaseModularWeather + val currentWeather: BaseModularWeather + get() = weatherbox.currentWeather +// var nextWeather: BaseModularWeather - lateinit var mixedWeather: BaseModularWeather + private var forceWindVec: Vector3? = null val globalLightNow = Cvec(0) private val cloudDrawColour = Color() @@ -105,13 +120,11 @@ internal object WeatherMixer : RNGConsumer { override fun loadFromSave(s0: Long, s1: Long) { super.loadFromSave(s0, s1) - currentWeather = weatherList[WEATHER_GENERIC]!![0] internalReset(s0, s1) initClouds() } fun internalReset() { - currentWeather = weatherList[WEATHER_GENERIC]!![0] internalReset(RNG.state0, RNG.state1) initClouds() } @@ -127,11 +140,14 @@ internal object WeatherMixer : RNGConsumer { clouds.clear() cloudsSpawned = 0 + forceWindVec = null windVector = Vector3(-0.98f, 0f, 0.21f) oldCamPos.set(WorldCamera.camVector) weatherbox = Weatherbox() + weatherbox.initWith(weatherDict["generic01"]!!, 3600L) + // TEST FILL WITH RANDOM VALUES (0..5).map { takeUniformRand(0f..1f) }.let { weatherbox.windDir.pM1 = it[0] @@ -152,7 +168,8 @@ internal object WeatherMixer : RNGConsumer { } init { - weatherList = HashMap>() + weatherDB = HashMap>() + weatherDict = HashMap() // read weather descriptions from assets/weather (modular weather) @@ -169,40 +186,16 @@ internal object WeatherMixer : RNGConsumer { for ((modname, raw) in weatherRawValidList) { val weather = readFromJson(modname, raw) + weatherDict[weather.identifier] = weather + // if List for the classification does not exist, make one - if (!weatherList.containsKey(weather.classification)) - weatherList.put(weather.classification, ArrayList()) + if (!weatherDB.containsKey(weather.classification)) + weatherDB.put(weather.classification, ArrayList()) - weatherList[weather.classification]!!.add(weather) + weatherDB[weather.classification]!!.add(weather) } - - - // initialise - try { - weatherList["titlescreen"] = arrayListOf(weatherList[WEATHER_GENERIC]!![0].copy(windSpeed = 1f)) - currentWeather = weatherList[WEATHER_GENERIC]!![0] - nextWeather = getRandomWeather(WEATHER_GENERIC) - } - catch (e: NullPointerException) { - e.printStackTrace() - - val defaultWeather = BaseModularWeather( - JsonValue(JsonValue.ValueType.`object`), - GdxColorMap(1, 3, Color(0x55aaffff), Color(0xaaffffff.toInt()), Color.WHITE), - GdxColorMap(2, 2, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE), - "default", - 0f, - 0f, - 0f, - Vector2(1f, 1f), - Vector2(0f, 0f), - listOf() - ) - - currentWeather = defaultWeather - nextWeather = defaultWeather - } + weatherDict["titlescreen"] = weatherDB[WEATHER_GENERIC]!![0].copy(windSpeed = 1f) } /** @@ -213,7 +206,8 @@ internal object WeatherMixer : RNGConsumer { // currentWeather = weatherList[WEATHER_GENERIC]!![0] // force set weather - updateWind(delta, world) + weatherbox.update(world) + updateWind() updateClouds(delta, world) @@ -230,12 +224,11 @@ internal object WeatherMixer : RNGConsumer { this[this.lastIndex] = f } + private val HALF_PI = 1.5707964f private val PI = 3.1415927f private val TWO_PI = 6.2831855f private val THREE_PI = 9.424778f - private val WIND_DIR_TIME_UNIT = 14400f // every 4hr - private val WIND_SPEED_TIME_UNIT = 3600f // every 1hr // see: https://stackoverflow.com/questions/2708476/rotation-interpolation/14498790#14498790 private fun getShortestAngle(start: Float, end: Float) = @@ -243,22 +236,20 @@ internal object WeatherMixer : RNGConsumer { if (it > PI) it - TWO_PI else it } - private fun updateWind(delta: Float, world: GameWorld) { + private fun updateWind() { + val currentWindSpeed = weatherbox.windSpeed.value + val currentWindDir = weatherbox.windDir.value * HALF_PI - val currentWindSpeed = weatherbox.windSpeed.getAndUpdate( world.worldTime.timeDelta / WIND_SPEED_TIME_UNIT) { - currentWeather.getRandomWindSpeed(takeUniformRand(-1f..1f)) - } - val currentWindDir = weatherbox.windDir.getAndUpdate( world.worldTime.timeDelta / WIND_DIR_TIME_UNIT) { RNG.nextFloat() * 4f } * Math.PI * 0.5 + printdbg(this, "Wind speed = $currentWindSpeed") - - if (currentWeather.forceWindVec != null) { - windVector.set(currentWeather.forceWindVec) + if (forceWindVec != null) { + windVector.set(forceWindVec) } else { windVector.set( - (cos(currentWindDir) * currentWindSpeed).toFloat(), + (cos(currentWindDir) * currentWindSpeed), 0f, - (sin(currentWindDir) * currentWindSpeed).toFloat() + (sin(currentWindDir) * currentWindSpeed) ) } } @@ -280,6 +271,7 @@ internal object WeatherMixer : RNGConsumer { val camvec2 = camvec.cpy() val testCamDelta = camvec.cpy().sub(oldCamPos) + // adjust camDelta to accomodate ROUNDWORLD if (testCamDelta.x.absoluteValue > world.width * TILE_SIZEF / 2f) { if (testCamDelta.x >= 0) camvec2.x -= world.width * TILE_SIZEF @@ -291,7 +283,7 @@ internal object WeatherMixer : RNGConsumer { camDelta.set(testCamDelta) - + // try to spawn an cloud val cloudChanceEveryMin = 60f / (currentWeather.cloudChance * currentWeather.windSpeed) // if chance = 0, the result will be +inf while (cloudUpdateAkku >= cloudChanceEveryMin) { @@ -302,12 +294,13 @@ internal object WeatherMixer : RNGConsumer { var immDespawnCount = 0 val immDespawnCauses = ArrayList() + printdbg(this, "Wind vector = $windVector") + // move the clouds clouds.forEach { // do parallax scrolling it.posX += camDelta.x * cloudParallaxMultX it.posY += camDelta.y * cloudParallaxMultY - it.update(windVector) if (DEBUG_CAUSE_OF_DESPAWN && it.life == 0) { @@ -498,8 +491,8 @@ internal object WeatherMixer : RNGConsumer { } internal fun titleScreenInitWeather() { - currentWeather = weatherList["titlescreen"]!![0] - currentWeather.forceWindVec = Vector3(-0.98f, 0f, -0.21f) + weatherbox.initWith(weatherDict["titlescreen"]!!, Long.MAX_VALUE) + forceWindVec = Vector3(-0.98f, 0f, -0.21f) initClouds() } @@ -728,7 +721,7 @@ internal object WeatherMixer : RNGConsumer { } } - fun getWeatherList(classification: String) = weatherList[classification]!! + fun getWeatherList(classification: String) = weatherDB[classification]!! fun getRandomWeather(classification: String) = getWeatherList(classification)[RNG.nextInt(getWeatherList(classification).size)] @@ -757,7 +750,7 @@ internal object WeatherMixer : RNGConsumer { val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}")) val daylight = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}")) - + val identifier = JSON.getString("identifier") val classification = JSON.getString("classification") @@ -797,6 +790,7 @@ internal object WeatherMixer : RNGConsumer { return BaseModularWeather( + identifier = identifier, json = JSON, skyboxGradColourMap = skybox, daylightClut = daylight, @@ -811,7 +805,7 @@ internal object WeatherMixer : RNGConsumer { } fun dispose() { - weatherList.values.forEach { list -> + weatherDB.values.forEach { list -> list.forEach { weather -> weather.clouds.forEach { it.spriteSheet.dispose() } } diff --git a/src/net/torvald/terrarum/weather/Weatherbox.kt b/src/net/torvald/terrarum/weather/Weatherbox.kt index 500ed13e0..14c92b2e3 100644 --- a/src/net/torvald/terrarum/weather/Weatherbox.kt +++ b/src/net/torvald/terrarum/weather/Weatherbox.kt @@ -1,15 +1,77 @@ package net.torvald.terrarum.weather +import com.jme3.math.FastMath import net.torvald.terrarum.floorToInt +import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.fmod -import kotlin.math.absoluteValue +import java.util.* +data class WeatherSchedule(val weather: BaseModularWeather = WeatherMixer.DEFAULT_WEATHER, val duration: Long = 3600) class Weatherbox { + companion object { + private val WIND_DIR_TIME_UNIT = 3600f * 5 // every 5hr + private val WIND_SPEED_TIME_UNIT = 3600f * 2 // every 2hr + + private val HALF_PIF = 1.5707964f + private val PIF = 3.1415927f + private val TWO_PIF = 6.2831855f + private val THREE_PIF = 9.424778f + } + + + private fun takeUniformRand(range: ClosedFloatingPointRange) = + FastMath.interpolateLinear(RNG.nextFloat(), range.start, range.endInclusive) + private fun takeTriangularRand(range: ClosedFloatingPointRange) = + FastMath.interpolateLinear((RNG.nextFloat() + RNG.nextFloat()) / 2f, range.start, range.endInclusive) + private fun takeGaussianRand(range: ClosedFloatingPointRange) = + FastMath.interpolateLinear((RNG.nextFloat() + RNG.nextFloat() + RNG.nextFloat() + RNG.nextFloat() + RNG.nextFloat() + RNG.nextFloat() + RNG.nextFloat() + RNG.nextFloat()) / 8f, range.start, range.endInclusive) + + val RNG: Random + get() = WeatherMixer.RNG + val windDir = WeatherDirBox() // 0 .. 1.0 val windSpeed = WeatherStateBox() // 0 .. arbitrarily large number + val weatherSchedule: MutableList = mutableListOf() + val currentWeather: BaseModularWeather + get() = weatherSchedule[0].weather + val currentWeatherDuration: Long + get() = weatherSchedule[0].duration + + fun initWith(initWeather: BaseModularWeather, duration: Long) { + weatherSchedule.add(WeatherSchedule(initWeather, duration)) + } + + var updateAkku = 0L; private set + + fun update(world: GameWorld) { + updateWind(world) + + if (updateAkku >= currentWeatherDuration) { + // TODO add more random weathers + if (weatherSchedule.size == 1) { + val newName = if (currentWeather.identifier == "generic01") "overcast01" else "generic01" + val newDuration = 3600L + weatherSchedule.add(WeatherSchedule(WeatherMixer.weatherDict[newName]!!, newDuration)) + + println("Queueing next weather '$newName' that will last $newDuration seconds") + } + + // subtract akku by old currentWeatherDuration + updateAkku -= weatherSchedule.removeAt(0).duration + } + + updateAkku += world.worldTime.timeDelta + } + + private fun updateWind(world: GameWorld) { + windSpeed.update(world.worldTime.timeDelta / WIND_SPEED_TIME_UNIT) { + currentWeather.getRandomWindSpeed(takeUniformRand(-1f..1f)) + } + windDir.update( world.worldTime.timeDelta / WIND_DIR_TIME_UNIT) { RNG.nextFloat() * 4f } + } } @@ -27,7 +89,7 @@ open class WeatherStateBox( // - removing p4 and beyond: for faster response to the changing weather schedule and make the forecasting less accurate like irl ) { - open fun value() = interpolate(x, p0, p1, p2, p3) + open val value: Float; get() = interpolate(x, p0, p1, p2, p3) open fun valueAt(x: Float) = when (x.floorToInt()) { -2 -> interpolate(x + 2, pM2,pM1, p0, p1) -1 -> interpolate(x + 1, pM1, p0, p1, p2) @@ -38,9 +100,8 @@ open class WeatherStateBox( else -> throw IllegalArgumentException() } - open fun getAndUpdate(xdelta: Float, next: () -> Float): Float { + open fun update(xdelta: Float, next: () -> Float) { synchronized(WeatherMixer.RNG) { - val y = value() x += xdelta while (x >= 1.0) { x -= 1.0f @@ -56,7 +117,6 @@ open class WeatherStateBox( // p4 = p5 // p5 = next() } - return y } } protected fun interpolate(u: Float, p0: Float, p1: Float, p2: Float, p3: Float): Float { @@ -85,7 +145,7 @@ class WeatherDirBox( p2: Float = 0f, p3: Float = 0f, ) : WeatherStateBox(x, pM2, pM1, p0, p1, p2, p3) { - override fun value() = valueAt(x) + override val value; get() = valueAt(x) override fun valueAt(x: Float): Float { var pM2 = pM2