mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-13 20:14:05 +09:00
com.torvald → net.torvald
Former-commit-id: 375604da8a20a6ba7cd0a8d05a44add02b2d04f4 Former-commit-id: 287287c5920b07618174d7a7573f049d350ded66
This commit is contained in:
562
src/net/torvald/terrarum/mapdrawer/LightmapRenderer.kt
Normal file
562
src/net/torvald/terrarum/mapdrawer/LightmapRenderer.kt
Normal file
@@ -0,0 +1,562 @@
|
||||
package net.torvald.terrarum.mapdrawer
|
||||
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||
import net.torvald.terrarum.gameactors.Luminous
|
||||
import net.torvald.terrarum.gamemap.WorldTime
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
import org.newdawn.slick.Color
|
||||
import org.newdawn.slick.Graphics
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
|
||||
object LightmapRenderer {
|
||||
/**
|
||||
* 8-Bit RGB values
|
||||
*/
|
||||
@Volatile private var lightmap: Array<IntArray> = Array(Terrarum.game.map.height) { IntArray(Terrarum.game.map.width) }
|
||||
private var lightMapInitialised = false
|
||||
|
||||
private val AIR = 0
|
||||
private val SUNSTONE = 41 // TODO add sunstone: emits same light as Map.GL. Goes dark at night
|
||||
|
||||
|
||||
private val OFFSET_R = 2
|
||||
private val OFFSET_G = 1
|
||||
private val OFFSET_B = 0
|
||||
|
||||
private const val TSIZE = MapDrawer.TILE_SIZE
|
||||
|
||||
// color model related constants
|
||||
const val MUL = 1024 // modify this to 1024 to implement 30-bit RGB
|
||||
const val MUL_2 = MUL * MUL
|
||||
const val CHANNEL_MAX = MUL - 1
|
||||
const val CHANNEL_MAX_FLOAT = CHANNEL_MAX.toFloat()
|
||||
const val COLOUR_RANGE_SIZE = MUL * MUL_2
|
||||
|
||||
fun getLight(x: Int, y: Int): Int? =
|
||||
if (x !in 0..Terrarum.game.map.width - 1 || y !in 0..Terrarum.game.map.height - 1)
|
||||
// if out of range then
|
||||
null
|
||||
else
|
||||
lightmap[y][x]
|
||||
|
||||
fun setLight(x: Int, y: Int, colour: Int) {
|
||||
lightmap[y][x] = colour
|
||||
}
|
||||
|
||||
fun renderLightMap() {
|
||||
val for_x_start = MapCamera.cameraX / TSIZE - 1 // fix for premature lightmap rendering
|
||||
val for_y_start = MapCamera.cameraY / TSIZE - 1 // on topmost/leftmost side
|
||||
|
||||
val for_x_end = for_x_start + MapCamera.getRenderWidth() / TSIZE + 3
|
||||
val for_y_end = for_y_start + MapCamera.getRenderHeight() / TSIZE + 2 // same fix as above
|
||||
|
||||
val overscan_open: Int = (256f / (TilePropCodex.getProp(TileNameCode.AIR).opacity and 0xFF).toFloat()).ceil()
|
||||
val overscan_opaque: Int = (256f / (TilePropCodex.getProp(TileNameCode.STONE).opacity and 0xFF).toFloat()).ceil()
|
||||
|
||||
/**
|
||||
* * true: overscanning is limited to 8 tiles in width (overscan_opaque)
|
||||
* * false: overscanning will fully applied to 32 tiles in width (overscan_open)
|
||||
*/
|
||||
val noop_mask = BitSet(2 * (for_x_end - for_x_start + 1) +
|
||||
2 * (for_y_end - for_y_start - 1))
|
||||
|
||||
/**
|
||||
* Updating order:
|
||||
* +--------+ +--+-----+ +-----+--+ +--------+ -
|
||||
* |↘ | | | 3| |3 | | | ↙| ↕︎ overscan_open / overscan_opaque
|
||||
* | +-----+ | | 2 | | 2 | | +-----+ | - depending on the noop_mask
|
||||
* | |1 | → | |1 | → | 1| | → | 1| |
|
||||
* | | 2 | | +-----+ +-----+ | | 2 | |
|
||||
* | | 3| |↗ | | ↖| |3 | |
|
||||
* +--+-----+ +--------+ +--------+ +-----+--+
|
||||
* round: 1 2 3 4
|
||||
* for all staticLightMap[y][x]
|
||||
*/
|
||||
|
||||
purgePartOfLightmap(for_x_start - overscan_open, for_y_start - overscan_open, for_x_end + overscan_open, for_y_end + overscan_open)
|
||||
|
||||
try {
|
||||
// Round 1
|
||||
for (y in for_y_start - overscan_open..for_y_end) {
|
||||
for (x in for_x_start - overscan_open..for_x_end) {
|
||||
setLight(x, y, calculate(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
// Round 2
|
||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||
for (x in for_x_start - overscan_open..for_x_end) {
|
||||
setLight(x, y, calculate(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
// Round 3
|
||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||
for (x in for_x_end + overscan_open downTo for_x_start) {
|
||||
setLight(x, y, calculate(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
// Round 4
|
||||
for (y in for_y_start - overscan_open..for_y_end) {
|
||||
for (x in for_x_end + overscan_open downTo for_x_start) {
|
||||
setLight(x, y, calculate(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun calculate(x: Int, y: Int): Int = calculate(x, y, false)
|
||||
|
||||
private fun calculate(x: Int, y: Int, doNotCalculateAmbient: Boolean): Int {
|
||||
var lightLevelThis: Int = 0
|
||||
val thisTerrain = Terrarum.game.map.getTileFromTerrain(x, y)
|
||||
val thisWall = Terrarum.game.map.getTileFromWall(x, y)
|
||||
val thisTileLuminosity = TilePropCodex.getProp(thisTerrain).luminosity
|
||||
val thisTileOpacity = TilePropCodex.getProp(thisTerrain).opacity
|
||||
val sunLight = Terrarum.game.map.globalLight
|
||||
|
||||
// MIX TILE
|
||||
// open air
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightLevelThis = sunLight
|
||||
}
|
||||
// luminous tile on top of air
|
||||
else if (thisWall == AIR && thisTileLuminosity.toInt() > 0) {
|
||||
val darkenSunlight = darkenColoured(sunLight, thisTileOpacity)
|
||||
lightLevelThis = maximiseRGB(darkenSunlight, thisTileLuminosity) // maximise to not exceed 1.0 with normal (<= 1.0) light
|
||||
}
|
||||
// opaque wall and luminous tile
|
||||
else if (thisWall != AIR && thisTileLuminosity.toInt() > 0) {
|
||||
lightLevelThis = thisTileLuminosity
|
||||
}
|
||||
// END MIX TILE
|
||||
|
||||
// mix luminous actor
|
||||
for (actor in Terrarum.game.actorContainer) {
|
||||
if (actor is Luminous && actor is ActorWithBody) {
|
||||
val tileX = Math.round(actor.hitbox!!.pointedX / TSIZE)
|
||||
val tileY = Math.round(actor.hitbox!!.pointedY / TSIZE) - 1
|
||||
val actorLuminosity = actor.luminosity
|
||||
if (x == tileX && y == tileY) {
|
||||
lightLevelThis = maximiseRGB(lightLevelThis, actorLuminosity) // maximise to not exceed 1.0 with normal (<= 1.0) light
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!doNotCalculateAmbient) {
|
||||
// calculate ambient
|
||||
var ambient: Int = 0
|
||||
var nearby: Int = 0
|
||||
for (yoff in -1..1) {
|
||||
for (xoff in -1..1) {
|
||||
/**
|
||||
* filter for 'v's as:
|
||||
* +-+-+-+
|
||||
* |a|v|a|
|
||||
* +-+-+-+
|
||||
* |v| |v|
|
||||
* +-+-+-+
|
||||
* |a|v|a|
|
||||
* +-+-+-+
|
||||
*/
|
||||
if (xoff != yoff && -xoff != yoff) {
|
||||
// 'v' tiles
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = getLight(x + xoff, y + yoff) ?: 0
|
||||
}
|
||||
}
|
||||
else if (xoff != 0 && yoff != 0) {
|
||||
// 'a' tiles
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = darkenUniformInt(getLight(x + xoff, y + yoff) ?: 0, 12) //2 for 40step
|
||||
// mix some to have more 'spreading'
|
||||
// so that light spreads in a shape of an octagon instead of a diamond
|
||||
}
|
||||
}
|
||||
else {
|
||||
nearby = 0 // exclude 'me' tile
|
||||
}
|
||||
|
||||
ambient = maximiseRGB(ambient, nearby) // keep base value as brightest nearby
|
||||
}
|
||||
}
|
||||
|
||||
ambient = darkenColoured(ambient,
|
||||
thisTileOpacity) // get real ambient by appling opacity value
|
||||
|
||||
// mix and return lightlevel and ambient
|
||||
return maximiseRGB(lightLevelThis, ambient)
|
||||
}
|
||||
else {
|
||||
return lightLevelThis
|
||||
}
|
||||
}
|
||||
|
||||
fun draw(g: Graphics) {
|
||||
val for_x_start = MapCamera.cameraX / TSIZE - 1 // fix for premature lightmap rendering
|
||||
val for_y_start = MapCamera.cameraY / TSIZE - 1 // on topmost/leftmost side
|
||||
|
||||
val for_x_end = for_x_start + MapCamera.getRenderWidth() / TSIZE + 3
|
||||
val for_y_end = for_y_start + MapCamera.getRenderHeight() / TSIZE + 2 // same fix as above
|
||||
|
||||
|
||||
// draw
|
||||
try {
|
||||
// loop for "scanlines"
|
||||
for (y in for_y_start..for_y_end) {
|
||||
// loop x
|
||||
var x = for_x_start
|
||||
while (x < for_x_end) {
|
||||
// smoothing enabled
|
||||
if (Terrarum.game.screenZoom >= 1
|
||||
&& Terrarum.gameConfig.getAsBoolean("smoothlighting") ?: false) {
|
||||
|
||||
val thisLightLevel = getLight(x, y) ?: 0
|
||||
|
||||
if (x < for_x_end && thisLightLevel == 0
|
||||
&& getLight(x, y - 1) == 0) {
|
||||
try {
|
||||
// coalesce zero intensity blocks to one
|
||||
var zeroLevelCounter = 1
|
||||
while (getLight(x + zeroLevelCounter, y) == 0) {
|
||||
zeroLevelCounter += 1
|
||||
|
||||
if (x + zeroLevelCounter >= for_x_end) break
|
||||
}
|
||||
|
||||
g.color = Color(0)
|
||||
g.fillRect(
|
||||
(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).round().toFloat(),
|
||||
(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).round().toFloat(),
|
||||
((TSIZE * Terrarum.game.screenZoom).ceil() * zeroLevelCounter).toFloat(),
|
||||
(TSIZE * Terrarum.game.screenZoom).ceil().toFloat()
|
||||
)
|
||||
|
||||
x += zeroLevelCounter - 1
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
/** a
|
||||
* +-+-+
|
||||
* |i|j|
|
||||
* b +-+-+ c
|
||||
* |k|l|
|
||||
* +-+-+
|
||||
* d
|
||||
*/
|
||||
val a = maximiseRGB(
|
||||
thisLightLevel,
|
||||
getLight(x, y - 1) ?: thisLightLevel
|
||||
)
|
||||
val d = maximiseRGB(
|
||||
thisLightLevel,
|
||||
getLight(x, y + 1) ?: thisLightLevel
|
||||
)
|
||||
val b = maximiseRGB(
|
||||
thisLightLevel,
|
||||
getLight(x - 1, y) ?: thisLightLevel
|
||||
)
|
||||
val c = maximiseRGB(
|
||||
thisLightLevel,
|
||||
getLight(x + 1, y) ?: thisLightLevel
|
||||
)
|
||||
val colourMapItoL = IntArray(4)
|
||||
colourMapItoL[0] = colourLinearMix(a, b)
|
||||
colourMapItoL[1] = colourLinearMix(a, c)
|
||||
colourMapItoL[2] = colourLinearMix(b, d)
|
||||
colourMapItoL[3] = colourLinearMix(c, d)
|
||||
|
||||
for (iy in 0..1) {
|
||||
for (ix in 0..1) {
|
||||
g.color = Color(colourMapItoL[iy * 2 + ix].rgb30ClampTo24())
|
||||
|
||||
g.fillRect(
|
||||
(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).round()
|
||||
+ ix * TSIZE / 2 * Terrarum.game.screenZoom,
|
||||
(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).round()
|
||||
+ iy * TSIZE / 2 * Terrarum.game.screenZoom,
|
||||
(TSIZE * Terrarum.game.screenZoom / 2).ceil().toFloat(),
|
||||
(TSIZE * Terrarum.game.screenZoom / 2).ceil().toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// smoothing disabled
|
||||
else {
|
||||
try {
|
||||
val thisLightLevel = getLight(x, y)
|
||||
|
||||
// coalesce identical intensity blocks to one
|
||||
var sameLevelCounter = 1
|
||||
while (getLight(x + sameLevelCounter, y) == thisLightLevel) {
|
||||
sameLevelCounter += 1
|
||||
|
||||
if (x + sameLevelCounter >= for_x_end) break
|
||||
}
|
||||
|
||||
g.color = Color((getLight(x, y) ?: 0).rgb30ClampTo24())
|
||||
g.fillRect(
|
||||
(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).round().toFloat(),
|
||||
(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).round().toFloat(),
|
||||
((TSIZE * Terrarum.game.screenZoom).ceil() * sameLevelCounter).toFloat(),
|
||||
(TSIZE * Terrarum.game.screenZoom).ceil().toFloat()
|
||||
)
|
||||
|
||||
x += sameLevelCounter - 1
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
x++
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract each channel's RGB value.
|
||||
*
|
||||
* It works like:
|
||||
*
|
||||
* f(data, darken) = RGB(data.r - darken.r, data.g - darken.g, data.b - darken.b)
|
||||
*
|
||||
* @param data Raw channel value (0-39) per channel
|
||||
* @param darken (0-39) per channel
|
||||
* @return darkened data (0-39) per channel
|
||||
*/
|
||||
fun darkenColoured(data: Int, darken: Int): Int {
|
||||
if (darken.toInt() < 0 || darken.toInt() >= COLOUR_RANGE_SIZE)
|
||||
throw IllegalArgumentException("darken: out of range ($darken)")
|
||||
|
||||
var r = data.r() * (1f - darken.r() * 6) // 6: Arbitrary value
|
||||
var g = data.g() * (1f - darken.g() * 6) // TODO gamma correction?
|
||||
var b = data.b() * (1f - darken.b() * 6)
|
||||
|
||||
return constructRGBFromFloat(r.clampZero(), g.clampZero(), b.clampZero())
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken each channel by 'darken' argument
|
||||
*
|
||||
* It works like:
|
||||
*
|
||||
* f(data, darken) = RGB(data.r - darken, data.g - darken, data.b - darken)
|
||||
* @param data (0-39) per channel
|
||||
* @param darken (0-39)
|
||||
* @return
|
||||
*/
|
||||
fun darkenUniformInt(data: Int, darken: Int): Int {
|
||||
if (darken < 0 || darken > CHANNEL_MAX)
|
||||
throw IllegalArgumentException("darken: out of range ($darken)")
|
||||
|
||||
val darkenColoured = constructRGBFromInt(darken, darken, darken)
|
||||
return darkenColoured(data, darkenColoured)
|
||||
}
|
||||
|
||||
/** Get each channel from two RGB values, return new RGB that has max value of each channel
|
||||
* @param rgb
|
||||
* *
|
||||
* @param rgb2
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun maximiseRGB(rgb: Int, rgb2: Int): Int {
|
||||
val r1 = rgb.rawR()
|
||||
val r2 = rgb2.rawR()
|
||||
val newR = if (r1 > r2) r1 else r2
|
||||
val g1 = rgb.rawG()
|
||||
val g2 = rgb2.rawG()
|
||||
val newG = if (g1 > g2) g1 else g2
|
||||
val b1 = rgb.rawB()
|
||||
val b2 = rgb2.rawB()
|
||||
val newB = if (b1 > b2) b1 else b2
|
||||
|
||||
return constructRGBFromInt(newR, newG, newB)
|
||||
}
|
||||
|
||||
private fun screenBlend(rgb: Int, rgb2: Int): Int {
|
||||
val r1 = rgb.r()
|
||||
val r2 = rgb2.r()
|
||||
val newR = 1 - (1 - r1) * (1 - r2)
|
||||
val g1 = rgb.g()
|
||||
val g2 = rgb2.g()
|
||||
val newG = 1 - (1 - g1) * (1 - g2)
|
||||
val b1 = rgb.b()
|
||||
val b2 = rgb2.b()
|
||||
val newB = 1 - (1 - b1) * (1 - b2)
|
||||
|
||||
return constructRGBFromFloat(newR, newG, newB)
|
||||
}
|
||||
|
||||
fun Int.rawR() = this / MUL_2
|
||||
fun Int.rawG() = this % MUL_2 / MUL
|
||||
fun Int.rawB() = this % MUL
|
||||
|
||||
fun Int.r(): Float = this.rawR() / CHANNEL_MAX_FLOAT
|
||||
fun Int.g(): Float = this.rawG() / CHANNEL_MAX_FLOAT
|
||||
fun Int.b(): Float = this.rawB() / CHANNEL_MAX_FLOAT
|
||||
|
||||
/**
|
||||
|
||||
* @param RGB
|
||||
* *
|
||||
* @param offset 2 = R, 1 = G, 0 = B
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
fun getRaw(RGB: Int, offset: Int): Int {
|
||||
if (offset == OFFSET_R) return RGB.rawR()
|
||||
else if (offset == OFFSET_G) return RGB.rawG()
|
||||
else if (offset == OFFSET_B) return RGB.rawB()
|
||||
else throw IllegalArgumentException("Channel offset out of range")
|
||||
}
|
||||
|
||||
private fun addRaw(rgb1: Int, rgb2: Int): Int {
|
||||
val newR = (rgb1.rawR() + rgb2.rawR()).clampChannel()
|
||||
val newG = (rgb1.rawG() + rgb2.rawG()).clampChannel()
|
||||
val newB = (rgb1.rawB() + rgb2.rawB()).clampChannel()
|
||||
|
||||
return constructRGBFromInt(newR, newG, newB)
|
||||
}
|
||||
|
||||
fun constructRGBFromInt(r: Int, g: Int, b: Int): Int {
|
||||
if (r !in 0..CHANNEL_MAX) throw IllegalArgumentException("Red: out of range ($r)")
|
||||
if (g !in 0..CHANNEL_MAX) throw IllegalArgumentException("Green: out of range ($g)")
|
||||
if (b !in 0..CHANNEL_MAX) throw IllegalArgumentException("Blue: out of range ($b)")
|
||||
return (r * MUL_2 + g * MUL + b)
|
||||
}
|
||||
|
||||
fun constructRGBFromFloat(r: Float, g: Float, b: Float): Int {
|
||||
if (r < 0 || r > 1.0f) throw IllegalArgumentException("Red: out of range ($r)")
|
||||
if (g < 0 || g > 1.0f) throw IllegalArgumentException("Green: out of range ($g)")
|
||||
if (b < 0 || b > 1.0f) throw IllegalArgumentException("Blue: out of range ($b)")
|
||||
|
||||
val intR = (r * CHANNEL_MAX).floor()
|
||||
val intG = (g * CHANNEL_MAX).floor()
|
||||
val intB = (b * CHANNEL_MAX).floor()
|
||||
|
||||
return constructRGBFromInt(intR, intG, intB)
|
||||
}
|
||||
|
||||
private fun colourLinearMix(colA: Int, colB: Int): Int {
|
||||
val r = (colA.rawR() + colB.rawR()) ushr 1
|
||||
val g = (colA.rawG() + colB.rawG()) ushr 1
|
||||
val b = (colA.rawB() + colB.rawB()) ushr 1
|
||||
return constructRGBFromInt(r, g, b)
|
||||
}
|
||||
|
||||
private fun outOfBounds(x: Int, y: Int): Boolean =
|
||||
x !in 0..Terrarum.game.map.width - 1 || y !in 0..Terrarum.game.map.height - 1
|
||||
|
||||
private fun outOfMapBounds(x: Int, y: Int): Boolean =
|
||||
//x !in 0..lightMapMSB!![0].size - 1 || y !in 0..lightMapMSB!!.size - 1
|
||||
x !in 0..lightmap[0].size - 1 || y !in 0..lightmap.size - 1
|
||||
|
||||
private fun Int.clampZero() = if (this < 0) 0 else this
|
||||
|
||||
private fun Float.clampZero() = if (this < 0) 0f else this
|
||||
|
||||
private fun Int.clampChannel() = if (this < 0) 0 else if (this > CHANNEL_MAX) CHANNEL_MAX else this
|
||||
|
||||
private fun Float.clampOne() = if (this < 0) 0f else if (this > 1) 1f else this
|
||||
|
||||
fun getValueFromMap(x: Int, y: Int): Int? = getLight(x, y)
|
||||
|
||||
private fun purgePartOfLightmap(x1: Int, y1: Int, x2: Int, y2: Int) {
|
||||
try {
|
||||
for (y in y1 - 1..y2 + 1) {
|
||||
for (x in x1 - 1..x2 + 1) {
|
||||
//if (y == y1 - 1 || y == y2 + 1 || x == x1 - 1 || x == x2 + 1) {
|
||||
// fill the rim with (pre) calculation
|
||||
// setLight(x, y, preCalculateUpdateGLOnly(x, y))
|
||||
//}
|
||||
//else {
|
||||
setLight(x, y, 0)
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun clampWTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
}
|
||||
else if (x > Terrarum.game.map.width) {
|
||||
return Terrarum.game.map.width
|
||||
}
|
||||
else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private fun clampHTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
}
|
||||
else if (x > Terrarum.game.map.height) {
|
||||
return Terrarum.game.map.height
|
||||
}
|
||||
else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private fun arithmeticAverage(vararg i: Int): Int {
|
||||
var sum = 0
|
||||
for (k in i.indices) {
|
||||
sum += i[k]
|
||||
}
|
||||
return Math.round(sum / i.size.toFloat())
|
||||
}
|
||||
|
||||
internal data class LightmapLantern(
|
||||
var x: Int,
|
||||
var y: Int,
|
||||
var intensity: Int
|
||||
)
|
||||
|
||||
private fun Int.clamp256() = if (this > 255) 255 else this
|
||||
|
||||
fun Int.rgb30ClampTo24(): Int {
|
||||
val r = this.rawR().clamp256()
|
||||
val g = this.rawG().clamp256()
|
||||
val b = this.rawB().clamp256()
|
||||
|
||||
return r.shl(16) or g.shl(8) or b
|
||||
}
|
||||
|
||||
infix fun Float.powerOf(f: Float) = FastMath.pow(this, f)
|
||||
private fun Float.sqr() = this * this
|
||||
private fun Float.sqrt() = FastMath.sqrt(this)
|
||||
fun Float.floor() = FastMath.floor(this)
|
||||
fun Float.round(): Int = Math.round(this)
|
||||
fun Float.ceil() = FastMath.ceil(this)
|
||||
}
|
||||
489
src/net/torvald/terrarum/mapdrawer/MapCamera.kt
Normal file
489
src/net/torvald/terrarum/mapdrawer/MapCamera.kt
Normal file
@@ -0,0 +1,489 @@
|
||||
package net.torvald.terrarum.mapdrawer
|
||||
|
||||
import net.torvald.terrarum.gamemap.GameMap
|
||||
import net.torvald.terrarum.gamemap.PairedMapLayer
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
import net.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.setBlendMul
|
||||
import net.torvald.terrarum.setBlendNormal
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
import org.newdawn.slick.SlickException
|
||||
import org.newdawn.slick.SpriteSheet
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
object MapCamera {
|
||||
private val map: GameMap = Terrarum.game.map;
|
||||
|
||||
var cameraX = 0
|
||||
private set
|
||||
var cameraY = 0
|
||||
private set
|
||||
|
||||
private val TSIZE = MapDrawer.TILE_SIZE
|
||||
|
||||
private var tilesWall: SpriteSheet = SpriteSheet("./res/graphics/terrain/wall.png", TSIZE, TSIZE)
|
||||
private var tilesTerrain: SpriteSheet = SpriteSheet("./res/graphics/terrain/terrain.png", TSIZE, TSIZE)
|
||||
private var tilesWire: SpriteSheet = SpriteSheet("./res/graphics/terrain/wire.png", TSIZE, TSIZE)
|
||||
private var tilesetBook: Array<SpriteSheet> = arrayOf(tilesWall, tilesTerrain, tilesWire)
|
||||
|
||||
private val WALL = GameMap.WALL
|
||||
private val TERRAIN = GameMap.TERRAIN
|
||||
private val WIRE = GameMap.WIRE
|
||||
|
||||
private var renderWidth: Int = 0
|
||||
private var renderHeight: Int = 0
|
||||
|
||||
private val NEARBY_TILE_KEY_UP = 0
|
||||
private val NEARBY_TILE_KEY_RIGHT = 1
|
||||
private val NEARBY_TILE_KEY_DOWN = 2
|
||||
private val NEARBY_TILE_KEY_LEFT = 3
|
||||
|
||||
private val NEARBY_TILE_CODE_UP = 1
|
||||
private val NEARBY_TILE_CODE_RIGHT = 2
|
||||
private val NEARBY_TILE_CODE_DOWN = 4
|
||||
private val NEARBY_TILE_CODE_LEFT = 8
|
||||
|
||||
/**
|
||||
* Connectivity group 01 : artificial tiles
|
||||
* It holds different shading rule to discriminate with group 02, index 0 is single tile.
|
||||
* These are the tiles that only connects to itself, will not connect to colour variants
|
||||
*/
|
||||
private val TILES_CONNECT_SELF = arrayOf(
|
||||
TileNameCode.ICE_MAGICAL
|
||||
, TileNameCode.ILLUMINATOR_BLACK
|
||||
, TileNameCode.ILLUMINATOR_BLUE
|
||||
, TileNameCode.ILLUMINATOR_BROWN
|
||||
, TileNameCode.ILLUMINATOR_CYAN
|
||||
, TileNameCode.ILLUMINATOR_FUCHSIA
|
||||
, TileNameCode.ILLUMINATOR_GREEN
|
||||
, TileNameCode.ILLUMINATOR_GREEN_DARK
|
||||
, TileNameCode.ILLUMINATOR_GREY_DARK
|
||||
, TileNameCode.ILLUMINATOR_GREY_LIGHT
|
||||
, TileNameCode.ILLUMINATOR_GREY_MED
|
||||
, TileNameCode.ILLUMINATOR_ORANGE
|
||||
, TileNameCode.ILLUMINATOR_PURPLE
|
||||
, TileNameCode.ILLUMINATOR_RED
|
||||
, TileNameCode.ILLUMINATOR_TAN
|
||||
, TileNameCode.ILLUMINATOR_WHITE
|
||||
, TileNameCode.ILLUMINATOR_YELLOW
|
||||
, TileNameCode.ILLUMINATOR_BLACK_OFF
|
||||
, TileNameCode.ILLUMINATOR_BLUE_OFF
|
||||
, TileNameCode.ILLUMINATOR_BROWN_OFF
|
||||
, TileNameCode.ILLUMINATOR_CYAN_OFF
|
||||
, TileNameCode.ILLUMINATOR_FUCHSIA_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREEN_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREEN_DARK_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_DARK_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_LIGHT_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_MED_OFF
|
||||
, TileNameCode.ILLUMINATOR_ORANGE_OFF
|
||||
, TileNameCode.ILLUMINATOR_PURPLE_OFF
|
||||
, TileNameCode.ILLUMINATOR_RED_OFF
|
||||
, TileNameCode.ILLUMINATOR_TAN_OFF
|
||||
, TileNameCode.ILLUMINATOR_WHITE_OFF
|
||||
, TileNameCode.ILLUMINATOR_YELLOW
|
||||
, TileNameCode.SANDSTONE
|
||||
, TileNameCode.SANDSTONE_BLACK
|
||||
, TileNameCode.SANDSTONE_DESERT
|
||||
, TileNameCode.SANDSTONE_RED
|
||||
, TileNameCode.SANDSTONE_WHITE
|
||||
, TileNameCode.SANDSTONE_GREEN
|
||||
)
|
||||
|
||||
/**
|
||||
* Connectivity group 02 : natural tiles
|
||||
* It holds different shading rule to discriminate with group 01, index 0 is middle tile.
|
||||
*/
|
||||
private val TILES_CONNECT_MUTUAL = arrayOf(
|
||||
TileNameCode.STONE
|
||||
, TileNameCode.DIRT
|
||||
, TileNameCode.GRASS
|
||||
, TileNameCode.PLANK_BIRCH
|
||||
, TileNameCode.PLANK_BLOODROSE
|
||||
, TileNameCode.PLANK_EBONY
|
||||
, TileNameCode.PLANK_NORMAL
|
||||
, TileNameCode.SAND
|
||||
, TileNameCode.SAND_WHITE
|
||||
, TileNameCode.SAND_RED
|
||||
, TileNameCode.SAND_DESERT
|
||||
, TileNameCode.SAND_BLACK
|
||||
, TileNameCode.SAND_GREEN
|
||||
, TileNameCode.GRAVEL
|
||||
, TileNameCode.GRAVEL_GREY
|
||||
, TileNameCode.SNOW
|
||||
, TileNameCode.ICE_NATURAL
|
||||
, TileNameCode.ORE_COPPER
|
||||
, TileNameCode.ORE_IRON
|
||||
, TileNameCode.ORE_GOLD
|
||||
, TileNameCode.ORE_SILVER
|
||||
, TileNameCode.ORE_ILMENITE
|
||||
, TileNameCode.ORE_AURICHALCUM
|
||||
|
||||
, TileNameCode.WATER
|
||||
, TileNameCode.WATER_1
|
||||
, TileNameCode.WATER_2
|
||||
, TileNameCode.WATER_3
|
||||
, TileNameCode.WATER_4
|
||||
, TileNameCode.WATER_5
|
||||
, TileNameCode.WATER_6
|
||||
, TileNameCode.WATER_7
|
||||
, TileNameCode.WATER_8
|
||||
, TileNameCode.WATER_9
|
||||
, TileNameCode.WATER_10
|
||||
, TileNameCode.WATER_11
|
||||
, TileNameCode.WATER_12
|
||||
, TileNameCode.WATER_13
|
||||
, TileNameCode.WATER_14
|
||||
, TileNameCode.WATER_15
|
||||
, TileNameCode.LAVA
|
||||
, TileNameCode.LAVA_1
|
||||
, TileNameCode.LAVA_2
|
||||
, TileNameCode.LAVA_3
|
||||
, TileNameCode.LAVA_4
|
||||
, TileNameCode.LAVA_5
|
||||
, TileNameCode.LAVA_6
|
||||
, TileNameCode.LAVA_7
|
||||
, TileNameCode.LAVA_8
|
||||
, TileNameCode.LAVA_9
|
||||
, TileNameCode.LAVA_10
|
||||
, TileNameCode.LAVA_11
|
||||
, TileNameCode.LAVA_12
|
||||
, TileNameCode.LAVA_13
|
||||
, TileNameCode.LAVA_14
|
||||
, TileNameCode.LAVA_15
|
||||
)
|
||||
|
||||
/**
|
||||
* Torches, levers, switches, ...
|
||||
*/
|
||||
private val TILES_WALL_STICKER = arrayOf(
|
||||
TileNameCode.TORCH
|
||||
, TileNameCode.TORCH_FROST
|
||||
, TileNameCode.TORCH_OFF
|
||||
, TileNameCode.TORCH_FROST_OFF
|
||||
)
|
||||
|
||||
/**
|
||||
* platforms, ...
|
||||
*/
|
||||
private val TILES_WALL_STICKER_CONNECT_SELF = arrayOf<Int>(
|
||||
|
||||
)
|
||||
|
||||
/**
|
||||
* Tiles that half-transparent and has hue
|
||||
* will blend colour using colour multiplication
|
||||
* i.e. red hues get lost if you dive into the water
|
||||
*/
|
||||
private val TILES_BLEND_MUL = arrayOf(
|
||||
TileNameCode.WATER
|
||||
, TileNameCode.WATER_1
|
||||
, TileNameCode.WATER_2
|
||||
, TileNameCode.WATER_3
|
||||
, TileNameCode.WATER_4
|
||||
, TileNameCode.WATER_5
|
||||
, TileNameCode.WATER_6
|
||||
, TileNameCode.WATER_7
|
||||
, TileNameCode.WATER_8
|
||||
, TileNameCode.WATER_9
|
||||
, TileNameCode.WATER_10
|
||||
, TileNameCode.WATER_11
|
||||
, TileNameCode.WATER_12
|
||||
, TileNameCode.WATER_13
|
||||
, TileNameCode.WATER_14
|
||||
, TileNameCode.WATER_15
|
||||
, TileNameCode.LAVA
|
||||
, TileNameCode.LAVA_1
|
||||
, TileNameCode.LAVA_2
|
||||
, TileNameCode.LAVA_3
|
||||
, TileNameCode.LAVA_4
|
||||
, TileNameCode.LAVA_5
|
||||
, TileNameCode.LAVA_6
|
||||
, TileNameCode.LAVA_7
|
||||
, TileNameCode.LAVA_8
|
||||
, TileNameCode.LAVA_9
|
||||
, TileNameCode.LAVA_10
|
||||
, TileNameCode.LAVA_11
|
||||
, TileNameCode.LAVA_12
|
||||
, TileNameCode.LAVA_13
|
||||
, TileNameCode.LAVA_14
|
||||
, TileNameCode.LAVA_15
|
||||
)
|
||||
|
||||
fun update(gc: GameContainer, delta_t: Int) {
|
||||
val player = Terrarum.game.player
|
||||
|
||||
renderWidth = FastMath.ceil(Terrarum.WIDTH / Terrarum.game.screenZoom) // div, not mul
|
||||
renderHeight = FastMath.ceil(Terrarum.HEIGHT / Terrarum.game.screenZoom)
|
||||
|
||||
// position - (WH / 2)
|
||||
cameraX = Math.round(FastMath.clamp(
|
||||
player.hitbox!!.centeredX - renderWidth / 2, TSIZE.toFloat(), map.width * TSIZE - renderWidth - TSIZE.toFloat()))
|
||||
cameraY = Math.round(FastMath.clamp(
|
||||
player.hitbox!!.centeredY - renderHeight / 2, TSIZE.toFloat(), map.height * TSIZE - renderHeight - TSIZE.toFloat()))
|
||||
}
|
||||
|
||||
fun renderBehind(gc: GameContainer, g: Graphics) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
setBlendNormal()
|
||||
drawTiles(WALL, false)
|
||||
drawTiles(TERRAIN, false)
|
||||
}
|
||||
|
||||
fun renderFront(gc: GameContainer, g: Graphics) {
|
||||
setBlendMul()
|
||||
drawTiles(TERRAIN, true)
|
||||
setBlendNormal()
|
||||
}
|
||||
|
||||
private fun drawTiles(mode: Int, drawModeTilesBlendMul: Boolean) {
|
||||
val for_y_start = div16(cameraY)
|
||||
val for_x_start = div16(cameraX)
|
||||
|
||||
val for_y_end = clampHTile(for_y_start + div16(renderHeight) + 2)
|
||||
val for_x_end = clampWTile(for_x_start + div16(renderWidth) + 2)
|
||||
|
||||
// initialise
|
||||
tilesetBook[mode].startUse()
|
||||
|
||||
// loop
|
||||
for (y in for_y_start..for_y_end - 1) {
|
||||
for (x in for_x_start..for_x_end - 1) {
|
||||
|
||||
val thisTile: Int?
|
||||
if (mode % 3 == WALL)
|
||||
thisTile = map.getTileFromWall(x, y)
|
||||
else if (mode % 3 == TERRAIN)
|
||||
thisTile = map.getTileFromTerrain(x, y)
|
||||
else if (mode % 3 == WIRE)
|
||||
thisTile = map.getTileFromWire(x, y)
|
||||
else
|
||||
throw IllegalArgumentException()
|
||||
|
||||
val noDamageLayer = mode % 3 == WIRE
|
||||
|
||||
// draw
|
||||
try {
|
||||
if (
|
||||
|
||||
(mode == WALL || mode == TERRAIN) // not an air tile
|
||||
|
||||
&& (thisTile ?: 0) > 0
|
||||
&&
|
||||
// check if light level of nearby or this tile is illuminated
|
||||
( LightmapRenderer.getValueFromMap(x, y) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x - 1, y) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x + 1, y) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x, y - 1) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x, y + 1) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x - 1, y - 1) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x + 1, y + 1) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x + 1, y - 1) ?: 0 > 0
|
||||
|| LightmapRenderer.getValueFromMap(x - 1, y + 1) ?: 0 > 0)
|
||||
) {
|
||||
|
||||
val nearbyTilesInfo: Int
|
||||
if (isWallSticker(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y)
|
||||
} else if (isConnectMutual(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode)
|
||||
} else if (isConnectSelf(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile)
|
||||
} else {
|
||||
nearbyTilesInfo = 0
|
||||
}
|
||||
|
||||
|
||||
val thisTileX: Int
|
||||
if (!noDamageLayer)
|
||||
thisTileX = PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo
|
||||
else
|
||||
thisTileX = nearbyTilesInfo
|
||||
|
||||
val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE
|
||||
|
||||
if (drawModeTilesBlendMul) {
|
||||
if (isBlendMul(thisTile)) {
|
||||
drawTile(mode, x, y, thisTileX, thisTileY)
|
||||
}
|
||||
} else {
|
||||
// do NOT add "if (!isBlendMul(thisTile))"!
|
||||
// or else they will not look like they should be when backed with wall
|
||||
drawTile(mode, x, y, thisTileX, thisTileY)
|
||||
}
|
||||
}
|
||||
} catch (e: NullPointerException) {
|
||||
// do nothing. This exception handling may hide erratic behaviour completely.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tilesetBook[mode].endUse()
|
||||
}
|
||||
|
||||
private fun getGrassInfo(x: Int, y: Int, from: Int, to: Int): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param x
|
||||
* *
|
||||
* @param y
|
||||
* *
|
||||
* @return [0-15] 1: up, 2: right, 4: down, 8: left
|
||||
*/
|
||||
private fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int {
|
||||
val nearbyTiles = IntArray(4)
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1) ?: 4906
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1) ?: 4096
|
||||
|
||||
// try for
|
||||
var ret = 0
|
||||
for (i in 0..3) {
|
||||
if (nearbyTiles[i] == mark) {
|
||||
ret += 1 shl i // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
|
||||
val nearbyTiles = IntArray(4)
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1) ?: 4096
|
||||
|
||||
// try for
|
||||
var ret = 0
|
||||
for (i in 0..3) {
|
||||
try {
|
||||
if (!TilePropCodex.getProp(nearbyTiles[i]).isSolid) {
|
||||
ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
|
||||
val nearbyTiles = IntArray(4)
|
||||
val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(TERRAIN, x + 1, y) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(TERRAIN, x, y + 1) ?: 4096
|
||||
nearbyTiles[NEARBY_TILE_KEY_BACK] = map.getTileFrom(WALL, x, y) ?: 4096
|
||||
|
||||
try {
|
||||
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid
|
||||
&& TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid) {
|
||||
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid)
|
||||
return 0
|
||||
else
|
||||
return 3
|
||||
} else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid) {
|
||||
return 2
|
||||
} else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid) {
|
||||
return 1
|
||||
} else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid) {
|
||||
return 0
|
||||
} else
|
||||
return 3
|
||||
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||
return if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid)
|
||||
0
|
||||
else
|
||||
3
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun drawTile(mode: Int, tilewisePosX: Int, tilewisePosY: Int, sheetX: Int, sheetY: Int) {
|
||||
if (Terrarum.game.screenZoom == 1f) {
|
||||
tilesetBook[mode].renderInUse(
|
||||
FastMath.floor((tilewisePosX * TSIZE).toFloat()), FastMath.floor((tilewisePosY * TSIZE).toFloat()), sheetX, sheetY)
|
||||
} else {
|
||||
tilesetBook[mode].getSprite(
|
||||
sheetX, sheetY).drawEmbedded(
|
||||
Math.round(tilewisePosX.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), Math.round(tilewisePosY.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
fun div16(x: Int): Int = x and 0x7FFFFFFF shr 4
|
||||
fun mod16(x: Int): Int = x and 15
|
||||
fun quantise16(x: Int): Int = x and 0xFFFFFFF0.toInt()
|
||||
|
||||
fun clampW(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.width * TSIZE) {
|
||||
return map.width * TSIZE
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
fun clampH(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.height * TSIZE) {
|
||||
return map.height * TSIZE
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
fun clampWTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.width) {
|
||||
return map.width
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
fun clampHTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.height) {
|
||||
return map.height
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
fun getRenderWidth(): Int = renderWidth
|
||||
fun getRenderHeight(): Int = renderHeight
|
||||
|
||||
fun getRenderStartX(): Int = div16(cameraX)
|
||||
fun getRenderStartY(): Int = div16(cameraY)
|
||||
|
||||
fun getRenderEndX(): Int = clampWTile(getRenderStartX() + div16(renderWidth) + 2)
|
||||
fun getRenderEndY(): Int = clampHTile(getRenderStartY() + div16(renderHeight) + 2)
|
||||
|
||||
private fun isConnectSelf(b: Int?): Boolean = TILES_CONNECT_SELF.contains(b)
|
||||
private fun isConnectMutual(b: Int?): Boolean = TILES_CONNECT_MUTUAL.contains(b)
|
||||
private fun isWallSticker(b: Int?): Boolean = TILES_WALL_STICKER.contains(b)
|
||||
private fun isPlatform(b: Int?): Boolean = TILES_WALL_STICKER_CONNECT_SELF.contains(b)
|
||||
|
||||
private fun isBlendMul(b: Int?): Boolean = TILES_BLEND_MUL.contains(b)
|
||||
|
||||
}
|
||||
90
src/net/torvald/terrarum/mapdrawer/MapDrawer.kt
Normal file
90
src/net/torvald/terrarum/mapdrawer/MapDrawer.kt
Normal file
@@ -0,0 +1,90 @@
|
||||
package net.torvald.terrarum.mapdrawer
|
||||
|
||||
import net.torvald.terrarum.gamemap.GameMap
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
import net.torvald.terrarum.tilestats.TileStats
|
||||
import com.jme3.math.FastMath
|
||||
import org.newdawn.slick.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
object MapDrawer {
|
||||
const val TILE_SIZE = 16
|
||||
|
||||
private var envOverlayColourmap: Image = Image("./res/graphics/black_body_col_1000_40000_K.png")
|
||||
|
||||
private val ENV_COLTEMP_LOWEST = 5500
|
||||
private val ENV_COLTEMP_HIGHEST = 7500
|
||||
|
||||
val ENV_COLTEMP_NOON = 6500
|
||||
|
||||
private var colTemp: Int = 0
|
||||
|
||||
private val TILES_COLD = intArrayOf(
|
||||
TileNameCode.ICE_MAGICAL
|
||||
, TileNameCode.ICE_FRAGILE
|
||||
, TileNameCode.ICE_NATURAL
|
||||
, TileNameCode.SNOW)
|
||||
|
||||
private val TILES_WARM = intArrayOf(
|
||||
TileNameCode.SAND_DESERT
|
||||
, TileNameCode.SAND_RED)
|
||||
|
||||
@JvmStatic
|
||||
fun update(gc: GameContainer, delta_t: Int) {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun render(gc: GameContainer, g: Graphics) {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun drawEnvOverlay(g: Graphics) {
|
||||
val onscreen_tiles_max = FastMath.ceil(Terrarum.HEIGHT * Terrarum.WIDTH / FastMath.sqr(TILE_SIZE.toFloat())) * 2
|
||||
val onscreen_tiles_cap = onscreen_tiles_max / 4f
|
||||
val onscreen_cold_tiles = TileStats.getCount(*TILES_COLD).toFloat()
|
||||
val onscreen_warm_tiles = TileStats.getCount(*TILES_WARM).toFloat()
|
||||
|
||||
val colTemp_cold = colTempLinearFunc(onscreen_cold_tiles / onscreen_tiles_cap)
|
||||
val colTemp_warm = colTempLinearFunc(-(onscreen_warm_tiles / onscreen_tiles_cap))
|
||||
colTemp = colTemp_warm + colTemp_cold - ENV_COLTEMP_NOON
|
||||
val zoom = Terrarum.game.screenZoom
|
||||
|
||||
g.color = getColourFromMap(colTemp)
|
||||
//g.color = getColourFromMap(3022)
|
||||
g.fillRect(
|
||||
MapCamera.cameraX * zoom,
|
||||
MapCamera.cameraY * zoom,
|
||||
Terrarum.WIDTH * if (zoom < 1) 1f / zoom else zoom,
|
||||
Terrarum.HEIGHT * if (zoom < 1) 1f / zoom else zoom
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param x [-1 , 1], 0 for 6500K (median of ENV_COLTEMP_HIGHEST and ENV_COLTEMP_LOWEST)
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun colTempLinearFunc(x: Float): Int {
|
||||
val colTempMedian = (ENV_COLTEMP_HIGHEST + ENV_COLTEMP_LOWEST) / 2
|
||||
|
||||
return Math.round((ENV_COLTEMP_HIGHEST - ENV_COLTEMP_LOWEST) / 2 * FastMath.clamp(x, -1f, 1f) + colTempMedian)
|
||||
}
|
||||
|
||||
fun getColourFromMap(K: Int): Color {
|
||||
return envOverlayColourmap.getColor(colTempToImagePos(K), 0)
|
||||
}
|
||||
|
||||
private fun colTempToImagePos(K: Int): Int {
|
||||
if (K < 1000 || K >= 40000) throw IllegalArgumentException("K: out of range. ($K)")
|
||||
return (K - 1000) / 10
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getColTemp(): Int {
|
||||
return colTemp
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user