mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-12 14:51:51 +09:00
com.torvald → net.torvald
Former-commit-id: 375604da8a20a6ba7cd0a8d05a44add02b2d04f4 Former-commit-id: 287287c5920b07618174d7a7573f049d350ded66
This commit is contained in:
48
src/net/torvald/CSVFetcher.kt
Normal file
48
src/net/torvald/CSVFetcher.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
package net.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()
|
||||
}
|
||||
}
|
||||
36
src/net/torvald/JsonFetcher.kt
Normal file
36
src/net/torvald/JsonFetcher.kt
Normal file
@@ -0,0 +1,36 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
43
src/net/torvald/JsonWriter.kt
Normal file
43
src/net/torvald/JsonWriter.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package net.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()
|
||||
}
|
||||
|
||||
}
|
||||
51
src/net/torvald/RasterWriter.kt
Normal file
51
src/net/torvald/RasterWriter.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package net.torvald
|
||||
|
||||
import net.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))
|
||||
}
|
||||
|
||||
}
|
||||
86
src/net/torvald/colourutil/Col216.kt
Normal file
86
src/net/torvald/colourutil/Col216.kt
Normal file
@@ -0,0 +1,86 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
72
src/net/torvald/colourutil/Col40
Normal file
72
src/net/torvald/colourutil/Col40
Normal file
@@ -0,0 +1,72 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
130
src/net/torvald/colourutil/Col4096.kt
Normal file
130
src/net/torvald/colourutil/Col4096.kt
Normal file
@@ -0,0 +1,130 @@
|
||||
package net.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
|
||||
}
|
||||
15
src/net/torvald/colourutil/HSV.kt
Normal file
15
src/net/torvald/colourutil/HSV.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.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
|
||||
)
|
||||
110
src/net/torvald/colourutil/HSVUtil.kt
Normal file
110
src/net/torvald/colourutil/HSVUtil.kt
Normal file
@@ -0,0 +1,110 @@
|
||||
package net.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)
|
||||
}
|
||||
|
||||
}
|
||||
17
src/net/torvald/colourutil/LimitedColours.kt
Normal file
17
src/net/torvald/colourutil/LimitedColours.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.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
|
||||
}
|
||||
557
src/net/torvald/imagefont/GameFontBase.kt
Normal file
557
src/net/torvald/imagefont/GameFontBase.kt
Normal file
@@ -0,0 +1,557 @@
|
||||
package net.torvald.imagefont
|
||||
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
79
src/net/torvald/imagefont/GameFontWhite.kt
Normal file
79
src/net/torvald/imagefont/GameFontWhite.kt
Normal file
@@ -0,0 +1,79 @@
|
||||
package net.torvald.imagefont
|
||||
|
||||
import net.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
|
||||
);*/
|
||||
}
|
||||
}
|
||||
22
src/net/torvald/point/Point2f.kt
Normal file
22
src/net/torvald/point/Point2f.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
13
src/net/torvald/random/Fudge3.kt
Normal file
13
src/net/torvald/random/Fudge3.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.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)
|
||||
47
src/net/torvald/random/FudgeDice.kt
Normal file
47
src/net/torvald/random/FudgeDice.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
59
src/net/torvald/random/HQRNG.java
Normal file
59
src/net/torvald/random/HQRNG.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package net.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));
|
||||
}
|
||||
|
||||
}
|
||||
1440
src/net/torvald/random/MTRandom.java
Normal file
1440
src/net/torvald/random/MTRandom.java
Normal file
File diff suppressed because it is too large
Load Diff
65
src/net/torvald/serialise/WriteCSV.kt
Normal file
65
src/net/torvald/serialise/WriteCSV.kt
Normal file
@@ -0,0 +1,65 @@
|
||||
package net.torvald.serialise
|
||||
|
||||
import net.torvald.CSVFetcher
|
||||
import net.torvald.terrarum.itemproperties.ItemPropCodex
|
||||
import net.torvald.terrarum.itemproperties.MaterialPropCodex
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.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
|
||||
}
|
||||
|
||||
}
|
||||
79
src/net/torvald/serialise/WriteGameMapData.kt
Normal file
79
src/net/torvald/serialise/WriteGameMapData.kt
Normal file
@@ -0,0 +1,79 @@
|
||||
package net.torvald.serialise
|
||||
|
||||
import net.torvald.terrarum.gamemap.GameMap
|
||||
import net.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()
|
||||
)
|
||||
}
|
||||
}
|
||||
101
src/net/torvald/serialise/WriteMeta.kt
Normal file
101
src/net/torvald/serialise/WriteMeta.kt
Normal file
@@ -0,0 +1,101 @@
|
||||
package net.torvald.serialise
|
||||
|
||||
import net.torvald.terrarum.mapgenerator.MapGenerator
|
||||
import net.torvald.terrarum.mapgenerator.RoguelikeRandomiser
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.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()
|
||||
)
|
||||
}
|
||||
}
|
||||
77
src/net/torvald/simplecipher/ROTUtil.kt
Normal file
77
src/net/torvald/simplecipher/ROTUtil.kt
Normal file
@@ -0,0 +1,77 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
261
src/net/torvald/spriteanimation/SpriteAnimation.kt
Normal file
261
src/net/torvald/spriteanimation/SpriteAnimation.kt
Normal file
@@ -0,0 +1,261 @@
|
||||
/* Original code author: Sean Laurvick
|
||||
* This code is based on the original author's code written in Lua.
|
||||
*/
|
||||
|
||||
package net.torvald.spriteanimation
|
||||
|
||||
import net.torvald.terrarum.Game
|
||||
import net.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)
|
||||
}
|
||||
}
|
||||
132
src/net/torvald/terrarum/ABOUT.md
Normal file
132
src/net/torvald/terrarum/ABOUT.md
Normal file
@@ -0,0 +1,132 @@
|
||||
## 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.
|
||||
|
||||
9
src/net/torvald/terrarum/COPYING.md
Normal file
9
src/net/torvald/terrarum/COPYING.md
Normal file
@@ -0,0 +1,9 @@
|
||||
* *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.
|
||||
19
src/net/torvald/terrarum/DefaultConfig.kt
Normal file
19
src/net/torvald/terrarum/DefaultConfig.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
323
src/net/torvald/terrarum/Game.kt
Normal file
323
src/net/torvald/terrarum/Game.kt
Normal file
@@ -0,0 +1,323 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import net.torvald.terrarum.gameactors.*
|
||||
import net.torvald.terrarum.console.Authenticator
|
||||
import net.torvald.terrarum.gamecontroller.GameController
|
||||
import net.torvald.terrarum.gamecontroller.Key
|
||||
import net.torvald.terrarum.gamecontroller.KeyMap
|
||||
import net.torvald.terrarum.gamecontroller.KeyToggler
|
||||
import net.torvald.terrarum.gamemap.GameMap
|
||||
import net.torvald.terrarum.gamemap.WorldTime
|
||||
import net.torvald.terrarum.mapdrawer.LightmapRenderer
|
||||
import net.torvald.terrarum.mapdrawer.MapCamera
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import net.torvald.terrarum.mapgenerator.MapGenerator
|
||||
import net.torvald.terrarum.mapgenerator.RoguelikeRandomiser
|
||||
import net.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import net.torvald.terrarum.tilestats.TileStats
|
||||
import net.torvald.terrarum.ui.BasicDebugInfoWindow
|
||||
import net.torvald.terrarum.ui.ConsoleWindow
|
||||
import net.torvald.terrarum.ui.Notification
|
||||
import net.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 = PBSigrid.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
|
||||
}
|
||||
}
|
||||
6
src/net/torvald/terrarum/GameConfig.kt
Normal file
6
src/net/torvald/terrarum/GameConfig.kt
Normal file
@@ -0,0 +1,6 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
class GameConfig : KVHashMap()
|
||||
97
src/net/torvald/terrarum/KVHashMap.kt
Normal file
97
src/net/torvald/terrarum/KVHashMap.kt
Normal file
@@ -0,0 +1,97 @@
|
||||
package net.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
|
||||
|
||||
}
|
||||
119
src/net/torvald/terrarum/MECHNANICS.md
Normal file
119
src/net/torvald/terrarum/MECHNANICS.md
Normal file
@@ -0,0 +1,119 @@
|
||||
|
||||
## 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
|
||||
|
||||
34
src/net/torvald/terrarum/MISC_FEATURES.md
Normal file
34
src/net/torvald/terrarum/MISC_FEATURES.md
Normal file
@@ -0,0 +1,34 @@
|
||||
## 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
|
||||
56
src/net/torvald/terrarum/SAVE_FORMAT.md
Normal file
56
src/net/torvald/terrarum/SAVE_FORMAT.md
Normal file
@@ -0,0 +1,56 @@
|
||||
## 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)
|
||||
377
src/net/torvald/terrarum/Terrarum.kt
Normal file
377
src/net/torvald/terrarum/Terrarum.kt
Normal file
@@ -0,0 +1,377 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import net.torvald.imagefont.GameFontWhite
|
||||
import net.torvald.JsonFetcher
|
||||
import net.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)
|
||||
}
|
||||
46
src/net/torvald/terrarum/console/Authenticator.kt
Normal file
46
src/net/torvald/terrarum/console/Authenticator.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
24
src/net/torvald/terrarum/console/Batch.kt
Normal file
24
src/net/torvald/terrarum/console/Batch.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.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")
|
||||
}
|
||||
}
|
||||
32
src/net/torvald/terrarum/console/CatStdout.kt
Normal file
32
src/net/torvald/terrarum/console/CatStdout.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
package net.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")
|
||||
}
|
||||
}
|
||||
45
src/net/torvald/terrarum/console/CodexEdictis.kt
Normal file
45
src/net/torvald/terrarum/console/CodexEdictis.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Game
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.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) }
|
||||
}
|
||||
|
||||
}
|
||||
47
src/net/torvald/terrarum/console/CommandDict.kt
Normal file
47
src/net/torvald/terrarum/console/CommandDict.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.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]!!
|
||||
}
|
||||
}
|
||||
116
src/net/torvald/terrarum/console/CommandInterpreter.kt
Normal file
116
src/net/torvald/terrarum/console/CommandInterpreter.kt
Normal file
@@ -0,0 +1,116 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
13
src/net/torvald/terrarum/console/ConsoleCommand.kt
Normal file
13
src/net/torvald/terrarum/console/ConsoleCommand.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
interface ConsoleCommand {
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun execute(args: Array<String>)
|
||||
|
||||
fun printUsage()
|
||||
|
||||
}
|
||||
26
src/net/torvald/terrarum/console/Echo.kt
Normal file
26
src/net/torvald/terrarum/console/Echo.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.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() {
|
||||
|
||||
}
|
||||
}
|
||||
38
src/net/torvald/terrarum/console/ExportAV.kt
Normal file
38
src/net/torvald/terrarum/console/ExportAV.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.JsonWriter
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-10.
|
||||
*/
|
||||
class ExportAV : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
try {
|
||||
JsonWriter.writeToFile(
|
||||
Terrarum.game.player.actorValue,
|
||||
Terrarum.defaultDir + "/Exports/" + args[1] + ".json")
|
||||
|
||||
Echo().execute("ExportAV: exported to " + args[1] + ".json")
|
||||
}
|
||||
catch (e: IOException) {
|
||||
Echo().execute("ExportAV: IOException raised.")
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Export ActorValue as JSON format.")
|
||||
echo.execute("Usage: exportav (id) filename-without-extension")
|
||||
echo.execute("blank ID for player")
|
||||
}
|
||||
}
|
||||
116
src/net/torvald/terrarum/console/ExportMap.kt
Normal file
116
src/net/torvald/terrarum/console/ExportMap.kt
Normal file
@@ -0,0 +1,116 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.colourutil.Col4096
|
||||
import net.torvald.RasterWriter
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
|
||||
import java.io.*
|
||||
import java.util.HashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-17.
|
||||
*/
|
||||
class ExportMap : ConsoleCommand {
|
||||
|
||||
//private var mapData: ByteArray? = null
|
||||
// private var mapDataPointer = 0
|
||||
|
||||
private val colorTable = HashMap<Int, Col4096>()
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
buildColorTable()
|
||||
|
||||
var mapData = ByteArray(Terrarum.game.map.width * Terrarum.game.map.height * 3)
|
||||
var mapDataPointer = 0
|
||||
|
||||
for (tile in Terrarum.game.map.terrainIterator()) {
|
||||
val colArray = (colorTable as Map<Int, Col4096>)
|
||||
.getOrElse(tile, { Col4096(0xFFF) }).toByteArray()
|
||||
|
||||
for (i in 0..2) {
|
||||
mapData[mapDataPointer + i] = colArray[i]
|
||||
}
|
||||
|
||||
mapDataPointer += 3
|
||||
}
|
||||
|
||||
val dir = Terrarum.defaultDir + "/Exports/"
|
||||
val dirAsFile = File(dir)
|
||||
if (!dirAsFile.exists()) {
|
||||
dirAsFile.mkdir()
|
||||
}
|
||||
|
||||
try {
|
||||
RasterWriter.writePNG_RGB(
|
||||
Terrarum.game.map.width, Terrarum.game.map.height, mapData, dir + args[1] + ".png")
|
||||
Echo().execute("ExportMap: exported to " + args[1] + ".png")
|
||||
|
||||
}
|
||||
catch (e: IOException) {
|
||||
Echo().execute("ExportMap: IOException raised.")
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
// mapData = null
|
||||
// mapDataPointer = 0
|
||||
|
||||
// Free up some memory
|
||||
System.gc()
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Usage: export <name>")
|
||||
echo.execute("Exports current map into visible image.")
|
||||
echo.execute("The image can be found at %adddata%/terrarum/Exports")
|
||||
}
|
||||
|
||||
private fun buildColorTable() {
|
||||
colorTable.put(TileNameCode.AIR, Col4096(0xCEF))
|
||||
colorTable.put(TileNameCode.STONE, Col4096(0x888))
|
||||
colorTable.put(TileNameCode.DIRT, Col4096(0x753))
|
||||
colorTable.put(TileNameCode.GRASS, Col4096(0x472))
|
||||
|
||||
colorTable.put(TileNameCode.ORE_COPPER, Col4096(0x6A8))
|
||||
colorTable.put(TileNameCode.ORE_IRON, Col4096(0xC75))
|
||||
colorTable.put(TileNameCode.ORE_GOLD, Col4096(0xA87))
|
||||
colorTable.put(TileNameCode.ORE_ILMENITE, Col4096(0x8AB))
|
||||
colorTable.put(TileNameCode.ORE_AURICHALCUM, Col4096(0xD92))
|
||||
colorTable.put(TileNameCode.ORE_SILVER, Col4096(0xDDD))
|
||||
|
||||
colorTable.put(TileNameCode.RAW_DIAMOND, Col4096(0x2BF))
|
||||
colorTable.put(TileNameCode.RAW_RUBY, Col4096(0xB10))
|
||||
colorTable.put(TileNameCode.RAW_EMERALD, Col4096(0x0B1))
|
||||
colorTable.put(TileNameCode.RAW_SAPPHIRE, Col4096(0x01B))
|
||||
colorTable.put(TileNameCode.RAW_TOPAZ, Col4096(0xC70))
|
||||
colorTable.put(TileNameCode.RAW_AMETHYST, Col4096(0x70C))
|
||||
|
||||
colorTable.put(TileNameCode.WATER, Col4096(0x038))
|
||||
colorTable.put(TileNameCode.LAVA, Col4096(0xF50))
|
||||
|
||||
colorTable.put(TileNameCode.SAND, Col4096(0xDDB))
|
||||
colorTable.put(TileNameCode.SAND_WHITE, Col4096(0xFFD))
|
||||
colorTable.put(TileNameCode.SAND_RED, Col4096(0xA32))
|
||||
colorTable.put(TileNameCode.SAND_DESERT, Col4096(0xEDB))
|
||||
colorTable.put(TileNameCode.SAND_BLACK, Col4096(0x444))
|
||||
colorTable.put(TileNameCode.SAND_GREEN, Col4096(0x9A6))
|
||||
|
||||
colorTable.put(TileNameCode.GRAVEL, Col4096(0x664))
|
||||
colorTable.put(TileNameCode.GRAVEL_GREY, Col4096(0x999))
|
||||
|
||||
colorTable.put(TileNameCode.ICE_NATURAL, Col4096(0x9AB))
|
||||
colorTable.put(TileNameCode.ICE_MAGICAL, Col4096(0x7AC))
|
||||
colorTable.put(TileNameCode.ICE_FRAGILE, Col4096(0x6AF))
|
||||
colorTable.put(TileNameCode.SNOW, Col4096(0xCDE))
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
src/net/torvald/terrarum/console/ForceGC.kt
Normal file
15
src/net/torvald/terrarum/console/ForceGC.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-18.
|
||||
*/
|
||||
class ForceGC : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
System.gc()
|
||||
Echo().execute("Invoked System.gc")
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Invoke garbage collection of JVM.")
|
||||
}
|
||||
}
|
||||
57
src/net/torvald/terrarum/console/GetAV.kt
Normal file
57
src/net/torvald/terrarum/console/GetAV.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.gameactors.ActorValue
|
||||
import net.torvald.terrarum.Game
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
class GetAV : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
val echo = Echo()
|
||||
|
||||
try {
|
||||
if (args.size == 1) {
|
||||
// print all actorvalue of player
|
||||
val av = Terrarum.game.player.actorValue
|
||||
val keyset = av.keySet
|
||||
|
||||
keyset.forEach { elem -> echo.execute("$elem = ${av[elem as String]}") }
|
||||
|
||||
}
|
||||
else if (args.size != 3 && args.size != 2) {
|
||||
printUsage()
|
||||
}
|
||||
else if (args.size == 2) {
|
||||
echo.execute("player." + args[1] + " = "
|
||||
+ Terrarum.game.player.actorValue[args[1]]
|
||||
+ " ("
|
||||
+ Terrarum.game.player.actorValue[args[1]]!!.javaClass.simpleName
|
||||
+ ")")
|
||||
}
|
||||
else if (args.size == 3) {
|
||||
|
||||
}
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
if (args.size == 2) {
|
||||
echo.execute(args[1] + ": actor value does not exist.")
|
||||
}
|
||||
else if (args.size == 3) {
|
||||
echo.execute(args[2] + ": actor value does not exist.")
|
||||
}
|
||||
else {
|
||||
throw NullPointerException()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Get desired actor value of specific target.")
|
||||
echo.execute("Usage: getav (id) <av>")
|
||||
echo.execute("blank ID for player")
|
||||
}
|
||||
}
|
||||
51
src/net/torvald/terrarum/console/GetFactioning.kt
Normal file
51
src/net/torvald/terrarum/console/GetFactioning.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.gameactors.faction.Faction
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
import java.util.HashSet
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-17.
|
||||
*/
|
||||
class GetFactioning : ConsoleCommand {
|
||||
|
||||
private val PRINT_INDENTATION = " --> "
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
val echo = Echo()
|
||||
|
||||
if (args.size == 1) {
|
||||
// get all factioning data of player
|
||||
val factionSet = Terrarum.game.player.faction
|
||||
|
||||
if (factionSet.isEmpty()) {
|
||||
echo.execute("The actor has empty faction set.")
|
||||
return
|
||||
}
|
||||
|
||||
val count = factionSet.size
|
||||
echo.execute(count.toString() + Lang.pluralise(" faction", count) + " assigned.")
|
||||
|
||||
for (faction in factionSet) {
|
||||
echo.execute("faction “${faction.factionName}”")
|
||||
echo.execute(" Amicable")
|
||||
faction.factionAmicable.forEach { s -> echo.execute(PRINT_INDENTATION + s) }
|
||||
|
||||
echo.execute(" Explicit neutral")
|
||||
faction.factionNeutral.forEach { s -> echo.execute(PRINT_INDENTATION + s) }
|
||||
|
||||
echo.execute(" Hostile")
|
||||
faction.factionHostile.forEach { s -> echo.execute(PRINT_INDENTATION + s) }
|
||||
|
||||
echo.execute(" Fearful")
|
||||
faction.factionFearful.forEach { s -> echo.execute(PRINT_INDENTATION + s) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
}
|
||||
}
|
||||
24
src/net/torvald/terrarum/console/GetLocale.kt
Normal file
24
src/net/torvald/terrarum/console/GetLocale.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-22.
|
||||
*/
|
||||
class GetLocale : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
Echo().execute(
|
||||
"Locale: "
|
||||
+ Lang.get("LANGUAGE_THIS")
|
||||
+ " ("
|
||||
+ Lang.get("LANGUAGE_EN")
|
||||
+ ")")
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Usage: getlocale")
|
||||
echo.execute("Get name of locale currently using.")
|
||||
}
|
||||
}
|
||||
21
src/net/torvald/terrarum/console/GetTime.kt
Normal file
21
src/net/torvald/terrarum/console/GetTime.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-20.
|
||||
*/
|
||||
class GetTime : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
val echo = Echo()
|
||||
val worldTime = Terrarum.game.map.worldTime
|
||||
echo.execute("Year ${worldTime.years}, Month ${worldTime.months}, " +
|
||||
"Day ${worldTime.days} (${worldTime.getDayNameShort()}), " +
|
||||
"${worldTime.getFormattedTime()}"
|
||||
)
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Print current world time in convenient form")
|
||||
}
|
||||
}
|
||||
47
src/net/torvald/terrarum/console/GsonTest.kt
Normal file
47
src/net/torvald/terrarum/console/GsonTest.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonElement
|
||||
|
||||
import java.io.BufferedWriter
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-10.
|
||||
*/
|
||||
class GsonTest : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
val avelem = Gson().toJsonTree(Terrarum.game.player)
|
||||
|
||||
val jsonString = avelem.toString()
|
||||
|
||||
val bufferedWriter: BufferedWriter
|
||||
val writer: FileWriter
|
||||
try {
|
||||
writer = FileWriter(Terrarum.defaultDir + "/Exports/" + args[1] + ".json")
|
||||
bufferedWriter = BufferedWriter(writer)
|
||||
|
||||
bufferedWriter.write(jsonString)
|
||||
bufferedWriter.close()
|
||||
|
||||
Echo().execute("GsonTest: exported to " + args[1] + ".json")
|
||||
}
|
||||
catch (e: IOException) {
|
||||
Echo().execute("GsonTest: IOException raised.")
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Usage: gsontest filename-without-extension")
|
||||
}
|
||||
}
|
||||
26
src/net/torvald/terrarum/console/Help.kt
Normal file
26
src/net/torvald/terrarum/console/Help.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-22.
|
||||
*/
|
||||
class Help : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
val echo = Echo()
|
||||
if (args.size == 1) {
|
||||
for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_$i"])
|
||||
}
|
||||
else if (args[1].toLowerCase() == "slow") {
|
||||
for (i in 1..4) echo.execute(Lang["HELP_OTF_SLOW_$i"])
|
||||
}
|
||||
else {
|
||||
for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_$i"])
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Prints some utility functions assigned to function row of the keyboard.")
|
||||
}
|
||||
}
|
||||
15
src/net/torvald/terrarum/console/QuitApp.kt
Normal file
15
src/net/torvald/terrarum/console/QuitApp.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
class QuitApp : ConsoleCommand {
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
}
|
||||
}
|
||||
61
src/net/torvald/terrarum/console/SetAV.kt
Normal file
61
src/net/torvald/terrarum/console/SetAV.kt
Normal file
@@ -0,0 +1,61 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Game
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
internal class SetAV : ConsoleCommand {
|
||||
|
||||
override fun printUsage() {
|
||||
val echo = Echo()
|
||||
echo.execute("Set actor value of specific target to desired value.")
|
||||
echo.execute("Usage: setav (id) <av> <val>")
|
||||
echo.execute("blank ID for player")
|
||||
echo.execute("Contaminated (float -> string) actor value will crash the game,")
|
||||
echo.execute(" so make it sure before you issue the command.")
|
||||
echo.execute("Use '__true' and '__false' for boolean value.")
|
||||
}
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
val echo = Echo()
|
||||
|
||||
// setav <id or "player"> <av> <val>
|
||||
if (args.size != 4 && args.size != 3) {
|
||||
printUsage()
|
||||
}
|
||||
else if (args.size == 3) {
|
||||
val `val`: Any
|
||||
|
||||
try {
|
||||
`val` = Integer(args[2]) // try for integer
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
|
||||
try {
|
||||
`val` = args[2].toFloat() // try for float
|
||||
}
|
||||
catch (ee: NumberFormatException) {
|
||||
if (args[2].equals("__true", ignoreCase = true)) {
|
||||
`val` = true
|
||||
}
|
||||
else if (args[2].equals("__false", ignoreCase = true)) {
|
||||
`val` = false
|
||||
}
|
||||
else {
|
||||
`val` = args[2] // string if not number
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Terrarum.game.player.actorValue[args[1]] = `val`
|
||||
echo.execute("Set " + args[1] + " to " + `val`)
|
||||
}
|
||||
else if (args.size == 4) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
31
src/net/torvald/terrarum/console/SetBulletin.kt
Normal file
31
src/net/torvald/terrarum/console/SetBulletin.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.ui.Notification
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-23.
|
||||
*/
|
||||
class SetBulletin : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
val testMsg = arrayOf(
|
||||
Lang["ERROR_SAVE_CORRUPTED"],
|
||||
Lang["MENU_LABEL_CONTINUE_QUESTION"]
|
||||
)
|
||||
send(testMsg)
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually send notifinator
|
||||
* @param message real message
|
||||
*/
|
||||
fun send(message: Array<String>) {
|
||||
Terrarum.game.sendNotification(message)
|
||||
println("sent notifinator")
|
||||
}
|
||||
}
|
||||
52
src/net/torvald/terrarum/console/SetGlobalLightLevel.kt
Normal file
52
src/net/torvald/terrarum/console/SetGlobalLightLevel.kt
Normal file
@@ -0,0 +1,52 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.mapdrawer.LightmapRenderer
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-17.
|
||||
*/
|
||||
class SetGlobalLightLevel : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 4) {
|
||||
try {
|
||||
val r = args[1].toInt()
|
||||
val g = args[2].toInt()
|
||||
val b = args[3].toInt()
|
||||
val GL = LightmapRenderer.constructRGBFromInt(r, g, b)
|
||||
|
||||
Terrarum.game.map.globalLight = GL
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
Echo().execute("Wrong number input.")
|
||||
}
|
||||
catch (e1: IllegalArgumentException) {
|
||||
Echo().execute("Range: 0-" + LightmapRenderer.CHANNEL_MAX + " per channel")
|
||||
}
|
||||
|
||||
}
|
||||
else if (args.size == 2) {
|
||||
try {
|
||||
val GL = args[1].toInt()
|
||||
|
||||
if (GL.toInt() < 0 || GL.toInt() >= LightmapRenderer.COLOUR_RANGE_SIZE) {
|
||||
Echo().execute("Range: 0-" + (LightmapRenderer.COLOUR_RANGE_SIZE - 1))
|
||||
}
|
||||
else {
|
||||
Terrarum.game.map.globalLight = GL
|
||||
}
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
Echo().execute("Wrong number input.")
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Usage: setgl [raw_value|r g b]")
|
||||
}
|
||||
}
|
||||
43
src/net/torvald/terrarum/console/SetLocale.kt
Normal file
43
src/net/torvald/terrarum/console/SetLocale.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.imagefont.GameFontBase
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import org.apache.commons.csv.CSVRecord
|
||||
import org.newdawn.slick.SlickException
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
class SetLocale : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
val prevLocale = Terrarum.gameLocale
|
||||
Terrarum.gameLocale = args[1]
|
||||
try {
|
||||
Echo().execute("Set locale to '" + Terrarum.gameLocale + "'.")
|
||||
}
|
||||
catch (e: IOException) {
|
||||
Echo().execute("could not read lang file.")
|
||||
Terrarum.gameLocale = prevLocale
|
||||
}
|
||||
|
||||
}
|
||||
else if (args.size == 1) {
|
||||
val echo = Echo()
|
||||
echo.execute("Locales:")
|
||||
|
||||
val record = Lang.getRecord("LANGUAGE_ID")
|
||||
record.forEach { field -> echo.execute("] " + field) }
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Usage: setlocale [locale]")
|
||||
}
|
||||
}
|
||||
42
src/net/torvald/terrarum/console/SetTime.kt
Normal file
42
src/net/torvald/terrarum/console/SetTime.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.gamemap.WorldTime
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-20.
|
||||
*/
|
||||
class SetTime : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
val lowercaseTime = args[1].toLowerCase()
|
||||
val timeToSet =
|
||||
if (args[1].length >= 4) {
|
||||
lowercaseTime.substringBefore('h').toInt() * WorldTime.HOUR_SEC +
|
||||
lowercaseTime.substringAfter('h').toInt() * WorldTime.MINUTE_SEC
|
||||
}
|
||||
else if (args[1].endsWith("h", true)) {
|
||||
lowercaseTime.substring(0, args[1].length - 1).toInt() * WorldTime.HOUR_SEC
|
||||
}
|
||||
else {
|
||||
lowercaseTime.toInt()
|
||||
}
|
||||
|
||||
Terrarum.game.map.worldTime.setTime(timeToSet)
|
||||
|
||||
Echo().execute("Set time to ${Terrarum.game.map.worldTime.elapsedSeconds()} " +
|
||||
"(${Terrarum.game.map.worldTime.hours}h${formatMin(Terrarum.game.map.worldTime.minutes)})")
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMin(min: Int): String {
|
||||
return if (min < 10) "0${min.toString()}" else min.toString()
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("usage: settime <39201-in sec or 13h32-in hour>")
|
||||
}
|
||||
}
|
||||
25
src/net/torvald/terrarum/console/SetTimeDelta.kt
Normal file
25
src/net/torvald/terrarum/console/SetTimeDelta.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-20.
|
||||
*/
|
||||
class SetTimeDelta : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
Terrarum.game.map.worldTime.setTimeDelta(args[1].toInt())
|
||||
if (Terrarum.game.map.worldTime.timeDelta == 0)
|
||||
Echo().execute("時間よ止まれ!ザ・ワルド!!")
|
||||
else
|
||||
Echo().execute("Set time delta to ${Terrarum.game.map.worldTime.timeDelta}")
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("usage: settimedelta <int>")
|
||||
}
|
||||
}
|
||||
38
src/net/torvald/terrarum/console/SpawnPhysTestBall.kt
Normal file
38
src/net/torvald/terrarum/console/SpawnPhysTestBall.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.gameactors.Actor
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||
import net.torvald.terrarum.gameactors.PhysTestBall
|
||||
import net.torvald.terrarum.mapdrawer.MapCamera
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-05.
|
||||
*/
|
||||
class SpawnPhysTestBall : ConsoleCommand {
|
||||
@Throws(Exception::class)
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
val mouseX = Terrarum.appgc.input.mouseX
|
||||
val mouseY = Terrarum.appgc.input.mouseY
|
||||
|
||||
val elasticity = args[1].toFloat()
|
||||
|
||||
val ball = PhysTestBall()
|
||||
ball.setPosition(
|
||||
(mouseX + MapCamera.cameraX).toFloat(),
|
||||
(mouseY + MapCamera.cameraY).toFloat()
|
||||
)
|
||||
ball.elasticity = elasticity
|
||||
|
||||
Terrarum.game.addActor(ball)
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("usage: spawnball [elasticity]")
|
||||
}
|
||||
}
|
||||
36
src/net/torvald/terrarum/console/TeleportPlayer.kt
Normal file
36
src/net/torvald/terrarum/console/TeleportPlayer.kt
Normal file
@@ -0,0 +1,36 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Game
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-24.
|
||||
*/
|
||||
class TeleportPlayer : ConsoleCommand {
|
||||
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size != 3) {
|
||||
printUsage()
|
||||
}
|
||||
else {
|
||||
|
||||
val x: Int
|
||||
val y: Int
|
||||
try {
|
||||
x = args[1].toInt() * MapDrawer.TILE_SIZE + MapDrawer.TILE_SIZE / 2
|
||||
y = args[2].toInt() * MapDrawer.TILE_SIZE + MapDrawer.TILE_SIZE / 2
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
Echo().execute("Wrong number input.")
|
||||
return
|
||||
}
|
||||
|
||||
Terrarum.game.player.setPosition(x.toFloat(), y.toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Usage: teleport [x-tile] [y-tile]")
|
||||
}
|
||||
}
|
||||
20
src/net/torvald/terrarum/console/ToggleNoClip.kt
Normal file
20
src/net/torvald/terrarum/console/ToggleNoClip.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Game
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
class ToggleNoClip : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
val status = Terrarum.game.player.isNoClip()
|
||||
|
||||
Terrarum.game.player.setNoClip(!status)
|
||||
Echo().execute("Set no-clip status to " + (!status).toString())
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("toggle no-clip status of player")
|
||||
}
|
||||
}
|
||||
42
src/net/torvald/terrarum/console/Zoom.kt
Normal file
42
src/net/torvald/terrarum/console/Zoom.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
class Zoom : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
|
||||
var zoom: Float
|
||||
try {
|
||||
zoom = args[1].toFloat()
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
Echo().execute("Wrong number input.")
|
||||
return
|
||||
}
|
||||
|
||||
if (zoom < Terrarum.game.ZOOM_MIN) {
|
||||
zoom = Terrarum.game.ZOOM_MIN
|
||||
}
|
||||
else if (zoom > Terrarum.game.ZOOM_MAX) {
|
||||
zoom = Terrarum.game.ZOOM_MAX
|
||||
}
|
||||
|
||||
Terrarum.game.screenZoom = zoom
|
||||
|
||||
System.gc()
|
||||
|
||||
Echo().execute("Set screen zoom to " + zoom.toString())
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Usage: zoom [zoom]")
|
||||
}
|
||||
}
|
||||
10
src/net/torvald/terrarum/gameactors/AIControlled.kt
Normal file
10
src/net/torvald/terrarum/gameactors/AIControlled.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.gameactors.ai.ActorAI
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface AIControlled {
|
||||
fun attachAI(ai: ActorAI)
|
||||
}
|
||||
34
src/net/torvald/terrarum/gameactors/AVKey.kt
Normal file
34
src/net/torvald/terrarum/gameactors/AVKey.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-04-02.
|
||||
*/
|
||||
object AVKey {
|
||||
const val MULT = "mult"
|
||||
|
||||
const val SPEED = "speed"
|
||||
const val SPEEDMULT = "speed$MULT"
|
||||
const val ACCEL = "accel"
|
||||
const val ACCELMULT = "accel$MULT"
|
||||
const val SCALE = "scale"
|
||||
const val BASEHEIGHT = "baseheight"
|
||||
const val BASEMASS = "basemass"
|
||||
const val JUMPPOWER = "jumppower"
|
||||
const val JUMPPOWERMULT = "jumppower$MULT"
|
||||
|
||||
const val STRENGTH = "strength"
|
||||
const val ENCUMBRANCE = "encumbrance"
|
||||
const val LUMINOSITY = "luminosity"
|
||||
const val PHYSIQUEMULT = "physique$MULT"
|
||||
|
||||
const val NAME = "name"
|
||||
|
||||
const val RACENAME = "racename"
|
||||
const val RACENAMEPLURAL = "racenameplural"
|
||||
const val TOOLSIZE = "toolsize"
|
||||
const val INTELLIGENT = "intelligent"
|
||||
|
||||
const val BASEDEFENCE = "basedefence" // creature base
|
||||
const val ARMOURDEFENCE = "armourdefence" // armour points
|
||||
const val ARMOURDEFENCEMULT = "armourdefence$MULT"
|
||||
}
|
||||
19
src/net/torvald/terrarum/gameactors/Actor.kt
Normal file
19
src/net/torvald/terrarum/gameactors/Actor.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package net.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
|
||||
}
|
||||
146
src/net/torvald/terrarum/gameactors/ActorInventory.kt
Normal file
146
src/net/torvald/terrarum/gameactors/ActorInventory.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.gameitem.InventoryItem
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
src/net/torvald/terrarum/gameactors/ActorValue.kt
Normal file
8
src/net/torvald/terrarum/gameactors/ActorValue.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.KVHashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-19.
|
||||
*/
|
||||
class ActorValue : KVHashMap()
|
||||
779
src/net/torvald/terrarum/gameactors/ActorWithBody.kt
Normal file
779
src/net/torvald/terrarum/gameactors/ActorWithBody.kt
Normal file
@@ -0,0 +1,779 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.gamemap.GameMap
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import net.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import net.torvald.spriteanimation.SpriteAnimation
|
||||
import com.jme3.math.FastMath
|
||||
import net.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
|
||||
*/
|
||||
val hitbox = Hitbox(0f,0f,0f,0f)
|
||||
@Transient val nextHitbox = Hitbox(0f,0f,0f,0f)
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@Transient private val map: GameMap
|
||||
|
||||
@Transient private val MASS_DEFAULT = 60f
|
||||
|
||||
|
||||
private var posAdjustX = 0
|
||||
private var posAdjustY = 0
|
||||
|
||||
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.set(
|
||||
x - (baseHitboxW / 2 - hitboxTranslateX) * scale,
|
||||
y - (baseHitboxH - hitboxTranslateY) * scale,
|
||||
baseHitboxW * scale,
|
||||
baseHitboxH * scale)
|
||||
|
||||
nextHitbox.set(
|
||||
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
|
||||
|
||||
// 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()
|
||||
veloY = 0f // reset veloY, simulating normal force
|
||||
elasticReflectY()
|
||||
grounded = true
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_BOTTOM, 0, 1)) { // the actor is standing ON the ground
|
||||
veloY = 0f // reset veloY, simulating normal force
|
||||
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()
|
||||
veloY = 0f // reset veloY, simulating normal force
|
||||
elasticReflectY()
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_TOP, 0, -1)) { // the actor is touching the ceiling
|
||||
veloY = 0f // reset veloY, simulating normal force
|
||||
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)
|
||||
|
||||
posAdjustY = -newYOff
|
||||
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)
|
||||
|
||||
posAdjustY = newYOff
|
||||
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()
|
||||
veloX = 0f // reset veloX, simulating normal force
|
||||
elasticReflectX()
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_RIGHT, 2, 0) && !isColliding(CONTACT_AREA_LEFT, 0, 0)) { // offset by +1, to fix directional quarks
|
||||
// the actor is touching the wall
|
||||
veloX = 0f // reset veloX, simulating normal force
|
||||
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()
|
||||
veloX = 0f // reset veloX, simulating normal force
|
||||
elasticReflectX()
|
||||
}
|
||||
else if (isColliding(CONTACT_AREA_LEFT, -1, 0) && !isColliding(CONTACT_AREA_RIGHT, 1, 0)) {
|
||||
// the actor is touching the wall
|
||||
veloX = 0f // reset veloX, simulating normal force
|
||||
elasticReflectX()
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
else { // check both sides?
|
||||
// System.out.println("updatehorizontal - |velo| < 0.5");
|
||||
//if (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT)) {
|
||||
// veloX = 0f // reset veloX, simulating normal force
|
||||
// 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 + 1, 0) // offset by +1, to fix directional quarks
|
||||
} while (newXOff < TSIZE && colliding)
|
||||
|
||||
val newX = nextHitbox.posX - newXOff // -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)
|
||||
|
||||
posAdjustX = newXOff
|
||||
val newX = nextHitbox.posX + newXOff // +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 * elasticity).abs() > 0.5) {
|
||||
veloX = -veloX * elasticity
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun elasticReflectY() {
|
||||
if (veloY != 0f && (veloY * elasticity).abs() > 0.5) {
|
||||
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..(if (side % 2 == 0) nextHitbox.width else nextHitbox.height).roundToInt() - 1) {
|
||||
// set tile positions
|
||||
val tileX: Int
|
||||
val tileY: Int
|
||||
if (side == CONTACT_AREA_BOTTOM) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxStart.x.roundToInt()
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxEnd.y.roundToInt() + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_TOP) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxStart.x.roundToInt()
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxStart.y.roundToInt() + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_RIGHT) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxEnd.x.roundToInt() + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxStart.y.roundToInt()
|
||||
+ i + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_LEFT) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxStart.x.roundToInt() + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxStart.y.roundToInt()
|
||||
+ 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..(if (side % 2 == 0) nextHitbox.width else nextHitbox.height).roundToInt() - 1) {
|
||||
// set tile positions
|
||||
val tileX: Int
|
||||
val tileY: Int
|
||||
if (side == CONTACT_AREA_BOTTOM) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxStart.x.roundToInt()
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxEnd.y.roundToInt() + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_TOP) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxStart.x.roundToInt()
|
||||
+ i + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxStart.y.roundToInt() + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_RIGHT) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxEnd.x.roundToInt() + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxStart.y.roundToInt()
|
||||
+ i + translateY)
|
||||
}
|
||||
else if (side == CONTACT_AREA_LEFT) {
|
||||
tileX = div16TruncateToMapWidth(nextHitbox.hitboxStart.x.roundToInt() + translateX)
|
||||
tileY = div16TruncateToMapHeight(nextHitbox.hitboxStart.y.roundToInt()
|
||||
+ 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 = (nextHitbox.posX / TSIZE).roundToInt()
|
||||
val tilePosXEnd = (nextHitbox.hitboxEnd.x / TSIZE).roundToInt()
|
||||
val tilePosY = (nextHitbox.pointedY / TSIZE).roundToInt()
|
||||
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 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 = (nextHitbox.posX / TSIZE).roundToInt()
|
||||
val tilePosYStart = (nextHitbox.posY / TSIZE).roundToInt()
|
||||
val tilePosXEnd = (nextHitbox.hitboxEnd.x / TSIZE).roundToInt()
|
||||
val tilePosYEnd = (nextHitbox.hitboxEnd.y / TSIZE).roundToInt()
|
||||
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() {
|
||||
|
||||
nextHitbox.set(
|
||||
(hitbox.posX + veloX).round()
|
||||
, (hitbox.posY + veloY).round()
|
||||
, (baseHitboxW * scale).round()
|
||||
, (baseHitboxH * scale).round()
|
||||
)
|
||||
/** 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) {
|
||||
TSIZE + nextHitbox.width / 2
|
||||
} else if (x >= (map.width * TSIZE).toFloat() - TSIZE.toFloat() - nextHitbox.width / 2) {
|
||||
(map.width * TSIZE).toFloat() - 1f - TSIZE.toFloat() - nextHitbox.width / 2
|
||||
} else {
|
||||
x
|
||||
}
|
||||
|
||||
private fun clampH(y: Float): Float =
|
||||
if (y < TSIZE + nextHitbox.height) {
|
||||
TSIZE + nextHitbox.height
|
||||
} else if (y >= (map.height * TSIZE).toFloat() - TSIZE.toFloat() - nextHitbox.height) {
|
||||
(map.height * TSIZE).toFloat() - 1f - TSIZE.toFloat() - nextHitbox.height
|
||||
} else {
|
||||
y
|
||||
}
|
||||
|
||||
private fun clampWtile(x: Int): Int =
|
||||
if (x < 0) {
|
||||
0
|
||||
} else if (x >= map.width) {
|
||||
map.width - 1
|
||||
} else {
|
||||
x
|
||||
}
|
||||
|
||||
private fun clampHtile(x: Int): Int =
|
||||
if (x < 0) {
|
||||
0
|
||||
} else if (x >= map.height) {
|
||||
map.height - 1
|
||||
} else {
|
||||
x
|
||||
}
|
||||
|
||||
private val isPlayerNoClip: Boolean
|
||||
get() = this is Player && this.isNoClip()
|
||||
|
||||
private fun quantiseTSize(v: Float): Int = FastMath.floor(v / TSIZE) * TSIZE
|
||||
|
||||
fun setDensity(density: Int) {
|
||||
if (density < 0)
|
||||
throw IllegalArgumentException("[ActorWithBody] $density: density cannot be negative.")
|
||||
|
||||
this.density = density.toFloat()
|
||||
}
|
||||
|
||||
fun Float.round() = Math.round(this).toFloat()
|
||||
fun Float.roundToInt(): Int = Math.round(this)
|
||||
fun Float.abs() = FastMath.abs(this)
|
||||
|
||||
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
|
||||
*/
|
||||
20
src/net/torvald/terrarum/gameactors/CanBeAnItem.kt
Normal file
20
src/net/torvald/terrarum/gameactors/CanBeAnItem.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.gameitem.InventoryItem
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface CanBeAnItem {
|
||||
|
||||
fun attachItemData()
|
||||
|
||||
fun getItemWeight(): Float
|
||||
|
||||
fun stopUpdateAndDraw()
|
||||
|
||||
fun resumeUpdateAndDraw()
|
||||
|
||||
var itemData: InventoryItem
|
||||
|
||||
}
|
||||
14
src/net/torvald/terrarum/gameactors/Controllable.kt
Normal file
14
src/net/torvald/terrarum/gameactors/Controllable.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package net.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)
|
||||
|
||||
}
|
||||
27
src/net/torvald/terrarum/gameactors/CreatureBuilder.kt
Normal file
27
src/net/torvald/terrarum/gameactors/CreatureBuilder.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.JsonFetcher
|
||||
import net.torvald.random.Fudge3
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import com.google.gson.JsonObject
|
||||
import org.newdawn.slick.SlickException
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
|
||||
object CreatureBuilder {
|
||||
|
||||
/**
|
||||
* @Param jsonFileName with extension
|
||||
*/
|
||||
@Throws(IOException::class, SlickException::class)
|
||||
fun create(jsonFileName: String): ActorWithBody {
|
||||
val actor = ActorWithBody()
|
||||
CreatureRawInjector.inject(actor.actorValue, jsonFileName)
|
||||
|
||||
return actor
|
||||
}
|
||||
}
|
||||
123
src/net/torvald/terrarum/gameactors/CreatureRawInjector.kt
Normal file
123
src/net/torvald/terrarum/gameactors/CreatureRawInjector.kt
Normal file
@@ -0,0 +1,123 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.JsonFetcher
|
||||
import net.torvald.random.Fudge3
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/net/torvald/terrarum/gameactors/DroppedItem.kt
Normal file
22
src/net/torvald/terrarum/gameactors/DroppedItem.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package net.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)
|
||||
}
|
||||
}
|
||||
13
src/net/torvald/terrarum/gameactors/Factionable.kt
Normal file
13
src/net/torvald/terrarum/gameactors/Factionable.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.gameactors.faction.Faction
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Factionable {
|
||||
|
||||
var faction: HashSet<Faction>
|
||||
|
||||
}
|
||||
13
src/net/torvald/terrarum/gameactors/Glowing.kt
Normal file
13
src/net/torvald/terrarum/gameactors/Glowing.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.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)
|
||||
}
|
||||
115
src/net/torvald/terrarum/gameactors/Hitbox.kt
Normal file
115
src/net/torvald/terrarum/gameactors/Hitbox.kt
Normal file
@@ -0,0 +1,115 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.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
|
||||
}
|
||||
19
src/net/torvald/terrarum/gameactors/LandHolder.kt
Normal file
19
src/net/torvald/terrarum/gameactors/LandHolder.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package net.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();
|
||||
|
||||
}
|
||||
24
src/net/torvald/terrarum/gameactors/Luminous.kt
Normal file
24
src/net/torvald/terrarum/gameactors/Luminous.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.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
|
||||
|
||||
}
|
||||
102
src/net/torvald/terrarum/gameactors/NPCIntelligentBase.kt
Normal file
102
src/net/torvald/terrarum/gameactors/NPCIntelligentBase.kt
Normal file
@@ -0,0 +1,102 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.terrarum.gameactors.ai.ActorAI
|
||||
import net.torvald.terrarum.gameactors.faction.Faction
|
||||
import net.torvald.terrarum.gameitem.InventoryItem
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
31
src/net/torvald/terrarum/gameactors/PBCynthia.kt
Normal file
31
src/net/torvald/terrarum/gameactors/PBCynthia.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.spriteanimation.SpriteAnimation
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-25.
|
||||
*/
|
||||
object PBCynthia {
|
||||
|
||||
fun create(): Player {
|
||||
val p: Player = Player()
|
||||
CreatureRawInjector.inject(p.actorValue, "CreatureHuman.json")
|
||||
|
||||
p.actorValue["selectedtile"] = 16
|
||||
|
||||
p.sprite = SpriteAnimation()
|
||||
p.sprite!!.setDimension(26, 42)
|
||||
p.sprite!!.setSpriteImage("res/graphics/sprites/test_player_2.png")
|
||||
p.sprite!!.setDelay(200)
|
||||
p.sprite!!.setRowsAndFrames(1, 1)
|
||||
p.sprite!!.setAsVisible()
|
||||
|
||||
p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: Player.BASE_HEIGHT, 9, 0)
|
||||
|
||||
p.setPosition((4096 * MapDrawer.TILE_SIZE).toFloat(), (300 * 16).toFloat())
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
}
|
||||
73
src/net/torvald/terrarum/gameactors/PBSigrid.kt
Normal file
73
src/net/torvald/terrarum/gameactors/PBSigrid.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.JsonFetcher
|
||||
import net.torvald.terrarum.gameactors.faction.Faction
|
||||
import net.torvald.spriteanimation.SpriteAnimation
|
||||
import com.google.gson.JsonObject
|
||||
import net.torvald.terrarum.gameactors.faction.FactionFactory
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import org.newdawn.slick.SlickException
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
|
||||
object PBSigrid {
|
||||
|
||||
fun create(): Player {
|
||||
val p = Player()
|
||||
|
||||
p.sprite = SpriteAnimation()
|
||||
p.sprite!!.setDimension(28, 51)
|
||||
p.sprite!!.setSpriteImage("res/graphics/sprites/test_player.png")
|
||||
p.sprite!!.setDelay(200)
|
||||
p.sprite!!.setRowsAndFrames(1, 1)
|
||||
p.sprite!!.setAsVisible()
|
||||
|
||||
p.spriteGlow = SpriteAnimation()
|
||||
p.spriteGlow!!.setDimension(28, 51)
|
||||
p.spriteGlow!!.setSpriteImage("res/graphics/sprites/test_player_glow.png")
|
||||
p.spriteGlow!!.setDelay(200)
|
||||
p.spriteGlow!!.setRowsAndFrames(1, 1)
|
||||
p.spriteGlow!!.setAsVisible()
|
||||
|
||||
p.actorValue = ActorValue()
|
||||
p.actorValue[AVKey.SCALE] = 1.0f
|
||||
p.actorValue[AVKey.SPEED] = 4.0f
|
||||
p.actorValue[AVKey.SPEEDMULT] = 1.0f
|
||||
p.actorValue[AVKey.ACCEL] = Player.WALK_ACCEL_BASE
|
||||
p.actorValue[AVKey.ACCELMULT] = 1.0f
|
||||
p.actorValue[AVKey.JUMPPOWER] = 5f
|
||||
|
||||
p.actorValue[AVKey.BASEMASS] = 80f
|
||||
p.actorValue[AVKey.PHYSIQUEMULT] = 1 // Constant 1.0 for player, meant to be used by random mobs
|
||||
/**
|
||||
* fixed value, or 'base value', from creature strength of Dwarf Fortress.
|
||||
* Human race uses 1000. (see CreatureHuman.json)
|
||||
*/
|
||||
p.actorValue[AVKey.STRENGTH] = 1414
|
||||
p.actorValue[AVKey.ENCUMBRANCE] = 1000
|
||||
p.actorValue[AVKey.BASEHEIGHT] = 46
|
||||
|
||||
p.actorValue[AVKey.NAME] = "Sigrid"
|
||||
|
||||
p.actorValue[AVKey.INTELLIGENT] = true
|
||||
|
||||
p.actorValue[AVKey.LUMINOSITY] = 95487100
|
||||
|
||||
p.actorValue[AVKey.BASEDEFENCE] = 141
|
||||
|
||||
p.actorValue["selectedtile"] = 16
|
||||
|
||||
p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT)!!, 10, 0)
|
||||
|
||||
p.inventory = ActorInventory(0x7FFFFFFF, true)
|
||||
|
||||
p.setPosition((4096 * MapDrawer.TILE_SIZE).toFloat(), (300 * 16).toFloat())
|
||||
|
||||
p.faction.add(FactionFactory.create("FactionSigrid.json"))
|
||||
|
||||
return p
|
||||
}
|
||||
}
|
||||
31
src/net/torvald/terrarum/gameactors/PhysTestBall.kt
Normal file
31
src/net/torvald/terrarum/gameactors/PhysTestBall.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.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)
|
||||
}
|
||||
}
|
||||
493
src/net/torvald/terrarum/gameactors/Player.kt
Normal file
493
src/net/torvald/terrarum/gameactors/Player.kt
Normal file
@@ -0,0 +1,493 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.gameactors.faction.Faction
|
||||
import net.torvald.terrarum.gamecontroller.EnumKeyFunc
|
||||
import net.torvald.terrarum.gamecontroller.KeyMap
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.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()
|
||||
}
|
||||
|
||||
}
|
||||
24
src/net/torvald/terrarum/gameactors/PlayerBuilder.kt
Normal file
24
src/net/torvald/terrarum/gameactors/PlayerBuilder.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import org.newdawn.slick.SlickException
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object PlayerBuilder {
|
||||
private val JSONPATH = "./res/raw/"
|
||||
private val jsonString = String()
|
||||
|
||||
@Throws(IOException::class, SlickException::class)
|
||||
fun create(): Player {
|
||||
val p: Player = Player()
|
||||
CreatureRawInjector.inject(p.actorValue, "CreatureHuman.json")
|
||||
|
||||
// attach sprite
|
||||
|
||||
// do etc.
|
||||
|
||||
return p
|
||||
}
|
||||
}
|
||||
10
src/net/torvald/terrarum/gameactors/Pocketed.kt
Normal file
10
src/net/torvald/terrarum/gameactors/Pocketed.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface Pocketed {
|
||||
|
||||
var inventory: ActorInventory
|
||||
|
||||
}
|
||||
13
src/net/torvald/terrarum/gameactors/Visible.kt
Normal file
13
src/net/torvald/terrarum/gameactors/Visible.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.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)
|
||||
}
|
||||
7
src/net/torvald/terrarum/gameactors/ai/ActorAI.kt
Normal file
7
src/net/torvald/terrarum/gameactors/ai/ActorAI.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package net.torvald.terrarum.gameactors.ai
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
interface ActorAI {
|
||||
}
|
||||
62
src/net/torvald/terrarum/gameactors/faction/Faction.kt
Normal file
62
src/net/torvald/terrarum/gameactors/faction/Faction.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
package net.torvald.terrarum.gameactors.faction
|
||||
|
||||
import net.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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.torvald.terrarum.gameactors.faction
|
||||
|
||||
import net.torvald.JsonFetcher
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-02-15.
|
||||
*/
|
||||
object FactionFactory {
|
||||
|
||||
const val JSONPATH = "./res/raw/factions/"
|
||||
|
||||
/**
|
||||
* @param filename with extension
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun create(filename: String): Faction {
|
||||
val jsonObj = JsonFetcher.readJson(JSONPATH + filename)
|
||||
val factionObj = Faction(jsonObj.get("factionname").asString)
|
||||
|
||||
|
||||
jsonObj.get("factionamicable").asJsonArray.forEach { s -> factionObj.addFactionAmicable(s.asString) }
|
||||
jsonObj.get("factionneutral").asJsonArray.forEach { s -> factionObj.addFactionNeutral(s.asString) }
|
||||
jsonObj.get("factionhostile").asJsonArray.forEach { s -> factionObj.addFactionHostile(s.asString) }
|
||||
jsonObj.get("factionfearful").asJsonArray.forEach { s -> factionObj.addFactionFearful(s.asString) }
|
||||
|
||||
return factionObj
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.torvald.terrarum.gameactors.scheduler
|
||||
|
||||
/**
|
||||
* Ultima-like NPC scheduler
|
||||
* Created by minjaesong on 16-03-26.
|
||||
*/
|
||||
interface NPCSchedule {
|
||||
}
|
||||
9
src/net/torvald/terrarum/gamecontroller/EnumKeyFunc.kt
Normal file
9
src/net/torvald/terrarum/gamecontroller/EnumKeyFunc.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package net.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
|
||||
}
|
||||
122
src/net/torvald/terrarum/gamecontroller/GameController.kt
Normal file
122
src/net/torvald/terrarum/gamecontroller/GameController.kt
Normal file
@@ -0,0 +1,122 @@
|
||||
package net.torvald.terrarum.gamecontroller
|
||||
|
||||
import net.torvald.terrarum.gameactors.Controllable
|
||||
import net.torvald.terrarum.gameactors.Player
|
||||
import net.torvald.terrarum.mapdrawer.MapCamera
|
||||
import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
import net.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import net.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
|
||||
}
|
||||
}
|
||||
86
src/net/torvald/terrarum/gamecontroller/Key.kt
Normal file
86
src/net/torvald/terrarum/gamecontroller/Key.kt
Normal file
@@ -0,0 +1,86 @@
|
||||
package net.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
|
||||
}
|
||||
31
src/net/torvald/terrarum/gamecontroller/KeyMap.kt
Normal file
31
src/net/torvald/terrarum/gamecontroller/KeyMap.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.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)
|
||||
}
|
||||
|
||||
}
|
||||
48
src/net/torvald/terrarum/gamecontroller/KeyToggler.kt
Normal file
48
src/net/torvald/terrarum/gamecontroller/KeyToggler.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
package net.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
|
||||
}
|
||||
|
||||
}
|
||||
66
src/net/torvald/terrarum/gameitem/InventoryItem.kt
Normal file
66
src/net/torvald/terrarum/gameitem/InventoryItem.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
package net.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)
|
||||
}
|
||||
39
src/net/torvald/terrarum/gameitem/TileAsItem.kt
Normal file
39
src/net/torvald/terrarum/gameitem/TileAsItem.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
package net.torvald.terrarum.gameitem
|
||||
|
||||
import net.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()
|
||||
}
|
||||
}
|
||||
223
src/net/torvald/terrarum/gamemap/GameMap.kt
Normal file
223
src/net/torvald/terrarum/gamemap/GameMap.kt
Normal file
@@ -0,0 +1,223 @@
|
||||
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
56
src/net/torvald/terrarum/gamemap/MapLayer.kt
Normal file
56
src/net/torvald/terrarum/gamemap/MapLayer.kt
Normal file
@@ -0,0 +1,56 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
|
||||
35
src/net/torvald/terrarum/gamemap/MapPoint.kt
Normal file
35
src/net/torvald/terrarum/gamemap/MapPoint.kt
Normal file
@@ -0,0 +1,35 @@
|
||||
package net.torvald.terrarum.gamemap
|
||||
|
||||
import net.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())
|
||||
}
|
||||
}
|
||||
86
src/net/torvald/terrarum/gamemap/PairedMapLayer.kt
Normal file
86
src/net/torvald/terrarum/gamemap/PairedMapLayer.kt
Normal file
@@ -0,0 +1,86 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
153
src/net/torvald/terrarum/gamemap/WorldTime.kt
Normal file
153
src/net/torvald/terrarum/gamemap/WorldTime.kt
Normal file
@@ -0,0 +1,153 @@
|
||||
package net.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
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user