KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/MovementControllerBindings.kt
DBotThePony f95bc9762f
Minimally working Monster entities
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
2024-06-28 22:44:13 +07:00

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
}
}