osb/source/game/StarActorMovementController.hpp
2023-06-20 14:33:09 +10:00

364 lines
12 KiB
C++

#ifndef STAR_ACTOR_MOVEMENT_CONTROLLER_HPP
#define STAR_ACTOR_MOVEMENT_CONTROLLER_HPP
#include "StarMovementController.hpp"
#include "StarPlatformerAStarTypes.hpp"
#include "StarAnchorableEntity.hpp"
#include "StarGameTimers.hpp"
namespace Star {
STAR_EXCEPTION(ActorMovementControllerException, MovementControllerException);
STAR_CLASS(ActorMovementController);
STAR_CLASS(PathController);
struct ActorJumpProfile {
ActorJumpProfile();
ActorJumpProfile(Json const& config);
Json toJson() const;
ActorJumpProfile merge(ActorJumpProfile const& rhs) const;
Maybe<float> jumpSpeed;
Maybe<float> jumpControlForce;
Maybe<float> jumpInitialPercentage;
// If this is greater than 0.0, jump hold time is limited by this factor.
Maybe<float> jumpHoldTime;
// If this is greater than 0.0, then the total jump time for *all jumps in a
// multi jump set* is limited by this factor.
Maybe<float> jumpTotalHoldTime;
Maybe<bool> multiJump;
Maybe<float> reJumpDelay;
Maybe<bool> autoJump;
Maybe<bool> collisionCancelled;
};
DataStream& operator>>(DataStream& ds, ActorJumpProfile& movementParameters);
DataStream& operator<<(DataStream& ds, ActorJumpProfile const& movementParameters);
// A not-quite superset of MovementParameters, with some fields from
// MovementParameters ignored because they make no sense, and other fields
// expanded out to different cases based on Actor specific things.
struct ActorMovementParameters {
// Load sensible defaults from a config file.
static ActorMovementParameters sensibleDefaults();
// Construct parameters from config with only those specified in the config
// set, if any.
explicit ActorMovementParameters(Json const& config = Json());
Json toJson() const;
// Merge the given set of movement parameters on top of this one, with any
// set parameters in rhs overwriting the ones in this set.
ActorMovementParameters merge(ActorMovementParameters const& rhs) const;
Maybe<float> mass;
Maybe<float> gravityMultiplier;
Maybe<float> liquidBuoyancy;
Maybe<float> airBuoyancy;
Maybe<float> bounceFactor;
Maybe<bool> stopOnFirstBounce;
Maybe<bool> enableSurfaceSlopeCorrection;
Maybe<float> slopeSlidingFactor;
Maybe<float> maxMovementPerStep;
Maybe<float> maximumCorrection;
Maybe<float> speedLimit;
Maybe<PolyF> standingPoly;
Maybe<PolyF> crouchingPoly;
Maybe<bool> stickyCollision;
Maybe<float> stickyForce;
Maybe<float> walkSpeed;
Maybe<float> runSpeed;
Maybe<float> flySpeed;
Maybe<float> airFriction;
Maybe<float> liquidFriction;
Maybe<float> minimumLiquidPercentage;
Maybe<float> liquidImpedance;
Maybe<float> normalGroundFriction;
Maybe<float> ambulatingGroundFriction;
Maybe<float> groundForce;
Maybe<float> airForce;
Maybe<float> liquidForce;
ActorJumpProfile airJumpProfile;
ActorJumpProfile liquidJumpProfile;
Maybe<float> fallStatusSpeedMin;
Maybe<int> fallThroughSustainFrames;
Maybe<float> maximumPlatformCorrection;
Maybe<float> maximumPlatformCorrectionVelocityFactor;
Maybe<StringSet> physicsEffectCategories;
Maybe<float> groundMovementMinimumSustain;
Maybe<float> groundMovementMaximumSustain;
Maybe<float> groundMovementCheckDistance;
Maybe<bool> collisionEnabled;
Maybe<bool> frictionEnabled;
Maybe<bool> gravityEnabled;
Maybe<float> pathExploreRate;
};
DataStream& operator>>(DataStream& ds, ActorMovementParameters& movementParameters);
DataStream& operator<<(DataStream& ds, ActorMovementParameters const& movementParameters);
// A set of normalized values that act as "modifiers" or "bonuses" to movement,
// and can be combined sensibly. A modifier of 0.0 represents a 0% change, a
// modifier of 0.2 represents a 20% increase, and a modifier of -0.2 represents
// a 20% decrease. Also includes some flags that disable functionality
// combined with logical OR.
struct ActorMovementModifiers {
explicit ActorMovementModifiers(Json const& config = Json());
Json toJson() const;
// Combines each modifier value through addition.
ActorMovementModifiers combine(ActorMovementModifiers const& rhs) const;
float groundMovementModifier;
float liquidMovementModifier;
float speedModifier;
float airJumpModifier;
float liquidJumpModifier;
bool runningSuppressed;
bool jumpingSuppressed;
// Suppresses left, right, down, crouch, jump, and fly controls
bool movementSuppressed;
bool facingSuppressed;
};
DataStream& operator>>(DataStream& ds, ActorMovementModifiers& movementModifiers);
DataStream& operator<<(DataStream& ds, ActorMovementModifiers const& movementModifiers);
class ActorMovementController : public MovementController {
public:
// Constructs an ActorMovementController with parameters loaded from sensible
// defaults, and the given parameters (if any) applied on top of them.
explicit ActorMovementController(ActorMovementParameters const& parameters = ActorMovementParameters());
// Currently active parameters.
ActorMovementParameters const& baseParameters() const;
// Apply any set parameters from the given set on top of the current set.
void updateBaseParameters(ActorMovementParameters const& parameters);
// Reset the parameters from the sensible defaults, and apply the given
// parameters (if any) on top of them.
void resetBaseParameters(ActorMovementParameters const& parameters = ActorMovementParameters());
// Currently active modifiers.
ActorMovementModifiers const& baseModifiers() const;
// Combine the given modifiers with the already active modifiers.
void updateBaseModifiers(ActorMovementModifiers const& modifiers);
// Reset all modifiers to the given values
void resetBaseModifiers(ActorMovementModifiers const& modifiers = ActorMovementModifiers());
// Stores and loads position, velocity, rotation, movingDirection,
// facingDirection, and crouching
Json storeState() const;
void loadState(Json const& state);
// Optionaly anchor this ActorMovementController to the given
// AnchorableEntity. position, rotation, and facing direction will be set
// based on the entity anchor alone every tick, and on slaved
// ActorMovementControllers it will be updated based on the actual slave-side
// AnchorableEntity state.
void setAnchorState(EntityAnchorState anchorState);
void resetAnchorState();
Maybe<EntityAnchorState> anchorState() const;
EntityAnchorConstPtr entityAnchor() const;
// ActorMovementController position and rotation honor the entity anchor, if
// an anchor is set.
Vec2F position() const;
float xPosition() const;
float yPosition() const;
float rotation() const;
PolyF collisionBody() const;
RectF localBoundBox() const;
RectF collisionBoundBox() const;
bool walking() const;
bool running() const;
Direction movingDirection() const;
Direction facingDirection() const;
bool crouching() const;
bool flying() const;
bool falling() const;
bool canJump() const;
bool jumping() const;
// Slightly different than onGround, in that this is sustained for a few
// extra frames of movement before it becomes false.
bool groundMovement() const;
bool liquidMovement() const;
bool pathfinding() const;
// Basic direct physics controls that can be called multiple times per
// update and will be combined.
void controlRotation(float rotationRate);
void controlAcceleration(Vec2F const& acceleration);
void controlForce(Vec2F const& force);
void controlApproachVelocity(Vec2F const& targetVelocity, float maxControlForce);
void controlApproachVelocityAlongAngle(float angle, float targetVelocity, float maxControlForce, bool positiveOnly = false);
void controlApproachXVelocity(float targetXVelocity, float maxControlForce);
void controlApproachYVelocity(float targetYVelocity, float maxControlForce);
// Apply ActorMovementParameters / ActorMovementModifiers only as long as
// the controls are active. Can be called multiple times per update and
// will be combined.
void controlParameters(ActorMovementParameters const& parameters);
void controlModifiers(ActorMovementModifiers const& modifiers);
// Higher level movement controls that use forces defined in the
// ActorMovementParameters. Calling more than once per update will override
// previous calls.
void controlMove(Direction direction, bool run = true);
void controlFace(Direction direction);
void controlDown();
void controlCrouch();
void controlJump(bool jumpEvenIfUnable = false);
void controlFly(Vec2F const& velocity);
Maybe<pair<Vec2F, bool>> pathMove(Vec2F const& pathPosition, bool run = false, Maybe<PlatformerAStar::Parameters> const& parameters = {});
Maybe<pair<Vec2F, bool>> controlPathMove(Vec2F const& pathPosition, bool run = false, Maybe<PlatformerAStar::Parameters> const& parameters = {});
// Clears all control data.
void clearControls();
// Integrates the ActorMovementController one WorldTimestep and applies all
// the control data and clears it for the next step.
void tickMaster();
void tickSlave();
private:
struct ApproachVelocityCommand {
Vec2F targetVelocity;
float maxControlForce;
};
struct ApproachVelocityAlongAngleCommand {
float alongAngle;
float targetVelocity;
float maxControlForce;
bool positiveOnly;
};
void applyMCParameters(ActorMovementParameters const& parameters);
void doSetAnchorState(Maybe<EntityAnchorState> anchorState);
ActorMovementParameters m_baseParameters;
ActorMovementModifiers m_baseModifiers;
// State data
NetElementBool m_walking;
NetElementBool m_running;
NetElementEnum<Direction> m_movingDirection;
NetElementEnum<Direction> m_facingDirection;
NetElementBool m_crouching;
NetElementBool m_flying;
NetElementBool m_falling;
NetElementBool m_canJump;
NetElementBool m_jumping;
NetElementBool m_groundMovement;
NetElementBool m_liquidMovement;
NetElementData<Maybe<EntityAnchorState>> m_anchorState;
EntityAnchorConstPtr m_entityAnchor;
// Command data
float m_controlRotationRate;
Vec2F m_controlAcceleration;
Vec2F m_controlForce;
List<ApproachVelocityCommand> m_controlApproachVelocities;
List<ApproachVelocityAlongAngleCommand> m_controlApproachVelocityAlongAngles;
Maybe<Direction> m_controlMove;
Maybe<Direction> m_controlFace;
bool m_controlRun;
bool m_controlCrouch;
bool m_controlDown;
bool m_controlJump;
bool m_controlJumpAnyway;
Maybe<Vec2F> m_controlFly;
Maybe<pair<Vec2F, bool>> m_controlPathMove;
Maybe<pair<Vec2F, bool>> m_pathMoveResult;
PathControllerPtr m_pathController;
ActorMovementParameters m_controlParameters;
ActorMovementModifiers m_controlModifiers;
// Internal state data
int m_fallThroughSustain;
bool m_lastControlJump;
bool m_lastControlDown;
GameTimer m_reJumpTimer;
Maybe<GameTimer> m_jumpHoldTimer;
GameTimer m_groundMovementSustainTimer;
// Target horizontal velocity for walking / running
float m_targetHorizontalAmbulatingVelocity;
};
class PathController {
public:
PathController(World* world);
PlatformerAStar::Parameters const& parameters();
void setParameters(PlatformerAStar::Parameters const& parameters);
void reset();
bool pathfinding() const;
Maybe<Vec2F> targetPosition() const;
Maybe<Direction> facing() const;
Maybe<PlatformerAStar::Action> curAction() const;
// return true for reaching goal, false for failing to find path, nothing for running
Maybe<bool> findPath(ActorMovementController& movementController, Vec2F const& targetPosition);
Maybe<bool> move(ActorMovementController& movementController, ActorMovementParameters const& parameters, ActorMovementModifiers const& modifiers, bool run, float dt);
private:
bool validateEdge(ActorMovementController& movementController, PlatformerAStar::Edge const& edge);
bool movingCollision(ActorMovementController& movementController, PolyF const& collisionPoly);
private:
bool onGround(ActorMovementController const& movementController, Vec2F const& position, CollisionSet const& collisionSet) const;
World* m_world;
PlatformerAStar::Parameters m_parameters;
Maybe<Vec2F> m_startPosition;
Maybe<Vec2F> m_targetPosition;
PlatformerAStar::PathFinderPtr m_pathFinder;
Maybe<Direction> m_controlFace;
size_t m_edgeIndex;
float m_edgeTimer;
Maybe<PlatformerAStar::Path> m_path;
};
}
#endif