package net.torvald.terrarum.weather import com.jme3.math.FastMath import net.torvald.JsonFetcher import net.torvald.colourutil.* import net.torvald.random.HQRNG import net.torvald.terrarum.* import net.torvald.terrarum.gameactors.ParticleTestRain import net.torvald.terrarum.gamecontroller.Key import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gameworld.WorldTime import org.newdawn.slick.Color import org.newdawn.slick.GameContainer import org.newdawn.slick.Graphics import org.newdawn.slick.Image import org.newdawn.slick.fills.GradientFill import org.newdawn.slick.geom.Rectangle import java.io.File import java.util.* /** * Current, next are there for cross-fading two weathers * * * Building a CLUT: * Brightest:Darkest must be "around" 10:1 * Is RGBA-formatted (32-bit) * * Created by minjaesong on 16-07-11. */ object WeatherMixer { var weatherList: HashMap> var currentWeather: BaseModularWeather var nextWeather: BaseModularWeather lateinit var mixedWeather: BaseModularWeather val globalLightNow = Color(0) private val world = Terrarum.ingame!!.world // Weather indices const val WEATHER_GENERIC = "generic" const val WEATHER_GENERIC_RAIN = "genericrain" // TODO add weather classification indices manually init { weatherList = HashMap>() // read weather descriptions from assets/weather (modular weather) val weatherRawValidList = ArrayList() val weatherRaws = ModMgr.getFiles("basegame", "weathers") weatherRaws.forEach { if (!it.isDirectory && it.name.endsWith(".json")) weatherRawValidList.add(it) } // --> read from directory and store file that looks like RAW for (raw in weatherRawValidList) { val weather = readFromJson(raw) // if List for the classification does not exist, make one if (!weatherList.containsKey(weather.classification)) weatherList.put(weather.classification, ArrayList()) weatherList[weather.classification]!!.add(weather) } // initialise currentWeather = weatherList[WEATHER_GENERIC]!![0] nextWeather = getRandomWeather(WEATHER_GENERIC) } fun update(gc: GameContainer, delta: Int) { currentWeather = weatherList[WEATHER_GENERIC]!![0] if (Terrarum.ingame!!.player != null) { // test rain toggled by F2 if (KeyToggler.isOn(Key.F2)) { val playerPos = Terrarum.ingame!!.player!!.centrePosPoint kotlin.repeat(4) { // 4 seems good val rainParticle = ParticleTestRain( playerPos.x + HQRNG().nextInt(Terrarum.WIDTH) - Terrarum.HALFW, playerPos.y - Terrarum.HALFH ) Terrarum.ingame!!.addParticle(rainParticle) } globalLightNow.set(getGlobalLightOfTime(world.time.todaySeconds).darker(0.3f)) } } } private fun Color.set(other: Color) { this.r = other.r this.g = other.g this.b = other.b this.a = other.a } fun render(g: Graphics) { // we will not care for nextSkybox for now val timeNow = Terrarum.ingame!!.world.time.todaySeconds val skyboxColourMap = currentWeather.skyboxGradColourMap val lightColourMap = currentWeather.globalLightColourMap // calculate global light val gradCol = getGradientColour(lightColourMap, 0, timeNow) globalLightNow.r = gradCol.r globalLightNow.g = gradCol.g globalLightNow.b = gradCol.b // draw skybox to provided (should be main) graphics instance val skyColourFill = GradientFill( 0f, 0f, getGradientColour(skyboxColourMap, 0, timeNow), 0f, Terrarum.HEIGHT.toFloat(),// / Terrarum.ingame!!.screenZoom, getGradientColour(skyboxColourMap, 1, timeNow) ) blendNormal() g.fill(Rectangle( 0f, 0f, Terrarum.WIDTH.toFloat(),// / Terrarum.ingame!!.screenZoom, Terrarum.HEIGHT.toFloat()// / Terrarum.ingame!!.screenZoom ), skyColourFill) } fun Float.clampOne() = if (this > 1) 1f else this private operator fun Color.times(other: Color) = Color(this.r * other.r, this.g * other.g, this.b * other.b, 1f) /** * Get a GL of specific time */ fun getGlobalLightOfTime(timeInSec: Int): Color = getGradientColour(currentWeather.globalLightColourMap, 0, timeInSec) fun getGradientColour(image: Image, row: Int, timeInSec: Int): Color { val dataPointDistance = WorldTime.DAY_LENGTH / image.width val phaseThis: Int = timeInSec / dataPointDistance // x-coord in gradmap val phaseNext: Int = (phaseThis + 1) % image.width val colourThis = image.getPixel(phaseThis, row).toColor() val colourNext = image.getPixel(phaseNext, row).toColor() // interpolate R, G and B val scale = (timeInSec % dataPointDistance).toFloat() / dataPointDistance // [0.0, 1.0] val newCol = CIELabUtil.getGradient(scale, colourThis, colourNext) /* // very nice monitor code // 65 -> 66 | 300 | 19623 | RGB8(255, 0, 255) -[41%]-> RGB8(193, 97, 23) | * `230`40`160` // ^ step |width| time | colour from scale colour to | output if (dataPointDistance == 300) println("$phaseThis -> $phaseNext | $dataPointDistance | $timeInSec" + " | ${colourThis.toStringRGB()} -[${scale.times(100).toInt()}%]-> ${colourNext.toStringRGB()}" + " | * `$r`$g`$b`")*/ return newCol } fun Color.toStringRGB() = "RGB8(${this.red}, ${this.green}, ${this.blue})" fun getWeatherList(classification: String) = weatherList[classification]!! fun getRandomWeather(classification: String) = getWeatherList(classification)[HQRNG().nextInt(getWeatherList(classification).size)] fun readFromJson(file: File): BaseModularWeather = readFromJson(file.path) fun readFromJson(path: String): BaseModularWeather { /* JSON structure: { "globalLight": "colourmap/sky_colour.tga", // integer for static, string (path to image) for dynamic "skyboxGradColourMap": "colourmap/sky_colour.tga", // integer for static, string (path to image) for dynamic "extraImages": [ // if any, it will be like: sun01.tga, clouds01.tga, clouds02.tga, auroraBlueViolet.tga ] } */ val pathToImage = "weathers" val JSON = JsonFetcher(path) val globalLightInJson = JSON.get("globalLight").asJsonPrimitive val skyboxInJson = JSON.get("skyboxGradColourMap").asJsonPrimitive val extraImagesPath = JSON.getAsJsonArray("extraImages") val globalLight: Image val skybox: Image val extraImages = ArrayList() val classification = JSON.get("classification").asJsonPrimitive.asString var mixFrom: String? try { mixFrom = JSON.get("mixFrom").asJsonPrimitive.asString } catch (e: NullPointerException) { mixFrom = null } var mixPercentage: Double? try { mixPercentage = JSON.get("mixPercentage").asJsonPrimitive.asDouble } catch (e: NullPointerException) { mixPercentage = null } // parse globalLight if (globalLightInJson.isString) globalLight = Image(ModMgr.getPath("basegame", "$pathToImage/${globalLightInJson.asString}")) else if (globalLightInJson.isNumber) { // make 1x1 image with specified colour globalLight = Image(1, 1) globalLight.graphics.color = Color(globalLightInJson.asNumber.toInt()) globalLight.graphics.fillRect(0f, 0f, 1f, 1f) } else throw IllegalStateException("In weather descriptor $path -- globalLight seems malformed.") // parse skyboxGradColourMap if (skyboxInJson.isString) skybox = Image(ModMgr.getPath("basegame", "$pathToImage/${skyboxInJson.asString}")) else if (globalLightInJson.isNumber) { // make 1x2 image with specified colour skybox = Image(1, 2) skybox.graphics.color = Color(skyboxInJson.asNumber.toInt()) skybox.graphics.fillRect(0f, 0f, 1f, 2f) } else throw IllegalStateException("In weather descriptor $path -- skyboxGradColourMap seems malformed.") // get extra images for (i in extraImagesPath) extraImages.add(Image(ModMgr.getPath("basegame", "$pathToImage/${i.asString}"))) // get mix from return BaseModularWeather(globalLight, skybox, classification, extraImages) } } fun IntArray.toColor() = Color(this[0], this[1], this[2], this[3])