WIP collision solver, colour-codes in game fonts

Former-commit-id: 0bb85999176d89956398bbcc24e1b33cacd3e87c
Former-commit-id: 0ef0c1ac9b88f8fe42a7439fee69a8d4792be96a
This commit is contained in:
Song Minjae
2016-04-23 23:08:42 +09:00
parent cffc9a9ba2
commit 1a1159b643
33 changed files with 557 additions and 230 deletions

View File

@@ -59,16 +59,16 @@ class Col216 : LimitedColours {
private fun assertRaw(i: Int) {
if (i >= COLOUR_RANGE_SIZE || i < 0) {
println("i: " + i.toString())
System.err.println("Illegal colour input: $i")
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())
System.err.println("Illegal colour input for channel r: $r")
System.err.println("Illegal colour input for channel g: $g")
System.err.println("Illegal colour input for channel b: $b")
throw IllegalArgumentException()
}
}

View File

@@ -111,17 +111,17 @@ class Col4096 : LimitedColours {
private fun assertRaw(i: Int) {
if (i > 0xFFFF || i < 0) {
println("i: " + i.toString())
System.err.println("Illegal colour input: $i")
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())
System.err.println("Illegal colour input for channel a: $a")
System.err.println("Illegal colour input for channel r: $r")
System.err.println("Illegal colour input for channel g: $g")
System.err.println("Illegal colour input for channel b: $b")
throw IllegalArgumentException()
}
}

View File

