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

summaryrefslogtreecommitdiff
path: root/source/game/StarMovementController.hpp
blob: 2dfe8e7dd70d88ff317f8d3549f2d9464a945676 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#pragma once

#include "StarJson.hpp"
#include "StarMaybe.hpp"
#include "StarNetElementSystem.hpp"
#include "StarWorld.hpp"
#include "StarPhysicsEntity.hpp"

namespace Star {

STAR_EXCEPTION(MovementControllerException, StarException);

STAR_CLASS(MovementController);

// List of all movement parameters that define a specific sort of movable
// object.  Each parameter is optional so that this structure can be used to
// selectively merge a specific set of parameters on top of another.
struct MovementParameters {
  // Load sensible defaults from a config file.
  static MovementParameters sensibleDefaults();

  // Construct parameters from config with only those specified in the config
  // set, if any.
  MovementParameters(Json const& config = Json());

  // Merge the given set of movement parameters on top of this one, with any
  // set parameters in rhs overwriting the ones in this set.
  MovementParameters merge(MovementParameters const& rhs) const;

  Json toJson() const;

  Maybe<float> mass;
  Maybe<float> gravityMultiplier;
  Maybe<float> liquidBuoyancy;
  Maybe<float> airBuoyancy;
  Maybe<float> bounceFactor;
  // If set to true, during an update that has more than one internal movement
  // step, the movement will stop on the first bounce.
  Maybe<bool> stopOnFirstBounce;
  // Cheat when sliding on the ground, by trying to correct upwards before
  // other directions (within a set limit).  Allows smooth sliding along
  // horizontal ground without losing horizontal speed.
  Maybe<bool> enableSurfaceSlopeCorrection;
  Maybe<float> slopeSlidingFactor;
  Maybe<float> maxMovementPerStep;
  Maybe<float> maximumCorrection;
  Maybe<float> speedLimit;
  Maybe<float> discontinuityThreshold;

  Maybe<PolyF> collisionPoly;

  Maybe<bool> stickyCollision;
  Maybe<float> stickyForce;

  Maybe<float> airFriction;
  Maybe<float> liquidFriction;
  Maybe<float> groundFriction;

  Maybe<bool> collisionEnabled;
  Maybe<bool> frictionEnabled;
  Maybe<bool> gravityEnabled;

  Maybe<bool> ignorePlatformCollision;
  Maybe<float> maximumPlatformCorrection;
  Maybe<float> maximumPlatformCorrectionVelocityFactor;

  Maybe<StringSet> physicsEffectCategories;

  Maybe<int> restDuration;
};

DataStream& operator>>(DataStream& ds, MovementParameters& movementParameters);
DataStream& operator<<(DataStream& ds, MovementParameters const& movementParameters);

class MovementController : public NetElementGroup {
public:
  // Constructs a MovementController with parameters loaded from sensible
  // defaults, and the given parameters (if any) applied on top of them.
  explicit MovementController(MovementParameters const& parameters = MovementParameters());

  MovementParameters const& parameters() const;

  // Apply any set parameters from the given set on top of the current set.
  void applyParameters(MovementParameters const& parameters);

  // Reset the parameters from the sensible defaults, and apply the given
  // parameters (if any) on top of them.
  void resetParameters(MovementParameters const& parameters = MovementParameters());

  // Stores and loads position, velocity, and rotation.
  Json storeState() const;
  void loadState(Json const& state);

  // Currently active mass parameter
  float mass() const;

  // Currently active collisionPoly parameter
  PolyF const& collisionPoly() const;
  void setCollisionPoly(PolyF const& poly);

  Vec2F position() const;
  float xPosition() const;
  float yPosition() const;

  Vec2F velocity() const;
  float xVelocity() const;
  float yVelocity() const;

  float rotation() const;

  // CollisionPoly rotated and translated by position
  PolyF collisionBody() const;

  // Gets the bounding box of the collisionPoly() rotated by current rotation,
  // but not translated into world space
  RectF localBoundBox() const;

  // Shorthand for getting the bound box of the current collisionBody()
  RectF collisionBoundBox() const;

  // Is the collision body colliding with any collision geometry.
  bool isColliding() const;
  // Is the collision body colliding with special "Null" collision blocks.
  bool isNullColliding() const;

  // Is the body currently stuck in an un-solvable collision.
  bool isCollisionStuck() const;

  // If this body is sticking, this is the angle toward the surface it's stuck to
  Maybe<float> stickingDirection() const;

  // From 0.0 to 1.0, the amount of the collision body (or if the collision
  // body is null, just the center position) that is in liquid.
  float liquidPercentage() const;

  // Returns the liquid that the body is most in, if any
  LiquidId liquidId() const;

  bool onGround() const;
  bool zeroG() const;

  bool atWorldLimit(bool bottomOnly = false) const;

  void setPosition(Vec2F position);
  void setXPosition(float xPosition);
  void setYPosition(float yPosition);

  void translate(Vec2F const& direction);

  void setVelocity(Vec2F velocity);
  void setXVelocity(float xVelocity);
  void setYVelocity(float yVelocity);

  void addMomentum(Vec2F const& momentum);

