mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-10 22:01:52 +09:00
com.torvald → net.torvald
Former-commit-id: 375604da8a20a6ba7cd0a8d05a44add02b2d04f4 Former-commit-id: 287287c5920b07618174d7a7573f049d350ded66
This commit is contained in:
@@ -1,48 +0,0 @@
|
||||
package com.torvald
|
||||
|
||||
import org.apache.commons.csv.CSVFormat
|
||||
import org.apache.commons.csv.CSVParser
|
||||
import org.apache.commons.csv.CSVRecord
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-16.
|
||||
*/
|
||||
object CSVFetcher {
|
||||
|
||||
private var csvString: StringBuffer? = null
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun readCSV(csvFilePath: String): List<CSVRecord> {
|
||||
csvString = StringBuffer() // reset buffer every time it called
|
||||
readCSVasString(csvFilePath)
|
||||
|
||||
val csvParser = CSVParser.parse(
|
||||
csvString!!.toString(),
|
||||
CSVFormat.DEFAULT.withIgnoreSurroundingSpaces()
|
||||
.withHeader()
|
||||
.withIgnoreEmptyLines()
|
||||
.withDelimiter(';')
|
||||
.withCommentMarker('#')
|
||||
.withNullString("N/A")
|
||||
.withRecordSeparator('\n')
|
||||
)
|
||||
|
||||
val csvRecordList = csvParser.records
|
||||
csvParser.close()
|
||||
|
||||
return csvRecordList
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun readCSVasString(path: String): String {
|
||||
csvString = StringBuffer()
|
||||
Files.lines(FileSystems.getDefault().getPath(path)).forEach(
|
||||
{ s -> csvString!!.append("$s\n") }
|
||||
)
|
||||
return csvString!!.toString()
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.torvald.colourutil
|
||||
|
||||
import org.newdawn.slick.Color
|
||||
|
||||
/**
|
||||
* 6-Step RGB with builtin utils.
|
||||
* Created by minjaesong on 16-02-11.
|
||||
*/
|
||||
class Col216 : LimitedColours {
|
||||
|
||||
var raw: Byte = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
|
||||
* @param data
|
||||
*/
|
||||
constructor(data: Byte) {
|
||||
create(data.toInt())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param r 0-5
|
||||
* *
|
||||
* @param g 0-5
|
||||
* *
|
||||
* @param b 0-5
|
||||
*/
|
||||
constructor(r: Int, g: Int, b: Int) {
|
||||
create(r, g, b)
|
||||
}
|
||||
|
||||
override fun createSlickColor(raw: Int): Color {
|
||||
assertRaw(raw)
|
||||
val r = raw / MUL_2
|
||||
val g = raw % MUL_2 / MUL
|
||||
val b = raw % MUL
|
||||
|
||||
return createSlickColor(r, g, b)
|
||||
}
|
||||
|
||||
override fun createSlickColor(r: Int, g: Int, b: Int): Color {
|
||||
assertRGB(r, g, b)
|
||||
return Color(LOOKUP[r], LOOKUP[g], LOOKUP[b])
|
||||
}
|
||||
|
||||
override fun create(raw: Int) {
|
||||
assertRaw(raw)
|
||||
this.raw = raw.toByte()
|
||||
}
|
||||
|
||||
override fun create(r: Int, g: Int, b: Int) {
|
||||
assertRGB(r, g, b)
|
||||
raw = (MUL_2 * r + MUL * g + b).toByte()
|
||||
}
|
||||
|
||||
override fun toSlickColour(): Color = createSlickColor(raw.toUint())
|
||||
|
||||
private fun assertRaw(i: Int) {
|
||||
if (i >= COLOUR_RANGE_SIZE || i < 0) {
|
||||
println("i: " + i.toString())
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertRGB(r: Int, g: Int, b: Int) {
|
||||
if (r !in 0..MAX_STEP || g !in 0..MAX_STEP || b !in 0..MAX_STEP) {
|
||||
println("r: " + r.toString())
|
||||
println("g: " + g.toString())
|
||||
println("b: " + b.toString())
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
fun Byte.toUint() = this.toInt() and 0xFF
|
||||
|
||||
companion object {
|
||||
@Transient private val LOOKUP = intArrayOf(0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
|
||||
|
||||
const val MUL = 6
|
||||
const val MUL_2 = MUL * MUL
|
||||
const val MAX_STEP = MUL - 1
|
||||
const val COLOUR_RANGE_SIZE = MUL_2 * MUL
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.torvald.colourutil
|
||||
|
||||
import org.newdawn.slick.Color
|
||||
|
||||
/**
|
||||
* 40-Step RGB with builtin utils.
|
||||
* Created by minjaesong on 16-02-20.
|
||||
*/
|
||||
class Col40 : LimitedColours {
|
||||
|
||||
var raw: Char = ' '
|
||||
private set
|
||||
|
||||
override fun createSlickColor(raw: Int): Color {
|
||||
assertRaw(raw)
|
||||
val r = raw / MUL_2
|
||||
val g = raw % MUL_2 / MUL
|
||||
val b = raw % MUL
|
||||
|
||||
return createSlickColor(r, g, b)
|
||||
}
|
||||
|
||||
override fun createSlickColor(r: Int, g: Int, b: Int): Color {
|
||||
assertRGB(r, g, b)
|
||||
return Color(LOOKUP[r] shl 16 or (LOOKUP[g] shl 8) or LOOKUP[b])
|
||||
}
|
||||
|
||||
override fun create(raw: Int) {
|
||||
assertRaw(raw)
|
||||
this.raw = raw.toChar()
|
||||
}
|
||||
|
||||
override fun create(r: Int, g: Int, b: Int) {
|
||||
assertRGB(r, g, b)
|
||||
raw = (MUL_2 * r + MUL * g + b).toChar()
|
||||
}
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
constructor(c: Color) {
|
||||
create(
|
||||
Math.round(c.r * (MUL - 1)),
|
||||
Math.round(c.g * (MUL - 1)),
|
||||
Math.round(c.b * (MUL - 1)))
|
||||
}
|
||||
|
||||
private fun assertRaw(i: Int) {
|
||||
if (i >= COLOUR_DOMAIN_SIZE || i < 0) {
|
||||
println("i: " + i.toString())
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertRGB(r: Int, g: Int, b: Int) {
|
||||
if (r !in 0..MAX_STEP || g !in 0..MAX_STEP || b !in 0..MAX_STEP) {
|
||||
println("r: " + r.toString())
|
||||
println("g: " + g.toString())
|
||||
println("b: " + b.toString())
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Transient private val LOOKUP = intArrayOf(0, 7, 13, 20, 26, 33, 39, 46, 52, 59, 65, 72, 78, 85, 92, 98, 105, 111, 118, 124, 131, 137, 144, 150, 157, 163, 170, 177, 183, 190, 196, 203, 209, 216, 222, 229, 235, 242, 248, 255)
|
||||
|
||||
const val MUL = 40
|
||||
const val MUL_2 = MUL * MUL
|
||||
const val MAX_STEP = MUL - 1
|
||||
const val COLOUR_DOMAIN_SIZE = MUL_2 * MUL
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package com.torvald.colourutil
|
||||
|
||||
import org.newdawn.slick.Color
|
||||
|
||||
/**
|
||||
* 12-bit (16-step) RGB with builtin utils.
|
||||
* Created by minjaesong on 16-01-23.
|
||||
*/
|
||||
class Col4096 : LimitedColours {
|
||||
|
||||
/**
|
||||
* Retrieve raw ARGB value
|
||||
* @return 0xARGB
|
||||
*/
|
||||
var raw: Short = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
|
||||
* @param data
|
||||
*/
|
||||
constructor(data: Int) {
|
||||
create(data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param r 0-15
|
||||
* *
|
||||
* @param g 0-15
|
||||
* *
|
||||
* @param b 0-15
|
||||
*/
|
||||
constructor(r: Int, g: Int, b: Int) {
|
||||
create(r, g, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Col4096 colour and convert it to Slick Color
|
||||
* @param i
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
override fun createSlickColor(raw: Int): Color {
|
||||
assertRaw(raw)
|
||||
|
||||
val a: Int
|
||||
val r: Int
|
||||
val g: Int
|
||||
val b: Int
|
||||
|
||||
r = raw and 0xF00 shr 8
|
||||
g = raw and 0x0F0 shr 4
|
||||
b = raw and 0x00F
|
||||
|
||||
if (raw > 0xFFF) {
|
||||
a = raw and 0xF000 shr 12
|
||||
|
||||
return Color(
|
||||
r.shl(4) or r, g.shl(4) or g, b.shl(4) or b, a.shl(4) or a)
|
||||
}
|
||||
else {
|
||||
return Color(
|
||||
r.shl(4) or r, g.shl(4) or g, b.shl(4) or b)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createSlickColor(r: Int, g: Int, b: Int): Color {
|
||||
assertARGB(0, r, g, b)
|
||||
return createSlickColor(r.shl(8) or g.shl(4) or b)
|
||||
}
|
||||
|
||||
fun createSlickColor(a: Int, r: Int, g: Int, b: Int): Color {
|
||||
assertARGB(a, r, g, b)
|
||||
return createSlickColor(a.shl(12) or r.shl(8) or g.shl(4) or b)
|
||||
}
|
||||
|
||||
override fun create(raw: Int) {
|
||||
assertRaw(raw)
|
||||
this.raw = (raw and 0xFFFF).toShort()
|
||||
}
|
||||
|
||||
override fun create(r: Int, g: Int, b: Int) {
|
||||
assertARGB(0, r, g, b)
|
||||
raw = (r.shl(8) or g.shl(4) or b).toShort()
|
||||
}
|
||||
|
||||
fun create(a: Int, r: Int, g: Int, b: Int) {
|
||||
assertARGB(a, r, g, b)
|
||||
raw = (a.shl(12) or r.shl(8) or g.shl(4) or b).toShort()
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to 3 byte values, for raster imaging.
|
||||
* @return byte[RR, GG, BB] e.g. 0x4B3 -> 0x44, 0xBB, 0x33
|
||||
*/
|
||||
fun toByteArray(): ByteArray {
|
||||
val ret = ByteArray(3)
|
||||
val r = (raw.toInt() and 0xF00) shr 8
|
||||
val g = (raw.toInt() and 0x0F0) shr 4
|
||||
val b = (raw.toInt() and 0x00F)
|
||||
|
||||
ret[0] = (r.shl(4) or r).toByte()
|
||||
ret[1] = (g.shl(4) or g).toByte()
|
||||
ret[2] = (b.shl(4) or b).toByte()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
override fun toSlickColour(): Color = createSlickColor(raw.toUint())
|
||||
|
||||
private fun assertRaw(i: Int) {
|
||||
if (i > 0xFFFF || i < 0) {
|
||||
println("i: " + i.toString())
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertARGB(a: Int, r: Int, g: Int, b: Int) {
|
||||
if (a !in 0..16 || r !in 0..16 || g !in 0..16 || b !in 0..16) {
|
||||
println("a: " + a.toString())
|
||||
println("r: " + r.toString())
|
||||
println("g: " + g.toString())
|
||||
println("b: " + b.toString())
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
fun Short.toUint(): Int = this.toInt() and 0xFFFF
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.torvald.colourutil
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-10.
|
||||
*/
|
||||
/**
|
||||
* @param h : Hue 0-359
|
||||
* @param s : Saturation 0-1
|
||||
* @param v : Value (brightness in Adobe Photoshop(TM)) 0-1
|
||||
*/
|
||||
data class HSV(
|
||||
var h: Float,
|
||||
var s: Float,
|
||||
var v: Float
|
||||
)
|
||||
@@ -1,110 +0,0 @@
|
||||
package com.torvald.colourutil
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import org.newdawn.slick.Color
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
object HSVUtil {
|
||||
|
||||
/**
|
||||
* Convert HSV parameters to RGB color.
|
||||
* @param H 0-359 Hue
|
||||
* *
|
||||
* @param S 0-1 Saturation
|
||||
* *
|
||||
* @param V 0-1 Value
|
||||
* *
|
||||
* @return org.newdawn.slick.Color
|
||||
* *
|
||||
* @link http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
|
||||
*/
|
||||
fun toRGB(H: Float, S: Float, V: Float): Color {
|
||||
var H = H
|
||||
H %= 360f
|
||||
|
||||
val C = V * S
|
||||
val X = C * (1 - FastMath.abs(
|
||||
H / 60f % 2 - 1))
|
||||
val m = V - C
|
||||
|
||||
var R_prime = java.lang.Float.NaN
|
||||
var G_prime = java.lang.Float.NaN
|
||||
var B_prime = java.lang.Float.NaN
|
||||
|
||||
if (H < 60) {
|
||||
R_prime = C
|
||||
G_prime = X
|
||||
B_prime = 0f
|
||||
}
|
||||
else if (H < 120) {
|
||||
R_prime = X
|
||||
G_prime = C
|
||||
B_prime = 0f
|
||||
}
|
||||
else if (H < 180) {
|
||||
R_prime = 0f
|
||||
G_prime = C
|
||||
B_prime = X
|
||||
}
|
||||
else if (H < 240) {
|
||||
R_prime = 0f
|
||||
G_prime = X
|
||||
B_prime = C
|
||||
}
|
||||
else if (H < 300) {
|
||||
R_prime = X
|
||||
G_prime = 0f
|
||||
B_prime = C
|
||||
}
|
||||
else if (H < 360) {
|
||||
R_prime = C
|
||||
G_prime = 0f
|
||||
B_prime = X
|
||||
}
|
||||
|
||||
return Color(
|
||||
R_prime + m, G_prime + m, B_prime + m)
|
||||
}
|
||||
|
||||
fun toRGB(hsv: HSV): Color {
|
||||
return toRGB(hsv.h, hsv.s, hsv.v)
|
||||
}
|
||||
|
||||
fun fromRGB(color: Color): HSV {
|
||||
val r = color.r
|
||||
val g = color.g
|
||||
val b = color.b
|
||||
|
||||
val rgbMin = FastMath.min(r, g, b)
|
||||
val rgbMax = FastMath.max(r, g, b)
|
||||
|
||||
var h: Float
|
||||
val s: Float
|
||||
val v = rgbMax
|
||||
|
||||
val delta = rgbMax - rgbMin
|
||||
|
||||
if (rgbMax != 0f)
|
||||
s = delta / rgbMax
|
||||
else {
|
||||
h = 0f
|
||||
s = 0f
|
||||
return HSV(h, s, v)
|
||||
}
|
||||
|
||||
if (r == rgbMax)
|
||||
h = (g - b) / delta
|
||||
else if (g == rgbMax)
|
||||
h = 2 + (b - r) / delta
|
||||
else
|
||||
h = 4 + (r - g) / delta
|
||||
|
||||
h *= 60f
|
||||
if (h < 0) h += 360f
|
||||
|
||||
return HSV(h, s, v)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.torvald.colourutil
|
||||
|
||||
import org.newdawn.slick.Color
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-11.
|
||||
*/
|
||||
interface LimitedColours {
|
||||
|
||||
fun createSlickColor(raw: Int): Color
|
||||
fun createSlickColor(r: Int, g: Int, b: Int): Color
|
||||
|
||||
fun create(raw: Int)
|
||||
fun create(r: Int, g: Int, b: Int)
|
||||
|
||||
fun toSlickColour(): Color
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
package com.torvald.imagefont
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.newdawn.slick.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-27.
|
||||
*/
|
||||
open class GameFontBase @Throws(SlickException::class)
|
||||
constructor() : Font {
|
||||
|
||||
private fun getHan(hanIndex: Int): IntArray {
|
||||
val han_x = hanIndex % JONG_COUNT
|
||||
val han_y = hanIndex / JONG_COUNT
|
||||
val ret = intArrayOf(han_x, han_y)
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getHanChosung(hanIndex: Int) = hanIndex / (JUNG_COUNT * JONG_COUNT)
|
||||
|
||||
private fun getHanJungseong(hanIndex: Int) = hanIndex / JONG_COUNT % JUNG_COUNT
|
||||
|
||||
private fun getHanJongseong(hanIndex: Int) = hanIndex % JONG_COUNT
|
||||
|
||||
|
||||
private fun getHanChoseongRow(hanIndex: Int): Int {
|
||||
val jungseongIndex = getHanJungseong(hanIndex)
|
||||
val jungseongWide = arrayOf(8, 12, 13, 17, 18, 21)
|
||||
val jungseongComplex = arrayOf(9, 10, 11, 14, 15, 16, 22)
|
||||
val ret: Int
|
||||
|
||||
if (jungseongWide.contains(jungseongIndex)) {
|
||||
ret = 2
|
||||
}
|
||||
else if (jungseongComplex.contains(jungseongIndex)) {
|
||||
ret = 4
|
||||
}
|
||||
else {
|
||||
ret = 0
|
||||
}
|
||||
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
|
||||
}
|
||||
|
||||
private fun getHanJungseongRow(hanIndex: Int) = if (getHanJongseong(hanIndex) == 0) 6 else 7
|
||||
|
||||
private val hanJongseongRow: Int
|
||||
get() = 8
|
||||
|
||||
private fun isAsciiEF(c: Char) = asciiEFList.contains(c)
|
||||
|
||||
private fun isExtAEF(c: Char) = extAEFList.contains(c)
|
||||
|
||||
private fun isHangul(c: Char) = c.toInt() >= 0xAC00 && c.toInt() < 0xD7A4
|
||||
|
||||
private fun isAscii(c: Char) = c.toInt() > 0 && c.toInt() <= 0xFF
|
||||
|
||||
private fun isRunic(c: Char) = runicList.contains(c)
|
||||
|
||||
private fun isExtA(c: Char) = c.toInt() >= 0x100 && c.toInt() < 0x180
|
||||
|
||||
private fun isKana(c: Char) = c.toInt() >= 0x3040 && c.toInt() < 0x3100
|
||||
|
||||
private fun isCJKPunct(c: Char) = c.toInt() >= 0x3000 && c.toInt() < 0x3040
|
||||
|
||||
private fun isUniHan(c: Char) = c.toInt() >= 0x3400 && c.toInt() < 0xA000
|
||||
|
||||
private fun isCyrilic(c: Char) = c.toInt() >= 0x400 && c.toInt() < 0x460
|
||||
|
||||
private fun isCyrilicEF(c: Char) = cyrilecEFList.contains(c)
|
||||
|
||||
private fun isFullwidthUni(c: Char) = c.toInt() >= 0xFF00 && c.toInt() < 0xFF20
|
||||
|
||||
private fun isUniPunct(c: Char) = c.toInt() >= 0x2000 && c.toInt() < 0x2070
|
||||
|
||||
private fun isWenQuanYi1(c: Char) = c.toInt() >= 0x33F3 && c.toInt() <= 0x69FC
|
||||
|
||||
private fun isWenQuanYi2(c: Char) = c.toInt() >= 0x69FD && c.toInt() <= 0x9FDC
|
||||
|
||||
|
||||
|
||||
private fun asciiEFindexX(c: Char) = asciiEFList.indexOf(c) % 16
|
||||
private fun asciiEFindexY(c: Char) = asciiEFList.indexOf(c) / 16
|
||||
|
||||
private fun extAEFindexX(c: Char) = extAEFList.indexOf(c) % 16
|
||||
private fun extAEFindexY(c: Char) = extAEFList.indexOf(c) / 16
|
||||
|
||||
private fun runicIndexX(c: Char) = runicList.indexOf(c) % 16
|
||||
private fun runicIndexY(c: Char) = runicList.indexOf(c) / 16
|
||||
|
||||
private fun kanaIndexX(c: Char) = (c.toInt() - 0x3040) % 16
|
||||
private fun kanaIndexY(c: Char) = (c.toInt() - 0x3040) / 16
|
||||
|
||||
private fun cjkPunctIndexX(c: Char) = (c.toInt() - 0x3000) % 16
|
||||
private fun cjkPunctIndexY(c: Char) = (c.toInt() - 0x3000) / 16
|
||||
|
||||
private fun uniHanIndexX(c: Char) = (c.toInt() - 0x3400) % 256
|
||||
private fun uniHanIndexY(c: Char) = (c.toInt() - 0x3400) / 256
|
||||
|
||||
private fun cyrilicIndexX(c: Char) = (c.toInt() - 0x400) % 16
|
||||
private fun cyrilicIndexY(c: Char) = (c.toInt() - 0x400) / 16
|
||||
|
||||
private fun cyrilicEFindexX(c: Char) = cyrilecEFList.indexOf(c) % 16
|
||||
private fun cyrilicEFindexY(c: Char) = cyrilecEFList.indexOf(c) / 16
|
||||
|
||||
private fun fullwidthUniIndexX(c: Char) = (c.toInt() - 0xFF00) % 16
|
||||
private fun fullwidthUniIndexY(c: Char) = (c.toInt() - 0xFF00) / 16
|
||||
|
||||
private fun uniPunctIndexX(c: Char) = (c.toInt() - 0x2000) % 16
|
||||
private fun uniPunctIndexY(c: Char) = (c.toInt() - 0x2000) / 16
|
||||
|
||||
private fun wenQuanYiIndexX(c: Char) =
|
||||
(c.toInt() - if (c.toInt() <= 0x4DB5) 0x33F3 else 0x33F3 + 0x4A) % 32
|
||||
private fun wenQuanYi1IndexY(c: Char) = (c.toInt() - (0x33F3 + 0x4A)) / 32
|
||||
private fun wenQuanYi2IndexY(c: Char) = (c.toInt() - 0x69FD) / 32
|
||||
|
||||
override fun getWidth(s: String) = getWidthSubstr(s, s.length)
|
||||
|
||||
|
||||
private fun getWidthSubstr(s: String, endIndex: Int): Int {
|
||||
var len = 0
|
||||
for (i in 0..endIndex - 1) {
|
||||
val c = getSheetType(s[i])
|
||||
|
||||
if (i > 0 && s[i].toInt() > 0x20) {
|
||||
// Kerning
|
||||
val cpre = getSheetType(s[i - 1])
|
||||
if ((cpre == SHEET_UNIHAN || cpre == SHEET_HANGUL) && !(c == SHEET_UNIHAN || c == SHEET_HANGUL)
|
||||
|
||||
|| (c == SHEET_UNIHAN || c == SHEET_HANGUL) && !(cpre == SHEET_UNIHAN || cpre == SHEET_HANGUL)) {
|
||||
// margin after/before hangul/unihan
|
||||
len += 2
|
||||
}
|
||||
else if ((c == SHEET_HANGUL || c == SHEET_KANA) && (cpre == SHEET_HANGUL || cpre == SHEET_KANA)) {
|
||||
// margin between hangul/kana
|
||||
len += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (c == SHEET_ASCII_EF || c == SHEET_EXTA_EF || c == SHEET_CYRILIC_EF)
|
||||
len += W_LATIN_NARROW
|
||||
else if (c == SHEET_KANA || c == SHEET_HANGUL || c == SHEET_CJK_PUNCT)
|
||||
len += W_CJK
|
||||
else if (c == SHEET_UNIHAN || c == SHEET_FW_UNI || c == SHEET_WENQUANYI_1 || c == SHEET_WENQUANYI_2)
|
||||
len += W_UNIHAN
|
||||
else
|
||||
len += W_LATIN_WIDE
|
||||
|
||||
if (i < endIndex - 1) len += interchar
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
override fun getHeight(s: String) = H
|
||||
|
||||
override fun getLineHeight() = H
|
||||
|
||||
override fun drawString(x: Float, y: Float, s: String) {
|
||||
drawString(x, y, s, Color.white)
|
||||
}
|
||||
|
||||
override fun drawString(x: Float, y: Float, s: String, color: Color) {
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glColorMask(true, true, true, true)
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
// hangul fonts first
|
||||
//hangulSheet.startUse() // disabling texture binding to make the font coloured
|
||||
// JOHAB
|
||||
for (i in 0..s.length - 1) {
|
||||
val ch = s[i]
|
||||
|
||||
if (isHangul(ch)) {
|
||||
val hIndex = ch.toInt() - 0xAC00
|
||||
|
||||
val indexCho = getHanChosung(hIndex)
|
||||
val indexJung = getHanJungseong(hIndex)
|
||||
val indexJong = getHanJongseong(hIndex)
|
||||
|
||||
val choRow = getHanChoseongRow(hIndex)
|
||||
val jungRow = getHanJungseongRow(hIndex)
|
||||
val jongRow = hanJongseongRow
|
||||
|
||||
val glyphW = getWidth("" + ch)
|
||||
|
||||
/*// initials
|
||||
hangulSheet.renderInUse(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW),
|
||||
Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f),
|
||||
indexCho, choRow
|
||||
)
|
||||
// medials
|
||||
hangulSheet.renderInUse(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW),
|
||||
Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f),
|
||||
indexJung, jungRow
|
||||
)
|
||||
// finals
|
||||
hangulSheet.renderInUse(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW),
|
||||
Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f),
|
||||
indexJong, jongRow
|
||||
)*/
|
||||
|
||||
hangulSheet.getSubImage(indexCho, choRow).draw(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(),
|
||||
Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f).toFloat(),
|
||||
color
|
||||
)
|
||||
hangulSheet.getSubImage(indexJung, jungRow).draw(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(),
|
||||
Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f).toFloat(),
|
||||
color
|
||||
)
|
||||
hangulSheet.getSubImage(indexJong, jongRow).draw(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(),
|
||||
Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f).toFloat(),
|
||||
color
|
||||
)
|
||||
}
|
||||
}
|
||||
//hangulSheet.endUse()
|
||||
|
||||
// unihan fonts
|
||||
/*uniHan.startUse();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
|
||||
if (isUniHan(ch)) {
|
||||
int glyphW = getWidth("" + ch);
|
||||
uniHan.renderInUse(
|
||||
Math.round(x
|
||||
+ getWidthSubstr(s, i + 1) - glyphW
|
||||
)
|
||||
, Math.round((H - H_UNIHAN) / 2 + y)
|
||||
, uniHanIndexX(ch)
|
||||
, uniHanIndexY(ch)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uniHan.endUse();*/
|
||||
|
||||
// WenQuanYi 1
|
||||
//wenQuanYi_1.startUse()
|
||||
|
||||
for (i in 0..s.length - 1) {
|
||||
val ch = s[i]
|
||||
|
||||
if (isWenQuanYi1(ch)) {
|
||||
val glyphW = getWidth("" + ch)
|
||||
/*wenQuanYi_1.renderInUse(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW),
|
||||
Math.round((H - H_UNIHAN) / 2 + y),
|
||||
wenQuanYiIndexX(ch),
|
||||
wenQuanYi1IndexY(ch)
|
||||
)*/
|
||||
wenQuanYi_1.getSubImage(wenQuanYiIndexX(ch), wenQuanYi1IndexY(ch)).draw(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(),
|
||||
Math.round((H - H_UNIHAN) / 2 + y).toFloat(),
|
||||
color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//wenQuanYi_1.endUse()
|
||||
// WenQuanYi 2
|
||||
//wenQuanYi_2.startUse()
|
||||
|
||||
for (i in 0..s.length - 1) {
|
||||
val ch = s[i]
|
||||
|
||||
if (isWenQuanYi2(ch)) {
|
||||
val glyphW = getWidth("" + ch)
|
||||
/*wenQuanYi_2.renderInUse(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW),
|
||||
Math.round((H - H_UNIHAN) / 2 + y),
|
||||
wenQuanYiIndexX(ch),
|
||||
wenQuanYi2IndexY(ch)
|
||||
)*/
|
||||
wenQuanYi_2.getSubImage(wenQuanYiIndexX(ch), wenQuanYi2IndexY(ch)).draw(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(),
|
||||
Math.round((H - H_UNIHAN) / 2 + y).toFloat(),
|
||||
color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//wenQuanYi_2.endUse()
|
||||
|
||||
// regular fonts
|
||||
var prevInstance = -1
|
||||
for (i in 0..s.length - 1) {
|
||||
val ch = s[i]
|
||||
|
||||
if (!isHangul(ch) && !isUniHan(ch)) {
|
||||
|
||||
// if not init, endUse first
|
||||
if (prevInstance != -1) {
|
||||
//sheetKey[prevInstance].endUse()
|
||||
}
|
||||
//sheetKey[getSheetType(ch)].startUse()
|
||||
prevInstance = getSheetType(ch)
|
||||
|
||||
val sheetX: Int
|
||||
val sheetY: Int
|
||||
when (prevInstance) {
|
||||
SHEET_ASCII_EF -> {
|
||||
sheetX = asciiEFindexX(ch)
|
||||
sheetY = asciiEFindexY(ch)
|
||||
}
|
||||
SHEET_EXTA_EF -> {
|
||||
sheetX = extAEFindexX(ch)
|
||||
sheetY = extAEFindexY(ch)
|
||||
}
|
||||
SHEET_RUNIC -> {
|
||||
sheetX = runicIndexX(ch)
|
||||
sheetY = runicIndexY(ch)
|
||||
}
|
||||
SHEET_EXTA_EM -> {
|
||||
sheetX = (ch.toInt() - 0x100) % 16
|
||||
sheetY = (ch.toInt() - 0x100) / 16
|
||||
}
|
||||
SHEET_KANA -> {
|
||||
sheetX = kanaIndexX(ch)
|
||||
sheetY = kanaIndexY(ch)
|
||||
}
|
||||
SHEET_CJK_PUNCT -> {
|
||||
sheetX = cjkPunctIndexX(ch)
|
||||
sheetY = cjkPunctIndexY(ch)
|
||||
}
|
||||
SHEET_CYRILIC_EM -> {
|
||||
sheetX = cyrilicIndexX(ch)
|
||||
sheetY = cyrilicIndexY(ch)
|
||||
}
|
||||
SHEET_CYRILIC_EF -> {
|
||||
sheetX = cyrilicEFindexX(ch)
|
||||
sheetY = cyrilicEFindexY(ch)
|
||||
}
|
||||
SHEET_FW_UNI -> {
|
||||
sheetX = fullwidthUniIndexX(ch)
|
||||
sheetY = fullwidthUniIndexY(ch)
|
||||
}
|
||||
SHEET_UNI_PUNCT -> {
|
||||
sheetX = uniPunctIndexX(ch)
|
||||
sheetY = uniPunctIndexY(ch)
|
||||
}
|
||||
else -> {
|
||||
sheetX = ch.toInt() % 16
|
||||
sheetY = ch.toInt() / 16
|
||||
}
|
||||
}
|
||||
|
||||
val glyphW = getWidth("" + ch)
|
||||
try {
|
||||
/*sheetKey[prevInstance].renderInUse(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW) // Interchar: pull punct right next to hangul to the left
|
||||
+ if (i > 0 && isHangul(s[i - 1])) -3 else 0,
|
||||
Math.round(y) + if (prevInstance == SHEET_CJK_PUNCT) -1
|
||||
else if (prevInstance == SHEET_FW_UNI) (H - H_HANGUL) / 2
|
||||
else 0,
|
||||
sheetX, sheetY
|
||||
)*/
|
||||
sheetKey[prevInstance].getSubImage(sheetX, sheetY).draw(
|
||||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat() // Interchar: pull punct right next to hangul to the left
|
||||
+ if (i > 0 && isHangul(s[i - 1])) -3f else 0f,
|
||||
Math.round(y).toFloat() + (if (prevInstance == SHEET_CJK_PUNCT) -1
|
||||
else if (prevInstance == SHEET_FW_UNI) (H - H_HANGUL) / 2
|
||||
else 0).toFloat(),
|
||||
color
|
||||
)
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
// character that does not exist in the sheet. No render, pass.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (prevInstance != -1) {
|
||||
//sheetKey[prevInstance].endUse()
|
||||
}
|
||||
|
||||
GL11.glEnd()
|
||||
}
|
||||
|
||||
private fun getSheetType(c: Char): Int {
|
||||
// EFs
|
||||
if (isAsciiEF(c))
|
||||
return SHEET_ASCII_EF
|
||||
else if (isExtAEF(c))
|
||||
return SHEET_EXTA_EF
|
||||
else if (isCyrilicEF(c))
|
||||
return SHEET_CYRILIC_EF
|
||||
else if (isRunic(c))
|
||||
return SHEET_RUNIC
|
||||
else if (isHangul(c))
|
||||
return SHEET_HANGUL
|
||||
else if (isKana(c))
|
||||
return SHEET_KANA
|
||||
else if (isUniHan(c))
|
||||
return SHEET_UNIHAN
|
||||
else if (isAscii(c))
|
||||
return SHEET_ASCII_EM
|
||||
else if (isExtA(c))
|
||||
return SHEET_EXTA_EM
|
||||
else if (isCyrilic(c))
|
||||
return SHEET_CYRILIC_EM
|
||||
else if (isUniPunct(c))
|
||||
return SHEET_UNI_PUNCT
|
||||
else if (isCJKPunct(c))
|
||||
return SHEET_CJK_PUNCT
|
||||
else if (isFullwidthUni(c))
|
||||
return SHEET_FW_UNI
|
||||
else
|
||||
return SHEET_ASCII_EM// fixed width punctuations
|
||||
// fixed width
|
||||
// fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw part of a string to the screen. Note that this will still position the text as though
|
||||
* it's part of the bigger string.
|
||||
* @param x
|
||||
* *
|
||||
* @param y
|
||||
* *
|
||||
* @param s
|
||||
* *
|
||||
* @param color
|
||||
* *
|
||||
* @param startIndex
|
||||
* *
|
||||
* @param endIndex
|
||||
*/
|
||||
override fun drawString(x: Float, y: Float, s: String, color: Color, startIndex: Int, endIndex: Int) {
|
||||
val unprintedHead = s.substring(0, startIndex)
|
||||
val printedBody = s.substring(startIndex, endIndex)
|
||||
val xoff = getWidth(unprintedHead)
|
||||
drawString(x + xoff, y, printedBody, color)
|
||||
}
|
||||
|
||||
@Throws(SlickException::class)
|
||||
open fun reloadUnihan() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set margin between characters
|
||||
* @param margin
|
||||
*/
|
||||
fun setInterchar(margin: Int) {
|
||||
interchar = margin
|
||||
}
|
||||
|
||||
private fun setBlendModeMul() {
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
|
||||
private fun setBlendModeNormal() {
|
||||
GL11.glDisable(GL11.GL_BLEND)
|
||||
Terrarum.appgc.graphics.setDrawMode(Graphics.MODE_NORMAL)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
lateinit internal var hangulSheet: SpriteSheet
|
||||
lateinit internal var asciiSheet: SpriteSheet
|
||||
lateinit internal var asciiSheetEF: SpriteSheet
|
||||
lateinit internal var runicSheet: SpriteSheet
|
||||
lateinit internal var extASheet: SpriteSheet
|
||||
lateinit internal var extASheetEF: SpriteSheet
|
||||
lateinit internal var kanaSheet: SpriteSheet
|
||||
lateinit internal var cjkPunct: SpriteSheet
|
||||
// static SpriteSheet uniHan;
|
||||
lateinit internal var cyrilic: SpriteSheet
|
||||
lateinit internal var cyrilicEF: SpriteSheet
|
||||
lateinit internal var fullwidthForms: SpriteSheet
|
||||
lateinit internal var uniPunct: SpriteSheet
|
||||
lateinit internal var wenQuanYi_1: SpriteSheet
|
||||
lateinit internal var wenQuanYi_2: SpriteSheet
|
||||
|
||||
internal val JUNG_COUNT = 21
|
||||
internal val JONG_COUNT = 28
|
||||
|
||||
internal val W_CJK = 10
|
||||
internal val W_UNIHAN = 16
|
||||
internal val W_LATIN_WIDE = 9 // width of regular letters, including m
|
||||
internal val W_LATIN_NARROW = 5 // width of letter f, t, i, l
|
||||
internal val H = 20
|
||||
internal val H_HANGUL = 16
|
||||
internal val H_UNIHAN = 16
|
||||
internal val H_KANA = 20
|
||||
|
||||
internal val SHEET_ASCII_EM = 0
|
||||
internal val SHEET_ASCII_EF = 1
|
||||
internal val SHEET_HANGUL = 2
|
||||
internal val SHEET_RUNIC = 3
|
||||
internal val SHEET_EXTA_EM = 4
|
||||
internal val SHEET_EXTA_EF = 5
|
||||
internal val SHEET_KANA = 6
|
||||
internal val SHEET_CJK_PUNCT = 7
|
||||
internal val SHEET_UNIHAN = 8
|
||||
internal val SHEET_CYRILIC_EM = 9
|
||||
internal val SHEET_CYRILIC_EF = 10
|
||||
internal val SHEET_FW_UNI = 11
|
||||
internal val SHEET_UNI_PUNCT = 12
|
||||
internal val SHEET_WENQUANYI_1 = 13
|
||||
internal val SHEET_WENQUANYI_2 = 14
|
||||
|
||||
lateinit internal var sheetKey: Array<SpriteSheet>
|
||||
internal val asciiEFList = arrayOf(' ', '!', '"', '\'', '(', ')', ',', '.', ':', ';', 'I', '[', ']', '`', 'f', 'i', 'j', 'l', 't', '{', '|', '}', 0xA1.toChar(), 'Ì', 'Í', 'Î', 'Ï', 'ì', 'í', 'î', 'ï', '·')
|
||||
|
||||
internal val extAEFList = arrayOf(
|
||||
0x12E.toChar(),
|
||||
0x12F.toChar(),
|
||||
0x130.toChar(),
|
||||
0x131.toChar(),
|
||||
0x135.toChar(),
|
||||
0x13A.toChar(),
|
||||
0x13C.toChar(),
|
||||
0x142.toChar(),
|
||||
0x163.toChar(),
|
||||
0x167.toChar(),
|
||||
0x17F.toChar()
|
||||
)
|
||||
|
||||
internal val cyrilecEFList = arrayOf(
|
||||
0x406.toChar(),
|
||||
0x407.toChar(),
|
||||
0x456.toChar(),
|
||||
0x457.toChar(),
|
||||
0x458.toChar()
|
||||
)
|
||||
|
||||
/**
|
||||
* Runic letters list used for game. The set is
|
||||
* Younger Futhark + Medieval rune 'e' + Punct + Runic Almanac
|
||||
|
||||
* BEWARE OF SIMILAR-LOOKING RUNES, especially:
|
||||
|
||||
* * Algiz ᛉ instead of Maðr ᛘ
|
||||
|
||||
* * Short-Twig Hagall ᚽ instead of Runic Letter E ᛂ
|
||||
|
||||
* * Runic Letter OE ᚯ instead of Óss ᚬ
|
||||
|
||||
* Examples:
|
||||
* ᛭ᛋᛁᚴᚱᛁᚦᛦ᛭
|
||||
* ᛭ᛂᛚᛋᛅ᛭ᛏᚱᚢᛏᚾᛁᚾᚴᚢᚾᛅ᛬ᛅᚱᚾᛅᛏᛅᛚᛋ
|
||||
*/
|
||||
internal val runicList = arrayOf('ᚠ', 'ᚢ', 'ᚦ', 'ᚬ', 'ᚱ', 'ᚴ', 'ᚼ', 'ᚾ', 'ᛁ', 'ᛅ', 'ᛋ', 'ᛏ', 'ᛒ', 'ᛘ', 'ᛚ', 'ᛦ', 'ᛂ', '᛬', '᛫', '᛭', 'ᛮ', 'ᛯ', 'ᛰ')
|
||||
|
||||
internal var interchar = 0
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.torvald.imagefont
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import org.newdawn.slick.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-20.
|
||||
*/
|
||||
class GameFontWhite @Throws(SlickException::class)
|
||||
constructor() : GameFontBase() {
|
||||
|
||||
init {
|
||||
|
||||
GameFontBase.hangulSheet = SpriteSheet(
|
||||
"./res/graphics/fonts/han_johab.png", GameFontBase.W_CJK, GameFontBase.H_HANGUL)
|
||||
GameFontBase.asciiSheet = SpriteSheet(
|
||||
"./res/graphics/fonts/ascii_majuscule.png", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.asciiSheetEF = SpriteSheet(
|
||||
"./res/graphics/fonts/ascii_special_ef.png", GameFontBase.W_LATIN_NARROW, GameFontBase.H)
|
||||
GameFontBase.runicSheet = SpriteSheet(
|
||||
"./res/graphics/fonts/futhark.png", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.extASheet = SpriteSheet(
|
||||
"./res/graphics/fonts/LatinExtA_majuscule.png", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.extASheetEF = SpriteSheet(
|
||||
"./res/graphics/fonts/LatinExtA_ef.png", GameFontBase.W_LATIN_NARROW, GameFontBase.H)
|
||||
GameFontBase.kanaSheet = SpriteSheet(
|
||||
"./res/graphics/fonts/kana.png", GameFontBase.W_CJK, GameFontBase.H_KANA)
|
||||
GameFontBase.cjkPunct = SpriteSheet(
|
||||
"./res/graphics/fonts/cjkpunct.png", GameFontBase.W_CJK, GameFontBase.H_KANA)
|
||||
/*uniHan = new SpriteSheet(
|
||||
"./res/graphics/fonts/unifont_unihan"
|
||||
+ ((!terrarum.gameLocale.contains("zh"))
|
||||
? "_ja" : "")
|
||||
+".png"
|
||||
, W_UNIHAN, H_UNIHAN
|
||||
);*/
|
||||
GameFontBase.cyrilic = SpriteSheet(
|
||||
"./res/graphics/fonts/cyrilic_majuscule.png", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.cyrilicEF = SpriteSheet(
|
||||
"./res/graphics/fonts/cyrilic_ef.png", GameFontBase.W_LATIN_NARROW, GameFontBase.H)
|
||||
GameFontBase.fullwidthForms = SpriteSheet(
|
||||
"./res/graphics/fonts/fullwidth_forms.png", GameFontBase.W_UNIHAN, GameFontBase.H_UNIHAN)
|
||||
GameFontBase.uniPunct = SpriteSheet(
|
||||
"./res/graphics/fonts/unipunct.png", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.wenQuanYi_1 = SpriteSheet(
|
||||
"./res/graphics/fonts/wenquanyi_11pt_part1.png", 16, 18, 2)
|
||||
GameFontBase.wenQuanYi_2 = SpriteSheet(
|
||||
"./res/graphics/fonts/wenquanyi_11pt_part2.png", 16, 18, 2)
|
||||
|
||||
val shk = arrayOf<SpriteSheet>(
|
||||
GameFontBase.asciiSheet,
|
||||
GameFontBase.asciiSheetEF,
|
||||
GameFontBase.hangulSheet,
|
||||
GameFontBase.runicSheet,
|
||||
GameFontBase.extASheet,
|
||||
GameFontBase.extASheetEF,
|
||||
GameFontBase.kanaSheet,
|
||||
GameFontBase.cjkPunct,
|
||||
GameFontBase.asciiSheet, // Filler
|
||||
GameFontBase.cyrilic,
|
||||
GameFontBase.cyrilicEF,
|
||||
GameFontBase.fullwidthForms,
|
||||
GameFontBase.uniPunct,
|
||||
GameFontBase.wenQuanYi_1,
|
||||
GameFontBase.wenQuanYi_2)//, uniHan
|
||||
GameFontBase.sheetKey = shk
|
||||
}
|
||||
|
||||
@Throws(SlickException::class)
|
||||
override fun reloadUnihan() {
|
||||
/*uniHan = new SpriteSheet(
|
||||
"./res/graphics/fonts/unifont_unihan"
|
||||
+ ((!terrarum.gameLocale.contains("zh"))
|
||||
? "_ja" : "")
|
||||
+".png"
|
||||
, W_UNIHAN, H_UNIHAN
|
||||
);*/
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.torvald
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.util.ArrayList
|
||||
import java.util.function.Consumer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-15.
|
||||
*/
|
||||
object JsonFetcher {
|
||||
|
||||
private var jsonString: StringBuffer? = null
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun readJson(jsonFilePath: String): JsonObject {
|
||||
jsonString = StringBuffer() // reset buffer every time it called
|
||||
readJsonFileAsString(jsonFilePath)
|
||||
|
||||
val jsonParser = JsonParser()
|
||||
val jsonObj = jsonParser.parse(jsonString!!.toString()).asJsonObject
|
||||
|
||||
return jsonObj
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readJsonFileAsString(path: String) {
|
||||
Files.lines(FileSystems.getDefault().getPath(path)).forEach(
|
||||
{ jsonString!!.append(it) }
|
||||
) // JSON does not require line break
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.torvald
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-04.
|
||||
*/
|
||||
object JsonWriter {
|
||||
|
||||
/**
|
||||
* serialise a class to the file as JSON, using Google GSON.
|
||||
*
|
||||
* @param c: a class
|
||||
* @param path: path to write a file
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun writeToFile(c: Any, path: String) {
|
||||
val classElem = Gson().toJsonTree(c)
|
||||
val jsonString = classElem.toString()
|
||||
val writer = FileWriter(path)
|
||||
writer.write(jsonString)
|
||||
writer.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* serialise JsonObject to the file as JSON, using Google GSON.
|
||||
*
|
||||
* @param jsonObject
|
||||
* @param path: path to write a file
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun writeToFile(jsonObject: JsonObject, path: String) {
|
||||
val writer = FileWriter(path)
|
||||
writer.write(jsonObject.toString())
|
||||
writer.close()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.torvald.point
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
class Point2f(x: Float, y: Float) {
|
||||
|
||||
var x: Float = 0.toFloat()
|
||||
private set
|
||||
var y: Float = 0.toFloat()
|
||||
private set
|
||||
|
||||
init {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
|
||||
operator fun set(x: Float, y: Float) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.torvald
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
|
||||
import javax.imageio.ImageIO
|
||||
import java.awt.*
|
||||
import java.awt.color.ColorSpace
|
||||
import java.awt.image.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-04.
|
||||
*/
|
||||
object RasterWriter {
|
||||
|
||||
val BANDOFFSET_RGB = intArrayOf(0, 1, 2)
|
||||
val BANDOFFSET_RGBA = intArrayOf(0, 1, 2, 3)
|
||||
val BANDOFFSET_ARGB = intArrayOf(3, 0, 1, 2)
|
||||
val BANDOFFSET_MONO = intArrayOf(0)
|
||||
|
||||
val COLORSPACE_SRGB = ColorSpace.CS_sRGB
|
||||
val COLORSPACE_GRAY = ColorSpace.CS_GRAY
|
||||
val COLORSPACE_GREY = COLORSPACE_GRAY
|
||||
val COLORSPACE_CIEXYZ = ColorSpace.CS_CIEXYZ
|
||||
val COLORSPACE_RGB_LINEAR_GAMMA = ColorSpace.CS_LINEAR_RGB
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun writePNG_RGB(w: Int, h: Int, rasterData: ByteArray, path: String) {
|
||||
writePNG(w, h, rasterData, BANDOFFSET_RGB, COLORSPACE_SRGB, path)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun writePNG_Mono(w: Int, h: Int, rasterData: ByteArray, path: String) {
|
||||
writePNG(w, h, rasterData, BANDOFFSET_MONO, COLORSPACE_GREY, path)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun writePNG(w: Int, h: Int, rasterData: ByteArray, bandOffsets: IntArray, awt_colorspace: Int, path: String) {
|
||||
val buffer = DataBufferByte(rasterData, rasterData.size)
|
||||
val raster = Raster.createInterleavedRaster(
|
||||
buffer, w, h, bandOffsets.size * w, bandOffsets.size, bandOffsets, null)
|
||||
|
||||
val colorModel = ComponentColorModel(ColorSpace.getInstance(awt_colorspace), false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE)
|
||||
|
||||
val image = BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied, null)
|
||||
|
||||
ImageIO.write(image, "PNG", File(path))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.torvald.serialise
|
||||
|
||||
import com.torvald.CSVFetcher
|
||||
import com.torvald.terrarum.itemproperties.ItemPropCodex
|
||||
import com.torvald.terrarum.itemproperties.MaterialPropCodex
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-18.
|
||||
*/
|
||||
object WriteCSV {
|
||||
val META_FILENAME_TILE = "worldinfo2"
|
||||
val META_FILENAME_ITEM = "worldinfo3"
|
||||
val META_FILENAME_MAT = "worldinfo4"
|
||||
|
||||
fun write(saveDirectoryName: String): Boolean {
|
||||
val tileCSV = CSVFetcher.readCSVasString(TilePropCodex.CSV_PATH)
|
||||
val itemCSV = CSVFetcher.readCSVasString(ItemPropCodex.CSV_PATH)
|
||||
val matCSV = CSVFetcher.readCSVasString(MaterialPropCodex.CSV_PATH)
|
||||
|
||||
val pathTile = Paths.get("${Terrarum.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${META_FILENAME_TILE}")
|
||||
val pathItem = Paths.get("${Terrarum.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${META_FILENAME_ITEM}")
|
||||
val pathMat = Paths.get("${Terrarum.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${META_FILENAME_MAT}")
|
||||
val tempPathTile = Files.createTempFile(pathTile.toString(), "_temp")
|
||||
val tempPathItem = Files.createTempFile(pathItem.toString(), "_temp")
|
||||
val tempPathMat = Files.createTempFile(pathMat.toString(), "_temp")
|
||||
|
||||
// TODO gzip
|
||||
|
||||
// write CSV to path
|
||||
Files.write(tempPathTile, tileCSV.toByteArray(Charsets.UTF_8))
|
||||
Files.write(tempPathItem, itemCSV.toByteArray(Charsets.UTF_8))
|
||||
Files.write(tempPathMat, matCSV.toByteArray(Charsets.UTF_8))
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
Files.copy(tempPathTile, pathTile, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPathTile)
|
||||
|
||||
Files.copy(tempPathItem, pathItem, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPathItem)
|
||||
|
||||
Files.copy(tempPathMat, pathMat, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPathMat)
|
||||
|
||||
println("Saved map data '${WriteGameMapData.META_FILENAME}' to $saveDirectoryName.")
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.torvald.serialise
|
||||
|
||||
import com.torvald.terrarum.gamemap.GameMap
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-18.
|
||||
*/
|
||||
object WriteGameMapData {
|
||||
|
||||
val META_FILENAME = "worldinfo1"
|
||||
|
||||
val MAGIC: ByteArray = byteArrayOf(
|
||||
'T'.toByte(),
|
||||
'E'.toByte(),
|
||||
'M'.toByte(),
|
||||
'D'.toByte()
|
||||
)
|
||||
|
||||
val BYTE_NULL: Byte = 0
|
||||
|
||||
|
||||
fun write(saveDirectoryName: String): Boolean {
|
||||
val path = Paths.get("${Terrarum.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${WriteMeta.META_FILENAME}")
|
||||
val tempPath = Files.createTempFile(path.toString(), "_temp")
|
||||
val map = Terrarum.game.map
|
||||
|
||||
// TODO gzip
|
||||
|
||||
// write binary
|
||||
Files.write(tempPath, MAGIC)
|
||||
Files.write(tempPath, byteArrayOf(GameMap.BITS))
|
||||
Files.write(tempPath, byteArrayOf(GameMap.LAYERS))
|
||||
Files.write(tempPath, byteArrayOf(BYTE_NULL))
|
||||
Files.write(tempPath, byteArrayOf(BYTE_NULL))
|
||||
Files.write(tempPath, toByteArray(map.width))
|
||||
Files.write(tempPath, toByteArray(map.height))
|
||||
Files.write(tempPath, toByteArray(map.spawnX))
|
||||
Files.write(tempPath, toByteArray(map.spawnY))
|
||||
map.layerTerrain.forEach(
|
||||
{b -> Files.write(tempPath, byteArrayOf(b))})
|
||||
map.layerWall.forEach(
|
||||
{b -> Files.write(tempPath, byteArrayOf(b))})
|
||||
map.terrainDamage.forEach(
|
||||
{b -> Files.write(tempPath, byteArrayOf(b))})
|
||||
map.wallDamage.forEach(
|
||||
{b -> Files.write(tempPath, byteArrayOf(b))})
|
||||
map.layerWire.forEach(
|
||||
{b -> Files.write(tempPath, byteArrayOf(b))})
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
Files.copy(tempPath, path, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPath)
|
||||
println("Saved map data '$META_FILENAME' to $saveDirectoryName.")
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun toByteArray(int: Int): ByteArray {
|
||||
return byteArrayOf(
|
||||
((int ushr 0x18) and 0xFF).toByte(),
|
||||
((int ushr 0x10) and 0xFF).toByte(),
|
||||
((int ushr 0x08) and 0xFF).toByte(),
|
||||
((int ) and 0xFF).toByte()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.torvald.serialise
|
||||
|
||||
import com.torvald.terrarum.mapgenerator.MapGenerator
|
||||
import com.torvald.terrarum.mapgenerator.RoguelikeRandomiser
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
import java.io.FileInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object WriteMeta {
|
||||
|
||||
val META_FILENAME = "world"
|
||||
|
||||
val MAGIC: ByteArray = byteArrayOf(
|
||||
'T'.toByte(),
|
||||
'E'.toByte(),
|
||||
'S'.toByte(),
|
||||
'V'.toByte()
|
||||
)
|
||||
|
||||
val BYTE_NULL: Byte = 0
|
||||
|
||||
val terraseed: Long = MapGenerator.SEED
|
||||
val rogueseed: Long = RoguelikeRandomiser.seed
|
||||
|
||||
/**
|
||||
* Write save meta to specified directory. Returns false if something went wrong.
|
||||
* @param saveDirectoryName
|
||||
* @param savegameName -- Nullable. If the value is not specified, saveDirectoryName will be used instead.
|
||||
*/
|
||||
fun write(saveDirectoryName: String, savegameName: String?): Boolean {
|
||||
val hashArray: ArrayList<ByteArray> = ArrayList()
|
||||
val savenameAsByteArray: ByteArray =
|
||||
(savegameName ?: saveDirectoryName).toByteArray(Charsets.UTF_8)
|
||||
|
||||
// define files to get hash
|
||||
val fileArray: Array<File> = arrayOf(
|
||||
File(TilePropCodex.CSV_PATH)
|
||||
//, File(ItemPropCodex.CSV_PATH)
|
||||
//, File(MaterialPropCodex.CSV_PATH)
|
||||
//,
|
||||
)
|
||||
|
||||
// get and store hash from fileArray
|
||||
for (file in fileArray) {
|
||||
val inputStream = FileInputStream(file)
|
||||
val hash = DigestUtils.sha256(inputStream)
|
||||
|
||||
hashArray.add(hash)
|
||||
}
|
||||
|
||||
// open file and delete it
|
||||
val metaPath = Paths.get("${Terrarum.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/$META_FILENAME")
|
||||
val metaTempPath = Files.createTempFile(metaPath.toString(), "_temp")
|
||||
|
||||
// TODO gzip
|
||||
|
||||
// write bytes in tempfile
|
||||
Files.write(metaTempPath, MAGIC)
|
||||
Files.write(metaTempPath, savenameAsByteArray)
|
||||
Files.write(metaTempPath, byteArrayOf(BYTE_NULL))
|
||||
Files.write(metaTempPath, toByteArray(terraseed))
|
||||
Files.write(metaTempPath, toByteArray(rogueseed))
|
||||
for (hash in hashArray)
|
||||
Files.write(metaTempPath, hash)
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
Files.copy(metaTempPath, metaPath, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(metaTempPath)
|
||||
println("Saved metadata to $saveDirectoryName.")
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun toByteArray(long: Long): ByteArray {
|
||||
return byteArrayOf(
|
||||
(long.ushr(0x38) and 0xFF).toByte(),
|
||||
(long.ushr(0x30) and 0xFF).toByte(),
|
||||
(long.ushr(0x28) and 0xFF).toByte(),
|
||||
(long.ushr(0x20) and 0xFF).toByte(),
|
||||
(long.ushr(0x18) and 0xFF).toByte(),
|
||||
(long.ushr(0x10) and 0xFF).toByte(),
|
||||
(long.ushr(0x08) and 0xFF).toByte(),
|
||||
(long and 0xFF).toByte()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.torvald.simplecipher
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-20.
|
||||
*/
|
||||
object ROTUtil {
|
||||
|
||||
const val CODE_CAP_A = 'A'.toInt()
|
||||
const val CODE_LOW_A = 'a'.toInt()
|
||||
|
||||
/**
|
||||
* ROT encryption, default setting
|
||||
*
|
||||
* * Latin alph: removes diacritics and do ROT13, string reverse, capitalised.
|
||||
* Ligatures are disassembled. Even if the target language does not have
|
||||
* certain alphabet (e.g. C in Icelandic), such alphabet will be printed anyway.
|
||||
*
|
||||
* * Cyrillic: removes diacritics and do ROTnn, string reverse, capitalised.
|
||||
*
|
||||
* * Kana: raise Sutegana, ROT3 on vowels (a i u e o -> e o a i u), string reverse
|
||||
* (Wa Wo -> Wo Wa), Nn will remain untouched, forced Katakana.
|
||||
*
|
||||
* * Hangul: ROT3 on initials\*, lower double initials to single
|
||||
* (ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ -> ㄹ ㄹ ㅁ ㅂ ㅂ ㅅ ㅇ ㅇ ㅈ ㅈ ㅊ ㅋ ㅋ ㅌ ㅍ ㅎ ㄱ ㄴ ㄷ),
|
||||
* string reverse
|
||||
*
|
||||
* * Chinese: NOT SUPPORTED
|
||||
*
|
||||
* * Numeric: no encrypt
|
||||
*/
|
||||
fun encrypt(plaintext: String): String {
|
||||
|
||||
fun removeDiacritics(c: Char): Char {
|
||||
val normaliseMap = hashMapOf(
|
||||
Pair('À', "A"),
|
||||
Pair('Á', "A"),
|
||||
Pair('Â', "A"),
|
||||
Pair('À', "A"),
|
||||
Pair('Ã', "A"),
|
||||
Pair('Å', "A"),
|
||||
Pair('Æ', "AE"),
|
||||
Pair('Ç', "C"),
|
||||
Pair('È', "E"),
|
||||
Pair('É', "E"),
|
||||
Pair('Ê', "E"),
|
||||
Pair('Ë', "E"),
|
||||
Pair('Ì', "I"),
|
||||
Pair('Í', "I"),
|
||||
Pair('Î', "I"),
|
||||
Pair('Ï', "I"),
|
||||
Pair('Ð', "D")
|
||||
)
|
||||
throw NotImplementedError("Feature WIP")
|
||||
}
|
||||
|
||||
throw NotImplementedError("Feature WIP")
|
||||
}
|
||||
|
||||
/**
|
||||
* Note; range starts with zero
|
||||
* @param number to rotate
|
||||
* @param rotation
|
||||
* @param range size of the rotation table. 4 means (0,1,2,3)
|
||||
*/
|
||||
fun rotN(number: Int, rotation: Int, range: Int): Int {
|
||||
return if (number < range - rotation + 1) number + rotation
|
||||
else number - (range - rotation + 1)
|
||||
}
|
||||
|
||||
fun rot13(c: Char): Char {
|
||||
return if (c in 'a'..'z')
|
||||
(rotN((c.toInt() - CODE_LOW_A), 13, 26) + CODE_LOW_A).toChar()
|
||||
else if (c in 'A'..'Z')
|
||||
(rotN((c.toInt() - CODE_CAP_A), 13, 26) + CODE_CAP_A).toChar()
|
||||
else c
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.torvald.terrarum
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
object DefaultConfig {
|
||||
fun fetch(): JsonObject {
|
||||
val jsonObject = JsonObject()
|
||||
|
||||
jsonObject.addProperty("smoothlighting", true)
|
||||
jsonObject.addProperty("imtooyoungtodie", false)
|
||||
jsonObject.addProperty("language", Terrarum.sysLang)
|
||||
jsonObject.addProperty("notificationshowuptime", 6500)
|
||||
|
||||
return jsonObject
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
package com.torvald.terrarum
|
||||
|
||||
import com.torvald.terrarum.gameactors.*
|
||||
import com.torvald.terrarum.console.Authenticator
|
||||
import com.torvald.terrarum.gamecontroller.GameController
|
||||
import com.torvald.terrarum.gamecontroller.Key
|
||||
import com.torvald.terrarum.gamecontroller.KeyMap
|
||||
import com.torvald.terrarum.gamecontroller.KeyToggler
|
||||
import com.torvald.terrarum.gamemap.GameMap
|
||||
import com.torvald.terrarum.gamemap.WorldTime
|
||||
import com.torvald.terrarum.mapdrawer.LightmapRenderer
|
||||
import com.torvald.terrarum.mapdrawer.MapCamera
|
||||
import com.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import com.torvald.terrarum.mapgenerator.MapGenerator
|
||||
import com.torvald.terrarum.mapgenerator.RoguelikeRandomiser
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.torvald.terrarum.tilestats.TileStats
|
||||
import com.torvald.terrarum.ui.BasicDebugInfoWindow
|
||||
import com.torvald.terrarum.ui.ConsoleWindow
|
||||
import com.torvald.terrarum.ui.Notification
|
||||
import com.torvald.terrarum.ui.UIHandler
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.newdawn.slick.*
|
||||
import org.newdawn.slick.fills.GradientFill
|
||||
import org.newdawn.slick.geom.Rectangle
|
||||
import org.newdawn.slick.state.BasicGameState
|
||||
import org.newdawn.slick.state.StateBasedGame
|
||||
import shader.Shader
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
class Game @Throws(SlickException::class)
|
||||
constructor() : BasicGameState() {
|
||||
internal var game_mode = 0
|
||||
|
||||
lateinit var map: GameMap
|
||||
|
||||
val actorContainer = LinkedList<Actor>()
|
||||
val uiContainer = LinkedList<UIHandler>()
|
||||
|
||||
lateinit var consoleHandler: UIHandler
|
||||
lateinit var debugWindow: UIHandler
|
||||
lateinit var notifinator: UIHandler
|
||||
|
||||
lateinit internal var player: Player
|
||||
|
||||
private var GRADIENT_IMAGE: Image? = null
|
||||
private var skyBox: Rectangle? = null
|
||||
|
||||
var screenZoom = 1.0f
|
||||
val ZOOM_MAX = 2.0f
|
||||
val ZOOM_MIN = 0.25f
|
||||
|
||||
private lateinit var shader12BitCol: Shader
|
||||
private lateinit var shaderBlurH: Shader
|
||||
private lateinit var shaderBlurV: Shader
|
||||
|
||||
private val useShader: Boolean = false
|
||||
private val shaderProgram = 0
|
||||
|
||||
|
||||
val memInUse: Long
|
||||
get() = ManagementFactory.getMemoryMXBean().heapMemoryUsage.used shr 20
|
||||
val totalVMMem: Long
|
||||
get() = Runtime.getRuntime().maxMemory() shr 20
|
||||
|
||||
val auth = Authenticator()
|
||||
|
||||
private var update_delta: Int = 0
|
||||
|
||||
val KEY_LIGHTMAP_RENDER = Key.F7
|
||||
val KEY_LIGHTMAP_SMOOTH = Key.F8
|
||||
|
||||
@Throws(SlickException::class)
|
||||
override fun init(gameContainer: GameContainer, stateBasedGame: StateBasedGame) {
|
||||
KeyMap.build()
|
||||
|
||||
shader12BitCol = Shader.makeShader("./res/4096.vrt", "./res/4096.frg")
|
||||
shaderBlurH = Shader.makeShader("./res/blurH.vrt", "./res/blur.frg")
|
||||
shaderBlurV = Shader.makeShader("./res/blurV.vrt", "./res/blur.frg")
|
||||
|
||||
|
||||
GRADIENT_IMAGE = Image("res/graphics/sky_colour.png")
|
||||
skyBox = Rectangle(0f, 0f, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat())
|
||||
|
||||
TilePropCodex()
|
||||
// new ItemPropCodex() -- This is kotlin object and already initialised.
|
||||
|
||||
map = GameMap(8192, 2048)
|
||||
map.gravitation = 9.8f
|
||||
|
||||
MapGenerator.attachMap(map)
|
||||
MapGenerator.SEED = 0x51621D2
|
||||
//mapgenerator.setSeed(new HQRNG().nextLong());
|
||||
MapGenerator.generateMap()
|
||||
|
||||
RoguelikeRandomiser.seed = 0x540198
|
||||
//RoguelikeRandomiser.setSeed(new HQRNG().nextLong());
|
||||
|
||||
|
||||
// add new player and put it to actorContainer
|
||||
player = PFSigrid.create()
|
||||
//player = PFCynthia.create()
|
||||
//player.setNoClip(true);
|
||||
addActor(player)
|
||||
|
||||
consoleHandler = UIHandler(ConsoleWindow())
|
||||
consoleHandler.setPosition(0, 0)
|
||||
|
||||
debugWindow = UIHandler(BasicDebugInfoWindow())
|
||||
debugWindow.setPosition(0, 0)
|
||||
|
||||
notifinator = UIHandler(Notification())
|
||||
notifinator.setPosition(
|
||||
(Terrarum.WIDTH - notifinator.UI.width) / 2, Terrarum.HEIGHT - notifinator.UI.height)
|
||||
notifinator.setVisibility(true)
|
||||
|
||||
if (Terrarum.gameConfig.getAsBoolean("smoothlighting") == true)
|
||||
KeyToggler.forceSet(KEY_LIGHTMAP_SMOOTH, true)
|
||||
else
|
||||
KeyToggler.forceSet(KEY_LIGHTMAP_SMOOTH, false)
|
||||
}
|
||||
|
||||
override fun update(gc: GameContainer, sbg: StateBasedGame, delta: Int) {
|
||||
update_delta = delta
|
||||
setAppTitle()
|
||||
|
||||
// GL at after_sunrise-noon_before_sunset
|
||||
map.updateWorldTime(delta)
|
||||
map.globalLight = globalLightByTime
|
||||
|
||||
GameController.processInput(gc.input)
|
||||
|
||||
TileStats.update()
|
||||
|
||||
MapDrawer.update(gc, delta)
|
||||
MapCamera.update(gc, delta)
|
||||
|
||||
actorContainer.forEach { actor -> actor.update(gc, delta) }
|
||||
actorContainer.forEach { actor ->
|
||||
if (actor is Visible) {
|
||||
actor.updateBodySprite(gc, delta)
|
||||
}
|
||||
if (actor is Glowing) {
|
||||
actor.updateGlowSprite(gc, delta)
|
||||
}
|
||||
}
|
||||
|
||||
uiContainer.forEach { ui -> ui.update(gc, delta) }
|
||||
consoleHandler.update(gc, delta)
|
||||
debugWindow.update(gc, delta)
|
||||
|
||||
|
||||
notifinator.update(gc, delta)
|
||||
|
||||
Terrarum.appgc.setVSync(Terrarum.appgc.fps >= Terrarum.VSYNC_TRIGGER_THRESHOLD)
|
||||
}
|
||||
|
||||
private fun setAppTitle() {
|
||||
Terrarum.appgc.setTitle(
|
||||
"Simple Slick Game — FPS: "
|
||||
+ Terrarum.appgc.fps + " ("
|
||||
+ Terrarum.TARGET_INTERNAL_FPS.toString()
|
||||
+ ") — "
|
||||
+ memInUse.toString() + "M / "
|
||||
+ totalVMMem.toString() + "M")
|
||||
}
|
||||
|
||||
override fun render(gc: GameContainer, sbg: StateBasedGame, g: Graphics) {
|
||||
setBlendNormal()
|
||||
|
||||
Terrarum.gameConfig["smoothlighting"] = KeyToggler.isOn(KEY_LIGHTMAP_SMOOTH)
|
||||
|
||||
if (!g.isAntiAlias) g.isAntiAlias = true
|
||||
|
||||
drawSkybox(g)
|
||||
|
||||
// compensate for zoom. UIs have to be treated specially! (see UIHandler)
|
||||
g.translate(
|
||||
-MapCamera.cameraX * screenZoom, -MapCamera.cameraY * screenZoom)
|
||||
|
||||
MapCamera.renderBehind(gc, g)
|
||||
|
||||
actorContainer.forEach { actor -> if (actor is Visible) actor.drawBody(gc, g) }
|
||||
player.drawBody(gc, g)
|
||||
|
||||
LightmapRenderer.renderLightMap()
|
||||
|
||||
MapCamera.renderFront(gc, g)
|
||||
MapDrawer.render(gc, g)
|
||||
|
||||
setBlendMul()
|
||||
|
||||
MapDrawer.drawEnvOverlay(g)
|
||||
|
||||
if (!KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) setBlendMul() else setBlendNormal()
|
||||
LightmapRenderer.draw(g)
|
||||
|
||||
setBlendNormal()
|
||||
|
||||
actorContainer.forEach { actor -> if (actor is Glowing) actor.drawGlow(gc, g) }
|
||||
player.drawGlow(gc, g)
|
||||
|
||||
uiContainer.forEach { ui -> ui.render(gc, g) }
|
||||
debugWindow.render(gc, g)
|
||||
consoleHandler.render(gc, g)
|
||||
notifinator.render(gc, g)
|
||||
}
|
||||
|
||||
private fun getGradientColour(row: Int): Color {
|
||||
val gradMapWidth = GRADIENT_IMAGE!!.width
|
||||
val phase = Math.round(
|
||||
map.worldTime.elapsedSeconds().toFloat() / WorldTime.DAY_LENGTH.toFloat() * gradMapWidth
|
||||
)
|
||||
|
||||
//update in every INTERNAL_FRAME frames
|
||||
return GRADIENT_IMAGE!!.getColor(phase, row)
|
||||
}
|
||||
|
||||
override fun keyPressed(key: Int, c: Char) {
|
||||
GameController.keyPressed(key, c)
|
||||
}
|
||||
|
||||
override fun keyReleased(key: Int, c: Char) {
|
||||
GameController.keyReleased(key, c)
|
||||
}
|
||||
|
||||
override fun mouseMoved(oldx: Int, oldy: Int, newx: Int, newy: Int) {
|
||||
GameController.mouseMoved(oldx, oldy, newx, newy)
|
||||
}
|
||||
|
||||
override fun mouseDragged(oldx: Int, oldy: Int, newx: Int, newy: Int) {
|
||||
GameController.mouseDragged(oldx, oldy, newx, newy)
|
||||
}
|
||||
|
||||
override fun mousePressed(button: Int, x: Int, y: Int) {
|
||||
GameController.mousePressed(button, x, y)
|
||||
}
|
||||
|
||||
override fun mouseReleased(button: Int, x: Int, y: Int) {
|
||||
GameController.mouseReleased(button, x, y)
|
||||
}
|
||||
|
||||
override fun mouseWheelMoved(change: Int) {
|
||||
GameController.mouseWheelMoved(change)
|
||||
}
|
||||
|
||||
override fun controllerButtonPressed(controller: Int, button: Int) {
|
||||
GameController.controllerButtonPressed(controller, button)
|
||||
}
|
||||
|
||||
override fun controllerButtonReleased(controller: Int, button: Int) {
|
||||
GameController.controllerButtonReleased(controller, button)
|
||||
}
|
||||
|
||||
override fun getID(): Int {
|
||||
return Terrarum.SCENE_ID_GAME
|
||||
}
|
||||
|
||||
private fun drawSkybox(g: Graphics) {
|
||||
val skyColourFill = GradientFill(
|
||||
0f, 0f, getGradientColour(0),
|
||||
0f, Terrarum.HEIGHT.toFloat(), getGradientColour(1)
|
||||
)
|
||||
g.fill(skyBox, skyColourFill)
|
||||
}
|
||||
|
||||
fun sendNotification(msg: Array<String>) {
|
||||
(notifinator.UI as Notification).sendNotification(Terrarum.appgc, update_delta, msg)
|
||||
notifinator.setAsOpening()
|
||||
}
|
||||
|
||||
private val globalLightByTime: Int
|
||||
get() = getGradientColour(2).getRGB24().rgb24ExpandToRgb30()
|
||||
|
||||
/**
|
||||
* extension function for org.newdawn.slick.Color
|
||||
*/
|
||||
fun Color.getRGB24(): Int = (this.redByte shl 16) or (this.greenByte shl 8) or (this.blueByte)
|
||||
fun Int.rgb24ExpandToRgb30(): Int = (this and 0xff) or
|
||||
(this and 0xff00).ushr(8).shl(10) or
|
||||
(this and 0xff0000).ushr(16).shl(20)
|
||||
|
||||
/**
|
||||
* actorContainer extensions
|
||||
*/
|
||||
fun hasActor(ID: Long): Boolean {
|
||||
for (actor in actorContainer) {
|
||||
if (actor.referenceID == ID) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun removeActor(ID: Long) {
|
||||
for (actor in actorContainer) {
|
||||
if (actor.referenceID == ID)
|
||||
actorContainer.remove(actor)
|
||||
}
|
||||
}
|
||||
|
||||
fun addActor(other: Actor): Boolean {
|
||||
if (hasActor(other.referenceID)) return false
|
||||
actorContainer.add(other)
|
||||
return true
|
||||
}
|
||||
|
||||
fun getActor(ID: Long): Actor {
|
||||
for (actor in actorContainer) {
|
||||
if (actor.referenceID == ID)
|
||||
return actor
|
||||
}
|
||||
throw NullPointerException("Actor with ID $ID does not exist.")
|
||||
}
|
||||
|
||||
fun addUI(other: UIHandler): Boolean {
|
||||
if (uiContainer.contains(other)) return false
|
||||
uiContainer.add(other)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.torvald.terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
class GameConfig : KVHashMap()
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.torvald.terrarum.gameitem
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
interface InventoryItem {
|
||||
/**
|
||||
* Internal ID of an Item, Long
|
||||
* 0-4096: Tiles
|
||||
* 4097-32767: Various items
|
||||
* >=32768: Actor RefID
|
||||
*/
|
||||
var itemID: Long
|
||||
|
||||
/**
|
||||
* Weight of the item, Float
|
||||
*/
|
||||
var mass: Float
|
||||
|
||||
/**
|
||||
* Scale of the item. Real mass: mass * (scale^3)
|
||||
*/
|
||||
var scale: Float
|
||||
|
||||
/**
|
||||
* Effects applied while in pocket
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun effectWhileInPocket(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied immediately only once if picked up
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun effectWhenPickedUp(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied while primary button (usually left mouse button) is down
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun primaryUse(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied while secondary button (usually right mouse button) is down
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun secondaryUse(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied immediately only once if thrown from pocket
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun effectWhenThrownAway(gc: GameContainer, delta_t: Int)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.torvald.terrarum.gameitem
|
||||
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import org.newdawn.slick.GameContainer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
class TileAsItem(tileNum: Int) : InventoryItem {
|
||||
|
||||
override var itemID: Long = -1
|
||||
override var mass: Float = 0f
|
||||
override var scale: Float = 1f
|
||||
|
||||
init {
|
||||
itemID = tileNum.toLong()
|
||||
mass = TilePropCodex.getProp(tileNum).density / 1000f
|
||||
}
|
||||
|
||||
override fun effectWhileInPocket(gc: GameContainer, delta_t: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun effectWhenPickedUp(gc: GameContainer, delta_t: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun primaryUse(gc: GameContainer, delta_t: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun secondaryUse(gc: GameContainer, delta_t: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun effectWhenThrownAway(gc: GameContainer, delta_t: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
|
||||
package com.torvald.terrarum.gamemap
|
||||
|
||||
import org.newdawn.slick.SlickException
|
||||
|
||||
class GameMap
|
||||
/**
|
||||
* @param width
|
||||
* *
|
||||
* @param height
|
||||
* *
|
||||
* @throws SlickException
|
||||
*/
|
||||
@Throws(SlickException::class)
|
||||
constructor(//properties
|
||||
val width: Int, val height: Int) {
|
||||
|
||||
//layers
|
||||
val layerWall: MapLayer
|
||||
/**
|
||||
* Get MapLayer object of terrain
|
||||
|
||||
* @return MapLayer terrain layer
|
||||
*/
|
||||
val layerTerrain: MapLayer
|
||||
val layerWire: MapLayer
|
||||
val wallDamage: PairedMapLayer
|
||||
val terrainDamage: PairedMapLayer
|
||||
val spawnX: Int
|
||||
val spawnY: Int
|
||||
|
||||
//public World physWorld = new World( new Vec2(0, -TerrarumMain.game.gravitationalAccel) );
|
||||
//physics
|
||||
/** \[m / s^2\] */
|
||||
var gravitation: Float = 9.8.toFloat()
|
||||
/** RGB in Integer */
|
||||
var globalLight: Int = 0
|
||||
val worldTime: WorldTime
|
||||
|
||||
init {
|
||||
this.spawnX = width / 2
|
||||
this.spawnY = 200
|
||||
|
||||
layerTerrain = MapLayer(width, height)
|
||||
layerWall = MapLayer(width, height)
|
||||
layerWire = MapLayer(width, height)
|
||||
terrainDamage = PairedMapLayer(width, height)
|
||||
wallDamage = PairedMapLayer(width, height)
|
||||
|
||||
globalLight = 0.toChar().toInt()
|
||||
worldTime = WorldTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 2d array data of terrain
|
||||
|
||||
* @return byte[][] terrain layer
|
||||
*/
|
||||
val terrainArray: Array<ByteArray>
|
||||
get() = layerTerrain.data
|
||||
|
||||
/**
|
||||
* Get 2d array data of wall
|
||||
|
||||
* @return byte[][] wall layer
|
||||
*/
|
||||
val wallArray: Array<ByteArray>
|
||||
get() = layerWall.data
|
||||
|
||||
/**
|
||||
* Get 2d array data of wire
|
||||
|
||||
* @return byte[][] wire layer
|
||||
*/
|
||||
val wireArray: Array<ByteArray>
|
||||
get() = layerWire.data
|
||||
|
||||
/**
|
||||
* Get paired array data of damage codes.
|
||||
* Format: 0baaaabbbb, aaaa for x = 0, 2, 4, ..., bbbb for x = 1, 3, 5, ...
|
||||
* @return byte[][] damage code pair
|
||||
*/
|
||||
val damageDataArray: Array<ByteArray>
|
||||
get() = terrainDamage.dataPair
|
||||
|
||||
fun getTileFromWall(x: Int, y: Int): Int? {
|
||||
val wall: Int? = layerWall.getTile(x, y)
|
||||
val wallDamage: Int? = getWallDamage(x, y)
|
||||
return if (wall == null || wallDamage == null)
|
||||
null
|
||||
else
|
||||
wall * PairedMapLayer.RANGE + wallDamage
|
||||
}
|
||||
|
||||
fun getTileFromTerrain(x: Int, y: Int): Int? {
|
||||
val terrain: Int? = layerTerrain.getTile(x, y)
|
||||
val terrainDamage: Int? = getTerrainDamage(x, y)
|
||||
return if (terrain == null || terrainDamage == null)
|
||||
null
|
||||
else
|
||||
terrain * PairedMapLayer.RANGE + terrainDamage
|
||||
}
|
||||
|
||||
fun getTileFromWire(x: Int, y: Int): Int? {
|
||||
return layerWire.getTile(x, y)
|
||||
}
|
||||
|
||||
fun getWallDamage(x: Int, y: Int): Int? {
|
||||
return wallDamage.getData(x, y)
|
||||
}
|
||||
|
||||
fun getTerrainDamage(x: Int, y: Int): Int? {
|
||||
return terrainDamage.getData(x, y)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tile of wall as specified, with damage value of zero.
|
||||
* @param x
|
||||
* *
|
||||
* @param y
|
||||
* *
|
||||
* @param combinedTilenum (tilenum * 16) + damage
|
||||
*/
|
||||
fun setTileWall(x: Int, y: Int, combinedTilenum: Int) {
|
||||
setTileWall(x, y, (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tile of wall as specified, with damage value of zero.
|
||||
* @param x
|
||||
* *
|
||||
* @param y
|
||||
* *
|
||||
* @param combinedTilenum (tilenum * 16) + damage
|
||||
*/
|
||||
fun setTileTerrain(x: Int, y: Int, combinedTilenum: Int) {
|
||||
setTileTerrain(x, y, (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
|
||||
}
|
||||
|
||||
fun setTileWall(x: Int, y: Int, tile: Byte, damage: Int) {
|
||||
layerWall.setTile(x, y, tile)
|
||||
wallDamage.setData(x, y, damage)
|
||||
}
|
||||
|
||||
fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) {
|
||||
layerTerrain.setTile(x, y, tile)
|
||||
terrainDamage.setData(x, y, damage)
|
||||
}
|
||||
|
||||
fun setTileWire(x: Int, y: Int, tile: Byte) {
|
||||
layerWire.data[y][x] = tile
|
||||
}
|
||||
|
||||
fun getTileFrom(mode: Int, x: Int, y: Int): Int? {
|
||||
if (mode == TERRAIN) {
|
||||
return getTileFromTerrain(x, y)
|
||||
}
|
||||
else if (mode == WALL) {
|
||||
return getTileFromWall(x, y)
|
||||
}
|
||||
else if (mode == WIRE) {
|
||||
return getTileFromWire(x, y)
|
||||
}
|
||||
else
|
||||
throw IllegalArgumentException("illegal mode input: " + mode.toString())
|
||||
}
|
||||
|
||||
fun updateWorldTime(delta: Int) {
|
||||
worldTime.update(delta)
|
||||
}
|
||||
|
||||
fun terrainIterator(): Iterator<Int> {
|
||||
return object : Iterator<Int> {
|
||||
|
||||
private var iteratorCount = 0
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return iteratorCount < width * height
|
||||
}
|
||||
|
||||
override fun next(): Int {
|
||||
val y = iteratorCount / width
|
||||
val x = iteratorCount % width
|
||||
// advance counter
|
||||
iteratorCount += 1
|
||||
|
||||
return getTileFromTerrain(x, y)!!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun wallIterator(): Iterator<Int> {
|
||||
return object : Iterator<Int> {
|
||||
|
||||
private var iteratorCount = 0
|
||||
|
||||
override fun hasNext(): Boolean =
|
||||
iteratorCount < width * height
|
||||
|
||||
override fun next(): Int {
|
||||
val y = iteratorCount / width
|
||||
val x = iteratorCount % width
|
||||
// advance counter
|
||||
iteratorCount += 1
|
||||
|
||||
return getTileFromWall(x, y)!!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@Transient val WALL = 0
|
||||
@Transient val TERRAIN = 1
|
||||
@Transient val WIRE = 2
|
||||
|
||||
@Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE
|
||||
@Transient val BITS: Byte = 1 // 1 for Byte, 2 for Char, 4 for Int, 8 for Long
|
||||
@Transient val LAYERS: Byte = 4 // terrain, wall (terrainDamage + wallDamage), wire
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.torvald.terrarum.gamemap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-17.
|
||||
*/
|
||||
class MapLayer(var width: Int, var height: Int) : Iterable<Byte> {
|
||||
|
||||
internal var data: Array<ByteArray>
|
||||
|
||||
init {
|
||||
data = Array(height) { ByteArray(width) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over elements of type `T`.
|
||||
|
||||
* @return an Iterator.
|
||||
*/
|
||||
override fun iterator(): Iterator<Byte> {
|
||||
return object : Iterator<Byte> {
|
||||
|
||||
private var iteratorCount = 0
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return iteratorCount < width * height
|
||||
}
|
||||
|
||||
override fun next(): Byte {
|
||||
val y = iteratorCount / width
|
||||
val x = iteratorCount % width
|
||||
// advance counter
|
||||
iteratorCount += 1
|
||||
|
||||
return data[y][x]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getTile(x: Int, y: Int): Int? {
|
||||
return if (x !in 0..width - 1 || y !in 0..height - 1)
|
||||
null
|
||||
else
|
||||
uint8ToInt32(data[y][x])
|
||||
}
|
||||
|
||||
internal fun setTile(x: Int, y: Int, tile: Byte) {
|
||||
data[y][x] = tile
|
||||
}
|
||||
|
||||
private fun uint8ToInt32(x: Byte): Int = java.lang.Byte.toUnsignedInt(x)
|
||||
|
||||
companion object {
|
||||
@Transient val RANGE = 256
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.torvald.terrarum.gamemap
|
||||
|
||||
import com.torvald.point.Point2f
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
|
||||
class MapPoint {
|
||||
var startPoint: Point2f? = null
|
||||
private set
|
||||
var endPoint: Point2f? = null
|
||||
private set
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
constructor(p1: Point2f, p2: Point2f) {
|
||||
setPoint(p1, p2)
|
||||
}
|
||||
|
||||
constructor(x1: Int, y1: Int, x2: Int, y2: Int) {
|
||||
setPoint(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
fun setPoint(p1: Point2f, p2: Point2f) {
|
||||
startPoint = p1
|
||||
endPoint = p2
|
||||
}
|
||||
|
||||
fun setPoint(x1: Int, y1: Int, x2: Int, y2: Int) {
|
||||
startPoint = Point2f(x1.toFloat(), y1.toFloat())
|
||||
endPoint = Point2f(x2.toFloat(), y2.toFloat())
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.torvald.terrarum.gamemap
|
||||
|
||||
import java.io.Serializable
|
||||
import java.util.Spliterator
|
||||
import java.util.function.Consumer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-15.
|
||||
*/
|
||||
class PairedMapLayer(width: Int, var height: Int) : Iterable<Byte> {
|
||||
|
||||
/**
|
||||
* 0b_xxxx_yyyy, x for lower index, y for higher index
|
||||
|
||||
* e.g.
|
||||
|
||||
* 0110 1101 is interpreted as
|
||||
* 6 for tile 0, 13 for tile 1.
|
||||
*/
|
||||
internal var dataPair: Array<ByteArray>
|
||||
|
||||
var width: Int = 0
|
||||
|
||||
init {
|
||||
this.width = width / 2
|
||||
|
||||
dataPair = Array(height) { ByteArray(width / 2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over elements of type `T`.
|
||||
* Note: this iterator will return combined damage, that is 0bxxxx_yyyy as whole.
|
||||
|
||||
* @return an Iterator.
|
||||
*/
|
||||
override fun iterator(): Iterator<Byte> {
|
||||
return object : Iterator<Byte> {
|
||||
|
||||
private var iteratorCount = 0
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return iteratorCount < width * height
|
||||
}
|
||||
|
||||
override fun next(): Byte {
|
||||
val y = iteratorCount / width
|
||||
val x = iteratorCount % width
|
||||
// advance counter
|
||||
iteratorCount += 1
|
||||
|
||||
return dataPair[y][x]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getData(x: Int, y: Int): Int? {
|
||||
return if (x !in 0..width * 2 - 1 || y !in 0..height - 1)
|
||||
null
|
||||
else {
|
||||
if (x and 0x1 == 0)
|
||||
// higher four bits for i = 0, 2, 4, ...
|
||||
(java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0xF0) ushr 4
|
||||
else
|
||||
// lower four bits for i = 1, 3, 5, ...
|
||||
java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0x0F
|
||||
}
|
||||
}
|
||||
|
||||
internal fun setData(x: Int, y: Int, data: Int) {
|
||||
if (data < 0 || data >= 16) throw IllegalArgumentException("[PairedMapLayer] $data: invalid data value.")
|
||||
if (x and 0x1 == 0)
|
||||
// higher four bits for i = 0, 2, 4, ...
|
||||
dataPair[y][x / 2] =
|
||||
(java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0x0F
|
||||
or (data and 0xF shl 4)).toByte()
|
||||
else
|
||||
// lower four bits for i = 1, 3, 5, ...
|
||||
dataPair[y][x / 2] = (java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0xF0
|
||||
or (data and 0xF)).toByte()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@Transient val RANGE = 16
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package com.torvald.terrarum.gamemap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-24.
|
||||
*/
|
||||
class WorldTime {
|
||||
internal var seconds: Int
|
||||
internal var minutes: Int
|
||||
internal var hours: Int
|
||||
|
||||
internal var yearlyDays: Int //NOT a calendar day
|
||||
|
||||
internal var days: Int
|
||||
internal var months: Int
|
||||
internal var years: Int
|
||||
|
||||
internal var dayOfWeek: Int //0: Mondag-The first day of weekday
|
||||
|
||||
internal var timeDelta = 1
|
||||
|
||||
@Transient private var realMillisec: Int
|
||||
|
||||
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
|
||||
"Mondag", "Tysdag", "Midtedag" //From Islenska Miðvikudagur
|
||||
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verdag" //From Norsk word 'verd'
|
||||
)
|
||||
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
|
||||
|
||||
|
||||
@Transient val REAL_SEC_IN_MILLI = 1000
|
||||
|
||||
init {
|
||||
seconds = 0
|
||||
minutes = 30
|
||||
hours = 8
|
||||
yearlyDays = 1
|
||||
days = 12
|
||||
months = 3
|
||||
years = 125
|
||||
dayOfWeek = 0
|
||||
realMillisec = 0
|
||||
}
|
||||
|
||||
fun update(delta: Int) {
|
||||
//time
|
||||
realMillisec += delta * timeDelta
|
||||
seconds = Math.round(GAME_MIN_TO_REAL_SEC.toFloat() / REAL_SEC_IN_MILLI.toFloat() * realMillisec.toFloat())
|
||||
|
||||
if (realMillisec >= REAL_SEC_IN_MILLI)
|
||||
realMillisec -= REAL_SEC_IN_MILLI
|
||||
|
||||
kickVariables()
|
||||
}
|
||||
|
||||
/**
|
||||
* How much time has passed today, in seconds.
|
||||
* 0 == 6 AM
|
||||
* @return
|
||||
*/
|
||||
fun elapsedSeconds(): Int {
|
||||
return (HOUR_SEC * hours + MINUTE_SEC * minutes + seconds) % DAY_LENGTH
|
||||
}
|
||||
|
||||
val isLeapYear: Boolean
|
||||
get() = years % 4 == 0 && years % 100 != 0 || years % 400 == 0
|
||||
|
||||
fun setTime(t: Int) {
|
||||
days += t / DAY_LENGTH
|
||||
hours = t / HOUR_SEC
|
||||
minutes = (t - HOUR_SEC * hours) / MINUTE_SEC
|
||||
seconds = t - minutes * MINUTE_SEC
|
||||
}
|
||||
|
||||
fun addTime(t: Int) {
|
||||
setTime(elapsedSeconds() + t)
|
||||
}
|
||||
|
||||
fun setTimeDelta(d: Int) {
|
||||
timeDelta = if (d < 0) 0 else d
|
||||
}
|
||||
|
||||
val dayName: String
|
||||
get() = DAY_NAMES[dayOfWeek]
|
||||
|
||||
private fun kickVariables() {
|
||||
if (seconds >= MINUTE_SEC) {
|
||||
seconds = 0
|
||||
minutes += 1
|
||||
}
|
||||
|
||||
if (minutes >= HOUR_MIN) {
|
||||
minutes = 0
|
||||
hours += 1
|
||||
}
|
||||
|
||||
if (hours >= DAY_LENGTH / HOUR_SEC) {
|
||||
hours = 0
|
||||
days += 1
|
||||
yearlyDays += 1
|
||||
dayOfWeek += 1
|
||||
}
|
||||
|
||||
//calendar (the world calendar)
|
||||
if (dayOfWeek == 7) {
|
||||
dayOfWeek = 0
|
||||
}
|
||||
if ((months == 12 || months == 7 && isLeapYear) && days == 31) {
|
||||
dayOfWeek = 7
|
||||
}
|
||||
|
||||
if ((months == 12 || months == 7 && isLeapYear) && days == 32) {
|
||||
days = 1
|
||||
months = 1
|
||||
years++
|
||||
}
|
||||
else if ((months == 1 || months == 4 || months == 7 || months == 10) && days > 31) {
|
||||
days = 1
|
||||
months++
|
||||
}
|
||||
else if (days > 30) {
|
||||
days = 1
|
||||
months++
|
||||
}
|
||||
|
||||
if (months > 12) {
|
||||
months = 1
|
||||
years++
|
||||
}
|
||||
}
|
||||
|
||||
fun getFormattedTime(): String {
|
||||
fun formatMin(min: Int): String {
|
||||
return if (min < 10) "0${min.toString()}" else min.toString()
|
||||
}
|
||||
|
||||
return "${hours}h${formatMin(minutes)}"
|
||||
}
|
||||
|
||||
fun getDayNameFull(): String = DAY_NAMES[dayOfWeek]
|
||||
fun getDayNameShort(): String = DAY_NAMES_SHORT[dayOfWeek]
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 22h
|
||||
*/
|
||||
val DAY_LENGTH = 79200 //must be the multiple of 3600
|
||||
|
||||
val HOUR_SEC: Int = 3600
|
||||
val MINUTE_SEC: Int = 60
|
||||
val HOUR_MIN: Int = 60
|
||||
val GAME_MIN_TO_REAL_SEC: Float = 60f
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.torvald.terrarum.itemproperties
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-18.
|
||||
*/
|
||||
internal data class ItemProp (
|
||||
var baseMass: Float,
|
||||
var material: Material
|
||||
)
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.torvald.terrarum.itemproperties
|
||||
|
||||
import com.torvald.terrarum.gameactors.CanBeAnItem
|
||||
import com.torvald.terrarum.gameitem.InventoryItem
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import org.newdawn.slick.GameContainer
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object ItemPropCodex {
|
||||
|
||||
val CSV_PATH = "./src/com/torvald/terrarum/itemproperties/itemprop.csv"
|
||||
|
||||
/**
|
||||
* <ItemID or RefID for Actor, TheItem>
|
||||
* Will return corresponding Actor if ID >= 32768
|
||||
*/
|
||||
private lateinit var itemCodex: Array<InventoryItem>
|
||||
|
||||
@JvmStatic val ITEM_UNIQUE_MAX = 32768
|
||||
|
||||
@JvmStatic
|
||||
fun buildItemProp() {
|
||||
itemCodex = arrayOf<InventoryItem>()
|
||||
|
||||
// read prop in csv
|
||||
|
||||
}
|
||||
|
||||
fun getItem(code: Long): InventoryItem {
|
||||
if (code < ITEM_UNIQUE_MAX)
|
||||
return itemCodex[(code and 0xFFFFFFFF).toInt()]
|
||||
else {
|
||||
for (actor in Terrarum.game.actorContainer) {
|
||||
if (actor is CanBeAnItem && actor.referenceID == code)
|
||||
return actor.itemData
|
||||
}
|
||||
|
||||
throw NullPointerException()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.torvald.terrarum.itemproperties
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-18.
|
||||
*/
|
||||
internal data class Material (
|
||||
var maxEdge: Int,
|
||||
var hardness: Int,
|
||||
var density: Int
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.torvald.terrarum.itemproperties
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
object MaterialFactory {
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.torvald.terrarum.itemproperties
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-18.
|
||||
*/
|
||||
object MaterialPropCodex {
|
||||
val CSV_PATH = "./src/com/torvald/terrarum/itemproperties/materialprop.csv"
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.torvald.terrarum
|
||||
|
||||
import com.google.gson.JsonPrimitive
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
open class KVHashMap {
|
||||
|
||||
private val hashMap: HashMap<String, Any>
|
||||
|
||||
init {
|
||||
hashMap = HashMap<String, Any>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add key-value pair to the configuration table.
|
||||
* If key does not exist on the table, new key will be generated.
|
||||
* If key already exists, the value will be overwritten.
|
||||
|
||||
* @param key case insensitive
|
||||
* *
|
||||
* @param value
|
||||
*/
|
||||
operator fun set(key: String, value: Any) {
|
||||
hashMap.put(key.toLowerCase(), value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value using key from configuration table.
|
||||
|
||||
* @param key case insensitive
|
||||
* *
|
||||
* @return Object value
|
||||
*/
|
||||
operator fun get(key: String): Any? {
|
||||
return hashMap[key.toLowerCase()]
|
||||
}
|
||||
|
||||
fun getAsInt(key: String): Int? {
|
||||
val value = get(key)
|
||||
|
||||
if (value is JsonPrimitive)
|
||||
return value.asInt
|
||||
|
||||
return get(key) as Int?
|
||||
}
|
||||
|
||||
fun getAsFloat(key: String): Float? {
|
||||
val value = get(key)
|
||||
|
||||
if (value is Int)
|
||||
return value.toFloat()
|
||||
else if (value is JsonPrimitive)
|
||||
return value.asFloat
|
||||
|
||||
return value as Float?
|
||||
}
|
||||
|
||||
fun getAsDouble(key: String): Double? {
|
||||
val value = get(key)
|
||||
|
||||
if (value is Int)
|
||||
return value.toDouble()
|
||||
else if (value is JsonPrimitive)
|
||||
return value.asDouble
|
||||
|
||||
return value as Double?
|
||||
}
|
||||
|
||||
fun getAsString(key: String): String? {
|
||||
val value = get(key)
|
||||
|
||||
if (value is JsonPrimitive)
|
||||
return value.asString
|
||||
|
||||
return value as String?
|
||||
}
|
||||
|
||||
fun getAsBoolean(key: String): Boolean? {
|
||||
val value = get(key)
|
||||
|
||||
if (value is JsonPrimitive)
|
||||
return value.asBoolean
|
||||
|
||||
return value as Boolean?
|
||||
}
|
||||
|
||||
fun hasKey(key: String): Boolean {
|
||||
return hashMap.containsKey(key)
|
||||
}
|
||||
|
||||
val keySet: Set<Any>
|
||||
get() = hashMap.keys
|
||||
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
package com.torvald.terrarum.langpack
|
||||
|
||||
import com.torvald.CSVFetcher
|
||||
import com.torvald.imagefont.GameFontWhite
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import org.apache.commons.csv.CSVRecord
|
||||
import org.newdawn.slick.SlickException
|
||||
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-22.
|
||||
*/
|
||||
object Lang {
|
||||
|
||||
private val CSV_COLUMN_FIRST = "STRING_ID"
|
||||
/**
|
||||
* Get record by its STRING_ID
|
||||
*/
|
||||
private var lang: HashMap<String, CSVRecord>
|
||||
private val FALLBACK_LANG_CODE = "enUS"
|
||||
|
||||
private val HANGUL_SYL_START = 0xAC00
|
||||
|
||||
private val PATH_TO_CSV = "./res/locales/"
|
||||
private val CSV_MAIN = "polyglot.csv"
|
||||
private val NAMESET_PREFIX = "nameset_"
|
||||
|
||||
private val HANGUL_POST_INDEX_ALPH = intArrayOf(// 0: 는, 가, ... 1: 은, 이, ...
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
private val HANGUL_POST_RO_INDEX_ALPH = intArrayOf(// 0: 로 1: 으로
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
private val ENGLISH_WORD_NORMAL_PLURAL = arrayOf("photo")
|
||||
|
||||
private val FRENCH_WORD_NORMAL_PLURAL = arrayOf("bal", "banal", "fatal", "final")
|
||||
|
||||
init {
|
||||
lang = HashMap<String, CSVRecord>()
|
||||
|
||||
// append CSV records to the main langpack
|
||||
val file = File(PATH_TO_CSV)
|
||||
val filter = FilenameFilter { dir, name -> name.contains(".csv") && !name.contains(NAMESET_PREFIX) }
|
||||
for (csvfilename in file.list(filter)) {
|
||||
val csv = CSVFetcher.readCSV(PATH_TO_CSV + csvfilename)
|
||||
//csv.forEach({ langPackCSV. })
|
||||
csv.forEach { it -> lang.put(it.get(CSV_COLUMN_FIRST), it) }
|
||||
}
|
||||
|
||||
// sort word lists
|
||||
Arrays.sort(ENGLISH_WORD_NORMAL_PLURAL)
|
||||
Arrays.sort(FRENCH_WORD_NORMAL_PLURAL)
|
||||
|
||||
// reload correct (C/J) unihan fonts if applicable
|
||||
try {
|
||||
(Terrarum.gameFont as GameFontWhite).reloadUnihan()
|
||||
}
|
||||
catch (e: SlickException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getRecord(key: String): CSVRecord {
|
||||
val record = lang[key]
|
||||
if (record == null) {
|
||||
println("[Lang] No such record: $key")
|
||||
throw NullPointerException()
|
||||
}
|
||||
return record
|
||||
}
|
||||
|
||||
operator fun get(key: String): String {
|
||||
fun fallback(): String = lang[key]!!.get(FALLBACK_LANG_CODE)
|
||||
|
||||
var value: String
|
||||
try {
|
||||
value = lang[key]!!.get(Terrarum.gameLocale)
|
||||
// fallback if empty string
|
||||
if (value.length == 0)
|
||||
value = fallback()
|
||||
}
|
||||
catch (e1: kotlin.KotlinNullPointerException) {
|
||||
value = "ERRNULL:$key"
|
||||
}
|
||||
catch (e: IllegalArgumentException) {
|
||||
//value = key
|
||||
value = fallback()
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
fun pluraliseLang(key: String, count: Int): String {
|
||||
return if (count > 1) get(key + "_PLURAL") else get(key)
|
||||
}
|
||||
|
||||
fun pluralise(word: String, count: Int): String {
|
||||
if (count < 2) return word
|
||||
|
||||
when (Terrarum.gameLocale) {
|
||||
"fr" -> {
|
||||
if (Arrays.binarySearch(FRENCH_WORD_NORMAL_PLURAL, word) >= 0) {
|
||||
return word + "s"
|
||||
}
|
||||
if (word.endsWith("al") || word.endsWith("au") || word.endsWith("eu") || word.endsWith("eau")) {
|
||||
return word.substring(0, word.length - 2) + "ux"
|
||||
}
|
||||
else if (word.endsWith("ail")) {
|
||||
return word.substring(0, word.length - 3) + "ux"
|
||||
}
|
||||
else {
|
||||
return word + "s"
|
||||
}
|
||||
}
|
||||
"en" -> {
|
||||
if (Arrays.binarySearch(ENGLISH_WORD_NORMAL_PLURAL, word) >= 0) {
|
||||
return word + "s"
|
||||
}
|
||||
else if (word.endsWith("f")) {
|
||||
// f -> ves
|
||||
return word.substring(0, word.length - 2) + "ves"
|
||||
}
|
||||
else if (word.endsWith("o") || word.endsWith("z")) {
|
||||
// o -> oes
|
||||
return word + "es"
|
||||
}
|
||||
else {
|
||||
return word + "s"
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (Arrays.binarySearch(ENGLISH_WORD_NORMAL_PLURAL, word) >= 0) {
|
||||
return word + "s"
|
||||
}
|
||||
else if (word.endsWith("f")) {
|
||||
return word.substring(0, word.length - 2) + "ves"
|
||||
}
|
||||
else if (word.endsWith("o") || word.endsWith("z")) {
|
||||
return word + "es"
|
||||
}
|
||||
else {
|
||||
return word + "s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun postEunNeun(word: String): String {
|
||||
val lastChar = getLastChar(word)
|
||||
|
||||
if (isHangul(lastChar)) {
|
||||
val index = lastChar.toInt() - HANGUL_SYL_START
|
||||
return if (index % 28 == 0) word + "는" else word + "은"
|
||||
}
|
||||
else if (lastChar >= 'A' && lastChar <= 'Z' || lastChar >= 'a' && lastChar <= 'z') {
|
||||
val index = (lastChar.toInt() - 0x41) % 0x20
|
||||
return if (HANGUL_POST_INDEX_ALPH[index] == 0) word + "는" else word + "은"
|
||||
}
|
||||
else {
|
||||
return "은(는)"
|
||||
}
|
||||
}
|
||||
|
||||
fun postIiGa(word: String): String {
|
||||
val lastChar = getLastChar(word)
|
||||
|
||||
if (isHangul(lastChar)) {
|
||||
val index = lastChar.toInt() - HANGUL_SYL_START
|
||||
return if (index % 28 == 0) word + "가" else word + "이"
|
||||
}
|
||||
else if (lastChar >= 'A' && lastChar <= 'Z' || lastChar >= 'a' && lastChar <= 'z') {
|
||||
val index = (lastChar.toInt() - 0x41) % 0x20
|
||||
return if (HANGUL_POST_INDEX_ALPH[index] == 0) word + "가" else word + "이"
|
||||
}
|
||||
else {
|
||||
return "이(가)"
|
||||
}
|
||||
}
|
||||
|
||||
private fun isHangul(c: Char): Boolean {
|
||||
return c.toInt() >= 0xAC00 && c.toInt() <= 0xD7A3
|
||||
}
|
||||
|
||||
private fun getLastChar(s: String): Char {
|
||||
return s[s.length - 1]
|
||||
}
|
||||
|
||||
private fun appendToLangByStringID(record: CSVRecord) {
|
||||
lang.put(record.get(CSV_COLUMN_FIRST), record)
|
||||
}
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
package com.torvald.terrarum.mapdrawer
|
||||
|
||||
import com.torvald.terrarum.gameactors.ActorWithBody
|
||||
import com.torvald.terrarum.gameactors.Luminous
|
||||
import com.torvald.terrarum.gamemap.WorldTime
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.jme3.math.FastMath
|
||||
import com.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 = div16(MapCamera.cameraX) - 1 // fix for premature lightmap rendering
|
||||
val for_y_start = div16(MapCamera.cameraY) - 1 // on topmost/leftmost side
|
||||
|
||||
val for_x_end = for_x_start + div16(MapCamera.getRenderWidth()) + 3
|
||||
val for_y_end = for_y_start + div16(MapCamera.getRenderHeight()) + 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 = div16(MapCamera.cameraX) - 1 // fix for premature lightmap rendering
|
||||
val for_y_start = div16(MapCamera.cameraY) - 1 // on topmost/leftmost side
|
||||
|
||||
val for_x_end = for_x_start + div16(MapCamera.getRenderWidth()) + 3
|
||||
val for_y_end = for_y_start + div16(MapCamera.getRenderHeight()) + 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 quantise16(x: Int): Int {
|
||||
if (x < 0) throw IllegalArgumentException("positive integer only.")
|
||||
return x and 0xFFFFFFF0.toInt()
|
||||
}
|
||||
|
||||
private fun div16(x: Int): Int {
|
||||
if (x < 0) throw IllegalArgumentException("positive integer only.")
|
||||
return x and 0x7FFFFFFF shr 4
|
||||
}
|
||||
|
||||
private fun mul16(x: Int): Int {
|
||||
if (x < 0) throw IllegalArgumentException("positive integer only.")
|
||||
return x shl 4
|
||||
}
|
||||
|
||||
private fun max(vararg i: Int): Int {
|
||||
Arrays.sort(i)
|
||||
return i[i.size - 1]
|
||||
}
|
||||
|
||||
private fun min(vararg i: Int): Int {
|
||||
Arrays.sort(i)
|
||||
return i[0]
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
package com.torvald.terrarum.mapdrawer
|
||||
|
||||
import com.torvald.terrarum.gamemap.GameMap
|
||||
import com.torvald.terrarum.gamemap.PairedMapLayer
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.tileproperties.TileNameCode
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.jme3.math.FastMath
|
||||
import com.torvald.terrarum.setBlendMul
|
||||
import com.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)
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.torvald.terrarum.mapdrawer
|
||||
|
||||
import com.torvald.terrarum.gamemap.GameMap
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.tileproperties.TileNameCode
|
||||
import com.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
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
import com.sun.javaws.exceptions.InvalidArgumentException
|
||||
|
||||
import java.util.Random
|
||||
|
||||
object FloatingIslandsPreset {
|
||||
|
||||
val PRESETS = 5
|
||||
|
||||
internal fun generatePreset(random: HQRNG): Array<IntArray> {
|
||||
val index = random.nextInt(PRESETS)
|
||||
return generatePreset(index, random)
|
||||
}
|
||||
|
||||
internal fun generatePreset(index: Int, random: Random): Array<IntArray> {
|
||||
if (index == 0) {
|
||||
return processPreset(random, FloatingIslePreset01.data, FloatingIslePreset01.w, FloatingIslePreset01.h)
|
||||
}
|
||||
else if (index == 1) {
|
||||
return processPreset(random, FloatingIslePreset02.data, FloatingIslePreset02.w, FloatingIslePreset02.h)
|
||||
}
|
||||
else if (index == 2) {
|
||||
return processPreset(random, FloatingIslePreset03.data, FloatingIslePreset03.w, FloatingIslePreset03.h)
|
||||
}
|
||||
else if (index == 3) {
|
||||
return processPreset(random, FloatingIslePreset04.data, FloatingIslePreset04.w, FloatingIslePreset04.h)
|
||||
}
|
||||
else {
|
||||
return processPreset(random, FloatingIslePreset05.data, FloatingIslePreset05.w, FloatingIslePreset05.h)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processPreset(random: Random, preset: IntArray, w: Int, h: Int): Array<IntArray> {
|
||||
val temp = Array(h) { IntArray(w) }
|
||||
var counter = 0
|
||||
val mirrored = random.nextBoolean()
|
||||
|
||||
for (i in 0..h - 1) {
|
||||
for (j in 0..w - 1) {
|
||||
if (!mirrored) {
|
||||
if (counter < preset.size - 1) {
|
||||
temp[i][j] = preset[counter]
|
||||
counter++
|
||||
}
|
||||
else {
|
||||
temp[i][j] = 0
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (counter < preset.size - 1) {
|
||||
temp[i][w - 1 - j] = preset[counter]
|
||||
counter++
|
||||
}
|
||||
else {
|
||||
temp[i][w - 1 - j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,948 +0,0 @@
|
||||
package com.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
import com.torvald.terrarum.gamemap.GameMap
|
||||
import com.torvald.terrarum.tileproperties.TileNameCode
|
||||
import com.jme3.math.FastMath
|
||||
import com.sudoplay.joise.Joise
|
||||
import com.sudoplay.joise.module.*
|
||||
import java.util.*
|
||||
|
||||
object MapGenerator {
|
||||
|
||||
private lateinit var map: GameMap
|
||||
private lateinit var random: Random
|
||||
//private static float[] noiseArray;
|
||||
var SEED: Long = 0
|
||||
var WIDTH: Int = 0
|
||||
var HEIGHT: Int = 0
|
||||
|
||||
//private lateinit var heightMap: IntArray
|
||||
private lateinit var terrainMap: Array<BitSet>
|
||||
|
||||
var DIRT_LAYER_DEPTH: Int = 0
|
||||
var TERRAIN_AVERAGE_HEIGHT: Int = 0
|
||||
private var minimumFloatingIsleHeight: Int = 0
|
||||
|
||||
private val NOISE_GRAD_START = 0.67f
|
||||
private val NOISE_GRAD_END = 0.56f
|
||||
|
||||
private val NOISE_SIMPLEX_ORE_START = 1.42f
|
||||
private val NOISE_SIMPLEX_ORE_END = 1.28f
|
||||
|
||||
private val HILL_WIDTH = 256 // power of two!
|
||||
//private val MAX_HILL_HEIGHT = 100
|
||||
private val TERRAIN_UNDULATION = 250
|
||||
|
||||
private val SIMPLEXGEN_LARGEST_FEATURE = 200
|
||||
|
||||
private var OCEAN_WIDTH = 400
|
||||
private var SHORE_WIDTH = 120
|
||||
private val MAX_OCEAN_DEPTH = 200
|
||||
|
||||
private var GLACIER_MOUNTAIN_WIDTH = 900
|
||||
private val GLACIER_MOUNTAIN_HEIGHT = 300
|
||||
|
||||
private val CAVEGEN_THRE_START = 0.95f
|
||||
private val CAVEGEN_THRE_END = 0.67f
|
||||
|
||||
|
||||
private var worldOceanPosition: Int = -1
|
||||
private val TYPE_OCEAN_LEFT = 0
|
||||
private val TYPE_OCEAN_RIGHT = 1
|
||||
|
||||
private val GRASSCUR_UP = 0
|
||||
private val GRASSCUR_RIGHT = 1
|
||||
private val GRASSCUR_DOWN = 2
|
||||
private val GRASSCUR_LEFT = 3
|
||||
|
||||
private val TILE_MACRO_ALL = -1
|
||||
|
||||
fun attachMap(map: GameMap) {
|
||||
this.map = map
|
||||
WIDTH = map.width
|
||||
HEIGHT = map.height
|
||||
|
||||
val widthMulFactor = WIDTH / 8192f
|
||||
|
||||
DIRT_LAYER_DEPTH = (100 * HEIGHT / 1024f).toInt()
|
||||
minimumFloatingIsleHeight = (25 * (HEIGHT / 1024f)).toInt()
|
||||
TERRAIN_AVERAGE_HEIGHT = HEIGHT / 4
|
||||
|
||||
OCEAN_WIDTH = Math.round(OCEAN_WIDTH * widthMulFactor)
|
||||
SHORE_WIDTH = Math.round(SHORE_WIDTH * widthMulFactor)
|
||||
GLACIER_MOUNTAIN_WIDTH = Math.round(GLACIER_MOUNTAIN_WIDTH * widthMulFactor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate terrain and override attached map
|
||||
*/
|
||||
fun generateMap() {
|
||||
random = HQRNG(SEED)
|
||||
println("[mapgenerator] Seed: " + SEED)
|
||||
|
||||
worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT
|
||||
|
||||
//heightMap = raise2(MAX_HILL_HEIGHT / 2)
|
||||
//generateOcean(heightMap)
|
||||
//placeGlacierMount(heightMap)
|
||||
//heightMapToObjectMap(heightMap)
|
||||
|
||||
|
||||
terrainMap = raise3()
|
||||
|
||||
|
||||
fillMapByNoiseMap()
|
||||
|
||||
/**
|
||||
* Done: more perturbed overworld (harder to supra-navigate)
|
||||
* Todo: veined ore distribution (metals) -- use veined simplex noise
|
||||
* Todo: clustered gem distribution (clusters: [Ruby, Sapphire], Amethyst, Yellow topaz, emerald, diamond) -- use regular simplex noise
|
||||
* Todo: Lakes! Aquifers! Lava chambers!
|
||||
* Todo: deserts (variants: SAND_DESERT, SAND_RED)
|
||||
* Todo: volcano(es?)
|
||||
* Done: variants of beach (SAND, SAND_BEACH, SAND_BLACK, SAND_GREEN)
|
||||
*/
|
||||
|
||||
val noiseArray = arrayOf(
|
||||
TaggedJoise("Carving caves", noiseRidged(1.7f, 1.4f), 1f, TILE_MACRO_ALL, TILE_MACRO_ALL, TileNameCode.AIR, NoiseFilterSqrt, CAVEGEN_THRE_START, CAVEGEN_THRE_END)
|
||||
, TaggedJoise("Collapsing caves", noiseBlobs(0.5f, 0.5f), 0.3f, TileNameCode.AIR, TileNameCode.STONE, TileNameCode.STONE, NoiseFilterUniform)
|
||||
|
||||
//, TaggedJoise("Putting stone patches on the ground", noiseBlobs(0.8f, 0.8f), 1.02f, TileNameCode.DIRT, TileNameCode.DIRT, TileNameCode.STONE, NoiseFilterQuadratic, noiseGradientEnd, noiseGradientStart)
|
||||
//, TaggedJoise("Placing dirt spots in the cave", noiseBlobs(0.5f, 0.5f), 0.98f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.DIRT, NoiseFilterQuadratic, noiseGradientEnd, noiseGradientStart)
|
||||
//, TaggedJoise("Quarrying some stone into gravels", noiseBlobs(0.5f, 0.5f), 0.98f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.GRAVEL, NoiseFilterQuadratic, noiseGradientEnd, noiseGradientStart)
|
||||
|
||||
//, TaggedJoise("Growing copper veins", noiseRidged(1.7f, 1.7f), 1.68f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_COPPER)
|
||||
//, TaggedJoise("Cutting copper veins", noiseBlobs(0.4f, 0.4f), 0.26f, TileNameCode.ORE_COPPER, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
//, TaggedJoise("Growing iron veins", noiseRidged(1.7f, 1.7f), 1.68f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_IRON)
|
||||
//, TaggedJoise("Cutting iron veins", noiseBlobs(0.7f, 0.7f), 0.26f, TileNameCode.ORE_IRON, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
//, TaggedJoise("Growing silver veins", noiseRidged(1.7f, 1.7f), 1.71f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_SILVER)
|
||||
//, TaggedJoise("Cutting silver veins", noiseBlobs(0.7f, 0.7f), 0.26f, TileNameCode.ORE_SILVER, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
//, TaggedJoise("Growing gold veins", noiseRidged(1.7f, 1.7f), 1.73f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.ORE_GOLD)
|
||||
//, TaggedJoise("Cutting gold veins", noiseBlobs(0.7f, 0.7f), 0.26f, TileNameCode.ORE_GOLD, TileNameCode.STONE, TileNameCode.STONE)
|
||||
|
||||
////, TaggedJoise("Growing topaz clusters", noiseBlobs(0.9f, 0.9f), 2f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_TOPAZ)
|
||||
//, TaggedJoise("Growing aluminium oxide clusters", noiseBlobs(0.9f, 0.9f), 1.7f, TileNameCode.STONE, TileNameCode.STONE, intArrayOf(TileNameCode.RAW_RUBY, TileNameCode.RAW_SAPPHIRE))
|
||||
//, TaggedJoise("Growing emerald clusters", noiseBlobs(0.9f, 0.9f), 1,7f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_EMERALD)
|
||||
//, TaggedJoise("Growing hearts of white", noiseBlobs(0.9f, 0.9f), 1.83f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_DIAMOND)
|
||||
|
||||
//, TaggedJoise("Growing hearts of violet", noiseRidged(2.5f, 2.5f), 1.75f, TileNameCode.STONE, TileNameCode.STONE, TileNameCode.RAW_AMETHYST)
|
||||
//, TaggedJoise("Cutting over-grown hearts", noiseBlobs(0.7f, 0.7f), 0.17f, TileNameCode.RAW_AMETHYST, TileNameCode.STONE, TileNameCode.STONE)
|
||||
)
|
||||
|
||||
processNoiseLayers(noiseArray)
|
||||
|
||||
/** TODO Cobaltite, Ilmenite, Aurichalcum (and possibly pitchblende?) */
|
||||
|
||||
floodBottomLava()
|
||||
// freeze()
|
||||
// fillOcean()
|
||||
plantGrass()
|
||||
|
||||
//post-process
|
||||
generateFloatingIslands()
|
||||
|
||||
//wire layer
|
||||
for (i in 0..HEIGHT - 1) {
|
||||
for (j in 0..WIDTH - 1) {
|
||||
map.wireArray[i][j] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Free some memories
|
||||
System.gc()
|
||||
}
|
||||
|
||||
/* 1. Raise */
|
||||
|
||||
private fun noiseRidged(xStretch: Float, yStretch: Float): Joise {
|
||||
val ridged = ModuleFractal()
|
||||
ridged.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
||||
ridged.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
ridged.setNumOctaves(4)
|
||||
ridged.setFrequency(1.0)
|
||||
ridged.seed = SEED xor random.nextLong()
|
||||
|
||||
val ridged_autocorrect = ModuleAutoCorrect()
|
||||
ridged_autocorrect.setRange(0.0, 1.0)
|
||||
ridged_autocorrect.setSource(ridged)
|
||||
|
||||
val ridged_scale = ModuleScaleDomain()
|
||||
ridged_scale.setScaleX(xStretch.toDouble())
|
||||
ridged_scale.setScaleY(yStretch.toDouble())
|
||||
ridged_scale.setSource(ridged_autocorrect)
|
||||
|
||||
return Joise(ridged_scale)
|
||||
}
|
||||
|
||||
private fun noiseBlobs(xStretch: Float, yStretch: Float): Joise {
|
||||
val gradval = ModuleBasisFunction()
|
||||
gradval.seed = SEED xor random.nextLong()
|
||||
gradval.setType(ModuleBasisFunction.BasisType.GRADVAL)
|
||||
gradval.setInterpolation(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
|
||||
val gradval_scale = ModuleScaleDomain()
|
||||
gradval_scale.setScaleX(1.0 / xStretch)
|
||||
gradval_scale.setScaleY(1.0 / yStretch)
|
||||
gradval_scale.setSource(gradval)
|
||||
|
||||
return Joise(gradval_scale)
|
||||
}
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* * Threshold 1.4 for rarer gem clusters, 1.35 for ores
|
||||
*/
|
||||
private fun noiseSimplex(xStretch: Float, yStretch: Float): Joise {
|
||||
val simplex = ModuleFractal()
|
||||
simplex.seed = SEED
|
||||
simplex.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.SIMPLEX)
|
||||
simplex.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.LINEAR)
|
||||
simplex.setNumOctaves(2)
|
||||
simplex.setFrequency(1.0)
|
||||
|
||||
val simplex_scale = ModuleScaleDomain()
|
||||
simplex_scale.setScaleX(1.0 / xStretch)
|
||||
simplex_scale.setScaleY(1.0 / yStretch)
|
||||
simplex_scale.setSource(simplex)
|
||||
|
||||
return Joise(simplex_scale)
|
||||
}
|
||||
|
||||
private fun generateOcean(noiseArrayLocal: IntArray): IntArray {
|
||||
val oceanLeftP1 = noiseArrayLocal[OCEAN_WIDTH]
|
||||
val oceanRightP1 = noiseArrayLocal[noiseArrayLocal.size - OCEAN_WIDTH]
|
||||
|
||||
/**
|
||||
* Add ocean so that:
|
||||
|
||||
* +1| - -
|
||||
* 0| - -- ...
|
||||
* -1|______ -
|
||||
|
||||
* interpolated to
|
||||
|
||||
* +1| - -
|
||||
* 0| _--- -- ...
|
||||
* -1|__- -
|
||||
|
||||
* ↑-- Rough, white noise
|
||||
|
||||
* -1 means -MAX_HILL_HEIGHT
|
||||
*/
|
||||
for (i in 0..OCEAN_WIDTH - 1) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
noiseArrayLocal[i] = Math.round(
|
||||
interpolateCosine(
|
||||
i.toFloat() / OCEAN_WIDTH, (-MAX_OCEAN_DEPTH).toFloat(), oceanLeftP1.toFloat()))
|
||||
} else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
noiseArrayLocal[noiseArrayLocal.size - OCEAN_WIDTH + i] = Math.round(
|
||||
interpolateCosine(
|
||||
i.toFloat() / OCEAN_WIDTH, oceanRightP1.toFloat(), (-MAX_OCEAN_DEPTH).toFloat()))
|
||||
} else {
|
||||
throw RuntimeException("Ocean position were not set correctly.")
|
||||
}
|
||||
}
|
||||
|
||||
return noiseArrayLocal
|
||||
}
|
||||
|
||||
/**
|
||||
* http://accidentalnoise.sourceforge.net/minecraftworlds.html
|
||||
*/
|
||||
private fun raise3(): Array<BitSet> {
|
||||
val noiseMap = Array(HEIGHT, { BitSet(WIDTH) })
|
||||
|
||||
// Height = Terrain undulation times 2.
|
||||
val SCALE_X: Double = (TERRAIN_UNDULATION * 0.5).toDouble()
|
||||
val SCALE_Y: Double = (TERRAIN_UNDULATION * 0.25).toDouble()
|
||||
|
||||
val ground_gradient = ModuleGradient()
|
||||
ground_gradient.setGradient(0.0, 0.0, 0.0, 1.0)
|
||||
|
||||
/* Lowlands */
|
||||
|
||||
val lowland_shape_fractal = ModuleFractal()
|
||||
lowland_shape_fractal.setType(ModuleFractal.FractalType.FBM)
|
||||
lowland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
lowland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
lowland_shape_fractal.setNumOctaves(4)
|
||||
lowland_shape_fractal.setFrequency(0.6)
|
||||
lowland_shape_fractal.seed = SEED xor random.nextLong()
|
||||
//println(lowland_shape_fractal.seed)
|
||||
|
||||
val lowland_autocorrect = ModuleAutoCorrect()
|
||||
lowland_autocorrect.setRange(0.0, 1.0)
|
||||
lowland_autocorrect.setSource(lowland_shape_fractal)
|
||||
|
||||
val lowland_scale = ModuleScaleOffset()
|
||||
lowland_scale.setSource(lowland_autocorrect)
|
||||
lowland_scale.setScale(0.8)
|
||||
lowland_scale.setOffset(-2.75)
|
||||
|
||||
val lowland_y_scale = ModuleScaleDomain()
|
||||
lowland_y_scale.setSource(lowland_scale)
|
||||
lowland_y_scale.setScaleY(0.0)
|
||||
|
||||
val lowland_terrain = ModuleTranslateDomain()
|
||||
lowland_terrain.setSource(ground_gradient)
|
||||
lowland_terrain.setAxisYSource(lowland_y_scale)
|
||||
|
||||
/* highlands */
|
||||
|
||||
val highland_shape_fractal = ModuleFractal()
|
||||
highland_shape_fractal.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
||||
highland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
highland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
highland_shape_fractal.setNumOctaves(4)
|
||||
highland_shape_fractal.setFrequency(0.5) // horizontal size. Higher == narrower
|
||||
highland_shape_fractal.seed = SEED xor random.nextLong()
|
||||
//println(highland_shape_fractal.seed)
|
||||
|
||||
val highland_autocorrect = ModuleAutoCorrect()
|
||||
highland_autocorrect.setSource(highland_shape_fractal)
|
||||
highland_autocorrect.setRange(0.0, 1.0)
|
||||
|
||||
val highland_scale = ModuleScaleOffset()
|
||||
highland_scale.setSource(highland_autocorrect)
|
||||
highland_scale.setScale(1.4) // vertical size. Higher == taller
|
||||
highland_scale.setOffset(-2.25)
|
||||
|
||||
val highland_y_scale = ModuleScaleDomain()
|
||||
highland_y_scale.setSource(highland_scale)
|
||||
highland_y_scale.setScaleY(0.0)
|
||||
|
||||
val highland_terrain = ModuleTranslateDomain()
|
||||
highland_terrain.setSource(ground_gradient)
|
||||
highland_terrain.setAxisYSource(highland_y_scale)
|
||||
|
||||
/* mountains */
|
||||
|
||||
val mountain_shape_fractal = ModuleFractal()
|
||||
mountain_shape_fractal.setType(ModuleFractal.FractalType.BILLOW)
|
||||
mountain_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
mountain_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
mountain_shape_fractal.setNumOctaves(6)
|
||||
mountain_shape_fractal.setFrequency(0.55)
|
||||
mountain_shape_fractal.seed = SEED xor random.nextLong()
|
||||
//println(mountain_shape_fractal.seed)
|
||||
|
||||
val mountain_autocorrect = ModuleAutoCorrect()
|
||||
mountain_autocorrect.setSource(mountain_shape_fractal)
|
||||
mountain_autocorrect.setRange(0.0, 1.0)
|
||||
|
||||
val mountain_scale = ModuleScaleOffset()
|
||||
mountain_scale.setSource(mountain_autocorrect)
|
||||
mountain_scale.setScale(1.66)
|
||||
mountain_scale.setOffset(-1.25)
|
||||
|
||||
val mountain_y_scale = ModuleScaleDomain()
|
||||
mountain_y_scale.setSource(mountain_scale)
|
||||
mountain_y_scale.setScaleY(0.1)
|
||||
|
||||
val mountain_terrain = ModuleTranslateDomain()
|
||||
mountain_terrain.setSource(ground_gradient)
|
||||
mountain_terrain.setAxisYSource(mountain_y_scale)
|
||||
|
||||
/* selection */
|
||||
|
||||
val terrain_type_fractal = ModuleFractal()
|
||||
terrain_type_fractal.setType(ModuleFractal.FractalType.MULTI)
|
||||
terrain_type_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
terrain_type_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
terrain_type_fractal.setNumOctaves(5)
|
||||
terrain_type_fractal.setFrequency(0.4) // <= 0.33
|
||||
terrain_type_fractal.seed = SEED xor random.nextLong()
|
||||
//println(terrain_type_fractal.seed)
|
||||
|
||||
val terrain_autocorrect = ModuleAutoCorrect()
|
||||
terrain_autocorrect.setSource(terrain_type_fractal)
|
||||
terrain_autocorrect.setRange(0.0, 1.0)
|
||||
|
||||
val terrain_type_scale = ModuleScaleDomain()
|
||||
terrain_type_scale.setScaleY(0.33)
|
||||
terrain_type_scale.setSource(terrain_autocorrect)
|
||||
|
||||
val terrain_type_cache = ModuleCache()
|
||||
terrain_type_cache.setSource(terrain_type_scale)
|
||||
|
||||
val highland_mountain_select = ModuleSelect()
|
||||
highland_mountain_select.setLowSource(highland_terrain)
|
||||
highland_mountain_select.setHighSource(mountain_terrain)
|
||||
highland_mountain_select.setControlSource(terrain_type_cache)
|
||||
highland_mountain_select.setThreshold(0.55)
|
||||
highland_mountain_select.setFalloff(0.15)
|
||||
|
||||
val highland_lowland_select = ModuleSelect()
|
||||
highland_lowland_select.setLowSource(lowland_terrain)
|
||||
highland_lowland_select.setHighSource(highland_mountain_select)
|
||||
highland_lowland_select.setControlSource(terrain_type_cache)
|
||||
highland_lowland_select.setThreshold(0.25)
|
||||
highland_lowland_select.setFalloff(0.15)
|
||||
|
||||
|
||||
val ground_select = ModuleSelect()
|
||||
ground_select.setLowSource(0.0)
|
||||
ground_select.setHighSource(1.0)
|
||||
ground_select.setThreshold(0.5)
|
||||
ground_select.setControlSource(highland_lowland_select)
|
||||
|
||||
val joise = Joise(ground_select)
|
||||
|
||||
// fill the area as Joise map
|
||||
println("[mapgenerator] Raising and eroding terrain...")
|
||||
for (y in 0..(TERRAIN_UNDULATION - 1)) {
|
||||
for (x in 0..WIDTH) {
|
||||
val map: Boolean = (
|
||||
joise.get(
|
||||
x / SCALE_X,
|
||||
y / SCALE_Y
|
||||
) == 1.0)
|
||||
noiseMap[y + TERRAIN_AVERAGE_HEIGHT - (TERRAIN_UNDULATION / 2)].set(x, map)
|
||||
}
|
||||
}
|
||||
// fill the area bottom of the above map as 'filled'
|
||||
for (y in TERRAIN_AVERAGE_HEIGHT + (TERRAIN_UNDULATION / 2)..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH) {
|
||||
noiseMap[y].set(x, true)
|
||||
}
|
||||
}
|
||||
|
||||
return noiseMap
|
||||
}
|
||||
|
||||
/**
|
||||
* | ----
|
||||
* | ---
|
||||
* | ---
|
||||
* | --
|
||||
* | -
|
||||
* | --
|
||||
* | ---
|
||||
* | ---
|
||||
* - ----------------------------
|
||||
|
||||
* @param func_x
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun getGlacierMountedAmplitude(func_x: Int): Float {
|
||||
if (func_x > GLACIER_MOUNTAIN_WIDTH) {
|
||||
return 0f
|
||||
} else {
|
||||
val func_y = GLACIER_MOUNTAIN_HEIGHT / 2f * Math.cos((10 * func_x / (FastMath.PI * GLACIER_MOUNTAIN_WIDTH)).toDouble()).toFloat() + GLACIER_MOUNTAIN_HEIGHT / 2
|
||||
return func_y
|
||||
}
|
||||
}
|
||||
|
||||
private fun placeGlacierMount(heightMap: IntArray) {
|
||||
println("[mapgenerator] Putting glacier...")
|
||||
|
||||
// raise
|
||||
for (i in heightMap.indices) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
heightMap[i] += Math.round(getGlacierMountedAmplitude(i))
|
||||
} else {
|
||||
heightMap[i] += Math.round(getGlacierMountedAmplitude(heightMap.size - i - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cosine interpolation between point a and b.
|
||||
* @param x [0.0, 1.0] relative position between a and b
|
||||
* *
|
||||
* @param a leftmost point
|
||||
* *
|
||||
* @param b rightmost point
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun interpolateCosine(x: Float, a: Float, b: Float): Float {
|
||||
val ft = x * FastMath.PI
|
||||
val f = (1 - FastMath.cos(ft)) * 0.5f
|
||||
|
||||
return a * (1 - f) + b * f
|
||||
}
|
||||
|
||||
private fun heightMapToObjectMap(fs: IntArray) {
|
||||
println("[mapgenerator] Shaping world as processed...")
|
||||
|
||||
// iterate for heightmap
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val medianPosition = TERRAIN_AVERAGE_HEIGHT
|
||||
val pillarOffset = medianPosition - fs[x]
|
||||
|
||||
// for pillar length
|
||||
for (i in 0..HEIGHT - pillarOffset - 1) {
|
||||
|
||||
if (i < DIRT_LAYER_DEPTH) {
|
||||
map.setTileTerrain(x, i + pillarOffset, TileNameCode.DIRT)
|
||||
map.setTileWall(x, i + pillarOffset, TileNameCode.DIRT)
|
||||
} else {
|
||||
map.setTileTerrain(x, i + pillarOffset, TileNameCode.STONE)
|
||||
map.setTileWall(x, i + pillarOffset, TileNameCode.STONE)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillMapByNoiseMap() {
|
||||
println("[mapgenerator] Shaping world...")
|
||||
// generate dirt-stone transition line
|
||||
// use catmull spline
|
||||
val dirtStoneLine = IntArray(WIDTH)
|
||||
val POINTS_GAP = 64 // power of two!
|
||||
val splineControlPoints = Array((WIDTH / POINTS_GAP) + 1, { Pair(0, 0) })
|
||||
|
||||
// get spline points
|
||||
for (x in 0..(WIDTH / POINTS_GAP)) {
|
||||
for (y in 0..TERRAIN_AVERAGE_HEIGHT + TERRAIN_UNDULATION) {
|
||||
splineControlPoints[x] = Pair(x * POINTS_GAP, y)
|
||||
if (terrainMap[y].get(splineControlPoints[x].first)) break
|
||||
}
|
||||
// println("Spline[$x] x: ${splineControlPoints[x].first}, " +
|
||||
// "y: ${splineControlPoints[x].second}")
|
||||
}
|
||||
|
||||
// do interpolation
|
||||
for (x in 0..dirtStoneLine.size - 1) {
|
||||
val x_1 = x / POINTS_GAP
|
||||
|
||||
val splineX0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
|
||||
val splineX1 = splineControlPoints[x_1].first
|
||||
val splineX2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
|
||||
val splineX3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].first
|
||||
|
||||
val splineP0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
|
||||
val splineP1 = splineControlPoints[x_1].second.toFloat()
|
||||
val splineP2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
|
||||
val splineP3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
|
||||
|
||||
if (x in POINTS_GAP - 1..WIDTH - 2 * POINTS_GAP) {
|
||||
dirtStoneLine[x] = Math.round(FastMath.interpolateCatmullRom(
|
||||
(x - splineX1) / POINTS_GAP.toFloat(),
|
||||
-0.3f,//0.01f,
|
||||
splineP0,
|
||||
splineP1,
|
||||
splineP2,
|
||||
splineP3
|
||||
))
|
||||
}
|
||||
else {
|
||||
dirtStoneLine[x] = Math.round(FastMath.interpolateCatmullRom(
|
||||
(x - splineX1) / POINTS_GAP.toFloat(),
|
||||
-0.3f,//0.01f,
|
||||
splineP0,
|
||||
splineP1,
|
||||
splineP2,
|
||||
splineP3
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// scan vertically
|
||||
for (x in 0..WIDTH - 1) {
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
if (terrainMap[clamp(y + DIRT_LAYER_DEPTH, 0, HEIGHT - 1)].get(x)) {
|
||||
// map.setTileTerrain(x, y, TileNameCode.DIRT)
|
||||
// map.setTileWall(x, y, TileNameCode.DIRT)
|
||||
val tile =
|
||||
if (y < dirtStoneLine[x]) TileNameCode.DIRT
|
||||
else TileNameCode.STONE
|
||||
map.setTileTerrain(x, y, tile)
|
||||
map.setTileWall(x, y, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Carve */
|
||||
|
||||
/**
|
||||
* Carve (place specified block) by noisemap, inversed gradation filter applied.
|
||||
* @param map noisemap
|
||||
* *
|
||||
* @param scarcity higher == rarer
|
||||
* * 1.0 is a default value. This value works as a multiplier to the gradient filter.
|
||||
* @param tile
|
||||
* *
|
||||
* @param message
|
||||
*/
|
||||
private fun carveByMap(noisemap: Any, scarcity: Float, tile: Int, message: String,
|
||||
filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
filterStart: Float = NOISE_GRAD_START,
|
||||
filterEnd: Float = NOISE_GRAD_END) {
|
||||
println("[mapgenerator] " + message)
|
||||
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = when (noisemap) {
|
||||
is Joise ->
|
||||
noisemap.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
is TaggedSimplexNoise -> noisemap.noiseModule.getNoise(
|
||||
Math.round(x / noisemap.xStretch),
|
||||
Math.round(y / noisemap.yStretch)
|
||||
)
|
||||
|
||||
else -> throw(IllegalArgumentException("[mapgenerator] Unknown noise module type '${noisemap.javaClass.simpleName}': Only the 'Joise' or 'TaggedSimplexNoise' is valid."))
|
||||
}
|
||||
|
||||
if (noise > filter.getGrad(y, filterStart, filterEnd) * scarcity) {
|
||||
map.setTileTerrain(x, y, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillByMap(noisemap: Any, scarcity: Float, replaceFrom: Int, replaceTo: Int, message: String,
|
||||
filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
filterStart: Float = NOISE_GRAD_START,
|
||||
filterEnd: Float = NOISE_GRAD_END) {
|
||||
println("[mapgenerator] " + message)
|
||||
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = when (noisemap) {
|
||||
is Joise ->
|
||||
noisemap.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
is TaggedSimplexNoise -> noisemap.noiseModule.getNoise(
|
||||
Math.round(x / noisemap.xStretch),
|
||||
Math.round(y / noisemap.yStretch)
|
||||
)
|
||||
|
||||
else -> throw(IllegalArgumentException("[mapgenerator] Unknown noise module type '${noisemap.javaClass.simpleName}': Only the 'Joise' or 'TaggedSimplexNoise' is valid."))
|
||||
}
|
||||
|
||||
if (noise > filter.getGrad(y, filterStart, filterEnd) * scarcity
|
||||
&& map.getTileFromTerrain(x, y) == replaceFrom) {
|
||||
map.setTileTerrain(x, y, replaceTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillByMap(noisemap: Any, scarcity: Float, replaceFrom: Int, tile: IntArray, message: String,
|
||||
filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
filterStart: Float = NOISE_GRAD_START,
|
||||
filterEnd: Float = NOISE_GRAD_END) {
|
||||
println("[mapgenerator] " + message)
|
||||
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = when (noisemap) {
|
||||
is Joise ->
|
||||
noisemap.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
is TaggedSimplexNoise -> noisemap.noiseModule.getNoise(
|
||||
Math.round(x / noisemap.xStretch),
|
||||
Math.round(y / noisemap.yStretch)
|
||||
)
|
||||
|
||||
else -> throw(IllegalArgumentException("[mapgenerator] Unknown noise module type '${noisemap.javaClass.simpleName}': Only the 'Joise' or 'TaggedSimplexNoise' is valid."))
|
||||
}
|
||||
|
||||
if (noise > filter.getGrad(y, filterStart, filterEnd) * scarcity && map.getTileFromTerrain(x, y) == replaceFrom) {
|
||||
map.setTileTerrain(x, y, tile[random.nextInt(tile.size)])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processNoiseLayers(noiseRecords: Array<TaggedJoise>) {
|
||||
for (record in noiseRecords) {
|
||||
println("[mapgenerator] ${record.message}...")
|
||||
for (y in 0..HEIGHT - 1) {
|
||||
for (x in 0..WIDTH - 1) {
|
||||
val noise: Float = record.noiseModule.get(
|
||||
x.toDouble() / 48.0, // 48: Fixed value
|
||||
y.toDouble() / 48.0
|
||||
).toFloat()
|
||||
|
||||
val fromTerr = record.replaceFromTerrain
|
||||
val fromWall = record.replaceFromWall
|
||||
val to: Int = when(record.replaceTo) {
|
||||
is Int -> record.replaceTo as Int
|
||||
is IntArray -> (record.replaceTo as IntArray)[random.nextInt((record.replaceTo as IntArray).size)]
|
||||
else -> throw IllegalArgumentException("[mapgenerator] Unknown replaceTo tile type '${record.replaceTo.javaClass.canonicalName}': Only 'kotlin.Int' and 'kotlin.IntArray' is valid.")
|
||||
}
|
||||
if (to == TILE_MACRO_ALL) throw IllegalArgumentException("[mapgenerator] Invalid replaceTo: TILE_MACRO_ALL")
|
||||
val threshold = record.filter.getGrad(y, record.filterArg1, record.filterArg2)
|
||||
|
||||
if (noise > threshold * record.scarcity) {
|
||||
if ((map.getTileFromTerrain(x, y) == fromTerr || fromTerr == TILE_MACRO_ALL)
|
||||
&& (map.getTileFromWall(x, y) == fromWall || fromWall == TILE_MACRO_ALL)) {
|
||||
map.setTileTerrain(x, y, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateFloatingIslands() {
|
||||
println("[mapgenerator] Placing floating islands...")
|
||||
|
||||
val nIslandsMax = Math.round(map.width * 6f / 8192f)
|
||||
val nIslandsMin = Math.max(2, Math.round(map.width * 4f / 8192f))
|
||||
val nIslands = random.nextInt(nIslandsMax - nIslandsMin) + nIslandsMin
|
||||
val prevIndex = -1
|
||||
|
||||
val tiles = intArrayOf(TileNameCode.AIR, TileNameCode.STONE, TileNameCode.DIRT, TileNameCode.GRASS)
|
||||
|
||||
for (i in 0..nIslands - 1) {
|
||||
var currentIndex = random.nextInt(FloatingIslandsPreset.PRESETS)
|
||||
while (currentIndex == prevIndex) {
|
||||
currentIndex = random.nextInt(FloatingIslandsPreset.PRESETS)
|
||||
}
|
||||
val island = FloatingIslandsPreset.generatePreset(currentIndex, random)
|
||||
|
||||
val startingPosX = random.nextInt(map.width - 2048) + 1024
|
||||
val startingPosY = minimumFloatingIsleHeight + random.nextInt(minimumFloatingIsleHeight)
|
||||
|
||||
for (j in island.indices) {
|
||||
for (k in 0..island[0].size - 1) {
|
||||
if (island[j][k] > 0) {
|
||||
map.setTileTerrain(k + startingPosX, j + startingPosY, tiles[island[j][k]])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flood */
|
||||
|
||||
private fun floodBottomLava() {
|
||||
println("[mapgenerator] Flooding bottom lava...")
|
||||
for (i in HEIGHT * 14 / 15..HEIGHT - 1) {
|
||||
for (j in 0..WIDTH - 1) {
|
||||
if (map.terrainArray[i][j].toInt() == 0) {
|
||||
map.setTileTerrain(j, i, TileNameCode.LAVA)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Plant */
|
||||
|
||||
private fun plantGrass() {
|
||||
println("[mapgenerator] Planting grass...")
|
||||
|
||||
/* TODO composing dirt and stone
|
||||
* over certain level, use background dirt with stone 'peckles'
|
||||
* beetween levels, use background dirt with larger and denser stone peckles.
|
||||
* under another certain level, use background stone with dirt peckles.
|
||||
*/
|
||||
|
||||
for (y in TERRAIN_AVERAGE_HEIGHT - TERRAIN_UNDULATION..TERRAIN_AVERAGE_HEIGHT + TERRAIN_UNDULATION - 1) {
|
||||
for (x in 0..map.width - 1) {
|
||||
|
||||
val thisTile = map.getTileFromTerrain(x, y)
|
||||
|
||||
for (i in 0..8) {
|
||||
var nearbyWallTile: Int?
|
||||
nearbyWallTile = map.getTileFromWall(x + i % 3 - 1, y + i / 3 - 1)
|
||||
|
||||
if (nearbyWallTile == null) break;
|
||||
|
||||
if (i != 4 && thisTile == TileNameCode.DIRT && nearbyWallTile == TileNameCode.AIR) {
|
||||
map.setTileTerrain(x, y, TileNameCode.GRASS)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun isGrassOrDirt(x: Int, y: Int): Boolean {
|
||||
return map.getTileFromTerrain(x, y) == TileNameCode.GRASS || map.getTileFromTerrain(x, y) == TileNameCode.DIRT
|
||||
}
|
||||
|
||||
private fun replaceIfTerrain(ifTileRaw: Int, x: Int, y: Int, replaceTileRaw: Int) {
|
||||
if (map.getTileFromTerrain(x, y) == ifTileRaw) {
|
||||
map.setTileTerrain(x, y, replaceTileRaw)
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceIfWall(ifTileRaw: Int, x: Int, y: Int, replaceTileRaw: Int) {
|
||||
if (map.getTileFromWall(x, y) == ifTileRaw) {
|
||||
map.setTileWall(x, y, replaceTileRaw)
|
||||
}
|
||||
}
|
||||
|
||||
/* Post-process */
|
||||
|
||||
private fun fillOcean() {
|
||||
val thisSandList = intArrayOf(
|
||||
TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND,
|
||||
TileNameCode.SAND_WHITE, TileNameCode.SAND_WHITE, TileNameCode.SAND_WHITE,
|
||||
TileNameCode.SAND_BLACK, TileNameCode.SAND_BLACK, TileNameCode.SAND_GREEN
|
||||
)
|
||||
val thisRand = HQRNG(SEED xor random.nextLong())
|
||||
val thisSand = thisSandList[thisRand.nextInt(thisSandList.size)]
|
||||
// val thisSand = TileNameCode.SAND_GREEN
|
||||
|
||||
val thisSandStr = if (thisSand == TileNameCode.SAND_BLACK)
|
||||
"black"
|
||||
else if (thisSand == TileNameCode.SAND_GREEN)
|
||||
"green"
|
||||
else if (thisSand == TileNameCode.SAND)
|
||||
"yellow"
|
||||
else
|
||||
"white"
|
||||
println("[mapgenerator] Beach sand type: $thisSandStr")
|
||||
|
||||
var ix = 0
|
||||
while (ix < OCEAN_WIDTH * 1.5) {
|
||||
//flooding
|
||||
if (ix < OCEAN_WIDTH) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
for (y in getTerrainHeightFromHeightMap(OCEAN_WIDTH)..getTerrainHeightFromHeightMap(ix) - 1) {
|
||||
map.setTileTerrain(ix, y, TileNameCode.WATER)
|
||||
}
|
||||
} else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
for (y in getTerrainHeightFromHeightMap(map.width - 1 - OCEAN_WIDTH)..getTerrainHeightFromHeightMap(map.width - 1 - ix) - 1) {
|
||||
map.setTileTerrain(map.width - 1 - ix, y, TileNameCode.WATER)
|
||||
}
|
||||
}
|
||||
}
|
||||
//sand
|
||||
// linearly increase thickness of the sand sheet
|
||||
for (iy in 0..40 - ix * 40 / (OCEAN_WIDTH + SHORE_WIDTH) - 1) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_LEFT) {
|
||||
val terrainPoint = getTerrainHeightFromHeightMap(ix)
|
||||
|
||||
|
||||
map.setTileTerrain(ix, terrainPoint + iy, thisSand)
|
||||
// clear grass and make the sheet thicker
|
||||
map.setTileTerrain(ix, terrainPoint + iy - 1, thisSand)
|
||||
} else if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
val terrainPoint = getTerrainHeightFromHeightMap(map.width - 1 - ix)
|
||||
|
||||
map.setTileTerrain(map.width - 1 - ix, terrainPoint + iy, thisSand)
|
||||
// clear grass and make the sheet thicker
|
||||
map.setTileTerrain(map.width - 1 - ix, terrainPoint + iy - 1, thisSand)
|
||||
}
|
||||
}
|
||||
ix++
|
||||
}
|
||||
}
|
||||
|
||||
private fun freeze() {
|
||||
for (y in 0..map.height - 1 - 1) {
|
||||
for (x in 0..getFrozenAreaWidth(y) - 1) {
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
replaceIfTerrain(TileNameCode.DIRT, x, y, TileNameCode.SNOW)
|
||||
replaceIfTerrain(TileNameCode.STONE, x, y, TileNameCode.ICE_NATURAL)
|
||||
|
||||
replaceIfWall(TileNameCode.DIRT, x, y, TileNameCode.SNOW)
|
||||
replaceIfWall(TileNameCode.STONE, x, y, TileNameCode.ICE_NATURAL)
|
||||
} else {
|
||||
replaceIfTerrain(TileNameCode.DIRT, map.width - 1 - x, y, TileNameCode.SNOW)
|
||||
replaceIfTerrain(TileNameCode.STONE, map.width - 1 - x, y, TileNameCode.ICE_NATURAL)
|
||||
|
||||
replaceIfWall(TileNameCode.DIRT, map.width - 1 - x, y, TileNameCode.SNOW)
|
||||
replaceIfWall(TileNameCode.STONE, map.width - 1 - x, y, TileNameCode.ICE_NATURAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @return width of the frozen area for mapgenerator.freeze
|
||||
*/
|
||||
private fun getFrozenAreaWidth(y: Int): Int {
|
||||
val randDeviation = 7
|
||||
// narrower that the actual width
|
||||
val width = Math.round(GLACIER_MOUNTAIN_WIDTH * 0.625f)
|
||||
val height: Int
|
||||
if (worldOceanPosition == TYPE_OCEAN_RIGHT) {
|
||||
height = getTerrainHeightFromHeightMap(width)
|
||||
} else {
|
||||
height = getTerrainHeightFromHeightMap(map.width - 1 - width)
|
||||
}
|
||||
val k = width / FastMath.sqrt(height.toFloat())
|
||||
|
||||
if (y < height) {
|
||||
// ground
|
||||
return width
|
||||
} else {
|
||||
// underground
|
||||
return Math.round(
|
||||
k * FastMath.sqrt(y.toFloat()) + (random.nextInt(3) - 1))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param x position of heightmap
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun getTerrainHeightFromHeightMap(x: Int): Int {
|
||||
TODO()
|
||||
}
|
||||
|
||||
|
||||
/* Utility */
|
||||
|
||||
private fun clampN(clampNumber: Int, num: Int): Int {
|
||||
return FastMath.floor((num / clampNumber).toFloat()) * clampNumber
|
||||
}
|
||||
|
||||
private fun outOfBound(w: Int, h: Int, x: Int, y: Int): Boolean {
|
||||
return !(x > 0 && y > 0 && x < w && y < h)
|
||||
}
|
||||
|
||||
private fun getDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
|
||||
return FastMath.sqrt(FastMath.pow(x1 - x2, 2f) + FastMath.pow(y2 - y1, 2f))
|
||||
}
|
||||
|
||||
private fun circularDig(i: Int, j: Int, brushSize: Int, fillFrom: Int, fill: Int) {
|
||||
val halfBrushSize = brushSize * 0.5f
|
||||
|
||||
for (pointerY in 0..brushSize - 1) {
|
||||
for (pointerX in 0..brushSize - 1) {
|
||||
if (getDistance(j.toFloat(), i.toFloat(), j + pointerX - halfBrushSize, i + pointerY - halfBrushSize) <= FastMath.floor((brushSize / 2).toFloat()) - 1) {
|
||||
if (Math.round(j + pointerX - halfBrushSize) > brushSize
|
||||
&& Math.round(j + pointerX - halfBrushSize) < WIDTH - brushSize
|
||||
&& Math.round(i + pointerY - halfBrushSize) > brushSize
|
||||
&& Math.round(i + pointerY - halfBrushSize) < HEIGHT - brushSize) {
|
||||
if (map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] == fillFrom.toByte()) {
|
||||
map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] = fill.toByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clamp(x: Int, min: Int, max: Int): Int = if (x < min) min else if (x > max) max else x
|
||||
|
||||
data class TaggedSimplexNoise(var noiseModule: SimplexNoise, var xStretch: Float, var yStretch: Float)
|
||||
|
||||
data class TaggedJoise(var message: String,
|
||||
var noiseModule: Joise, var scarcity: Float,
|
||||
var replaceFromTerrain: Int, var replaceFromWall: Int,
|
||||
var replaceTo: Any,
|
||||
var filter: NoiseFilter = NoiseFilterQuadratic,
|
||||
var filterArg1: Float = NOISE_GRAD_START,
|
||||
var filterArg2: Float = NOISE_GRAD_END)
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package com.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.torvald.colourutil.Col4096
|
||||
import com.torvald.random.HQRNG
|
||||
import org.newdawn.slick.Color
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-23.
|
||||
*/
|
||||
object RoguelikeRandomiser {
|
||||
|
||||
val POTION_PRIMARY_COLSET = intArrayOf(15, 15, 7, 7, 0, 0)
|
||||
|
||||
var potionColours: HashMap<Int, Col4096> = HashMap()
|
||||
var coloursDiscovered: HashMap<Col4096, Boolean> = HashMap()
|
||||
|
||||
val coloursTaken: ArrayList<Col4096> = ArrayList()
|
||||
|
||||
var seed: Long = 0
|
||||
private val random: Random = HQRNG()
|
||||
|
||||
private val POTION_HEAL_TIER1 = 0x00
|
||||
private val POTION_HEAL_TIRE2 = 0x01
|
||||
|
||||
private val POTION_MAGIC_REGEN_TIER1 = 0x10
|
||||
|
||||
private val POTION_BERSERK_TIER1 = 0x20
|
||||
|
||||
|
||||
|
||||
fun setupColours() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* For external classes/objects, does not touch COLOUR SET
|
||||
* @param arr Array of Int(0-15)
|
||||
*/
|
||||
fun composeColourFrom(arr: IntArray): Color {
|
||||
val colourElements = arr.copyOf()
|
||||
shuffleArrayInt(colourElements, HQRNG())
|
||||
|
||||
val colourStack = IntArrayStack(colourElements)
|
||||
|
||||
return Col4096(colourStack.pop(),
|
||||
colourStack.pop(),
|
||||
colourStack.pop())
|
||||
.toSlickColour()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun shuffleArrayInt(ar: IntArray, rnd: Random) {
|
||||
for (i in ar.size - 1 downTo 0) {
|
||||
val index = rnd.nextInt(i + 1);
|
||||
// Simple swap
|
||||
val a = ar[index];
|
||||
ar[index] = ar[i];
|
||||
ar[i] = a;
|
||||
}
|
||||
}
|
||||
|
||||
class IntArrayStack {
|
||||
/**
|
||||
* Number of elements in the stack
|
||||
*/
|
||||
var depth: Int = 0
|
||||
private set
|
||||
|
||||
var size: Int
|
||||
get() = data.size
|
||||
set(newSize) {
|
||||
if (newSize > depth) inflate(newSize - data.size)
|
||||
else deflate(data.size - newSize)
|
||||
}
|
||||
|
||||
private lateinit var data: IntArray
|
||||
|
||||
constructor(stackSize: Int) {
|
||||
data = IntArray(stackSize)
|
||||
}
|
||||
|
||||
constructor(arr: IntArray) {
|
||||
data = arr.copyOf()
|
||||
depth = size
|
||||
}
|
||||
|
||||
fun push(v: Int) {
|
||||
if (depth >= data.size) throw StackOverflowError()
|
||||
data[depth++] = v
|
||||
}
|
||||
|
||||
fun pop(): Int {
|
||||
if (depth == 0) throw EmptyStackException()
|
||||
return data[--depth]
|
||||
}
|
||||
|
||||
fun peek(): Int? {
|
||||
if (depth == 0) return null
|
||||
return data[depth - 1]
|
||||
}
|
||||
|
||||
fun dup() {
|
||||
if (depth == 0) throw EmptyStackException()
|
||||
if (depth == data.size) throw StackOverflowError()
|
||||
push(peek()!!)
|
||||
}
|
||||
|
||||
fun swap() {
|
||||
if (depth < 2) throw UnsupportedOperationException("Stack is empty or has only one element.")
|
||||
val up = pop()
|
||||
val dn = pop()
|
||||
push(up)
|
||||
push(dn)
|
||||
}
|
||||
|
||||
fun drop() {
|
||||
if (depth == 0) throw EmptyStackException()
|
||||
--depth
|
||||
}
|
||||
|
||||
fun defineFromArray(arr: IntArray) { data = arr.copyOf() }
|
||||
|
||||
/**
|
||||
* Increase the stack size by a factor.
|
||||
*/
|
||||
fun inflate(sizeToAdd: Int) {
|
||||
if (sizeToAdd < 0) throw UnsupportedOperationException("$sizeToAdd: Cannot deflate the stack with this function. Use deflate(int) instead.")
|
||||
size += sizeToAdd
|
||||
val oldStack = this.asArray()
|
||||
data = IntArray(size, { if (it < oldStack.size) oldStack[it] else 0 })
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the stack size by a factor. Overflowing data will be removed.
|
||||
*/
|
||||
fun deflate(sizeToTake: Int) {
|
||||
if (size - sizeToTake < 1) throw UnsupportedOperationException("$sizeToTake: Cannot deflate the stack to the size of zero or negative.")
|
||||
size -= sizeToTake
|
||||
val oldStack = this.asArray()
|
||||
data = IntArray(size, { oldStack[it] })
|
||||
if (depth > data.size) depth = data.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert stack as array. Index zero is the bottommost element.
|
||||
* @return array of data, with array size equivalent to the stack depth.
|
||||
*/
|
||||
fun asArray() = data.copyOfRange(0, depth - 1)
|
||||
|
||||
fun equalTo(other: IntArrayStack) = (this.asArray() == other.asArray())
|
||||
|
||||
fun plus() { data[depth - 2] += pop() }
|
||||
fun minus() { data[depth - 2] -= pop() }
|
||||
fun times() { data[depth - 2] *= pop() }
|
||||
fun div() { data[depth - 2] /= pop() }
|
||||
fun mod() { data[depth - 2] %= pop() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.torvald.terrarum.mapgenerator
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
import com.jme3.math.FastMath
|
||||
|
||||
class SimplexNoise
|
||||
/**
|
||||
* @param largestFeature
|
||||
* *
|
||||
* @param persistence higher the value, rougher the output
|
||||
* *
|
||||
* @param seed
|
||||
*/
|
||||
(internal var largestFeature: Int, internal var persistence: Float, internal var seed: Long) {
|
||||
|
||||
internal var octaves: Array<SimplexNoise_octave>
|
||||
internal var frequencys: FloatArray
|
||||
internal var amplitudes: FloatArray
|
||||
|
||||
init {
|
||||
|
||||
//receives a number (e.g. 128) and calculates what power of 2 it is (e.g. 2^7)
|
||||
val numberOfOctaves = FastMath.intLog2(largestFeature)
|
||||
val rnd = HQRNG(seed)
|
||||
|
||||
octaves = Array<SimplexNoise_octave>(numberOfOctaves, {i -> SimplexNoise_octave(rnd.nextInt())})
|
||||
frequencys = FloatArray(numberOfOctaves)
|
||||
amplitudes = FloatArray(numberOfOctaves)
|
||||
|
||||
for (i in 0..numberOfOctaves - 1) {
|
||||
octaves[i] = SimplexNoise_octave(rnd.nextInt())
|
||||
|
||||
frequencys[i] = FastMath.pow(2f, i.toFloat())
|
||||
amplitudes[i] = FastMath.pow(persistence, (octaves.size - i).toFloat())
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun getNoise(x: Int, y: Int): Float {
|
||||
|
||||
var result = 0f
|
||||
|
||||
for (i in octaves.indices) {
|
||||
//float frequency = FastMath.pow(2,i);
|
||||
//float amplitude = FastMath.pow(persistence,octaves.length-i);
|
||||
|
||||
result += (octaves[i].noise((x / frequencys[i]).toDouble(), (y / frequencys[i]).toDouble()) * amplitudes[i]).toFloat()
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
fun getNoise(x: Int, y: Int, z: Int): Float {
|
||||
|
||||
var result = 0f
|
||||
|
||||
for (i in octaves.indices) {
|
||||
val frequency = FastMath.pow(2f, i.toFloat())
|
||||
val amplitude = FastMath.pow(persistence, (octaves.size - i).toFloat())
|
||||
|
||||
result += (octaves[i].noise((x / frequency).toDouble(), (y / frequency).toDouble(), (z / frequency).toDouble()) * amplitude).toFloat()
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,457 +0,0 @@
|
||||
package com.torvald.terrarum.mapgenerator
|
||||
|
||||
/*
|
||||
* A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
|
||||
*
|
||||
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
|
||||
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
|
||||
* Better rank ordering method by Stefan Gustavson in 2012.
|
||||
*
|
||||
* This could be speeded up even further, but it's useful as it is.
|
||||
*
|
||||
* Version 2012-03-09
|
||||
*
|
||||
* This code was placed in the public domain by its original author,
|
||||
* Stefan Gustavson. You may use it as you see fit, but
|
||||
* attribution is appreciated.
|
||||
*
|
||||
*/
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
|
||||
class SimplexNoise_octave(seed: Int) { // Simplex noise in 2D, 3D and 4D
|
||||
|
||||
private var p = ShortArray(p_supply.size)
|
||||
|
||||
// To remove the need for index wrapping, double the permutation table length
|
||||
private val perm = ShortArray(512)
|
||||
private val permMod12 = ShortArray(512)
|
||||
|
||||
init {
|
||||
p = p_supply.clone()
|
||||
|
||||
if (seed == RANDOMSEED) {
|
||||
throw IllegalArgumentException("Seed cannot be zero.")
|
||||
}
|
||||
|
||||
//the random for the swaps
|
||||
val rand = HQRNG(seed.toLong())
|
||||
|
||||
//the seed determines the swaps that occur between the default order and the order we're actually going to use
|
||||
for (i in 0..NUMBEROFSWAPS - 1) {
|
||||
val swapFrom = rand.nextInt(p.size)
|
||||
val swapTo = rand.nextInt(p.size)
|
||||
|
||||
val temp = p[swapFrom]
|
||||
p[swapFrom] = p[swapTo]
|
||||
p[swapTo] = temp
|
||||
}
|
||||
|
||||
|
||||
for (i in 0..511) {
|
||||
perm[i] = p[i and 255]
|
||||
permMod12[i] = (perm[i] % 12).toShort()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2D simplex noise
|
||||
fun noise(xin: Double, yin: Double): Double {
|
||||
val n0: Double
|
||||
val n1: Double
|
||||
val n2: Double // Noise contributions from the three corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
val s = (xin + yin) * F2 // Hairy factor for 2D
|
||||
val i = fastfloor(xin + s)
|
||||
val j = fastfloor(yin + s)
|
||||
val t = (i + j) * G2
|
||||
val X0 = i - t // Unskew the cell origin back to (x,y) space
|
||||
val Y0 = j - t
|
||||
val x0 = xin - X0 // The x,y distances from the cell origin
|
||||
val y0 = yin - Y0
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
val i1: Int
|
||||
val j1: Int // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||
if (x0 > y0) {
|
||||
i1 = 1
|
||||
j1 = 0
|
||||
} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||
else {
|
||||
i1 = 0
|
||||
j1 = 1
|
||||
} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
val x1 = x0 - i1 + G2 // Offsets for middle corner in (x,y) unskewed coords
|
||||
val y1 = y0 - j1 + G2
|
||||
val x2 = x0 - 1.0 + 2.0 * G2 // Offsets for last corner in (x,y) unskewed coords
|
||||
val y2 = y0 - 1.0 + 2.0 * G2
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
val ii = i and 255
|
||||
val jj = j and 255
|
||||
val gi0 = permMod12[ii + perm[jj]].toInt()
|
||||
val gi1 = permMod12[ii + i1 + perm[jj + j1].toInt()].toInt()
|
||||
val gi2 = permMod12[ii + 1 + perm[jj + 1].toInt()].toInt()
|
||||
// Calculate the contribution from the three corners
|
||||
var t0 = 0.5 - x0 * x0 - y0 * y0
|
||||
if (t0 < 0)
|
||||
n0 = 0.0
|
||||
else {
|
||||
t0 *= t0
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0) // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
var t1 = 0.5 - x1 * x1 - y1 * y1
|
||||
if (t1 < 0)
|
||||
n1 = 0.0
|
||||
else {
|
||||
t1 *= t1
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1)
|
||||
}
|
||||
var t2 = 0.5 - x2 * x2 - y2 * y2
|
||||
if (t2 < 0)
|
||||
n2 = 0.0
|
||||
else {
|
||||
t2 *= t2
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2)
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 70.0 * (n0 + n1 + n2)
|
||||
}
|
||||
|
||||
|
||||
// 3D simplex noise
|
||||
fun noise(xin: Double, yin: Double, zin: Double): Double {
|
||||
val n0: Double
|
||||
val n1: Double
|
||||
val n2: Double
|
||||
val n3: Double // Noise contributions from the four corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
val s = (xin + yin + zin) * F3 // Very nice and simple skew factor for 3D
|
||||
val i = fastfloor(xin + s)
|
||||
val j = fastfloor(yin + s)
|
||||
val k = fastfloor(zin + s)
|
||||
val t = (i + j + k) * G3
|
||||
val X0 = i - t // Unskew the cell origin back to (x,y,z) space
|
||||
val Y0 = j - t
|
||||
val Z0 = k - t
|
||||
val x0 = xin - X0 // The x,y,z distances from the cell origin
|
||||
val y0 = yin - Y0
|
||||
val z0 = zin - Z0
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
// Determine which simplex we are in.
|
||||
val i1: Int
|
||||
val j1: Int
|
||||
val k1: Int // Offsets for second corner of simplex in (i,j,k) coords
|
||||
val i2: Int
|
||||
val j2: Int
|
||||
val k2: Int // Offsets for third corner of simplex in (i,j,k) coords
|
||||
if (x0 >= y0) {
|
||||
if (y0 >= z0) {
|
||||
i1 = 1
|
||||
j1 = 0
|
||||
k1 = 0
|
||||
i2 = 1
|
||||
j2 = 1
|
||||
k2 = 0
|
||||
} // X Y Z order
|
||||
else if (x0 >= z0) {
|
||||
i1 = 1
|
||||
j1 = 0
|
||||
k1 = 0
|
||||
i2 = 1
|
||||
j2 = 0
|
||||
k2 = 1
|
||||
} // X Z Y order
|
||||
else {
|
||||
i1 = 0
|
||||
j1 = 0
|
||||
k1 = 1
|
||||
i2 = 1
|
||||
j2 = 0
|
||||
k2 = 1
|
||||
} // Z X Y order
|
||||
}
|
||||
else {
|
||||
// x0<y0
|
||||
if (y0 < z0) {
|
||||
i1 = 0
|
||||
j1 = 0
|
||||
k1 = 1
|
||||
i2 = 0
|
||||
j2 = 1
|
||||
k2 = 1
|
||||
} // Z Y X order
|
||||
else if (x0 < z0) {
|
||||
i1 = 0
|
||||
j1 = 1
|
||||
k1 = 0
|
||||
i2 = 0
|
||||
j2 = 1
|
||||
k2 = 1
|
||||
} // Y Z X order
|
||||
else {
|
||||
i1 = 0
|
||||
j1 = 1
|
||||
k1 = 0
|
||||
i2 = 1
|
||||
j2 = 1
|
||||
k2 = 0
|
||||
} // Y X Z order
|
||||
}
|
||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||
// c = 1/6.
|
||||
val x1 = x0 - i1 + G3 // Offsets for second corner in (x,y,z) coords
|
||||
val y1 = y0 - j1 + G3
|
||||
val z1 = z0 - k1 + G3
|
||||
val x2 = x0 - i2 + 2.0 * G3 // Offsets for third corner in (x,y,z) coords
|
||||
val y2 = y0 - j2 + 2.0 * G3
|
||||
val z2 = z0 - k2 + 2.0 * G3
|
||||
val x3 = x0 - 1.0 + 3.0 * G3 // Offsets for last corner in (x,y,z) coords
|
||||
val y3 = y0 - 1.0 + 3.0 * G3
|
||||
val z3 = z0 - 1.0 + 3.0 * G3
|
||||
// Work out the hashed gradient indices of the four simplex corners
|
||||
val ii = i and 255
|
||||
val jj = j and 255
|
||||
val kk = k and 255
|
||||
val gi0 = permMod12[ii + perm[jj + perm[kk]]].toInt()
|
||||
val gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1].toInt()].toInt()].toInt()
|
||||
val gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2].toInt()].toInt()].toInt()
|
||||
val gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1].toInt()].toInt()].toInt()
|
||||
// Calculate the contribution from the four corners
|
||||
var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0
|
||||
if (t0 < 0)
|
||||
n0 = 0.0
|
||||
else {
|
||||
t0 *= t0
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0)
|
||||
}
|
||||
var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1
|
||||
if (t1 < 0)
|
||||
n1 = 0.0
|
||||
else {
|
||||
t1 *= t1
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1)
|
||||
}
|
||||
var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2
|
||||
if (t2 < 0)
|
||||
n2 = 0.0
|
||||
else {
|
||||
t2 *= t2
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2)
|
||||
}
|
||||
var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3
|
||||
if (t3 < 0)
|
||||
n3 = 0.0
|
||||
else {
|
||||
t3 *= t3
|
||||
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3)
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to stay just inside [-1,1]
|
||||
return 32.0 * (n0 + n1 + n2 + n3)
|
||||
}
|
||||
|
||||
|
||||
// 4D simplex noise, better simplex rank ordering method 2012-03-09
|
||||
fun noise(x: Double, y: Double, z: Double, w: Double): Double {
|
||||
|
||||
val n0: Double
|
||||
val n1: Double
|
||||
val n2: Double
|
||||
val n3: Double
|
||||
val n4: Double // Noise contributions from the five corners
|
||||
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
||||
val s = (x + y + z + w) * F4 // Factor for 4D skewing
|
||||
val i = fastfloor(x + s)
|
||||
val j = fastfloor(y + s)
|
||||
val k = fastfloor(z + s)
|
||||
val l = fastfloor(w + s)
|
||||
val t = (i + j + k + l) * G4 // Factor for 4D unskewing
|
||||
val X0 = i - t // Unskew the cell origin back to (x,y,z,w) space
|
||||
val Y0 = j - t
|
||||
val Z0 = k - t
|
||||
val W0 = l - t
|
||||
val x0 = x - X0 // The x,y,z,w distances from the cell origin
|
||||
val y0 = y - Y0
|
||||
val z0 = z - Z0
|
||||
val w0 = w - W0
|
||||
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
||||
// To find out which of the 24 possible simplices we're in, we need to
|
||||
// determine the magnitude ordering of x0, y0, z0 and w0.
|
||||
// Six pair-wise comparisons are performed between each possible pair
|
||||
// of the four coordinates, and the results are used to rank the numbers.
|
||||
var rankx = 0
|
||||
var ranky = 0
|
||||
var rankz = 0
|
||||
var rankw = 0
|
||||
if (x0 > y0) rankx++ else ranky++
|
||||
if (x0 > z0) rankx++ else rankz++
|
||||
if (x0 > w0) rankx++ else rankw++
|
||||
if (y0 > z0) ranky++ else rankz++
|
||||
if (y0 > w0) ranky++ else rankw++
|
||||
if (z0 > w0) rankz++ else rankw++
|
||||
val i1: Int
|
||||
val j1: Int
|
||||
val k1: Int
|
||||
val l1: Int // The integer offsets for the second simplex corner
|
||||
val i2: Int
|
||||
val j2: Int
|
||||
val k2: Int
|
||||
val l2: Int // The integer offsets for the third simplex corner
|
||||
val i3: Int
|
||||
val j3: Int
|
||||
val k3: Int
|
||||
val l3: Int // The integer offsets for the fourth simplex corner
|
||||
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
||||
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
||||
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
||||
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
||||
// Rank 3 denotes the largest coordinate.
|
||||
i1 = if (rankx >= 3) 1 else 0
|
||||
j1 = if (ranky >= 3) 1 else 0
|
||||
k1 = if (rankz >= 3) 1 else 0
|
||||
l1 = if (rankw >= 3) 1 else 0
|
||||
// Rank 2 denotes the second largest coordinate.
|
||||
i2 = if (rankx >= 2) 1 else 0
|
||||
j2 = if (ranky >= 2) 1 else 0
|
||||
k2 = if (rankz >= 2) 1 else 0
|
||||
l2 = if (rankw >= 2) 1 else 0
|
||||
// Rank 1 denotes the second smallest coordinate.
|
||||
i3 = if (rankx >= 1) 1 else 0
|
||||
j3 = if (ranky >= 1) 1 else 0
|
||||
k3 = if (rankz >= 1) 1 else 0
|
||||
l3 = if (rankw >= 1) 1 else 0
|
||||
// The fifth corner has all coordinate offsets = 1, so no need to compute that.
|
||||
val x1 = x0 - i1 + G4 // Offsets for second corner in (x,y,z,w) coords
|
||||
val y1 = y0 - j1 + G4
|
||||
val z1 = z0 - k1 + G4
|
||||
val w1 = w0 - l1 + G4
|
||||
val x2 = x0 - i2 + 2.0 * G4 // Offsets for third corner in (x,y,z,w) coords
|
||||
val y2 = y0 - j2 + 2.0 * G4
|
||||
val z2 = z0 - k2 + 2.0 * G4
|
||||
val w2 = w0 - l2 + 2.0 * G4
|
||||
val x3 = x0 - i3 + 3.0 * G4 // Offsets for fourth corner in (x,y,z,w) coords
|
||||
val y3 = y0 - j3 + 3.0 * G4
|
||||
val z3 = z0 - k3 + 3.0 * G4
|
||||
val w3 = w0 - l3 + 3.0 * G4
|
||||
val x4 = x0 - 1.0 + 4.0 * G4 // Offsets for last corner in (x,y,z,w) coords
|
||||
val y4 = y0 - 1.0 + 4.0 * G4
|
||||
val z4 = z0 - 1.0 + 4.0 * G4
|
||||
val w4 = w0 - 1.0 + 4.0 * G4
|
||||
// Work out the hashed gradient indices of the five simplex corners
|
||||
val ii = i and 255
|
||||
val jj = j and 255
|
||||
val kk = k and 255
|
||||
val ll = l and 255
|
||||
val gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32
|
||||
val gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1].toInt()].toInt()].toInt()] % 32
|
||||
val gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2].toInt()].toInt()].toInt()] % 32
|
||||
val gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3].toInt()].toInt()].toInt()] % 32
|
||||
val gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1].toInt()].toInt()].toInt()] % 32
|
||||
// Calculate the contribution from the five corners
|
||||
var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0
|
||||
if (t0 < 0)
|
||||
n0 = 0.0
|
||||
else {
|
||||
t0 *= t0
|
||||
n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0)
|
||||
}
|
||||
var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1
|
||||
if (t1 < 0)
|
||||
n1 = 0.0
|
||||
else {
|
||||
t1 *= t1
|
||||
n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1)
|
||||
}
|
||||
var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2
|
||||
if (t2 < 0)
|
||||
n2 = 0.0
|
||||
else {
|
||||
t2 *= t2
|
||||
n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2)
|
||||
}
|
||||
var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3
|
||||
if (t3 < 0)
|
||||
n3 = 0.0
|
||||
else {
|
||||
t3 *= t3
|
||||
n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3)
|
||||
}
|
||||
var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4
|
||||
if (t4 < 0)
|
||||
n4 = 0.0
|
||||
else {
|
||||
t4 *= t4
|
||||
n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4)
|
||||
}
|
||||
// Sum up and scale the result to cover the range [-1,1]
|
||||
return 27.0 * (n0 + n1 + n2 + n3 + n4)
|
||||
}
|
||||
|
||||
// Inner class to speed upp gradient computations
|
||||
// (array access is a lot slower than member access)
|
||||
private class Grad {
|
||||
internal var x: Double = 0.toDouble()
|
||||
internal var y: Double = 0.toDouble()
|
||||
internal var z: Double = 0.toDouble()
|
||||
internal var w: Double = 0.toDouble()
|
||||
|
||||
internal constructor(x: Double, y: Double, z: Double) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
}
|
||||
|
||||
internal constructor(x: Double, y: Double, z: Double, w: Double) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
this.w = w
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
var RANDOMSEED = 0
|
||||
private val NUMBEROFSWAPS = 400
|
||||
|
||||
private val grad3 = arrayOf(Grad(1.0, 1.0, 0.0), Grad(-1.0, 1.0, 0.0), Grad(1.0, -1.0, 0.0), Grad(-1.0, -1.0, 0.0), Grad(1.0, 0.0, 1.0), Grad(-1.0, 0.0, 1.0), Grad(1.0, 0.0, -1.0), Grad(-1.0, 0.0, -1.0), Grad(0.0, 1.0, 1.0), Grad(0.0, -1.0, 1.0), Grad(0.0, 1.0, -1.0), Grad(0.0, -1.0, -1.0))
|
||||
|
||||
private val grad4 = arrayOf(Grad(0.0, 1.0, 1.0, 1.0), Grad(0.0, 1.0, 1.0, -1.0), Grad(0.0, 1.0, -1.0, 1.0), Grad(0.0, 1.0, -1.0, -1.0), Grad(0.0, -1.0, 1.0, 1.0), Grad(0.0, -1.0, 1.0, -1.0), Grad(0.0, -1.0, -1.0, 1.0), Grad(0.0, -1.0, -1.0, -1.0), Grad(1.0, 0.0, 1.0, 1.0), Grad(1.0, 0.0, 1.0, -1.0), Grad(1.0, 0.0, -1.0, 1.0), Grad(1.0, 0.0, -1.0, -1.0), Grad(-1.0, 0.0, 1.0, 1.0), Grad(-1.0, 0.0, 1.0, -1.0), Grad(-1.0, 0.0, -1.0, 1.0), Grad(-1.0, 0.0, -1.0, -1.0), Grad(1.0, 1.0, 0.0, 1.0), Grad(1.0, 1.0, 0.0, -1.0), Grad(1.0, -1.0, 0.0, 1.0), Grad(1.0, -1.0, 0.0, -1.0), Grad(-1.0, 1.0, 0.0, 1.0), Grad(-1.0, 1.0, 0.0, -1.0), Grad(-1.0, -1.0, 0.0, 1.0), Grad(-1.0, -1.0, 0.0, -1.0), Grad(1.0, 1.0, 1.0, 0.0), Grad(1.0, 1.0, -1.0, 0.0), Grad(1.0, -1.0, 1.0, 0.0), Grad(1.0, -1.0, -1.0, 0.0), Grad(-1.0, 1.0, 1.0, 0.0), Grad(-1.0, 1.0, -1.0, 0.0), Grad(-1.0, -1.0, 1.0, 0.0), Grad(-1.0, -1.0, -1.0, 0.0))
|
||||
|
||||
private val p_supply = shortArrayOf(151, 160, 137, 91, 90, 15, //this contains all the numbers between 0 and 255, these are put in a random order depending upon the seed
|
||||
131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180)
|
||||
|
||||
// Skewing and unskewing factors for 2, 3, and 4 dimensions
|
||||
private val F2 = 0.5 * (Math.sqrt(3.0) - 1.0)
|
||||
private val G2 = (3.0 - Math.sqrt(3.0)) / 6.0
|
||||
private val F3 = 1.0 / 3.0
|
||||
private val G3 = 1.0 / 6.0
|
||||
private val F4 = (Math.sqrt(5.0) - 1.0) / 4.0
|
||||
private val G4 = (5.0 - Math.sqrt(5.0)) / 20.0
|
||||
|
||||
// This method is a *lot* faster than using (int)Math.floor(x)
|
||||
private fun fastfloor(x: Double): Int {
|
||||
val xi = x.toInt()
|
||||
return if (x < xi) xi - 1 else xi
|
||||
}
|
||||
|
||||
private fun dot(g: Grad, x: Double, y: Double): Double {
|
||||
return g.x * x + g.y * y
|
||||
}
|
||||
|
||||
private fun dot(g: Grad, x: Double, y: Double, z: Double): Double {
|
||||
return g.x * x + g.y * y + g.z * z
|
||||
}
|
||||
|
||||
private fun dot(g: Grad, x: Double, y: Double, z: Double, w: Double): Double {
|
||||
return g.x * x + g.y * y + g.z * z + g.w * w
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,377 +0,0 @@
|
||||
package com.torvald.terrarum
|
||||
|
||||
import com.torvald.imagefont.GameFontWhite
|
||||
import com.torvald.JsonFetcher
|
||||
import com.torvald.JsonWriter
|
||||
import org.lwjgl.input.Controllers
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.newdawn.slick.AppGameContainer
|
||||
import org.newdawn.slick.Font
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.SlickException
|
||||
import org.newdawn.slick.state.StateBasedGame
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.logging.FileHandler
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
import java.util.logging.SimpleFormatter
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-30.
|
||||
* Kotlin code: Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
class Terrarum @Throws(SlickException::class)
|
||||
constructor(gamename: String) : StateBasedGame(gamename) {
|
||||
|
||||
init {
|
||||
|
||||
gameConfig = GameConfig()
|
||||
|
||||
getDefaultDirectory()
|
||||
createDirs()
|
||||
|
||||
val readFromDisk = readConfigJson()
|
||||
if (!readFromDisk) readConfigJson()
|
||||
|
||||
// get locale from config
|
||||
gameLocale = gameConfig.getAsString("language") ?: sysLang
|
||||
|
||||
// if bad game locale were set, use system locale
|
||||
if (gameLocale.length < 4)
|
||||
gameLocale = sysLang
|
||||
|
||||
println("[terrarum] Locale: " + gameLocale)
|
||||
}
|
||||
|
||||
@Throws(SlickException::class)
|
||||
override fun initStatesList(gc: GameContainer) {
|
||||
gameFont = GameFontWhite()
|
||||
|
||||
hasController = gc.input.controllerCount > 0
|
||||
if (hasController) {
|
||||
for (c in 0..Controllers.getController(0).axisCount - 1) {
|
||||
Controllers.getController(0).setDeadZone(c, CONTROLLER_DEADZONE)
|
||||
}
|
||||
}
|
||||
|
||||
appgc.input.enableKeyRepeat()
|
||||
|
||||
game = Game()
|
||||
addState(game)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* To be used with physics simulator
|
||||
*/
|
||||
val TARGET_FPS = 50
|
||||
|
||||
/**
|
||||
* To be used with render, to achieve smooth frame drawing
|
||||
|
||||
* TARGET_INTERNAL_FPS > TARGET_FPS for smooth frame drawing
|
||||
|
||||
* Must choose a value so that (1000 / VAL) is still integer
|
||||
*/
|
||||
val TARGET_INTERNAL_FPS = 100
|
||||
|
||||
lateinit var appgc: AppGameContainer
|
||||
|
||||
val WIDTH = 1060
|
||||
val HEIGHT = 742 // IMAX ratio
|
||||
var VSYNC = true
|
||||
val VSYNC_TRIGGER_THRESHOLD = 56
|
||||
|
||||
lateinit var game: Game
|
||||
lateinit var gameConfig: GameConfig
|
||||
|
||||
lateinit var OSName: String
|
||||
private set
|
||||
lateinit var OSVersion: String
|
||||
private set
|
||||
lateinit var OperationSystem: String
|
||||
private set
|
||||
lateinit var defaultDir: String
|
||||
private set
|
||||
lateinit var defaultSaveDir: String
|
||||
private set
|
||||
|
||||
var gameLocale = "" // locale override
|
||||
|
||||
lateinit var gameFont: Font
|
||||
|
||||
val SCENE_ID_HOME = 1
|
||||
val SCENE_ID_GAME = 3
|
||||
|
||||
var hasController = false
|
||||
val CONTROLLER_DEADZONE = 0.1f
|
||||
|
||||
private lateinit var configDir: String
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
try {
|
||||
appgc = AppGameContainer(Terrarum("Terrarum"))
|
||||
appgc.setDisplayMode(WIDTH, HEIGHT, false)
|
||||
|
||||
appgc.setTargetFrameRate(TARGET_INTERNAL_FPS)
|
||||
appgc.setVSync(VSYNC)
|
||||
appgc.setMaximumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS)
|
||||
appgc.setMinimumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS - 1)
|
||||
|
||||
appgc.setShowFPS(false)
|
||||
appgc.setUpdateOnlyWhenVisible(false)
|
||||
|
||||
appgc.start()
|
||||
}
|
||||
catch (ex: SlickException) {
|
||||
val logger = Logger.getLogger(Terrarum::class.java.name)
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
|
||||
val calendar = Calendar.getInstance()
|
||||
val filepath = "$defaultDir/crashlog-${dateFormat.format(calendar.time)}.txt"
|
||||
val fileHandler = FileHandler(filepath)
|
||||
logger.addHandler(fileHandler)
|
||||
|
||||
val formatter = SimpleFormatter()
|
||||
fileHandler.formatter = formatter
|
||||
|
||||
//logger.info()
|
||||
|
||||
println("The game has been crashed!")
|
||||
println("Crash log were saved to $filepath.")
|
||||
println("================================================================================")
|
||||
logger.log(Level.SEVERE, null, ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getDefaultDirectory() {
|
||||
OSName = System.getProperty("os.name")
|
||||
OSVersion = System.getProperty("os.version")
|
||||
|
||||
val OS = System.getProperty("os.name").toUpperCase()
|
||||
if (OS.contains("WIN")) {
|
||||
OperationSystem = "WINDOWS"
|
||||
defaultDir = System.getenv("APPDATA") + "/terrarum"
|
||||
}
|
||||
else if (OS.contains("OS X")) {
|
||||
OperationSystem = "OSX"
|
||||
defaultDir = System.getProperty("user.home") + "/Library/Application Support/terrarum"
|
||||
}
|
||||
else if (OS.contains("NUX") || OS.contains("NIX")) {
|
||||
OperationSystem = "LINUX"
|
||||
defaultDir = System.getProperty("user.home") + "/.terrarum"
|
||||
}
|
||||
else if (OS.contains("SUNOS")) {
|
||||
OperationSystem = "SOLARIS"
|
||||
defaultDir = System.getProperty("user.home") + "/.terrarum"
|
||||
}
|
||||
else {
|
||||
OperationSystem = "UNKNOWN"
|
||||
defaultDir = System.getProperty("user.home") + "/.terrarum"
|
||||
}
|
||||
|
||||
defaultSaveDir = defaultDir + "/Saves"
|
||||
configDir = defaultDir + "/config.json"
|
||||
}
|
||||
|
||||
private fun createDirs() {
|
||||
val dirs = arrayOf(File(defaultSaveDir))
|
||||
|
||||
for (d in dirs) {
|
||||
if (!d.exists()) {
|
||||
d.mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun createConfigJson() {
|
||||
val configFile = File(configDir)
|
||||
|
||||
if (!configFile.exists() || configFile.length() == 0L) {
|
||||
JsonWriter.writeToFile(DefaultConfig.fetch(), configDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readConfigJson(): Boolean {
|
||||
try {
|
||||
// read from disk and build config from it
|
||||
val jsonObject = JsonFetcher.readJson(configDir)
|
||||
|
||||
// make config
|
||||
jsonObject.entrySet().forEach { entry -> gameConfig[entry.key] = entry.value }
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
// write default config to game dir. Call this method again to read config from it.
|
||||
try {
|
||||
createConfigJson()
|
||||
}
|
||||
catch (e1: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// exception handling
|
||||
val sysLang: String
|
||||
get() {
|
||||
val lan = System.getProperty("user.language")
|
||||
var country = System.getProperty("user.country")
|
||||
if (lan == "en")
|
||||
country = "US"
|
||||
else if (lan == "fr")
|
||||
country = "FR"
|
||||
else if (lan == "de")
|
||||
country = "DE"
|
||||
else if (lan == "ko") country = "KR"
|
||||
|
||||
return lan + country
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return config from config set. If the config does not exist, default value will be returned.
|
||||
* @param key
|
||||
* *
|
||||
* @return Config from config set or default config if it does not exist.
|
||||
* *
|
||||
* @throws NullPointerException if the specified config simply does not exist.
|
||||
*/
|
||||
fun getConfigInt(key: String): Int {
|
||||
var cfg: Int = 0
|
||||
try {
|
||||
cfg = gameConfig.getAsInt(key)!!
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
// if the config set does not have the key, try for the default config
|
||||
try {
|
||||
cfg = DefaultConfig.fetch().get(key).asInt
|
||||
}
|
||||
catch (e1: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
catch (e: TypeCastException) {
|
||||
// if the config set does not have the key, try for the default config
|
||||
try {
|
||||
cfg = DefaultConfig.fetch().get(key).asInt
|
||||
}
|
||||
catch (e1: kotlin.TypeCastException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config from config set. If the config does not exist, default value will be returned.
|
||||
* @param key
|
||||
* *
|
||||
* @return Config from config set or default config if it does not exist.
|
||||
* *
|
||||
* @throws NullPointerException if the specified config simply does not exist.
|
||||
*/
|
||||
fun getConfigFloat(key: String): Float {
|
||||
var cfg = 0f
|
||||
try {
|
||||
cfg = gameConfig.getAsFloat(key)!!
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
try {
|
||||
cfg = DefaultConfig.fetch().get(key).asFloat
|
||||
}
|
||||
catch (e1: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config from config set. If the config does not exist, default value will be returned.
|
||||
* @param key
|
||||
* *
|
||||
* @return Config from config set or default config if it does not exist.
|
||||
* *
|
||||
* @throws NullPointerException if the specified config simply does not exist.
|
||||
*/
|
||||
fun getConfigString(key: String): String {
|
||||
var cfg = ""
|
||||
try {
|
||||
cfg = gameConfig.getAsString(key)!!
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
try {
|
||||
cfg = DefaultConfig.fetch().get(key).asString
|
||||
}
|
||||
catch (e1: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config from config set. If the config does not exist, default value will be returned.
|
||||
* @param key
|
||||
* *
|
||||
* @return Config from config set or default config if it does not exist.
|
||||
* *
|
||||
* @throws NullPointerException if the specified config simply does not exist.
|
||||
*/
|
||||
fun getConfigBoolean(key: String): Boolean {
|
||||
var cfg = false
|
||||
try {
|
||||
cfg = gameConfig.getAsBoolean(key)!!
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
try {
|
||||
cfg = DefaultConfig.fetch().get(key).asBoolean
|
||||
}
|
||||
catch (e1: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) = Terrarum.main(args)
|
||||
|
||||
fun setBlendMul() {
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glColorMask(true, true, true, true)
|
||||
GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
|
||||
fun setBlendNormal() {
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glColorMask(true, true, true, true)
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
|
||||
fun setBlendAlphaMap() {
|
||||
GL11.glDisable(GL11.GL_BLEND)
|
||||
GL11.glColorMask(false, false, false, true)
|
||||
}
|
||||
|
||||
fun setBlendDisable() {
|
||||
GL11.glDisable(GL11.GL_BLEND)
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package com.torvald.terrarum.tileproperties
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-21.
|
||||
*/
|
||||
object TileNameCode {
|
||||
|
||||
val AIR = 0
|
||||
|
||||
val STONE = TilePropCodex.idDamageToIndex(1, 0)
|
||||
val STONE_QUARRIED = TilePropCodex.idDamageToIndex(1, 1)
|
||||
|
||||
val DIRT = TilePropCodex.idDamageToIndex(2, 0)
|
||||
val GRASS = TilePropCodex.idDamageToIndex(2, 1)
|
||||
|
||||
val PLANK_NORMAL = TilePropCodex.idDamageToIndex(3, 0)
|
||||
val PLANK_EBONY = TilePropCodex.idDamageToIndex(3, 1)
|
||||
val PLANK_BIRCH = TilePropCodex.idDamageToIndex(3, 2)
|
||||
val PLANK_BLOODROSE = TilePropCodex.idDamageToIndex(3, 3)
|
||||
|
||||
val TRUNK_NORMAL = TilePropCodex.idDamageToIndex(4, 0)
|
||||
val TRUNK_EBONY = TilePropCodex.idDamageToIndex(4, 1)
|
||||
val TRUNK_BIRCH = TilePropCodex.idDamageToIndex(4, 2)
|
||||
val TRUNK_BLOODROSE = TilePropCodex.idDamageToIndex(4, 3)
|
||||
|
||||
val SAND = TilePropCodex.idDamageToIndex(5, 0)
|
||||
val SAND_WHITE = TilePropCodex.idDamageToIndex(5, 1)
|
||||
val SAND_RED = TilePropCodex.idDamageToIndex(5, 2)
|
||||
val SAND_DESERT = TilePropCodex.idDamageToIndex(5, 3)
|
||||
val SAND_BLACK = TilePropCodex.idDamageToIndex(5, 4)
|
||||
val SAND_GREEN = TilePropCodex.idDamageToIndex(5, 5)
|
||||
|
||||
val GRAVEL = TilePropCodex.idDamageToIndex(6, 0)
|
||||
val GRAVEL_GREY = TilePropCodex.idDamageToIndex(6, 1)
|
||||
|
||||
val ORE_COPPER = TilePropCodex.idDamageToIndex(7, 0)
|
||||
val ORE_IRON = TilePropCodex.idDamageToIndex(7, 1)
|
||||
val ORE_GOLD = TilePropCodex.idDamageToIndex(7, 2)
|
||||
val ORE_SILVER = TilePropCodex.idDamageToIndex(7, 3)
|
||||
val ORE_ILMENITE = TilePropCodex.idDamageToIndex(7, 4)
|
||||
val ORE_AURICHALCUM = TilePropCodex.idDamageToIndex(7, 5)
|
||||
|
||||
val RAW_RUBY = TilePropCodex.idDamageToIndex(8, 0)
|
||||
val RAW_EMERALD = TilePropCodex.idDamageToIndex(8, 1)
|
||||
val RAW_SAPPHIRE = TilePropCodex.idDamageToIndex(8, 2)
|
||||
val RAW_TOPAZ = TilePropCodex.idDamageToIndex(8, 3)
|
||||
val RAW_DIAMOND = TilePropCodex.idDamageToIndex(8, 4)
|
||||
val RAW_AMETHYST = TilePropCodex.idDamageToIndex(8, 5)
|
||||
|
||||
val SNOW = TilePropCodex.idDamageToIndex(9, 0)
|
||||
val ICE_FRAGILE = TilePropCodex.idDamageToIndex(9, 1)
|
||||
val ICE_NATURAL = TilePropCodex.idDamageToIndex(9, 2)
|
||||
val ICE_MAGICAL = TilePropCodex.idDamageToIndex(9, 3)
|
||||
|
||||
val PLATFORM_STONE = TilePropCodex.idDamageToIndex(10, 0)
|
||||
val PLATFORM_WOODEN = TilePropCodex.idDamageToIndex(10, 1)
|
||||
val PLATFORM_EBONY = TilePropCodex.idDamageToIndex(10, 2)
|
||||
val PLATFORM_BIRCH = TilePropCodex.idDamageToIndex(10, 3)
|
||||
val PLATFORM_BLOODROSE = TilePropCodex.idDamageToIndex(10, 4)
|
||||
|
||||
val TORCH = TilePropCodex.idDamageToIndex(11, 0)
|
||||
val TORCH_FROST = TilePropCodex.idDamageToIndex(11, 1)
|
||||
|
||||
val TORCH_OFF = TilePropCodex.idDamageToIndex(12, 0)
|
||||
val TORCH_FROST_OFF = TilePropCodex.idDamageToIndex(12, 1)
|
||||
|
||||
val ILLUMINATOR_WHITE = TilePropCodex.idDamageToIndex(13, 0)
|
||||
val ILLUMINATOR_YELLOW = TilePropCodex.idDamageToIndex(13, 1)
|
||||
val ILLUMINATOR_ORANGE = TilePropCodex.idDamageToIndex(13, 2)
|
||||
val ILLUMINATOR_RED = TilePropCodex.idDamageToIndex(13, 3)
|
||||
val ILLUMINATOR_FUCHSIA = TilePropCodex.idDamageToIndex(13, 4)
|
||||
val ILLUMINATOR_PURPLE = TilePropCodex.idDamageToIndex(13, 5)
|
||||
val ILLUMINATOR_BLUE = TilePropCodex.idDamageToIndex(13, 6)
|
||||
val ILLUMINATOR_CYAN = TilePropCodex.idDamageToIndex(13, 7)
|
||||
val ILLUMINATOR_GREEN = TilePropCodex.idDamageToIndex(13, 8)
|
||||
val ILLUMINATOR_GREEN_DARK = TilePropCodex.idDamageToIndex(13, 9)
|
||||
val ILLUMINATOR_BROWN = TilePropCodex.idDamageToIndex(13, 10)
|
||||
val ILLUMINATOR_TAN = TilePropCodex.idDamageToIndex(13, 11)
|
||||
val ILLUMINATOR_GREY_LIGHT = TilePropCodex.idDamageToIndex(13, 12)
|
||||
val ILLUMINATOR_GREY_MED = TilePropCodex.idDamageToIndex(13, 13)
|
||||
val ILLUMINATOR_GREY_DARK = TilePropCodex.idDamageToIndex(13, 14)
|
||||
val ILLUMINATOR_BLACK = TilePropCodex.idDamageToIndex(13, 15)
|
||||
|
||||
val ILLUMINATOR_WHITE_OFF = TilePropCodex.idDamageToIndex(14, 0)
|
||||
val ILLUMINATOR_YELLOW_OFF = TilePropCodex.idDamageToIndex(14, 1)
|
||||
val ILLUMINATOR_ORANGE_OFF = TilePropCodex.idDamageToIndex(14, 2)
|
||||
val ILLUMINATOR_RED_OFF = TilePropCodex.idDamageToIndex(14, 3)
|
||||
val ILLUMINATOR_FUCHSIA_OFF = TilePropCodex.idDamageToIndex(14, 4)
|
||||
val ILLUMINATOR_PURPLE_OFF = TilePropCodex.idDamageToIndex(14, 5)
|
||||
val ILLUMINATOR_BLUE_OFF = TilePropCodex.idDamageToIndex(14, 6)
|
||||
val ILLUMINATOR_CYAN_OFF = TilePropCodex.idDamageToIndex(14, 7)
|
||||
val ILLUMINATOR_GREEN_OFF = TilePropCodex.idDamageToIndex(14, 8)
|
||||
val ILLUMINATOR_GREEN_DARK_OFF = TilePropCodex.idDamageToIndex(14, 9)
|
||||
val ILLUMINATOR_BROWN_OFF = TilePropCodex.idDamageToIndex(14, 10)
|
||||
val ILLUMINATOR_TAN_OFF = TilePropCodex.idDamageToIndex(14, 11)
|
||||
val ILLUMINATOR_GREY_LIGHT_OFF = TilePropCodex.idDamageToIndex(14, 12)
|
||||
val ILLUMINATOR_GREY_MED_OFF = TilePropCodex.idDamageToIndex(14, 13)
|
||||
val ILLUMINATOR_GREY_DARK_OFF = TilePropCodex.idDamageToIndex(14, 14)
|
||||
val ILLUMINATOR_BLACK_OFF = TilePropCodex.idDamageToIndex(14, 15)
|
||||
|
||||
val SANDSTONE = TilePropCodex.idDamageToIndex(15, 0)
|
||||
val SANDSTONE_WHITE = TilePropCodex.idDamageToIndex(15, 1)
|
||||
val SANDSTONE_RED = TilePropCodex.idDamageToIndex(15, 2)
|
||||
val SANDSTONE_DESERT = TilePropCodex.idDamageToIndex(15, 3)
|
||||
val SANDSTONE_BLACK = TilePropCodex.idDamageToIndex(15, 4)
|
||||
val SANDSTONE_GREEN = TilePropCodex.idDamageToIndex(15, 5)
|
||||
|
||||
val WATER_1 = TilePropCodex.idDamageToIndex(255, 0)
|
||||
val WATER_2 = TilePropCodex.idDamageToIndex(255, 1)
|
||||
val WATER_3 = TilePropCodex.idDamageToIndex(255, 2)
|
||||
val WATER_4 = TilePropCodex.idDamageToIndex(255, 3)
|
||||
val WATER_5 = TilePropCodex.idDamageToIndex(255, 4)
|
||||
val WATER_6 = TilePropCodex.idDamageToIndex(255, 5)
|
||||
val WATER_7 = TilePropCodex.idDamageToIndex(255, 6)
|
||||
val WATER_8 = TilePropCodex.idDamageToIndex(255, 7)
|
||||
val WATER_9 = TilePropCodex.idDamageToIndex(255, 8)
|
||||
val WATER_10 = TilePropCodex.idDamageToIndex(255, 9)
|
||||
val WATER_11 = TilePropCodex.idDamageToIndex(255, 10)
|
||||
val WATER_12 = TilePropCodex.idDamageToIndex(255, 11)
|
||||
val WATER_13 = TilePropCodex.idDamageToIndex(255, 12)
|
||||
val WATER_14 = TilePropCodex.idDamageToIndex(255, 13)
|
||||
val WATER_15 = TilePropCodex.idDamageToIndex(255, 14)
|
||||
val WATER = TilePropCodex.idDamageToIndex(255, 15)
|
||||
|
||||
val LAVA_1 = TilePropCodex.idDamageToIndex(254, 0)
|
||||
val LAVA_2 = TilePropCodex.idDamageToIndex(254, 1)
|
||||
val LAVA_3 = TilePropCodex.idDamageToIndex(254, 2)
|
||||
val LAVA_4 = TilePropCodex.idDamageToIndex(254, 3)
|
||||
val LAVA_5 = TilePropCodex.idDamageToIndex(254, 4)
|
||||
val LAVA_6 = TilePropCodex.idDamageToIndex(254, 5)
|
||||
val LAVA_7 = TilePropCodex.idDamageToIndex(254, 6)
|
||||
val LAVA_8 = TilePropCodex.idDamageToIndex(254, 7)
|
||||
val LAVA_9 = TilePropCodex.idDamageToIndex(254, 8)
|
||||
val LAVA_10 = TilePropCodex.idDamageToIndex(254, 9)
|
||||
val LAVA_11 = TilePropCodex.idDamageToIndex(254, 10)
|
||||
val LAVA_12 = TilePropCodex.idDamageToIndex(254, 11)
|
||||
val LAVA_13 = TilePropCodex.idDamageToIndex(254, 12)
|
||||
val LAVA_14 = TilePropCodex.idDamageToIndex(254, 13)
|
||||
val LAVA_15 = TilePropCodex.idDamageToIndex(254, 14)
|
||||
val LAVA = TilePropCodex.idDamageToIndex(254, 15)
|
||||
|
||||
val NULL = 4096
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.torvald.terrarum.tileproperties
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-16.
|
||||
*/
|
||||
class TileProp {
|
||||
|
||||
var id: Int = 0
|
||||
|
||||
var damage: Int = 0
|
||||
var name: String = ""
|
||||
|
||||
/**
|
||||
* @param opacity Raw RGB value, without alpha
|
||||
*/
|
||||
var opacity: Int = 0 // colour attenuation
|
||||
|
||||
var strength: Int = 0
|
||||
|
||||
|
||||
var density: Int = 0
|
||||
|
||||
var isFluid: Boolean = false
|
||||
var movementResistance: Int = 0
|
||||
|
||||
var isSolid: Boolean = false
|
||||
|
||||
var isWallable: Boolean = false
|
||||
|
||||
/**
|
||||
* @param luminosity Raw RGB value, without alpha
|
||||
*/
|
||||
var luminosity: Int = 0
|
||||
|
||||
var drop: Int = 0
|
||||
var dropDamage: Int = 0
|
||||
|
||||
var isFallable: Boolean = false
|
||||
|
||||
var friction: Int = 0
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package com.torvald.terrarum.tileproperties
|
||||
|
||||
import com.torvald.CSVFetcher
|
||||
import com.torvald.terrarum.gamemap.MapLayer
|
||||
import com.torvald.terrarum.gamemap.PairedMapLayer
|
||||
import org.apache.commons.csv.CSVRecord
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-16.
|
||||
*/
|
||||
class TilePropCodex {
|
||||
|
||||
init {
|
||||
tileProps = Array<TileProp>(MapLayer.RANGE * PairedMapLayer.RANGE + 1,
|
||||
{i -> TileProp() }
|
||||
)
|
||||
|
||||
for (i in tileProps.indices) {
|
||||
tileProps[i] = TileProp()
|
||||
}
|
||||
|
||||
try {
|
||||
// todo verify CSV using pre-calculated SHA256 hash
|
||||
val records = CSVFetcher.readCSV(CSV_PATH)
|
||||
|
||||
println("[TilePropCodex] Building tile properties table")
|
||||
|
||||
records.forEach { record -> setProp(
|
||||
tileProps[idDamageToIndex(intVal(record, "id"), intVal(record, "dmg"))]
|
||||
, record)
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private lateinit var tileProps: Array<TileProp>
|
||||
|
||||
val CSV_PATH = "./src/com/torvald/terrarum/tileproperties/tileprop.csv"
|
||||
|
||||
fun getProp(index: Int, damage: Int): TileProp {
|
||||
try {
|
||||
tileProps[idDamageToIndex(index, damage)].id
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
throw NullPointerException("Tile prop with id $index and damage $damage does not exist.")
|
||||
}
|
||||
|
||||
return tileProps[idDamageToIndex(index, damage)]
|
||||
}
|
||||
|
||||
fun getProp(rawIndex: Int?): TileProp {
|
||||
try {
|
||||
tileProps[rawIndex ?: TileNameCode.NULL].id
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
throw NullPointerException("Tile prop with raw id $rawIndex does not exist.")
|
||||
}
|
||||
|
||||
return tileProps[rawIndex ?: TileNameCode.NULL]
|
||||
}
|
||||
|
||||
private fun setProp(prop: TileProp, record: CSVRecord) {
|
||||
prop.name = record.get("name")
|
||||
|
||||
prop.id = intVal(record, "id")
|
||||
prop.damage = intVal(record, "dmg")
|
||||
|
||||
prop.opacity = intVal(record, "opacity")
|
||||
prop.strength = intVal(record, "strength")
|
||||
prop.density = intVal(record, "dsty")
|
||||
prop.luminosity = intVal(record, "lumcolor")
|
||||
prop.drop = intVal(record, "drop")
|
||||
prop.dropDamage = intVal(record, "ddmg")
|
||||
prop.friction = intVal(record, "friction")
|
||||
|
||||
prop.isFluid = boolVal(record, "fluid")
|
||||
prop.isSolid = boolVal(record, "solid")
|
||||
prop.isWallable = boolVal(record, "wall")
|
||||
prop.isFallable = boolVal(record, "fall")
|
||||
|
||||
if (prop.isFluid) prop.movementResistance = intVal(record, "movr")
|
||||
|
||||
print(formatNum3(prop.id) + ":" + formatNum2(prop.damage))
|
||||
println("\t" + prop.name)
|
||||
}
|
||||
|
||||
private fun intVal(rec: CSVRecord, s: String): Int {
|
||||
var ret = -1
|
||||
try {
|
||||
ret = Integer.decode(rec.get(s))!!
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun boolVal(rec: CSVRecord, s: String) = intVal(rec, s) != 0
|
||||
|
||||
fun idDamageToIndex(index: Int, damage: Int) = index * PairedMapLayer.RANGE + damage
|
||||
|
||||
private fun formatNum3(i: Int): String {
|
||||
if (i < 10)
|
||||
return "00" + i
|
||||
else if (i < 100)
|
||||
return "0" + i
|
||||
else
|
||||
return i.toString()
|
||||
}
|
||||
|
||||
private fun formatNum2(i: Int) = if (i < 10) "0" + i else i.toString()
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
"id";"dmg";"name" ; "opacity";"strength";"dsty";"fluid";"movr";"solid";"wall"; "lumcolor";"drop";"ddmg";"fall";"friction"
|
||||
"0"; "0";"TILE_AIR" ; "8396808"; "0"; "1"; "0"; "0"; "0"; "0"; "0"; "0"; "0"; "0";"16"
|
||||
"1"; "0";"TILE_STONE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "1"; "0"; "1"; "0"; "0";"16"
|
||||
"1"; "1";"TILE_STONE_QUARRIED" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "1"; "0"; "1"; "1"; "0";"16"
|
||||
"2"; "0";"TILE_DIRT" ; "33587232"; "6";"1400"; "0"; "0"; "1"; "1"; "0"; "2"; "0"; "0";"16"
|
||||
"2"; "1";"TILE_GRASS" ; "33587232"; "6";"1400"; "0"; "0"; "1"; "1"; "0"; "2"; "1"; "0";"16"
|
||||
"3"; "0";"TILE_PLANK_NORMAL" ; "33587232"; "12"; "740"; "0"; "0"; "1"; "1"; "0"; "3"; "0"; "0";"16"
|
||||
"3"; "1";"TILE_PLANK_EBONY" ; "33587232"; "12";"1200"; "0"; "0"; "1"; "1"; "0"; "3"; "1"; "0";"16"
|
||||
"3"; "2";"TILE_PLANK_BIRCH" ; "33587232"; "12"; "670"; "0"; "0"; "1"; "1"; "0"; "3"; "2"; "0";"16"
|
||||
"3"; "3";"TILE_PLANK_BLOODROSE" ; "33587232"; "12"; "900"; "0"; "0"; "1"; "1"; "0"; "3"; "3"; "0";"16"
|
||||
"4"; "0";"TILE_TRUNK_NORMAL" ; "33587232"; "12"; "740"; "0"; "0"; "1"; "0"; "0"; "3"; "0"; "0";"16"
|
||||
"4"; "1";"TILE_TRUNK_EBONY" ; "33587232"; "12";"1200"; "0"; "0"; "1"; "0"; "0"; "3"; "1"; "0";"16"
|
||||
"4"; "2";"TILE_TRUNK_BIRCH" ; "33587232"; "12"; "670"; "0"; "0"; "1"; "0"; "0"; "3"; "2"; "0";"16"
|
||||
"4"; "3";"TILE_TRUNK_BLOODROSE" ; "33587232"; "12"; "900"; "0"; "0"; "1"; "0"; "0"; "3"; "3"; "0";"16"
|
||||
"5"; "0";"TILE_SAND" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "0"; "1";"16"
|
||||
"5"; "1";"TILE_SAND_WHITE" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "1"; "1";"16"
|
||||
"5"; "2";"TILE_SAND_RED" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "2"; "1";"16"
|
||||
"5"; "3";"TILE_SAND_DESERT" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "3"; "1";"16"
|
||||
"5"; "4";"TILE_SAND_BLACK" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "4"; "1";"16"
|
||||
"5"; "5";"TILE_SAND_GREEN" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "5"; "1";"16"
|
||||
"6"; "0";"TILE_GRAVEL" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "6"; "0"; "1";"16"
|
||||
"6"; "1";"TILE_GRAVEL_GREY" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "6"; "1"; "1";"16"
|
||||
"7"; "0";"TILE_ORE_MALACHITE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "0"; "0";"16"
|
||||
"7"; "1";"TILE_ORE_HEMATITE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "1"; "0";"16"
|
||||
"7"; "2";"TILE_ORE_NATURAL_GOLD" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "2"; "0";"16"
|
||||
"7"; "3";"TILE_ORE_NATURAL_SILVER" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "3"; "0";"16"
|
||||
"7"; "4";"TILE_ORE_RUTILE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "4"; "0";"16"
|
||||
"7"; "5";"TILE_ORE_AURICHALCUMITE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "5"; "0";"16"
|
||||
"8"; "0";"TILE_GEM_RUBY" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "0"; "0";"16"
|
||||
"8"; "1";"TILE_GEM_EMERALD" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "1"; "0";"16"
|
||||
"8"; "2";"TILE_GEM_SAPPHIRE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "2"; "0";"16"
|
||||
"8"; "3";"TILE_GEM_TOPAZ" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "3"; "0";"16"
|
||||
"8"; "4";"TILE_GEM_DIAMOND" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "4"; "0";"16"
|
||||
"8"; "5";"TILE_GEM_AMETHYST" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "5"; "0";"16"
|
||||
"9"; "0";"TILE_SNOW" ; "33587232"; "6"; "500"; "0"; "0"; "1"; "1"; "0"; "9"; "0"; "0";"16"
|
||||
"9"; "1";"TILE_ICE_FRAGILE" ; "13644813"; "1"; "930"; "0"; "0"; "1"; "0"; "0"; "9"; "1"; "0";"16"
|
||||
"9"; "2";"TILE_ICE_NATURAL" ; "27289626"; "25"; "930"; "0"; "0"; "1"; "1"; "0"; "9"; "2"; "0"; "8"
|
||||
"9"; "3";"TILE_ICE_CLEAR_MAGICAL" ; "33587232"; "25";"3720"; "0"; "0"; "1"; "1"; "1253434"; "9"; "3"; "0"; "8"
|
||||
"10"; "0";"TILE_PLATFORM_STONE" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "0"; "0";"16"
|
||||
"10"; "1";"TILE_PLATFORM_WOODEN" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "1"; "0";"16"
|
||||
"10"; "2";"TILE_PLATFORM_EBONY" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "2"; "0";"16"
|
||||
"10"; "3";"TILE_PLATFORM_BIRCH" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "3"; "0";"16"
|
||||
"10"; "4";"TILE_PLATFORM_BLOODROSE" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "4"; "0";"16"
|
||||
"11"; "0";"TILE_TORCH" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "15304000"; "11"; "0"; "0";"16"
|
||||
"11"; "1";"TILE_TORCH_FROST" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "5143807"; "11"; "1"; "0";"16"
|
||||
"12"; "0";"TILE_TORCH" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "0"; "11"; "0"; "0";"16"
|
||||
"12"; "1";"TILE_TORCH_FROST" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "0"; "11"; "1"; "0";"16"
|
||||
"13"; "0";"TILE_ILLUMINATOR_WHITE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "15461355"; "13"; "0"; "0";"16"
|
||||
"13"; "1";"TILE_ILLUMINATOR_YELLOW" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "15461120"; "13"; "1"; "0";"16"
|
||||
"13"; "2";"TILE_ILLUMINATOR_ORANGE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "15447808"; "13"; "2"; "0";"16"
|
||||
"13"; "3";"TILE_ILLUMINATOR_RED" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "15400960"; "13"; "3"; "0";"16"
|
||||
"13"; "4";"TILE_ILLUMINATOR_FUCHSIA" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "15401143"; "13"; "4"; "0";"16"
|
||||
"13"; "5";"TILE_ILLUMINATOR_PURPLE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "11993323"; "13"; "5"; "0";"16"
|
||||
"13"; "6";"TILE_ILLUMINATOR_BLUE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "235"; "13"; "6"; "0";"16"
|
||||
"13"; "7";"TILE_ILLUMINATOR_CYAN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "51947"; "13"; "7"; "0";"16"
|
||||
"13"; "8";"TILE_ILLUMINATOR_GREEN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "4311552"; "13"; "8"; "0";"16"
|
||||
"13"; "9";"TILE_ILLUMINATOR_GREEN_DARK";"8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "2123776"; "13"; "9"; "0";"16"
|
||||
"13"; "10";"TILE_ILLUMINATOR_BROWN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "5578752"; "13"; "10"; "0";"16"
|
||||
"13"; "11";"TILE_ILLUMINATOR_TAN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "9857076"; "13"; "11"; "0";"16"
|
||||
"13"; "12";"TILE_ILLUMINATOR_GREY_LIGHT";"8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "12434877"; "13"; "12"; "0";"16"
|
||||
"13"; "13";"TILE_ILLUMINATOR_GREY_MED"; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "7697781"; "13"; "13"; "0";"16"
|
||||
"13"; "14";"TILE_ILLUMINATOR_GREY_DARK"; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "4276545"; "13"; "14"; "0";"16"
|
||||
"13"; "15";"TILE_ILLUMINATOR_BLACK" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "7274751"; "13"; "15"; "0";"16"
|
||||
"14"; "0";"TILE_ILLUMINATOR_WHITE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "0"; "0";"16"
|
||||
"14"; "1";"TILE_ILLUMINATOR_YELLOW" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "1"; "0";"16"
|
||||
"14"; "2";"TILE_ILLUMINATOR_ORANGE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "2"; "0";"16"
|
||||
"14"; "3";"TILE_ILLUMINATOR_RED" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "3"; "0";"16"
|
||||
"14"; "4";"TILE_ILLUMINATOR_FUCHSIA" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "4"; "0";"16"
|
||||
"14"; "5";"TILE_ILLUMINATOR_PURPLE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "5"; "0";"16"
|
||||
"14"; "6";"TILE_ILLUMINATOR_BLUE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "6"; "0";"16"
|
||||
"14"; "7";"TILE_ILLUMINATOR_CYAN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "7"; "0";"16"
|
||||
"14"; "8";"TILE_ILLUMINATOR_GREEN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "8"; "0";"16"
|
||||
"14"; "9";"TILE_ILLUMINATOR_GREEN_DARK";"33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "9"; "0";"16"
|
||||
"14"; "10";"TILE_ILLUMINATOR_BROWN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "10"; "0";"16"
|
||||
"14"; "11";"TILE_ILLUMINATOR_TAN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "11"; "0";"16"
|
||||
"14"; "12";"TILE_ILLUMINATOR_GREY_LIGHT";"33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "12"; "0";"16"
|
||||
"14"; "13";"TILE_ILLUMINATOR_GREY_MED"; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "13"; "0";"16"
|
||||
"14"; "14";"TILE_ILLUMINATOR_GREY_DARK";"33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "14"; "0";"16"
|
||||
"14"; "15";"TILE_ILLUMINATOR_BLACK" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "15"; "0";"16"
|
||||
"15"; "0";"TILE_SANDSTONE" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "0"; "0";"16"
|
||||
"15"; "1";"TILE_SANDSTONE_WHITE" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "1"; "0";"16"
|
||||
"15"; "2";"TILE_SANDSTONE_RED" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "2"; "0";"16"
|
||||
"15"; "3";"TILE_SANDSTONE_DESERT" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "3"; "0";"16"
|
||||
"15"; "4";"TILE_SANDSTONE_BLACK" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "4"; "0";"16"
|
||||
"15"; "5";"TILE_SANDSTONE_BLACK" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "5"; "0";"16"
|
||||
"16"; "0";"TILE_LANTERN_IRON_REGULAR"; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "16769944"; "16"; "0"; "0";"16"
|
||||
"255"; "0";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "1";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "2";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "3";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "4";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "5";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "6";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "7";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "8";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "9";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "10";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "11";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "12";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "13";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "14";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"255"; "15";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "0";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "1";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "2";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "3";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "4";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "5";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "6";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "7";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "8";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "9";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "10";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "11";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "12";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "13";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "14";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"254"; "15";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16"
|
||||
"256"; "0";"TILE_NULL" ; "0"; "-1";"2600"; "0"; "0"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16"
|
||||
# Friction: 0: frictionless, <16: slippery, 16: regular, >16: sticky
|
||||
# Opacity/Lumcolor: 40-step RGB
|
||||
# Solid: whether the tile has full collision
|
||||
# movr: Movement resistance, (walkspeedmax) / (1 + (n/16)), 16 halves movement speed
|
||||
# dsty: density. As we are putting water an 1000, it is identical to specific gravity. [g/l]
|
||||
|
||||
# Defalut torch : L 77 a 47 b 59; real candlelight colour taken from properly configured camera.
|
||||
|
||||
# 16 colour palette : Old Apple Macintosh 16-colour palette
|
||||
|
||||
# Magical ice: theoretical __metallic__ ice that might form under super-high pressure (> 5 TPa). Its density is a wild guess.
|
||||
|
||||
# Off illuminator: NO OPACITY! this is intended!
|
||||
|
||||
# References:
|
||||
# * Density of various woods : http://www.engineeringtoolbox.com/wood-density-d_40.html
|
||||
# * Density of various phases of ice : http://www1.lsbu.ac.uk/water/ice_phases.html
|
||||
|
Can't render this file because it contains an unexpected character in line 1 and column 18.
|
@@ -1,10 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.terrarum.gameactors.ai.ActorAI
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface AIControlled {
|
||||
fun attachAI(ai: ActorAI)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Actor {
|
||||
|
||||
fun update(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Valid RefID is equal to or greater than 32768.
|
||||
* @return Reference ID. (32768-0x7FFF_FFFF_FFFF_FFFF)
|
||||
*/
|
||||
var referenceID: Long
|
||||
|
||||
var actorValue: ActorValue
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.terrarum.gameitem.InventoryItem
|
||||
import com.torvald.terrarum.itemproperties.ItemPropCodex
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
|
||||
class ActorInventory() {
|
||||
|
||||
@Transient val CAPACITY_MAX = 0x7FFFFFFF
|
||||
@Transient val CAPACITY_MODE_NO_ENCUMBER = 0
|
||||
@Transient val CAPACITY_MODE_COUNT = 1
|
||||
@Transient val CAPACITY_MODE_WEIGHT = 2
|
||||
|
||||
|
||||
private var capacityByCount: Int
|
||||
private var capacityByWeight: Int
|
||||
private var capacityMode: Int
|
||||
|
||||
/**
|
||||
* <ReferenceID, Amounts>
|
||||
*/
|
||||
private val itemList: HashMap<Long, Int> = HashMap()
|
||||
|
||||
/**
|
||||
* Default constructor with no encumbrance.
|
||||
*/
|
||||
init {
|
||||
capacityMode = CAPACITY_MODE_NO_ENCUMBER
|
||||
capacityByCount = 0
|
||||
capacityByWeight = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct new inventory with specified capacity.
|
||||
* @param capacity if is_weight is true, killogramme value is required, counts of items otherwise.
|
||||
* *
|
||||
* @param is_weight whether encumbrance should be calculated upon the weight of the inventory. False to use item counts.
|
||||
*/
|
||||
constructor(capacity: Int, is_weight: Boolean) : this() {
|
||||
if (is_weight) {
|
||||
capacityByWeight = capacity
|
||||
capacityMode = CAPACITY_MODE_WEIGHT
|
||||
} else {
|
||||
capacityByCount = capacity
|
||||
capacityMode = CAPACITY_MODE_COUNT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capacity of inventory
|
||||
* @return
|
||||
*/
|
||||
fun getCapacity(): Int {
|
||||
if (capacityMode == CAPACITY_MODE_NO_ENCUMBER) {
|
||||
return CAPACITY_MAX
|
||||
}
|
||||
else if (capacityMode == CAPACITY_MODE_WEIGHT) {
|
||||
return capacityByWeight
|
||||
}
|
||||
else {
|
||||
return capacityByCount
|
||||
}
|
||||
}
|
||||
|
||||
fun getCapacityMode(): Int {
|
||||
return capacityMode
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference to the itemList
|
||||
* @return
|
||||
*/
|
||||
fun getItemList(): Map<Long, Int>? {
|
||||
return itemList
|
||||
}
|
||||
|
||||
/**
|
||||
* Get clone of the itemList
|
||||
* @return
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun getCopyOfItemList(): Map<Long, Int>? {
|
||||
return itemList.clone() as Map<Long, Int>
|
||||
}
|
||||
|
||||
fun getTotalWeight(): Float {
|
||||
var weight = 0f
|
||||
|
||||
for (item in itemList.entries) {
|
||||
weight += ItemPropCodex.getItem(item.key).mass * item.value
|
||||
}
|
||||
|
||||
return weight
|
||||
}
|
||||
|
||||
fun getTotalCount(): Int {
|
||||
var count = 0
|
||||
|
||||
for (item in itemList.entries) {
|
||||
count += item.value
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
fun getTotalUniqueCount(): Int {
|
||||
return itemList.entries.size
|
||||
}
|
||||
|
||||
fun appendToPocket(item: InventoryItem) {
|
||||
appendToPocket(item, 1)
|
||||
}
|
||||
|
||||
fun appendToPocket(item: InventoryItem, count: Int) {
|
||||
val key = item.itemID
|
||||
|
||||
// if (key == Player.PLAYER_REF_ID)
|
||||
// throw new IllegalArgumentException("Attempted to put player into the inventory.");
|
||||
|
||||
if (itemList.containsKey(key))
|
||||
// increment amount if it already has specified item
|
||||
itemList.put(key, itemList[key]!! + count)
|
||||
else
|
||||
// add new entry if it does not have specified item
|
||||
itemList.put(key, count)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the itemList contains too many items
|
||||
* @return
|
||||
*/
|
||||
fun isEncumbered(): Boolean {
|
||||
if (getCapacityMode() == CAPACITY_MODE_WEIGHT) {
|
||||
return capacityByWeight < getTotalWeight()
|
||||
} else if (getCapacityMode() == CAPACITY_MODE_COUNT) {
|
||||
return capacityByCount < getTotalWeight()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.terrarum.KVHashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
class ActorValue : KVHashMap()
|
||||
@@ -1,805 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
import com.torvald.terrarum.*
|
||||
import com.torvald.terrarum.gamemap.GameMap
|
||||
import com.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.torvald.spriteanimation.SpriteAnimation
|
||||
import com.jme3.math.FastMath
|
||||
import com.torvald.terrarum.tileproperties.TileNameCode
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
|
||||
/**
|
||||
* Base class for every actor that has physical (or visible) body. This includes furnishings, paintings, gadgets, etc.
|
||||
*
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
open class ActorWithBody constructor() : Actor, Visible, Glowing {
|
||||
|
||||
override var actorValue: ActorValue = ActorValue()
|
||||
|
||||
var hitboxTranslateX: Float = 0.toFloat()// relative to spritePosX
|
||||
var hitboxTranslateY: Float = 0.toFloat()// relative to spritePosY
|
||||
var baseHitboxW: Int = 0
|
||||
var baseHitboxH: Int = 0
|
||||
|
||||
/**
|
||||
* Velocity for newtonian sim.
|
||||
* Fluctuation in, otherwise still, velocity is equal to acceleration.
|
||||
|
||||
* Acceleration: used in code like:
|
||||
* veloY += 3.0
|
||||
* +3.0 is acceleration. You __accumulate__ acceleration to the velocity.
|
||||
*/
|
||||
@Volatile var veloX: Float = 0.toFloat()
|
||||
@Volatile var veloY: Float = 0.toFloat()
|
||||
@Transient private val VELO_HARD_LIMIT = 10000f
|
||||
|
||||
var grounded = false
|
||||
|
||||
@Transient var sprite: SpriteAnimation? = null
|
||||
@Transient var spriteGlow: SpriteAnimation? = null
|
||||
/** Default to 'false' */
|
||||
var isVisible = false
|
||||
/** Default to 'true' */
|
||||
var isUpdate = true
|
||||
|
||||
var isNoSubjectToGrav = false
|
||||
var isNoCollideWorld = false
|
||||
var isNoSubjectToFluidResistance = false
|
||||
|
||||
internal var baseSpriteWidth: Int = 0
|
||||
internal var baseSpriteHeight: Int = 0
|
||||
|
||||
override var referenceID: Long = 0L
|
||||
/**
|
||||
* Positions: top-left point
|
||||
*/
|
||||
@Volatile var hitbox: Hitbox? = null
|
||||
@Volatile @Transient var nextHitbox: Hitbox? = null
|
||||
|
||||
/**
|
||||
* Physical properties
|
||||
*/
|
||||
@Volatile @Transient var scale = 1f
|
||||
@Volatile @Transient var mass = 2f
|
||||
@Transient private val MASS_LOWEST = 2f
|
||||
/** Valid range: [0, 1] */
|
||||
var elasticity = 0f
|
||||
set(value) {
|
||||
if (value < 0)
|
||||
throw IllegalArgumentException("[ActorWithBody] $value: valid elasticity value is [0, 1].")
|
||||
else if (value > 1) {
|
||||
println("[ActorWithBody] Elasticity were capped to 1.")
|
||||
field = ELASTICITY_MAX
|
||||
}
|
||||
else
|
||||
field = value * ELASTICITY_MAX
|
||||
}
|
||||
@Transient private val ELASTICITY_MAX = 0.993f
|
||||
private var density = 1000f
|
||||
|
||||
/**
|
||||
* Gravitational Constant G. Load from gamemap.
|
||||
* [m / s^2]
|
||||
* s^2 = 1/FPS = 1/60 if FPS is targeted to 60
|
||||
* meter to pixel : 24/FPS
|
||||
*/
|
||||
@Transient private val METER = 24f
|
||||
/**
|
||||
* [m / s^2] * SI_TO_GAME_ACC -> [px / IFrame^2]
|
||||
*/
|
||||
@Transient private val SI_TO_GAME_ACC = METER / FastMath.sqr(Terrarum.TARGET_FPS.toFloat())
|
||||
/**
|
||||
* [m / s] * SI_TO_GAME_VEL -> [px / IFrame]
|
||||
*/
|
||||
@Transient private val SI_TO_GAME_VEL = METER / Terrarum.TARGET_FPS
|
||||
|
||||
@Transient private var gravitation: Float = 0.toFloat()
|
||||
@Transient private val DRAG_COEFF = 1f
|
||||
|
||||
@Transient private val CONTACT_AREA_TOP = 0
|
||||
@Transient private val CONTACT_AREA_RIGHT = 1
|
||||
@Transient private val CONTACT_AREA_BOTTOM = 2
|
||||
@Transient private val CONTACT_AREA_LEFT = 3
|
||||
|
||||
@Transient private val UD_COMPENSATOR_MAX = TSIZE
|
||||
@Transient private val LR_COMPENSATOR_MAX = TSIZE
|
||||
@Transient private val TILE_AUTOCLIMB_RATE = 4
|
||||
|
||||
/**
|
||||
* A constant to make falling faster so that the game is more playable
|
||||
*/
|
||||
@Transient private val G_MUL_PLAYABLE_CONST = 1.4142f
|
||||
|
||||
@Transient private val EVENT_MOVE_TOP = 0
|
||||
@Transient private val EVENT_MOVE_RIGHT = 1
|
||||
@Transient private val EVENT_MOVE_BOTTOM = 2
|
||||
@Transient private val EVENT_MOVE_LEFT = 3
|
||||
@Transient private val EVENT_MOVE_NONE = -1
|
||||
|
||||
@Transient internal var eventMoving = EVENT_MOVE_NONE // cannot collide both X-axis and Y-axis, or else jump control breaks up.
|
||||
|
||||
/**
|
||||
* in milliseconds
|
||||
*/
|
||||
@Transient val INVINCIBILITY_TIME = 500
|
||||
|
||||
/**
|
||||
* Will ignore fluid resistance if (submerged height / actor height) <= this var
|
||||
*/
|
||||
@Transient private val FLUID_RESISTANCE_IGNORE_THRESHOLD_RATIO = 0.2f
|
||||
@Transient private val FLUID_RESISTANCE_APPLY_FULL_RATIO = 0.5f
|
||||
|
||||
@Transient private val map: GameMap
|
||||
|
||||
@Transient private val MASS_DEFAULT = 60f
|
||||
|
||||
init {
|
||||
do {
|
||||
referenceID = HQRNG().nextLong() // set new ID
|
||||
} while (Terrarum.game.hasActor(referenceID)) // check for collision
|
||||
|
||||
map = Terrarum.game.map
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param w
|
||||
* *
|
||||
* @param h
|
||||
* *
|
||||
* @param tx +: translate drawn sprite to LEFT.
|
||||
* *
|
||||
* @param ty +: translate drawn sprite to DOWN.
|
||||
* *
|
||||
* @see ActorWithBody.drawBody
|
||||
* @see ActorWithBody.drawGlow
|
||||
*/
|
||||
fun setHitboxDimension(w: Int, h: Int, tx: Int, ty: Int) {
|
||||
baseHitboxH = h
|
||||
baseHitboxW = w
|
||||
hitboxTranslateX = tx.toFloat()
|
||||
hitboxTranslateY = ty.toFloat()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hitbox position from bottom-center point
|
||||
* @param x
|
||||
* *
|
||||
* @param y
|
||||
*/
|
||||
fun setPosition(x: Float, y: Float) {
|
||||
hitbox = Hitbox(
|
||||
x - (baseHitboxW / 2 - hitboxTranslateX) * scale, y - (baseHitboxH - hitboxTranslateY) * scale, baseHitboxW * scale, baseHitboxH * scale)
|
||||
|
||||
nextHitbox = Hitbox(
|
||||
x - (baseHitboxW / 2 - hitboxTranslateX) * scale, y - (baseHitboxH - hitboxTranslateY) * scale, baseHitboxW * scale, baseHitboxH * scale)
|
||||
}
|
||||
|
||||
private fun updatePhysicalInfos() {
|
||||
scale = actorValue.getAsFloat(AVKey.SCALE) ?: 1f
|
||||
mass = (actorValue.getAsFloat(AVKey.BASEMASS) ?: MASS_DEFAULT) * FastMath.pow(scale, 3f)
|
||||
if (elasticity != 0f) elasticity = 0f
|
||||
}
|
||||
|
||||
override fun update(gc: GameContainer, delta_t: Int) {
|
||||
if (isUpdate) {
|
||||
updatePhysicalInfos()
|
||||
/**
|
||||
* Update variables
|
||||
*/
|
||||
// make NoClip work for player
|
||||
if (this is Player) {
|
||||
isNoSubjectToGrav = isPlayerNoClip
|
||||
isNoCollideWorld = isPlayerNoClip
|
||||
isNoSubjectToFluidResistance = isPlayerNoClip
|
||||
}
|
||||
|
||||
// clamp to the minimum possible mass
|
||||
if (mass < MASS_LOWEST) mass = MASS_LOWEST
|
||||
|
||||
// set sprite dimension vars if there IS sprite for the actor
|
||||
if (sprite != null) {
|
||||
baseSpriteHeight = sprite!!.height
|
||||
baseSpriteWidth = sprite!!.width
|
||||
}
|
||||
|
||||
// copy gravitational constant from the map the actor is in
|
||||
gravitation = map.gravitation
|
||||
|
||||
// Auto climb rate. Clamp to TSIZE
|
||||
AUTO_CLIMB_RATE = Math.min(TSIZE / 8 * FastMath.sqrt(scale), TSIZE.toFloat()).toInt()
|
||||
|
||||
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
||||
if (!isNoSubjectToGrav) {
|
||||
applyGravitation()
|
||||
applyBuoyancy()
|
||||
}
|
||||
|
||||
// hard limit velocity
|
||||
if (veloX > VELO_HARD_LIMIT) veloX = VELO_HARD_LIMIT
|
||||
if (veloY > VELO_HARD_LIMIT) veloY = VELO_HARD_LIMIT
|
||||
// limit velocity by fluid resistance
|
||||
//int tilePropResistance = getTileMvmtRstc();
|
||||
//if (!noSubjectToFluidResistance) {
|
||||
// veloX *= mvmtRstcToMultiplier(tilePropResistance);
|
||||
// veloY *= mvmtRstcToMultiplier(tilePropResistance);
|
||||
//}
|
||||
|
||||
|
||||
// Set 'next' position (hitbox) to fiddle with
|
||||
updateNextHitboxFromVelo()
|
||||
|
||||
|
||||
// if not horizontally moving then ...
|
||||
//if (Math.abs(veloX) < 0.5) { // fix for special situations (see fig. 1 at the bottom of the source)
|
||||
// updateVerticalPos();
|
||||
// updateHorizontalPos();
|
||||
//}
|
||||
//else {
|
||||
// compensate for colliding
|
||||
updateHorizontalPos()
|
||||
updateVerticalPos()
|
||||
//}
|
||||
|
||||
// apply our compensation to actual hitbox
|
||||
updateHitboxX()
|
||||
updateHitboxY()
|
||||
|
||||
// make sure the actor does not go out of the map
|
||||
clampNextHitbox()
|
||||
clampHitbox()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply gravitation to the every falling body (unless not levitating)
|
||||
|
||||
* Apply only if not grounded; normal force is not implemented (and redundant)
|
||||
* so we manually reset G to zero (not applying G. force) if grounded.
|
||||
*/
|
||||
// FIXME abnormal jump behaviour if mass < 2, same thing happens if mass == 0 (but zero mass is invalid anyway).
|
||||
private fun applyGravitation() {
|
||||
if (!grounded) {
|
||||
/**
|
||||
* weight; gravitational force in action
|
||||
* W = mass * G (9.8 [m/s^2])
|
||||
*/
|
||||
val W = gravitation * mass
|
||||
/**
|
||||
* Drag of atmosphere
|
||||
* D = Cd (drag coefficient) * 0.5 * rho (density) * V^2 (velocity) * A (area)
|
||||
*/
|
||||
val A = scale * scale
|
||||
val D = DRAG_COEFF * 0.5f * 1.292f * veloY * veloY * A
|
||||
|
||||
veloY += clampCeil(
|
||||
(W - D) / mass * SI_TO_GAME_ACC * G_MUL_PLAYABLE_CONST, VELO_HARD_LIMIT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVerticalPos() {
|
||||
if (!isNoCollideWorld) {
|
||||
if (veloY >= 0) { // check downward
|
||||
if (isColliding(CONTACT_AREA_BOTTOM)) { // the ground has dug into the body
|
||||
adjustHitBottom()
|
||||
elasticReflectY()
|
||||
grounded = true
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_BOTTOM, 0, 1)) { // the actor is standing ON the ground
|
||||
elasticReflectY()
|
||||
grounded = true
|
||||
}
|
||||
else { // the actor is not grounded at all
|
||||
grounded = false
|
||||
}
|
||||
}
|
||||
else if (veloY < 0) { // check upward
|
||||
grounded = false
|
||||
if (isColliding(CONTACT_AREA_TOP)) { // the ceiling has dug into the body
|
||||
adjustHitTop()
|
||||
elasticReflectY()
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_TOP, 0, -1)) { // the actor is touching the ceiling
|
||||
elasticReflectY() // reflect on ceiling, for reversed gravity
|
||||
}
|
||||
else { // the actor is not grounded at all
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustHitBottom() {
|
||||
val newX = nextHitbox!!.pointedX // look carefully, getPos or getPointed
|
||||
// int-ify posY of nextHitbox
|
||||
nextHitbox!!.setPositionYFromPoint(FastMath.floor(nextHitbox!!.pointedY).toFloat())
|
||||
|
||||
var newYOff = 0 // always positive
|
||||
|
||||
// count up Y offset until the actor is not touching the ground
|
||||
var colliding: Boolean
|
||||
do {
|
||||
newYOff += 1
|
||||
colliding = isColliding(CONTACT_AREA_BOTTOM, 0, -newYOff)
|
||||
} while (colliding)
|
||||
|
||||
val newY = nextHitbox!!.pointedY - newYOff
|
||||
nextHitbox!!.setPositionFromPoint(newX, newY)
|
||||
}
|
||||
|
||||
private fun adjustHitTop() {
|
||||
val newX = nextHitbox!!.posX
|
||||
// int-ify posY of nextHitbox
|
||||
nextHitbox!!.setPositionY(FastMath.ceil(nextHitbox!!.posY).toFloat())
|
||||
|
||||
var newYOff = 0 // always positive
|
||||
|
||||
// count up Y offset until the actor is not touching the ceiling
|
||||
var colliding: Boolean
|
||||
do {
|
||||
newYOff += 1
|
||||
colliding = isColliding(CONTACT_AREA_TOP, 0, newYOff)
|
||||
} while (colliding)
|
||||
|
||||
val newY = nextHitbox!!.posY + newYOff
|
||||
nextHitbox!!.setPosition(newX, newY)
|
||||
}
|
||||
|
||||
private fun updateHorizontalPos() {
|
||||
if (!isNoCollideWorld) {
|
||||
if (veloX >= 0.5) { // check right
|
||||
if (isColliding(CONTACT_AREA_RIGHT) && !isColliding(CONTACT_AREA_LEFT)) {
|
||||
// the actor is embedded to the wall
|
||||
adjustHitRight()
|
||||
elasticReflectX()
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_RIGHT, 1, 0) && !isColliding(CONTACT_AREA_LEFT, -1, 0)) {
|
||||
// the actor is touching the wall
|
||||
elasticReflectX()
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
else if (veloX <= -0.5) { // check left
|
||||
// System.out.println("collidingleft");
|
||||
if (isColliding(CONTACT_AREA_LEFT) && !isColliding(CONTACT_AREA_RIGHT)) {
|
||||
// the actor is embedded to the wall
|
||||
adjustHitLeft()
|
||||
elasticReflectX()
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_LEFT, -1, 0) && !isColliding(CONTACT_AREA_RIGHT, 1, 0)) {
|
||||
// the actor is touching the wall
|
||||
elasticReflectX()
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
else { // check both sides?
|
||||
// System.out.println("updatehorizontal - |velo| < 0.5");
|
||||
//if (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT)) {
|
||||
// elasticReflectX()
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustHitRight() {
|
||||
val newY = nextHitbox!!.posY // look carefully, posY or pointedY
|
||||
// int-ify posY of nextHitbox
|
||||
nextHitbox!!.setPositionX(FastMath.floor(nextHitbox!!.posX + nextHitbox!!.width) - nextHitbox!!.width)
|
||||
|
||||
var newXOff = 0 // always positive
|
||||
|
||||
// count up Y offset until the actor is not touching the wall
|
||||
var colliding: Boolean
|
||||
do {
|
||||
newXOff += 1
|
||||
colliding = isColliding(CONTACT_AREA_BOTTOM, -newXOff, 0)
|
||||
} while (newXOff < TSIZE && colliding)
|
||||
|
||||
val newX = nextHitbox!!.posX - newXOff -1 // -1: Q&D way to prevent the actor sticking to the wall and won't detach
|
||||
nextHitbox!!.setPosition(newX, newY)
|
||||
}
|
||||
|
||||
private fun adjustHitLeft() {
|
||||
val newY = nextHitbox!!.posY
|
||||
// int-ify posY of nextHitbox
|
||||
nextHitbox!!.setPositionX(FastMath.ceil(nextHitbox!!.posX).toFloat())
|
||||
|
||||
var newXOff = 0 // always positive
|
||||
|
||||
// count up Y offset until the actor is not touching the wall
|
||||
var colliding: Boolean
|
||||
do {
|
||||
newXOff += 1
|
||||
colliding = isColliding(CONTACT_AREA_TOP, newXOff, 0)
|
||||
} while (newXOff < TSIZE && colliding)
|
||||
|
||||
val newX = nextHitbox!!.posX + newXOff +1 // +1: Q&D way to prevent the actor sticking to the wall and won't detach
|
||||
nextHitbox!!.setPosition(newX, newY)
|
||||
}
|
||||
|
||||
private fun elasticReflectX() {
|
||||
if (veloX != 0f) veloX = -veloX * elasticity
|
||||
}
|
||||
|
||||
private fun elasticReflectY() {
|
||||
if (veloY != 0f) veloY = -veloY * elasticity
|
||||
}
|
||||
|
||||
private fun isColliding(side: Int, tx: Int = 0, ty: Int = 0): Boolean = getContactingArea(side, tx, ty) > 1
|
||||
|
||||
private fun getContactingArea(side: Int, translateX: Int = 0, translateY: Int = 0): Int {
|
||||
var contactAreaCounter = 0
|
||||
for (i in 0..Math.round(if (side % 2 == 0) nextHitbox!!.width else nextHitbox!!.height) - 1) {
|
||||
// set tile positions
|
||||
val tileX: Int
|
||||
val tileY: Int
|
||||
if (side == CONTACT_AREA_BOTTOM) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxStart.x)
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxEnd.y) + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_TOP) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxStart.x)
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxStart.y) + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_RIGHT) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxEnd.x) + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxStart.y)
|
||||
+ i + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_LEFT) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxStart.x) + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxStart.y)
|
||||
+ i + translateY)
|
||||
}
|
||||
else {
|
||||
throw IllegalArgumentException(side.toString() + ": Wrong side input")
|
||||
}
|
||||
|
||||
// evaluate
|
||||
if (TilePropCodex.getProp(map.getTileFromTerrain(tileX, tileY) ?: TileNameCode.STONE).isSolid) {
|
||||
contactAreaCounter += 1
|
||||
}
|
||||
}
|
||||
|
||||
return contactAreaCounter
|
||||
}
|
||||
|
||||
private fun getContactingAreaFluid(side: Int, translateX: Int = 0, translateY: Int = 0): Int {
|
||||
var contactAreaCounter = 0
|
||||
for (i in 0..Math.round(if (side % 2 == 0) nextHitbox!!.width else nextHitbox!!.height) - 1) {
|
||||
// set tile positions
|
||||
val tileX: Int
|
||||
val tileY: Int
|
||||
if (side == CONTACT_AREA_BOTTOM) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxStart.x)
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxEnd.y) + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_TOP) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxStart.x)
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxStart.y) + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_RIGHT) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxEnd.x) + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxStart.y)
|
||||
+ i + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_LEFT) {
|
||||
tileX = div16TruncateToMapWidth(Math.round(nextHitbox!!.hitboxStart.x) + translateX)
|
||||
tileY = div16TruncateToMapHeight(Math.round(nextHitbox!!.hitboxStart.y)
|
||||
+ i + translateY)
|
||||
}
|
||||
else {
|
||||
throw IllegalArgumentException(side.toString() + ": Wrong side input")
|
||||
}
|
||||
|
||||
// evaluate
|
||||
if (TilePropCodex.getProp(map.getTileFromTerrain(tileX, tileY)).isFluid) {
|
||||
contactAreaCounter += 1
|
||||
}
|
||||
}
|
||||
|
||||
return contactAreaCounter
|
||||
}
|
||||
|
||||
/**
|
||||
* [N] = [kg * m / s^2]
|
||||
* F(bo) = density * submerged_volume * gravitational_acceleration [N]
|
||||
*/
|
||||
private fun applyBuoyancy() {
|
||||
val fluidDensity = tileDensity
|
||||
val submergedVolume = submergedVolume
|
||||
|
||||
if (!isPlayerNoClip && !grounded) {
|
||||
// System.out.println("density: "+density);
|
||||
veloY -= ((fluidDensity - this.density).toDouble()
|
||||
* map.gravitation.toDouble() * submergedVolume.toDouble()
|
||||
* Math.pow(mass.toDouble(), -1.0)
|
||||
* SI_TO_GAME_ACC.toDouble()).toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private val submergedVolume: Float
|
||||
get() = submergedHeight * hitbox!!.width * hitbox!!.width
|
||||
|
||||
private val submergedHeight: Float
|
||||
get() = Math.max(
|
||||
getContactingAreaFluid(CONTACT_AREA_LEFT),
|
||||
getContactingAreaFluid(CONTACT_AREA_RIGHT)
|
||||
).toFloat()
|
||||
|
||||
|
||||
/**
|
||||
* Get highest friction value from feet tiles.
|
||||
* @return
|
||||
*/
|
||||
private val tileFriction: Int
|
||||
get() {
|
||||
var friction = 0
|
||||
|
||||
//get highest fluid density
|
||||
val tilePosXStart = Math.round(nextHitbox!!.posX / TSIZE)
|
||||
val tilePosXEnd = Math.round(nextHitbox!!.hitboxEnd.x / TSIZE)
|
||||
val tilePosY = Math.round(nextHitbox!!.pointedY / TSIZE)
|
||||
for (x in tilePosXStart..tilePosXEnd) {
|
||||
val tile = map.getTileFromTerrain(x, tilePosY)
|
||||
if (TilePropCodex.getProp(tile).isFluid) {
|
||||
val thisFluidDensity = TilePropCodex.getProp(tile).friction
|
||||
|
||||
if (thisFluidDensity > friction) friction = thisFluidDensity
|
||||
}
|
||||
}
|
||||
|
||||
return friction
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest movement resistance value from tiles that the body occupies.
|
||||
* @return
|
||||
*/
|
||||
private val tileMvmtRstc: Int
|
||||
get() {
|
||||
var resistance = 0
|
||||
|
||||
//get highest fluid density
|
||||
val tilePosXStart = Math.round(nextHitbox!!.posX / TSIZE)
|
||||
val tilePosYStart = Math.round(nextHitbox!!.posY / TSIZE)
|
||||
val tilePosXEnd = Math.round(nextHitbox!!.hitboxEnd.x / TSIZE)
|
||||
val tilePosYEnd = Math.round(nextHitbox!!.hitboxEnd.y / TSIZE)
|
||||
for (y in tilePosYStart..tilePosYEnd) {
|
||||
for (x in tilePosXStart..tilePosXEnd) {
|
||||
val tile = map.getTileFromTerrain(x, y)
|
||||
if (TilePropCodex.getProp(tile).isFluid) {
|
||||
val thisFluidDensity = TilePropCodex.getProp(tile).movementResistance
|
||||
|
||||
if (thisFluidDensity > resistance) resistance = thisFluidDensity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resistance
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest density (specific gravity) value from tiles that the body occupies.
|
||||
* @return
|
||||
*/
|
||||
private val tileDensity: Int
|
||||
get() {
|
||||
var density = 0
|
||||
|
||||
//get highest fluid density
|
||||
val tilePosXStart = Math.round(nextHitbox!!.posX / TSIZE)
|
||||
val tilePosYStart = Math.round(nextHitbox!!.posY / TSIZE)
|
||||
val tilePosXEnd = Math.round(nextHitbox!!.hitboxEnd.x / TSIZE)
|
||||
val tilePosYEnd = Math.round(nextHitbox!!.hitboxEnd.y / TSIZE)
|
||||
for (y in tilePosYStart..tilePosYEnd) {
|
||||
for (x in tilePosXStart..tilePosXEnd) {
|
||||
val tile = map.getTileFromTerrain(x, y)
|
||||
if (TilePropCodex.getProp(tile).isFluid) {
|
||||
val thisFluidDensity = TilePropCodex.getProp(tile).density
|
||||
|
||||
if (thisFluidDensity > density) density = thisFluidDensity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return density
|
||||
}
|
||||
|
||||
private fun mvmtRstcToMultiplier(movementResistanceValue: Int): Float {
|
||||
return 1f / (1 + movementResistanceValue / 16f)
|
||||
}
|
||||
|
||||
private fun clampHitbox() {
|
||||
hitbox!!.setPositionFromPoint(
|
||||
clampW(hitbox!!.pointedX), clampH(hitbox!!.pointedY))
|
||||
}
|
||||
|
||||
private fun clampNextHitbox() {
|
||||
nextHitbox!!.setPositionFromPoint(
|
||||
clampW(nextHitbox!!.pointedX), clampH(nextHitbox!!.pointedY))
|
||||
}
|
||||
|
||||
private fun updateNextHitboxFromVelo() {
|
||||
val fluidResistance = mvmtRstcToMultiplier(tileMvmtRstc)
|
||||
val submergedRatio = FastMath.clamp(
|
||||
submergedHeight / nextHitbox!!.height,
|
||||
0f, 1f
|
||||
)
|
||||
val applyResistance: Boolean = !isNoSubjectToFluidResistance
|
||||
&& submergedRatio > FLUID_RESISTANCE_IGNORE_THRESHOLD_RATIO
|
||||
val resistance: Float = FastMath.interpolateLinear(
|
||||
submergedRatio,
|
||||
1f, fluidResistance
|
||||
)
|
||||
|
||||
nextHitbox!!.set(
|
||||
Math.round(hitbox!!.posX + veloX * (if (!applyResistance) 1f else resistance)).toFloat()
|
||||
, Math.round(hitbox!!.posY + veloY * (if (!applyResistance) 1f else resistance)).toFloat()
|
||||
, Math.round(baseHitboxW * scale).toFloat()
|
||||
, Math.round(baseHitboxH * scale).toFloat())
|
||||
/** Full quantisation; wonder what havoc these statements would wreak...
|
||||
*/
|
||||
}
|
||||
|
||||
private fun updateHitboxX() {
|
||||
hitbox!!.setDimension(
|
||||
nextHitbox!!.width, nextHitbox!!.height)
|
||||
hitbox!!.setPositionX(nextHitbox!!.posX)
|
||||
}
|
||||
|
||||
private fun updateHitboxY() {
|
||||
hitbox!!.setDimension(
|
||||
nextHitbox!!.width, nextHitbox!!.height)
|
||||
hitbox!!.setPositionY(nextHitbox!!.posY)
|
||||
}
|
||||
|
||||
override fun drawGlow(gc: GameContainer, g: Graphics) {
|
||||
if (isVisible && spriteGlow != null) {
|
||||
if (!sprite!!.flippedHorizontal()) {
|
||||
spriteGlow!!.render(g, hitbox!!.posX - hitboxTranslateX * scale, hitbox!!.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2, scale)
|
||||
} else {
|
||||
spriteGlow!!.render(g, hitbox!!.posX - scale, hitbox!!.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2, scale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun drawBody(gc: GameContainer, g: Graphics) {
|
||||
if (isVisible && sprite != null) {
|
||||
if (!sprite!!.flippedHorizontal()) {
|
||||
sprite!!.render(g, hitbox!!.posX - hitboxTranslateX * scale, hitbox!!.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2, scale)
|
||||
} else {
|
||||
sprite!!.render(g, hitbox!!.posX - scale, hitbox!!.posY + hitboxTranslateY * scale - (baseSpriteHeight - baseHitboxH) * scale + 2, scale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateGlowSprite(gc: GameContainer, delta: Int) {
|
||||
if (spriteGlow != null) spriteGlow!!.update(delta)
|
||||
}
|
||||
|
||||
override fun updateBodySprite(gc: GameContainer, delta: Int) {
|
||||
if (sprite != null) sprite!!.update(delta)
|
||||
}
|
||||
|
||||
private fun clampW(x: Float): Float {
|
||||
if (x < TSIZE + nextHitbox!!.width / 2) {
|
||||
return TSIZE + nextHitbox!!.width / 2
|
||||
} else if (x >= (map.width * TSIZE).toFloat() - TSIZE.toFloat() - nextHitbox!!.width / 2) {
|
||||
return (map.width * TSIZE).toFloat() - 1f - TSIZE.toFloat() - nextHitbox!!.width / 2
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private fun clampH(y: Float): Float {
|
||||
if (y < TSIZE + nextHitbox!!.height) {
|
||||
return TSIZE + nextHitbox!!.height
|
||||
} else if (y >= (map.height * TSIZE).toFloat() - TSIZE.toFloat() - nextHitbox!!.height) {
|
||||
return (map.height * TSIZE).toFloat() - 1f - TSIZE.toFloat() - nextHitbox!!.height
|
||||
} else {
|
||||
return y
|
||||
}
|
||||
}
|
||||
|
||||
private fun clampWtile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x >= map.width) {
|
||||
return map.width - 1
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private fun clampHtile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x >= map.height) {
|
||||
return map.height - 1
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private val isPlayerNoClip: Boolean
|
||||
get() = this is Player && this.isNoClip()
|
||||
|
||||
private fun quantiseTSize(v: Float): Int {
|
||||
return FastMath.floor(v / TSIZE) * TSIZE
|
||||
}
|
||||
|
||||
fun setDensity(density: Int) {
|
||||
if (density < 0)
|
||||
throw IllegalArgumentException("[ActorWithBody] $density: density cannot be negative.")
|
||||
|
||||
this.density = density.toFloat()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@Transient private val TSIZE = MapDrawer.TILE_SIZE
|
||||
private var AUTO_CLIMB_RATE = TSIZE / 8
|
||||
|
||||
private fun div16(x: Int): Int {
|
||||
if (x < 0) {
|
||||
throw IllegalArgumentException("div16: Positive integer only: " + x.toString())
|
||||
}
|
||||
return x and 0x7FFFFFFF shr 4
|
||||
}
|
||||
|
||||
private fun div16TruncateToMapWidth(x: Int): Int {
|
||||
if (x < 0)
|
||||
return 0
|
||||
else if (x >= Terrarum.game.map.width shl 4)
|
||||
return Terrarum.game.map.width - 1
|
||||
else
|
||||
return x and 0x7FFFFFFF shr 4
|
||||
}
|
||||
|
||||
private fun div16TruncateToMapHeight(y: Int): Int {
|
||||
if (y < 0)
|
||||
return 0
|
||||
else if (y >= Terrarum.game.map.height shl 4)
|
||||
return Terrarum.game.map.height - 1
|
||||
else
|
||||
return y and 0x7FFFFFFF shr 4
|
||||
}
|
||||
|
||||
private fun mod16(x: Int): Int {
|
||||
if (x < 0) {
|
||||
throw IllegalArgumentException("mod16: Positive integer only: " + x.toString())
|
||||
}
|
||||
return x and 15
|
||||
}
|
||||
|
||||
private fun clampCeil(x: Float, ceil: Float): Float {
|
||||
return if (Math.abs(x) > ceil) ceil else x
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Give new random ReferenceID and initialise ActorValue
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
= = ↑
|
||||
=== ===@!
|
||||
=↑ =↑
|
||||
=↑ =
|
||||
=↑ =
|
||||
=@ (pressing R) =
|
||||
================== ==================
|
||||
|
||||
Fig. 1: the fix was not applied
|
||||
*/
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import org.newdawn.slick.Input
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Controllable {
|
||||
|
||||
fun processInput(input: Input)
|
||||
|
||||
fun keyPressed(key: Int, c: Char)
|
||||
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.JsonFetcher
|
||||
import com.torvald.random.Fudge3
|
||||
import com.torvald.terrarum.langpack.Lang
|
||||
import com.google.gson.JsonObject
|
||||
import org.newdawn.slick.SlickException
|
||||
import java.io.IOException
|
||||
import java.security.SecureRandom
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-25.
|
||||
*/
|
||||
object CreatureRawInjector {
|
||||
|
||||
const val JSONPATH = "./res/raw/creatures/"
|
||||
private const val MULTIPLIER_RAW_ELEM_SUFFIX = AVKey.MULT
|
||||
|
||||
/**
|
||||
* 'Injects' creature raw ActorValue to the ActorValue reference provided.
|
||||
*
|
||||
* @param actorValueRef ActorValue object to be injected.
|
||||
* @param jsonFileName with extension
|
||||
*/
|
||||
@Throws(IOException::class, SlickException::class)
|
||||
fun inject(actorValueRef: ActorValue, jsonFileName: String) {
|
||||
val jsonObj = JsonFetcher.readJson(JSONPATH + jsonFileName)
|
||||
|
||||
val elementsString = arrayOf(AVKey.RACENAME, AVKey.RACENAMEPLURAL)
|
||||
val elementsFloat = arrayOf(AVKey.BASEHEIGHT, AVKey.BASEMASS, AVKey.ACCEL, AVKey.TOOLSIZE, AVKey.ENCUMBRANCE)
|
||||
val elementsFloatVariable = arrayOf(AVKey.STRENGTH, AVKey.SPEED, AVKey.JUMPPOWER, AVKey.SCALE, AVKey.SPEED)
|
||||
val elementsBoolean = arrayOf(AVKey.INTELLIGENT)
|
||||
// val elementsMultiplyFromOne = arrayOf()
|
||||
|
||||
setAVStrings(actorValueRef, elementsString, jsonObj)
|
||||
setAVFloats(actorValueRef, elementsFloat, jsonObj)
|
||||
setAVFloatsVariable(actorValueRef, elementsFloatVariable, jsonObj)
|
||||
// setAVMultiplyFromOne(actorValueRef, elementsMultiplyFromOne, jsonObj)
|
||||
setAVBooleans(actorValueRef, elementsBoolean, jsonObj)
|
||||
|
||||
actorValueRef[AVKey.ACCEL] = Player.WALK_ACCEL_BASE
|
||||
actorValueRef[AVKey.ACCELMULT] = 1f
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set actor values that have 'variable' appended. E.g. strength
|
||||
* @param avRef
|
||||
* *
|
||||
* @param elemSet
|
||||
* *
|
||||
* @param jsonObject
|
||||
*/
|
||||
private fun setAVFloatsVariable(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
|
||||
for (s in elemSet) {
|
||||
val baseValue = jsonObject.get(s).asFloat
|
||||
// roll fudge dice and get value [-3, 3] as [0, 6]
|
||||
val varSelected = Fudge3(SecureRandom()).rollForArray()
|
||||
// get multiplier from json. Assuming percentile
|
||||
val multiplier = jsonObject.get(s + MULTIPLIER_RAW_ELEM_SUFFIX).asJsonArray.get(varSelected).asInt
|
||||
val realValue = baseValue * multiplier / 100f
|
||||
|
||||
avRef[s] = realValue
|
||||
avRef[s + MULTIPLIER_RAW_ELEM_SUFFIX] = 1.0f // use multiplied value as 'base' for all sort of things
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set string actor values
|
||||
* @param avRef
|
||||
* *
|
||||
* @param elemSet
|
||||
* *
|
||||
* @param jsonObject
|
||||
*/
|
||||
private fun setAVStrings(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
|
||||
for (s in elemSet) {
|
||||
val key = jsonObject.get(s).asString
|
||||
avRef[s] = Lang[key]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set float actor values
|
||||
* @param avRef
|
||||
* *
|
||||
* @param elemSet
|
||||
* *
|
||||
* @param jsonObject
|
||||
*/
|
||||
private fun setAVFloats(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
|
||||
for (s in elemSet) {
|
||||
avRef[s] = jsonObject.get(s).asFloat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and set actor values that should multiplier be applied to the base value of 1.
|
||||
* E.g. physiquemult
|
||||
* @param avRef
|
||||
* *
|
||||
* @param elemSet
|
||||
* *
|
||||
* @param jsonObject
|
||||
*/
|
||||
private fun setAVMultiplyFromOne(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
|
||||
for (s in elemSet) {
|
||||
val baseValue = 1f
|
||||
// roll fudge dice and get value [-3, 3] as [0, 6]
|
||||
val varSelected = Fudge3(SecureRandom()).rollForArray()
|
||||
// get multiplier from json. Assuming percentile
|
||||
val multiplier = jsonObject.get(s).asJsonArray.get(varSelected).asInt
|
||||
val realValue = baseValue * multiplier / 100f
|
||||
|
||||
avRef[s] = realValue
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAVBooleans(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
|
||||
for (s in elemSet) {
|
||||
avRef[s] = jsonObject.get(s).asBoolean
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
class DroppedItem constructor() : ActorWithBody() {
|
||||
|
||||
init {
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
override fun update(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun drawBody(gc: GameContainer, g: Graphics) {
|
||||
drawBody(gc, g)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.terrarum.gameactors.faction.Faction
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Factionable {
|
||||
|
||||
var faction: HashSet<Faction>
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Glowing {
|
||||
fun drawGlow(gc: GameContainer, g: Graphics)
|
||||
|
||||
fun updateGlowSprite(gc: GameContainer, delta: Int)
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.point.Point2f
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
class Hitbox(x1: Float, y1: Float, width: Float, height: Float) {
|
||||
|
||||
var hitboxStart: Point2f
|
||||
private set
|
||||
var hitboxEnd: Point2f
|
||||
private set
|
||||
var width: Float = 0.toFloat()
|
||||
private set
|
||||
var height: Float = 0.toFloat()
|
||||
private set
|
||||
|
||||
init {
|
||||
hitboxStart = Point2f(x1, y1)
|
||||
hitboxEnd = Point2f(x1 + width, y1 + height)
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bottom-centered point of hitbox.
|
||||
* @return pointX
|
||||
*/
|
||||
val pointedX: Float
|
||||
get() = hitboxStart.x + width / 2
|
||||
|
||||
/**
|
||||
* Returns bottom-centered point of hitbox.
|
||||
* @return pointY
|
||||
*/
|
||||
val pointedY: Float
|
||||
get() = hitboxEnd.y
|
||||
|
||||
/**
|
||||
* Set to the point top left
|
||||
* @param x1
|
||||
* *
|
||||
* @param y1
|
||||
* *
|
||||
* @param width
|
||||
* *
|
||||
* @param height
|
||||
*/
|
||||
operator fun set(x1: Float, y1: Float, width: Float, height: Float) {
|
||||
hitboxStart = Point2f(x1, y1)
|
||||
hitboxEnd = Point2f(x1 + width, y1 + height)
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
|
||||
fun setPosition(x1: Float, y1: Float) {
|
||||
hitboxStart = Point2f(x1, y1)
|
||||
hitboxEnd = Point2f(x1 + width, y1 + height)
|
||||
}
|
||||
|
||||
fun setPositionX(x: Float) {
|
||||
setPosition(x, posY)
|
||||
}
|
||||
|
||||
fun setPositionY(y: Float) {
|
||||
setPosition(posX, y)
|
||||
}
|
||||
|
||||
fun setPositionFromPoint(x1: Float, y1: Float) {
|
||||
hitboxStart = Point2f(x1 - width / 2, y1 - height)
|
||||
hitboxEnd = Point2f(hitboxStart.x + width, hitboxStart.y + height)
|
||||
}
|
||||
|
||||
fun setPositionXFromPoint(x: Float) {
|
||||
setPositionFromPoint(x, pointedY)
|
||||
}
|
||||
|
||||
fun setPositionYFromPoint(y: Float) {
|
||||
setPositionFromPoint(pointedX, y)
|
||||
}
|
||||
|
||||
fun translatePosX(d: Float) {
|
||||
setPositionX(posX + d)
|
||||
}
|
||||
|
||||
fun translatePosY(d: Float) {
|
||||
setPositionY(posY + d)
|
||||
}
|
||||
|
||||
fun setDimension(w: Float, h: Float) {
|
||||
width = w
|
||||
height = h
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns x value of start point
|
||||
* @return top-left point posX
|
||||
*/
|
||||
val posX: Float
|
||||
get() = hitboxStart.x
|
||||
|
||||
/**
|
||||
* Returns y value of start point
|
||||
* @return top-left point posY
|
||||
*/
|
||||
val posY: Float
|
||||
get() = hitboxStart.y
|
||||
|
||||
val centeredX: Float
|
||||
get() = (hitboxStart.x + hitboxEnd.x) * 0.5f
|
||||
|
||||
val centeredY: Float
|
||||
get() = (hitboxStart.y + hitboxEnd.y) * 0.5f
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface LandHolder {
|
||||
|
||||
/**
|
||||
* Absolute tile index. index(x, y) = y * map.width + x
|
||||
* The arraylist will be saved in JSON format with GSON.
|
||||
*/
|
||||
var houseDesignation: ArrayList<Int>?
|
||||
fun addHouseTile(x: Int, y: Int);
|
||||
fun removeHouseTile(x: Int, y: Int);
|
||||
fun clearHouseDesignation();
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Luminous {
|
||||
|
||||
/**
|
||||
* Recommended implementation:
|
||||
*
|
||||
override var luminosity: Char
|
||||
get() = if (actorValue.get("luminosity") != null) {
|
||||
actorValue.get("luminosity") as Char
|
||||
}
|
||||
else {
|
||||
0 as Char
|
||||
}
|
||||
set(value) {
|
||||
actorValue.set("luminosity", value)
|
||||
}
|
||||
*/
|
||||
var luminosity: Int
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
import com.torvald.terrarum.gameactors.ai.ActorAI
|
||||
import com.torvald.terrarum.gameactors.faction.Faction
|
||||
import com.torvald.terrarum.gameitem.InventoryItem
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import org.newdawn.slick.GameContainer
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
open class NPCIntelligentBase : ActorWithBody()
|
||||
, AIControlled, Pocketed, CanBeAnItem, Factionable, LandHolder {
|
||||
|
||||
override var itemData: InventoryItem = object : InventoryItem {
|
||||
override var itemID = HQRNG().nextLong()
|
||||
|
||||
override var mass: Float
|
||||
get() = actorValue.get("mass") as Float
|
||||
set(value) {
|
||||
actorValue.set("mass", value)
|
||||
}
|
||||
|
||||
override var scale: Float
|
||||
get() = actorValue.get("scale") as Float
|
||||
set(value) {
|
||||
actorValue.set("scale", value)
|
||||
}
|
||||
|
||||
override fun effectWhileInPocket(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun effectWhenPickedUp(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun primaryUse(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun secondaryUse(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun effectWhenThrownAway(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Transient private var ai: ActorAI? = null
|
||||
override var inventory: ActorInventory = ActorInventory()
|
||||
|
||||
private val factionSet = HashSet<Faction>()
|
||||
|
||||
override var referenceID: Long = HQRNG().nextLong()
|
||||
|
||||
override var faction: HashSet<Faction> = HashSet()
|
||||
|
||||
override var houseDesignation: ArrayList<Int>? = null
|
||||
/**
|
||||
* Absolute tile index. index(x, y) = y * map.width + x
|
||||
* The arraylist will be saved in JSON format with GSON.
|
||||
*/
|
||||
private var houseTiles = ArrayList<Int>()
|
||||
|
||||
override fun attachItemData() {
|
||||
|
||||
}
|
||||
|
||||
override fun getItemWeight(): Float {
|
||||
return mass
|
||||
}
|
||||
|
||||
override fun addHouseTile(x: Int, y: Int) {
|
||||
houseTiles.add(Terrarum.game.map.width * y + x)
|
||||
}
|
||||
|
||||
override fun removeHouseTile(x: Int, y: Int) {
|
||||
houseTiles.remove(Terrarum.game.map.width * y + x)
|
||||
}
|
||||
|
||||
override fun clearHouseDesignation() {
|
||||
houseTiles.clear()
|
||||
}
|
||||
|
||||
override fun stopUpdateAndDraw() {
|
||||
isUpdate = false
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
override fun resumeUpdateAndDraw() {
|
||||
isUpdate = true
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
override fun attachAI(ai: ActorAI) {
|
||||
this.ai = ai
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.terrarum.mapgenerator.RoguelikeRandomiser
|
||||
import org.newdawn.slick.Color
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
class PhysTestBall : ActorWithBody {
|
||||
|
||||
private var color = Color.orange
|
||||
|
||||
constructor(): super() {
|
||||
setHitboxDimension(16, 16, 0, 0)
|
||||
isVisible = true
|
||||
mass = 10f
|
||||
|
||||
color = RoguelikeRandomiser.composeColourFrom(RoguelikeRandomiser.POTION_PRIMARY_COLSET)
|
||||
}
|
||||
|
||||
override fun drawBody(gc: GameContainer, g: Graphics) {
|
||||
g.color = color
|
||||
g.fillOval(
|
||||
hitbox!!.posX,
|
||||
hitbox!!.posY,
|
||||
hitbox!!.width,
|
||||
hitbox!!.height)
|
||||
}
|
||||
}
|
||||
@@ -1,493 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import com.torvald.terrarum.gameactors.faction.Faction
|
||||
import com.torvald.terrarum.gamecontroller.EnumKeyFunc
|
||||
import com.torvald.terrarum.gamecontroller.KeyMap
|
||||
import com.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.spriteanimation.SpriteAnimation
|
||||
import com.jme3.math.FastMath
|
||||
import org.lwjgl.input.Controller
|
||||
import org.lwjgl.input.Controllers
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Input
|
||||
import org.newdawn.slick.SlickException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
|
||||
class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, LandHolder {
|
||||
|
||||
/**
|
||||
* empirical value.
|
||||
*/
|
||||
// private transient final float JUMP_ACCELERATION_MOD = ???f / 10000f; //quadratic mode
|
||||
@Transient private val JUMP_ACCELERATION_MOD = 170f / 10000f //linear mode
|
||||
@Transient private val WALK_FRAMES_TO_MAX_ACCEL = 6
|
||||
|
||||
@Transient private val LEFT = 1
|
||||
@Transient private val RIGHT = 2
|
||||
|
||||
@Transient private val KEY_NULL = -1
|
||||
|
||||
|
||||
var vehicleRiding: Controllable? = null
|
||||
|
||||
internal var jumpCounter = 0
|
||||
internal var walkPowerCounter = 0
|
||||
@Transient private val MAX_JUMP_LENGTH = 17 // use 17; in internal frames
|
||||
|
||||
private var readonly_totalX = 0f
|
||||
private var readonly_totalY = 0f
|
||||
|
||||
internal var jumping = false
|
||||
|
||||
internal var walkHeading: Int = 0
|
||||
|
||||
@Transient private var prevHMoveKey = KEY_NULL
|
||||
@Transient private var prevVMoveKey = KEY_NULL
|
||||
|
||||
internal var noClip = false
|
||||
|
||||
@Transient private val AXIS_POSMAX = 1.0f
|
||||
@Transient private val GAMEPAD_JUMP = 5
|
||||
|
||||
@Transient private val TSIZE = MapDrawer.TILE_SIZE
|
||||
|
||||
private val factionSet = HashSet<Faction>()
|
||||
|
||||
@Transient private val BASE_DENSITY = 1020
|
||||
|
||||
/** Must be set by PlayerFactory */
|
||||
override var inventory: ActorInventory = ActorInventory()
|
||||
|
||||
/** Must be set by PlayerFactory */
|
||||
override var faction: HashSet<Faction> = HashSet()
|
||||
|
||||
override var houseDesignation: ArrayList<Int>? = null
|
||||
|
||||
override var luminosity: Int
|
||||
get() = actorValue.getAsInt(AVKey.LUMINOSITY) ?: 0
|
||||
set(value) {
|
||||
actorValue[AVKey.LUMINOSITY] = value
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Transient internal const val ACCEL_MULT_IN_FLIGHT = 0.48f
|
||||
@Transient internal const val WALK_STOP_ACCEL = 0.32f
|
||||
@Transient internal const val WALK_ACCEL_BASE = 0.32f
|
||||
|
||||
@Transient const val PLAYER_REF_ID: Long = 0x51621D
|
||||
@Transient const val BASE_HEIGHT = 40
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Player instance with empty elements (sprites, actorvalue, etc.).
|
||||
|
||||
* **Use PlayerFactory to build player!**
|
||||
|
||||
* @throws SlickException
|
||||
*/
|
||||
@Throws(SlickException::class)
|
||||
constructor() : super() {
|
||||
isVisible = true
|
||||
referenceID = PLAYER_REF_ID
|
||||
super.setDensity(BASE_DENSITY)
|
||||
}
|
||||
|
||||
override fun update(gc: GameContainer, delta_t: Int) {
|
||||
if (vehicleRiding is Player)
|
||||
throw RuntimeException("Attempted to 'ride' " + "player object.")
|
||||
|
||||
super.update(gc, delta_t)
|
||||
|
||||
updateSprite(delta_t)
|
||||
|
||||
updateMovementControl()
|
||||
|
||||
if (noClip) {
|
||||
grounded = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param left (even if the game is joypad controlled, you must give valid value)
|
||||
* *
|
||||
* @param absAxisVal (set AXIS_POSMAX if keyboard controlled)
|
||||
*/
|
||||
private fun walkHorizontal(left: Boolean, absAxisVal: Float) {
|
||||
//if ((!super.isWalledLeft() && left) || (!super.isWalledRight() && !left)) {
|
||||
readonly_totalX = veloX +
|
||||
actorValue.getAsFloat(AVKey.ACCEL)!! *
|
||||
actorValue.getAsFloat(AVKey.ACCELMULT)!! *
|
||||
FastMath.sqrt(scale) *
|
||||
applyAccelRealism(walkPowerCounter) *
|
||||
(if (left) -1 else 1).toFloat() *
|
||||
absAxisVal
|
||||
|
||||
veloX = readonly_totalX
|
||||
|
||||
if (walkPowerCounter < WALK_FRAMES_TO_MAX_ACCEL) {
|
||||
walkPowerCounter += 1
|
||||
}
|
||||
|
||||
// Clamp veloX
|
||||
veloX = absClamp(veloX, actorValue.getAsFloat(AVKey.SPEED)!!
|
||||
* actorValue.getAsFloat(AVKey.SPEEDMULT)!!
|
||||
* FastMath.sqrt(scale))
|
||||
|
||||
// Heading flag
|
||||
if (left)
|
||||
walkHeading = LEFT
|
||||
else
|
||||
walkHeading = RIGHT
|
||||
//}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param up (even if the game is joypad controlled, you must give valid value)
|
||||
* *
|
||||
* @param absAxisVal (set AXIS_POSMAX if keyboard controlled)
|
||||
*/
|
||||
private fun walkVertical(up: Boolean, absAxisVal: Float) {
|
||||
readonly_totalY = veloY +
|
||||
actorValue.getAsFloat(AVKey.ACCEL)!! *
|
||||
actorValue.getAsFloat(AVKey.ACCELMULT)!! *
|
||||
FastMath.sqrt(scale) *
|
||||
applyAccelRealism(walkPowerCounter) *
|
||||
(if (up) -1 else 1).toFloat() *
|
||||
absAxisVal
|
||||
|
||||
veloY = readonly_totalY
|
||||
|
||||
if (walkPowerCounter < WALK_FRAMES_TO_MAX_ACCEL) {
|
||||
walkPowerCounter += 1
|
||||
}
|
||||
|
||||
// Clamp veloX
|
||||
veloY = absClamp(veloY, actorValue.getAsFloat(AVKey.SPEED)!!
|
||||
* actorValue.getAsFloat(AVKey.SPEEDMULT)!!
|
||||
* FastMath.sqrt(scale))
|
||||
}
|
||||
|
||||
/**
|
||||
* For realistic accelerating while walking.
|
||||
|
||||
* Naïve 'veloX += 3' is actually like:
|
||||
|
||||
* a
|
||||
* | ------------
|
||||
* |
|
||||
* |
|
||||
* 0+------············ t
|
||||
|
||||
* which is unrealistic, so this method tries to introduce some realism by doing:
|
||||
|
||||
* a
|
||||
* | ------------
|
||||
* | ---
|
||||
* | -
|
||||
* | ---
|
||||
* 0+----··················· t
|
||||
|
||||
|
||||
* @param x
|
||||
*/
|
||||
private fun applyAccelRealism(x: Int): Float {
|
||||
return 0.5f + 0.5f * -FastMath.cos(10 * x / (WALK_FRAMES_TO_MAX_ACCEL * FastMath.PI))
|
||||
}
|
||||
|
||||
private fun walkHStop() {
|
||||
if (veloX > 0) {
|
||||
veloX -= actorValue.getAsFloat(AVKey.ACCEL)!! *
|
||||
actorValue.getAsFloat(AVKey.ACCELMULT)!! *
|
||||
FastMath.sqrt(scale)
|
||||
|
||||
// compensate overshoot
|
||||
if (veloX < 0) veloX = 0f
|
||||
} else if (veloX < 0) {
|
||||
veloX += actorValue.getAsFloat(AVKey.ACCEL)!! *
|
||||
actorValue.getAsFloat(AVKey.ACCELMULT)!! *
|
||||
FastMath.sqrt(scale)
|
||||
|
||||
// compensate overshoot
|
||||
if (veloX > 0) veloX = 0f
|
||||
} else {
|
||||
veloX = 0f
|
||||
}
|
||||
|
||||
walkPowerCounter = 0
|
||||
}
|
||||
|
||||
private fun walkVStop() {
|
||||
if (veloY > 0) {
|
||||
veloY -= WALK_STOP_ACCEL *
|
||||
actorValue.getAsFloat(AVKey.ACCELMULT)!! *
|
||||
FastMath.sqrt(scale)
|
||||
|
||||
// compensate overshoot
|
||||
if (veloY < 0)
|
||||
veloY = 0f
|
||||
} else if (veloY < 0) {
|
||||
veloY += WALK_STOP_ACCEL *
|
||||
actorValue.getAsFloat(AVKey.ACCELMULT)!! *
|
||||
FastMath.sqrt(scale)
|
||||
|
||||
// compensate overshoot
|
||||
if (veloY > 0) veloY = 0f
|
||||
} else {
|
||||
veloY = 0f
|
||||
}
|
||||
|
||||
walkPowerCounter = 0
|
||||
}
|
||||
|
||||
private fun updateMovementControl() {
|
||||
if (!noClip) {
|
||||
if (grounded) {
|
||||
actorValue.set(AVKey.ACCELMULT, 1f)
|
||||
} else {
|
||||
actorValue.set(AVKey.ACCELMULT, ACCEL_MULT_IN_FLIGHT)
|
||||
}
|
||||
} else {
|
||||
actorValue.set(AVKey.ACCELMULT, 1f)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processInput(input: Input) {
|
||||
var gamepad: Controller? = null
|
||||
var axisX = 0f
|
||||
var axisY = 0f
|
||||
var axisRX = 0f
|
||||
var axisRY = 0f
|
||||
if (Terrarum.hasController) {
|
||||
gamepad = Controllers.getController(0)
|
||||
axisX = gamepad!!.getAxisValue(0)
|
||||
axisY = gamepad.getAxisValue(1)
|
||||
axisRX = gamepad.getAxisValue(2)
|
||||
axisRY = gamepad.getAxisValue(3)
|
||||
|
||||
if (Math.abs(axisX) < Terrarum.CONTROLLER_DEADZONE) axisX = 0f
|
||||
if (Math.abs(axisY) < Terrarum.CONTROLLER_DEADZONE) axisY = 0f
|
||||
if (Math.abs(axisRX) < Terrarum.CONTROLLER_DEADZONE) axisRX = 0f
|
||||
if (Math.abs(axisRY) < Terrarum.CONTROLLER_DEADZONE) axisRY = 0f
|
||||
}
|
||||
|
||||
/**
|
||||
* L-R stop
|
||||
*/
|
||||
if (Terrarum.hasController) {
|
||||
if (axisX == 0f) {
|
||||
walkHStop()
|
||||
}
|
||||
} else {
|
||||
// ↑F, ↑S
|
||||
if (!isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHStop()
|
||||
prevHMoveKey = KEY_NULL
|
||||
}
|
||||
}
|
||||
/**
|
||||
* U-D stop
|
||||
*/
|
||||
if (Terrarum.hasController) {
|
||||
if (axisY == 0f) {
|
||||
walkVStop()
|
||||
}
|
||||
} else {
|
||||
// ↑E
|
||||
// ↑D
|
||||
if (isNoClip()
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_UP)
|
||||
&& !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVStop()
|
||||
prevVMoveKey = KEY_NULL
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Left/Right movement
|
||||
*/
|
||||
|
||||
if (Terrarum.hasController) {
|
||||
if (axisX != 0f) {
|
||||
walkHorizontal(axisX < 0, AXIS_POSMAX)
|
||||
}
|
||||
} else {
|
||||
// ↑F, ↓S
|
||||
if (isFuncDown(input, EnumKeyFunc.MOVE_RIGHT) && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT)) {
|
||||
walkHorizontal(false, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHorizontal(true, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) {
|
||||
walkHorizontal(false, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)
|
||||
} else if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHorizontal(true, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)
|
||||
}
|
||||
}// ↓F, ↓S
|
||||
// ↓F, ↑S
|
||||
}
|
||||
|
||||
/**
|
||||
* Up/Down movement
|
||||
*/
|
||||
if (noClip) {
|
||||
if (Terrarum.hasController) {
|
||||
if (axisY != 0f) {
|
||||
walkVertical(axisY > 0, AXIS_POSMAX)
|
||||
}
|
||||
} else {
|
||||
// ↑E
|
||||
// ↓D
|
||||
if (isFuncDown(input, EnumKeyFunc.MOVE_DOWN) && !isFuncDown(input, EnumKeyFunc.MOVE_UP)) {
|
||||
walkVertical(false, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVertical(true, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) {
|
||||
walkVertical(false, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)
|
||||
} else if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVertical(true, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)
|
||||
}
|
||||
}// ↓E
|
||||
// ↓D
|
||||
// ↓E
|
||||
// ↑D
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump control
|
||||
*/
|
||||
if (isFuncDown(input, EnumKeyFunc.JUMP) || Terrarum.hasController && gamepad!!.isButtonPressed(GAMEPAD_JUMP)) {
|
||||
if (!noClip) {
|
||||
if (grounded) {
|
||||
jumping = true
|
||||
}
|
||||
jump()
|
||||
} else {
|
||||
walkVertical(true, AXIS_POSMAX)
|
||||
}
|
||||
} else {
|
||||
jumping = false
|
||||
jumpCounter = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun keyPressed(key: Int, c: Char) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See ./work_files/Jump\ power\ by\ pressing\ time.gcx
|
||||
*/
|
||||
private fun jump() {
|
||||
if (jumping) {
|
||||
val len = MAX_JUMP_LENGTH.toFloat()
|
||||
val pwr = actorValue.getAsFloat(AVKey.JUMPPOWER)!! * (actorValue.getAsFloat(AVKey.JUMPPOWERMULT) ?: 1f)
|
||||
|
||||
// increment jump counter
|
||||
if (jumpCounter < len) jumpCounter += 1
|
||||
|
||||
// linear time mode
|
||||
val init = (len + 1) / 2f
|
||||
var timedJumpCharge = init - init / len * jumpCounter
|
||||
if (timedJumpCharge < 0) timedJumpCharge = 0f
|
||||
|
||||
val jumpAcc = pwr * timedJumpCharge * JUMP_ACCELERATION_MOD * FastMath.sqrt(scale)
|
||||
|
||||
veloY -= jumpAcc
|
||||
|
||||
// try concave mode?
|
||||
}
|
||||
|
||||
// for mob ai:
|
||||
//super.setVeloY(veloY
|
||||
// -
|
||||
// pwr * FastMath.sqrt(scale)
|
||||
//);
|
||||
}
|
||||
|
||||
private fun jumpFuncLin(pwr: Float, len: Float): Float {
|
||||
return -(pwr / len) * jumpCounter
|
||||
}
|
||||
|
||||
private fun jumpFuncSqu(pwr: Float, len: Float): Float {
|
||||
return pwr / (len * len) * (jumpCounter - len * jumpCounter - len) - pwr
|
||||
}
|
||||
|
||||
private fun jumpFuncExp(pwr: Float, len: Float): Float {
|
||||
val a = FastMath.pow(pwr + 1, 1 / len)
|
||||
return -FastMath.pow(a, len) + 1
|
||||
}
|
||||
|
||||
private fun isFuncDown(input: Input, fn: EnumKeyFunc): Boolean {
|
||||
return input.isKeyDown(KeyMap.getKeyCode(fn))
|
||||
}
|
||||
|
||||
private fun absClamp(i: Float, ceil: Float): Float {
|
||||
if (i > 0)
|
||||
return if (i > ceil) ceil else i
|
||||
else if (i < 0)
|
||||
return if (-i > ceil) -ceil else i
|
||||
else
|
||||
return 0f
|
||||
}
|
||||
|
||||
private fun updateSprite(delta_t: Int) {
|
||||
sprite!!.update(delta_t)
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow!!.update(delta_t)
|
||||
}
|
||||
|
||||
if (grounded) {
|
||||
if (walkHeading == LEFT) {
|
||||
sprite!!.flip(true, false)
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow!!.flip(true, false)
|
||||
}
|
||||
} else {
|
||||
sprite!!.flip(false, false)
|
||||
if (spriteGlow != null) {
|
||||
spriteGlow!!.flip(false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isNoClip(): Boolean {
|
||||
return noClip
|
||||
}
|
||||
|
||||
fun setNoClip(b: Boolean) {
|
||||
noClip = b
|
||||
}
|
||||
|
||||
override fun addHouseTile(x: Int, y: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun removeHouseTile(x: Int, y: Int) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun clearHouseDesignation() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Pocketed {
|
||||
|
||||
var inventory: ActorInventory
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Visible {
|
||||
fun drawBody(gc: GameContainer, g: Graphics)
|
||||
|
||||
fun updateBodySprite(gc: GameContainer, delta: Int)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors.ai
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface ActorAI {
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.torvald.terrarum.gameactors.faction
|
||||
|
||||
import com.torvald.random.HQRNG
|
||||
import java.util.HashSet
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-15.
|
||||
*/
|
||||
class Faction(factionName: String) {
|
||||
|
||||
lateinit var factionName: String
|
||||
lateinit var factionAmicable: HashSet<String>
|
||||
lateinit var factionNeutral: HashSet<String>
|
||||
lateinit var factionHostile: HashSet<String>
|
||||
lateinit var factionFearful: HashSet<String>
|
||||
var factionID: Long = HQRNG().nextLong()
|
||||
|
||||
init {
|
||||
this.factionName = factionName
|
||||
factionAmicable = HashSet<String>()
|
||||
factionNeutral = HashSet<String>()
|
||||
factionHostile = HashSet<String>()
|
||||
factionFearful = HashSet<String>()
|
||||
}
|
||||
|
||||
fun renewFactionName(factionName: String) {
|
||||
this.factionName = factionName
|
||||
}
|
||||
|
||||
fun addFactionAmicable(faction: String) {
|
||||
factionAmicable.add(faction)
|
||||
}
|
||||
|
||||
fun addFactionNeutral(faction: String) {
|
||||
factionNeutral.add(faction)
|
||||
}
|
||||
|
||||
fun addFactionHostile(faction: String) {
|
||||
factionHostile.add(faction)
|
||||
}
|
||||
|
||||
fun addFactionFearful(faction: String) {
|
||||
factionFearful.add(faction)
|
||||
}
|
||||
|
||||
fun removeFactionAmicable(faction: String) {
|
||||
factionAmicable.remove(faction)
|
||||
}
|
||||
|
||||
fun removeFactionNeutral(faction: String) {
|
||||
factionNeutral.remove(faction)
|
||||
}
|
||||
|
||||
fun removeFactionHostile(faction: String) {
|
||||
factionHostile.remove(faction)
|
||||
}
|
||||
|
||||
fun removeFactionFearful(faction: String) {
|
||||
factionFearful.remove(faction)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.torvald.terrarum.gamecontroller
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
enum class EnumKeyFunc {
|
||||
UI_CONSOLE, UI_BASIC_INFO,
|
||||
MOVE_LEFT, MOVE_RIGHT, MOVE_UP, MOVE_DOWN, JUMP
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package com.torvald.terrarum.gamecontroller
|
||||
|
||||
import com.torvald.terrarum.gameactors.Controllable
|
||||
import com.torvald.terrarum.gameactors.Player
|
||||
import com.torvald.terrarum.mapdrawer.MapCamera
|
||||
import com.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.tileproperties.TileNameCode
|
||||
import com.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import com.torvald.terrarum.ui.UIHandler
|
||||
import org.newdawn.slick.Input
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
object GameController {
|
||||
|
||||
fun processInput(input: Input) {
|
||||
val mouseTileX = ((MapCamera.cameraX + input.mouseX / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
|
||||
val mouseTileY = ((MapCamera.cameraY + input.mouseY / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
|
||||
|
||||
|
||||
KeyToggler.update(input)
|
||||
|
||||
|
||||
if (!Terrarum.game.consoleHandler.isTakingControl) {
|
||||
if (Terrarum.game.player.vehicleRiding != null) {
|
||||
Terrarum.game.player.vehicleRiding!!.processInput(input)
|
||||
}
|
||||
|
||||
Terrarum.game.player.processInput(input)
|
||||
|
||||
for (ui in Terrarum.game.uiContainer) {
|
||||
ui.processInput(input)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Terrarum.game.consoleHandler.processInput(input)
|
||||
}
|
||||
|
||||
|
||||
if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
|
||||
// test tile remove
|
||||
try {
|
||||
Terrarum.game.map.setTileTerrain(mouseTileX, mouseTileY, TileNameCode.AIR)
|
||||
// terrarum.game.map.setTileWall(mouseTileX, mouseTileY, TileNameCode.AIR);
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
else if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) {
|
||||
// test tile place
|
||||
try {
|
||||
Terrarum.game.map.setTileTerrain(mouseTileX, mouseTileY, Terrarum.game.player.actorValue.getAsInt("selectedtile")!!)
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun keyPressed(key: Int, c: Char) {
|
||||
if (keyPressedByCode(key, EnumKeyFunc.UI_CONSOLE)) {
|
||||
Terrarum.game.consoleHandler.toggleOpening()
|
||||
}
|
||||
else if (keyPressedByCode(key, EnumKeyFunc.UI_BASIC_INFO)) {
|
||||
Terrarum.game.debugWindow.toggleOpening()
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!Terrarum.game.consoleHandler.isTakingControl) {
|
||||
if (Terrarum.game.player.vehicleRiding != null) {
|
||||
Terrarum.game.player.vehicleRiding!!.keyPressed(key, c)
|
||||
}
|
||||
|
||||
Terrarum.game.player.keyPressed(key, c)
|
||||
}
|
||||
else {
|
||||
Terrarum.game.consoleHandler.keyPressed(key, c)
|
||||
}
|
||||
|
||||
//System.out.println(String.valueOf(key) + ", " + String.valueOf(c));
|
||||
}
|
||||
|
||||
fun keyReleased(key: Int, c: Char) {
|
||||
|
||||
}
|
||||
|
||||
fun mouseMoved(oldx: Int, oldy: Int, newx: Int, newy: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun mouseDragged(oldx: Int, oldy: Int, newx: Int, newy: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun mousePressed(button: Int, x: Int, y: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun mouseReleased(button: Int, x: Int, y: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun mouseWheelMoved(change: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun controllerButtonPressed(controller: Int, button: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun controllerButtonReleased(controller: Int, button: Int) {
|
||||
|
||||
}
|
||||
|
||||
private fun keyPressedByCode(key: Int, fn: EnumKeyFunc): Boolean {
|
||||
return KeyMap.getKeyCode(fn) == key
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.torvald.terrarum.gamecontroller
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
object Key {
|
||||
|
||||
val RET = 28
|
||||
val BKSP = 14
|
||||
val GRAVE = 41
|
||||
val TAB = 15
|
||||
val ESCAPE = 1
|
||||
val SPACE = 57
|
||||
|
||||
val L_SHIFT = 42
|
||||
val R_SHIFT = 54
|
||||
|
||||
val UP = 200
|
||||
val DOWN = 208
|
||||
val LEFT = 203
|
||||
val RIGHT = 205
|
||||
|
||||
val F1 = 59
|
||||
val F2 = 60
|
||||
val F3 = 61
|
||||
val F4 = 62
|
||||
|
||||
val F5 = 63
|
||||
val F6 = 64
|
||||
val F7 = 65
|
||||
val F8 = 66
|
||||
|
||||
val F9 = 67
|
||||
val F10 = 68
|
||||
val F11 = 87
|
||||
val F12 = 88
|
||||
|
||||
val NUM_1 = 2
|
||||
val NUM_2 = 3
|
||||
val NUM_3 = 4
|
||||
val NUM_4 = 5
|
||||
val NUM_5 = 6
|
||||
val NUM_6 = 7
|
||||
val NUM_7 = 8
|
||||
val NUM_8 = 9
|
||||
val NUM_9 = 10
|
||||
val NUM_0 = 11
|
||||
|
||||
val Q = 16
|
||||
val W = 17
|
||||
val E = 18
|
||||
val R = 19
|
||||
val T = 20
|
||||
|
||||
val Y = 21
|
||||
val U = 22
|
||||
val I = 23
|
||||
val O = 24
|
||||
val P = 25
|
||||
|
||||
val A = 30
|
||||
val S = 31
|
||||
val D = 32
|
||||
val F = 33
|
||||
val G = 34
|
||||
|
||||
val H = 35
|
||||
val J = 36
|
||||
val K = 37
|
||||
val L = 38
|
||||
val SEMICOLON = 39
|
||||
|
||||
val Z = 44
|
||||
val X = 45
|
||||
val C = 46
|
||||
val V = 47
|
||||
val B = 48
|
||||
|
||||
val N = 49
|
||||
val M = 50
|
||||
|
||||
val PGUP = 201
|
||||
val PGDN = 209
|
||||
val HOME = 199
|
||||
val END = 207
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.torvald.terrarum.gamecontroller
|
||||
|
||||
import java.util.Hashtable
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
object KeyMap {
|
||||
|
||||
var map_code = Hashtable<EnumKeyFunc, Int>()
|
||||
|
||||
fun build() {
|
||||
|
||||
map_code.put(EnumKeyFunc.MOVE_UP, Key.E)
|
||||
map_code.put(EnumKeyFunc.MOVE_LEFT, Key.S)
|
||||
map_code.put(EnumKeyFunc.MOVE_DOWN, Key.D)
|
||||
map_code.put(EnumKeyFunc.MOVE_RIGHT, Key.F)
|
||||
map_code.put(EnumKeyFunc.JUMP, Key.SPACE)
|
||||
map_code.put(EnumKeyFunc.UI_CONSOLE, Key.GRAVE)
|
||||
map_code.put(EnumKeyFunc.UI_BASIC_INFO, Key.F3)
|
||||
}
|
||||
|
||||
fun getKeyCode(fn: EnumKeyFunc): Int {
|
||||
return map_code[fn]!!
|
||||
}
|
||||
|
||||
operator fun set(func: EnumKeyFunc, key: Int) {
|
||||
map_code.put(func, key)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.torvald.terrarum.gamecontroller
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Input
|
||||
import java.util.*
|
||||
|
||||
object KeyToggler {
|
||||
|
||||
private val currentState = BitSet(256)
|
||||
private val isPressed = BitSet(256)
|
||||
private val isToggled = BitSet(256)
|
||||
|
||||
fun isOn(key: Int): Boolean {
|
||||
return currentState[key]
|
||||
}
|
||||
|
||||
fun update(input: Input) {
|
||||
for (i in 0..255) {
|
||||
if (input.isKeyDown(i)) {
|
||||
isPressed[i] = true
|
||||
}
|
||||
else {
|
||||
isPressed[i] = false
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0..255) {
|
||||
if (isPressed[i] && !currentState[i] && !isToggled[i]) {
|
||||
currentState[i] = true
|
||||
isToggled[i] = true
|
||||
}
|
||||
else if (isPressed[i] && currentState[i] && !isToggled[i]) {
|
||||
currentState[i] = false
|
||||
isToggled[i] = true
|
||||
}
|
||||
|
||||
if (!isPressed[i] && isToggled[i]) {
|
||||
isToggled[i] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun forceSet(key: Int, b: Boolean) {
|
||||
currentState[key] = b
|
||||
isToggled[key] = true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/* Original code author: Sean Laurvick
|
||||
* This code is based on the original author's code written in Lua.
|
||||
*/
|
||||
|
||||
package com.torvald.spriteanimation
|
||||
|
||||
import com.torvald.terrarum.Game
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.jme3.math.FastMath
|
||||
import org.newdawn.slick.Graphics
|
||||
import org.newdawn.slick.Image
|
||||
import org.newdawn.slick.SlickException
|
||||
import org.newdawn.slick.SpriteSheet
|
||||
|
||||
class SpriteAnimation @Throws(SlickException::class)
|
||||
constructor() {
|
||||
|
||||
private var spriteImage: SpriteSheet? = null
|
||||
var height: Int = 0
|
||||
private set
|
||||
var width: Int = 0
|
||||
private set
|
||||
private var currentFrame = 1
|
||||
private var currentRow = 1
|
||||
private var nFrames: Int = 0
|
||||
private var nRows: Int = 0
|
||||
private var delay = 200
|
||||
private var delta = 0
|
||||
private val looping = true
|
||||
private var animationRunning = true
|
||||
private var flipHorizontal = false
|
||||
private var flipVertical = false
|
||||
private var visible = false
|
||||
|
||||
private val offsetX = 0
|
||||
private val offsetY = 0
|
||||
|
||||
private var prevScale = 1f
|
||||
private var currentImage: Image? = null
|
||||
|
||||
/**
|
||||
* Sets spritesheet.
|
||||
* MUST be called AFTER setDimension.
|
||||
* @param imagePath path to the sprite sheet image.
|
||||
* *
|
||||
* @throws SlickException
|
||||
*/
|
||||
@Throws(SlickException::class)
|
||||
fun setSpriteImage(imagePath: String) {
|
||||
spriteImage = SpriteSheet(imagePath, this.width, this.height)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets animation delay. Will default to 200 if not called.
|
||||
* @param delay in milliseconds
|
||||
*/
|
||||
fun setDelay(delay: Int) {
|
||||
this.delay = delay
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sprite dimension. This is necessary.
|
||||
* @param w
|
||||
* *
|
||||
* @param h
|
||||
*/
|
||||
fun setDimension(w: Int, h: Int) {
|
||||
width = w
|
||||
height = h
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sheet rows and animation frames. Will default to
|
||||
* 1, 1 (still image of top left from the sheet) if not called.
|
||||
* @param rows
|
||||
* *
|
||||
* @param frames
|
||||
*/
|
||||
fun setRowsAndFrames(rows: Int, frames: Int) {
|
||||
nRows = rows
|
||||
nFrames = frames
|
||||
}
|
||||
|
||||
fun setAsVisible() {
|
||||
visible = true
|
||||
}
|
||||
|
||||
fun setAsInvisible() {
|
||||
visible = false
|
||||
}
|
||||
|
||||
fun update(delta: Int) {
|
||||
if (animationRunning) {
|
||||
//skip this if animation is stopped
|
||||
this.delta += delta
|
||||
|
||||
//check if it's time to advance the frame
|
||||
if (this.delta >= this.delay) {
|
||||
//if set to not loop, keep the frame at the last frame
|
||||
if (this.currentFrame == this.nFrames && !this.looping) {
|
||||
this.currentFrame = this.nFrames - 1
|
||||
}
|
||||
|
||||
//advance one frame, then reset delta counter
|
||||
this.currentFrame = this.currentFrame % this.nFrames + 1
|
||||
this.delta = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render to specific coordinates. Will assume bottom-center point as image position.
|
||||
* Will round to integer.
|
||||
* @param g
|
||||
* *
|
||||
* @param posX bottom-center point
|
||||
* *
|
||||
* @param posY bottom-center point
|
||||
* *
|
||||
* @param scale
|
||||
*/
|
||||
@JvmOverloads fun render(g: Graphics, posX: Float, posY: Float, scale: Float = 1f) {
|
||||
var scale = scale
|
||||
scale *= Terrarum.game.screenZoom
|
||||
|
||||
// Null checking
|
||||
if (currentImage == null) {
|
||||
currentImage = getScaledSprite(scale)
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
// re-scale image if scale has been changed
|
||||
if (prevScale != scale) {
|
||||
currentImage = getScaledSprite(scale)
|
||||
prevScale = scale
|
||||
}
|
||||
|
||||
val flippedImage = currentImage!!.getFlippedCopy(flipHorizontal, flipVertical)
|
||||
|
||||
flippedImage.startUse()
|
||||
flippedImage.drawEmbedded(
|
||||
Math.round(posX * Terrarum.game.screenZoom).toFloat(),
|
||||
Math.round(posY * Terrarum.game.screenZoom).toFloat(),
|
||||
FastMath.floor(width * scale).toFloat(),
|
||||
FastMath.floor(height * scale).toFloat()
|
||||
)
|
||||
flippedImage.endUse()
|
||||
}
|
||||
}
|
||||
|
||||
fun switchSprite(newRow: Int) {
|
||||
currentRow = newRow
|
||||
|
||||
//if beyond the frame index then reset
|
||||
if (currentFrame > nFrames) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
fun switchSprite(newRow: Int, newMax: Int) {
|
||||
if (newMax > 0) {
|
||||
nFrames = newMax
|
||||
}
|
||||
|
||||
currentRow = newRow
|
||||
|
||||
//if beyond the frame index then reset
|
||||
if (currentFrame > nFrames) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
fun switchSpriteDelay(newDelay: Int) {
|
||||
if (newDelay > 0) {
|
||||
delay = newDelay
|
||||
}
|
||||
}
|
||||
|
||||
fun switchSprite(newRow: Int, newMax: Int, newDelay: Int) {
|
||||
if (newMax > 0) {
|
||||
nFrames = newMax
|
||||
}
|
||||
|
||||
if (newDelay > 0) {
|
||||
delay = newDelay
|
||||
}
|
||||
|
||||
currentRow = newRow
|
||||
|
||||
//if beyond the frame index then reset
|
||||
if (currentFrame > nFrames) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
currentFrame = 1
|
||||
}
|
||||
|
||||
fun start() {
|
||||
//starts the animation
|
||||
animationRunning = true
|
||||
}
|
||||
|
||||
fun start(selectFrame: Int) {
|
||||
//starts the animation
|
||||
animationRunning = true
|
||||
|
||||
//optional: seleft the frame no which to start the animation
|
||||
currentFrame = selectFrame
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
animationRunning = false
|
||||
}
|
||||
|
||||
fun stop(selectFrame: Int) {
|
||||
animationRunning = false
|
||||
|
||||
currentFrame = selectFrame
|
||||
}
|
||||
|
||||
fun flip(horizontal: Boolean, vertical: Boolean) {
|
||||
flipHorizontal = horizontal
|
||||
flipVertical = vertical
|
||||
}
|
||||
|
||||
fun flippedHorizontal(): Boolean {
|
||||
return flipHorizontal
|
||||
}
|
||||
|
||||
fun flippedVertical(): Boolean {
|
||||
return flipVertical
|
||||
}
|
||||
|
||||
private fun getScaledSprite(scale: Float): Image {
|
||||
val selectedImage = spriteImage!!.getSprite(currentFrame - 1, currentRow - 1)
|
||||
//Image selectedImage = sprites[currentRow - 1][currentFrame - 1];
|
||||
|
||||
// resample
|
||||
/*float nearestResampleScale = (scale > 1) ? Math.round(scale) : 1;
|
||||
float linearResampleScale = scale / nearestResampleScale;
|
||||
|
||||
// scale 1.8 -> resample in 2(nearest), then resample in 0.9(linear)
|
||||
// scale by nearestResampleScale (2, 3, ...)
|
||||
selectedImage.setFilter(Image.FILTER_NEAREST);
|
||||
Image selImgNearestScaled = selectedImage.getScaledCopy(nearestResampleScale);
|
||||
// scale by linearResampleScale (.x)
|
||||
Image selImgLinearScaled;
|
||||
if (scale % 1 > 0) {
|
||||
selImgNearestScaled.setFilter(Image.FILTER_LINEAR);
|
||||
selImgLinearScaled = selImgNearestScaled.getScaledCopy(linearResampleScale);
|
||||
return selImgLinearScaled;
|
||||
}
|
||||
else {
|
||||
return selImgNearestScaled;
|
||||
}*/
|
||||
selectedImage.filter = Image.FILTER_NEAREST
|
||||
return selectedImage.getScaledCopy(scale)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.torvald.random
|
||||
|
||||
import java.util.Random
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-03.
|
||||
*/
|
||||
class Fudge3
|
||||
/**
|
||||
* Define new set of fudge dice with three dice.
|
||||
* @param randfunc java.util.Random or its extension
|
||||
*/
|
||||
(randfunc: Random) : FudgeDice(randfunc, 3)
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.torvald.random
|
||||
|
||||
import java.util.Random
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-03.
|
||||
*/
|
||||
open class FudgeDice
|
||||
/**
|
||||
* Define new set of fudge dice with given counts.
|
||||
* @param randfunc java.util.Random or its extension
|
||||
* *
|
||||
* @param counts amount of die
|
||||
*/
|
||||
(private val randfunc: Random, val diceCounts: Int) {
|
||||
|
||||
/**
|
||||
* Roll dice and get result.
|
||||
* @return Normal distributed integer [-N , N] for diceCount of N. 0 is the most frequent return.
|
||||
*/
|
||||
fun roll(): Int {
|
||||
var diceResult = 0
|
||||
for (c in 0..diceCounts - 1) {
|
||||
diceResult += rollSingleDie()
|
||||
}
|
||||
|
||||
return diceResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll dice and get result, for array index
|
||||
* @return Normal distributed integer [0 , N] for N = 2 × DiceCounts + 1. 0 is the most frequent return.
|
||||
*/
|
||||
fun rollForArray(): Int {
|
||||
return roll() + diceCounts
|
||||
}
|
||||
|
||||
val sizeOfProbabilityRange: Int
|
||||
get() = 2 * diceCounts + 1
|
||||
|
||||
/**
|
||||
* @return integer randomly picked from {-1, 0, 1}
|
||||
*/
|
||||
private fun rollSingleDie(): Int {
|
||||
return randfunc.nextInt(3) - 1
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.torvald.random;
|
||||
|
||||
import java.util.Random;
|
||||
//import java.util.concurrent.locks.*;
|
||||
|
||||
/**
|
||||
* This class implements a better random number generator than the standard LCG that is implemented in java.util.Random.
|
||||
* It is based on <a href="http://www.amazon.com/gp/product/0521880688?ie=UTF8&tag=javamex-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0521880688">Numerical Recipes: The Art of Scientific Computing</a>,
|
||||
* and gives a good compromise between quality and speed. It is a combined generator: two XORShift generators are combined with an LCG and a multiply with carry generator.
|
||||
* (Without going into all the details here, notice the two blocks of three shifts each, which are the XORShifts; the first line which is the LCG, similar to the standard
|
||||
* Java Random algorithm, and the line between the two XORShifts, which is a multiply with carry generator.)
|
||||
* Note that this version is <b>not</b> thread-safe. In order to make it thread-safe, uncomment the lock-related lines. It is also <b>not</b> cryptographically secure, like the java.security.SecureRandom class.
|
||||
* @author Numerical Recipes
|
||||
*/
|
||||
|
||||
public class HQRNG extends Random {
|
||||
|
||||
//private Lock l = new ReentrantLock();
|
||||
private long u;
|
||||
private long v = 4101842887655102017L;
|
||||
private long w = 1;
|
||||
|
||||
public HQRNG() {
|
||||
this(System.nanoTime());
|
||||
}
|
||||
public HQRNG(long seed) {
|
||||
//l.lock();
|
||||
u = seed ^ v;
|
||||
nextLong();
|
||||
v = u;
|
||||
nextLong();
|
||||
w = v;
|
||||
nextLong();
|
||||
//l.unlock();
|
||||
}
|
||||
|
||||
public long nextLong() {
|
||||
// l.lock();
|
||||
try {
|
||||
u = u * 2862933555777941757L + 7046029254386353087L;
|
||||
v ^= v >>> 17;
|
||||
v ^= v << 31;
|
||||
v ^= v >>> 8;
|
||||
w = 4294957665L * (w & 0xffffffff) + (w >>> 32);
|
||||
long x = u ^ (u << 21);
|
||||
x ^= x >>> 35;
|
||||
x ^= x << 4;
|
||||
long ret = (x + v) ^ w;
|
||||
return ret;
|
||||
} finally {
|
||||
//l.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
return (int) (nextLong() >>> (64-bits));
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,132 +0,0 @@
|
||||
## About gamemaking ##
|
||||
|
||||
### CHALLENGING, NOT PUNISHING ###
|
||||
https://www.youtube.com/watch?v=ea6UuRTjkKs
|
||||
|
||||
1. CONSISTENT RULES
|
||||
- No arbitrary unstoppable death
|
||||
|
||||
2. Player's skill involved
|
||||
- Can play around, not restart
|
||||
|
||||
3. Usability of in-game tools
|
||||
- Players should be able to 'regret' their strategy and adjust.
|
||||
|
||||
4. Comfortable control
|
||||
|
||||
5. Make players overcome the challenge, not defeating them
|
||||
|
||||
6. Let players have "aha" moment when they failed.
|
||||
- Make them hungry to retry with new strategies.
|
||||
- Some small things they've could done differently
|
||||
- e.g. "One-big-hit didn't worked, may I should've picked up high DPS one"
|
||||
|
||||
|
||||
### MORE DEPTH, LESS COMPLEXITY ###
|
||||
https://www.youtube.com/watch?v=jVL4st0blGU
|
||||
|
||||
1. Memorise less!
|
||||
- Less burden to, even starting the game
|
||||
- Start with gentle learning curve, getting slowly steep
|
||||
- Intuitive UX (UI, control, ...)
|
||||
- Good tutorial = lessens complexity
|
||||
|
||||
2. Intuitive!
|
||||
|
||||
3. Calculations per second
|
||||
- reduce!
|
||||
|
||||
4. Players have to know everything to even begin the play ### FAIL (irreducible complexity)
|
||||
- Make them get familiar with rules of the game
|
||||
- Dwarf Fortress failed this!
|
||||
|
||||
|
||||
### Lots of things players can play with (aka don't make them bored) ###
|
||||
|
||||
- Combat, battle, building, mechanics, adventure, dungeon explore, spelunking
|
||||
- Not scaled; easy combat, tough combat, tedious combat, etc.
|
||||
|
||||
|
||||
### Achieving perfect imbalance ###
|
||||
https://www.youtube.com/watch?v=e31OSVZF77w
|
||||
|
||||
- Make sure no matter how you skilled, your playable character cannot be good at everything
|
||||
- Give players __wide pool of options__ to solve problem
|
||||
(kill the boss, defend their adobe, fast transportation, etc.)
|
||||
|
||||
|
||||
**_What feeling do you want to convey?_**
|
||||
|
||||
|
||||
### Always think WHY you want to add _something_ on the game ###
|
||||
|
||||
- e.g. Why are you adding RPG leveling system? What it would do to the players? How would they play with?
|
||||
|
||||
|
||||
See also: *HEARTS, CLUBS, DIAMONDS, SPADES: PLAYERS WHO SUIT MUDS*
|
||||
|
||||
|
||||
## About this very game ##
|
||||
|
||||
### Friendlier version of Dwarf Fortress Adventure mode ###
|
||||
|
||||
- Yet _lots of fun_
|
||||
- Add Fortress mode features by 'make your own settling'
|
||||
- Hard to actually die, but once you die, you're done.
|
||||
+ Config: imtooyoungtodie for easy mode
|
||||
|
||||
- Genre: Adventure, Open world (towns in RPG, building, town managing (conquer existing one or
|
||||
you build one and persuade existing people to move in) -> See Dwarf Fortress and Animal Crossing)
|
||||
|
||||
* Adventure: adventure this vast—5,5 km wide—world, discover new (and good/horrible) things
|
||||
|
||||
* Open world:
|
||||
- Building: building your own houses, structures, etc.
|
||||
- Town managing:
|
||||
1. Build your own little hamlet and manage it
|
||||
or-
|
||||
2. Conquer existing one and become a ruler
|
||||
The town is a special hamlet that can be tailored for your taste
|
||||
- Survival:
|
||||
mobs will trying to attack your assets (yourself, your hamlet, your people)
|
||||
|
||||
|
||||
|
||||
### Side view ###
|
||||
|
||||
### Interact menu w/ mouse right ###
|
||||
|
||||
### Pixelated sprites ###
|
||||
- Use 2x sprites if rotating does not work well
|
||||
|
||||
|
||||
### User experience ###
|
||||
|
||||
* Indicative mouse cursor
|
||||
|
||||
|
||||
### Game mechanics ###
|
||||
|
||||
* 24 pixels == 1 metre
|
||||
|
||||
|
||||
### Purpose of the game ###
|
||||
|
||||
* Boss
|
||||
- Will be mentioned/shown as absolute _evil_.
|
||||
- But actually is not.
|
||||
|
||||
* Theme
|
||||
- Is an evil really really is what we think?
|
||||
- Is there a thing as 'absolute evil'?
|
||||
|
||||
* Boss character
|
||||
- From debugger character
|
||||
- Name key: "Sigriðr hinn Dróttningin" (can be changed)
|
||||
* Little setting
|
||||
- A ruler, hated by people
|
||||
|
||||
* Mechanics
|
||||
- Beating boss does not ends the game, but grants an ability to
|
||||
create new character as it.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
* *Terrarum* by Torvald
|
||||
Copyright 2015-2016 Torvald. All rights reserved.
|
||||
mailto: skyhi14 *64* __115875741922660__ *46* __6516589__
|
||||
|
||||
* *Simplex Noise Generator*, version 2012-03-09 by Stefan Gustavson
|
||||
Released as public domain
|
||||
|
||||
* *Joise* modular noise library
|
||||
Copyright (C) 2013 Jason Taylor. Released as open-source under Apache License, Version 2.0.
|
||||
@@ -1,119 +0,0 @@
|
||||
|
||||
## Weapon tier ##
|
||||
|
||||
Natural / Common Stone -> Copper -> Iron -> Silver -> Titanium
|
||||
Forging --------------> Steel --------^
|
||||
Exotic ('elven') Glass Aurichalcum
|
||||
Special (something 'adamant') ??? (Use material spec of CNT, tensile strength 180 GPa)
|
||||
|
||||
* Metal graphics
|
||||
- Gold: Hue 43, low Saturation
|
||||
- Aurichalcum: Hue 43, mid-high Saturation
|
||||
- Copper: Hue 33,
|
||||
- Copper rust: Hue 160
|
||||
- Iron rust: Hue 21
|
||||
|
||||
|
||||
## Size variation ##
|
||||
|
||||
Race base weapon/tool size <- 10 [kg]
|
||||
Size tolerance <- (50% * str/1000), or say, 20%
|
||||
|
||||
If the size is bigger than tolerable, weapon speed severely slows down, tools become unusable
|
||||
if use time >* 0.75 second, the weapon/tool cannot be equipped.
|
||||
Small weapons/tools gains no (dis)advantage
|
||||
|
||||
When drawing: scale by (craftedWeaponSize / baseWeaponSize)
|
||||
|
||||
Crafted tool/weapon size is dependent to the baseRaceMass.
|
||||
|
||||
|
||||
## Gemstone tier ##
|
||||
|
||||
Topaz -> R·G·B -> Diamond·Amethyst
|
||||
|
||||
|
||||
## Colouring ##
|
||||
|
||||
Natural: Use 4096
|
||||
Magical/Surreal: Use 24 Bits
|
||||
|
||||
* Colouring of potion
|
||||
- Randomised, roguelike fashion
|
||||
- Choose Col(R40, G40, B40) from set of finite cards:
|
||||
39, 39, 19, 19, 0, 0
|
||||
- MULTIPLY blend chosen colour with white texture
|
||||
|
||||
|
||||
## Roguelike identity ##
|
||||
|
||||
* Randomised things
|
||||
- E.g. potion
|
||||
Lime-coloured potion
|
||||
First play: "Potion (???)"
|
||||
After drank: "Potion (Healing)" is revealed.
|
||||
|
||||
Second (new) play: "Potion (???)"
|
||||
After drank: "Potion (Neurotoxin)" is revealed.
|
||||
|
||||
|
||||
## Making sprite ##
|
||||
|
||||
* Layers
|
||||
- (Optional) Glow
|
||||
- (Optional) Hair foreground
|
||||
- Right arm dress
|
||||
- Right arm body
|
||||
- Dress
|
||||
- Boots
|
||||
- Body
|
||||
- (Optional) Hair accessory
|
||||
- Hair
|
||||
- Head
|
||||
- Left arm dress
|
||||
- Left arm body
|
||||
- (Optional) SFX
|
||||
|
||||
* Size
|
||||
- Regular sprite 'height' (hitbox height) : 40 px
|
||||
- Apparent height may vary
|
||||
|
||||
|
||||
## Chargen ##
|
||||
|
||||
* Select hair, colours, then compile them into single spritesheet
|
||||
|
||||
* NO gender distinction, but have masculine/neutral/feminine looks (in clothing, hairstyles, etc.)
|
||||
|
||||
* Colour: 4096 colours (12-bit 0x000 - 0xFFF)
|
||||
|
||||
* Base mass: 60 kg
|
||||
|
||||
|
||||
## Food/Potion dose ##
|
||||
|
||||
Scale ^ 3 ^ (3/4) == (ThisWgt / TargetWgt) ^ (3/4)
|
||||
|
||||
|
||||
## (De)serialisation ##
|
||||
|
||||
see SAVE_FORMAT.md
|
||||
|
||||
|
||||
## Actor as universal tool ##
|
||||
|
||||
* Utility tiles that have states (e.g. noteblock) are implemented using Actor.
|
||||
|
||||
|
||||
## NPC Killing ##
|
||||
|
||||
* AI:
|
||||
Attacked first: Warn player to not attack
|
||||
Attacked second: The NPC becomes hostile until player sheathe the weapon
|
||||
Attacked thrice: All the NPCs within the same faction become hostile until the player is away
|
||||
|
||||
|
||||
## Ownership of lands ##
|
||||
|
||||
* Codex of Real Estate → assign owner to the tiles → copy the information to the NPC instance
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
## Gadgets ##
|
||||
|
||||
### Looms for custom pattern ###
|
||||
|
||||
- Players can create their own décors (hang on wall), dresses.
|
||||
- Two looms (216 colour mode, 4096 colour mode)
|
||||
|
||||
|
||||
### Music making ###
|
||||
|
||||
- Automated glockenspiel thingy
|
||||
- Single tile can hold 48 notes, single track
|
||||
- Work like Modtracker, incl. physical arrangements
|
||||
|
||||
Arrangements in the map
|
||||
Time →→→→
|
||||
voice 1 → □□□□□□□□□□□□□□□□...
|
||||
voice 2 → □□□□□□□□□□□□□□□□...
|
||||
↑ played simultaneously along the X-axis
|
||||
|
||||
- Each tracker head and body are connected by placing tracks adjacent to each other or connecting them with wire.
|
||||
Connect two or more tracker head to play the array of trackers play simultaneously (multi-tracking)
|
||||
|
||||
- Serialisation
|
||||
|
||||
<actorid>.json
|
||||
{
|
||||
notes = [arr<int>, fixed size of 48],
|
||||
speed = 120
|
||||
}
|
||||
|
||||
*int: (0-63) number of the note pitch that is struck. 32: Middle C (C3).
|
||||
'A' just above of Middle C (A3) has base pitch of 440 Hz.
|
||||
*speed: in BPM
|
||||
@@ -1,56 +0,0 @@
|
||||
## Format ##
|
||||
|
||||
* Save meta
|
||||
- GZip'd binary (for more security)
|
||||
- Filename : world (with no extension)
|
||||
|
||||
|Type |Mnemonic |Description |
|
||||
|------------|------------|---------------------------------------|
|
||||
|Byte[4] |TESV |Magic |
|
||||
|Byte[n] |name |Savegame name, UTF-8 |
|
||||
|Byte |NULL |String terminator |
|
||||
|Byte[8] |terraseed |Terrain seed |
|
||||
|Byte[8] |rogueseed |Randomiser seed |
|
||||
|Byte[32] |hash1 |SHA-256 hash of worldinfo1 being stored (when not zipped)|
|
||||
|Byte[32] |hash2 |SHA-256 hash of worldinfo2 being stored (when not zipped)|
|
||||
|Byte[32] |hash3 |SHA-256 hash of worldinfo3 being stored (when not zipped)|
|
||||
|Byte[32] |hash4 |SHA-256 hash of worldinfo4 being stored (when not zipped)|
|
||||
|
||||
Endianness: Big
|
||||
|
||||
* Actor/Faction data
|
||||
- GZip'd GSON
|
||||
- Filename : (refid) (with no extension)
|
||||
|
||||
|
||||
* Prop data
|
||||
- GZip'd CSV
|
||||
- Filename : (with no extension)
|
||||
worldinfo2 -- tileprop
|
||||
worldinfo3 -- itemprop
|
||||
worldinfo4 -- materialprop
|
||||
|
||||
|
||||
* Human-readable
|
||||
- Tiles_list.txt -- list of tiles in csv
|
||||
- Items_list.txt -- list of items in csv
|
||||
- Materials_list.txt -- list of materials in csv
|
||||
|
||||
|
||||
|
||||
## How it works ##
|
||||
* If hash discrepancy has detected, (hash of csv in save dir != stored hash || hash of TEMD != stored hash), printout "Save file corrupted. Continue?" with prompt "Yes/No"
|
||||
|
||||
Directory:
|
||||
|
||||
+--- <save1>
|
||||
--- 2a93bc5fd...f823 Actor/Faction/etc. data
|
||||
--- 423bdc838...93bd Actor/Faction/etc. data
|
||||
--- Items_list.txt Human-readable
|
||||
--- Materials_list.txt Human-readable
|
||||
--- Tiles_list.txt Human-readable
|
||||
--- world save meta (binary, GZip)
|
||||
--- worldinfo1 TEMD (binary, GZip)
|
||||
--- worldinfo2 tileprop (GZip)
|
||||
--- worldinfo3 itemprop (GZip)
|
||||
--- worldinfo4 materialprop (GZip)
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.ui.ConsoleWindow
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-19.
|
||||
*/
|
||||
class Authenticator : ConsoleCommand {
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
val pwd = args[1]
|
||||
val hashedPwd = DigestUtils.sha256Hex(pwd)
|
||||
|
||||
if ("54c5b3dd459d5ef778bb2fa1e23a5fb0e1b62ae66970bcb436e8f81a1a1a8e41".equals(hashedPwd, ignoreCase = true)) {
|
||||
// alpine
|
||||
val msg = if (a) "Locked" else "Authenticated"
|
||||
Echo().execute(msg)
|
||||
println("[Authenticator] " + msg)
|
||||
a = !a
|
||||
(Terrarum.game.consoleHandler.UI as ConsoleWindow).reset()
|
||||
}
|
||||
else {
|
||||
printUsage() // thou shalt not pass!
|
||||
}
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
fun b(): Boolean {
|
||||
return a
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
CommandInterpreter.echoUnknownCmd("auth")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private var a = false
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-07.
|
||||
*/
|
||||
class Batch : ConsoleCommand {
|
||||
@Throws(Exception::class)
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
Files.lines(FileSystems.getDefault().getPath(args[1])).forEach(
|
||||
{ CommandInterpreter.execute(it) })
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("batch path/to/batch.txt")
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-10.
|
||||
*/
|
||||
class CatStdout : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
|
||||
val echo = Echo()
|
||||
|
||||
if (args.size == 1) {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
Files.lines(FileSystems.getDefault().getPath(args[1])).forEach({ echo.execute(it) })
|
||||
}
|
||||
catch (e: IOException) {
|
||||
echo.execute("CatStdout: could not read file -- IOException")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("usage: cat 'path/to/text/file")
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import com.torvald.terrarum.Game
|
||||
import com.torvald.terrarum.langpack.Lang
|
||||
import com.torvald.terrarum.ui.ConsoleWindow
|
||||
|
||||
import java.util.Formatter
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
class CodexEdictis : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 1) {
|
||||
printList()
|
||||
}
|
||||
else {
|
||||
try {
|
||||
val commandObj = CommandDict.getCommand(args[1].toLowerCase())
|
||||
commandObj.printUsage()
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
val sb = StringBuilder()
|
||||
val formatter = Formatter(sb)
|
||||
|
||||
Echo().execute("Codex: " + formatter.format(Lang.get("DEV_MESSAGE_CONSOLE_COMMAND_UNKNOWN"), args[1]).toString())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Usage: codex (command)")
|
||||
echo.execute("shows how to use 'command'")
|
||||
echo.execute("leave blank to get list of available commands")
|
||||
}
|
||||
|
||||
private fun printList() {
|
||||
val echo = Echo()
|
||||
echo.execute(Lang.get("DEV_MESSAGE_CONSOLE_AVAILABLE_COMMANDS"))
|
||||
CommandDict.dict.keys.forEach { s -> echo.execute("] " + s) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
|
||||
import java.util.HashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
object CommandDict {
|
||||
|
||||
internal var dict: HashMap<String, ConsoleCommand> = hashMapOf(
|
||||
Pair("echo", Echo()),
|
||||
Pair("setav", SetAV()),
|
||||
Pair("qqq", QuitApp()),
|
||||
Pair("codex", CodexEdictis()),
|
||||
Pair("export", ExportMap()),
|
||||
Pair("gc", ForceGC()),
|
||||
Pair("getav", GetAV()),
|
||||
Pair("getlocale", GetLocale()),
|
||||
Pair("togglenoclip", ToggleNoClip()),
|
||||
Pair("nc", ToggleNoClip()),
|
||||
Pair("setlocale", SetLocale()),
|
||||
Pair("zoom", Zoom()),
|
||||
Pair("teleport", TeleportPlayer()),
|
||||
Pair("tp", TeleportPlayer()),
|
||||
Pair("cat", CatStdout()),
|
||||
Pair("exportav", ExportAV()),
|
||||
Pair("setgl", SetGlobalLightLevel()),
|
||||
Pair("getfaction", GetFactioning()),
|
||||
Pair("auth", Terrarum.game.auth),
|
||||
Pair("spawnball", SpawnPhysTestBall()),
|
||||
Pair("batch", Batch()),
|
||||
Pair("settime", SetTime()),
|
||||
Pair("gettime", GetTime()),
|
||||
Pair("settimedelta", SetTimeDelta()),
|
||||
Pair("help", Help()),
|
||||
|
||||
// Test codes
|
||||
Pair("bulletintest", SetBulletin()),
|
||||
Pair("gsontest", GsonTest())
|
||||
)
|
||||
|
||||
fun getCommand(commandName: String): ConsoleCommand {
|
||||
return dict[commandName]!!
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import com.torvald.terrarum.langpack.Lang
|
||||
import com.torvald.terrarum.Terrarum
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.Formatter
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
object CommandInterpreter {
|
||||
|
||||
private val commandsNoAuth = arrayOf("auth", "qqq", "zoom", "setlocale", "getlocale", "help")
|
||||
|
||||
fun execute(command: String) {
|
||||
val cmd = parse(command)
|
||||
|
||||
for (single_command in cmd) {
|
||||
var commandObj: ConsoleCommand? = null
|
||||
try {
|
||||
if (commandsNoAuth.contains(single_command!!.name.toLowerCase())) {
|
||||
commandObj = CommandDict.getCommand(single_command.name.toLowerCase())
|
||||
}
|
||||
else {
|
||||
if (Terrarum.game.auth.b()) {
|
||||
commandObj = CommandDict.getCommand(
|
||||
single_command.name.toLowerCase())
|
||||
}
|
||||
else {
|
||||
// System.out.println("ee1");
|
||||
throw NullPointerException() // if not authorised, say "Unknown command"
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (commandObj != null) {
|
||||
commandObj.execute(single_command!!.toStringArray())
|
||||
}
|
||||
else {
|
||||
echoUnknownCmd(single_command!!.name)
|
||||
// System.out.println("ee3");
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
println("[CommandInterpreter] :")
|
||||
e.printStackTrace()
|
||||
Echo().execute(Lang.get("ERROR_GENERIC_TEXT"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parse(input: String): Array<CommandInput?> {
|
||||
val patternCommands = Pattern.compile("[^;]+")
|
||||
val patternTokensInCommand = Pattern.compile("[\"'][^;]+[\"']|[^ ]+")
|
||||
|
||||
val commands = ArrayList<String>()
|
||||
|
||||
// split multiple commands
|
||||
var m = patternCommands.matcher(input)
|
||||
while (m.find()) commands.add(m.group())
|
||||
|
||||
// split command tokens from a command
|
||||
val parsedCommands = arrayOfNulls<CommandInput>(commands.size)
|
||||
|
||||
|
||||
for (i in parsedCommands.indices) {
|
||||
val tokens = ArrayList<String>()
|
||||
|
||||
m = patternTokensInCommand.matcher(commands[i])
|
||||
while (m.find()) {
|
||||
val regexGroup = m.group().replace("[\"\']".toRegex(), "")
|
||||
tokens.add(regexGroup)
|
||||
}
|
||||
|
||||
// create new command
|
||||
parsedCommands[i] = CommandInput(tokens.toArray())
|
||||
|
||||
}
|
||||
|
||||
return parsedCommands
|
||||
}
|
||||
|
||||
internal fun echoUnknownCmd(cmdname: String) {
|
||||
val sb = StringBuilder()
|
||||
val formatter = Formatter(sb)
|
||||
|
||||
Echo().execute(
|
||||
formatter.format(Lang.get("DEV_MESSAGE_CONSOLE_COMMAND_UNKNOWN"), cmdname).toString())
|
||||
}
|
||||
|
||||
private class CommandInput(o: Array<Any>) {
|
||||
private val tokens: Array<String>
|
||||
|
||||
init {
|
||||
tokens = Array<String>(o.size, { i -> o[i] as String })
|
||||
}
|
||||
|
||||
fun toStringArray(): Array<String> {
|
||||
return tokens
|
||||
}
|
||||
|
||||
val name: String
|
||||
get() = tokens[0]
|
||||
|
||||
val argsCount: Int
|
||||
get() = tokens.size
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
interface ConsoleCommand {
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun execute(args: Array<String>)
|
||||
|
||||
fun printUsage()
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.torvald.terrarum.console
|
||||
|
||||
import com.torvald.terrarum.Terrarum
|
||||
import com.torvald.terrarum.ui.ConsoleWindow
|
||||
|
||||
import java.util.Arrays
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
internal class Echo : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
val argsWoHeader = Array<String>(args.size - 1, {it -> args[it + 1]})
|
||||
|
||||
argsWoHeader.forEach(
|
||||
{ (Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage(it) })
|
||||
}
|
||||
|
||||
fun execute(single_line: String) {
|
||||
(Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage(single_line)
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user