PathController, PathFinder Actor movement controller Lua bindings Game loading no longer block Universe thread, more efficient registry population synchronization Environmental status effects now can be stat modifiers
329 lines
13 KiB
Kotlin
329 lines
13 KiB
Kotlin
package ru.dbotthepony.kstarbound.lua.bindings
|
|
|
|
import org.classdump.luna.Table
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.defs.actor.ActorMovementModifiers
|
|
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
|
import ru.dbotthepony.kstarbound.fromJsonFast
|
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
import ru.dbotthepony.kstarbound.lua.from
|
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
|
import ru.dbotthepony.kstarbound.lua.set
|
|
import ru.dbotthepony.kstarbound.lua.tableFrom
|
|
import ru.dbotthepony.kstarbound.lua.tableOf
|
|
import ru.dbotthepony.kstarbound.lua.toByteString
|
|
import ru.dbotthepony.kstarbound.lua.toJson
|
|
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
|
import ru.dbotthepony.kstarbound.lua.toVector2d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
import ru.dbotthepony.kstarbound.world.Direction
|
|
import ru.dbotthepony.kstarbound.world.entities.ActorMovementController
|
|
import ru.dbotthepony.kstarbound.world.entities.AnchorState
|
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
|
import kotlin.math.PI
|
|
|
|
class MovementControllerBindings(val self: ActorMovementController) {
|
|
fun init(lua: LuaEnvironment) {
|
|
val callbacks = lua.newTable()
|
|
lua.globals["mcontroller"] = callbacks
|
|
|
|
// pass-through
|
|
callbacks["mass"] = luaFunction {
|
|
returnBuffer.setTo(self.mass)
|
|
}
|
|
|
|
callbacks["localBoundBox"] = luaFunction {
|
|
returnBuffer.setTo(from(self.computeLocalCollisionAABB()))
|
|
}
|
|
|
|
callbacks["boundBox"] = luaFunction {
|
|
returnBuffer.setTo(from(self.computeLocalCollisionAABB()))
|
|
}
|
|
|
|
callbacks["collisionBoundBox"] = luaFunction {
|
|
returnBuffer.setTo(from(self.computeGlobalCollisionAABB()))
|
|
}
|
|
|
|
callbacks["collisionPoly"] = luaFunction {
|
|
returnBuffer.setTo(from(self.movementParameters.collisionPoly?.map({ it }, { it.firstOrNull() }) ?: Poly.EMPTY))
|
|
}
|
|
|
|
callbacks["collisionPolies"] = luaFunction {
|
|
returnBuffer.setTo(tableFrom((self.movementParameters.collisionPoly?.map({ listOf(it) }, { it }) ?: listOf(Poly.EMPTY)).map { from(it) }))
|
|
}
|
|
|
|
callbacks["collisionBody"] = luaFunction {
|
|
returnBuffer.setTo(from(self.computeGlobalHitboxes().firstOrNull() ?: Poly.EMPTY))
|
|
}
|
|
|
|
callbacks["collisionBodies"] = luaFunction {
|
|
returnBuffer.setTo(tableFrom(self.computeGlobalHitboxes().map { from(it) }))
|
|
}
|
|
|
|
callbacks["position"] = luaFunction { returnBuffer.setTo(tableOf(self.xPosition, self.yPosition)) }
|
|
callbacks["xPosition"] = luaFunction { returnBuffer.setTo(self.xPosition) }
|
|
callbacks["yPosition"] = luaFunction { returnBuffer.setTo(self.yPosition) }
|
|
callbacks["velocity"] = luaFunction { returnBuffer.setTo(tableOf(self.xVelocity, self.yVelocity)) }
|
|
callbacks["xVelocity"] = luaFunction { returnBuffer.setTo(self.xVelocity) }
|
|
callbacks["yVelocity"] = luaFunction { returnBuffer.setTo(self.yVelocity) }
|
|
callbacks["rotation"] = luaFunction { returnBuffer.setTo(self.rotation) }
|
|
callbacks["isColliding"] = luaFunction { returnBuffer.setTo(self.isColliding) }
|
|
callbacks["isNullColliding"] = luaFunction { returnBuffer.setTo(self.isCollidingWithNull) }
|
|
callbacks["isCollisionStuck"] = luaFunction { returnBuffer.setTo(self.isCollisionStuck) }
|
|
callbacks["stickingDirection"] = luaFunction { returnBuffer.setTo(self.stickingDirection) }
|
|
callbacks["liquidPercentage"] = luaFunction { returnBuffer.setTo(self.liquidPercentage) }
|
|
callbacks["liquidId"] = luaFunction { returnBuffer.setTo(self.liquid?.id ?: 0) }
|
|
callbacks["liquidName"] = luaFunction { returnBuffer.setTo(self.liquid?.key.toByteString()) }
|
|
callbacks["onGround"] = luaFunction { returnBuffer.setTo(self.isOnGround) }
|
|
callbacks["zeroG"] = luaFunction { returnBuffer.setTo(self.isZeroGravity) }
|
|
|
|
callbacks["atWorldLimit"] = luaFunction { bottomOnly: Boolean ->
|
|
returnBuffer.setTo(self.isAtWorldLimit(bottomOnly))
|
|
}
|
|
|
|
callbacks["setAnchorState"] = luaFunction { anchor: Number, index: Number ->
|
|
self.anchorState = AnchorState(anchor.toInt(), index.toInt())
|
|
}
|
|
|
|
callbacks["resetAnchorState"] = luaFunction {
|
|
self.anchorState = null
|
|
}
|
|
|
|
callbacks["anchorState"] = luaFunction {
|
|
val anchorState = self.anchorState
|
|
|
|
if (anchorState != null) {
|
|
returnBuffer.setTo(anchorState.entityID, anchorState.positionIndex)
|
|
}
|
|
}
|
|
|
|
callbacks["setPosition"] = luaFunction { value: Table ->
|
|
resetPathMove = true
|
|
self.position = toVector2d(value)
|
|
}
|
|
|
|
callbacks["setXPosition"] = luaFunction { value: Number ->
|
|
resetPathMove = true
|
|
self.xPosition = value.toDouble()
|
|
}
|
|
|
|
callbacks["setYPosition"] = luaFunction { value: Number ->
|
|
resetPathMove = true
|
|
self.yPosition = value.toDouble()
|
|
}
|
|
|
|
callbacks["translate"] = luaFunction { value: Table ->
|
|
resetPathMove = true
|
|
self.position += toVector2d(value)
|
|
}
|
|
|
|
callbacks["setVelocity"] = luaFunction { value: Table ->
|
|
resetPathMove = true
|
|
self.velocity = toVector2d(value)
|
|
}
|
|
|
|
callbacks["setXVelocity"] = luaFunction { value: Number ->
|
|
resetPathMove = true
|
|
self.xVelocity = value.toDouble()
|
|
}
|
|
|
|
callbacks["setYVelocity"] = luaFunction { value: Number ->
|
|
resetPathMove = true
|
|
self.yVelocity = value.toDouble()
|
|
}
|
|
|
|
callbacks["addMomentum"] = luaFunction { value: Table ->
|
|
resetPathMove = true
|
|
if (self.mass != 0.0) // let's not collapse into black hole
|
|
self.velocity += toVector2d(value) / self.mass
|
|
}
|
|
|
|
callbacks["setRotation"] = luaFunction { value: Number ->
|
|
resetPathMove = true
|
|
self.rotation = value.toDouble()
|
|
}
|
|
|
|
callbacks["baseParameters"] = luaFunction { returnBuffer.setTo(from(Starbound.gson.toJsonTree(self.actorMovementParameters))) }
|
|
callbacks["walking"] = luaFunction { returnBuffer.setTo(self.isWalking) }
|
|
callbacks["running"] = luaFunction { returnBuffer.setTo(self.isRunning) }
|
|
callbacks["movingDirection"] = luaFunction { returnBuffer.setTo(self.movingDirection.luaValue) }
|
|
callbacks["facingDirection"] = luaFunction { returnBuffer.setTo(self.facingDirection.luaValue) }
|
|
callbacks["crouching"] = luaFunction { returnBuffer.setTo(self.isCrouching) }
|
|
callbacks["flying"] = luaFunction { returnBuffer.setTo(self.isFlying) }
|
|
callbacks["falling"] = luaFunction { returnBuffer.setTo(self.isFalling) }
|
|
callbacks["canJump"] = luaFunction { returnBuffer.setTo(self.canJump) }
|
|
callbacks["jumping"] = luaFunction { returnBuffer.setTo(self.isJumping) }
|
|
callbacks["groundMovement"] = luaFunction { returnBuffer.setTo(self.isGroundMovement) }
|
|
callbacks["liquidMovement"] = luaFunction { returnBuffer.setTo(self.isLiquidMovement) }
|
|
|
|
// controls, stored locally, reset automatically or are persistent
|
|
callbacks["controlRotation"] = luaFunction { value: Number ->
|
|
controlRotationRate += value.toDouble()
|
|
}
|
|
|
|
callbacks["controlAcceleration"] = luaFunction { value: Table ->
|
|
controlAcceleration += toVector2d(value)
|
|
}
|
|
|
|
callbacks["controlForce"] = luaFunction { value: Table ->
|
|
controlForce += toVector2d(value)
|
|
}
|
|
|
|
callbacks["controlApproachVelocity"] = luaFunction { value: Table, rate: Number ->
|
|
controlApproachVelocity = ActorMovementController.ApproachVelocityCommand(toVector2d(value), rate.toDouble())
|
|
}
|
|
|
|
callbacks["controlApproachVelocityAlongAngle"] = luaFunction { angle: Number, targetVelocity: Number, maxControlForce: Number, positiveOnly: Boolean ->
|
|
controlApproachVelocityAlongAngle = ActorMovementController.ApproachVelocityAngleCommand(angle.toDouble(), targetVelocity.toDouble(), maxControlForce.toDouble(), positiveOnly)
|
|
}
|
|
|
|
callbacks["controlApproachXVelocity"] = luaFunction { targetVelocity: Number, maxControlForce: Number ->
|
|
controlApproachVelocityAlongAngle = ActorMovementController.ApproachVelocityAngleCommand(0.0, targetVelocity.toDouble(), maxControlForce.toDouble(), false)
|
|
}
|
|
|
|
callbacks["controlApproachYVelocity"] = luaFunction { targetVelocity: Number, maxControlForce: Number ->
|
|
controlApproachVelocityAlongAngle = ActorMovementController.ApproachVelocityAngleCommand(PI / 2.0, targetVelocity.toDouble(), maxControlForce.toDouble(), false)
|
|
}
|
|
|
|
callbacks["controlParameters"] = luaFunction { data: Table ->
|
|
controlParameters = controlParameters.merge(Starbound.gson.fromJsonFast(data.toJson(true), ActorMovementParameters::class.java))
|
|
}
|
|
|
|
callbacks["controlModifiers"] = luaFunction { data: Table ->
|
|
controlModifiers = controlModifiers.merge(Starbound.gson.fromJsonFast(data.toJson(true), ActorMovementModifiers::class.java))
|
|
}
|
|
|
|
callbacks["controlMove"] = luaFunction { direction: Number, shouldRun: Boolean? ->
|
|
// why?
|
|
val new = Direction.valueOf(direction)
|
|
|
|
if (new != null) {
|
|
controlMove = new
|
|
controlShouldRun = shouldRun ?: false
|
|
}
|
|
}
|
|
|
|
callbacks["controlFace"] = luaFunction { direction: Number ->
|
|
// why?
|
|
val new = Direction.valueOf(direction)
|
|
|
|
if (new != null)
|
|
controlFace = new
|
|
}
|
|
|
|
callbacks["controlDown"] = luaFunction { controlDown = true }
|
|
callbacks["controlCrouch"] = luaFunction { controlCrouch = true }
|
|
callbacks["controlJump"] = luaFunction { should: Boolean -> controlJump = should }
|
|
callbacks["controlHoldJump"] = luaFunction { controlHoldJump = true }
|
|
callbacks["controlFly"] = luaFunction { value: Table -> controlFly = toVector2d(value) }
|
|
|
|
callbacks["controlPathMove"] = luaFunction { position: Table, run: Boolean?, parameters: Table? ->
|
|
if (pathMoveResult?.first == toVector2d(position)) {
|
|
val pathMoveResult = pathMoveResult!!
|
|
this@MovementControllerBindings.pathMoveResult = null
|
|
returnBuffer.setTo(pathMoveResult.second)
|
|
} else {
|
|
pathMoveResult = null
|
|
val result = self.pathMove(toVector2d(position), run == true, Starbound.gson.fromJsonFast(toJsonFromLua(parameters)))
|
|
|
|
if (result == null) {
|
|
controlPathMove = toVector2d(position) to (run == true)
|
|
}
|
|
|
|
returnBuffer.setTo(result?.second)
|
|
}
|
|
}
|
|
|
|
callbacks["pathfinding"] = luaFunction {
|
|
returnBuffer.setTo(self.pathController.isPathfinding)
|
|
}
|
|
|
|
callbacks["autoClearControls"] = luaFunction {
|
|
returnBuffer.setTo(autoClearControls)
|
|
}
|
|
|
|
callbacks["setAutoClearControls"] = luaFunction { should: Boolean ->
|
|
autoClearControls = should
|
|
}
|
|
|
|
callbacks["clearControls"] = luaFunction { clearControls() }
|
|
}
|
|
|
|
private var autoClearControls = true
|
|
|
|
private var controlRotationRate = 0.0
|
|
private var controlAcceleration = Vector2d.ZERO
|
|
private var controlForce = Vector2d.ZERO
|
|
private var controlApproachVelocity: ActorMovementController.ApproachVelocityCommand? = null
|
|
private var controlApproachVelocityAlongAngle: ActorMovementController.ApproachVelocityAngleCommand? = null
|
|
private var controlParameters = ActorMovementParameters.EMPTY
|
|
private var controlModifiers = ActorMovementModifiers.EMPTY
|
|
|
|
private var controlMove: Direction? = null
|
|
private var controlFace: Direction? = null
|
|
private var controlShouldRun: Boolean? = null
|
|
|
|
private var controlDown = false
|
|
private var controlCrouch = false
|
|
private var controlJump = false
|
|
private var controlHoldJump = false
|
|
private var controlFly: Vector2d? = null
|
|
|
|
private var resetPathMove = false
|
|
|
|
private var pathMoveResult: Pair<Vector2d, Boolean>? = null
|
|
private var controlPathMove: Pair<Vector2d, Boolean>? = null
|
|
|
|
fun apply() {
|
|
self.controlRotationRate += controlRotationRate
|
|
self.controlAcceleration += controlAcceleration
|
|
self.controlForce += controlForce
|
|
|
|
if (controlApproachVelocity != null) self.approachVelocities.add(controlApproachVelocity!!)
|
|
if (controlApproachVelocityAlongAngle != null) self.approachVelocityAngles.add(controlApproachVelocityAlongAngle!!)
|
|
self.controlActorMovementParameters = self.controlActorMovementParameters.merge(controlParameters)
|
|
self.controlMovementModifiers = self.controlMovementModifiers.merge(controlModifiers)
|
|
if (controlMove != null) self.controlMove = controlMove
|
|
if (controlShouldRun != null) self.controlRun = controlShouldRun!!
|
|
if (controlFace != null) self.controlFace = controlFace
|
|
if (controlDown) self.controlDown = true
|
|
if (controlCrouch) self.controlCrouch = true
|
|
if (controlJump) self.controlJump = true
|
|
if (controlHoldJump && !self.isOnGround) self.controlJump = true
|
|
if (controlFly != null) self.controlFly = controlFly
|
|
|
|
// some action was taken that has priority over pathing, setting position or velocity
|
|
if (resetPathMove)
|
|
controlPathMove = null
|
|
|
|
if (controlPathMove != null && pathMoveResult == null)
|
|
pathMoveResult = self.controlPathMove(controlPathMove!!.first, controlPathMove!!.second)
|
|
}
|
|
|
|
fun clearControlsIfNeeded() {
|
|
if (autoClearControls)
|
|
clearControls()
|
|
}
|
|
|
|
private fun clearControls() {
|
|
controlRotationRate = 0.0
|
|
controlAcceleration = Vector2d.ZERO
|
|
controlForce = Vector2d.ZERO
|
|
controlApproachVelocity = null
|
|
controlApproachVelocityAlongAngle = null
|
|
controlParameters = ActorMovementParameters.EMPTY
|
|
controlModifiers = ActorMovementModifiers.EMPTY
|
|
controlMove = null
|
|
controlFace = null
|
|
controlShouldRun = null
|
|
controlDown = false
|
|
controlCrouch = false
|
|
controlJump = false
|
|
controlHoldJump = false
|
|
controlFly = null
|
|
pathMoveResult = null
|
|
controlPathMove = null
|
|
resetPathMove = false
|
|
}
|
|
}
|