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