mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-17 05:54:05 +09:00
API: AI control on HumanoidNPC
Former-commit-id: ca8fe2f23f6d1bc36f86445017f9a3fdfe6fa2de Former-commit-id: a0f3233a44a79b4f5bbc9f0c8f19b71df11bedd6
This commit is contained in:
@@ -1,12 +1,21 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.terrarum.gameactors.ai.ActorAI
|
||||
|
||||
/**
|
||||
* Note: AI-controlled actor must be 'Controllable'
|
||||
*
|
||||
* Created by minjaesong on 16-03-14.
|
||||
* Created by minjaesong on 16-01-31.
|
||||
*/
|
||||
interface AIControlled {
|
||||
val scriptPath: String
|
||||
|
||||
fun moveLeft()
|
||||
fun moveRight()
|
||||
fun moveUp()
|
||||
fun moveDown()
|
||||
fun moveJump()
|
||||
|
||||
/** fly toward arbitrary angle WARNING: the map is looped! */
|
||||
fun moveTo(bearing: Double)
|
||||
/** fly toward arbitrary coord WARNING: the map is looped! */
|
||||
fun moveTo(toX: Double, toY: Double)
|
||||
}
|
||||
492
src/net/torvald/terrarum/gameactors/ActorHumanoid.kt
Normal file
492
src/net/torvald/terrarum/gameactors/ActorHumanoid.kt
Normal file
@@ -0,0 +1,492 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
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 net.torvald.terrarum.realestate.RealEstateUtility
|
||||
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<Faction> = 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<Long>? = ArrayList()
|
||||
|
||||
override fun addHouseTile(x: Int, y: Int) {
|
||||
if (houseDesignation != null) houseDesignation!!.add(RealEstateUtility.getAbsoluteTileNumber(x, y))
|
||||
}
|
||||
|
||||
override fun removeHouseTile(x: Int, y: Int) {
|
||||
if (houseDesignation != null) houseDesignation!!.remove(RealEstateUtility.getAbsoluteTileNumber(x, y))
|
||||
}
|
||||
|
||||
override fun clearHouseDesignation() {
|
||||
if (houseDesignation != null) houseDesignation!!.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Hitbox>
|
||||
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
|
||||
|
||||
protected var isUpDown = false
|
||||
protected var isDownDown = false
|
||||
protected var isLeftDown = false
|
||||
protected var isRightDown = false
|
||||
protected var isJumpDown = false
|
||||
protected val isGamer: Boolean
|
||||
get() = this is Player
|
||||
|
||||
|
||||
override fun update(gc: GameContainer, delta: Int) {
|
||||
super.update(gc, delta)
|
||||
|
||||
// don't put this into keyPressed; execution order is important!
|
||||
updateGamerControlBox(gc.input)
|
||||
|
||||
updateMovementControl()
|
||||
updateSprite(delta)
|
||||
|
||||
if (noClip) {
|
||||
grounded = true
|
||||
}
|
||||
|
||||
// reset control box of AI
|
||||
if (!isGamer) {
|
||||
isUpDown = false
|
||||
isDownDown = false
|
||||
isLeftDown = false
|
||||
isRightDown = false
|
||||
isJumpDown = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateGamerControlBox(input: Input) {
|
||||
if (isGamer) {
|
||||
isUpDown = isFuncDown(input, EnumKeyFunc.MOVE_UP)
|
||||
isLeftDown = isFuncDown(input, EnumKeyFunc.MOVE_LEFT)
|
||||
isDownDown = isFuncDown(input, EnumKeyFunc.MOVE_DOWN)
|
||||
isRightDown = isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)
|
||||
isJumpDown = isFuncDown(input, EnumKeyFunc.JUMP)
|
||||
}
|
||||
}
|
||||
|
||||
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 (isGamer && 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 (isGamer && Terrarum.hasController && !isWalkingH) {
|
||||
if (axisX == 0f) {
|
||||
walkHStop()
|
||||
}
|
||||
}
|
||||
// ↑F, ↑S
|
||||
if (isWalkingH && !isLeftDown && !isRightDown) {
|
||||
walkHStop()
|
||||
prevHMoveKey = KEY_NULL
|
||||
}
|
||||
/**
|
||||
* U-D stop
|
||||
*/
|
||||
if (isGamer && Terrarum.hasController) {
|
||||
if (axisY == 0f) {
|
||||
walkVStop()
|
||||
}
|
||||
}
|
||||
// ↑E
|
||||
// ↑D
|
||||
if (isNoClip() && !isUpDown && !isDownDown) {
|
||||
walkVStop()
|
||||
prevVMoveKey = KEY_NULL
|
||||
}
|
||||
|
||||
/**
|
||||
* Left/Right movement
|
||||
*/
|
||||
|
||||
if (isGamer && Terrarum.hasController) {
|
||||
if (axisX != 0f) {
|
||||
walkHorizontal(axisX < 0f, axisX.abs())
|
||||
}
|
||||
}
|
||||
// ↑F, ↓S
|
||||
if (isRightDown && !isLeftDown) {
|
||||
walkHorizontal(false, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)
|
||||
} // ↓F, ↑S
|
||||
else if (isLeftDown && !isRightDown) {
|
||||
walkHorizontal(true, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)
|
||||
} // ↓F, ↓S
|
||||
/*else if (isLeftDown && isRightDown) {
|
||||
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 (isGamer && Terrarum.hasController) {
|
||||
if (axisY != 0f) {
|
||||
walkVertical(axisY < 0, axisY.abs())
|
||||
}
|
||||
}
|
||||
// ↑E, ↓D
|
||||
if (isDownDown && !isUpDown) {
|
||||
walkVertical(false, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)
|
||||
} // ↓E, ↑D
|
||||
else if (isUpDown && !isDownDown) {
|
||||
walkVertical(true, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)
|
||||
} // ↓E, ↓D
|
||||
/*else if (isUpDown && isDownDown) {
|
||||
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 (isJumpDown || isGamer && 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() {
|
||||
walkCounterX = 0
|
||||
isWalkingH = false
|
||||
}
|
||||
|
||||
// stops; let the friction kick in by doing nothing to the velocity here
|
||||
private fun walkVStop() {
|
||||
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) {
|
||||
if (isGamer) { // jump power increases as the gamer hits JUMP longer time
|
||||
// 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))
|
||||
}
|
||||
else { // no such minute control for AIs
|
||||
//veloY -= pwr * Math.sqrt(scale)
|
||||
jumpAcc = -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,10 +51,10 @@ open class ActorWithBody : Actor(), Visible {
|
||||
internal val velocity = Vector2(0.0, 0.0)
|
||||
var veloX: Double
|
||||
get() = velocity.x
|
||||
private set(value) { velocity.x = value }
|
||||
protected set(value) { velocity.x = value }
|
||||
var veloY: Double
|
||||
get() = velocity.y
|
||||
private set(value) { velocity.y = value }
|
||||
protected set(value) { velocity.y = value }
|
||||
|
||||
val moveDelta = Vector2(0.0, 0.0)
|
||||
@Transient private val VELO_HARD_LIMIT = 100.0
|
||||
@@ -65,10 +65,10 @@ open class ActorWithBody : Actor(), Visible {
|
||||
var controllerVel: Vector2? = if (this is Controllable) Vector2() else null
|
||||
var walkX: Double
|
||||
get() = controllerVel!!.x
|
||||
internal set(value) { controllerVel!!.x = value }
|
||||
protected set(value) { controllerVel!!.x = value }
|
||||
var walkY: Double
|
||||
get() = controllerVel!!.y
|
||||
internal set(value) { controllerVel!!.y = value }
|
||||
protected set(value) { controllerVel!!.y = value }
|
||||
|
||||
/**
|
||||
* Physical properties.
|
||||
|
||||
@@ -106,6 +106,38 @@ open class HumanoidNPC(aiFile: String, born: GameDate) : ActorHumanoid(born), AI
|
||||
|
||||
}
|
||||
|
||||
override fun moveLeft() { // hit the buttons on the controller box
|
||||
isLeftDown = true
|
||||
}
|
||||
|
||||
override fun moveRight() { // hit the buttons on the controller box
|
||||
isRightDown = true
|
||||
}
|
||||
|
||||
override fun moveUp() { // hit the buttons on the controller box
|
||||
isUpDown = true
|
||||
}
|
||||
|
||||
override fun moveDown() { // hit the buttons on the controller box
|
||||
isDownDown = true
|
||||
}
|
||||
|
||||
override fun moveJump() { // hit the buttons on the controller box
|
||||
isJumpDown = true
|
||||
}
|
||||
|
||||
/** fly toward arbitrary angle WARNING: the map is looped! */
|
||||
override fun moveTo(bearing: Double) {
|
||||
// if your NPC should fly, override this
|
||||
throw UnsupportedOperationException("Humans cannot fly :p")
|
||||
}
|
||||
|
||||
/** fly toward arbitrary coord WARNING: the map is looped! */
|
||||
override fun moveTo(toX: Double, toY: Double) {
|
||||
// if your NPC should fly, override this
|
||||
throw UnsupportedOperationException("Humans cannot fly :p")
|
||||
}
|
||||
|
||||
var currentExecutionThread = Thread()
|
||||
var threadRun = false
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class Player(born: GameDate) : ActorHumanoid(born) {
|
||||
internal val quickBarRegistration = IntArray(UIQuickBar.SLOT_COUNT, { -1 })
|
||||
|
||||
companion object {
|
||||
@Transient const val PLAYER_REF_ID: Int = 0x51621D
|
||||
@Transient const val PLAYER_REF_ID: Int = 0x91A7E2
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.torvald.terrarum.mapdrawer.MapDrawer
|
||||
/**
|
||||
* Created by minjaesong on 16-03-25.
|
||||
*/
|
||||
object PBCynthia {
|
||||
object PlayerBuilderCynthia {
|
||||
|
||||
fun create(): Player {
|
||||
val p: Player = Player(GameDate(100, 143)) // random value thrown
|
||||
@@ -14,11 +14,13 @@ import java.io.IOException
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
|
||||
object PBSigrid {
|
||||
object PlayerBuilderSigrid {
|
||||
|
||||
fun create(): Player {
|
||||
val p = Player(GameDate(-2147483648, 0)) // XD
|
||||
|
||||
p.referenceID = 0x51621D // the only constant of this procedural universe
|
||||
|
||||
p.sprite = SpriteAnimation()
|
||||
p.sprite!!.setDimension(28, 51)
|
||||
p.sprite!!.setSpriteImage("assets/graphics/sprites/test_player.png")
|
||||
@@ -1,9 +1,6 @@
|
||||
package net.torvald.terrarum.gameactors.ai
|
||||
|
||||
import net.torvald.colourutil.CIELab
|
||||
import net.torvald.colourutil.CIELabUtil
|
||||
import net.torvald.colourutil.RGB
|
||||
import net.torvald.colourutil.toLab
|
||||
import net.torvald.terrarum.gameactors.AIControlled
|
||||
import net.torvald.terrarum.gameactors.AVKey
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||
import net.torvald.terrarum.mapdrawer.LightmapRenderer
|
||||
@@ -19,12 +16,20 @@ import org.luaj.vm2.lib.ZeroArgFunction
|
||||
internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
|
||||
|
||||
init {
|
||||
if (actor !is AIControlled) throw IllegalArgumentException("The actor is not AIControlled! $actor")
|
||||
|
||||
// load things. WARNING: THIS IS MANUAL!
|
||||
g["ai"] = LuaValue.tableOf()
|
||||
g["ai"]["getNearestActor"] = GetNearestActor()
|
||||
g["ai"]["getNearestPlayer"] = GetNearestPlayer()
|
||||
g["ai"]["getX"] = GetX(actor)
|
||||
g["ai"]["getY"] = GetY(actor)
|
||||
g["ai"]["moveUp"] = MoveUp(actor)
|
||||
g["ai"]["moveDown"] = MoveDown(actor)
|
||||
g["ai"]["moveLeft"] = MoveLeft(actor)
|
||||
g["ai"]["moveRight"] = MoveRight(actor)
|
||||
g["ai"]["moveTo"] = MoveTo(actor)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -42,12 +47,13 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
|
||||
t["height"] = actor.hitbox.height
|
||||
|
||||
val lumrgb: Int = actor.actorValue.getAsInt(AVKey.LUMINOSITY) ?: 0
|
||||
val MUL_2 = LightmapRenderer.MUL_2
|
||||
val MUL = LightmapRenderer.MUL
|
||||
val CHMAX = LightmapRenderer.CHANNEL_MAX
|
||||
t["luminosity_rgb"] = lumrgb
|
||||
t["luminosity"] = RGB( // perceived luminosity
|
||||
lumrgb.div(LightmapRenderer.MUL_2).mod(LightmapRenderer.MUL) / LightmapRenderer.CHANNEL_MAX_FLOAT,
|
||||
lumrgb.div(LightmapRenderer.MUL_2).mod(LightmapRenderer.MUL) / LightmapRenderer.CHANNEL_MAX_FLOAT,
|
||||
lumrgb.div(LightmapRenderer.MUL_2).mod(LightmapRenderer.MUL) / LightmapRenderer.CHANNEL_MAX_FLOAT
|
||||
).toLab().L.div(100.0)
|
||||
t["luminosity"] = (lumrgb.div(MUL_2).and(CHMAX).times(3) +
|
||||
lumrgb.div(MUL).and(CHMAX).times(4) +
|
||||
lumrgb.and(1023)) / 8
|
||||
|
||||
return t
|
||||
}
|
||||
@@ -95,4 +101,44 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
|
||||
}
|
||||
}
|
||||
|
||||
class MoveLeft(val actor: AIControlled) : ZeroArgFunction() {
|
||||
override fun call(): LuaValue {
|
||||
actor.moveLeft()
|
||||
return LuaValue.NONE
|
||||
}
|
||||
}
|
||||
|
||||
class MoveRight(val actor: AIControlled) : ZeroArgFunction() {
|
||||
override fun call(): LuaValue {
|
||||
actor.moveRight()
|
||||
return LuaValue.NONE
|
||||
}
|
||||
}
|
||||
|
||||
class MoveUp(val actor: AIControlled) : ZeroArgFunction() {
|
||||
override fun call(): LuaValue {
|
||||
actor.moveUp()
|
||||
return LuaValue.NONE
|
||||
}
|
||||
}
|
||||
|
||||
class MoveDown(val actor: AIControlled) : ZeroArgFunction() {
|
||||
override fun call(): LuaValue {
|
||||
actor.moveDown()
|
||||
return LuaValue.NONE
|
||||
}
|
||||
}
|
||||
|
||||
class MoveTo(val actor: AIControlled) : LuaFunction() {
|
||||
override fun call(bearing: LuaValue): LuaValue {
|
||||
actor.moveTo(bearing.checkdouble())
|
||||
return LuaValue.NONE
|
||||
}
|
||||
|
||||
override fun call(toX: LuaValue, toY: LuaValue): LuaValue {
|
||||
actor.moveTo(toX.checkdouble(), toY.checkdouble())
|
||||
return LuaValue.NONE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user