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
import net.torvald.imagefont.GameFontBase
import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.console.Authenticator
import net.torvald.terrarum.gamecontroller.GameController
@@ -146,31 +147,11 @@ constructor() : BasicGameState() {
MapCamera.update(gc, delta)
// determine whether the inactive actor should be re-active
actorContainerInactive.forEach { actor ->
if (actor is Visible && distToActorSqr(actor, player) <= ACTOR_UPDATE_RANGE.sqr()) {
addActor(actor)
}
}
actorContainer.forEach { if (actorContainerInactive.contains(it))
actorContainerInactive.remove(it)
}
wakeDormantActors()
actorContainer.forEach { actor -> // update actors
// determine whether the actor should be active by their distance from the player
// will put inactive actors to list specifically for them
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) }
// determine whether the actor should be active or dormant
// also updates active actors
inactivateDistantActors(gc, delta)
uiContainer.forEach { ui -> ui.update(gc, delta) }
consoleHandler.update(gc, delta)
@@ -184,12 +165,9 @@ constructor() : BasicGameState() {
private fun setAppTitle() {
Terrarum.appgc.setTitle(
"Simple Slick Game — FPS: "
+ Terrarum.appgc.fps + " ("
+ Terrarum.TARGET_INTERNAL_FPS.toString()
+ ") — "
+ memInUse.toString() + "M / "
+ totalVMMem.toString() + "M")
"Simple Slick Game" +
" — FPS: ${Terrarum.appgc.fps} (${Terrarum.TARGET_INTERNAL_FPS})" +
"${memInUse}M / ${totalVMMem}M")
}
override fun render(gc: GameContainer, sbg: StateBasedGame, g: Graphics) {
@@ -210,10 +188,7 @@ constructor() : BasicGameState() {
// draw actors
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
if (actor is Visible && actor.inScreen()) { // if visible and within screen
actor.drawBody(gc, g)
}
}
@@ -235,10 +210,7 @@ constructor() : BasicGameState() {
// draw actor glows
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 (actor is Visible && actor.inScreen()) { // if visible and within screen
actor.drawGlow(gc, g)
}
}
@@ -255,6 +227,15 @@ constructor() : BasicGameState() {
actor.hitbox.posX,
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()
}
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
get() = getGradientColour(2).getRGB24().rgb24ExpandToRgb30()
@@ -342,8 +362,11 @@ constructor() : BasicGameState() {
fun Float.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()
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
*/
@@ -353,30 +376,33 @@ constructor() : BasicGameState() {
else
actorContainer.binarySearch(ID) >= 0
/**
* Remove actor and sort the list
*/
fun removeActor(actor: Actor) = removeActor(actor.referenceID)
fun removeActor(ID: Int) {
for (actor in actorContainer) {
if (actor.referenceID == ID) {
actorContainer.remove(actor)
actorContainer.sort()
break
}
}
if (ID == player.referenceID) throw IllegalArgumentException("Attemped to remove player.")
// get index of the actor and delete by the index.
// we can do this as the list is guaranteed to be sorted
// and only contains unique values
val indexToDelete = actorContainer.binarySearch(ID)
if (indexToDelete >= 0) actorContainer.removeAt(indexToDelete)
}
/**
* Add actor and sort the list
*/
fun addActor(other: Actor): Boolean {
if (hasActor(other.referenceID)) return false
actorContainer.add(other)
actorContainer.sort()
fun addActor(actor: Actor): Boolean {
if (hasActor(actor.referenceID)) throw IllegalArgumentException("Actor with ID ${actor.referenceID} already exists.")
actorContainer.add(actor)
insertionSortLastElem(actorContainer) // we can do this as we only add one actor
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.")
val index = actorContainer.binarySearch(ID)
@@ -386,7 +412,25 @@ constructor() : BasicGameState() {
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 {
// code from collections/Collections.kt
var low = 0
var high = actorContainer.size - 1

View File

@@ -114,9 +114,22 @@ constructor(gamename: String) : StateBasedGame(gamename) {
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>) {
try {
appgc = AppGameContainer(Terrarum("Terrarum"))
appgc = AppGameContainer(Terrarum(NAME))
appgc.setDisplayMode(WIDTH, HEIGHT, false)
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 setBlendMul() {

View File

@@ -15,7 +15,7 @@ import java.util.regex.Pattern
*/
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 ccG = GameFontBase.colToCode["g"]

View File

@@ -52,7 +52,7 @@ class GetAV : ConsoleCommand {
}
else {
// 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 keyset = av.keySet
@@ -74,14 +74,14 @@ class GetAV : ConsoleCommand {
val id = args[1].toInt()
val av = args[2]
echo.execute("$ccW$id.$ccM$av $ccW= $ccG" +
Terrarum.game.getActor(id).actorValue[av] +
Terrarum.game.getActorByID(id).actorValue[av] +
" $ccO" +
Terrarum.game.getActor(id).actorValue[av]!!.javaClass.simpleName
Terrarum.game.getActorByID(id).actorValue[av]!!.javaClass.simpleName
)
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()
fun printOutFactioning(id: Int) {
val a = Terrarum.game.getActor(id)
val a = Terrarum.game.getActorByID(id)
if (a is Factionable) {
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()}' ==")

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
package net.torvald.terrarum.gameactors
import net.torvald.random.HQRNG
import net.torvald.terrarum.Terrarum
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 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
/**
* 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 baseSpriteHeight: Int = 0
override var referenceID: Int = 0
override var referenceID: Int = generateUniqueReferenceID()
/**
* Positions: top-left point
*/
@@ -137,10 +137,6 @@ open class ActorWithBody constructor() : Actor(), Visible {
private var posAdjustY = 0
init {
do {
referenceID = HQRNG().nextInt() // set new ID
} while (Terrarum.game.hasActor(referenceID)) // check for collision
map = Terrarum.game.map
}
@@ -719,19 +715,13 @@ open class ActorWithBody constructor() : Actor(), Visible {
fun Float.round() = Math.round(this).toFloat()
fun Float.roundToInt(): Int = Math.round(this)
fun Float.abs() = FastMath.abs(this)
fun Int.abs() = if (this < 0) -this else 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
@@ -750,13 +740,6 @@ open class ActorWithBody constructor() : Actor(), Visible {
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
}

View File

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