package ru.dbotthepony.kstarbound.lua.bindings import com.google.gson.JsonNull import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.ActorMovementParameters import ru.dbotthepony.kstarbound.defs.actor.ActorMovementModifiers import ru.dbotthepony.kstarbound.fromJsonFast import ru.dbotthepony.kstarbound.lua.LuaThread import ru.dbotthepony.kstarbound.lua.nextVector2d import ru.dbotthepony.kstarbound.lua.push import ru.dbotthepony.kstarbound.lua.setTableValue 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.AnchorNetworkState import ru.dbotthepony.kstarbound.world.physics.Poly import kotlin.math.PI class MovementControllerBindings(val self: ActorMovementController) { private fun mass(args: LuaThread.ArgStack): Int { args.lua.push(self.mass) return 1 } private fun localBoundBox(args: LuaThread.ArgStack): Int { args.lua.push(self.computeLocalCollisionAABB()) return 1 } // TODO private fun boundBox(args: LuaThread.ArgStack): Int { args.lua.push(self.computeLocalCollisionAABB()) return 1 } private fun collisionBoundBox(args: LuaThread.ArgStack): Int { args.lua.push(self.computeGlobalCollisionAABB()) return 1 } private fun collisionPoly(args: LuaThread.ArgStack): Int { args.lua.push(self.movementParameters.collisionPoly?.map({ it }, { it.firstOrNull() }) ?: Poly.EMPTY) return 1 } private fun collisionPolies(args: LuaThread.ArgStack): Int { val result = self.movementParameters.collisionPoly?.map({ listOf(it) }, { it }) ?: listOf(Poly.EMPTY) args.lua.pushTable(result.size) for ((i, v) in result.withIndex()) args.lua.setTableValue(i + 1L, v) return 1 } private fun collisionBody(args: LuaThread.ArgStack): Int { args.lua.push(self.computeGlobalHitboxes().firstOrNull() ?: Poly.EMPTY) return 1 } private fun collisionBodies(args: LuaThread.ArgStack): Int { val result = self.computeGlobalHitboxes() args.lua.pushTable(result.size) for ((i, v) in result.withIndex()) args.lua.setTableValue(i + 1L, v) return 1 } private fun position(args: LuaThread.ArgStack): Int { args.lua.push(self.position); return 1 } private fun xPosition(args: LuaThread.ArgStack): Int { args.lua.push(self.xPosition); return 1 } private fun yPosition(args: LuaThread.ArgStack): Int { args.lua.push(self.yPosition); return 1 } private fun velocity(args: LuaThread.ArgStack): Int { args.lua.push(self.velocity); return 1 } private fun xVelocity(args: LuaThread.ArgStack): Int { args.lua.push(self.xVelocity); return 1 } private fun yVelocity(args: LuaThread.ArgStack): Int { args.lua.push(self.yVelocity); return 1 } private fun rotation(args: LuaThread.ArgStack): Int { args.lua.push(self.rotation); return 1 } private fun isColliding(args: LuaThread.ArgStack): Int { args.lua.push(self.isColliding); return 1 } private fun isNullColliding(args: LuaThread.ArgStack): Int { args.lua.push(self.isCollidingWithNull); return 1 } private fun isCollisionStuck(args: LuaThread.ArgStack): Int { args.lua.push(self.isCollisionStuck); return 1 } private fun stickingDirection(args: LuaThread.ArgStack): Int { args.lua.push(self.stickingDirection); return 1 } private fun liquidPercentage(args: LuaThread.ArgStack): Int { args.lua.push(self.liquidPercentage); return 1 } private fun liquidId(args: LuaThread.ArgStack): Int { args.lua.push(self.liquid?.id?.toLong() ?: 0L); return 1 } private fun liquidName(args: LuaThread.ArgStack): Int { args.lua.push(self.liquid?.key); return 1 } private fun onGround(args: LuaThread.ArgStack): Int { args.lua.push(self.isOnGround); return 1 } private fun zeroG(args: LuaThread.ArgStack): Int { args.lua.push(self.isZeroGravity); return 1 } private fun atWorldLimit(args: LuaThread.ArgStack): Int { args.lua.push(self.isAtWorldLimit(args.nextBoolean())) return 1 } private fun setAnchorState(args: LuaThread.ArgStack): Int { self.anchorNetworkState = AnchorNetworkState(args.nextInt(), args.nextInt()) return 0 } private fun resetAnchorState(args: LuaThread.ArgStack): Int { self.anchorNetworkState = null return 0 } private fun anchorState(args: LuaThread.ArgStack): Int { val anchorState = self.anchorNetworkState if (anchorState != null) { args.lua.push(anchorState.entityID.toLong()) args.lua.push(anchorState.positionIndex.toLong()) return 2 } else { return 0 } } private fun setPosition(args: LuaThread.ArgStack): Int { resetPathMove = true self.position = args.nextVector2d() return 0 } private fun translate(args: LuaThread.ArgStack): Int { resetPathMove = true self.position += args.nextVector2d() return 0 } private fun setXPosition(args: LuaThread.ArgStack): Int { resetPathMove = true self.xPosition = args.nextDouble() return 0 } private fun setYPosition(args: LuaThread.ArgStack): Int { resetPathMove = true self.yPosition = args.nextDouble() return 0 } private fun setVelocity(args: LuaThread.ArgStack): Int { resetPathMove = true self.velocity = args.nextVector2d() return 0 } private fun setXVelocity(args: LuaThread.ArgStack): Int { resetPathMove = true self.xVelocity = args.nextDouble() return 0 } private fun setYVelocity(args: LuaThread.ArgStack): Int { resetPathMove = true self.yVelocity = args.nextDouble() return 0 } private fun addMomentum(args: LuaThread.ArgStack): Int { resetPathMove = true if (self.mass != 0.0) // let's not collapse into black hole self.velocity += args.nextVector2d() / self.mass return 0 } private fun setRotation(args: LuaThread.ArgStack): Int { resetPathMove = true self.rotation = args.nextDouble() return 0 } private fun baseParameters(args: LuaThread.ArgStack): Int { args.lua.push(Starbound.gson.toJsonTree(self.actorMovementParameters)); return 1 } private fun walking(args: LuaThread.ArgStack): Int { args.lua.push(self.isWalking); return 1 } private fun running(args: LuaThread.ArgStack): Int { args.lua.push(self.isRunning); return 1 } private fun movingDirection(args: LuaThread.ArgStack): Int { args.lua.push(self.movingDirection.numericalValue); return 1 } private fun facingDirection(args: LuaThread.ArgStack): Int { args.lua.push(self.facingDirection.numericalValue); return 1 } private fun crouching(args: LuaThread.ArgStack): Int { args.lua.push(self.isCrouching); return 1 } private fun flying(args: LuaThread.ArgStack): Int { args.lua.push(self.isFlying); return 1 } private fun falling(args: LuaThread.ArgStack): Int { args.lua.push(self.isFalling); return 1 } private fun canJump(args: LuaThread.ArgStack): Int { args.lua.push(self.canJump); return 1 } private fun jumping(args: LuaThread.ArgStack): Int { args.lua.push(self.isJumping); return 1 } private fun groundMovement(args: LuaThread.ArgStack): Int { args.lua.push(self.isGroundMovement); return 1 } private fun liquidMovement(args: LuaThread.ArgStack): Int { args.lua.push(self.isLiquidMovement); return 1 } // controls, stored locally, reset automatically or are persistent private fun controlRotation(args: LuaThread.ArgStack): Int { controlRotationRate += args.nextDouble() return 0 } private fun controlAcceleration(args: LuaThread.ArgStack): Int { controlAcceleration += args.nextVector2d() return 0 } private fun controlForce(args: LuaThread.ArgStack): Int { controlForce += args.nextVector2d() return 0 } private fun controlApproachVelocity(args: LuaThread.ArgStack): Int { controlApproachVelocity = ActorMovementController.ApproachVelocityCommand(args.nextVector2d(), args.nextDouble()) return 0 } private fun controlApproachVelocityAlongAngle(args: LuaThread.ArgStack): Int { controlApproachVelocityAlongAngle = ActorMovementController.ApproachVelocityAngleCommand(args.nextDouble(), args.nextDouble(), args.nextDouble(), args.nextBoolean()) return 0 } private fun controlApproachXVelocity(args: LuaThread.ArgStack): Int { controlApproachVelocityAlongAngle = ActorMovementController.ApproachVelocityAngleCommand(0.0, args.nextDouble(), args.nextDouble(), false) return 0 } private fun controlApproachYVelocity(args: LuaThread.ArgStack): Int { controlApproachVelocityAlongAngle = ActorMovementController.ApproachVelocityAngleCommand(PI / 2.0, args.nextDouble(), args.nextDouble(), false) return 0 } private fun controlParameters(args: LuaThread.ArgStack): Int { controlParameters = controlParameters.merge(Starbound.gson.fromJsonFast(args.nextJson(), ActorMovementParameters::class.java)) return 0 } private fun controlModifiers(args: LuaThread.ArgStack): Int { controlModifiers = controlModifiers.merge(Starbound.gson.fromJsonFast(args.nextJson(), ActorMovementModifiers::class.java)) return 0 } private fun controlMove(args: LuaThread.ArgStack): Int { // why? val new = Direction.valueOf(args.nextDouble()) val shouldRun = args.nextOptionalBoolean() if (new != null) { controlMove = new controlShouldRun = shouldRun ?: false } return 0 } private fun controlFace(args: LuaThread.ArgStack): Int { // why? val new = Direction.valueOf(args.nextDouble()) if (new != null) controlFace = new return 0 } private fun controlDown(args: LuaThread.ArgStack): Int { controlDown = true; return 0 } private fun controlCrouch(args: LuaThread.ArgStack): Int { controlCrouch = true; return 0 } private fun controlHoldJump(args: LuaThread.ArgStack): Int { controlHoldJump = true; return 0 } private fun controlJump(args: LuaThread.ArgStack): Int { controlJump = args.nextBoolean(); return 0 } private fun controlFly(args: LuaThread.ArgStack): Int { controlFly = args.nextVector2d(); return 0 } private fun controlPathMove(args: LuaThread.ArgStack): Int { val position = args.nextVector2d() if (pathMoveResult?.first == position) { val pathMoveResult = this.pathMoveResult!! this.pathMoveResult = null args.lua.push(pathMoveResult.second) } else { pathMoveResult = null val run = args.nextOptionalBoolean() == true val parameters = args.nextOptionalJson() val result = self.pathMove(position, run, Starbound.gson.fromJsonFast(parameters ?: JsonNull.INSTANCE)) if (result == null) { controlPathMove = position to false } args.lua.push(result?.second) } return 1 } private fun pathfinding(args: LuaThread.ArgStack): Int { args.lua.push(self.pathController.isPathfinding) return 1 } private fun autoClearControls(args: LuaThread.ArgStack): Int { args.lua.push(autoClearControls) return 1 } private fun setAutoClearControls(args: LuaThread.ArgStack): Int { autoClearControls = args.nextBoolean() return 0 } private fun clearControls(args: LuaThread.ArgStack): Int { clearControls() return 0 } fun init(lua: LuaThread) { lua.pushTable() lua.dup() lua.storeGlobal("mcontroller") lua.setTableValue("mass", ::mass) lua.setTableValue("localBoundBox", ::localBoundBox) lua.setTableValue("boundBox", ::boundBox) lua.setTableValue("collisionBoundBox", ::collisionBoundBox) lua.setTableValue("collisionPoly", ::collisionPoly) lua.setTableValue("collisionPolies", ::collisionPolies) lua.setTableValue("collisionBody", ::collisionBody) lua.setTableValue("collisionBodies", ::collisionBodies) lua.setTableValue("position", ::position) lua.setTableValue("xPosition", ::xPosition) lua.setTableValue("yPosition", ::yPosition) lua.setTableValue("velocity", ::velocity) lua.setTableValue("xVelocity", ::xVelocity) lua.setTableValue("yVelocity", ::yVelocity) lua.setTableValue("rotation", ::rotation) lua.setTableValue("isColliding", ::isColliding) lua.setTableValue("isNullColliding", ::isNullColliding) lua.setTableValue("isCollisionStuck", ::isCollisionStuck) lua.setTableValue("stickingDirection", ::stickingDirection) lua.setTableValue("liquidPercentage", ::liquidPercentage) lua.setTableValue("liquidId", ::liquidId) lua.setTableValue("liquidName", ::liquidName) lua.setTableValue("onGround", ::onGround) lua.setTableValue("zeroG", ::zeroG) lua.setTableValue("atWorldLimit", ::atWorldLimit) lua.setTableValue("setAnchorState", ::setAnchorState) lua.setTableValue("resetAnchorState", ::resetAnchorState) lua.setTableValue("anchorState", ::anchorState) lua.setTableValue("setPosition", ::setPosition) lua.setTableValue("setXPosition", ::setXPosition) lua.setTableValue("setYPosition", ::setYPosition) lua.setTableValue("translate", ::translate) lua.setTableValue("setVelocity", ::setVelocity) lua.setTableValue("setXVelocity", ::setXVelocity) lua.setTableValue("setYVelocity", ::setYVelocity) lua.setTableValue("addMomentum", ::addMomentum) lua.setTableValue("setRotation", ::setRotation) lua.setTableValue("baseParameters", ::baseParameters) lua.setTableValue("walking", ::walking) lua.setTableValue("running", ::running) lua.setTableValue("movingDirection", ::movingDirection) lua.setTableValue("facingDirection", ::facingDirection) lua.setTableValue("crouching", ::crouching) lua.setTableValue("flying", ::flying) lua.setTableValue("falling", ::falling) lua.setTableValue("canJump", ::canJump) lua.setTableValue("jumping", ::jumping) lua.setTableValue("groundMovement", ::groundMovement) lua.setTableValue("liquidMovement", ::liquidMovement) lua.setTableValue("controlRotation", ::controlRotation) lua.setTableValue("controlAcceleration", ::controlAcceleration) lua.setTableValue("controlForce", ::controlForce) lua.setTableValue("controlApproachVelocity", ::controlApproachVelocity) lua.setTableValue("controlApproachVelocityAlongAngle", ::controlApproachVelocityAlongAngle) lua.setTableValue("controlApproachXVelocity", ::controlApproachXVelocity) lua.setTableValue("controlApproachYVelocity", ::controlApproachYVelocity) lua.setTableValue("controlParameters", ::controlParameters) lua.setTableValue("controlModifiers", ::controlModifiers) lua.setTableValue("controlMove", ::controlMove) lua.setTableValue("controlFace", ::controlFace) lua.setTableValue("controlDown", ::controlDown) lua.setTableValue("controlCrouch", ::controlCrouch) lua.setTableValue("controlHoldJump", ::controlHoldJump) lua.setTableValue("controlJump", ::controlJump) lua.setTableValue("controlFly", ::controlFly) lua.setTableValue("controlPathMove", ::controlPathMove) lua.setTableValue("pathfinding", ::pathfinding) lua.setTableValue("autoClearControls", ::autoClearControls) lua.setTableValue("setAutoClearControls", ::setAutoClearControls) lua.setTableValue("clearControls", ::clearControls) lua.pop() } 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 } }