summary refs log tree commit diff stats
path: root/src/systems/pondering.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/systems/pondering.cpp')
-rw-r--r--src/systems/pondering.cpp1232
1 files changed, 632 insertions, 600 deletions
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4aa47f2..e6417eb 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp
@@ -15,15 +15,6 @@
15 15
16void PonderingSystem::tick(double dt) 16void PonderingSystem::tick(double dt)
17{ 17{
18 auto& realizable = game_.getEntityManager().
19 getComponent<RealizableComponent>(
20 game_.getSystemManager().getSystem<RealizingSystem>().getSingleton());
21
22 id_type mapEntity = realizable.activeMap;
23
24 auto& mappable = game_.getEntityManager().
25 getComponent<MappableComponent>(mapEntity);
26
27 auto entities = game_.getEntityManager().getEntitiesWithComponents< 18 auto entities = game_.getEntityManager().getEntitiesWithComponents<
28 PonderableComponent, 19 PonderableComponent,
29 TransformableComponent>(); 20 TransformableComponent>();
@@ -33,750 +24,791 @@ void PonderingSystem::tick(double dt)
33 auto& ponderable = game_.getEntityManager(). 24 auto& ponderable = game_.getEntityManager().
34 getComponent<PonderableComponent>(entity); 25 getComponent<PonderableComponent>(entity);
35 26
36 if (!ponderable.active || ponderable.frozen) 27 // We will recursively process ferried bodies after their ferries have been
28 // processed, so hold off on processing ferried bodies at the top level.
29 if (ponderable.ferried)
37 { 30 {
38 continue; 31 continue;
39 } 32 }
40 33
41 auto& transformable = game_.getEntityManager(). 34 tickBody(
42 getComponent<TransformableComponent>(entity); 35 entity,
36 dt,
37 entities);
38 }
39}
43 40
44 // Accelerate 41void PonderingSystem::initializeBody(
45 ponderable.velX += ponderable.accelX * dt; 42 id_type entity,
46 ponderable.velY += ponderable.accelY * dt; 43 PonderableComponent::Type type)
44{
45 auto& ponderable = game_.getEntityManager().
46 emplaceComponent<PonderableComponent>(entity, type);
47 47
48 if ((ponderable.type == PonderableComponent::Type::freefalling) 48 if (type == PonderableComponent::Type::freefalling)
49 && (ponderable.velY > TERMINAL_VELOCITY)) 49 {
50 { 50 ponderable.accelY = NORMAL_GRAVITY;
51 ponderable.velY = TERMINAL_VELOCITY; 51 }
52 } 52}
53 53
54 const double oldX = transformable.x; 54void PonderingSystem::initPrototype(id_type prototype)
55 const double oldY = transformable.y; 55{
56 const double oldRight = oldX + transformable.w; 56 auto& ponderable = game_.getEntityManager().
57 const double oldBottom = oldY + transformable.h; 57 getComponent<PonderableComponent>(prototype);
58 58
59 CollisionResult result; 59 ponderable.velX = 0.0;
60 ponderable.velY = 0.0;
61 ponderable.accelX = 0.0;
62 ponderable.accelY = 0.0;
63 ponderable.grounded = false;
64 ponderable.frozen = false;
65 ponderable.collidable = true;
66 ponderable.ferried = false;
67 ponderable.passengers.clear();
68}
60 69
61 if (ponderable.ferried) 70void PonderingSystem::unferry(id_type entity)
62 { 71{
63 auto& ferryTrans = game_.getEntityManager(). 72 auto& ponderable = game_.getEntityManager().
64 getComponent<TransformableComponent>(ponderable.ferry); 73 getComponent<PonderableComponent>(entity);
65
66 result.newX = ferryTrans.x + ponderable.relX;
67 result.newY = ferryTrans.y + ponderable.relY;
68 } else {
69 result.newX = transformable.x;
70 result.newY = transformable.y;
71 }
72 74
73 result.newX += ponderable.velX * dt; 75 if (ponderable.ferried)
74 result.newY += ponderable.velY * dt; 76 {
77 ponderable.ferried = false;
75 78
76 bool oldGrounded = ponderable.grounded; 79 auto& ferryPonder = game_.getEntityManager().
77 ponderable.grounded = false; 80 getComponent<PonderableComponent>(ponderable.ferry);
78 81
79 // Find horizontal collisions. 82 ferryPonder.passengers.erase(entity);
80 if (result.newX < oldX) 83 }
81 { 84}
82 bool boundaryCollision = false;
83 auto it = mappable.leftBoundaries.lower_bound(oldX);
84 85
85 // Find the axis distance of the closest environmental boundary. 86void PonderingSystem::tickBody(
86 for (; 87 id_type entity,
87 (it != std::end(mappable.leftBoundaries)) && 88 double dt,
88 (it->first >= result.newX); 89 const std::set<id_type>& entities)
89 it++) 90{
90 { 91 auto& ponderable = game_.getEntityManager().
91 // Check that the boundary is in range for the other axis. 92 getComponent<PonderableComponent>(entity);
92 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
93 {
94 // We have a collision!
95 boundaryCollision = true;
96 93
97 break; 94 if (!ponderable.active || ponderable.frozen)
98 } 95 {
99 } 96 continue;
97 }
100 98
101 // Find a list of potential colliders, sorted so that the closest is 99 auto& realizable = game_.getEntityManager().
102 // first. 100 getComponent<RealizableComponent>(
103 std::vector<id_type> colliders; 101 game_.getSystemManager().getSystem<RealizingSystem>().getSingleton());
104 102
105 for (id_type collider : entities) 103 id_type mapEntity = realizable.activeMap;
106 {
107 // Can't collide with self.
108 if (collider == entity)
109 {
110 continue;
111 }
112 104
113 auto& colliderPonder = game_.getEntityManager(). 105 auto& mappable = game_.getEntityManager().
114 getComponent<PonderableComponent>(collider); 106 getComponent<MappableComponent>(mapEntity);
115 107
116 // Only check objects that are active. 108 auto& transformable = game_.getEntityManager().
117 if (!colliderPonder.active) 109 getComponent<TransformableComponent>(entity);
118 {
119 continue;
120 }
121 110
122 auto& colliderTrans = game_.getEntityManager(). 111 // Accelerate
123 getComponent<TransformableComponent>(collider); 112 ponderable.velX += ponderable.accelX * dt;
124 113 ponderable.velY += ponderable.accelY * dt;
125 // Check if the entity would move into the potential collider,
126 if ((colliderTrans.x + colliderTrans.w > result.newX) &&
127 // that it wasn't already colliding,
128 (colliderTrans.x + colliderTrans.w <= oldX) &&
129 // that the position on the other axis is in range,
130 (colliderTrans.y + colliderTrans.h > oldY) &&
131 (colliderTrans.y < oldBottom) &&
132 // and that the collider is not farther away than the environmental
133 // boundary.
134 (!boundaryCollision ||
135 (colliderTrans.x + colliderTrans.w >= it->first)))
136 {
137 colliders.push_back(collider);
138 }
139 }
140 114
141 std::sort( 115 if ((ponderable.type == PonderableComponent::Type::freefalling)
142 std::begin(colliders), 116 && (ponderable.velY > TERMINAL_VELOCITY))
143 std::end(colliders), 117 {
144 [&] (id_type left, id_type right) { 118 ponderable.velY = TERMINAL_VELOCITY;
145 auto& leftTrans = game_.getEntityManager(). 119 }
146 getComponent<TransformableComponent>(left);
147 120
148 auto& rightTrans = game_.getEntityManager(). 121 const double oldX = transformable.x;
149 getComponent<TransformableComponent>(right); 122 const double oldY = transformable.y;
123 const double oldRight = oldX + transformable.w;
124 const double oldBottom = oldY + transformable.h;
150 125
151 return (rightTrans.x < leftTrans.x); 126 CollisionResult result;
152 });
153 127
154 for (id_type collider : colliders) 128 if (ponderable.ferried)
155 { 129 {
156 auto& colliderTrans = game_.getEntityManager(). 130 auto& ferryTrans = game_.getEntityManager().
157 getComponent<TransformableComponent>(collider); 131 getComponent<TransformableComponent>(ponderable.ferry);
132
133 result.newX = ferryTrans.x + ponderable.relX;
134 result.newY = ferryTrans.y + ponderable.relY;
135 } else {
136 result.newX = transformable.x;
137 result.newY = transformable.y;
138 }
158 139
159 // Check if the entity would still move into the potential collider. 140 result.newX += ponderable.velX * dt;
160 if (colliderTrans.x + colliderTrans.w <= result.newX) 141 result.newY += ponderable.velY * dt;
161 {
162 break;
163 }
164 142
165 auto& colliderPonder = game_.getEntityManager(). 143 bool oldGrounded = ponderable.grounded;
166 getComponent<PonderableComponent>(collider); 144 ponderable.grounded = false;
167 145
168 processCollision( 146 // Find horizontal collisions.
169 entity, 147 if (result.newX < oldX)
170 collider, 148 {
171 Direction::left, 149 bool boundaryCollision = false;
172 colliderPonder.colliderType, 150 auto it = mappable.leftBoundaries.lower_bound(oldX);
173 colliderTrans.x + colliderTrans.w, 151
174 colliderTrans.y, 152 // Find the axis distance of the closest environmental boundary.
175 colliderTrans.y + colliderTrans.h, 153 for (;
176 result); 154 (it != std::end(mappable.leftBoundaries)) &&
155 (it->first >= result.newX);
156 it++)
157 {
158 // Check that the boundary is in range for the other axis.
159 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
160 {
161 // We have a collision!
162 boundaryCollision = true;
177 163
178 if (result.stopProcessing) 164 break;
179 {
180 break;
181 }
182 } 165 }
166 }
183 167
184 // If movement hasn't been stopped by an intermediary object, and 168 // Find a list of potential colliders, sorted so that the closest is
185 // collision checking hasn't been stopped, process the environmental 169 // first.
186 // boundaries closest to the entity. 170 std::vector<id_type> colliders;
187 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
188 {
189 double boundaryAxis = it->first;
190 171
191 for (; 172 for (id_type collider : entities)
192 (it != std::end(mappable.leftBoundaries)) &&
193 (it->first == boundaryAxis);
194 it++)
195 {
196 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
197 {
198 processCollision(
199 entity,
200 mapEntity,
201 Direction::left,
202 it->second.type,
203 it->first,
204 it->second.lower,
205 it->second.upper,
206 result);
207
208 if (result.stopProcessing)
209 {
210 break;
211 }
212 }
213 }
214 }
215 } else if (result.newX > oldX)
216 { 173 {
217 bool boundaryCollision = false; 174 // Can't collide with self.
218 auto it = mappable.rightBoundaries.lower_bound(oldRight); 175 if (collider == entity)
219
220 // Find the axis distance of the closest environmental boundary.
221 for (;
222 (it != std::end(mappable.rightBoundaries))
223 && (it->first <= (result.newX + transformable.w));
224 it++)
225 { 176 {
226 // Check that the boundary is in range for the other axis. 177 continue;
227 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
228 {
229 // We have a collision!
230 boundaryCollision = true;
231
232 break;
233 }
234 } 178 }
235 179
236 // Find a list of potential colliders, sorted so that the closest is 180 auto& colliderPonder = game_.getEntityManager().
237 // first. 181 getComponent<PonderableComponent>(collider);
238 std::vector<id_type> colliders;
239 182
240 for (id_type collider : entities) 183 // Only check objects that are active.
184 if (!colliderPonder.active)
241 { 185 {
242 // Can't collide with self. 186 continue;
243 if (collider == entity) 187 }
244 {
245 continue;
246 }
247
248 auto& colliderPonder = game_.getEntityManager().
249 getComponent<PonderableComponent>(collider);
250
251 // Only check objects that are active.
252 if (!colliderPonder.active)
253 {
254 continue;
255 }
256 188
257 auto& colliderTrans = game_.getEntityManager(). 189 auto& colliderTrans = game_.getEntityManager().
258 getComponent<TransformableComponent>(collider); 190 getComponent<TransformableComponent>(collider);
259 191
260 // Check if the entity would move into the potential collider, 192 // Check if the entity would move into the potential collider,
261 if ((colliderTrans.x < result.newX + transformable.w) && 193 if ((colliderTrans.x + colliderTrans.w > result.newX) &&
262 // that it wasn't already colliding, 194 // that it wasn't already colliding,
263 (colliderTrans.x >= oldRight) && 195 (colliderTrans.x + colliderTrans.w <= oldX) &&
264 // that the position on the other axis is in range, 196 // that the position on the other axis is in range,
265 (colliderTrans.y + colliderTrans.h > oldY) && 197 (colliderTrans.y + colliderTrans.h > oldY) &&
266 (colliderTrans.y < oldBottom) && 198 (colliderTrans.y < oldBottom) &&
267 // and that the collider is not farther away than the environmental 199 // and that the collider is not farther away than the environmental
268 // boundary. 200 // boundary.
269 (!boundaryCollision || (colliderTrans.x <= it->first))) 201 (!boundaryCollision ||
270 { 202 (colliderTrans.x + colliderTrans.w >= it->first)))
271 colliders.push_back(collider); 203 {
272 } 204 colliders.push_back(collider);
273 } 205 }
206 }
274 207
275 std::sort( 208 std::sort(
276 std::begin(colliders), 209 std::begin(colliders),
277 std::end(colliders), 210 std::end(colliders),
278 [&] (id_type left, id_type right) { 211 [&] (id_type left, id_type right) {
279 auto& leftTrans = game_.getEntityManager(). 212 auto& leftTrans = game_.getEntityManager().
280 getComponent<TransformableComponent>(left); 213 getComponent<TransformableComponent>(left);
281 214
282 auto& rightTrans = game_.getEntityManager(). 215 auto& rightTrans = game_.getEntityManager().
283 getComponent<TransformableComponent>(right); 216 getComponent<TransformableComponent>(right);
284 217
285 return (leftTrans.x < rightTrans.x); 218 return (rightTrans.x < leftTrans.x);
286 }); 219 });
287 220
288 for (id_type collider : colliders) 221 for (id_type collider : colliders)
289 { 222 {
290 auto& colliderTrans = game_.getEntityManager(). 223 auto& colliderTrans = game_.getEntityManager().
291 getComponent<TransformableComponent>(collider); 224 getComponent<TransformableComponent>(collider);
292 225
293 // Check if the entity would still move into the potential collider. 226 // Check if the entity would still move into the potential collider.
294 if (colliderTrans.x >= result.newX + transformable.w) 227 if (colliderTrans.x + colliderTrans.w <= result.newX)
295 { 228 {
296 break; 229 break;
297 } 230 }
298 231
299 auto& colliderPonder = game_.getEntityManager(). 232 auto& colliderPonder = game_.getEntityManager().
300 getComponent<PonderableComponent>(collider); 233 getComponent<PonderableComponent>(collider);
301 234
302 processCollision( 235 processCollision(
303 entity, 236 entity,
304 collider, 237 collider,
305 Direction::right, 238 Direction::left,
306 colliderPonder.colliderType, 239 colliderPonder.colliderType,
307 colliderTrans.x, 240 colliderTrans.x + colliderTrans.w,
308 colliderTrans.y, 241 colliderTrans.y,
309 colliderTrans.y + colliderTrans.h, 242 colliderTrans.y + colliderTrans.h,
310 result); 243 result);
311 244
312 if (result.stopProcessing) 245 if (result.stopProcessing)
313 { 246 {
314 break; 247 break;
315 }
316 } 248 }
249 }
317 250
318 // If movement hasn't been stopped by an intermediary object, and 251 // If movement hasn't been stopped by an intermediary object, and
319 // collision checking hasn't been stopped, process the environmental 252 // collision checking hasn't been stopped, process the environmental
320 // boundaries closest to the entity. 253 // boundaries closest to the entity.
321 if (!result.stopProcessing && !result.touchedWall && boundaryCollision) 254 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
322 { 255 {
323 double boundaryAxis = it->first; 256 double boundaryAxis = it->first;
324 257
325 for (; 258 for (;
326 (it != std::end(mappable.rightBoundaries)) && 259 (it != std::end(mappable.leftBoundaries)) &&
327 (it->first == boundaryAxis); 260 (it->first == boundaryAxis);
328 it++) 261 it++)
262 {
263 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
329 { 264 {
330 if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) 265 processCollision(
266 entity,
267 mapEntity,
268 Direction::left,
269 it->second.type,
270 it->first,
271 it->second.lower,
272 it->second.upper,
273 result);
274
275 if (result.stopProcessing)
331 { 276 {
332 processCollision( 277 break;
333 entity,
334 mapEntity,
335 Direction::right,
336 it->second.type,
337 it->first,
338 it->second.lower,
339 it->second.upper,
340 result);
341
342 if (result.stopProcessing)
343 {
344 break;
345 }
346 } 278 }
347 } 279 }
348 } 280 }
349 } 281 }
350 282 } else if (result.newX > oldX)
351 // Find vertical collisions 283 {
352 result.touchedWall = false; 284 bool boundaryCollision = false;
353 285 auto it = mappable.rightBoundaries.lower_bound(oldRight);
354 if ((!result.stopProcessing) && (result.newY < oldY)) 286
287 // Find the axis distance of the closest environmental boundary.
288 for (;
289 (it != std::end(mappable.rightBoundaries))
290 && (it->first <= (result.newX + transformable.w));
291 it++)
355 { 292 {
356 bool boundaryCollision = false; 293 // Check that the boundary is in range for the other axis.
357 auto it = mappable.upBoundaries.lower_bound(oldY); 294 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
358
359 // Find the axis distance of the closest environmental boundary.
360 for (;
361 (it != std::end(mappable.upBoundaries)) &&
362 (it->first >= result.newY);
363 it++)
364 { 295 {
365 // Check that the boundary is in range for the other axis. 296 // We have a collision!
366 if ((result.newX + transformable.h > it->second.lower) && 297 boundaryCollision = true;
367 (result.newX < it->second.upper))
368 {
369 // We have a collision!
370 boundaryCollision = true;
371 298
372 break; 299 break;
373 }
374 } 300 }
301 }
375 302
376 // Find a list of potential colliders, sorted so that the closest is 303 // Find a list of potential colliders, sorted so that the closest is
377 // first. 304 // first.
378 std::vector<id_type> colliders; 305 std::vector<id_type> colliders;
379 306
380 for (id_type collider : entities) 307 for (id_type collider : entities)
308 {
309 // Can't collide with self.
310 if (collider == entity)
381 { 311 {
382 // Can't collide with self. 312 continue;
383 if (collider == entity) 313 }
384 {
385 continue;
386 }
387 314
388 auto& colliderPonder = game_.getEntityManager(). 315 auto& colliderPonder = game_.getEntityManager().
389 getComponent<PonderableComponent>(collider); 316 getComponent<PonderableComponent>(collider);
390 317
391 // Only check objects that are active. 318 // Only check objects that are active.
392 if (!colliderPonder.active) 319 if (!colliderPonder.active)
393 { 320 {
394 continue; 321 continue;
395 } 322 }
396 323
397 auto& colliderTrans = game_.getEntityManager(). 324 auto& colliderTrans = game_.getEntityManager().
398 getComponent<TransformableComponent>(collider); 325 getComponent<TransformableComponent>(collider);
399 326
400 // Check if the entity would move into the potential collider, 327 // Check if the entity would move into the potential collider,
401 if ((colliderTrans.y + colliderTrans.h > result.newY) && 328 if ((colliderTrans.x < result.newX + transformable.w) &&
402 // that it wasn't already colliding, 329 // that it wasn't already colliding,
403 (colliderTrans.y + colliderTrans.h <= oldY) && 330 (colliderTrans.x >= oldRight) &&
404 // that the position on the other axis is in range, 331 // that the position on the other axis is in range,
405 (colliderTrans.x + colliderTrans.w > result.newX) && 332 (colliderTrans.y + colliderTrans.h > oldY) &&
406 (colliderTrans.x < result.newX + transformable.w) && 333 (colliderTrans.y < oldBottom) &&
407 // and that the collider is not farther away than the environmental 334 // and that the collider is not farther away than the environmental
408 // boundary. 335 // boundary.
409 (!boundaryCollision || 336 (!boundaryCollision || (colliderTrans.x <= it->first)))
410 (colliderTrans.y + colliderTrans.h >= it->first))) 337 {
411 { 338 colliders.push_back(collider);
412 colliders.push_back(collider);
413 }
414 } 339 }
340 }
415 341
416 std::sort( 342 std::sort(
417 std::begin(colliders), 343 std::begin(colliders),
418 std::end(colliders), 344 std::end(colliders),
419 [&] (id_type left, id_type right) { 345 [&] (id_type left, id_type right) {
420 auto& leftTrans = game_.getEntityManager(). 346 auto& leftTrans = game_.getEntityManager().
421 getComponent<TransformableComponent>(left); 347 getComponent<TransformableComponent>(left);
422 348
423 auto& rightTrans = game_.getEntityManager(). 349 auto& rightTrans = game_.getEntityManager().
424 getComponent<TransformableComponent>(right); 350 getComponent<TransformableComponent>(right);
425 351
426 return (rightTrans.y < leftTrans.y); 352 return (leftTrans.x < rightTrans.x);
427 }); 353 });
428 354
429 for (id_type collider : colliders) 355 for (id_type collider : colliders)
430 { 356 {
431 auto& colliderTrans = game_.getEntityManager(). 357 auto& colliderTrans = game_.getEntityManager().
432 getComponent<TransformableComponent>(collider); 358 getComponent<TransformableComponent>(collider);
433 359
434 // Check if the entity would still move into the potential collider. 360 // Check if the entity would still move into the potential collider.
435 if (colliderTrans.y + colliderTrans.h <= result.newY) 361 if (colliderTrans.x >= result.newX + transformable.w)
436 { 362 {
437 break; 363 break;
438 } 364 }
439 365
440 auto& colliderPonder = game_.getEntityManager(). 366 auto& colliderPonder = game_.getEntityManager().
441 getComponent<PonderableComponent>(collider); 367 getComponent<PonderableComponent>(collider);
442 368
443 processCollision( 369 processCollision(
444 entity, 370 entity,
445 collider, 371 collider,
446 Direction::up, 372 Direction::right,
447 colliderPonder.colliderType, 373 colliderPonder.colliderType,
448 colliderTrans.y + colliderTrans.h, 374 colliderTrans.x,
449 colliderTrans.x, 375 colliderTrans.y,
450 colliderTrans.x + colliderTrans.w, 376 colliderTrans.y + colliderTrans.h,
451 result); 377 result);
452 378
453 if (result.stopProcessing) 379 if (result.stopProcessing)
454 { 380 {
455 break; 381 break;
456 }
457 } 382 }
383 }
458 384
459 // If movement hasn't been stopped by an intermediary object, and 385 // If movement hasn't been stopped by an intermediary object, and
460 // collision checking hasn't been stopped, process the environmental 386 // collision checking hasn't been stopped, process the environmental
461 // boundaries closest to the entity. 387 // boundaries closest to the entity.
462 if (!result.stopProcessing && !result.touchedWall && boundaryCollision) 388 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
463 { 389 {
464 double boundaryAxis = it->first; 390 double boundaryAxis = it->first;
465 391
466 for (; 392 for (;
467 (it != std::end(mappable.upBoundaries)) && 393 (it != std::end(mappable.rightBoundaries)) &&
468 (it->first == boundaryAxis); 394 (it->first == boundaryAxis);
469 it++) 395 it++)
396 {
397 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
470 { 398 {
471 if ((result.newX + transformable.w > it->second.lower) && 399 processCollision(
472 (result.newX < it->second.upper)) 400 entity,
401 mapEntity,
402 Direction::right,
403 it->second.type,
404 it->first,
405 it->second.lower,
406 it->second.upper,
407 result);
408
409 if (result.stopProcessing)
473 { 410 {
474 processCollision( 411 break;
475 entity,
476 mapEntity,
477 Direction::up,
478 it->second.type,
479 it->first,
480 it->second.lower,
481 it->second.upper,
482 result);
483
484 if (result.stopProcessing)
485 {
486 break;
487 }
488 } 412 }
489 } 413 }
490 } 414 }
491 } else if ((!result.stopProcessing) && (result.newY > oldY)) 415 }
492 { 416 }
493 bool boundaryCollision = false;
494 auto it = mappable.downBoundaries.lower_bound(oldBottom);
495 417
496 // Find the axis distance of the closest environmental boundary. 418 // Find vertical collisions
497 for (; 419 result.touchedWall = false;
498 (it != std::end(mappable.downBoundaries)) 420
499 && (it->first <= (result.newY + transformable.h)); 421 if ((!result.stopProcessing) && (result.newY < oldY))
500 it++) 422 {
423 bool boundaryCollision = false;
424 auto it = mappable.upBoundaries.lower_bound(oldY);
425
426 // Find the axis distance of the closest environmental boundary.
427 for (;
428 (it != std::end(mappable.upBoundaries)) &&
429 (it->first >= result.newY);
430 it++)
431 {
432 // Check that the boundary is in range for the other axis.
433 if ((result.newX + transformable.h > it->second.lower) &&
434 (result.newX < it->second.upper))
501 { 435 {
502 // Check that the boundary is in range for the other axis. 436 // We have a collision!
503 if ((result.newX + transformable.w > it->second.lower) && 437 boundaryCollision = true;
504 (result.newX < it->second.upper))
505 {
506 // We have a collision!
507 boundaryCollision = true;
508 438
509 break; 439 break;
510 }
511 } 440 }
441 }
512 442
513 // Find a list of potential colliders, sorted so that the closest is 443 // Find a list of potential colliders, sorted so that the closest is
514 // first. 444 // first.
515 std::vector<id_type> colliders; 445 std::vector<id_type> colliders;
516 446
517 for (id_type collider : entities) 447 for (id_type collider : entities)
448 {
449 // Can't collide with self.
450 if (collider == entity)
518 { 451 {
519 // Can't collide with self. 452 continue;
520 if (collider == entity) 453 }
521 {
522 continue;
523 }
524 454
525 auto& colliderPonder = game_.getEntityManager(). 455 auto& colliderPonder = game_.getEntityManager().
526 getComponent<PonderableComponent>(collider); 456 getComponent<PonderableComponent>(collider);
527 457
528 // Only check objects that are active. 458 // Only check objects that are active.
529 if (!colliderPonder.active) 459 if (!colliderPonder.active)
530 { 460 {
531 continue; 461 continue;
532 } 462 }
533 463
534 auto& colliderTrans = game_.getEntityManager(). 464 auto& colliderTrans = game_.getEntityManager().
535 getComponent<TransformableComponent>(collider); 465 getComponent<TransformableComponent>(collider);
536 466
537 // Check if the entity would move into the potential collider, 467 // Check if the entity would move into the potential collider,
538 if ((colliderTrans.y < result.newY + transformable.h) && 468 if ((colliderTrans.y + colliderTrans.h > result.newY) &&
539 // that it wasn't already colliding, 469 // that it wasn't already colliding,
540 (colliderTrans.y >= oldBottom) && 470 (colliderTrans.y + colliderTrans.h <= oldY) &&
541 // that the position on the other axis is in range, 471 // that the position on the other axis is in range,
542 (colliderTrans.x + colliderTrans.w > result.newX) && 472 (colliderTrans.x + colliderTrans.w > result.newX) &&
543 (colliderTrans.x < result.newX + transformable.w) && 473 (colliderTrans.x < result.newX + transformable.w) &&
544 // and that the collider is not farther away than the environmental 474 // and that the collider is not farther away than the environmental
545 // boundary. 475 // boundary.
546 (!boundaryCollision || (colliderTrans.y <= it->first))) 476 (!boundaryCollision ||
547 { 477 (colliderTrans.y + colliderTrans.h >= it->first)))
548 colliders.push_back(collider); 478 {
549 } 479 colliders.push_back(collider);
550 } 480 }
481 }
551 482
552 std::sort( 483 std::sort(
553 std::begin(colliders), 484 std::begin(colliders),
554 std::end(colliders), 485 std::end(colliders),
555 [&] (id_type left, id_type right) { 486 [&] (id_type left, id_type right) {
556 auto& leftTrans = game_.getEntityManager(). 487 auto& leftTrans = game_.getEntityManager().
557 getComponent<TransformableComponent>(left); 488 getComponent<TransformableComponent>(left);
558 489
559 auto& rightTrans = game_.getEntityManager(). 490 auto& rightTrans = game_.getEntityManager().
560 getComponent<TransformableComponent>(right); 491 getComponent<TransformableComponent>(right);
561 492
562 return (leftTrans.y < rightTrans.y); 493 return (rightTrans.y < leftTrans.y);
563 }); 494 });
564 495
565 for (id_type collider : colliders) 496 for (id_type collider : colliders)
566 { 497 {
567 auto& colliderTrans = game_.getEntityManager(). 498 auto& colliderTrans = game_.getEntityManager().
568 getComponent<TransformableComponent>(collider); 499 getComponent<TransformableComponent>(collider);
569 500
570 // Check if the entity would still move into the potential collider. 501 // Check if the entity would still move into the potential collider.
571 if (colliderTrans.y >= result.newY + transformable.h) 502 if (colliderTrans.y + colliderTrans.h <= result.newY)
572 { 503 {
573 break; 504 break;
574 } 505 }
575 506
576 auto& colliderPonder = game_.getEntityManager(). 507 auto& colliderPonder = game_.getEntityManager().
577 getComponent<PonderableComponent>(collider); 508 getComponent<PonderableComponent>(collider);
578 509
579 processCollision( 510 processCollision(
580 entity, 511 entity,
581 collider, 512 collider,
582 Direction::down, 513 Direction::up,
583 colliderPonder.colliderType, 514 colliderPonder.colliderType,
584 colliderTrans.y, 515 colliderTrans.y + colliderTrans.h,
585 colliderTrans.x, 516 colliderTrans.x,
586 colliderTrans.x + colliderTrans.w, 517 colliderTrans.x + colliderTrans.w,
587 result); 518 result);
588 519
589 if (result.stopProcessing) 520 if (result.stopProcessing)
590 { 521 {
591 break; 522 break;
592 }
593 } 523 }
524 }
594 525
595 // If movement hasn't been stopped by an intermediary object, and 526 // If movement hasn't been stopped by an intermediary object, and
596 // collision checking hasn't been stopped, process the environmental 527 // collision checking hasn't been stopped, process the environmental
597 // boundaries closest to the entity. 528 // boundaries closest to the entity.
598 if (!result.stopProcessing && !result.touchedWall && boundaryCollision) 529 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
599 { 530 {
600 double boundaryAxis = it->first; 531 double boundaryAxis = it->first;
601 532
602 for (; 533 for (;
603 (it != std::end(mappable.downBoundaries)) && 534 (it != std::end(mappable.upBoundaries)) &&
604 (it->first == boundaryAxis); 535 (it->first == boundaryAxis);
605 it++) 536 it++)
537 {
538 if ((result.newX + transformable.w > it->second.lower) &&
539 (result.newX < it->second.upper))
606 { 540 {
607 if ((result.newX + transformable.w > it->second.lower) && 541 processCollision(
608 (result.newX < it->second.upper)) 542 entity,
543 mapEntity,
544 Direction::up,
545 it->second.type,
546 it->first,
547 it->second.lower,
548 it->second.upper,
549 result);
550
551 if (result.stopProcessing)
609 { 552 {
610 processCollision( 553 break;
611 entity,
612 mapEntity,
613 Direction::down,
614 it->second.type,
615 it->first,
616 it->second.lower,
617 it->second.upper,
618 result);
619
620 if (result.stopProcessing)
621 {
622 break;
623 }
624 } 554 }
625 } 555 }
626 } 556 }
627 } 557 }
558 } else if ((!result.stopProcessing) && (result.newY > oldY))
559 {
560 bool boundaryCollision = false;
561 auto it = mappable.downBoundaries.lower_bound(oldBottom);
562
563 // Find the axis distance of the closest environmental boundary.
564 for (;
565 (it != std::end(mappable.downBoundaries))
566 && (it->first <= (result.newY + transformable.h));
567 it++)
568 {
569 // Check that the boundary is in range for the other axis.
570 if ((result.newX + transformable.w > it->second.lower) &&
571 (result.newX < it->second.upper))
572 {
573 // We have a collision!
574 boundaryCollision = true;
628 575
629 // Move 576 break;
630 transformable.x = result.newX; 577 }
631 transformable.y = result.newY; 578 }
579
580 // Find a list of potential colliders, sorted so that the closest is
581 // first.
582 std::vector<id_type> colliders;
632 583
633 // Perform cleanup for orientable entites 584 for (id_type collider : entities)
634 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity))
635 { 585 {
636 auto& orientable = game_.getEntityManager(). 586 // Can't collide with self.
637 getComponent<OrientableComponent>(entity); 587 if (collider == entity)
588 {
589 continue;
590 }
591
592 auto& colliderPonder = game_.getEntityManager().
593 getComponent<PonderableComponent>(collider);
638 594
639 // Handle changes in groundedness 595 // Only check objects that are active.
640 if (ponderable.grounded != oldGrounded) 596 if (!colliderPonder.active)
641 { 597 {
642 if (ponderable.grounded) 598 continue;
643 {
644 game_.getSystemManager().getSystem<OrientingSystem>().land(entity);
645 } else {
646 game_.getSystemManager().
647 getSystem<OrientingSystem>().startFalling(entity);
648 }
649 } 599 }
650 600
651 // Complete dropping, if necessary 601 auto& colliderTrans = game_.getEntityManager().
652 if (orientable.getDropState() == OrientableComponent::DropState::active) 602 getComponent<TransformableComponent>(collider);
603
604 // Check if the entity would move into the potential collider,
605 if ((colliderTrans.y < result.newY + transformable.h) &&
606 // that it wasn't already colliding,
607 (colliderTrans.y >= oldBottom) &&
608 // that the position on the other axis is in range,
609 (colliderTrans.x + colliderTrans.w > result.newX) &&
610 (colliderTrans.x < result.newX + transformable.w) &&
611 // and that the collider is not farther away than the environmental
612 // boundary.
613 (!boundaryCollision || (colliderTrans.y <= it->first)))
653 { 614 {
654 orientable.setDropState(OrientableComponent::DropState::none); 615 colliders.push_back(collider);
655 } 616 }
656 } 617 }
657 618
658 // Ferry or unferry as necessary 619 std::sort(
659 if ((ponderable.type == PonderableComponent::Type::freefalling) && 620 std::begin(colliders),
660 (ponderable.grounded != oldGrounded)) 621 std::end(colliders),
661 { 622 [&] (id_type left, id_type right) {
662 if (ponderable.grounded && 623 auto& leftTrans = game_.getEntityManager().
663 game_.getEntityManager(). 624 getComponent<TransformableComponent>(left);
664 hasComponent<PonderableComponent>(result.groundEntity)) 625
665 { 626 auto& rightTrans = game_.getEntityManager().
666 // The body is now being ferried 627 getComponent<TransformableComponent>(right);
667 auto& ferryPonder = game_.getEntityManager().
668 getComponent<PonderableComponent>(result.groundEntity);
669 628
670 ponderable.ferried = true; 629 return (leftTrans.y < rightTrans.y);
671 ponderable.ferry = result.groundEntity; 630 });
672 631
673 ferryPonder.passengers.insert(entity); 632 for (id_type collider : colliders)
674 } else if (ponderable.ferried) 633 {
634 auto& colliderTrans = game_.getEntityManager().
635 getComponent<TransformableComponent>(collider);
636
637 // Check if the entity would still move into the potential collider.
638 if (colliderTrans.y >= result.newY + transformable.h)
675 { 639 {
676 // The body is no longer being ferried 640 break;
677 unferry(entity);
678 } 641 }
679 }
680 642
681 // Update a ferry passenger's relative position 643 auto& colliderPonder = game_.getEntityManager().
682 if (ponderable.ferried) 644 getComponent<PonderableComponent>(collider);
683 {
684 auto& ferryTrans = game_.getEntityManager().
685 getComponent<TransformableComponent>(ponderable.ferry);
686 645
687 ponderable.relX = transformable.x - ferryTrans.x; 646 processCollision(
688 ponderable.relY = transformable.y - ferryTrans.y; 647 entity,
648 collider,
649 Direction::down,
650 colliderPonder.colliderType,
651 colliderTrans.y,
652 colliderTrans.x,
653 colliderTrans.x + colliderTrans.w,
654 result);
655
656 if (result.stopProcessing)
657 {
658 break;
659 }
689 } 660 }
690 661
691 // Move to an adjacent map, if necessary 662 // If movement hasn't been stopped by an intermediary object, and
692 if (result.adjacentlyWarping) 663 // collision checking hasn't been stopped, process the environmental
664 // boundaries closest to the entity.
665 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
693 { 666 {
694 double warpX = result.newX; 667 double boundaryAxis = it->first;
695 double warpY = result.newY;
696 668
697 switch (result.adjWarpDir) 669 for (;
670 (it != std::end(mappable.downBoundaries)) &&
671 (it->first == boundaryAxis);
672 it++)
698 { 673 {
699 case Direction::left: 674 if ((result.newX + transformable.w > it->second.lower) &&
675 (result.newX < it->second.upper))
700 { 676 {
701 warpX = GAME_WIDTH + WALL_GAP - transformable.w; 677 processCollision(
702 678 entity,
703 break; 679 mapEntity,
680 Direction::down,
681 it->second.type,
682 it->first,
683 it->second.lower,
684 it->second.upper,
685 result);
686
687 if (result.stopProcessing)
688 {
689 break;
690 }
704 } 691 }
692 }
693 }
694 }
705 695
706 case Direction::right: 696 // Move
707 { 697 transformable.x = result.newX;
708 warpX = -WALL_GAP; 698 transformable.y = result.newY;
709 699
710 break; 700 // Perform cleanup for orientable entites
711 } 701 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity))
702 {
703 auto& orientable = game_.getEntityManager().
704 getComponent<OrientableComponent>(entity);
712 705
713 case Direction::up: 706 // Handle changes in groundedness
714 { 707 if (ponderable.grounded != oldGrounded)
715 warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; 708 {
709 if (ponderable.grounded)
710 {
711 game_.getSystemManager().getSystem<OrientingSystem>().land(entity);
712 } else {
713 game_.getSystemManager().
714 getSystem<OrientingSystem>().startFalling(entity);
715 }
716 }
716 717
717 break; 718 // Complete dropping, if necessary
718 } 719 if (orientable.getDropState() == OrientableComponent::DropState::active)
720 {
721 orientable.setDropState(OrientableComponent::DropState::none);
722 }
723 }
719 724
720 case Direction::down: 725 // Ferry or unferry as necessary
721 { 726 if ((ponderable.type == PonderableComponent::Type::freefalling) &&
722 warpY = -WALL_GAP; 727 (ponderable.grounded != oldGrounded))
728 {
729 if (ponderable.grounded &&
730 game_.getEntityManager().
731 hasComponent<PonderableComponent>(result.groundEntity))
732 {
733 // The body is now being ferried
734 auto& ferryPonder = game_.getEntityManager().
735 getComponent<PonderableComponent>(result.groundEntity);
723 736
724 break; 737 ponderable.ferried = true;
725 } 738 ponderable.ferry = result.groundEntity;
726 }
727 739
728 game_.getSystemManager().getSystem<PlayingSystem>(). 740 ferryPonder.passengers.insert(entity);
729 changeMap( 741 } else if (ponderable.ferried)
730 entity, 742 {
731 result.adjWarpMapId, 743 // The body is no longer being ferried
732 warpX, 744 unferry(entity);
733 warpY);
734 } 745 }
735 } 746 }
736}
737 747
738void PonderingSystem::initializeBody( 748 // Update a ferry passenger's relative position
739 id_type entity, 749 if (ponderable.ferried)
740 PonderableComponent::Type type) 750 {
741{ 751 auto& ferryTrans = game_.getEntityManager().
742 auto& ponderable = game_.getEntityManager(). 752 getComponent<TransformableComponent>(ponderable.ferry);
743 emplaceComponent<PonderableComponent>(entity, type);
744 753
745 if (type == PonderableComponent::Type::freefalling) 754 ponderable.relX = transformable.x - ferryTrans.x;
755 ponderable.relY = transformable.y - ferryTrans.y;
756 }
757
758 // Handle ferry passengers
759 std::set<id_type> passengers = ponderable.passengers;
760
761 for (id_type passenger : passengers)
746 { 762 {
747 ponderable.accelY = NORMAL_GRAVITY; 763 tickBody(
764 passenger,
765 dt,
766 entities);
748 } 767 }
749}
750 768
751void PonderingSystem::initPrototype(id_type prototype) 769 // Move to an adjacent map, if necessary
752{ 770 if (result.adjacentlyWarping)
753 auto& ponderable = game_.getEntityManager(). 771 {
754 getComponent<PonderableComponent>(prototype); 772 double warpX = result.newX;
773 double warpY = result.newY;
755 774
756 ponderable.velX = 0.0; 775 switch (result.adjWarpDir)
757 ponderable.velY = 0.0; 776 {
758 ponderable.accelX = 0.0; 777 case Direction::left:
759 ponderable.accelY = 0.0; 778 {
760 ponderable.grounded = false; 779 warpX = GAME_WIDTH + WALL_GAP - transformable.w;
761 ponderable.frozen = false;
762 ponderable.collidable = true;
763 ponderable.ferried = false;
764 ponderable.passengers.clear();
765}
766 780
767void PonderingSystem::unferry(id_type entity) 781 break;
768{ 782 }
769 auto& ponderable = game_.getEntityManager().
770 getComponent<PonderableComponent>(entity);
771 783
772 if (ponderable.ferried) 784 case Direction::right:
773 { 785 {
774 ponderable.ferried = false; 786 warpX = -WALL_GAP;
775 787
776 auto& ferryPonder = game_.getEntityManager(). 788 break;
777 getComponent<PonderableComponent>(ponderable.ferry); 789 }
778 790
779 ferryPonder.passengers.erase(entity); 791 case Direction::up:
792 {
793 warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h;
794
795 break;
796 }
797
798 case Direction::down:
799 {
800 warpY = -WALL_GAP;
801
802 break;
803 }
804 }
805
806 game_.getSystemManager().getSystem<PlayingSystem>().
807 changeMap(
808 entity,
809 result.adjWarpMapId,
810 warpX,
811 warpY);
780 } 812 }
781} 813}
782 814