@@ -53,7 +53,7 @@ constructor() : Font {
private fun isHangul(c: Char) = c.toInt() >= 0xAC00 && c.toInt() < 0xD7A4
private fun isAscii(c: Char) = c.toInt() > 0 && c.toInt() <= 0xFF
private fun isAscii(c: Char) = c.toInt() > 0x20 && c.toInt() <= 0xFF
private fun isRunic(c: Char) = runicList.contains(c)
@@ -138,7 +138,9 @@ constructor() : Font {
}
if (c == SHEET_ASCII_EF || c == SHEET_EXTA_EF || c == SHEET_CYRILIC_EF)
if (c == SHEET_COLOURCODE)
len += 0
else 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
@@ -163,12 +165,19 @@ constructor() : Font {
GL11.glColorMask(true, true, true, true)
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
var thisCol = color
// 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 (ch.isColourCode()) {
thisCol = colourKey[ch]!!
continue
}
if (isHangul(ch)) {
val hIndex = ch.toInt() - 0xAC00
@@ -204,17 +213,17 @@ constructor() : Font {
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
thisCol
)
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
thisCol
)
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
thisCol
)
}
}
@@ -246,6 +255,11 @@ constructor() : Font {
for (i in 0..s.length - 1) {
val ch = s[i]
if (ch.isColourCode()) {
thisCol = colourKey[ch]!!
continue
}
if (isWenQuanYi1(ch)) {
val glyphW = getWidth("" + ch)
/*wenQuanYi_1.renderInUse(
@@ -257,7 +271,7 @@ constructor() : Font {
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
thisCol
)
}
}
@@ -269,6 +283,11 @@ constructor() : Font {
for (i in 0..s.length - 1) {
val ch = s[i]
if (ch.isColourCode()) {
thisCol = colourKey[ch]!!
continue
}
if (isWenQuanYi2(ch)) {
val glyphW = getWidth("" + ch)
/*wenQuanYi_2.renderInUse(
@@ -280,7 +299,7 @@ constructor() : Font {
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
thisCol
)
}
}
@@ -292,6 +311,11 @@ constructor() : Font {
for (i in 0..s.length - 1) {
val ch = s[i]
if (ch.isColourCode()) {
thisCol = colourKey[ch]!!
continue
}
if (!isHangul(ch) && !isUniHan(ch)) {
// if not init, endUse first
@@ -369,7 +393,7 @@ constructor() : Font {
else if (prevInstance == SHEET_FW_UNI) (H - H_HANGUL) / 2
else 0).toFloat(),
color
thisCol
)
}
catch (e: ArrayIndexOutOfBoundsException) {
@@ -413,6 +437,8 @@ constructor() : Font {
return SHEET_CJK_PUNCT
else if (isFullwidthUni(c))
return SHEET_FW_UNI
else if (c.isColourCode())
return SHEET_COLOURCODE
else
return SHEET_ASCII_EM// fixed width punctuations
// fixed width
@@ -464,6 +490,8 @@ constructor() : Font {
Terrarum.appgc.graphics.setDrawMode(Graphics.MODE_NORMAL)
}
fun Char.isColourCode() = colourKey.containsKey(this)
companion object {
lateinit internal var hangulSheet: SpriteSheet
@@ -510,6 +538,8 @@ constructor() : Font {
internal val SHEET_WENQUANYI_1 = 13
internal val SHEET_WENQUANYI_2 = 14
internal val SHEET_COLOURCODE = 255
lateinit internal var sheetKey: Array<SpriteSheet>
internal val asciiEFList = arrayOf(' ', '!', '"', '\'', '(', ')', ',', '.', ':', ';', 'I', '[', ']', '`', 'f', 'i', 'j', 'l', 't', '{', '|', '}', 0xA1.toChar(), 'Ì', 'Í', 'Î', 'Ï', 'ì', 'í', 'î', 'ï', '·')
@@ -554,5 +584,30 @@ constructor() : Font {
internal val runicList = arrayOf('ᚠ', 'ᚢ', 'ᚦ', 'ᚬ', 'ᚱ', 'ᚴ', 'ᚼ', 'ᚾ', '', 'ᛅ', 'ᛋ', 'ᛏ', 'ᛒ', 'ᛘ', 'ᛚ', 'ᛦ', 'ᛂ', '', '᛫', '', 'ᛮ', 'ᛯ', 'ᛰ')
internal var interchar = 0
val colourKey = hashMapOf(
Pair(0x11.toChar(), Color(0xFFFFFF)), //w
Pair(0x12.toChar(), Color(0xFF8080)), //r
Pair(0x13.toChar(), Color(0x80FF80)), //g
Pair(0x14.toChar(), Color(0x8080FF)), //b
Pair(0x15.toChar(), Color(0xFFE080)), //y
Pair(0x16.toChar(), Color(0x808080)) //k
)
val colToCode = hashMapOf(
Pair("w", 0x11.toChar()),
Pair("r", 0x12.toChar()),
Pair("g", 0x13.toChar()),
Pair("b", 0x14.toChar()),
Pair("y", 0x15.toChar()),
Pair("k", 0x16.toChar())
)
val codeToCol = hashMapOf(
Pair("w", colourKey[0x11.toChar()]),
Pair("r", colourKey[0x12.toChar()]),
Pair("g", colourKey[0x13.toChar()]),
Pair("b", colourKey[0x14.toChar()]),
Pair("y", colourKey[0x15.toChar()]),
Pair("k", colourKey[0x16.toChar()])
)
}
}

View File

@@ -47,7 +47,7 @@ constructor() : GameFontBase() {
GameFontBase.wenQuanYi_2 = SpriteSheet(
"./res/graphics/fonts/wenquanyi_11pt_part2.png", 16, 18, 2)
val shk = arrayOf<SpriteSheet>(
val shk = arrayOf(
GameFontBase.asciiSheet,
GameFontBase.asciiSheetEF,
GameFontBase.hangulSheet,

View File

@@ -1,50 +0,0 @@
package net.torvald.imagefont
import org.newdawn.slick.Color
import org.newdawn.slick.Font
import org.newdawn.slick.SpriteSheet
/**
* Created by minjaesong on 16-04-15.
*/
class SmallNumbers : Font {
internal val fontSheet: SpriteSheet
internal val W = 8
internal val H = 8
private val chars = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-')
init {
fontSheet = SpriteSheet("./res/graphics/fonts/numeric_small.png", W, H)
}
override fun getHeight(str: String): Int = H
override fun getWidth(str: String): Int = str.length * W
override fun getLineHeight(): Int = H
override fun drawString(x: Float, y: Float, text: String) = drawString(x, y, text, Color.white)
override fun drawString(x: Float, y: Float, text: String, col: Color) {
for (i in 0..text.length - 1) {
val index = charToSpriteNum(text.codePointAt(i))
if (index != null) {
fontSheet.getSubImage(index, 0).draw(
x + i * W, y, col
)
}
}
}
override fun drawString(x: Float, y: Float, text: String, col: Color, startIndex: Int, endIndex: Int) {
throw UnsupportedOperationException()
}
private fun charToSpriteNum(ch: Int): Int? =
if (ch in '0'.toInt()..'9'.toInt()) ch - '0'.toInt()
else if (ch == '-'.toInt()) 10
else null
}

View File

@@ -0,0 +1,78 @@
package net.torvald.imagefont
import org.newdawn.slick.Color
import org.newdawn.slick.Font
import org.newdawn.slick.SpriteSheet
import java.util.*
/**
* Created by minjaesong on 16-04-15.
*/
class TinyAlphNum : Font {
internal val fontSheet: SpriteSheet
internal val W = 8
internal val H = 8
private val chars = arrayOf(
'0','1','2','3','4','5','6','7',
'8','9','[','#','@',':','>','?',
' ','A','B','C','D','E','F','G',
'H','I','&','.',']','(','<','\\',
'^','J','K','L','M','N','O','P',
'Q','R','-','¤','*',')',';','\'',
'+','/','S','T','U','V','W','X',
'Y','Z','_',',','%','=','"','!'
)
private val mappingTable = HashMap<Int, Int>()
init {
fontSheet = SpriteSheet("./res/graphics/fonts/alphanumeric_small.png", W, H)
chars.forEachIndexed { i, c -> mappingTable[c.toInt()] = i }
}
override fun getHeight(str: String): Int = H
override fun getWidth(str: String): Int {
var ret = 0
for (i in 0..str.length - 1) {
val c = str.codePointAt(i).toChar()
if (!c.isColourCode())
ret += W
}
return ret
}
override fun getLineHeight(): Int = H
override fun drawString(x: Float, y: Float, text: String) = drawString(x, y, text, Color.white)
override fun drawString(x: Float, y: Float, text: String, col: Color) {
var thisCol = col
var textPosOffset = 0
for (i in 0..text.length - 1) {
val index = charToSpriteNum(text.toUpperCase().codePointAt(i))
val ch = text[i]
if (ch.isColourCode()) {
thisCol = GameFontBase.colourKey[ch]!!
continue
}
if (index != null) {
fontSheet.getSubImage(index % 8, index / 8).draw(
x + textPosOffset, y, thisCol
)
}
textPosOffset += W
}
}
override fun drawString(x: Float, y: Float, text: String, col: Color, startIndex: Int, endIndex: Int) {
throw UnsupportedOperationException()
}
private fun charToSpriteNum(ch: Int): Int? = mappingTable[ch]
fun Char.isColourCode() = GameFontBase.colourKey.containsKey(this)
}

View File

@@ -34,11 +34,17 @@ import java.util.*
*/
class Game @Throws(SlickException::class)
constructor() : BasicGameState() {
private val ACTOR_UPDATE_RANGE = 4096
internal var game_mode = 0
lateinit var map: GameMap
val actorContainer = LinkedList<Actor>()
/**
* Linked list of Actors that is sorted by Actors' referenceID
*/
val actorContainer = ArrayList<Actor>(128)
val actorcontainerInactive = ArrayList<Actor>(128)
val uiContainer = LinkedList<UIHandler>()
lateinit var consoleHandler: UIHandler
@@ -128,7 +134,6 @@ constructor() : BasicGameState() {
update_delta = delta
setAppTitle()
// GL at after_sunrise-noon_before_sunset
map.updateWorldTime(delta)
map.globalLight = globalLightByTime
@@ -139,12 +144,19 @@ constructor() : BasicGameState() {
MapDrawer.update(gc, delta)
MapCamera.update(gc, delta)
actorContainer.forEach { actor -> actor.update(gc, delta) }
actorContainer.forEach { actor ->
if (actor is Visible) {
actorContainer.forEach { actor -> // update actors
if (actor !is Visible
|| actor is Visible && distToActorSqr(actor, player) < ACTOR_UPDATE_RANGE.sqr())
// update if the does not have specific position. (visible)
// if the actor has position (visible), update only if it is within the range
actor.update(gc, delta)
}
actorContainer.forEach { actor -> // update sprite(s)
if (actor is Visible &&
distToActorSqr(actor, player) <= (Terrarum.WIDTH.plus(actor.hitbox.width.div(2)).sqr() +
Terrarum.HEIGHT.plus(actor.hitbox.height.div(2)).sqr())
) { // if visible and within screen
actor.updateBodySprite(gc, delta)
}
if (actor is Glowing) {
actor.updateGlowSprite(gc, delta)
}
}
@@ -186,7 +198,14 @@ constructor() : BasicGameState() {
MapCamera.renderBehind(gc, g)
// draw actors
actorContainer.forEach { actor -> if (actor is Visible) actor.drawBody(gc, g) }
actorContainer.forEach { actor ->
if (actor is Visible &&
distToActorSqr(actor, player) <= (Terrarum.WIDTH.plus(actor.hitbox.width.div(2)).sqr() +
Terrarum.HEIGHT.plus(actor.hitbox.height.div(2)).sqr())
) { // if visible and within screen
actor.drawBody(gc, g)
}
}
player.drawBody(gc, g)
LightmapRenderer.renderLightMap()
@@ -204,12 +223,19 @@ constructor() : BasicGameState() {
setBlendNormal()
// draw actor glows
actorContainer.forEach { actor -> if (actor is Glowing) actor.drawGlow(gc, g) }
actorContainer.forEach { actor ->
if (actor is Visible &&
distToActorSqr(actor, player) <= (Terrarum.WIDTH.plus(actor.hitbox.width.div(2)).sqr() +
Terrarum.HEIGHT.plus(actor.hitbox.height.div(2)).sqr())
) {
actor.drawGlow(gc, g)
}
}
player.drawGlow(gc, g)
// draw reference ID if debugWindow is open
if (debugWindow.isVisible) {
actorContainer.forEach { actor ->
if (debugWindow.visible) {
actorContainer.forEachIndexed { i, actor ->
if (actor is Visible) {
g.color = Color.white
g.font = Terrarum.smallNumbers
@@ -218,6 +244,12 @@ constructor() : BasicGameState() {
actor.hitbox.posX,
actor.hitbox.pointedY + 4
)
g.color = Color(0x80FF80)
g.drawString(
i.toString(),
actor.hitbox.posX,
actor.hitbox.pointedY + 12
)
g.font = Terrarum.gameFont
}
}
@@ -304,40 +336,67 @@ constructor() : BasicGameState() {
(this and 0xff00).ushr(8).shl(10) or
(this and 0xff0000).ushr(16).shl(20)
fun Float.sqr() = this * this
fun Int.sqr() = this * this
private fun distToActorSqr(a: Visible, p: Player) =
(a.hitbox.centeredX - p.hitbox.centeredX).sqr() + (a.hitbox.centeredY - p.hitbox.centeredY).sqr()
/**
* actorContainer extensions
*/
fun hasActor(ID: Int): Boolean {
for (actor in actorContainer) {
if (actor.referenceID == ID) return true
}
return false
}
fun hasActor(ID: Int): Boolean =
if (actorContainer.size == 0)
false
else
actorContainer.binarySearch(ID) >= 0
/**
* Remove actor and sort the list
*/
fun removeActor(ID: Int) {
for (actor in actorContainer) {
if (actor.referenceID == ID)
if (actor.referenceID == ID) {
actorContainer.remove(actor)
actorContainer.sort()
break
}
}
}
/**
* Add actor and sort the list
*/
fun addActor(other: Actor): Boolean {
if (hasActor(other.referenceID)) return false
actorContainer.add(other)
actorContainer.sort()
return true
}
fun getActor(ID: Int): Actor {
for (actor in actorContainer) {
if (actor.referenceID == ID)
return actor
}
throw NullPointerException("Actor with ID $ID does not exist.")
if (actorContainer.size == 0) throw IllegalArgumentException("Actor with ID $ID does not exist.")
val index = actorContainer.binarySearch(ID)
if (index < 0)
throw IllegalArgumentException("Actor with ID $ID does not exist.")
else
return actorContainer[index]
}
fun addUI(other: UIHandler): Boolean {
if (uiContainer.contains(other)) return false
uiContainer.add(other)
return true
private fun ArrayList<Actor>.binarySearch(ID: Int): Int {
var low = 0
var high = actorContainer.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
if (ID > midVal.referenceID)
low = mid + 1
else if (ID < midVal.referenceID)
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
}

View File

@@ -3,7 +3,7 @@ package net.torvald.terrarum
import net.torvald.imagefont.GameFontWhite
import net.torvald.JsonFetcher
import net.torvald.JsonWriter
import net.torvald.imagefont.SmallNumbers
import net.torvald.imagefont.TinyAlphNum
import org.lwjgl.input.Controllers
import org.lwjgl.opengl.GL11
import org.newdawn.slick.*
@@ -47,7 +47,7 @@ constructor(gamename: String) : StateBasedGame(gamename) {
@Throws(SlickException::class)
override fun initStatesList(gc: GameContainer) {
gameFont = GameFontWhite()
smallNumbers = SmallNumbers()
smallNumbers = TinyAlphNum()
hasController = gc.input.controllerCount > 0
if (hasController) {

View File

@@ -9,6 +9,8 @@ import org.apache.commons.codec.digest.DigestUtils
*/
class Authenticator : ConsoleCommand {
private var a = false
override fun execute(args: Array<String>) {
if (args.size == 2) {
val pwd = args[1]
@@ -38,9 +40,4 @@ class Authenticator : ConsoleCommand {
override fun printUsage() {
CommandInterpreter.echoUnknownCmd("auth")
}
companion object {
private var a = false
}
}

View File

@@ -48,9 +48,9 @@ object CommandInterpreter {
}
}
catch (e: Exception) {
println("[CommandInterpreter] :")
System.err.print("[CommandInterpreter] ")
e.printStackTrace()
Echo().execute(Lang.get("ERROR_GENERIC_TEXT"))
Echo().error(Lang["ERROR_GENERIC_TEXT"])
}
}
@@ -92,8 +92,8 @@ object CommandInterpreter {
val sb = StringBuilder()
val formatter = Formatter(sb)
Echo().execute(
formatter.format(Lang.get("DEV_MESSAGE_CONSOLE_COMMAND_UNKNOWN"), cmdname).toString())
Echo().error(
formatter.format(Lang["DEV_MESSAGE_CONSOLE_COMMAND_UNKNOWN"], cmdname).toString())
}
private class CommandInput(o: Array<Any>) {

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.console
import net.torvald.imagefont.GameFontBase
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.ui.ConsoleWindow
@@ -20,6 +21,10 @@ internal class Echo : ConsoleCommand {
(Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage(single_line)
}
fun error(single_line: String) {
(Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage("${GameFontBase.colToCode["r"]}$single_line")
}
override fun printUsage() {
}

View File

@@ -17,37 +17,73 @@ class GetAV : ConsoleCommand {
val av = Terrarum.game.player.actorValue
val keyset = av.keySet
echo.execute("== ActorValue list for player ==")
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
+ ")")
// check if args[1] is number or not
if (!args[1].isNum()) { // args[1] is ActorValue name
echo.execute("player.${args[1]} = "
+ Terrarum.game.player.actorValue[args[1]]
+ " ("
+ Terrarum.game.player.actorValue[args[1]]!!.javaClass.simpleName
+ ")"
)
}
else { // args[1] is actor ID
val av = Terrarum.game.getActor(args[1].toInt()).actorValue
val keyset = av.keySet
echo.execute("== ActorValue list for ${args[1].toInt()} ==")
if (keyset.size == 0)
echo.execute("(nothing)")
else
keyset.forEach { elem -> echo.execute("$elem = ${av[elem as String]}") }
}
}
else if (args.size == 3) {
val id = args[1].toInt()
val av = args[2]
echo.execute("$id.$av = " +
Terrarum.game.getActor(id).actorValue[av] +
" (" +
Terrarum.game.getActor(id).actorValue[av]!!.javaClass.simpleName +
")"
)
}
}
catch (e: NullPointerException) {
if (args.size == 2) {
echo.execute(args[1] + ": actor value does not exist.")
echo.error(args[1] + ": actor value does not exist.")
}
else if (args.size == 3) {
echo.execute(args[2] + ": actor value does not exist.")
echo.error(args[2] + ": actor value does not exist.")
}
else {
throw NullPointerException()
}
}
catch (e1: IllegalArgumentException) {
if (args.size == 3) {
echo.error(args[1] + ": no actor with this ID.")
}
}
}
fun String.isNum(): Boolean {
try {
this.toInt()
return true
}
catch (e: NumberFormatException) {
return false
}
}
override fun printUsage() {
val echo = Echo()
echo.execute("Get desired actor value of specific target.")

View File

@@ -19,43 +19,81 @@ internal class SetAV : ConsoleCommand {
}
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) {
fun parseAVInput(arg: String): Any {
val `val`: Any
try {
`val` = Integer(args[2]) // try for integer
`val` = Integer(arg) // try for integer
}
catch (e: NumberFormatException) {
try {
`val` = args[2].toFloat() // try for float
`val` = arg.toFloat() // try for float
}
catch (ee: NumberFormatException) {
if (args[2].equals("__true", ignoreCase = true)) {
if (arg.equals("__true", ignoreCase = true)) {
`val` = true
}
else if (args[2].equals("__false", ignoreCase = true)) {
else if (arg.equals("__false", ignoreCase = true)) {
`val` = false
}
else {
`val` = args[2] // string if not number
`val` = arg // string if not number
}
}
}
return `val`
}
val echo = Echo()
// setav <id, or blank for player> <av> <val>
if (args.size != 4 && args.size != 3) {
printUsage()
}
else if (args.size == 3) {
val `val` = parseAVInput(args[2])
// check if av is number
if (args[1].isNum()) {
echo.error("Illegal ActorValue “${args[1]}”: ActorValue cannot be a number.")
return
}
Terrarum.game.player.actorValue[args[1]] = `val`
echo.execute("Set " + args[1] + " to " + `val`)
echo.execute("Set ${args[1]} to $`val`")
}
else if (args.size == 4) {
try {
val id = args[1].toInt()
val `val` = parseAVInput(args[3])
// check if av is number
if (args[2].isNum()) {
echo.error("Illegal ActorValue “${args[2]}”: ActorValue cannot be a number.")
return
}
Terrarum.game.getActor(id).actorValue[args[2]] = `val`
echo.execute("Set ${args[2]} of $id to $`val`")
}
catch (e: IllegalArgumentException) {
if (args.size == 4)
echo.error(args[1] + ": no actor with this ID.")
}
}
}
fun String.isNum(): Boolean {
try {
this.toInt()
return true
}
catch (e: NumberFormatException) {
return false
}
}
}

View File

@@ -5,15 +5,20 @@ import org.newdawn.slick.GameContainer
/**
* Created by minjaesong on 16-03-14.
*/
interface Actor {
abstract class Actor : Comparable<Actor> {
fun update(gc: GameContainer, delta_t: Int)
abstract fun update(gc: GameContainer, delta_t: Int)
/**
* Valid RefID is equal to or greater than 32768.
* @return Reference ID. (32768-0xFFFF_FFFF)
*/
var referenceID: Int
abstract var referenceID: Int
var actorValue: ActorValue
abstract var actorValue: ActorValue
override fun equals(other: Any?) = referenceID == (other as Actor).referenceID
override fun hashCode() = referenceID
override fun toString() = "ActorID: ${hashCode()}"
override fun compareTo(other: Actor): Int = this.referenceID - other.referenceID
}

View File

@@ -16,7 +16,7 @@ import org.newdawn.slick.Graphics
*
* Created by minjaesong on 16-03-14.
*/
open class ActorWithBody constructor() : Actor, Visible, Glowing {
open class ActorWithBody constructor() : Actor(), Visible {
override var actorValue: ActorValue = ActorValue()
@@ -26,15 +26,13 @@ open class ActorWithBody constructor() : Actor, Visible, Glowing {
var baseHitboxH: Int = 0
/**
* Velocity for newtonian sim.
* Fluctuation in, otherwise still, velocity is equal to acceleration.
* Velocity vector (broken down by axes) for newtonian sim.
* 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()
var veloX: Float = 0.toFloat()
var veloY: Float = 0.toFloat()
@Transient private val VELO_HARD_LIMIT = 10000f
var grounded = false
@@ -61,16 +59,17 @@ open class ActorWithBody constructor() : Actor, Visible, Glowing {
@Transient val nextHitbox = Hitbox(0f,0f,0f,0f)
/**
* Physical properties
* Physical properties.
* Values derived from ActorValue must be @Transient.
*/
@Volatile @Transient var scale = 1f
@Volatile @Transient var mass = 2f
@Transient var scale = 1f
@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].")
throw IllegalArgumentException("[ActorWithBody] Invalid elasticity value: $value; valid elasticity value is [0, 1].")
else if (value > 1) {
println("[ActorWithBody] Elasticity were capped to 1.")
field = ELASTICITY_MAX
@@ -89,11 +88,11 @@ open class ActorWithBody constructor() : Actor, Visible, Glowing {
*/
@Transient private val METER = 24f
/**
* [m / s^2] * SI_TO_GAME_ACC -> [px / IFrame^2]
* [m / s^2] * SI_TO_GAME_ACC -> [px / InternalFrame^2]
*/
@Transient private val SI_TO_GAME_ACC = METER / FastMath.sqr(Terrarum.TARGET_FPS.toFloat())
/**
* [m / s] * SI_TO_GAME_VEL -> [px / IFrame]
* [m / s] * SI_TO_GAME_VEL -> [px / InternalFrame]
*/
@Transient private val SI_TO_GAME_VEL = METER / Terrarum.TARGET_FPS
@@ -131,6 +130,8 @@ open class ActorWithBody constructor() : Actor, Visible, Glowing {
@Transient private val MASS_DEFAULT = 60f
internal val physSleep: Boolean
get() = veloX.abs() < 0.5 && veloY.abs() < 0.5
private var posAdjustX = 0
private var posAdjustY = 0

View File

@@ -0,0 +1,104 @@
package net.torvald.terrarum.gameactors
import com.jme3.math.FastMath
import net.torvald.terrarum.Terrarum
import java.util.*
/**
* Created by minjaesong on 16-04-22.
*/
object CollisionSolver {
private const val STARTPOINT = 1
private const val ENDPOINT = 2
private const val COLL_LIST_SIZE = 256
private const val COLL_CANDIDATES_SIZE = 128
private const val COLL_FINAL_CANDIDATES_SIZE = 16
private val collListX = ArrayList<CollisionMarkings>(COLL_LIST_SIZE)
private val collListY = ArrayList<CollisionMarkings>(COLL_LIST_SIZE)
private val collCandidateX = ArrayList<Pair<ActorWithBody, ActorWithBody>>(COLL_CANDIDATES_SIZE)
private val collCandidateY = ArrayList<Pair<ActorWithBody, ActorWithBody>>(COLL_CANDIDATES_SIZE)
private val collCandidates = ArrayList<Pair<ActorWithBody, ActorWithBody>>(COLL_FINAL_CANDIDATES_SIZE)
/**
* @link https://www.toptal.com/game/video-game-physics-part-ii-collision-detection-for-solid-objects
*/
fun process() {
// mark list x
Terrarum.game.actorContainer.forEach { it ->
if (it is ActorWithBody) {
collListX.add(CollisionMarkings(it.hitbox.hitboxStart.x, STARTPOINT, it.referenceID))
collListX.add(CollisionMarkings(it.hitbox.hitboxEnd.x, ENDPOINT, it.referenceID))
}
}
// sort list x (will use Timsort with Java SE >= 8, Mergesort otherwise)
collListX.sortBy { it.pos }
// set candidateX
// mark list y
Terrarum.game.actorContainer.forEach { it ->
if (it is ActorWithBody) {
collListY.add(CollisionMarkings(it.hitbox.hitboxStart.y, STARTPOINT, it.referenceID))
collListY.add(CollisionMarkings(it.hitbox.hitboxEnd.y, ENDPOINT, it.referenceID))
}
}
// sort list y
collListY.sortBy { it.pos }
// set candidateY
// look for overlaps in candidate X/Y and put them into collCandidates
// solve collision for actors in collCandidates
}
private fun solveCollision(a: ActorWithBody, b: ActorWithBody) {
}
private infix fun ActorWithBody.isCollidingWith(other: ActorWithBody): Boolean {
val ax = this.hitbox.centeredX
val ay = this.hitbox.centeredY
val bx = other.hitbox.centeredX
val by = other.hitbox.centeredY
// will refer 'actor_dist_t' as 't' afterward
val actor_dist_t_sqr = ((ay - by).sqr() + (ax - bx).sqr()) // no sqrt; 'power' is slower than 'times'
val dist_x = (ax - bx).abs() // 'tx'
val dist_y = (ay - by).abs() // 'ty'
val tangent = dist_y / dist_x
var t_ax: Float; var t_ay: Float
if (dist_x > dist_y) {
t_ax = this.hitbox.width / 2
t_ay = t_ax * tangent
}
else {
t_ay = this.hitbox.height / 2
t_ax = t_ay * tangent
}
return (t_ax.sqr() + t_ay.sqr()) < actor_dist_t_sqr
}
fun Float.abs() = if (this < 0) -this else this
fun Float.sqr() = this * this
data class CollisionMarkings(
val pos: Float,
val kind: Int,
val actorID: Int
)
/**
* === Some useful physics knowledge ===
*
* * Momentum = mass × Velocity
*/
}

View File

@@ -1,13 +0,0 @@
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)
}

View File

@@ -15,7 +15,7 @@ class PhysTestBall : ActorWithBody {
constructor(): super() {
setHitboxDimension(16, 16, 0, 0)
isVisible = true
mass = 10f
actorValue[AVKey.BASEMASS] = 10f
color = RoguelikeRandomiser.composeColourFrom(RoguelikeRandomiser.POTION_PRIMARY_COLSET)
}
@@ -23,9 +23,9 @@ class PhysTestBall : ActorWithBody {
override fun drawBody(gc: GameContainer, g: Graphics) {
g.color = color
g.fillOval(
hitbox!!.posX,
hitbox!!.posY,
hitbox!!.width,
hitbox!!.height)
hitbox.posX,
hitbox.posY,
hitbox.width,
hitbox.height)
}
}

View File

@@ -12,4 +12,8 @@ interface Visible {
fun drawBody(gc: GameContainer, g: Graphics)
fun updateBodySprite(gc: GameContainer, delta: Int)
fun drawGlow(gc: GameContainer, g: Graphics)
fun updateGlowSprite(gc: GameContainer, delta: Int)
}

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.ui
import com.jme3.math.FastMath
import net.torvald.imagefont.GameFontBase
import net.torvald.terrarum.gamemap.PairedMapLayer
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.mapdrawer.LightmapRenderer
@@ -18,7 +19,7 @@ import java.util.*
/**
* Created by minjaesong on 16-03-14.
*/
class BasicDebugInfoWindow : UICanvas {
class BasicDebugInfoWindow:UICanvas {
override var width: Int = Terrarum.WIDTH
override var height: Int = Terrarum.HEIGHT
@@ -59,30 +60,35 @@ class BasicDebugInfoWindow : UICanvas {
val mouseTileX = ((MapCamera.cameraX + gc.input.mouseX / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
val mouseTileY = ((MapCamera.cameraY + gc.input.mouseY / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
g.color = Color.white
g.font = Terrarum.smallNumbers
g.color = GameFontBase.codeToCol["y"]
val hitbox = player.hitbox
val nextHitbox = player.nextHitbox
printLine(g, 1, "posX: "
val ccG = "${GameFontBase.colToCode["g"]}"
printLine(g, 1, "posX "
+ ccG
+ "${hitbox.pointedX.toString()}"
+ " ("
+ "${(hitbox.pointedX / MapDrawer.TILE_SIZE).toInt().toString()}"
+ ")")
printLine(g, 2, "posY: "
printLine(g, 2, "posY "
+ ccG
+ hitbox.pointedY.toString()
+ " ("
+ (hitbox.pointedY / MapDrawer.TILE_SIZE).toInt().toString()
+ ")")
printLine(g, 3, "veloX reported: ${player.veloX}")
printLine(g, 4, "veloY reported: ${player.veloY}")
printLine(g, 3, "veloX reported $ccG${player.veloX}")
printLine(g, 4, "veloY reported $ccG${player.veloY}")
printLineColumn(g, 2, 3, "veloX measured: ${xdelta}")
printLineColumn(g, 2, 4, "veloY measured: ${ydelta}")
printLineColumn(g, 2, 3, "veloX measured $ccG${xdelta}")
printLineColumn(g, 2, 4, "veloY measured $ccG${ydelta}")
printLine(g, 5, "grounded : ${player.grounded}")
printLine(g, 6, "noClip : ${player.noClip}")
printLine(g, 5, "grounded $ccG${player.grounded}")
printLine(g, 6, "noClip $ccG${player.noClip}")
val lightVal: String
var mtX = mouseTileX.toString()
@@ -100,7 +106,7 @@ class BasicDebugInfoWindow : UICanvas {
rawB.toString() + ")"
printLine(g, 7, "light at cursor : " + lightVal)
printLine(g, 7, "light@cursor $ccG$lightVal")
val tileNo: String
val tileNumRaw = Terrarum.game.map.getTileFromTerrain(mouseTileX, mouseTileY) ?: -1
@@ -108,33 +114,22 @@ class BasicDebugInfoWindow : UICanvas {
val tiledmg = tileNumRaw % PairedMapLayer.RANGE
tileNo = if (tileNumRaw == -1) "" else "$tilenum:$tiledmg"
printLine(g, 8, "tile at cursor : $tileNo ($mtX, $mtY)")
printLine(g, 8, "tile@cursor $ccG$tileNo ($mtX, $mtY)")
/**
* Second column
*/
printLineColumn(g, 2, 1, "${Lang["MENU_OPTIONS_VSYNC"]} : " + Terrarum.appgc.isVSyncRequested)
printLineColumn(g, 2, 2, "Env colour temp : " + MapDrawer.getColTemp())
printLineColumn(g, 2, 5, "Time : ${Terrarum.game.map.worldTime.elapsedSeconds()}" +
printLineColumn(g, 2, 1, "VSync $ccG" + Terrarum.appgc.isVSyncRequested)
printLineColumn(g, 2, 2, "Env colour temp $ccG" + MapDrawer.getColTemp())
printLineColumn(g, 2, 5, "Time $ccG${Terrarum.game.map.worldTime.elapsedSeconds()}" +
" (${Terrarum.game.map.worldTime.getFormattedTime()})")
printLineColumn(g, 2, 6, "Mass : ${player.mass}")
printLineColumn(g, 2, 6, "Mass $ccG${player.mass}")
/**
* On screen
*/
// Memory allocation
val memInUse = Terrarum.game.memInUse
val totalVMMem = Terrarum.game.totalVMMem
g.color = Color(0xFF7F00)
g.drawString(
Lang["DEV_MEMORY_SHORT_CAP"]
+ " : "
+ formatter.format(
Lang["DEV_MEMORY_A_OF_B"], memInUse, totalVMMem), (Terrarum.WIDTH - 200).toFloat(), line(1).toFloat())
// Hitbox
val zoom = Terrarum.game.screenZoom
g.color = Color(0x007f00)
@@ -148,12 +143,12 @@ class BasicDebugInfoWindow : UICanvas {
, (hitbox.pointedY - 1) * zoom - MapCamera.cameraY * zoom
, 3f, 3f)
g.drawString(
Lang["DEV_COLOUR_LEGEND_GREEN"] + " : hitbox", (Terrarum.WIDTH - 200).toFloat()
, line(2).toFloat())
"hitbox", (Terrarum.WIDTH - 15 * 8 - 2).toFloat().toFloat()
, line(1))
// Next hitbox
g.color = Color.blue
g.drawRect(nextHitbox!!.hitboxStart.x * zoom - MapCamera.cameraX * zoom
g.drawRect(nextHitbox.hitboxStart.x * zoom - MapCamera.cameraX * zoom
, nextHitbox.hitboxStart.y * zoom - MapCamera.cameraY * zoom
, nextHitbox.width * zoom
, nextHitbox.height * zoom)
@@ -163,21 +158,41 @@ class BasicDebugInfoWindow : UICanvas {
, (nextHitbox.pointedY - 1) * zoom - MapCamera.cameraY * zoom
, 3f, 3f)
g.drawString(
Lang["DEV_COLOUR_LEGEND_BLUE"] + " : nextHitbox", (Terrarum.WIDTH - 200).toFloat()
, line(3).toFloat())
"nextHitbox", (Terrarum.WIDTH - 15 * 8 - 2).toFloat().toFloat()
, line(2))
drawHistogram(g, LightmapRenderer.histogram,
Terrarum.WIDTH - histogramW - 30,
Terrarum.HEIGHT - histogramH - 30
)
g.color = GameFontBase.codeToCol["y"]
g.drawString("MEM ", (Terrarum.WIDTH - 15 * 8 - 2).toFloat(), 2f)
//g.drawString("FPS ", (Terrarum.WIDTH - 6 * 8 - 2).toFloat(), 10f)
g.drawString("Actors total ", 2f, Terrarum.HEIGHT - 10f)
g.drawString("Active ", (2 + 17*8).toFloat(), Terrarum.HEIGHT - 10f)
g.color = GameFontBase.codeToCol["g"]
g.drawString("${Terrarum.game.memInUse}M",
(Terrarum.WIDTH - 11 * 8 - 2).toFloat(), 2f)
g.drawString("/${Terrarum.game.totalVMMem}M",
(Terrarum.WIDTH - 6 * 8 - 2).toFloat(), 2f)
//g.drawString("${Terrarum.appgc.fps}",
// (Terrarum.WIDTH - 2 * 8 - 2).toFloat(), 10f)
g.drawString("${Terrarum.game.actorContainer.size + Terrarum.game.actorcontainerInactive.size}",
(2 + 13*8).toFloat(), Terrarum.HEIGHT - 10f
)
g.drawString(Terrarum.game.actorContainer.size.toString(),
(2 + 24*8).toFloat(), Terrarum.HEIGHT - 10f
)
}
private fun printLine(g: Graphics, l: Int, s: String) {
g.drawString(s, 20f, line(l).toFloat())
g.drawString(s, 10f, line(l).toFloat())
}
private fun printLineColumn(g: Graphics, col: Int, row: Int, s: String) {
g.drawString(s, (20 + column(col)).toFloat(), line(row).toFloat())
g.drawString(s, (10 + column(col)).toFloat(), line(row).toFloat())
}
val histogramW = 256
@@ -196,6 +211,10 @@ class BasicDebugInfoWindow : UICanvas {
g.color = uiColour
g.fillRect(x.toFloat(), y.toFloat(), w.plus(1).toFloat(), h.toFloat())
g.color = Color.gray
g.drawString("0", x.toFloat(), y.toFloat() + h + 2)
g.drawString("255", x.toFloat() + w + 1 - 8*3, y.toFloat() + h + 2)
g.drawString("Histogramme", x + w / 2 - 5.5f * 8, y.toFloat() + h + 2)
setBlendScreen()
for (c in 0..2) {
@@ -219,9 +238,9 @@ class BasicDebugInfoWindow : UICanvas {
setBlendNormal()
}
private fun line(i: Int): Int = i * 20
private fun line(i: Int): Float = i * 10f
private fun column(i: Int): Int = 250 * (i - 1)
private fun column(i: Int): Float = 250f * (i - 1)
override fun doOpening(gc: GameContainer, delta: Int) {

View File

@@ -33,7 +33,10 @@ constructor(val UI: UICanvas) {
private var opening = false
private var closing = false
private var opened = false // fully opened
private var visible = false
private var _visible = false
val visible: Boolean
get() = if (alwaysVisible) true
else _visible
var openCloseCounter = 0
@@ -48,12 +51,12 @@ constructor(val UI: UICanvas) {
fun update(gc: GameContainer, delta: Int) {
if (visible || alwaysVisible) {
if (_visible || alwaysVisible) {
UI.update(gc, delta)
}
if (opening) {
visible = true
_visible = true
openCloseCounter += delta
// println("UI ${UI.javaClass.simpleName} (open)")
@@ -84,14 +87,14 @@ constructor(val UI: UICanvas) {
UI.endClosing(gc, delta)
closing = false
opened = false
visible = false
_visible = false
openCloseCounter = 0
}
}
}
fun render(gc: GameContainer, gameGraphicInstance: Graphics) {
if (visible || alwaysVisible) {
if (_visible || alwaysVisible) {
UIGraphicInstance.clear()
UIGraphicInstance.font = Terrarum.gameFont
@@ -113,22 +116,12 @@ constructor(val UI: UICanvas) {
if (alwaysVisible) {
throw RuntimeException("[UIHandler] Tried to 'set visibility of' constant UI")
}
visible = b
_visible = b
}
val isVisible: Boolean
get() {
if (alwaysVisible) {
return true
}
else {
return visible
}
}
fun setAsAlwaysVisible() {
alwaysVisible = true
visible = true
_visible = true
opened = true
opening = false
closing = false
@@ -155,7 +148,7 @@ constructor(val UI: UICanvas) {
if (alwaysVisible) {
throw RuntimeException("[UIHandler] Tried to 'toggle opening of' constant UI")
}
if (visible) {
if (_visible) {
if (!closing) {
setAsClosing()
}
@@ -168,61 +161,61 @@ constructor(val UI: UICanvas) {
}
fun processInput(input: Input) {
if (visible) {
if (_visible) {
UI.processInput(input)
}
}
fun keyPressed(key: Int, c: Char) {
if (visible && UI is UITypable) {
if (_visible && UI is UITypable) {
UI.keyPressed(key, c)
}
}
fun keyReleased(key: Int, c: Char) {
if (visible && UI is UITypable) {
if (_visible && UI is UITypable) {
UI.keyReleased(key, c)
}
}
fun mouseMoved(oldx: Int, oldy: Int, newx: Int, newy: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.mouseMoved(oldx, oldy, newx, newy)
}
}
fun mouseDragged(oldx: Int, oldy: Int, newx: Int, newy: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.mouseDragged(oldx, oldy, newx, newy)
}
}
fun mousePressed(button: Int, x: Int, y: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.mousePressed(button, x, y)
}
}
fun mouseReleased(button: Int, x: Int, y: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.mouseReleased(button, x, y)
}
}
fun mouseWheelMoved(change: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.mouseWheelMoved(change)
}
}
fun controllerButtonPressed(controller: Int, button: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.controllerButtonPressed(controller, button)
}
}
fun controllerButtonReleased(controller: Int, button: Int) {
if (visible && UI is UIClickable) {
if (_visible && UI is UIClickable) {
UI.controllerButtonReleased(controller, button)
}
}
@@ -233,6 +226,6 @@ constructor(val UI: UICanvas) {
if (alwaysVisible) {
return false
}
return visible && !opening
return _visible && !opening
}
}