From 53c82a34d5fe2837164be39ce65841c5fe9b066a Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Mon, 24 Oct 2016 12:18:44 +0900 Subject: [PATCH] controlling part of Player moved to ActorHumanoid to alleviate the potential pain on your arse when implementing any NPCs Former-commit-id: 33f5dba5becef6d60e1413bb8085eeaa0673a345 Former-commit-id: 8c5a3ad7a9d8565908829798110ade8f294c8d44 --- src/net/torvald/terrarum/StateInGame.kt | 7 +- .../torvald/terrarum/console/ActorHumanoid.kt | 504 ++++++++++++++++++ .../torvald/terrarum/gameactors/AnyPlayer.kt | 5 +- .../gameactors/CreatureRawInjector.kt | 3 +- .../terrarum/gameactors/HistoricalFigure.kt | 17 +- .../torvald/terrarum/gameactors/PBCynthia.kt | 3 +- .../torvald/terrarum/gameactors/PBSigrid.kt | 3 +- src/net/torvald/terrarum/gameactors/Player.kt | 462 +--------------- 8 files changed, 524 insertions(+), 480 deletions(-) create mode 100644 src/net/torvald/terrarum/console/ActorHumanoid.kt diff --git a/src/net/torvald/terrarum/StateInGame.kt b/src/net/torvald/terrarum/StateInGame.kt index fc213576e..4724b9fd9 100644 --- a/src/net/torvald/terrarum/StateInGame.kt +++ b/src/net/torvald/terrarum/StateInGame.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum import net.torvald.imagefont.GameFontBase import net.torvald.terrarum.audio.AudioResourceLibrary import net.torvald.terrarum.concurrent.ThreadPool +import net.torvald.terrarum.console.ActorHumanoid import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.console.Authenticator import net.torvald.terrarum.console.CommandDict @@ -59,7 +60,7 @@ constructor() : BasicGameState() { lateinit var notifier: UIHandler lateinit internal var playerWrapper: AnyPlayer - internal val player: HistoricalFigure // currently POSSESSED actor :) + internal val player: ActorHumanoid // currently POSSESSED actor :) get() = playerWrapper.actor //private var GRADIENT_IMAGE: Image? = null @@ -251,13 +252,13 @@ constructor() : BasicGameState() { throw IllegalArgumentException("No such actor in actorContainer: $refid") } - playerWrapper = AnyPlayer(getActorByID(refid) as HistoricalFigure) + playerWrapper = AnyPlayer(getActorByID(refid) as ActorHumanoid) WorldSimulator(world, player, UPDATE_DELTA) } private fun setAppTitle() { Terrarum.appgc.setTitle( - "${Terrarum.NAME}" + + Terrarum.NAME + " — F: ${Terrarum.appgc.fps} (${Terrarum.TARGET_INTERNAL_FPS})" + " — M: ${Terrarum.memInUse}M / ${Terrarum.totalVMMem}M") } diff --git a/src/net/torvald/terrarum/console/ActorHumanoid.kt b/src/net/torvald/terrarum/console/ActorHumanoid.kt new file mode 100644 index 000000000..1b28aa45a --- /dev/null +++ b/src/net/torvald/terrarum/console/ActorHumanoid.kt @@ -0,0 +1,504 @@ +package net.torvald.terrarum.console + +import com.jme3.math.FastMath +import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.gameactors.* +import net.torvald.terrarum.gameactors.faction.Faction +import net.torvald.terrarum.gamecontroller.EnumKeyFunc +import net.torvald.terrarum.gamecontroller.KeyMap +import org.dyn4j.geometry.Vector2 +import org.lwjgl.input.Controller +import org.lwjgl.input.Controllers +import org.newdawn.slick.GameContainer +import org.newdawn.slick.Input +import java.util.* + +/** + * Humanoid actor class to provide same controlling function (such as work, jump) + * + * Created by minjaesong on 16-10-24. + */ +open class ActorHumanoid(birth: GameDate, death: GameDate? = null) +: HistoricalFigure(birth, death), Controllable, Pocketed, Factionable, Luminous, LandHolder { + + /** Must be set by PlayerFactory */ + override var inventory: ActorInventory = ActorInventory() + + /** Must be set by PlayerFactory */ + override var faction: HashSet = HashSet() + /** + * Absolute tile index. index(x, y) = y * map.width + x + * The arraylist will be saved in JSON format with GSON. + */ + override var houseDesignation: ArrayList? = null + + override fun addHouseTile(x: Int, y: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun removeHouseTile(x: Int, y: Int) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun clearHouseDesignation() { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Recommended implementation: + * + override var luminosity: Int + get() = actorValue.getAsInt(AVKey.LUMINOSITY) ?: 0 + set(value) { + actorValue[AVKey.LUMINOSITY] = value + } + */ + override var luminosity: Int + get() = actorValue.getAsInt(AVKey.LUMINOSITY) ?: 0 + set(value) { + actorValue[AVKey.LUMINOSITY] = value + } + + /** + * Arguments: + * + * Hitbox(x-offset, y-offset, width, height) + * (Use ArrayList for normal circumstances) + */ + override val lightBoxList: List + get() = arrayOf(Hitbox(0.0, 0.0, hitbox.width, hitbox.height)).toList() // use getter; dimension of the player may change by time. + + @Transient val BASE_DENSITY = 980.0 + + companion object { + @Transient internal const val ACCEL_MULT_IN_FLIGHT: Double = 0.21 + @Transient internal const val WALK_ACCEL_BASE: Double = 0.67 + + @Transient const val BASE_HEIGHT = 40 + } + + //////////////////////////////// + // MOVEMENT RELATED FUNCTIONS // + //////////////////////////////// + + var gamepad: Controller? = null + var axisX = 0f + var axisY = 0f + var axisRX = 0f + var axisRY = 0f + + /** empirical value. */ + @Transient private val JUMP_ACCELERATION_MOD = 170.0 / 10000.0 //linear mode + @Transient private val WALK_FRAMES_TO_MAX_ACCEL = 6 + + @Transient private val LEFT = 1 + @Transient private val RIGHT = 2 + + @Transient private val KEY_NULL = -1 + + /** how long the jump button has down, in frames */ + internal var jumpCounter = 0 + internal var jumpAcc = 0.0 + /** how long the walk button has down, in frames */ + internal var walkCounterX = 0 + internal var walkCounterY = 0 + @Transient private val MAX_JUMP_LENGTH = 17 // use 17; in internal frames + + private var readonly_totalX = 0.0 + private var readonly_totalY = 0.0 + + internal var jumping = false + + internal var walkHeading: Int = 0 + + @Transient private var prevHMoveKey = KEY_NULL + @Transient private var prevVMoveKey = KEY_NULL + + internal var noClip = false + + @Transient private val AXIS_POSMAX = 1.0f + @Transient private val GAMEPAD_JUMP = 7 + + + override fun update(gc: GameContainer, delta: Int) { + super.update(gc, delta) + + updateMovementControl() + updateSprite(delta) + + if (noClip) { + grounded = true + } + } + + private fun updateMovementControl() { + if (!noClip) { + if (grounded) { + actorValue[AVKey.ACCELMULT] = 1.0 + } else { + actorValue[AVKey.ACCELMULT] = ACCEL_MULT_IN_FLIGHT + } + } else { + actorValue[AVKey.ACCELMULT] = 1.0 + } + } + + override fun processInput(input: Input) { + if (Terrarum.hasController) { + gamepad = Controllers.getController(0) + axisX = gamepad!!.getAxisValue(0) + axisY = gamepad!!.getAxisValue(1) + axisRX = gamepad!!.getAxisValue(2) + axisRY = gamepad!!.getAxisValue(3) + + // deadzonning + if (Math.abs(axisX) < Terrarum.CONTROLLER_DEADZONE) axisX = 0f + if (Math.abs(axisY) < Terrarum.CONTROLLER_DEADZONE) axisY = 0f + if (Math.abs(axisRX) < Terrarum.CONTROLLER_DEADZONE) axisRX = 0f + if (Math.abs(axisRY) < Terrarum.CONTROLLER_DEADZONE) axisRY = 0f + } + + /** + * L-R stop + */ + if (Terrarum.hasController && !isWalkingH) { + if (axisX == 0f) { + walkHStop() + } + } + // ↑F, ↑S + if (isWalkingH && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { + walkHStop() + prevHMoveKey = KEY_NULL + } + /** + * U-D stop + */ + if (Terrarum.hasController) { + if (axisY == 0f) { + walkVStop() + } + } + // ↑E + // ↑D + if (isNoClip() + && !isFuncDown(input, EnumKeyFunc.MOVE_UP) + && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { + walkVStop() + prevVMoveKey = KEY_NULL + } + + /** + * Left/Right movement + */ + + if (Terrarum.hasController) { + if (axisX != 0f) { + walkHorizontal(axisX < 0f, axisX.abs()) + } + } + // ↑F, ↓S + if (isFuncDown(input, EnumKeyFunc.MOVE_RIGHT) && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT)) { + walkHorizontal(false, AXIS_POSMAX) + prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT) + } // ↓F, ↑S + else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { + walkHorizontal(true, AXIS_POSMAX) + prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT) + } // ↓F, ↓S + /*else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { + if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) { + walkHorizontal(false, AXIS_POSMAX) + prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT) + } else if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)) { + walkHorizontal(true, AXIS_POSMAX) + prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT) + } + }*/ + + /** + * Up/Down movement + */ + if (noClip) { + if (Terrarum.hasController) { + if (axisY != 0f) { + walkVertical(axisY < 0, axisY.abs()) + } + } + // ↑E, ↓D + if (isFuncDown(input, EnumKeyFunc.MOVE_DOWN) && !isFuncDown(input, EnumKeyFunc.MOVE_UP)) { + walkVertical(false, AXIS_POSMAX) + prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN) + } // ↓E, ↑D + else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { + walkVertical(true, AXIS_POSMAX) + prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP) + } // ↓E, ↓D + /*else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { + if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) { + walkVertical(false, AXIS_POSMAX) + prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN) + } else if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)) { + walkVertical(true, AXIS_POSMAX) + prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP) + } + }*/ + } + + /** + * Jump control + */ + if (isFuncDown(input, EnumKeyFunc.JUMP) || Terrarum.hasController && gamepad!!.isButtonPressed(GAMEPAD_JUMP)) { + if (!noClip) { + if (grounded) { + jumping = true + } + jump() + } else { + walkVertical(true, AXIS_POSMAX) + } + } else { + jumping = false + jumpCounter = 0 + jumpAcc = 0.0 + } + + } + + override fun keyPressed(key: Int, c: Char) { + + } + + /** + * This code directly controls VELOCITY for walking, called walkX and walkY. + * + * In theory, we must add ACCELERATION to the velocity, but unfortunately it's arduous task + * with this simulation code base. + * + * Reason: we have naïve friction code that is not adaptive at all and to add proper walking code to + * this code base, ACCELERATION must be changed (in other words, we must deal with JERK) accordingly + * to the FRICTION. + * + * So I'm adding walkX/Y and getting the ActorWithBody.setNewNextHitbox to use the velocity value of + * walkX/Y + velocity, which is stored in variable moveDelta. + * + * Be warned. + * + * @param left (even if the game is joypad controlled, you must give valid value) + * @param absAxisVal (set AXIS_POSMAX if keyboard controlled) + * @author minjaesong + */ + private fun walkHorizontal(left: Boolean, absAxisVal: Float) { + if ((!walledLeft && left) || (!walledRight && !left)) { + readonly_totalX = + absMax( // keyboard + actorValue.getAsDouble(AVKey.ACCEL)!! * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) * + applyVelo(walkCounterX) * + (if (left) -1f else 1f) + , // gamepad + actorValue.getAsDouble(AVKey.ACCEL)!! * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) * + (if (left) -1f else 1f) * absAxisVal + // do not add applyVelo(walkCounterY) here, as it prevents player from moving with gamepad + ) + + //applyForce(Vector2(readonly_totalX, 0.0)) + walkX += readonly_totalX + walkX = absClamp(walkX, actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDMULT)!!) + + walkCounterX += 1 + + isWalkingH = true + } + + // Heading flag + if (left) + walkHeading = LEFT + else + walkHeading = RIGHT + } + + /** + + * @param up (even if the game is joypad controlled, you must give valid value) + * * + * @param absAxisVal (set AXIS_POSMAX if keyboard controlled) + */ + private fun walkVertical(up: Boolean, absAxisVal: Float) { + readonly_totalY = + absMax( // keyboard + actorValue.getAsDouble(AVKey.ACCEL)!! * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) * + applyVelo(walkCounterY) * + (if (up) -1f else 1f) + , // gamepad + actorValue.getAsDouble(AVKey.ACCEL)!! * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) * + (if (up) -1f else 1f) * absAxisVal + ) + + walkY += readonly_totalY + walkY = absClamp(walkY, actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDMULT)!!) + + walkCounterY += 1 + + isWalkingV = true + } + + private fun applyAccel(x: Int): Double { + return if (x < WALK_FRAMES_TO_MAX_ACCEL) + Math.sin(Math.PI * x / WALK_FRAMES_TO_MAX_ACCEL) + else 0.0 + } + + private fun applyVelo(x: Int): Double { + return if (x < WALK_FRAMES_TO_MAX_ACCEL) + 0.5 - 0.5 * Math.cos(Math.PI * x / WALK_FRAMES_TO_MAX_ACCEL) + else 1.0 + } + + // stops; let the friction kick in by doing nothing to the velocity here + private fun walkHStop() { + /*if (veloX > 0) { + veloX -= actorValue.getAsDouble(AVKey.ACCEL)!! * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) + + // compensate overshoot + if (veloX < 0) veloX = 0f + } else if (veloX < 0) { + veloX += actorValue.getAsDouble(AVKey.ACCEL)!! * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) + + // compensate overshoot + if (veloX > 0) veloX = 0f + } else { + veloX = 0f + }*/ + + //veloX = 0f + + walkCounterX = 0 + isWalkingH = false + } + + // stops; let the friction kick in by doing nothing to the velocity here + private fun walkVStop() { + /*if (veloY > 0) { + veloY -= WALK_STOP_ACCEL * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) + + // compensate overshoot + if (veloY < 0) + veloY = 0f + } else if (veloY < 0) { + veloY += WALK_STOP_ACCEL * + actorValue.getAsDouble(AVKey.ACCELMULT)!! * + Math.sqrt(scale) + + // compensate overshoot + if (veloY > 0) veloY = 0f + } else { + veloY = 0f + }*/ + + ///veloY = 0f + + walkCounterY = 0 + isWalkingV = false + } + + /** + * See ./work_files/Jump power by pressing time.gcx + * + * TODO linear function (play Super Mario Bros. and you'll get what I'm talking about) + */ + private fun jump() { + + val len = MAX_JUMP_LENGTH.toFloat() + val pwr = actorValue.getAsDouble(AVKey.JUMPPOWER)!! * (actorValue.getAsDouble(AVKey.JUMPPOWERMULT) ?: 1.0) + val jumpLinearThre = 0.08 + + fun jumpFunc(x: Int): Double { + if (x >= len) return 0.0 + val ret = pwr - 0.02 * x + + if (ret < jumpLinearThre) return jumpLinearThre + else return ret + } + + if (jumping) { + // increment jump counter + if (jumpCounter < len) jumpCounter += 1 + + // linear time mode + val init = (len + 1) / 2.0 + var timedJumpCharge = init - init / len * jumpCounter + if (timedJumpCharge < 0) timedJumpCharge = 0.0 + + // one that uses jumpFunc(x) + //val timedJumpCharge = jumpFunc(jumpCounter) + + jumpAcc = -pwr * timedJumpCharge * JUMP_ACCELERATION_MOD * Math.sqrt(scale) // positive value + + applyForce(Vector2(0.0, jumpAcc)) + } + + // for mob ai: + //super.setVeloY(veloY + // - + // pwr * Math.sqrt(scale) + //); + } + + private fun isFuncDown(input: Input, fn: EnumKeyFunc): Boolean { + return input.isKeyDown(KeyMap.getKeyCode(fn)) + } + + private fun absClamp(i: Double, ceil: Double): Double { + if (i > 0) + return if (i > ceil) ceil else i + else if (i < 0) + return if (-i > ceil) -ceil else i + else + return 0.0 + } + + fun isNoClip(): Boolean { + return noClip + } + + fun setNoClip(b: Boolean) { + noClip = b + } + + fun Float.abs() = FastMath.abs(this) + + private fun updateSprite(delta: Int) { + sprite!!.update(delta) + if (spriteGlow != null) { + spriteGlow!!.update(delta) + } + + if (grounded) { + if (walkHeading == LEFT) { + sprite!!.flip(true, false) + if (spriteGlow != null) { + spriteGlow!!.flip(true, false) + } + } else { + sprite!!.flip(false, false) + if (spriteGlow != null) { + spriteGlow!!.flip(false, false) + } + } + } + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameactors/AnyPlayer.kt b/src/net/torvald/terrarum/gameactors/AnyPlayer.kt index 3ea7a7108..ac76edd97 100644 --- a/src/net/torvald/terrarum/gameactors/AnyPlayer.kt +++ b/src/net/torvald/terrarum/gameactors/AnyPlayer.kt @@ -1,11 +1,14 @@ package net.torvald.terrarum.gameactors +import net.torvald.terrarum.console.ActorHumanoid import org.newdawn.slick.Input /** + * A wrapper to support instant player changing (or possessing other NPCs maybe) + * * Created by minjaesong on 16-10-23. */ -class AnyPlayer(val actor: HistoricalFigure) { +class AnyPlayer(val actor: ActorHumanoid) { init { if (actor !is Controllable) diff --git a/src/net/torvald/terrarum/gameactors/CreatureRawInjector.kt b/src/net/torvald/terrarum/gameactors/CreatureRawInjector.kt index 53a57d441..8bef645d7 100644 --- a/src/net/torvald/terrarum/gameactors/CreatureRawInjector.kt +++ b/src/net/torvald/terrarum/gameactors/CreatureRawInjector.kt @@ -4,6 +4,7 @@ import net.torvald.JsonFetcher import net.torvald.random.Fudge3 import net.torvald.terrarum.langpack.Lang import com.google.gson.JsonObject +import net.torvald.terrarum.console.ActorHumanoid import org.newdawn.slick.SlickException import java.io.IOException import java.security.SecureRandom @@ -40,7 +41,7 @@ object CreatureRawInjector { // setAVMultiplyFromOne(actorValueRef, elementsMultiplyFromOne, jsonObj) setAVBooleans(actorValueRef, elementsBoolean, jsonObj) - actorValueRef[AVKey.ACCEL] = Player.WALK_ACCEL_BASE + actorValueRef[AVKey.ACCEL] = ActorHumanoid.WALK_ACCEL_BASE actorValueRef[AVKey.ACCELMULT] = 1.0 } diff --git a/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt b/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt index 9253c2e01..6392a0172 100644 --- a/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt +++ b/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt @@ -11,7 +11,7 @@ import org.newdawn.slick.Input * * Created by minjaesong on 16-10-10. */ -open class HistoricalFigure(val born: GameDate, val dead: GameDate? = null) : ActorWithBody(), Controllable { +open class HistoricalFigure(val born: GameDate, val dead: GameDate? = null) : ActorWithBody() { init { this.actorValue["_bornyear"] = born.year @@ -23,21 +23,6 @@ open class HistoricalFigure(val born: GameDate, val dead: GameDate? = null) : Ac } } - override fun processInput(input: Input) { - // let out children play - } - - override fun keyPressed(key: Int, c: Char) { - // let out children play - } - - open internal var noClip = false - - open fun isNoClip() = noClip - - open fun setNoClip(b: Boolean) { - noClip = b - } } data class GameDate(val year: Int, val yearlyDay: Int) { diff --git a/src/net/torvald/terrarum/gameactors/PBCynthia.kt b/src/net/torvald/terrarum/gameactors/PBCynthia.kt index 56bc1b5c9..a6b26d91a 100644 --- a/src/net/torvald/terrarum/gameactors/PBCynthia.kt +++ b/src/net/torvald/terrarum/gameactors/PBCynthia.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.gameactors import net.torvald.spriteanimation.SpriteAnimation +import net.torvald.terrarum.console.ActorHumanoid import net.torvald.terrarum.mapdrawer.MapDrawer /** @@ -23,7 +24,7 @@ object PBCynthia { p.sprite!!.setRowsAndFrames(1, 1) p.sprite!!.setAsVisible() - p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: Player.BASE_HEIGHT, 9, 0) + p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 9, 0) p.setPosition((4096 * MapDrawer.TILE_SIZE).toDouble(), (300 * 16).toDouble()) diff --git a/src/net/torvald/terrarum/gameactors/PBSigrid.kt b/src/net/torvald/terrarum/gameactors/PBSigrid.kt index 8e6667274..6b99ca640 100644 --- a/src/net/torvald/terrarum/gameactors/PBSigrid.kt +++ b/src/net/torvald/terrarum/gameactors/PBSigrid.kt @@ -4,6 +4,7 @@ import net.torvald.JsonFetcher import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.spriteanimation.SpriteAnimation import com.google.gson.JsonObject +import net.torvald.terrarum.console.ActorHumanoid import net.torvald.terrarum.gameactors.faction.FactionFactory import net.torvald.terrarum.mapdrawer.MapDrawer import org.newdawn.slick.SlickException @@ -36,7 +37,7 @@ object PBSigrid { p.actorValue[AVKey.SCALE] = 1.0 p.actorValue[AVKey.SPEED] = 4.0 p.actorValue[AVKey.SPEEDMULT] = 1.0 - p.actorValue[AVKey.ACCEL] = Player.WALK_ACCEL_BASE + p.actorValue[AVKey.ACCEL] = ActorHumanoid.WALK_ACCEL_BASE p.actorValue[AVKey.ACCELMULT] = 1.0 p.actorValue[AVKey.JUMPPOWER] = 5.0 diff --git a/src/net/torvald/terrarum/gameactors/Player.kt b/src/net/torvald/terrarum/gameactors/Player.kt index 8a1b6d1d1..c5adf8030 100644 --- a/src/net/torvald/terrarum/gameactors/Player.kt +++ b/src/net/torvald/terrarum/gameactors/Player.kt @@ -6,6 +6,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.console.ActorHumanoid import net.torvald.terrarum.ui.UIQuickBar import org.dyn4j.geometry.Vector2 import org.lwjgl.input.Controller @@ -21,78 +22,14 @@ import java.util.* * Created by minjaesong on 16-03-14. */ -class Player(born: GameDate) : HistoricalFigure(born), Controllable, Pocketed, Factionable, Luminous, LandHolder { - - /** - * empirical value. - */ - @Transient private val JUMP_ACCELERATION_MOD = 170.0 / 10000.0 //linear mode - @Transient private val WALK_FRAMES_TO_MAX_ACCEL = 6 - - @Transient private val LEFT = 1 - @Transient private val RIGHT = 2 - - @Transient private val KEY_NULL = -1 - +class Player(born: GameDate) : ActorHumanoid(born) { var vehicleRiding: Controllable? = null - /** how long the jump button has down, in frames */ - internal var jumpCounter = 0 - internal var jumpAcc = 0.0 - /** how long the walk button has down, in frames */ - internal var walkCounterX = 0 - internal var walkCounterY = 0 - @Transient private val MAX_JUMP_LENGTH = 17 // use 17; in internal frames - - private var readonly_totalX = 0.0 - private var readonly_totalY = 0.0 - - internal var jumping = false - - internal var walkHeading: Int = 0 - - @Transient private var prevHMoveKey = KEY_NULL - @Transient private var prevVMoveKey = KEY_NULL - - override internal var noClip = false - - @Transient private val AXIS_POSMAX = 1.0f - @Transient private val GAMEPAD_JUMP = 7 - - @Transient private val TSIZE = MapDrawer.TILE_SIZE - - @Transient private val BASE_DENSITY = 980.0 - - /** 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() - - override var houseDesignation: ArrayList? = null - - override var luminosity: Int - get() = actorValue.getAsInt(AVKey.LUMINOSITY) ?: 0 - set(value) { - actorValue[AVKey.LUMINOSITY] = value - } - override val lightBoxList: List - get() = arrayOf(Hitbox(0.0, 0.0, hitbox.width, hitbox.height)).toList() // use getter; dimension of the player may change by time. - - var gamepad: Controller? = null - var axisX = 0f - var axisY = 0f - var axisRX = 0f - var axisRY = 0f - companion object { - @Transient internal const val ACCEL_MULT_IN_FLIGHT: Double = 0.21 - @Transient internal const val WALK_ACCEL_BASE: Double = 0.67 - @Transient const val PLAYER_REF_ID: Int = 0x51621D - @Transient const val BASE_HEIGHT = 40 } /** @@ -118,400 +55,11 @@ class Player(born: GameDate) : HistoricalFigure(born), Controllable, Pocketed, F override fun update(gc: GameContainer, delta: Int) { if (vehicleRiding is Player) - throw RuntimeException("Attempted to 'ride' " + "player object.") + throw Error("Attempted to 'ride' player object. ($vehicleRiding)") + if (vehicleRiding != null && (vehicleRiding == this)) + throw Error("Attempted to 'ride' itself. ($vehicleRiding)") super.update(gc, delta) - - updateSprite(delta) - - updateMovementControl() - - if (noClip) { - grounded = true - } - } - /** - * This code directly controls VELOCITY for walking, called walkX and walkY. - * - * In theory, we must add ACCELERATION to the velocity, but unfortunately it's arduous task - * with this simulation code base. - * - * Reason: we have naïve friction code that is not adaptive at all and to add proper walking code to - * this code base, ACCELERATION must be changed (in other words, we must deal with JERK) accordingly - * to the FRICTION. - * - * So I'm adding walkX/Y and getting the ActorWithBody.setNewNextHitbox to use the velocity value of - * walkX/Y + velocity, which is stored in variable moveDelta. - * - * Be warned. - * - * @param left (even if the game is joypad controlled, you must give valid value) - * @param absAxisVal (set AXIS_POSMAX if keyboard controlled) - * @author minjaesong - */ - private fun walkHorizontal(left: Boolean, absAxisVal: Float) { - if ((!walledLeft && left) || (!walledRight && !left)) { - readonly_totalX = - absMax( // keyboard - actorValue.getAsDouble(AVKey.ACCEL)!! * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) * - applyVelo(walkCounterX) * - (if (left) -1f else 1f) - , // gamepad - actorValue.getAsDouble(AVKey.ACCEL)!! * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) * - (if (left) -1f else 1f) * absAxisVal - // do not add applyVelo(walkCounterY) here, as it prevents player from moving with gamepad - ) - - //applyForce(Vector2(readonly_totalX, 0.0)) - walkX += readonly_totalX - walkX = absClamp(walkX, actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDMULT)!!) - - walkCounterX += 1 - - isWalkingH = true - } - - // Heading flag - if (left) - walkHeading = LEFT - else - walkHeading = RIGHT - } - - /** - - * @param up (even if the game is joypad controlled, you must give valid value) - * * - * @param absAxisVal (set AXIS_POSMAX if keyboard controlled) - */ - private fun walkVertical(up: Boolean, absAxisVal: Float) { - readonly_totalY = - absMax( // keyboard - actorValue.getAsDouble(AVKey.ACCEL)!! * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) * - applyVelo(walkCounterY) * - (if (up) -1f else 1f) - , // gamepad - actorValue.getAsDouble(AVKey.ACCEL)!! * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) * - (if (up) -1f else 1f) * absAxisVal - ) - - walkY += readonly_totalY - walkY = absClamp(walkY, actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDMULT)!!) - - walkCounterY += 1 - - isWalkingV = true - } - - private fun applyAccel(x: Int): Double { - return if (x < WALK_FRAMES_TO_MAX_ACCEL) - Math.sin(Math.PI * x / WALK_FRAMES_TO_MAX_ACCEL) - else 0.0 - } - - private fun applyVelo(x: Int): Double { - return if (x < WALK_FRAMES_TO_MAX_ACCEL) - 0.5 - 0.5 * Math.cos(Math.PI * x / WALK_FRAMES_TO_MAX_ACCEL) - else 1.0 - } - - // stops; let the friction kick in by doing nothing to the velocity here - private fun walkHStop() { - /*if (veloX > 0) { - veloX -= actorValue.getAsDouble(AVKey.ACCEL)!! * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) - - // compensate overshoot - if (veloX < 0) veloX = 0f - } else if (veloX < 0) { - veloX += actorValue.getAsDouble(AVKey.ACCEL)!! * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) - - // compensate overshoot - if (veloX > 0) veloX = 0f - } else { - veloX = 0f - }*/ - - //veloX = 0f - - walkCounterX = 0 - isWalkingH = false - } - - // stops; let the friction kick in by doing nothing to the velocity here - private fun walkVStop() { - /*if (veloY > 0) { - veloY -= WALK_STOP_ACCEL * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) - - // compensate overshoot - if (veloY < 0) - veloY = 0f - } else if (veloY < 0) { - veloY += WALK_STOP_ACCEL * - actorValue.getAsDouble(AVKey.ACCELMULT)!! * - Math.sqrt(scale) - - // compensate overshoot - if (veloY > 0) veloY = 0f - } else { - veloY = 0f - }*/ - - ///veloY = 0f - - walkCounterY = 0 - isWalkingV = false - } - - /** - * See ./work_files/Jump power by pressing time.gcx - * - * TODO linear function (play Super Mario Bros. and you'll get what I'm talking about) - */ - private fun jump() { - - val len = MAX_JUMP_LENGTH.toFloat() - val pwr = actorValue.getAsDouble(AVKey.JUMPPOWER)!! * (actorValue.getAsDouble(AVKey.JUMPPOWERMULT) ?: 1.0) - val jumpLinearThre = 0.08 - - fun jumpFunc(x: Int): Double { - if (x >= len) return 0.0 - val ret = pwr - 0.02 * x - - if (ret < jumpLinearThre) return jumpLinearThre - else return ret - } - - if (jumping) { - // increment jump counter - if (jumpCounter < len) jumpCounter += 1 - - // linear time mode - val init = (len + 1) / 2.0 - var timedJumpCharge = init - init / len * jumpCounter - if (timedJumpCharge < 0) timedJumpCharge = 0.0 - - // one that uses jumpFunc(x) - //val timedJumpCharge = jumpFunc(jumpCounter) - - jumpAcc = -pwr * timedJumpCharge * JUMP_ACCELERATION_MOD * Math.sqrt(scale) // positive value - - applyForce(Vector2(0.0, jumpAcc)) - } - - // for mob ai: - //super.setVeloY(veloY - // - - // pwr * Math.sqrt(scale) - //); - } - - private fun updateMovementControl() { - if (!noClip) { - if (grounded) { - actorValue[AVKey.ACCELMULT] = 1.0 - } else { - actorValue[AVKey.ACCELMULT] = ACCEL_MULT_IN_FLIGHT - } - } else { - actorValue[AVKey.ACCELMULT] = 1.0 - } - } - - override fun processInput(input: Input) { - if (Terrarum.hasController) { - gamepad = Controllers.getController(0) - axisX = gamepad!!.getAxisValue(0) - axisY = gamepad!!.getAxisValue(1) - axisRX = gamepad!!.getAxisValue(2) - axisRY = gamepad!!.getAxisValue(3) - - // deadzonning - if (Math.abs(axisX) < Terrarum.CONTROLLER_DEADZONE) axisX = 0f - if (Math.abs(axisY) < Terrarum.CONTROLLER_DEADZONE) axisY = 0f - if (Math.abs(axisRX) < Terrarum.CONTROLLER_DEADZONE) axisRX = 0f - if (Math.abs(axisRY) < Terrarum.CONTROLLER_DEADZONE) axisRY = 0f - } - - /** - * L-R stop - */ - if (Terrarum.hasController && !isWalkingH) { - if (axisX == 0f) { - walkHStop() - } - } - // ↑F, ↑S - if (isWalkingH && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { - walkHStop() - prevHMoveKey = KEY_NULL - } - /** - * U-D stop - */ - if (Terrarum.hasController) { - if (axisY == 0f) { - walkVStop() - } - } - // ↑E - // ↑D - if (isNoClip() - && !isFuncDown(input, EnumKeyFunc.MOVE_UP) - && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { - walkVStop() - prevVMoveKey = KEY_NULL - } - - /** - * Left/Right movement - */ - - if (Terrarum.hasController) { - if (axisX != 0f) { - walkHorizontal(axisX < 0f, axisX.abs()) - } - } - // ↑F, ↓S - if (isFuncDown(input, EnumKeyFunc.MOVE_RIGHT) && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT)) { - walkHorizontal(false, AXIS_POSMAX) - prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT) - } // ↓F, ↑S - else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { - walkHorizontal(true, AXIS_POSMAX) - prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT) - } // ↓F, ↓S - /*else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { - if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) { - walkHorizontal(false, AXIS_POSMAX) - prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT) - } else if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)) { - walkHorizontal(true, AXIS_POSMAX) - prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT) - } - }*/ - - /** - * Up/Down movement - */ - if (noClip) { - if (Terrarum.hasController) { - if (axisY != 0f) { - walkVertical(axisY < 0, axisY.abs()) - } - } - // ↑E, ↓D - if (isFuncDown(input, EnumKeyFunc.MOVE_DOWN) && !isFuncDown(input, EnumKeyFunc.MOVE_UP)) { - walkVertical(false, AXIS_POSMAX) - prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN) - } // ↓E, ↑D - else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { - walkVertical(true, AXIS_POSMAX) - prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP) - } // ↓E, ↓D - /*else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { - if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) { - walkVertical(false, AXIS_POSMAX) - prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN) - } else if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)) { - walkVertical(true, AXIS_POSMAX) - prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP) - } - }*/ - } - - /** - * Jump control - */ - if (isFuncDown(input, EnumKeyFunc.JUMP) || Terrarum.hasController && gamepad!!.isButtonPressed(GAMEPAD_JUMP)) { - if (!noClip) { - if (grounded) { - jumping = true - } - jump() - } else { - walkVertical(true, AXIS_POSMAX) - } - } else { - jumping = false - jumpCounter = 0 - jumpAcc = 0.0 - } - - } - - override fun keyPressed(key: Int, c: Char) { - - } - - private fun isFuncDown(input: Input, fn: EnumKeyFunc): Boolean { - return input.isKeyDown(KeyMap.getKeyCode(fn)) - } - - private fun absClamp(i: Double, ceil: Double): Double { - if (i > 0) - return if (i > ceil) ceil else i - else if (i < 0) - return if (-i > ceil) -ceil else i - else - return 0.0 - } - - private fun updateSprite(delta_t: Int) { - sprite!!.update(delta_t) - if (spriteGlow != null) { - spriteGlow!!.update(delta_t) - } - - if (grounded) { - if (walkHeading == LEFT) { - sprite!!.flip(true, false) - if (spriteGlow != null) { - spriteGlow!!.flip(true, false) - } - } else { - sprite!!.flip(false, false) - if (spriteGlow != null) { - spriteGlow!!.flip(false, false) - } - } - } - } - - override fun isNoClip(): Boolean { - return noClip - } - - override fun setNoClip(b: Boolean) { - noClip = b - } - - override fun addHouseTile(x: Int, y: Int) { - throw UnsupportedOperationException() - } - - override fun removeHouseTile(x: Int, y: Int) { - throw UnsupportedOperationException() - } - - override fun clearHouseDesignation() { - throw UnsupportedOperationException() - } - - fun Float.abs() = FastMath.abs(this) } \ No newline at end of file