diff --git a/res/graphics/fonts/numeric_small.png b/res/graphics/fonts/numeric_small.png new file mode 100644 index 000000000..bb5d30de1 Binary files /dev/null and b/res/graphics/fonts/numeric_small.png differ diff --git a/res/graphics/gui/hotbar/hotbar.png b/res/graphics/gui/quickbar/hotbar.png similarity index 100% rename from res/graphics/gui/hotbar/hotbar.png rename to res/graphics/gui/quickbar/hotbar.png diff --git a/res/graphics/gui/hotbar/hotbarIndicator.png b/res/graphics/gui/quickbar/hotbarIndicator.png similarity index 100% rename from res/graphics/gui/hotbar/hotbarIndicator.png rename to res/graphics/gui/quickbar/hotbarIndicator.png diff --git a/res/graphics/gui/quickbar/item_slot.bmp b/res/graphics/gui/quickbar/item_slot.bmp new file mode 100644 index 000000000..6fd711a57 Binary files /dev/null and b/res/graphics/gui/quickbar/item_slot.bmp differ diff --git a/res/graphics/gui/quickbar/item_slot.png b/res/graphics/gui/quickbar/item_slot.png new file mode 100644 index 000000000..5476b55f1 Binary files /dev/null and b/res/graphics/gui/quickbar/item_slot.png differ diff --git a/res/graphics/gui/hotbar/slot_separator.png b/res/graphics/gui/quickbar/slot_separator.png similarity index 100% rename from res/graphics/gui/hotbar/slot_separator.png rename to res/graphics/gui/quickbar/slot_separator.png diff --git a/res/graphics/gui/hotbar/topbar_creative.png b/res/graphics/gui/quickbar/topbar_creative.png similarity index 100% rename from res/graphics/gui/hotbar/topbar_creative.png rename to res/graphics/gui/quickbar/topbar_creative.png diff --git a/src/net/torvald/terrarum/StateInGame.kt b/src/net/torvald/terrarum/StateInGame.kt index 6e29fdf64..3c0a9f904 100644 --- a/src/net/torvald/terrarum/StateInGame.kt +++ b/src/net/torvald/terrarum/StateInGame.kt @@ -19,10 +19,7 @@ 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 net.torvald.terrarum.ui.* import net.torvald.terrarum.weather.WeatherMixer import org.lwjgl.opengl.GL11 import org.newdawn.slick.* @@ -87,6 +84,13 @@ constructor() : BasicGameState() { var UPDATE_DELTA: Int = 0 + // UI aliases + val uiAliases = HashMap() + private val UI_PIE_MENU = "uiPieMenu" + private val UI_QUICK_BAR = "uiQuickBar" + private val UI_INVENTORY_PLAYER = "uiInventoryPlayer" + private val UI_INVENTORY_ANON = "uiInventoryAnon" + @Throws(SlickException::class) override fun init(gameContainer: GameContainer, stateBasedGame: StateBasedGame) { // load necessary shaders @@ -126,12 +130,20 @@ constructor() : BasicGameState() { notifier = UIHandler(Notification()) notifier.setPosition( (Terrarum.WIDTH - notifier.UI.width) / 2, Terrarum.HEIGHT - notifier.UI.height) - notifier.visible = true + notifier.isVisible = true // set smooth lighting as in config KeyToggler.forceSet(KEY_LIGHTMAP_SMOOTH, Terrarum.getConfigBoolean("smoothlighting")) - + // queue up game UIs + // lesser UIs + uiAliases[UI_PIE_MENU] = UIHandler(UIPieMenu()) + uiAliases[UI_PIE_MENU]!!.setPosition( + (Terrarum.WIDTH - uiAliases[UI_PIE_MENU]!!.UI.width) / 2, + (Terrarum.HEIGHT - uiAliases[UI_PIE_MENU]!!.UI.height) / 2 + ) + uiAliases[UI_PIE_MENU]!!.UI.handler = uiAliases[UI_PIE_MENU] + uiContainer.add(uiAliases[UI_PIE_MENU]!!) // audio test //AudioResourceLibrary.ambientsWoods[0].play() @@ -149,6 +161,7 @@ constructor() : BasicGameState() { world.globalLight = globalLightByTime.toInt() GameController.processInput(gc.input) + uiContainer.forEach { it.processInput(gc.input) } TileStats.update() @@ -184,6 +197,8 @@ constructor() : BasicGameState() { } override fun render(gc: GameContainer, sbg: StateBasedGame, g: Graphics) { + g.setAntiAlias(true) + setBlendNormal() // determine if lightmap blending should be done @@ -232,7 +247,7 @@ constructor() : BasicGameState() { } // draw reference ID if debugWindow is open - if (debugWindow.visible) { + if (debugWindow.isVisible) { actorContainer.forEachIndexed { i, actor -> if (actor is Visible) { g.color = Color.white @@ -266,38 +281,68 @@ constructor() : BasicGameState() { override fun keyPressed(key: Int, c: Char) { GameController.keyPressed(key, c) + + if (Terrarum.getConfigIntArray("keyquickselalt").contains(key) + || key == Terrarum.getConfigInt("keyquicksel")) { + uiAliases[UI_PIE_MENU]!!.setAsOpening() + // TODO hide quick bar + } + + uiContainer.forEach { it.keyPressed(key, c) } } override fun keyReleased(key: Int, c: Char) { GameController.keyReleased(key, c) + + if (Terrarum.getConfigIntArray("keyquickselalt").contains(key) + || key == Terrarum.getConfigInt("keyquicksel")) { + uiAliases[UI_PIE_MENU]!!.setAsClosing() + // TODO show quick bar + } + + uiContainer.forEach { it.keyReleased(key, c) } } override fun mouseMoved(oldx: Int, oldy: Int, newx: Int, newy: Int) { GameController.mouseMoved(oldx, oldy, newx, newy) + + uiContainer.forEach { it.mouseMoved(oldx, oldy, newx, newy) } } override fun mouseDragged(oldx: Int, oldy: Int, newx: Int, newy: Int) { GameController.mouseDragged(oldx, oldy, newx, newy) + + uiContainer.forEach { it.mouseDragged(oldx, oldy, newx, newy) } } override fun mousePressed(button: Int, x: Int, y: Int) { GameController.mousePressed(button, x, y) + + uiContainer.forEach { it.mousePressed(button, x, y) } } override fun mouseReleased(button: Int, x: Int, y: Int) { GameController.mouseReleased(button, x, y) + + uiContainer.forEach { it.mouseReleased(button, x, y) } } override fun mouseWheelMoved(change: Int) { GameController.mouseWheelMoved(change) + + uiContainer.forEach { it.mouseWheelMoved(change) } } override fun controllerButtonPressed(controller: Int, button: Int) { GameController.controllerButtonPressed(controller, button) + + uiContainer.forEach { it.controllerButtonPressed(controller, button) } } override fun controllerButtonReleased(controller: Int, button: Int) { GameController.controllerButtonReleased(controller, button) + + uiContainer.forEach { it.controllerButtonReleased(controller, button) } } override fun getID(): Int = Terrarum.SCENE_ID_GAME diff --git a/src/net/torvald/terrarum/StateMonitorCheck.kt b/src/net/torvald/terrarum/StateMonitorCheck.kt index 0c3190e4a..bf9011f04 100644 --- a/src/net/torvald/terrarum/StateMonitorCheck.kt +++ b/src/net/torvald/terrarum/StateMonitorCheck.kt @@ -20,7 +20,7 @@ class StateMonitorCheck : BasicGameState() { override fun init(gc: GameContainer, g: StateBasedGame) { uiMonitorCheck = UIHandler(MonitorCheckUI()) - uiMonitorCheck.visible = true + uiMonitorCheck.isVisible = true } override fun update(gc: GameContainer, sbg: StateBasedGame, delta: Int) { @@ -41,6 +41,9 @@ class StateMonitorCheck : BasicGameState() { override var width = Terrarum.WIDTH override var height = Terrarum.HEIGHT override var openCloseTime = 150 + override var openCloseTimer: Int = 0 + + override var handler: UIHandler? = null private val colourLUT = arrayOf( 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index f4ca5e284..bb3c2290a 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -1,5 +1,8 @@ package net.torvald.terrarum +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive import net.torvald.imagefont.GameFontWhite import net.torvald.JsonFetcher import net.torvald.JsonWriter @@ -156,7 +159,7 @@ constructor(gamename: String) : StateBasedGame(gamename) { * * e.g. 0x02010034 can be translated as 2.1.52 */ - const val VERSION_RAW = 0x00020041 + const val VERSION_RAW = 0x0002006D const val VERSION_STRING: String = "${VERSION_RAW.ushr(24)}.${VERSION_RAW.and(0xFF0000).ushr(16)}.${VERSION_RAW.and(0xFFFF)}" const val NAME = "Terrarum" @@ -281,30 +284,11 @@ constructor(gamename: String) : StateBasedGame(gamename) { * @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 + val cfg = getConfigMaster(key) + if (cfg is JsonPrimitive) + return cfg.asInt + else + return cfg as Int } /** @@ -316,21 +300,11 @@ constructor(gamename: String) : StateBasedGame(gamename) { * @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 + val cfg = getConfigMaster(key) + if (cfg is JsonPrimitive) + return cfg.asString + else + return cfg as String } /** @@ -342,21 +316,31 @@ constructor(gamename: String) : StateBasedGame(gamename) { * @throws NullPointerException if the specified config simply does not exist. */ fun getConfigBoolean(key: String): Boolean { - var cfg = false - try { - cfg = gameConfig.getAsBoolean(key)!! + val cfg = getConfigMaster(key) + if (cfg is JsonPrimitive) + return cfg.asBoolean + else + return cfg as Boolean + } + + fun getConfigIntArray(key: String): IntArray { + val cfg = getConfigMaster(key) + if (cfg is JsonArray) { + val jsonArray = cfg.asJsonArray + return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt }) } + else + return cfg as IntArray + } + + private fun getConfigMaster(key: String): Any { + var cfg: Any? = null + try { cfg = gameConfig[key]!! } catch (e: NullPointerException) { - try { - cfg = DefaultConfig.fetch().get(key).asBoolean - } - catch (e1: NullPointerException) { - e.printStackTrace() - } - + try { cfg = DefaultConfig.fetch()[key] } + catch (e1: NullPointerException) { e.printStackTrace() } } - - return cfg + return cfg!! } } } diff --git a/src/net/torvald/terrarum/gameactors/AVKey.kt b/src/net/torvald/terrarum/gameactors/AVKey.kt index e83b66233..fbc5ab982 100644 --- a/src/net/torvald/terrarum/gameactors/AVKey.kt +++ b/src/net/torvald/terrarum/gameactors/AVKey.kt @@ -73,4 +73,8 @@ object AVKey { */ const val ARMOURDEFENCE = "armourdefence" const val ARMOURDEFENCEMULT = "armourdefence$MULT" + + + + const val _PLAYER_QUICKBARSEL = "__quickbarselection" } \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameactors/ActorInventory.kt b/src/net/torvald/terrarum/gameactors/ActorInventory.kt index ff3946bad..36b4a5ecc 100644 --- a/src/net/torvald/terrarum/gameactors/ActorInventory.kt +++ b/src/net/torvald/terrarum/gameactors/ActorInventory.kt @@ -21,7 +21,7 @@ class ActorInventory() { private var capacityMode: Int /** - * <ReferenceID, Amounts> + * HashMap<ReferenceID, Amounts> */ private val itemList: HashMap = HashMap() diff --git a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt index 28783a272..268163705 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt @@ -52,11 +52,21 @@ open class ActorWithBody : Actor(), Visible { var veloY: Double get() = velocity.y private set(value) { velocity.y = value } - var walkX: Double = 0.0 - var walkY: Double = 0.0 + val moveDelta = Vector2(0.0, 0.0) @Transient private val VELO_HARD_LIMIT = 100.0 + /** + * for "Controllable" actors + */ + var controllerVel: Vector2? = if (this is Controllable) Vector2() else null + var walkX: Double + get() = controllerVel!!.x + internal set(value) { controllerVel!!.x = value } + var walkY: Double + get() = controllerVel!!.x + internal set(value) { controllerVel!!.x = value } + /** * Physical properties. * Values derived from ActorValue must be @Transient. @@ -281,7 +291,7 @@ open class ActorWithBody : Actor(), Visible { * Actual physics thing (altering velocity) starts from here */ - applyControllerMovement() + applyMovementVelocity() // applyBuoyancy() @@ -298,13 +308,14 @@ open class ActorWithBody : Actor(), Visible { // Set 'next' position (hitbox) from canonical and walking velocity setNewNextHitbox() + applyNormalForce() /** * solveCollision()? * If and only if: * This body is NON-STATIC and the other body is STATIC */ + setNewNextHitbox() displaceByCCD() - applyNormalForce() setHorizontalFriction() if (isPlayerNoClip) // or hanging on the rope, etc. @@ -317,25 +328,41 @@ open class ActorWithBody : Actor(), Visible { clampHitbox() } - // useless - walledLeft = false//isColliding(hitbox, COLLIDING_LEFT) - walledRight = false//isColliding(hitbox, COLLIDING_RIGHT) + // cheap solution for sticking into the wall while Left or Right is held + walledLeft = false//isTouchingSide(hitbox, COLLIDING_LEFT) + walledRight = false//isTouchingSide(hitbox, COLLIDING_RIGHT) } } - private fun applyControllerMovement() { - // decide whether ignore walkX - if (!(isCollidingSide(hitbox, COLLIDING_LEFT) && walkX < 0) - || !(isCollidingSide(hitbox, COLLIDING_RIGHT) && walkX > 0) - ) { - moveDelta.x = veloX + walkX - } + private fun applyMovementVelocity() { + if (this is Controllable) { + // decide whether to ignore walkX + if (!(isCollidingSide(hitbox, COLLIDING_LEFT) && walkX < 0) + || !(isCollidingSide(hitbox, COLLIDING_RIGHT) && walkX > 0) + ) { + moveDelta.x = veloX + walkX + } - // decide whether ignore walkY - if (!(isCollidingSide(hitbox, COLLIDING_TOP) && walkY < 0) - || !(isCollidingSide(hitbox, COLLIDING_BOTTOM) && walkY > 0) - ) { - moveDelta.y = veloY + walkY + // decide whether to ignore walkY + if (!(isCollidingSide(hitbox, COLLIDING_TOP) && walkY < 0) + || !(isCollidingSide(hitbox, COLLIDING_BOTTOM) && walkY > 0) + ) { + moveDelta.y = veloY + walkY + } + } + else { + if (!isCollidingSide(hitbox, COLLIDING_LEFT) + || !isCollidingSide(hitbox, COLLIDING_RIGHT) + ) { + moveDelta.x = veloX + } + + // decide whether to ignore walkY + if (!isCollidingSide(hitbox, COLLIDING_TOP) + || !isCollidingSide(hitbox, COLLIDING_BOTTOM) + ) { + moveDelta.y = veloY + } } } @@ -369,18 +396,21 @@ open class ActorWithBody : Actor(), Visible { /** * FIXME the culprit! - * (5566 -> no colliiding but player does not "sink") - * (5567 -> colliding) + * (5566 -> no collision but player does not "sink") + * (5567 -> collision) * How to fix: */ private fun applyNormalForce() { if (!isNoCollideWorld) { // axis Y. Use operand >= if (moveDelta.y >= 0.0) { // was moving downward? - if (isTouchingSide(nextHitbox, COLLIDING_BOTTOM)) { // actor hit something on its bottom + if (isColliding(nextHitbox)) { hitAndReflectY() grounded = true } + else if (isTouchingSide(nextHitbox, COLLIDING_BOTTOM)) { // actor hit something on its bottom + grounded = true + } else { // the actor is not grounded at all grounded = false } @@ -408,7 +438,7 @@ open class ActorWithBody : Actor(), Visible { private fun displaceByCCD() { if (!isNoCollideWorld){ // do some CCD between hitbox and nextHitbox - val ccdBox = nextHitbox.clone().snapToPixel() + val ccdBox = nextHitbox.clone()//.snapToPixel() val ccdDelta = nextHitbox.toVector() to hitbox.toVector() val deltaMax = Math.max(ccdDelta.x.abs(), ccdDelta.y.abs()) // set ccdDelta so that CCD move a pixel per round @@ -418,15 +448,14 @@ open class ActorWithBody : Actor(), Visible { //println("deltaMax: $deltaMax") //println("ccdDelta: $ccdDelta") - while (!ccdDelta.isZero && isColliding(ccdBox)) { + while (!ccdDelta.isZero && isColliding(ccdBox, COLLIDING_ALLSIDE)) { nextHitbox.translate(ccdDelta) ccdCollided = true - ccdBox.reassign(nextHitbox).snapToPixel() + //ccdBox.reassign(nextHitbox).snapToPixel() + ccdBox.reassign(nextHitbox) } - nextHitbox.snapToPixel() - //println("ccdCollided: $ccdCollided") } else { @@ -437,22 +466,22 @@ open class ActorWithBody : Actor(), Visible { private fun hitAndReflectX() { if ((veloX * elasticity).abs() > Epsilon.E) { veloX *= -elasticity - walkX *= -elasticity + if (this is Controllable) walkX *= -elasticity } else { veloX = 0.0 - walkX = 0.0 + if (this is Controllable) walkX = 0.0 } } private fun hitAndReflectY() { if ((veloY * elasticity).abs() > Epsilon.E) { - veloY *= -elasticity - walkY *= -elasticity + veloY *= -elasticity + if (this is Controllable) walkY *= -elasticity } else { veloY = 0.0 - walkY *= 0.0 + if (this is Controllable) walkY *= 0.0 } } @@ -510,15 +539,7 @@ open class ActorWithBody : Actor(), Visible { val tyStart = y1.div(TSIZE).floorInt() val tyEnd = y2.div(TSIZE).floorInt() - for (y in tyStart..tyEnd) { - for (x in txStart..txEnd) { - val tile = world.getTileFromTerrain(x, y) - if (TilePropCodex.getProp(tile).isSolid) - return true - } - } - - return false + return isCollidingInternal(txStart, tyStart, txEnd, tyEnd) } private fun isTouchingSide(hitbox: Hitbox, option: Int): Boolean { @@ -549,20 +570,12 @@ open class ActorWithBody : Actor(), Visible { } else throw IllegalArgumentException() - val txStart = x1.plus(1.0).div(TSIZE).floorInt() - val txEnd = x2.plus(1.0).div(TSIZE).floorInt() - val tyStart = y1.plus(1.0).div(TSIZE).floorInt() - val tyEnd = y2.plus(1.0).div(TSIZE).floorInt() + val txStart = x1.div(TSIZE).floorInt() + val txEnd = x2.div(TSIZE).floorInt() + val tyStart = y1.div(TSIZE).floorInt() + val tyEnd = y2.div(TSIZE).floorInt() - for (y in tyStart..tyEnd) { - for (x in txStart..txEnd) { - val tile = world.getTileFromTerrain(x, y) - if (TilePropCodex.getProp(tile).isSolid) - return true - } - } - - return false + return isCollidingInternal(txStart, tyStart, txEnd, tyEnd) } @@ -599,7 +612,11 @@ open class ActorWithBody : Actor(), Visible { val tyStart = y1.div(TSIZE).roundInt() val tyEnd = y2.div(TSIZE).roundInt() - for (y in tyStart..tyEnd) { + return isCollidingInternal(txStart, tyStart, txEnd, tyEnd) + } + + private fun isCollidingInternal(txStart: Int, tyStart: Int, txEnd: Int, tyEnd: Int): Boolean { + /*for (y in tyStart..tyEnd) { for (x in txStart..txEnd) { val tile = world.getTileFromTerrain(x, y) if (TilePropCodex.getProp(tile).isSolid) @@ -607,7 +624,8 @@ open class ActorWithBody : Actor(), Visible { } } - return false + return false*/ + return if (tyEnd <= 347) false else true } private fun getContactingAreaFluid(side: Int, translateX: Int = 0, translateY: Int = 0): Int { @@ -653,7 +671,7 @@ open class ActorWithBody : Actor(), Visible { val friction = if (isPlayerNoClip) BASE_FRICTION * TilePropCodex.getProp(TileNameCode.STONE).friction.tileFrictionToMult() else - BASE_FRICTION * tileFriction.tileFrictionToMult() + BASE_FRICTION * bodyFriction.tileFrictionToMult() if (veloX < 0) { veloX += friction @@ -664,13 +682,15 @@ open class ActorWithBody : Actor(), Visible { if (veloX < 0) veloX = 0.0 // compensate overshoot } - if (walkX < 0) { - walkX += friction - if (walkX > 0) walkX = 0.0 - } - else if (walkX > 0) { - walkX -= friction - if (walkX < 0) walkX = 0.0 + if (this is Controllable) { + if (walkX < 0) { + walkX += friction + if (walkX > 0) walkX = 0.0 + } + else if (walkX > 0) { + walkX -= friction + if (walkX < 0) walkX = 0.0 + } } } @@ -678,7 +698,7 @@ open class ActorWithBody : Actor(), Visible { val friction = if (isPlayerNoClip) BASE_FRICTION * TilePropCodex.getProp(TileNameCode.STONE).friction.tileFrictionToMult() else - BASE_FRICTION * tileFriction.tileFrictionToMult() + BASE_FRICTION * bodyFriction.tileFrictionToMult() if (veloY < 0) { veloY += friction @@ -689,13 +709,15 @@ open class ActorWithBody : Actor(), Visible { if (veloY < 0) veloY = 0.0 // compensate overshoot } - if (walkY < 0) { - walkY += friction - if (walkY > 0) walkY = 0.0 - } - else if (walkY > 0) { - walkY -= friction - if (walkY < 0) walkY = 0.0 + if (this is Controllable) { + if (walkY < 0) { + walkY += friction + if (walkY > 0) walkY = 0.0 + } + else if (walkY > 0) { + walkY -= friction + if (walkY < 0) walkY = 0.0 + } } } @@ -730,9 +752,9 @@ open class ActorWithBody : Actor(), Visible { * Get highest friction value from feet tiles. * @return */ - internal val tileFriction: Int + internal val bodyFriction: Int get() { - var friction = 0 + /*var friction = 0 // take highest value val tilePosXStart = (hitbox.posX / TSIZE).roundInt() @@ -745,9 +767,15 @@ open class ActorWithBody : Actor(), Visible { if (thisFriction > friction) friction = thisFriction } - return friction + return friction*/ + return 1 + } + fun Int.tileFrictionToMult(): Double = this / 16.0 + + internal val feetFriction: Int + get() { + return 16 } - fun Int.tileFrictionToMult() = this / 16.0 /** * Get highest tile density from occupying tiles, fluid only diff --git a/src/net/torvald/terrarum/gameactors/Controllable.kt b/src/net/torvald/terrarum/gameactors/Controllable.kt index c911f4125..a35a5b03f 100644 --- a/src/net/torvald/terrarum/gameactors/Controllable.kt +++ b/src/net/torvald/terrarum/gameactors/Controllable.kt @@ -1,8 +1,11 @@ package net.torvald.terrarum.gameactors +import org.dyn4j.geometry.Vector2 import org.newdawn.slick.Input /** + * Actors that has movement controlled by Keyboard or AI + * * Created by minjaesong on 16-03-14. */ interface Controllable { diff --git a/src/net/torvald/terrarum/gameactors/PBSigrid.kt b/src/net/torvald/terrarum/gameactors/PBSigrid.kt index c58c092ce..144465ff0 100644 --- a/src/net/torvald/terrarum/gameactors/PBSigrid.kt +++ b/src/net/torvald/terrarum/gameactors/PBSigrid.kt @@ -59,6 +59,7 @@ object PBSigrid { p.actorValue[AVKey.BASEDEFENCE] = 141 p.actorValue["selectedtile"] = 16 + p.actorValue[AVKey._PLAYER_QUICKBARSEL] = 0 p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT)!!, 10, 0) diff --git a/src/net/torvald/terrarum/gameactors/Player.kt b/src/net/torvald/terrarum/gameactors/Player.kt index babe57e12..3e79a659b 100644 --- a/src/net/torvald/terrarum/gameactors/Player.kt +++ b/src/net/torvald/terrarum/gameactors/Player.kt @@ -5,6 +5,7 @@ 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.terrarum.ui.UIQuickBar import org.dyn4j.geometry.Vector2 import org.lwjgl.input.Controller import org.lwjgl.input.Controllers @@ -60,6 +61,7 @@ class Player : ActorWithBody(), Controllable, Pocketed, Factionable, Luminous, L /** Must be set by PlayerFactory */ override var inventory: ActorInventory = ActorInventory() + internal val quickBarRegistration = IntArray(UIQuickBar.SLOT_COUNT, { -1 }) /** Must be set by PlayerFactory */ override var faction: HashSet = HashSet() diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt index 65e9240a9..4c91e4ebb 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt @@ -18,6 +18,7 @@ object PlayerBuilder { // attach sprite // do etc. + p.actorValue[AVKey._PLAYER_QUICKBARSEL] = 0 return p } diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index 79f3c6ad8..1daeb3da8 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -25,6 +25,9 @@ class BasicDebugInfoWindow:UICanvas { override var height: Int = Terrarum.HEIGHT override var openCloseTime: Int = 0 + override var openCloseTimer: Int = 0 + + override var handler: UIHandler? = null private var prevPlayerX = 0.0 private var prevPlayerY = 0.0 diff --git a/src/net/torvald/terrarum/ui/ConsoleWindow.kt b/src/net/torvald/terrarum/ui/ConsoleWindow.kt index f9c514adb..11badfa5a 100644 --- a/src/net/torvald/terrarum/ui/ConsoleWindow.kt +++ b/src/net/torvald/terrarum/ui/ConsoleWindow.kt @@ -34,11 +34,13 @@ class ConsoleWindow : UICanvas, UITypable { override var height: Int = LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT + 1) override var openCloseTime: Int = 0 + override var openCloseTimer: Int = 0 private var drawOffX: Float = 0f private var drawOffY: Float = -height.toFloat() private var openingTimeCounter = 0 + override var handler: UIHandler? = null private var historyIndex = -1 @@ -170,7 +172,10 @@ class ConsoleWindow : UICanvas, UITypable { commandHistory = HistoryArray(COMMAND_HISTORY_MAX) commandInputPool = StringBuilder() - if (Terrarum.ingame.auth.b()) sendMessage(Lang["DEV_MESSAGE_CONSOLE_CODEX"]) + if (Terrarum.ingame.auth.b()) { + sendMessage("${Terrarum.NAME} ${Terrarum.VERSION_STRING}") + sendMessage(Lang["DEV_MESSAGE_CONSOLE_CODEX"]) + } } override fun doOpening(gc: GameContainer, delta: Int) { diff --git a/src/net/torvald/terrarum/ui/ItemSlotImageBuilder.kt b/src/net/torvald/terrarum/ui/ItemSlotImageBuilder.kt new file mode 100644 index 000000000..3f98ad59d --- /dev/null +++ b/src/net/torvald/terrarum/ui/ItemSlotImageBuilder.kt @@ -0,0 +1,52 @@ +package net.torvald.terrarum.ui + +import net.torvald.terrarum.setBlendNormal +import org.newdawn.slick.Color +import org.newdawn.slick.Image +import org.newdawn.slick.SpriteSheet +import org.newdawn.slick.SpriteSheetFont + +/** + * Make item slot image with number on bottom-right + * + * Created by minjaesong on 16-07-20. + */ +object ItemSlotImageBuilder { + + const val COLOR_BLACK = 1 + const val COLOR_WHITE = 2 + + private val colourBlack = Color(0x404040) + private val colourWhite = Color(0xC0C0C0) + + private val numberFont = SpriteSheetFont( + SpriteSheet("./res/graphics/fonts/numeric_small.png", 5, 8), + '0' + ) + private val slotImage = Image("./res/graphics/gui/quickbar/item_slot.png") + private val canvas = Image(slotImage.width, slotImage.height) + + fun produce(color: Int, number: Int = -1): Image { + if (color == COLOR_BLACK) + canvas.graphics.drawImage(slotImage, 0f, 0f, colourBlack) + else if (color == COLOR_WHITE) + canvas.graphics.drawImage(slotImage, 0f, 0f, colourWhite) + + if (number >= 0) { + canvas.graphics.font = numberFont + + if (color == COLOR_BLACK) + canvas.graphics.color = colourWhite + else if (color == COLOR_WHITE) + canvas.graphics.color = colourBlack + + canvas.graphics.drawString(number.mod(UIQuickBar.SLOT_COUNT).toString(), + slotImage.width - 6f, + slotImage.height - 10f + ) + } + + return canvas + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/ui/MessageWindow.kt b/src/net/torvald/terrarum/ui/MessageWindow.kt index cdfd4db2b..0c1edbc3b 100644 --- a/src/net/torvald/terrarum/ui/MessageWindow.kt +++ b/src/net/torvald/terrarum/ui/MessageWindow.kt @@ -28,7 +28,9 @@ constructor(override var width: Int, isBlackVariant: Boolean) : UICanvas { override var openCloseTime: Int = OPEN_CLOSE_TIME internal var opacity = 0f - internal var openCloseCounter = 0 + override var openCloseTimer = 0 + + override var handler: UIHandler? = null private lateinit var uidrawCanvas: Image // render all the images and fonts here; will be faded @@ -85,27 +87,27 @@ constructor(override var width: Int, isBlackVariant: Boolean) : UICanvas { } override fun doOpening(gc: GameContainer, delta: Int) { - openCloseCounter += delta - opacity = FastMath.interpolateLinear(openCloseCounter.toFloat() / openCloseTime.toFloat(), + openCloseTimer += delta + opacity = FastMath.interpolateLinear(openCloseTimer.toFloat() / openCloseTime.toFloat(), 0f, 1f ) } override fun doClosing(gc: GameContainer, delta: Int) { - openCloseCounter += delta - opacity = FastMath.interpolateLinear(openCloseCounter.toFloat() / openCloseTime.toFloat(), + openCloseTimer += delta + opacity = FastMath.interpolateLinear(openCloseTimer.toFloat() / openCloseTime.toFloat(), 1f, 0f ) } override fun endOpening(gc: GameContainer, delta: Int) { opacity = 1f - openCloseCounter = 0 + openCloseTimer = 0 } override fun endClosing(gc: GameContainer, delta: Int) { opacity = 0f - openCloseCounter = 0 + openCloseTimer = 0 } private fun drawSegments(g: Graphics) { diff --git a/src/net/torvald/terrarum/ui/Notification.kt b/src/net/torvald/terrarum/ui/Notification.kt index 4081e4e71..3abaaff40 100644 --- a/src/net/torvald/terrarum/ui/Notification.kt +++ b/src/net/torvald/terrarum/ui/Notification.kt @@ -15,7 +15,7 @@ constructor() : UICanvas { override var width: Int = 0 override var height: Int = 0 internal var visibleTime: Int - internal var showupTimeConuter = 0 + override var openCloseTimer = 0 internal var isShowing = false internal var message: Array = Array(MessageWindow.MESSAGES_DISPLAY, { i -> ""}) @@ -24,6 +24,8 @@ constructor() : UICanvas { override var openCloseTime: Int = MessageWindow.OPEN_CLOSE_TIME + override var handler: UIHandler? = null + private val SHOWUP_MAX = 15000 init { @@ -37,7 +39,7 @@ constructor() : UICanvas { } override fun update(gc: GameContainer, delta: Int) { - if (showupTimeConuter >= visibleTime && isShowing) { + if (openCloseTimer >= visibleTime && isShowing) { // invoke closing mode doClosing(gc, delta) // check if msgUI is fully fade out @@ -48,7 +50,7 @@ constructor() : UICanvas { } if (isShowing) { - showupTimeConuter += delta + openCloseTimer += delta } } @@ -82,6 +84,6 @@ constructor() : UICanvas { isShowing = true this.message = message msgUI.setMessage(this.message) - showupTimeConuter = 0 + openCloseTimer = 0 } } diff --git a/src/net/torvald/terrarum/ui/UICanvas.kt b/src/net/torvald/terrarum/ui/UICanvas.kt index c30baa170..ae112a5ff 100644 --- a/src/net/torvald/terrarum/ui/UICanvas.kt +++ b/src/net/torvald/terrarum/ui/UICanvas.kt @@ -15,6 +15,9 @@ interface UICanvas { * In milliseconds */ var openCloseTime: Int + var openCloseTimer: Int + + var handler: UIHandler? fun update(gc: GameContainer, delta: Int) diff --git a/src/net/torvald/terrarum/ui/UIHandler.kt b/src/net/torvald/terrarum/ui/UIHandler.kt index dbbfc8325..34c5bd79c 100644 --- a/src/net/torvald/terrarum/ui/UIHandler.kt +++ b/src/net/torvald/terrarum/ui/UIHandler.kt @@ -35,10 +35,10 @@ constructor(val UI: UICanvas) { private val UIGraphicInstance: Graphics private val UIDrawnCanvas: Image - private var opening = false - private var closing = false - private var opened = false // fully opened - var visible: Boolean = false + var isOpening = false + var isClosing = false + var isOpened = false // fully opened + var isVisible: Boolean = false get() = if (alwaysVisible) true else field set(value) { @@ -48,6 +48,9 @@ constructor(val UI: UICanvas) { field = value } + var opacity = 1f + var scale = 1f + var openCloseCounter = 0 init { @@ -56,18 +59,19 @@ constructor(val UI: UICanvas) { UIDrawnCanvas = Image( //FastMath.nearestPowerOfTwo(UI.width), FastMath.nearestPowerOfTwo(UI.height)) UI.width, UI.height) + UIDrawnCanvas.filter = Image.FILTER_LINEAR UIGraphicInstance = UIDrawnCanvas.graphics } fun update(gc: GameContainer, delta: Int) { - if (visible || alwaysVisible) { + if (isVisible || alwaysVisible) { UI.update(gc, delta) } - if (opening) { - visible = true + if (isOpening) { + isVisible = true openCloseCounter += delta // println("UI ${UI.javaClass.simpleName} (open)") @@ -79,12 +83,12 @@ constructor(val UI: UICanvas) { } else { UI.endOpening(gc, delta) - opening = false - opened = true + isOpening = false + isOpened = true openCloseCounter = 0 } } - else if (closing) { + else if (isClosing) { openCloseCounter += delta // println("UI ${UI.javaClass.simpleName} (close)") @@ -96,32 +100,37 @@ constructor(val UI: UICanvas) { } else { UI.endClosing(gc, delta) - closing = false - opened = false - visible = false + isClosing = false + isOpened = false + isVisible = false openCloseCounter = 0 } } } - fun render(gc: GameContainer, sbg: StateBasedGame, gameGraphicInstance: Graphics) { - if (visible || alwaysVisible) { + fun render(gc: GameContainer, sbg: StateBasedGame, ingameGraphics: Graphics) { + if (isVisible || alwaysVisible) { UIGraphicInstance.clear() UIGraphicInstance.font = Terrarum.gameFont + UIGraphicInstance.setAntiAlias(true) + UI.render(gc, UIGraphicInstance) if (sbg.currentStateID == Terrarum.SCENE_ID_GAME) { - gameGraphicInstance.drawImage(UIDrawnCanvas, - posX + MapCamera.cameraX * Terrarum.ingame.screenZoom, - posY + MapCamera.cameraY * Terrarum.ingame.screenZoom + ingameGraphics.drawImage(UIDrawnCanvas.getScaledCopy(scale), + posX + MapCamera.cameraX * Terrarum.ingame.screenZoom - (UI.width / 2f * scale.minus(1)), + posY + MapCamera.cameraY * Terrarum.ingame.screenZoom - (UI.height / 2f * scale.minus(1)), + Color(1f, 1f, 1f, opacity) )// compensate for screenZoom AND camera translation // (see Game.render -> g.translate()) } else { - gameGraphicInstance.drawImage(UIDrawnCanvas, - posX.toFloat(), - posY.toFloat() + ingameGraphics.drawImage(UIDrawnCanvas.getScaledCopy(scale), + posX.toFloat() - (UI.width / 2f * scale.minus(1)), + posY.toFloat() - (UI.height / 2f * scale.minus(1)), + Color(1f, 1f, 1f, opacity) ) + } } } @@ -133,10 +142,10 @@ constructor(val UI: UICanvas) { fun setAsAlwaysVisible() { alwaysVisible = true - visible = true - opened = true - opening = false - closing = false + isVisible = true + isOpened = true + isOpening = false + isClosing = false } /** @@ -146,8 +155,10 @@ constructor(val UI: UICanvas) { if (alwaysVisible) { throw RuntimeException("[UIHandler] Tried to 'open' constant UI") } - opened = false - opening = true + if (!isOpened && !isVisible) { + isOpened = false + isOpening = true + } } /** @@ -157,82 +168,82 @@ constructor(val UI: UICanvas) { if (alwaysVisible) { throw RuntimeException("[UIHandler] Tried to 'close' constant UI") } - opened = false - closing = true + isOpened = false + isClosing = true } fun toggleOpening() { if (alwaysVisible) { throw RuntimeException("[UIHandler] Tried to 'toggle opening of' constant UI") } - if (visible) { - if (!closing) { + if (isVisible) { + if (!isClosing) { setAsClosing() } } else { - if (!opening) { + if (!isOpening) { setAsOpening() } } } fun processInput(input: Input) { - if (visible) { + if (isVisible) { UI.processInput(input) } } fun keyPressed(key: Int, c: Char) { - if (visible && UI is UITypable) { + if (isVisible && UI is UITypable) { UI.keyPressed(key, c) } } fun keyReleased(key: Int, c: Char) { - if (visible && UI is UITypable) { + if (isVisible && UI is UITypable) { UI.keyReleased(key, c) } } fun mouseMoved(oldx: Int, oldy: Int, newx: Int, newy: Int) { - if (visible && UI is UIClickable) { + if (isVisible && 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 (isVisible && UI is UIClickable) { UI.mouseDragged(oldx, oldy, newx, newy) } } fun mousePressed(button: Int, x: Int, y: Int) { - if (visible && UI is UIClickable) { + if (isVisible && UI is UIClickable) { UI.mousePressed(button, x, y) } } fun mouseReleased(button: Int, x: Int, y: Int) { - if (visible && UI is UIClickable) { + if (isVisible && UI is UIClickable) { UI.mouseReleased(button, x, y) } } fun mouseWheelMoved(change: Int) { - if (visible && UI is UIClickable) { + if (isVisible && UI is UIClickable) { UI.mouseWheelMoved(change) } } fun controllerButtonPressed(controller: Int, button: Int) { - if (visible && UI is UIClickable) { + if (isVisible && UI is UIClickable) { UI.controllerButtonPressed(controller, button) } } fun controllerButtonReleased(controller: Int, button: Int) { - if (visible && UI is UIClickable) { + if (isVisible && UI is UIClickable) { UI.controllerButtonReleased(controller, button) } } @@ -243,6 +254,6 @@ constructor(val UI: UICanvas) { if (alwaysVisible) { return false } - return visible && !opening + return isVisible && !isOpening } } diff --git a/src/net/torvald/terrarum/ui/UIPieMenu.kt b/src/net/torvald/terrarum/ui/UIPieMenu.kt new file mode 100644 index 000000000..03c241a86 --- /dev/null +++ b/src/net/torvald/terrarum/ui/UIPieMenu.kt @@ -0,0 +1,108 @@ +package net.torvald.terrarum.ui + +import com.jme3.math.FastMath +import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.gameactors.AVKey +import org.dyn4j.geometry.Vector2 +import org.newdawn.slick.Color +import org.newdawn.slick.GameContainer +import org.newdawn.slick.Graphics +import org.newdawn.slick.Input + +/** + * Created by minjaesong on 16-07-20. + */ +class UIPieMenu : UICanvas { + private val cellSize = 32 + + private val slotCount = UIQuickBar.SLOT_COUNT + private val roundRectRadius = 6 + + override var width: Int = cellSize * 7 + override var height: Int = width + /** + * In milliseconds + */ + override var openCloseTime: Int = 160 + override var openCloseTimer: Int = 0 + + override var handler: UIHandler? = null + + private val smallenSize = 0.93f + + var menuSelection: Int = -1 + + override fun update(gc: GameContainer, delta: Int) { + if (menuSelection >= 0) + Terrarum.ingame.player.actorValue[AVKey._PLAYER_QUICKBARSEL] = + menuSelection % quickbarSlots + } + + override fun render(gc: GameContainer, g: Graphics) { + val centrePoint = Vector2(width / 2.0, height / 2.0) + + // draw radial thingies + for (i in 0..slotCount - 1) { + // set position + val angle = Math.PI * 2.0 * (i.toDouble() / slotCount) + Math.PI // 180 deg monitor-wise + val slotCentrePoint = Vector2(0.0, cellSize * 3.0).setDirection(angle) + centrePoint + + // draw cells + g.color = if (menuSelection == i) Color(0xC0C0C0) else Color(0x404040) + g.drawImage(ItemSlotImageBuilder.produce( + if (menuSelection == i) + ItemSlotImageBuilder.COLOR_WHITE + else + ItemSlotImageBuilder.COLOR_BLACK, + i + 1 + ), + slotCentrePoint.x.toFloat() - (cellSize / 2f), + slotCentrePoint.y.toFloat() - (cellSize / 2f) + ) + + // TODO draw item + } + } + + override fun processInput(input: Input) { + if (handler!!.isOpened || handler!!.isOpening) { + val cursorPos = Vector2(input.mouseX.toDouble(), input.mouseY.toDouble()) + val centre = Vector2(Terrarum.WIDTH / 2.0, Terrarum.HEIGHT / 2.0) + val deg = (centre - cursorPos).direction.toFloat() + + menuSelection = Math.round(deg * slotCount / FastMath.TWO_PI) + if (menuSelection < 0) menuSelection += 10 + } + } + + override fun doOpening(gc: GameContainer, delta: Int) { + if (openCloseTimer < openCloseTime) { + openCloseTimer += delta + + handler!!.opacity = openCloseTimer.toFloat() / openCloseTime + handler!!.scale = smallenSize + (1f.minus(smallenSize) * handler!!.opacity) + } + } + + override fun doClosing(gc: GameContainer, delta: Int) { + if (openCloseTimer < openCloseTime) { + openCloseTimer += delta + handler!!.isOpened = false + + handler!!.opacity = (openCloseTime - openCloseTimer.toFloat()) / openCloseTime + handler!!.scale = smallenSize + (1f.minus(smallenSize) * handler!!.opacity) + } + } + + override fun endOpening(gc: GameContainer, delta: Int) { + openCloseTimer = 0 + handler!!.opacity = 1f + handler!!.scale = 1f + } + + override fun endClosing(gc: GameContainer, delta: Int) { + openCloseTimer = 0 + handler!!.opacity = 0f + handler!!.scale = 1f + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/ui/UIQuickBar.kt b/src/net/torvald/terrarum/ui/UIQuickBar.kt new file mode 100644 index 000000000..4cf71897c --- /dev/null +++ b/src/net/torvald/terrarum/ui/UIQuickBar.kt @@ -0,0 +1,64 @@ +package net.torvald.terrarum.ui + +import org.newdawn.slick.GameContainer +import org.newdawn.slick.Graphics +import org.newdawn.slick.Input + +/** + * Created by minjaesong on 16-07-20. + */ +class UIQuickBar : UICanvas { + override var width: Int + get() = throw UnsupportedOperationException() + set(value) { + } + override var height: Int + get() = throw UnsupportedOperationException() + set(value) { + } + /** + * In milliseconds + */ + override var openCloseTime: Int + get() = throw UnsupportedOperationException() + set(value) { + } + override var openCloseTimer: Int + get() = throw UnsupportedOperationException() + set(value) { + } + + override var handler: UIHandler? = null + + override fun update(gc: GameContainer, delta: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun render(gc: GameContainer, g: Graphics) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun processInput(input: Input) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun doOpening(gc: GameContainer, delta: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun doClosing(gc: GameContainer, delta: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun endOpening(gc: GameContainer, delta: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun endClosing(gc: GameContainer, delta: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + companion object { + const val SLOT_COUNT = 10 + } +} \ No newline at end of file diff --git a/work_files/UI/inventory.numbers b/work_files/UI/inventory.numbers index 85d85f861..e4c996c91 100644 Binary files a/work_files/UI/inventory.numbers and b/work_files/UI/inventory.numbers differ diff --git a/work_files/UI/quickbar_related.numbers b/work_files/UI/quickbar_related.numbers new file mode 100644 index 000000000..b808d86d4 Binary files /dev/null and b/work_files/UI/quickbar_related.numbers differ diff --git a/work_files/UI/quickbar_related.pdf b/work_files/UI/quickbar_related.pdf new file mode 100644 index 000000000..5837b4014 Binary files /dev/null and b/work_files/UI/quickbar_related.pdf differ diff --git a/work_files/graphics/gui/hotbar/.gitattributes b/work_files/graphics/gui/quickbar/.gitattributes similarity index 100% rename from work_files/graphics/gui/hotbar/.gitattributes rename to work_files/graphics/gui/quickbar/.gitattributes