mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-11 14:21:52 +09:00
weather sched progression wip
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<WeatherStateBox> {
|
||||
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<BaseModularWeather> {
|
||||
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()]!!
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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<String, ArrayList<BaseModularWeather>>
|
||||
val weatherDB: HashMap<String, ArrayList<BaseModularWeather>> // search by classification
|
||||
val weatherDict: HashMap<String, BaseModularWeather> // 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<String, ArrayList<BaseModularWeather>>()
|
||||
weatherDB = HashMap<String, ArrayList<BaseModularWeather>>()
|
||||
weatherDict = HashMap<String, BaseModularWeather>()
|
||||
|
||||
|
||||
// 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<String>()
|
||||
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() }
|
||||
}
|
||||
|
||||
@@ -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<Float>) =
|
||||
FastMath.interpolateLinear(RNG.nextFloat(), range.start, range.endInclusive)
|
||||
private fun takeTriangularRand(range: ClosedFloatingPointRange<Float>) =
|
||||
FastMath.interpolateLinear((RNG.nextFloat() + RNG.nextFloat()) / 2f, range.start, range.endInclusive)
|
||||
private fun takeGaussianRand(range: ClosedFloatingPointRange<Float>) =
|
||||
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<WeatherSchedule> = mutableListOf<WeatherSchedule>()
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user