diff options
Diffstat (limited to 'source/game/scripting/StarLuaActorMovementComponent.hpp')
-rw-r--r-- | source/game/scripting/StarLuaActorMovementComponent.hpp | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/source/game/scripting/StarLuaActorMovementComponent.hpp b/source/game/scripting/StarLuaActorMovementComponent.hpp new file mode 100644 index 0000000..6dc0fc6 --- /dev/null +++ b/source/game/scripting/StarLuaActorMovementComponent.hpp @@ -0,0 +1,440 @@ +#ifndef STAR_ACTOR_MOVEMENT_COMPONENT_HPP +#define STAR_ACTOR_MOVEMENT_COMPONENT_HPP + +#include "StarActorMovementController.hpp" +#include "StarLuaGameConverters.hpp" + +namespace Star { + +// Wraps a LuaUpdatableComponent to handle the particularly tricky case of +// maintaining ActorMovementController controls when we do not call the script +// update every tick. +template <typename Base> +class LuaActorMovementComponent : public Base { +public: + LuaActorMovementComponent(); + + void addActorMovementCallbacks(ActorMovementController* actorMovementController); + void removeActorMovementCallbacks(); + + // If true, then the controls are automatically cleared on script update. + // Defaults to true + bool autoClearControls() const; + void setAutoClearControls(bool autoClearControls); + + // Updates the lua script component and applies held controls. If no script + // update is scheduled this tick, then the controls from the last update will + // be held and not cleared. If a script update is scheduled this tick, then + // the controls will be cleared only if autoClearControls is set to true. + template <typename Ret = LuaValue, typename... V> + Maybe<Ret> update(V&&... args); + +private: + void performControls(); + void clearControls(); + + ActorMovementController* m_movementController; + bool m_autoClearControls; + + float m_controlRotation; + Vec2F m_controlAcceleration; + Vec2F m_controlForce; + Maybe<tuple<Vec2F, float>> m_controlApproachVelocity; + Maybe<tuple<float, float, float, bool>> m_controlApproachVelocityAlongAngle; + Maybe<ActorMovementParameters> m_controlParameters; + Maybe<ActorMovementModifiers> m_controlModifiers; + Maybe<tuple<Direction, bool>> m_controlMove; + Maybe<Direction> m_controlFace; + bool m_controlDown; + bool m_controlCrouch; + Maybe<bool> m_controlJump; + bool m_controlHoldJump; + Maybe<Vec2F> m_controlFly; + + bool m_resetPathMove; + Maybe<pair<Vec2F, bool>> m_controlPathMove; + Maybe<pair<Vec2F, bool>> m_pathMoveResult; +}; + +template <typename Base> +LuaActorMovementComponent<Base>::LuaActorMovementComponent() + : m_autoClearControls(true), + m_controlRotation(0.0f), + m_controlDown(false), + m_controlCrouch(false), + m_controlHoldJump(false) {} + +template <typename Base> +void LuaActorMovementComponent<Base>::addActorMovementCallbacks(ActorMovementController* actorMovementController) { + m_movementController = actorMovementController; + if (m_movementController) { + LuaCallbacks callbacks; + + callbacks.registerCallback("mass", [this]() { + return m_movementController->mass(); + }); + + callbacks.registerCallback("boundBox", [this]() { + return m_movementController->collisionPoly().boundBox(); + }); + + callbacks.registerCallback("collisionPoly", [this]() { + return m_movementController->collisionPoly(); + }); + + callbacks.registerCallback("collisionBody", [this]() { + return m_movementController->collisionBody(); + }); + + callbacks.registerCallback("position", [this]() { + return m_movementController->position(); + }); + + callbacks.registerCallback("xPosition", [this]() { + return m_movementController->xPosition(); + }); + + callbacks.registerCallback("yPosition", [this]() { + return m_movementController->yPosition(); + }); + + callbacks.registerCallback("velocity", [this]() { + return m_movementController->velocity(); + }); + + callbacks.registerCallback("xVelocity", [this]() { + return m_movementController->xVelocity(); + }); + + callbacks.registerCallback("yVelocity", [this]() { + return m_movementController->yVelocity(); + }); + + callbacks.registerCallback("rotation", [this]() { + return m_movementController->rotation(); + }); + + callbacks.registerCallback("isColliding", [this]() { + return m_movementController->isColliding(); + }); + + callbacks.registerCallback("isNullColliding", [this]() { + return m_movementController->isNullColliding(); + }); + + callbacks.registerCallback("isCollisionStuck", [this]() { + return m_movementController->isCollisionStuck(); + }); + + callbacks.registerCallback("stickingDirection", [this]() { + return m_movementController->stickingDirection(); + }); + + callbacks.registerCallback("liquidPercentage", [this]() { + return m_movementController->liquidPercentage(); + }); + + callbacks.registerCallback("liquidId", [this]() { + return m_movementController->liquidId(); + }); + + callbacks.registerCallback("onGround", [this]() { + return m_movementController->onGround(); + }); + + callbacks.registerCallback("zeroG", [this]() { + return m_movementController->zeroG(); + }); + + callbacks.registerCallback("atWorldLimit", [this](bool bottomOnly) { + return m_movementController->atWorldLimit(bottomOnly); + }); + + callbacks.registerCallback("setAnchorState", [this](EntityId anchorableEntity, size_t anchorPosition) { + m_movementController->setAnchorState({anchorableEntity, anchorPosition}); + }); + + callbacks.registerCallback("resetAnchorState", [this]() { + m_movementController->resetAnchorState(); + }); + + callbacks.registerCallback("anchorState", [this]() { + if (auto anchorState = m_movementController->anchorState()) + return LuaVariadic<LuaValue>{LuaInt(anchorState->entityId), LuaInt(anchorState->positionIndex)}; + return LuaVariadic<LuaValue>(); + }); + + callbacks.registerCallback("setPosition", [this](Vec2F const& pos) { + m_movementController->setPosition(pos); + }); + + callbacks.registerCallback("setXPosition", [this](float xPosition) { + m_movementController->setXPosition(xPosition); + }); + + callbacks.registerCallback("setYPosition", [this](float yPosition) { + m_movementController->setYPosition(yPosition); + }); + + callbacks.registerCallback("translate", [this](Vec2F const& translate) { + m_movementController->translate(translate); + }); + + callbacks.registerCallback("setVelocity", [this](Vec2F const& vel) { + m_resetPathMove = true; + m_movementController->setVelocity(vel); + }); + + callbacks.registerCallback("setXVelocity", [this](float xVel) { + m_resetPathMove = true; + m_movementController->setXVelocity(xVel); + }); + + callbacks.registerCallback("setYVelocity", [this](float yVel) { + m_resetPathMove = true; + m_movementController->setYVelocity(yVel); + }); + + callbacks.registerCallback("addMomentum", [this](Vec2F const& momentum) { + m_resetPathMove = true; + m_movementController->addMomentum(momentum); + }); + + callbacks.registerCallback("setRotation", [this](float rotation) { + m_resetPathMove = true; + m_movementController->setRotation(rotation); + }); + + callbacks.registerCallback("baseParameters", [this]() { + return m_movementController->baseParameters(); + }); + + callbacks.registerCallback("walking", [this]() { + return m_movementController->walking(); + }); + + callbacks.registerCallback("running", [this]() { + return m_movementController->running(); + }); + + callbacks.registerCallback("movingDirection", [this]() { + return numericalDirection(m_movementController->movingDirection()); + }); + + callbacks.registerCallback("facingDirection", [this]() { + return numericalDirection(m_movementController->facingDirection()); + }); + + callbacks.registerCallback("crouching", [this]() { + return m_movementController->crouching(); + }); + + callbacks.registerCallback("flying", [this]() { + return m_movementController->flying(); + }); + + callbacks.registerCallback("falling", [this]() { + return m_movementController->falling(); + }); + + callbacks.registerCallback("canJump", [this]() { + return m_movementController->canJump(); + }); + + callbacks.registerCallback("jumping", [this]() { + return m_movementController->jumping(); + }); + + callbacks.registerCallback("groundMovement", [this]() { + return m_movementController->groundMovement(); + }); + + callbacks.registerCallback("liquidMovement", [this]() { + return m_movementController->liquidMovement(); + }); + + callbacks.registerCallback("controlRotation", [this](float rotation) { + m_controlRotation += rotation; + }); + + callbacks.registerCallback("controlAcceleration", [this](Vec2F const& accel) { + m_controlAcceleration += accel; + }); + + callbacks.registerCallback("controlForce", [this](Vec2F const& force) { + m_controlForce += force; + }); + + callbacks.registerCallback("controlApproachVelocity", [this](Vec2F const& arg1, float arg2) { + m_controlApproachVelocity.set(make_tuple(arg1, arg2)); + }); + + callbacks.registerCallback("controlApproachVelocityAlongAngle", [this](float angle, float targetVelocity, float maxControlForce, bool positiveOnly) { + m_controlApproachVelocityAlongAngle.set(make_tuple(angle, targetVelocity, maxControlForce, positiveOnly)); + }); + + callbacks.registerCallback("controlApproachXVelocity", [this](float targetXVelocity, float maxControlForce) { + m_controlApproachVelocityAlongAngle.set(make_tuple(0.0f, targetXVelocity, maxControlForce, false)); + }); + + callbacks.registerCallback("controlApproachYVelocity", [this](float targetYVelocity, float maxControlForce) { + m_controlApproachVelocityAlongAngle.set( + make_tuple(Constants::pi / 2.0f, targetYVelocity, maxControlForce, false)); + }); + + callbacks.registerCallback("controlParameters", [this](ActorMovementParameters const& arg1) { + m_controlParameters = m_controlParameters.value().merge(arg1); + }); + + callbacks.registerCallback("controlModifiers", [this](ActorMovementModifiers const& arg1) { + m_controlModifiers = m_controlModifiers.value().combine(arg1); + }); + + callbacks.registerCallback("controlMove", [this](Maybe<float> const& arg1, Maybe<bool> const& arg2) { + if (auto direction = directionOf(arg1.value())) + m_controlMove.set(make_tuple(*direction, arg2.value(true))); + }); + + callbacks.registerCallback("controlFace", [this](Maybe<float> const& arg1) { + if (auto direction = directionOf(arg1.value())) + m_controlFace = *direction; + }); + + callbacks.registerCallback("controlDown", [this]() { + m_controlDown = true; + }); + + callbacks.registerCallback("controlCrouch", [this]() { + m_controlCrouch = true; + }); + + callbacks.registerCallback("controlJump", [this](bool arg1) { + m_controlJump = arg1; + }); + + callbacks.registerCallback("controlHoldJump", [this]() { + m_controlHoldJump = true; + }); + + callbacks.registerCallback("controlFly", [this](Vec2F const& arg1) { + m_controlFly = arg1; + }); + + callbacks.registerCallback("controlPathMove", [this](Vec2F const& position, Maybe<bool> run, Maybe<PlatformerAStar::Parameters> parameters) -> Maybe<bool> { + if (m_pathMoveResult && m_pathMoveResult->first == position) { + return take(m_pathMoveResult).apply([](pair<Vec2F, bool> const& p) { return p.second; }); + } else { + m_pathMoveResult.reset(); + auto result = m_movementController->pathMove(position, run.value(false), parameters); + if (result.isNothing()) + m_controlPathMove = pair<Vec2F, bool>(position, run.value(false)); + return result.apply([](pair<Vec2F, bool> const& p) { return p.second; }); + } + }); + + callbacks.registerCallback("pathfinding", [this]() -> bool { + return m_movementController->pathfinding(); + }); + + callbacks.registerCallbackWithSignature<bool>("autoClearControls", bind(&LuaActorMovementComponent::autoClearControls, this)); + callbacks.registerCallbackWithSignature<void, bool>("setAutoClearControls", bind(&LuaActorMovementComponent::setAutoClearControls, this, _1)); + callbacks.registerCallbackWithSignature<void>("clearControls", bind(&LuaActorMovementComponent::clearControls, this)); + + Base::addCallbacks("mcontroller", callbacks); + + } else { + Base::removeCallbacks("mcontroller"); + } +} + +template <typename Base> +void LuaActorMovementComponent<Base>::removeActorMovementCallbacks() { + addActorMovementCallbacks(nullptr); +} + +template <typename Base> +bool LuaActorMovementComponent<Base>::autoClearControls() const { + return m_autoClearControls; +} + +template <typename Base> +void LuaActorMovementComponent<Base>::setAutoClearControls(bool autoClearControls) { + m_autoClearControls = autoClearControls; +} + +template <typename Base> +template <typename Ret, typename... V> +Maybe<Ret> LuaActorMovementComponent<Base>::update(V&&... args) { + if (Base::updateReady()) { + if (m_autoClearControls) + clearControls(); + } + Maybe<Ret> ret = Base::template update<Ret>(forward<V>(args)...); + performControls(); + return ret; +} + +template <typename Base> +void LuaActorMovementComponent<Base>::performControls() { + if (m_movementController) { + m_movementController->controlRotation(m_controlRotation); + m_movementController->controlAcceleration(m_controlAcceleration); + m_movementController->controlForce(m_controlForce); + if (m_controlApproachVelocity) + tupleUnpackFunction(bind(&ActorMovementController::controlApproachVelocity, m_movementController, _1, _2), *m_controlApproachVelocity); + if (m_controlApproachVelocityAlongAngle) + tupleUnpackFunction(bind(&ActorMovementController::controlApproachVelocityAlongAngle, m_movementController, _1, _2, _3, _4), *m_controlApproachVelocityAlongAngle); + if (m_controlParameters) + m_movementController->controlParameters(*m_controlParameters); + if (m_controlModifiers) + m_movementController->controlModifiers(*m_controlModifiers); + if (m_controlMove) + tupleUnpackFunction(bind(&ActorMovementController::controlMove, m_movementController, _1, _2), *m_controlMove); + if (m_controlFace) + m_movementController->controlFace(*m_controlFace); + if (m_controlDown) + m_movementController->controlDown(); + if (m_controlCrouch) + m_movementController->controlCrouch(); + if (m_controlJump) + m_movementController->controlJump(*m_controlJump); + if (m_controlHoldJump && !m_movementController->onGround()) + m_movementController->controlJump(); + if (m_controlFly) + m_movementController->controlFly(*m_controlFly); + + // some action was taken that has priority over pathing, setting position or velocity + if (m_resetPathMove) + m_controlPathMove = {}; + if (m_controlPathMove && m_pathMoveResult.isNothing()) + m_pathMoveResult = m_movementController->controlPathMove(m_controlPathMove->first, m_controlPathMove->second); + } +} + +template <typename Base> +void LuaActorMovementComponent<Base>::clearControls() { + m_controlRotation = {}; + m_controlAcceleration = {}; + m_controlForce = {}; + m_controlApproachVelocity = {}; + m_controlApproachVelocityAlongAngle = {}; + m_controlParameters = {}; + m_controlModifiers = {}; + m_controlMove = {}; + m_controlFace = {}; + m_controlDown = {}; + m_controlCrouch = {}; + m_controlJump = {}; + m_controlHoldJump = {}; + m_controlFly = {}; + + m_resetPathMove = false; + // Clear path move result one clear after controlPathMove is no longer called + // to keep the result available for the following update + if (m_controlPathMove.isNothing()) + m_pathMoveResult = {}; + m_controlPathMove = {}; +} +} + +#endif |