  void setRotation(float angle);

  // Apply one timestep of rotation.
  void rotate(float rotationRate);

  // Apply one timestep of acceleration.
  void accelerate(Vec2F const& acceleration);

  // Apply one timestep of force.
  void force(Vec2F const& force);

  // Apply up to the maxControlForce of force to approach the given velocity.
  void approachVelocity(Vec2F const& targetVelocity, float maxControlForce);

  // Approach a velocity in the given angle, ignoring the component of velocity
  // normal to that angle.  If positiveOnly is true, then only approaches the
  // velocity by applying force in the direction of the given angle, never
  // opposite it, so avoids slowing down.
  void approachVelocityAlongAngle(float angle, float targetVelocity, float maxControlForce, bool positiveOnly = false);

  // Shorthand for approachVelocityAlongAngle with 0 and pi/2.
  void approachXVelocity(float targetXVelocity, float maxControlForce);
  void approachYVelocity(float targetYVelocity, float maxControlForce);

  void init(World* world);
  void uninit();

  // Stores dt value for Lua calls.
  void setTimestep(float dt);

  // Integrates the ActorMovementController one GlobalTimestep and applies all
  // forces.
  void tickMaster(float dt);

  // Does not integrate, only tracks master state and updates non-networked
  // fields based on local data
  void tickSlave(float dt);

  void setIgnorePhysicsEntities(Set<EntityId> ignorePhysicsEntities);
  // iterate over all physics entity collision polys in the region, iteration stops if the callback returns false
  void forEachMovingCollision(RectF const& region, function<bool(MovingCollisionId, PhysicsMovingCollision, PolyF, RectF)> callback);

protected:
  // forces the movement controller onGround status, used when manually controlling movement outside the movement controller
  void updateForceRegions(float dt);
  void updateLiquidPercentage();
  void setOnGround(bool onGround);

  // whether force regions were applied in the last update
  bool appliedForceRegion() const;
  // The collision correction applied during the most recent update, if any.
  Vec2F collisionCorrection() const;
  // Horizontal slope of the ground the collision body has collided with, if
  // any.
  Vec2F surfaceSlope() const;
  // Velocity of the surface that the body is resting on, if any
  Vec2F surfaceVelocity() const;

  World* world();

private:
  struct CollisionResult {
    Vec2F movement;
    Vec2F correction;
    Maybe<MovingCollisionId> surfaceMovingCollisionId;
    bool isStuck;
    bool onGround;
    Vec2F groundSlope;
    CollisionKind collisionKind;
  };

  struct CollisionSeparation {
    Vec2F correction;
    bool solutionFound;
    Maybe<MovingCollisionId> movingCollisionId;
    CollisionKind collisionKind;
  };

  struct CollisionPoly {
    PolyF poly;
    RectF polyBounds;
    Vec2F sortPosition;
    Maybe<MovingCollisionId> movingCollisionId;
    CollisionKind collisionKind;
    float sortDistance;
  };

  static CollisionKind maxOrNullCollision(CollisionKind a, CollisionKind b);
  static CollisionResult collisionMove(List<CollisionPoly>& collisionPolys, PolyF const& body, Vec2F const& movement,
      bool ignorePlatforms, bool enableSurfaceSlopeCorrection, float maximumCorrection, float maximumPlatformCorrection, Vec2F sortCenter, float dt);
  static CollisionSeparation collisionSeparate(List<CollisionPoly>& collisionPolys, PolyF const& poly,
      bool ignorePlatforms, float maximumPlatformCorrection, Vec2F const& sortCenter, bool upward, float separationTolerance);

  void updateParameters(MovementParameters parameters);
  void updatePositionInterpolators();

  void queryCollisions(RectF const& region);

  float gravity();

  MovementParameters m_parameters;

  World* m_world;

  Set<EntityId> m_ignorePhysicsEntities;

  NetElementData<PolyF> m_collisionPoly;
  NetElementFloat m_mass;
  NetElementFloat m_xPosition;
  NetElementFloat m_yPosition;
  NetElementFloat m_xVelocity;
  NetElementFloat m_yVelocity;
  NetElementFloat m_rotation;

  NetElementBool m_colliding;
  NetElementBool m_collisionStuck;
  NetElementBool m_nullColliding;
  NetElementData<Maybe<float>> m_stickingDirection;
  NetElementBool m_onGround;
  NetElementBool m_zeroG;

  float m_liquidPercentage;
  LiquidId m_liquidId;

  NetElementData<Maybe<MovingCollisionId>> m_surfaceMovingCollision;
  NetElementFloat m_xRelativeSurfaceMovingCollisionPosition;
  NetElementFloat m_yRelativeSurfaceMovingCollisionPosition;

  bool m_appliedForceRegion;
  Vec2F m_collisionCorrection;
  Vec2F m_surfaceSlope;
  Vec2F m_surfaceMovingCollisionPosition;
  Vec2F m_surfaceVelocity;
  Vec2F m_environmentVelocity;

  bool m_resting;
  int m_restTicks;
  float m_timeStep;

  List<CollisionPoly> m_workingCollisions;
  List<PolyF> m_collisionBuffers;
};

}