fixing the critical bug in active/dormant thingies, actor ID is now positive integer (I had to), class Actor is now shipped with ID generator, optimisation in add/removeActor

Former-commit-id: f743ecb27ba1cea05215889d7e1a77e10171cb8c
Former-commit-id: 4b88f9711c34542a8a504682cffe79a2f8a43ed8
This commit is contained in:
Song Minjae
2016-04-25 01:58:17 +09:00
parent c4b64140be
commit 1dc3e6df3e
10 changed files with 145 additions and 98 deletions

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum package net.torvald.terrarum
import net.torvald.imagefont.GameFontBase
import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.console.Authenticator import net.torvald.terrarum.console.Authenticator
import net.torvald.terrarum.gamecontroller.GameController import net.torvald.terrarum.gamecontroller.GameController
@@ -146,31 +147,11 @@ constructor() : BasicGameState() {
MapCamera.update(gc, delta) MapCamera.update(gc, delta)
// determine whether the inactive actor should be re-active // determine whether the inactive actor should be re-active
actorContainerInactive.forEach { actor -> wakeDormantActors()
if (actor is Visible && distToActorSqr(actor, player) <= ACTOR_UPDATE_RANGE.sqr()) {
addActor(actor)
}
}
actorContainer.forEach { if (actorContainerInactive.contains(it))
actorContainerInactive.remove(it)
}
actorContainer.forEach { actor -> // update actors // determine whether the actor should be active or dormant
// determine whether the actor should be active by their distance from the player // also updates active actors
// will put inactive actors to list specifically for them inactivateDistantActors(gc, delta)
if (actor is Visible && distToActorSqr(actor, player) > ACTOR_UPDATE_RANGE.sqr()) {
actorContainerInactive.add(actor)
}
else {
// update our remaining active actors
actor.update(gc, delta)
if (actor is Visible) {
actor.updateBodySprite(gc, delta)
actor.updateGlowSprite(gc, delta)
}
}
}
actorContainerInactive.forEach { removeActor(it.referenceID) }
uiContainer.forEach { ui -> ui.update(gc, delta) } uiContainer.forEach { ui -> ui.update(gc, delta) }
consoleHandler.update(gc, delta) consoleHandler.update(gc, delta)
@@ -184,12 +165,9 @@ constructor() : BasicGameState() {
private fun setAppTitle() { private fun setAppTitle() {
Terrarum.appgc.setTitle( Terrarum.appgc.setTitle(
"Simple Slick Game — FPS: " "Simple Slick Game" +
+ Terrarum.appgc.fps + " (" " — FPS: ${Terrarum.appgc.fps} (${Terrarum.TARGET_INTERNAL_FPS})" +
+ Terrarum.TARGET_INTERNAL_FPS.toString() "${memInUse}M / ${totalVMMem}M")
+ ") — "
+ memInUse.toString() + "M / "
+ totalVMMem.toString() + "M")
} }
override fun render(gc: GameContainer, sbg: StateBasedGame, g: Graphics) { override fun render(gc: GameContainer, sbg: StateBasedGame, g: Graphics) {
@@ -210,10 +188,7 @@ constructor() : BasicGameState() {
// draw actors // draw actors
actorContainer.forEach { actor -> actorContainer.forEach { actor ->
if (actor is Visible && if (actor is Visible && actor.inScreen()) { // if visible and within screen
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) actor.drawBody(gc, g)
} }
} }
@@ -235,10 +210,7 @@ constructor() : BasicGameState() {
// draw actor glows // draw actor glows
actorContainer.forEach { actor -> actorContainer.forEach { actor ->
if (actor is Visible && if (actor is Visible && actor.inScreen()) { // if visible and within screen
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) actor.drawGlow(gc, g)
} }
} }
@@ -255,6 +227,15 @@ constructor() : BasicGameState() {
actor.hitbox.posX, actor.hitbox.posX,
actor.hitbox.pointedY + 4 actor.hitbox.pointedY + 4
) )
if (DEBUG_ARRAY) {
g.color = GameFontBase.codeToCol["g"]
g.drawString(
i.toString(),
actor.hitbox.posX,
actor.hitbox.pointedY + 4 + 10
)
}
} }
} }
} }
@@ -329,6 +310,45 @@ constructor() : BasicGameState() {
notifinator.setAsOpening() notifinator.setAsOpening()
} }
fun wakeDormantActors() {
// determine whether the inactive actor should be re-active
var actorContainerSize = actorContainerInactive.size
var i = 0
while (i < actorContainerSize) { // loop thru actorContainerInactive
val actor = actorContainerInactive[i]
val actorIndex = i
if (actor is Visible && actor.inUpdateRange()) {
addActor(actor)
actorContainerInactive.removeAt(actorIndex)
actorContainerSize -= 1
i-- // array removed 1 elem, so also decrement counter by 1
}
i++
}
}
fun inactivateDistantActors(gc: GameContainer, delta: Int) {
var actorContainerSize = actorContainer.size
var i = 0
// determine whether the actor should be active or dormant by its distance from the player
// will put dormant actors to list specifically for them
// if the actor is not to be dormant, update it
while (i < actorContainerSize) { // loop thru actorContainer
val actor = actorContainer[i]
val actorIndex = i
if (actor is Visible && !actor.inUpdateRange()) {
actorContainerInactive.add(actor)
actorContainer.removeAt(actorIndex)
actorContainerSize -= 1
i-- // array removed 1 elem, so also decrement counter by 1
}
else {
actorContainer[i].update(gc, delta)
}
i++
}
}
private val globalLightByTime: Int private val globalLightByTime: Int
get() = getGradientColour(2).getRGB24().rgb24ExpandToRgb30() get() = getGradientColour(2).getRGB24().rgb24ExpandToRgb30()
@@ -342,8 +362,11 @@ constructor() : BasicGameState() {
fun Float.sqr() = this * this fun Float.sqr() = this * this
fun Int.sqr() = this * this fun Int.sqr() = this * this
private fun distToActorSqr(a: Visible, p: Player) = private fun distToActorSqr(a: Visible, p: Player): Float =
(a.hitbox.centeredX - p.hitbox.centeredX).sqr() + (a.hitbox.centeredY - p.hitbox.centeredY).sqr() (a.hitbox.centeredX - p.hitbox.centeredX).sqr() + (a.hitbox.centeredY - p.hitbox.centeredY).sqr()
private fun Visible.inScreen() = distToActorSqr(this, player) <= (Terrarum.WIDTH.plus(this.hitbox.width.div(2)).times(1 / Terrarum.game.screenZoom).sqr() +
Terrarum.HEIGHT.plus(this.hitbox.height.div(2)).times(1 / Terrarum.game.screenZoom).sqr())
private fun Visible.inUpdateRange() = distToActorSqr(this, player) <= ACTOR_UPDATE_RANGE.sqr()
/** /**
* actorContainer extensions * actorContainer extensions
*/ */
@@ -353,30 +376,33 @@ constructor() : BasicGameState() {
else else
actorContainer.binarySearch(ID) >= 0 actorContainer.binarySearch(ID) >= 0
/** fun removeActor(actor: Actor) = removeActor(actor.referenceID)
* Remove actor and sort the list
*/
fun removeActor(ID: Int) { fun removeActor(ID: Int) {
for (actor in actorContainer) { if (ID == player.referenceID) throw IllegalArgumentException("Attemped to remove player.")
if (actor.referenceID == ID) { // get index of the actor and delete by the index.
actorContainer.remove(actor) // we can do this as the list is guaranteed to be sorted
actorContainer.sort() // and only contains unique values
break val indexToDelete = actorContainer.binarySearch(ID)
} if (indexToDelete >= 0) actorContainer.removeAt(indexToDelete)
}
} }
/** /**
* Add actor and sort the list * Add actor and sort the list
*/ */
fun addActor(other: Actor): Boolean { fun addActor(actor: Actor): Boolean {
if (hasActor(other.referenceID)) return false if (hasActor(actor.referenceID)) throw IllegalArgumentException("Actor with ID ${actor.referenceID} already exists.")
actorContainer.add(other) actorContainer.add(actor)
actorContainer.sort() insertionSortLastElem(actorContainer) // we can do this as we only add one actor
return true return true
} }
fun getActor(ID: Int): Actor { /**
* Whether the game should display actorContainer elem number when F3 is on
*/
val DEBUG_ARRAY = false
fun getActorByID(ID: Int): Actor {
if (actorContainer.size == 0) throw IllegalArgumentException("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) val index = actorContainer.binarySearch(ID)
@@ -386,7 +412,25 @@ constructor() : BasicGameState() {
return actorContainer[index] return actorContainer[index]
} }
private fun insertionSortLastElem(arr: ArrayList<Actor>) {
// 'insertion sort' last element
var x: Actor
var j: Int
var index: Int = arr.size - 1
x = arr[index]
j = index - 1
while (j > 0 && arr[j].referenceID > x.referenceID) {
arr[j + 1] = arr[j]
j -= 1
}
arr[j + 1] = x
}
private fun ArrayList<Actor>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID)
private fun ArrayList<Actor>.binarySearch(ID: Int): Int { private fun ArrayList<Actor>.binarySearch(ID: Int): Int {
// code from collections/Collections.kt
var low = 0 var low = 0
var high = actorContainer.size - 1 var high = actorContainer.size - 1

View File

@@ -114,9 +114,22 @@ constructor(gamename: String) : StateBasedGame(gamename) {
private lateinit var configDir: String private lateinit var configDir: String
/**
* 0xAA_BB_XXXX
* AA: Major version
* BB: Minor version
* XXXX: Revision
*
* e.g. 0x02010034 can be translated as 2.1.52
*/
const val VERSION_RAW = 0x00020041
const val VERSION_STRING: String =
"${VERSION_RAW.ushr(24)}.${VERSION_RAW.and(0xFF0000).ushr(16)}.${VERSION_RAW.and(0xFFFF)}"
const val NAME = "Terrarum"
fun main(args: Array<String>) { fun main(args: Array<String>) {
try { try {
appgc = AppGameContainer(Terrarum("Terrarum")) appgc = AppGameContainer(Terrarum(NAME))
appgc.setDisplayMode(WIDTH, HEIGHT, false) appgc.setDisplayMode(WIDTH, HEIGHT, false)
appgc.setTargetFrameRate(TARGET_INTERNAL_FPS) appgc.setTargetFrameRate(TARGET_INTERNAL_FPS)
@@ -355,18 +368,6 @@ constructor(gamename: String) : StateBasedGame(gamename) {
} }
} }
/**
* 0xAA_BB_XXXX
* AA: Major version
* BB: Minor version
* XXXX: Revision
*
* e.g. 0x02010034 can be translated as 2.1.52
*/
const val VERSION_RAW = 0x00024000
const val VERSION_STRING: String =
"${VERSION_RAW.ushr(24)}.${VERSION_RAW.and(0xFF0000).ushr(16)}.${VERSION_RAW.and(0xFFFF)}"
fun main(args: Array<String>) = Terrarum.main(args) fun main(args: Array<String>) = Terrarum.main(args)
fun setBlendMul() { fun setBlendMul() {

View File

@@ -15,7 +15,7 @@ import java.util.regex.Pattern
*/ */
object CommandInterpreter { object CommandInterpreter {
private val commandsNoAuth = arrayOf("auth", "qqq", "zoom", "setlocale", "getlocale", "help") private val commandsNoAuth = arrayOf("auth", "qqq", "zoom", "setlocale", "getlocale", "help", "version")
private val ccW = GameFontBase.colToCode["w"] private val ccW = GameFontBase.colToCode["w"]
private val ccG = GameFontBase.colToCode["g"] private val ccG = GameFontBase.colToCode["g"]

View File

@@ -52,7 +52,7 @@ class GetAV : ConsoleCommand {
} }
else { else {
// args[1] is actor ID // args[1] is actor ID
val actor = Terrarum.game.getActor(args[1].toInt()) val actor = Terrarum.game.getActorByID(args[1].toInt())
val av = actor.actorValue val av = actor.actorValue
val keyset = av.keySet val keyset = av.keySet
@@ -74,14 +74,14 @@ class GetAV : ConsoleCommand {
val id = args[1].toInt() val id = args[1].toInt()
val av = args[2] val av = args[2]
echo.execute("$ccW$id.$ccM$av $ccW= $ccG" + echo.execute("$ccW$id.$ccM$av $ccW= $ccG" +
Terrarum.game.getActor(id).actorValue[av] + Terrarum.game.getActorByID(id).actorValue[av] +
" $ccO" + " $ccO" +
Terrarum.game.getActor(id).actorValue[av]!!.javaClass.simpleName Terrarum.game.getActorByID(id).actorValue[av]!!.javaClass.simpleName
) )
println("id.av = " + println("id.av = " +
Terrarum.game.getActor(id).actorValue[av] + Terrarum.game.getActorByID(id).actorValue[av] +
" " + " " +
Terrarum.game.getActor(id).actorValue[av]!!.javaClass.simpleName Terrarum.game.getActorByID(id).actorValue[av]!!.javaClass.simpleName
) )
} }
} }

View File

@@ -24,7 +24,7 @@ class GetFactioning : ConsoleCommand {
val echo = Echo() val echo = Echo()
fun printOutFactioning(id: Int) { fun printOutFactioning(id: Int) {
val a = Terrarum.game.getActor(id) val a = Terrarum.game.getActorByID(id)
if (a is Factionable) { if (a is Factionable) {
echo.execute("$ccW== Faction assignment for $ccY${if (id == Player.PLAYER_REF_ID) "player" else id.toString()} $ccW==") echo.execute("$ccW== Faction assignment for $ccY${if (id == Player.PLAYER_REF_ID) "player" else id.toString()} $ccW==")
println("[GetFactioning] == Faction assignment for '${if (id == Player.PLAYER_REF_ID) "player" else id.toString()}' ==") println("[GetFactioning] == Faction assignment for '${if (id == Player.PLAYER_REF_ID) "player" else id.toString()}' ==")

View File

@@ -77,7 +77,7 @@ internal class SetAV : ConsoleCommand {
try { try {
val id = args[1].toInt() val id = args[1].toInt()
val `val` = parseAVInput(args[3]) val `val` = parseAVInput(args[3])
val actor = Terrarum.game.getActor(id) val actor = Terrarum.game.getActorByID(id)
// check if av is number // check if av is number
if (args[2].isNum()) { if (args[2].isNum()) {

View File

@@ -1,13 +1,13 @@
package net.torvald.terrarum.console package net.torvald.terrarum.console
import net.torvald.terrarum.VERSION_STRING import net.torvald.terrarum.Terrarum
/** /**
* Created by minjaesong on 16-04-23. * Created by minjaesong on 16-04-23.
*/ */
class Version : ConsoleCommand { class Version : ConsoleCommand {
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
Echo().execute(VERSION_STRING) Echo().execute("${Terrarum.NAME} ${Terrarum.VERSION_STRING}")
} }
override fun printUsage() { override fun printUsage() {

View File

@@ -1,5 +1,7 @@
package net.torvald.terrarum.gameactors package net.torvald.terrarum.gameactors
import net.torvald.random.HQRNG
import net.torvald.terrarum.Terrarum
import org.newdawn.slick.GameContainer import org.newdawn.slick.GameContainer
/** /**
@@ -19,6 +21,23 @@ abstract class Actor : Comparable<Actor> {
override fun equals(other: Any?) = referenceID == (other as Actor).referenceID override fun equals(other: Any?) = referenceID == (other as Actor).referenceID
override fun hashCode() = referenceID override fun hashCode() = referenceID
override fun toString() = "ID: ${hashCode()}" override fun toString() = if (actorValue.getAsString(AVKey.NAME).isNullOrEmpty())
"ID: ${hashCode()}"
else
"ID: ${hashCode()} (${actorValue.getAsString(AVKey.NAME)})"
override fun compareTo(other: Actor): Int = this.referenceID - other.referenceID override fun compareTo(other: Actor): Int = this.referenceID - other.referenceID
/**
* Usage:
*
* override var referenceID: Int = generateUniqueReferenceID()
*/
fun generateUniqueReferenceID(): Int {
fun Int.abs() = if (this < 0) -this else this
var ret: Int
do {
ret = HQRNG().nextInt().abs() // set new ID
} while (Terrarum.game.hasActor(ret)) // check for collision
return ret
}
} }

View File

@@ -51,7 +51,7 @@ open class ActorWithBody constructor() : Actor(), Visible {
internal var baseSpriteWidth: Int = 0 internal var baseSpriteWidth: Int = 0
internal var baseSpriteHeight: Int = 0 internal var baseSpriteHeight: Int = 0
override var referenceID: Int = 0 override var referenceID: Int = generateUniqueReferenceID()
/** /**
* Positions: top-left point * Positions: top-left point
*/ */
@@ -137,10 +137,6 @@ open class ActorWithBody constructor() : Actor(), Visible {
private var posAdjustY = 0 private var posAdjustY = 0
init { init {
do {
referenceID = HQRNG().nextInt() // set new ID
} while (Terrarum.game.hasActor(referenceID)) // check for collision
map = Terrarum.game.map map = Terrarum.game.map
} }
@@ -719,19 +715,13 @@ open class ActorWithBody constructor() : Actor(), Visible {
fun Float.round() = Math.round(this).toFloat() fun Float.round() = Math.round(this).toFloat()
fun Float.roundToInt(): Int = Math.round(this) fun Float.roundToInt(): Int = Math.round(this)
fun Float.abs() = FastMath.abs(this) fun Float.abs() = FastMath.abs(this)
fun Int.abs() = if (this < 0) -this else this
companion object { companion object {
@Transient private val TSIZE = MapDrawer.TILE_SIZE @Transient private val TSIZE = MapDrawer.TILE_SIZE
private var AUTO_CLIMB_RATE = TSIZE / 8 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 { private fun div16TruncateToMapWidth(x: Int): Int {
if (x < 0) if (x < 0)
return 0 return 0
@@ -750,13 +740,6 @@ open class ActorWithBody constructor() : Actor(), Visible {
return y and 0x7FFFFFFF shr 4 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 { private fun clampCeil(x: Float, ceil: Float): Float {
return if (Math.abs(x) > ceil) ceil else x return if (Math.abs(x) > ceil) ceil else x
} }

View File

@@ -33,7 +33,7 @@ object ItemPropCodex {
if (code < ITEM_UNIQUE_MAX) if (code < ITEM_UNIQUE_MAX)
return itemCodex[code] return itemCodex[code]
else { else {
val a = Terrarum.game.getActor(code) val a = Terrarum.game.getActorByID(code)
if (a is CanBeAnItem) return a.itemData if (a is CanBeAnItem) return a.itemData
throw NullPointerException() throw NullPointerException()