osb/source/game/scripting/StarLuaActorMovementComponent.hpp
2024-02-25 15:46:47 +01:00

438 lines
15 KiB
C++

#pragma once
#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>(std::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 = {};
}
}