mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-14 20:44:05 +09:00
weather sched progression wip
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"identifier": "generic01",
|
||||||
"skyboxGradColourMap": "generic_skybox.tga",
|
"skyboxGradColourMap": "generic_skybox.tga",
|
||||||
"daylightClut": "clut_daylight.tga",
|
"daylightClut": "clut_daylight.tga",
|
||||||
"classification": "generic",
|
"classification": "generic",
|
||||||
"cloudChance": 250,
|
"cloudChance": 125,
|
||||||
"cloudGamma": [0.48, 2.4],
|
"cloudGamma": [0.48, 2.4],
|
||||||
"cloudGammaVariance": [0.1, 0.1],
|
"cloudGammaVariance": [0.1, 0.1],
|
||||||
"windSpeed": 0.25,
|
"windSpeed": 0.25,
|
||||||
@@ -10,13 +11,13 @@
|
|||||||
"clouds": {
|
"clouds": {
|
||||||
"cumulonimbus": {
|
"cumulonimbus": {
|
||||||
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.2,
|
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.2,
|
||||||
"baseScale": 2.0, "scaleVariance": 0.3333333,
|
"baseScale": 4.0, "scaleVariance": 0.3333333,
|
||||||
"altLow": 80, "altHigh": 120
|
"altLow": 1080, "altHigh": 1120
|
||||||
},
|
},
|
||||||
"cumulus": {
|
"cumulus": {
|
||||||
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0,
|
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0,
|
||||||
"baseScale": 1.0, "scaleVariance": 0.6,
|
"baseScale": 2.0, "scaleVariance": 0.6,
|
||||||
"altLow": 80, "altHigh": 800
|
"altLow": 1080, "altHigh": 1800
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"atmoTurbidity": 3.5
|
"atmoTurbidity": 3.5
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"identifier": "generic02",
|
||||||
"skyboxGradColourMap": "generic_skybox.tga",
|
"skyboxGradColourMap": "generic_skybox.tga",
|
||||||
"daylightClut": "clut_daylight.tga",
|
"daylightClut": "clut_daylight.tga",
|
||||||
"classification": "generic2",
|
"classification": "generic2",
|
||||||
"cloudChance": 1600,
|
"cloudChance": 800,
|
||||||
"cloudGamma": [0.48, 2.4],
|
"cloudGamma": [0.48, 2.4],
|
||||||
"cloudGammaVariance": [0.1, 0.1],
|
"cloudGammaVariance": [0.1, 0.1],
|
||||||
"windSpeed": 0.25,
|
"windSpeed": 0.25,
|
||||||
@@ -10,18 +11,18 @@
|
|||||||
"clouds": {
|
"clouds": {
|
||||||
"cumulonimbus": {
|
"cumulonimbus": {
|
||||||
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.02,
|
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.02,
|
||||||
"baseScale": 2.0, "scaleVariance": 0.3333333,
|
"baseScale": 4.0, "scaleVariance": 0.3333333,
|
||||||
"altLow": 80, "altHigh": 120
|
"altLow": 1080, "altHigh": 1120
|
||||||
},
|
},
|
||||||
"cumulus": {
|
"cumulus": {
|
||||||
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.05,
|
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.05,
|
||||||
"baseScale": 1.0, "scaleVariance": 0.6,
|
"baseScale": 2.0, "scaleVariance": 0.6,
|
||||||
"altLow": 80, "altHigh": 800
|
"altLow": 1080, "altHigh": 1800
|
||||||
},
|
},
|
||||||
"altocumulus": {
|
"altocumulus": {
|
||||||
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0,
|
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0,
|
||||||
"baseScale": 0.5, "scaleVariance": 0.1,
|
"baseScale": 1.0, "scaleVariance": 0.1,
|
||||||
"altLow": 1600, "altHigh": 2400
|
"altLow": 2600, "altHigh": 3400
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"atmoTurbidity": 3.5,
|
"atmoTurbidity": 3.5,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"identifier": "overcast01",
|
||||||
"skyboxGradColourMap": "generic_skybox.tga",
|
"skyboxGradColourMap": "generic_skybox.tga",
|
||||||
"daylightClut": "clut_daylight.tga",
|
"daylightClut": "clut_daylight.tga",
|
||||||
"classification": "overcast",
|
"classification": "overcast",
|
||||||
@@ -10,18 +11,18 @@
|
|||||||
"clouds": {
|
"clouds": {
|
||||||
"cumulus": {
|
"cumulus": {
|
||||||
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.1,
|
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.1,
|
||||||
"baseScale": 0.8, "scaleVariance": 0.6,
|
"baseScale": 1.6, "scaleVariance": 0.6,
|
||||||
"altLow": 80, "altHigh": 800
|
"altLow": 580, "altHigh": 1300
|
||||||
},
|
},
|
||||||
"cumulonimbus": {
|
"cumulonimbus": {
|
||||||
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.4,
|
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.4,
|
||||||
"baseScale": 2.0, "scaleVariance": 0.3333333,
|
"baseScale": 4.0, "scaleVariance": 0.3333333,
|
||||||
"altLow": 90, "altHigh": 120
|
"altLow": 590, "altHigh":60
|
||||||
},
|
},
|
||||||
"nimbostratus": {
|
"nimbostratus": {
|
||||||
"filename": "cloud_wide.png", "tw": 4096, "th": 1024, "probability": 1.0,
|
"filename": "cloud_wide.png", "tw": 4096, "th": 1024, "probability": 1.0,
|
||||||
"baseScale": 4.0, "scaleVariance": 0.1,
|
"baseScale": 8.0, "scaleVariance": 0.1,
|
||||||
"altLow": 100, "altHigh": 100
|
"altLow": 600, "altHigh": 700
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"atmoTurbidity": 9.5
|
"atmoTurbidity": 9.5
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import net.torvald.terrarum.savegame.ByteArray64GrowableOutputStream
|
|||||||
import net.torvald.terrarum.savegame.ByteArray64InputStream
|
import net.torvald.terrarum.savegame.ByteArray64InputStream
|
||||||
import net.torvald.terrarum.savegame.ByteArray64Reader
|
import net.torvald.terrarum.savegame.ByteArray64Reader
|
||||||
import net.torvald.terrarum.utils.*
|
import net.torvald.terrarum.utils.*
|
||||||
|
import net.torvald.terrarum.weather.BaseModularWeather
|
||||||
|
import net.torvald.terrarum.weather.WeatherMixer
|
||||||
import net.torvald.terrarum.weather.WeatherStateBox
|
import net.torvald.terrarum.weather.WeatherStateBox
|
||||||
import org.apache.commons.codec.digest.DigestUtils
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -217,15 +219,25 @@ object Common {
|
|||||||
// WeatherStateBox
|
// WeatherStateBox
|
||||||
jsoner.setSerializer(WeatherStateBox::class.java, object : Json.Serializer<WeatherStateBox> {
|
jsoner.setSerializer(WeatherStateBox::class.java, object : Json.Serializer<WeatherStateBox> {
|
||||||
override fun write(json: Json, obj: WeatherStateBox, knownType: Class<*>?) {
|
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 {
|
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): WeatherStateBox {
|
||||||
return jsonData.asString().split(';').map { it.toFloat() }.let {
|
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 == 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)}." +
|
"${((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) }
|
(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
|
private val gap = 14f
|
||||||
|
|
||||||
@@ -236,10 +240,10 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
try {
|
try {
|
||||||
world?.let {
|
world?.let {
|
||||||
val valRaw = LightmapRenderer.getLight(mouseTileX, mouseTileY)
|
val valRaw = LightmapRenderer.getLight(mouseTileX, mouseTileY)
|
||||||
val rawR = valRaw?.r?.toDouble().toIntAndFrac(1,3)
|
val rawR = valRaw?.r?.toIntAndFrac(1,3)
|
||||||
val rawG = valRaw?.g?.toDouble().toIntAndFrac(1,3)
|
val rawG = valRaw?.g?.toIntAndFrac(1,3)
|
||||||
val rawB = valRaw?.b?.toDouble().toIntAndFrac(1,3)
|
val rawB = valRaw?.b?.toIntAndFrac(1,3)
|
||||||
val rawA = valRaw?.a?.toDouble().toIntAndFrac(1,3)
|
val rawA = valRaw?.a?.toIntAndFrac(1,3)
|
||||||
|
|
||||||
val wallNum = it.getTileFromWall(mouseTileX, mouseTileY)
|
val wallNum = it.getTileFromWall(mouseTileX, mouseTileY)
|
||||||
val tileNum = it.getTileFromTerrain(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$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$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, "$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))
|
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) {
|
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))
|
val drawX = App.scr.width - 170
|
||||||
drawWeatherStateBox(batch, WeatherMixer.weatherbox.windDir, "WindDir", App.scr.width - 170, App.scr.height - 140)
|
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())
|
private val colHairline = Color(0xf22100ff.toInt())
|
||||||
@@ -468,7 +483,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
|
|
||||||
// text
|
// text
|
||||||
batch.color = Color.WHITE
|
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 ""
|
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.
|
* Created by minjaesong on 2016-07-11.
|
||||||
*/
|
*/
|
||||||
data class BaseModularWeather(
|
data class BaseModularWeather(
|
||||||
|
val identifier: String,
|
||||||
val json: JsonValue,
|
val json: JsonValue,
|
||||||
var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA)
|
var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA)
|
||||||
val daylightClut: GdxColorMap,
|
val daylightClut: GdxColorMap,
|
||||||
@@ -30,9 +31,8 @@ data class BaseModularWeather(
|
|||||||
|
|
||||||
val mixFrom: String? = null,
|
val mixFrom: String? = null,
|
||||||
val mixPercentage: Double? = null,
|
val mixPercentage: Double? = null,
|
||||||
|
|
||||||
var forceWindVec: Vector3? = null
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param rnd random number between -1 and +1
|
* @param rnd random number between -1 and +1
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import java.io.File
|
|||||||
import java.io.FileFilter
|
import java.io.FileFilter
|
||||||
import java.lang.Double.doubleToLongBits
|
import java.lang.Double.doubleToLongBits
|
||||||
import java.lang.Math.toDegrees
|
import java.lang.Math.toDegrees
|
||||||
import java.lang.Math.toRadians
|
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
@@ -52,16 +51,32 @@ import kotlin.math.*
|
|||||||
*/
|
*/
|
||||||
internal object WeatherMixer : RNGConsumer {
|
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()
|
override val RNG = HQRNG()
|
||||||
|
|
||||||
var globalLightOverridden = false
|
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
|
val currentWeather: BaseModularWeather
|
||||||
var nextWeather: BaseModularWeather
|
get() = weatherbox.currentWeather
|
||||||
|
// var nextWeather: BaseModularWeather
|
||||||
|
|
||||||
lateinit var mixedWeather: BaseModularWeather
|
private var forceWindVec: Vector3? = null
|
||||||
|
|
||||||
val globalLightNow = Cvec(0)
|
val globalLightNow = Cvec(0)
|
||||||
private val cloudDrawColour = Color()
|
private val cloudDrawColour = Color()
|
||||||
@@ -105,13 +120,11 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
|
|
||||||
override fun loadFromSave(s0: Long, s1: Long) {
|
override fun loadFromSave(s0: Long, s1: Long) {
|
||||||
super.loadFromSave(s0, s1)
|
super.loadFromSave(s0, s1)
|
||||||
currentWeather = weatherList[WEATHER_GENERIC]!![0]
|
|
||||||
internalReset(s0, s1)
|
internalReset(s0, s1)
|
||||||
initClouds()
|
initClouds()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun internalReset() {
|
fun internalReset() {
|
||||||
currentWeather = weatherList[WEATHER_GENERIC]!![0]
|
|
||||||
internalReset(RNG.state0, RNG.state1)
|
internalReset(RNG.state0, RNG.state1)
|
||||||
initClouds()
|
initClouds()
|
||||||
}
|
}
|
||||||
@@ -127,11 +140,14 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
|
|
||||||
clouds.clear()
|
clouds.clear()
|
||||||
cloudsSpawned = 0
|
cloudsSpawned = 0
|
||||||
|
forceWindVec = null
|
||||||
windVector = Vector3(-0.98f, 0f, 0.21f)
|
windVector = Vector3(-0.98f, 0f, 0.21f)
|
||||||
|
|
||||||
oldCamPos.set(WorldCamera.camVector)
|
oldCamPos.set(WorldCamera.camVector)
|
||||||
|
|
||||||
weatherbox = Weatherbox()
|
weatherbox = Weatherbox()
|
||||||
|
weatherbox.initWith(weatherDict["generic01"]!!, 3600L)
|
||||||
|
|
||||||
// TEST FILL WITH RANDOM VALUES
|
// TEST FILL WITH RANDOM VALUES
|
||||||
(0..5).map { takeUniformRand(0f..1f) }.let {
|
(0..5).map { takeUniformRand(0f..1f) }.let {
|
||||||
weatherbox.windDir.pM1 = it[0]
|
weatherbox.windDir.pM1 = it[0]
|
||||||
@@ -152,7 +168,8 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
weatherList = HashMap<String, ArrayList<BaseModularWeather>>()
|
weatherDB = HashMap<String, ArrayList<BaseModularWeather>>()
|
||||||
|
weatherDict = HashMap<String, BaseModularWeather>()
|
||||||
|
|
||||||
|
|
||||||
// read weather descriptions from assets/weather (modular weather)
|
// read weather descriptions from assets/weather (modular weather)
|
||||||
@@ -169,40 +186,16 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
for ((modname, raw) in weatherRawValidList) {
|
for ((modname, raw) in weatherRawValidList) {
|
||||||
val weather = readFromJson(modname, raw)
|
val weather = readFromJson(modname, raw)
|
||||||
|
|
||||||
|
weatherDict[weather.identifier] = weather
|
||||||
|
|
||||||
// if List for the classification does not exist, make one
|
// if List for the classification does not exist, make one
|
||||||
if (!weatherList.containsKey(weather.classification))
|
if (!weatherDB.containsKey(weather.classification))
|
||||||
weatherList.put(weather.classification, ArrayList())
|
weatherDB.put(weather.classification, ArrayList())
|
||||||
|
|
||||||
weatherList[weather.classification]!!.add(weather)
|
weatherDB[weather.classification]!!.add(weather)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weatherDict["titlescreen"] = weatherDB[WEATHER_GENERIC]!![0].copy(windSpeed = 1f)
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +206,8 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
|
|
||||||
// currentWeather = weatherList[WEATHER_GENERIC]!![0] // force set weather
|
// currentWeather = weatherList[WEATHER_GENERIC]!![0] // force set weather
|
||||||
|
|
||||||
updateWind(delta, world)
|
weatherbox.update(world)
|
||||||
|
updateWind()
|
||||||
updateClouds(delta, world)
|
updateClouds(delta, world)
|
||||||
|
|
||||||
|
|
||||||
@@ -230,12 +224,11 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
this[this.lastIndex] = f
|
this[this.lastIndex] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val HALF_PI = 1.5707964f
|
||||||
private val PI = 3.1415927f
|
private val PI = 3.1415927f
|
||||||
private val TWO_PI = 6.2831855f
|
private val TWO_PI = 6.2831855f
|
||||||
private val THREE_PI = 9.424778f
|
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
|
// see: https://stackoverflow.com/questions/2708476/rotation-interpolation/14498790#14498790
|
||||||
private fun getShortestAngle(start: Float, end: Float) =
|
private fun getShortestAngle(start: Float, end: Float) =
|
||||||
@@ -243,22 +236,20 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
if (it > PI) it - TWO_PI else it
|
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) {
|
printdbg(this, "Wind speed = $currentWindSpeed")
|
||||||
currentWeather.getRandomWindSpeed(takeUniformRand(-1f..1f))
|
|
||||||
}
|
|
||||||
val currentWindDir = weatherbox.windDir.getAndUpdate( world.worldTime.timeDelta / WIND_DIR_TIME_UNIT) { RNG.nextFloat() * 4f } * Math.PI * 0.5
|
|
||||||
|
|
||||||
|
if (forceWindVec != null) {
|
||||||
if (currentWeather.forceWindVec != null) {
|
windVector.set(forceWindVec)
|
||||||
windVector.set(currentWeather.forceWindVec)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
windVector.set(
|
windVector.set(
|
||||||
(cos(currentWindDir) * currentWindSpeed).toFloat(),
|
(cos(currentWindDir) * currentWindSpeed),
|
||||||
0f,
|
0f,
|
||||||
(sin(currentWindDir) * currentWindSpeed).toFloat()
|
(sin(currentWindDir) * currentWindSpeed)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,6 +271,7 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
val camvec2 = camvec.cpy()
|
val camvec2 = camvec.cpy()
|
||||||
val testCamDelta = camvec.cpy().sub(oldCamPos)
|
val testCamDelta = camvec.cpy().sub(oldCamPos)
|
||||||
|
|
||||||
|
// adjust camDelta to accomodate ROUNDWORLD
|
||||||
if (testCamDelta.x.absoluteValue > world.width * TILE_SIZEF / 2f) {
|
if (testCamDelta.x.absoluteValue > world.width * TILE_SIZEF / 2f) {
|
||||||
if (testCamDelta.x >= 0)
|
if (testCamDelta.x >= 0)
|
||||||
camvec2.x -= world.width * TILE_SIZEF
|
camvec2.x -= world.width * TILE_SIZEF
|
||||||
@@ -291,7 +283,7 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
|
|
||||||
camDelta.set(testCamDelta)
|
camDelta.set(testCamDelta)
|
||||||
|
|
||||||
|
// try to spawn an cloud
|
||||||
val cloudChanceEveryMin = 60f / (currentWeather.cloudChance * currentWeather.windSpeed) // if chance = 0, the result will be +inf
|
val cloudChanceEveryMin = 60f / (currentWeather.cloudChance * currentWeather.windSpeed) // if chance = 0, the result will be +inf
|
||||||
|
|
||||||
while (cloudUpdateAkku >= cloudChanceEveryMin) {
|
while (cloudUpdateAkku >= cloudChanceEveryMin) {
|
||||||
@@ -302,12 +294,13 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
|
|
||||||
var immDespawnCount = 0
|
var immDespawnCount = 0
|
||||||
val immDespawnCauses = ArrayList<String>()
|
val immDespawnCauses = ArrayList<String>()
|
||||||
|
printdbg(this, "Wind vector = $windVector")
|
||||||
|
// move the clouds
|
||||||
clouds.forEach {
|
clouds.forEach {
|
||||||
// do parallax scrolling
|
// do parallax scrolling
|
||||||
it.posX += camDelta.x * cloudParallaxMultX
|
it.posX += camDelta.x * cloudParallaxMultX
|
||||||
it.posY += camDelta.y * cloudParallaxMultY
|
it.posY += camDelta.y * cloudParallaxMultY
|
||||||
|
|
||||||
|
|
||||||
it.update(windVector)
|
it.update(windVector)
|
||||||
|
|
||||||
if (DEBUG_CAUSE_OF_DESPAWN && it.life == 0) {
|
if (DEBUG_CAUSE_OF_DESPAWN && it.life == 0) {
|
||||||
@@ -498,8 +491,8 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun titleScreenInitWeather() {
|
internal fun titleScreenInitWeather() {
|
||||||
currentWeather = weatherList["titlescreen"]!![0]
|
weatherbox.initWith(weatherDict["titlescreen"]!!, Long.MAX_VALUE)
|
||||||
currentWeather.forceWindVec = Vector3(-0.98f, 0f, -0.21f)
|
forceWindVec = Vector3(-0.98f, 0f, -0.21f)
|
||||||
initClouds()
|
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) =
|
fun getRandomWeather(classification: String) =
|
||||||
getWeatherList(classification)[RNG.nextInt(getWeatherList(classification).size)]
|
getWeatherList(classification)[RNG.nextInt(getWeatherList(classification).size)]
|
||||||
|
|
||||||
@@ -757,7 +750,7 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}"))
|
val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}"))
|
||||||
val daylight = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}"))
|
val daylight = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}"))
|
||||||
|
|
||||||
|
val identifier = JSON.getString("identifier")
|
||||||
val classification = JSON.getString("classification")
|
val classification = JSON.getString("classification")
|
||||||
|
|
||||||
|
|
||||||
@@ -797,6 +790,7 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
|
|
||||||
|
|
||||||
return BaseModularWeather(
|
return BaseModularWeather(
|
||||||
|
identifier = identifier,
|
||||||
json = JSON,
|
json = JSON,
|
||||||
skyboxGradColourMap = skybox,
|
skyboxGradColourMap = skybox,
|
||||||
daylightClut = daylight,
|
daylightClut = daylight,
|
||||||
@@ -811,7 +805,7 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
weatherList.values.forEach { list ->
|
weatherDB.values.forEach { list ->
|
||||||
list.forEach { weather ->
|
list.forEach { weather ->
|
||||||
weather.clouds.forEach { it.spriteSheet.dispose() }
|
weather.clouds.forEach { it.spriteSheet.dispose() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,77 @@
|
|||||||
package net.torvald.terrarum.weather
|
package net.torvald.terrarum.weather
|
||||||
|
|
||||||
|
import com.jme3.math.FastMath
|
||||||
import net.torvald.terrarum.floorToInt
|
import net.torvald.terrarum.floorToInt
|
||||||
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
import net.torvald.terrarum.gameworld.fmod
|
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 {
|
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 windDir = WeatherDirBox() // 0 .. 1.0
|
||||||
val windSpeed = WeatherStateBox() // 0 .. arbitrarily large number
|
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
|
// - 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()) {
|
open fun valueAt(x: Float) = when (x.floorToInt()) {
|
||||||
-2 -> interpolate(x + 2, pM2,pM1, p0, p1)
|
-2 -> interpolate(x + 2, pM2,pM1, p0, p1)
|
||||||
-1 -> interpolate(x + 1, pM1, p0, p1, p2)
|
-1 -> interpolate(x + 1, pM1, p0, p1, p2)
|
||||||
@@ -38,9 +100,8 @@ open class WeatherStateBox(
|
|||||||
else -> throw IllegalArgumentException()
|
else -> throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getAndUpdate(xdelta: Float, next: () -> Float): Float {
|
open fun update(xdelta: Float, next: () -> Float) {
|
||||||
synchronized(WeatherMixer.RNG) {
|
synchronized(WeatherMixer.RNG) {
|
||||||
val y = value()
|
|
||||||
x += xdelta
|
x += xdelta
|
||||||
while (x >= 1.0) {
|
while (x >= 1.0) {
|
||||||
x -= 1.0f
|
x -= 1.0f
|
||||||
@@ -56,7 +117,6 @@ open class WeatherStateBox(
|
|||||||
// p4 = p5
|
// p4 = p5
|
||||||
// p5 = next()
|
// p5 = next()
|
||||||
}
|
}
|
||||||
return y
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected fun interpolate(u: Float, p0: Float, p1: Float, p2: Float, p3: Float): Float {
|
protected fun interpolate(u: Float, p0: Float, p1: Float, p2: Float, p3: Float): Float {
|
||||||
@@ -85,7 +145,7 @@ class WeatherDirBox(
|
|||||||
p2: Float = 0f,
|
p2: Float = 0f,
|
||||||
p3: Float = 0f,
|
p3: Float = 0f,
|
||||||
) : WeatherStateBox(x, pM2, pM1, p0, p1, p2, p3) {
|
) : 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 {
|
override fun valueAt(x: Float): Float {
|
||||||
var pM2 = pM2
|
var pM2 = pM2
|
||||||
|
|||||||
Reference in New Issue
Block a user