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? = null private var controlPathMove: Pair? = 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 } }