Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/scripting/StarLuaActorMovementComponent.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/scripting/StarLuaActorMovementComponent.hpp')
-rw-r--r--source/game/scripting/StarLuaActorMovementComponent.hpp440
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