diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/collision.cpp | 97 | ||||
-rw-r--r-- | src/collision.h | 81 | ||||
-rw-r--r-- | src/components/mappable.h | 4 | ||||
-rw-r--r-- | src/components/ponderable.h | 16 | ||||
-rw-r--r-- | src/systems/mapping.cpp | 2 | ||||
-rw-r--r-- | src/systems/pondering.cpp | 882 | ||||
-rw-r--r-- | src/systems/pondering.h | 23 | ||||
-rw-r--r-- | src/systems/realizing.cpp | 4 |
8 files changed, 714 insertions, 395 deletions
diff --git a/src/collision.cpp b/src/collision.cpp deleted file mode 100644 index b747a90..0000000 --- a/src/collision.cpp +++ /dev/null | |||
@@ -1,97 +0,0 @@ | |||
1 | #include "collision.h" | ||
2 | |||
3 | bool Collision::operator<(const Collision& other) const | ||
4 | { | ||
5 | // Most important is the type of collision | ||
6 | if (type_ != other.type_) | ||
7 | { | ||
8 | return (static_cast<int>(type_) > static_cast<int>(other.type_)); | ||
9 | } | ||
10 | |||
11 | // Next, categorize the collisions arbitrarily based on direction | ||
12 | if (dir_ != other.dir_) | ||
13 | { | ||
14 | return (static_cast<int>(dir_) < static_cast<int>(other.dir_)); | ||
15 | } | ||
16 | |||
17 | // We want to process closer collisions first | ||
18 | if (axis_ != other.axis_) | ||
19 | { | ||
20 | switch (dir_) | ||
21 | { | ||
22 | case Direction::left: | ||
23 | case Direction::up: | ||
24 | { | ||
25 | return (axis_ < other.axis_); | ||
26 | } | ||
27 | |||
28 | case Direction::right: | ||
29 | case Direction::down: | ||
30 | { | ||
31 | return (axis_ > other.axis_); | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | // Order the remaining attributes arbitrarily | ||
37 | return std::tie(collider_, lower_, upper_) < | ||
38 | std::tie(other.collider_, other.lower_, other.upper_); | ||
39 | } | ||
40 | |||
41 | bool Collision::isColliding( | ||
42 | double x, | ||
43 | double y, | ||
44 | int w, | ||
45 | int h) const | ||
46 | { | ||
47 | int right = x + w; | ||
48 | int bottom = y + h; | ||
49 | |||
50 | switch (dir_) | ||
51 | { | ||
52 | case Direction::left: | ||
53 | case Direction::right: | ||
54 | { | ||
55 | if (!((bottom > lower_) && (y < upper_))) | ||
56 | { | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | break; | ||
61 | } | ||
62 | |||
63 | case Direction::up: | ||
64 | case Direction::down: | ||
65 | { | ||
66 | if (!((right > lower_) && (x < upper_))) | ||
67 | { | ||
68 | return false; | ||
69 | } | ||
70 | |||
71 | break; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | switch (dir_) | ||
76 | { | ||
77 | case Direction::left: | ||
78 | { | ||
79 | return (axis_ >= x); | ||
80 | } | ||
81 | |||
82 | case Direction::right: | ||
83 | { | ||
84 | return (axis_ <= right); | ||
85 | } | ||
86 | |||
87 | case Direction::up: | ||
88 | { | ||
89 | return (axis_ >= y); | ||
90 | } | ||
91 | |||
92 | case Direction::down: | ||
93 | { | ||
94 | return (axis_ <= bottom); | ||
95 | } | ||
96 | } | ||
97 | } | ||
diff --git a/src/collision.h b/src/collision.h deleted file mode 100644 index e5371f8..0000000 --- a/src/collision.h +++ /dev/null | |||
@@ -1,81 +0,0 @@ | |||
1 | #ifndef COLLISION_H_53D84877 | ||
2 | #define COLLISION_H_53D84877 | ||
3 | |||
4 | #include "entity_manager.h" | ||
5 | #include "direction.h" | ||
6 | |||
7 | class Collision { | ||
8 | public: | ||
9 | |||
10 | using id_type = EntityManager::id_type; | ||
11 | |||
12 | // Types are defined in descending priority order | ||
13 | enum class Type { | ||
14 | wall, | ||
15 | platform, | ||
16 | adjacency, | ||
17 | warp, | ||
18 | danger | ||
19 | }; | ||
20 | |||
21 | Collision( | ||
22 | id_type collider, | ||
23 | Direction dir, | ||
24 | Type type, | ||
25 | int axis, | ||
26 | double lower, | ||
27 | double upper) : | ||
28 | collider_(collider), | ||
29 | dir_(dir), | ||
30 | type_(type), | ||
31 | axis_(axis), | ||
32 | lower_(lower), | ||
33 | upper_(upper) | ||
34 | { | ||
35 | } | ||
36 | |||
37 | inline id_type getCollider() const | ||
38 | { | ||
39 | return collider_; | ||
40 | } | ||
41 | |||
42 | inline Direction getDirection() const | ||
43 | { | ||
44 | return dir_; | ||
45 | } | ||
46 | |||
47 | inline Type getType() const | ||
48 | { | ||
49 | return type_; | ||
50 | } | ||
51 | |||
52 | inline int getAxis() const | ||
53 | { | ||
54 | return axis_; | ||
55 | } | ||
56 | |||
57 | inline double getLower() const | ||
58 | { | ||
59 | return lower_; | ||
60 | } | ||
61 | |||
62 | inline double getUpper() const | ||
63 | { | ||
64 | return upper_; | ||
65 | } | ||
66 | |||
67 | bool operator<(const Collision& other) const; | ||
68 | |||
69 | bool isColliding(double x, double y, int w, int h) const; | ||
70 | |||
71 | private: | ||
72 | |||
73 | id_type collider_; | ||
74 | Direction dir_; | ||
75 | Type type_; | ||
76 | int axis_; | ||
77 | double lower_; | ||
78 | double upper_; | ||
79 | }; | ||
80 | |||
81 | #endif /* end of include guard: COLLISION_H_53D84877 */ | ||
diff --git a/src/components/mappable.h b/src/components/mappable.h index 6f3d38e..e92074e 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h | |||
@@ -7,7 +7,7 @@ | |||
7 | #include <list> | 7 | #include <list> |
8 | #include "component.h" | 8 | #include "component.h" |
9 | #include "renderer/texture.h" | 9 | #include "renderer/texture.h" |
10 | #include "collision.h" | 10 | #include "components/ponderable.h" |
11 | #include "entity_manager.h" | 11 | #include "entity_manager.h" |
12 | 12 | ||
13 | class MappableComponent : public Component { | 13 | class MappableComponent : public Component { |
@@ -46,7 +46,7 @@ public: | |||
46 | class Boundary { | 46 | class Boundary { |
47 | public: | 47 | public: |
48 | 48 | ||
49 | using Type = Collision::Type; | 49 | using Type = PonderableComponent::Collision; |
50 | 50 | ||
51 | Boundary( | 51 | Boundary( |
52 | double axis, | 52 | double axis, |
diff --git a/src/components/ponderable.h b/src/components/ponderable.h index fd7e775..5354f87 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h | |||
@@ -21,6 +21,17 @@ public: | |||
21 | }; | 21 | }; |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * List of different types of collidable surfaces. | ||
25 | */ | ||
26 | enum class Collision { | ||
27 | wall, | ||
28 | platform, | ||
29 | adjacency, | ||
30 | warp, | ||
31 | danger | ||
32 | }; | ||
33 | |||
34 | /** | ||
24 | * Constructor for initializing the body type, which is a constant. | 35 | * Constructor for initializing the body type, which is a constant. |
25 | */ | 36 | */ |
26 | PonderableComponent(Type type) : type(type) | 37 | PonderableComponent(Type type) : type(type) |
@@ -67,6 +78,11 @@ public: | |||
67 | bool collidable = true; | 78 | bool collidable = true; |
68 | 79 | ||
69 | /** | 80 | /** |
81 | * The effect that colliding with this body has. | ||
82 | */ | ||
83 | Collision colliderType = Collision::wall; | ||
84 | |||
85 | /** | ||
70 | * If this flag is disabled, the entity will be ignored by the pondering | 86 | * If this flag is disabled, the entity will be ignored by the pondering |
71 | * system. | 87 | * system. |
72 | * | 88 | * |
diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index af67aed..d78c8fe 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp | |||
@@ -175,7 +175,7 @@ void MappingSystem::generateBoundaries(id_type mapEntity) | |||
175 | 175 | ||
176 | addBoundary( | 176 | addBoundary( |
177 | mappable.downBoundaries, | 177 | mappable.downBoundaries, |
178 | y * TILE_HEIGHT, | 178 | y * TILE_HEIGHT + 1, |
179 | x * TILE_WIDTH, | 179 | x * TILE_WIDTH, |
180 | (x+1) * TILE_WIDTH, | 180 | (x+1) * TILE_WIDTH, |
181 | MappableComponent::Boundary::Type::danger); | 181 | MappableComponent::Boundary::Type::danger); |
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4ae6176..04b45a1 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp | |||
@@ -11,7 +11,6 @@ | |||
11 | #include "systems/playing.h" | 11 | #include "systems/playing.h" |
12 | #include "systems/realizing.h" | 12 | #include "systems/realizing.h" |
13 | #include "consts.h" | 13 | #include "consts.h" |
14 | #include "collision.h" | ||
15 | 14 | ||
16 | void PonderingSystem::tick(double dt) | 15 | void PonderingSystem::tick(double dt) |
17 | { | 16 | { |
@@ -56,304 +55,566 @@ void PonderingSystem::tick(double dt) | |||
56 | const double oldRight = oldX + transformable.w; | 55 | const double oldRight = oldX + transformable.w; |
57 | const double oldBottom = oldY + transformable.h; | 56 | const double oldBottom = oldY + transformable.h; |
58 | 57 | ||
59 | double newX = oldX + ponderable.velX * dt; | 58 | CollisionResult result; |
60 | double newY = oldY + ponderable.velY * dt; | 59 | result.newX = oldX + ponderable.velX * dt; |
60 | result.newY = oldY + ponderable.velY * dt; | ||
61 | 61 | ||
62 | bool oldGrounded = ponderable.grounded; | 62 | bool oldGrounded = ponderable.grounded; |
63 | ponderable.grounded = false; | 63 | ponderable.grounded = false; |
64 | 64 | ||
65 | std::priority_queue<Collision> collisions; | 65 | // Find horizontal collisions. |
66 | 66 | if (result.newX < oldX) | |
67 | // Find collisions | ||
68 | if (newX < oldX) | ||
69 | { | 67 | { |
70 | for (auto it = mappable.leftBoundaries.lower_bound(oldX); | 68 | bool boundaryCollision = false; |
71 | (it != std::end(mappable.leftBoundaries)) && (it->first >= newX); | 69 | auto it = mappable.leftBoundaries.lower_bound(oldX); |
72 | it++) | 70 | |
71 | // Find the axis distance of the closest environmental boundary. | ||
72 | for (; | ||
73 | (it != std::end(mappable.leftBoundaries)) && | ||
74 | (it->first >= result.newX); | ||
75 | it++) | ||
73 | { | 76 | { |
74 | if ((oldBottom > it->second.lower) | 77 | // Check that the boundary is in range for the other axis. |
75 | && (oldY < it->second.upper)) | 78 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) |
76 | { | 79 | { |
77 | // We have a collision! | 80 | // We have a collision! |
78 | collisions.emplace( | 81 | boundaryCollision = true; |
79 | mapEntity, | 82 | |
80 | Direction::left, | 83 | break; |
81 | it->second.type, | ||
82 | it->first, | ||
83 | it->second.lower, | ||
84 | it->second.upper); | ||
85 | } | 84 | } |
86 | } | 85 | } |
87 | } else if (newX > oldX) | 86 | |
88 | { | 87 | // Find a list of potential colliders, sorted so that the closest is |
89 | for (auto it = mappable.rightBoundaries.lower_bound(oldRight); | 88 | // first. |
90 | (it != std::end(mappable.rightBoundaries)) | 89 | std::vector<id_type> colliders; |
91 | && (it->first <= (newX + transformable.w)); | 90 | |
92 | it++) | 91 | for (id_type collider : entities) |
93 | { | 92 | { |
94 | if ((oldBottom > it->second.lower) | 93 | // Can't collide with self. |
95 | && (oldY < it->second.upper)) | 94 | if (collider == entity) |
96 | { | 95 | { |
97 | // We have a collision! | 96 | continue; |
98 | collisions.emplace( | ||
99 | mapEntity, | ||
100 | Direction::right, | ||
101 | it->second.type, | ||
102 | it->first, | ||
103 | it->second.lower, | ||
104 | it->second.upper); | ||
105 | } | 97 | } |
106 | } | ||
107 | } | ||
108 | 98 | ||
109 | if (newY < oldY) | 99 | auto& colliderPonder = game_.getEntityManager(). |
110 | { | 100 | getComponent<PonderableComponent>(collider); |
111 | for (auto it = mappable.upBoundaries.lower_bound(oldY); | 101 | |
112 | (it != std::end(mappable.upBoundaries)) && (it->first >= newY); | 102 | // Only check objects that are active. |
113 | it++) | 103 | if (!colliderPonder.active) |
114 | { | ||
115 | if ((oldRight > it->second.lower) | ||
116 | && (oldX < it->second.upper)) | ||
117 | { | 104 | { |
118 | // We have a collision! | 105 | continue; |
119 | collisions.emplace( | ||
120 | mapEntity, | ||
121 | Direction::up, | ||
122 | it->second.type, | ||
123 | it->first, | ||
124 | it->second.lower, | ||
125 | it->second.upper); | ||
126 | } | 106 | } |
127 | } | 107 | |
128 | } else if (newY > oldY) | 108 | auto& colliderTrans = game_.getEntityManager(). |
129 | { | 109 | getComponent<TransformableComponent>(collider); |
130 | for (auto it = mappable.downBoundaries.lower_bound(oldBottom); | 110 | |
131 | (it != std::end(mappable.downBoundaries)) | 111 | // Check if the entity would move into the potential collider, |
132 | && (it->first <= (newY + transformable.h)); | 112 | if ((colliderTrans.x + colliderTrans.w > result.newX) && |
133 | it++) | 113 | // that it wasn't already colliding, |
134 | { | 114 | (colliderTrans.x + colliderTrans.w <= oldX) && |
135 | if ((oldRight > it->second.lower) | 115 | // that the position on the other axis is in range, |
136 | && (oldX < it->second.upper)) | 116 | (colliderTrans.y + colliderTrans.h > oldY) && |
117 | (colliderTrans.y < oldBottom) && | ||
118 | // and that the collider is not farther away than the environmental | ||
119 | // boundary. | ||
120 | (!boundaryCollision || | ||
121 | (colliderTrans.x + colliderTrans.w >= it->first))) | ||
137 | { | 122 | { |
138 | // We have a collision! | 123 | colliders.push_back(collider); |
139 | collisions.emplace( | ||
140 | mapEntity, | ||
141 | Direction::down, | ||
142 | it->second.type, | ||
143 | it->first, | ||
144 | it->second.lower, | ||
145 | it->second.upper); | ||
146 | } | 124 | } |
147 | } | 125 | } |
148 | } | ||
149 | 126 | ||
150 | // Process collisions in order of priority | 127 | std::sort( |
151 | bool adjacentlyWarping = false; | 128 | std::begin(colliders), |
152 | Direction adjWarpDir; | 129 | std::end(colliders), |
153 | size_t adjWarpMapId; | 130 | [&] (id_type left, id_type right) { |
131 | auto& leftTrans = game_.getEntityManager(). | ||
132 | getComponent<TransformableComponent>(left); | ||
154 | 133 | ||
155 | while (!collisions.empty()) | 134 | auto& rightTrans = game_.getEntityManager(). |
156 | { | 135 | getComponent<TransformableComponent>(right); |
157 | Collision collision = collisions.top(); | ||
158 | collisions.pop(); | ||
159 | |||
160 | // Make sure that they are still colliding | ||
161 | if (!collision.isColliding( | ||
162 | newX, | ||
163 | newY, | ||
164 | transformable.w, | ||
165 | transformable.h)) | ||
166 | { | ||
167 | continue; | ||
168 | } | ||
169 | 136 | ||
170 | bool touchedWall = false; | 137 | return (rightTrans.x < leftTrans.x); |
171 | bool stopProcessing = false; | 138 | }); |
172 | 139 | ||
173 | switch (collision.getType()) | 140 | for (id_type collider : colliders) |
174 | { | 141 | { |
175 | case Collision::Type::wall: | 142 | auto& colliderTrans = game_.getEntityManager(). |
143 | getComponent<TransformableComponent>(collider); | ||
144 | |||
145 | // Check if the entity would still move into the potential collider. | ||
146 | if (colliderTrans.x + colliderTrans.w <= result.newX) | ||
176 | { | 147 | { |
177 | touchedWall = true; | 148 | break; |
149 | } | ||
178 | 150 | ||
151 | auto& colliderPonder = game_.getEntityManager(). | ||
152 | getComponent<PonderableComponent>(collider); | ||
153 | |||
154 | processCollision( | ||
155 | entity, | ||
156 | collider, | ||
157 | Direction::left, | ||
158 | colliderPonder.colliderType, | ||
159 | colliderTrans.x + colliderTrans.w, | ||
160 | colliderTrans.y, | ||
161 | colliderTrans.y + colliderTrans.h, | ||
162 | result); | ||
163 | |||
164 | if (result.stopProcessing) | ||
165 | { | ||
179 | break; | 166 | break; |
180 | } | 167 | } |
168 | } | ||
181 | 169 | ||
182 | case Collision::Type::platform: | 170 | // If movement hasn't been stopped by an intermediary object, and |
171 | // collision checking hasn't been stopped, process the environmental | ||
172 | // boundaries closest to the entity. | ||
173 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
174 | { | ||
175 | double boundaryAxis = it->first; | ||
176 | |||
177 | for (; | ||
178 | (it != std::end(mappable.leftBoundaries)) && | ||
179 | (it->first == boundaryAxis); | ||
180 | it++) | ||
183 | { | 181 | { |
184 | if (game_.getEntityManager(). | 182 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) |
185 | hasComponent<OrientableComponent>(entity)) | ||
186 | { | 183 | { |
187 | auto& orientable = game_.getEntityManager(). | 184 | processCollision( |
188 | getComponent<OrientableComponent>(entity); | 185 | entity, |
189 | 186 | mapEntity, | |
190 | if (orientable.getDropState() != | 187 | Direction::left, |
191 | OrientableComponent::DropState::none) | 188 | it->second.type, |
189 | it->first, | ||
190 | it->second.lower, | ||
191 | it->second.upper, | ||
192 | result); | ||
193 | |||
194 | if (result.stopProcessing) | ||
192 | { | 195 | { |
193 | orientable.setDropState(OrientableComponent::DropState::active); | 196 | break; |
194 | } else { | ||
195 | touchedWall = true; | ||
196 | } | 197 | } |
197 | } else { | ||
198 | touchedWall = true; | ||
199 | } | 198 | } |
199 | } | ||
200 | } | ||
201 | } else if (result.newX > oldX) | ||
202 | { | ||
203 | bool boundaryCollision = false; | ||
204 | auto it = mappable.rightBoundaries.lower_bound(oldRight); | ||
205 | |||
206 | // Find the axis distance of the closest environmental boundary. | ||
207 | for (; | ||
208 | (it != std::end(mappable.rightBoundaries)) | ||
209 | && (it->first <= (result.newX + transformable.w)); | ||
210 | it++) | ||
211 | { | ||
212 | // Check that the boundary is in range for the other axis. | ||
213 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) | ||
214 | { | ||
215 | // We have a collision! | ||
216 | boundaryCollision = true; | ||
200 | 217 | ||
201 | break; | 218 | break; |
202 | } | 219 | } |
220 | } | ||
221 | |||
222 | // Find a list of potential colliders, sorted so that the closest is | ||
223 | // first. | ||
224 | std::vector<id_type> colliders; | ||
203 | 225 | ||
204 | case Collision::Type::adjacency: | 226 | for (id_type collider : entities) |
227 | { | ||
228 | // Can't collide with self. | ||
229 | if (collider == entity) | ||
205 | { | 230 | { |
206 | auto& mappable = game_.getEntityManager(). | 231 | continue; |
207 | getComponent<MappableComponent>(collision.getCollider()); | 232 | } |
208 | 233 | ||
209 | auto& adj = [&] () -> const MappableComponent::Adjacent& { | 234 | auto& colliderPonder = game_.getEntityManager(). |
210 | switch (collision.getDirection()) | 235 | getComponent<PonderableComponent>(collider); |
211 | { | ||
212 | case Direction::left: return mappable.leftAdjacent; | ||
213 | case Direction::right: return mappable.rightAdjacent; | ||
214 | case Direction::up: return mappable.upAdjacent; | ||
215 | case Direction::down: return mappable.downAdjacent; | ||
216 | } | ||
217 | }(); | ||
218 | 236 | ||
219 | switch (adj.type) | 237 | // Only check objects that are active. |
220 | { | 238 | if (!colliderPonder.active) |
221 | case MappableComponent::Adjacent::Type::wall: | 239 | { |
222 | { | 240 | continue; |
223 | touchedWall = true; | 241 | } |
224 | 242 | ||
225 | break; | 243 | auto& colliderTrans = game_.getEntityManager(). |
226 | } | 244 | getComponent<TransformableComponent>(collider); |
245 | |||
246 | // Check if the entity would move into the potential collider, | ||
247 | if ((colliderTrans.x < result.newX + transformable.w) && | ||
248 | // that it wasn't already colliding, | ||
249 | (colliderTrans.x >= oldRight) && | ||
250 | // that the position on the other axis is in range, | ||
251 | (colliderTrans.y + colliderTrans.h > oldY) && | ||
252 | (colliderTrans.y < oldBottom) && | ||
253 | // and that the collider is not farther away than the environmental | ||
254 | // boundary. | ||
255 | (!boundaryCollision || (colliderTrans.x <= it->first))) | ||
256 | { | ||
257 | colliders.push_back(collider); | ||
258 | } | ||
259 | } | ||
227 | 260 | ||
228 | case MappableComponent::Adjacent::Type::wrap: | 261 | std::sort( |
229 | { | 262 | std::begin(colliders), |
230 | switch (collision.getDirection()) | 263 | std::end(colliders), |
231 | { | 264 | [&] (id_type left, id_type right) { |
232 | case Direction::left: | 265 | auto& leftTrans = game_.getEntityManager(). |
233 | { | 266 | getComponent<TransformableComponent>(left); |
234 | newX = GAME_WIDTH + WALL_GAP - transformable.w; | ||
235 | 267 | ||
236 | break; | 268 | auto& rightTrans = game_.getEntityManager(). |
237 | } | 269 | getComponent<TransformableComponent>(right); |
238 | 270 | ||
239 | case Direction::right: | 271 | return (leftTrans.x < rightTrans.x); |
240 | { | 272 | }); |
241 | newX = -WALL_GAP; | ||
242 | 273 | ||
243 | break; | 274 | for (id_type collider : colliders) |
244 | } | 275 | { |
276 | auto& colliderTrans = game_.getEntityManager(). | ||
277 | getComponent<TransformableComponent>(collider); | ||
245 | 278 | ||
246 | case Direction::up: | 279 | // Check if the entity would still move into the potential collider. |
247 | { | 280 | if (colliderTrans.x >= result.newX + transformable.w) |
248 | newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h; | 281 | { |
282 | break; | ||
283 | } | ||
249 | 284 | ||
250 | break; | 285 | auto& colliderPonder = game_.getEntityManager(). |
251 | } | 286 | getComponent<PonderableComponent>(collider); |
252 | 287 | ||
253 | case Direction::down: | 288 | processCollision( |
254 | { | 289 | entity, |
255 | newY = -WALL_GAP; | 290 | collider, |
291 | Direction::right, | ||
292 | colliderPonder.colliderType, | ||
293 | colliderTrans.x, | ||
294 | colliderTrans.y, | ||
295 | colliderTrans.y + colliderTrans.h, | ||
296 | result); | ||
297 | |||
298 | if (result.stopProcessing) | ||
299 | { | ||
300 | break; | ||
301 | } | ||
302 | } | ||
256 | 303 | ||
257 | break; | 304 | // If movement hasn't been stopped by an intermediary object, and |
258 | } | 305 | // collision checking hasn't been stopped, process the environmental |
259 | } | 306 | // boundaries closest to the entity. |
260 | } | 307 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) |
308 | { | ||
309 | double boundaryAxis = it->first; | ||
261 | 310 | ||
262 | case MappableComponent::Adjacent::Type::warp: | 311 | for (; |
312 | (it != std::end(mappable.rightBoundaries)) && | ||
313 | (it->first == boundaryAxis); | ||
314 | it++) | ||
315 | { | ||
316 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) | ||
317 | { | ||
318 | processCollision( | ||
319 | entity, | ||
320 | mapEntity, | ||
321 | Direction::right, | ||
322 | it->second.type, | ||
323 | it->first, | ||
324 | it->second.lower, | ||
325 | it->second.upper, | ||
326 | result); | ||
327 | |||
328 | if (result.stopProcessing) | ||
263 | { | 329 | { |
264 | if (game_.getEntityManager(). | ||
265 | hasComponent<PlayableComponent>(entity)) | ||
266 | { | ||
267 | adjacentlyWarping = true; | ||
268 | adjWarpDir = collision.getDirection(); | ||
269 | adjWarpMapId = adj.mapId; | ||
270 | } | ||
271 | |||
272 | break; | 330 | break; |
273 | } | 331 | } |
332 | } | ||
333 | } | ||
334 | } | ||
335 | } | ||
274 | 336 | ||
275 | case MappableComponent::Adjacent::Type::reverse: | 337 | // Find vertical collisions |
276 | { | 338 | result.touchedWall = false; |
277 | // TODO: not yet implemented. | ||
278 | 339 | ||
279 | break; | 340 | if ((!result.stopProcessing) && (result.newY < oldY)) |
280 | } | 341 | { |
281 | } | 342 | bool boundaryCollision = false; |
343 | auto it = mappable.upBoundaries.lower_bound(oldY); | ||
344 | |||
345 | // Find the axis distance of the closest environmental boundary. | ||
346 | for (; | ||
347 | (it != std::end(mappable.upBoundaries)) && | ||
348 | (it->first >= result.newY); | ||
349 | it++) | ||
350 | { | ||
351 | // Check that the boundary is in range for the other axis. | ||
352 | if ((result.newX + transformable.h > it->second.lower) && | ||
353 | (result.newX < it->second.upper)) | ||
354 | { | ||
355 | // We have a collision! | ||
356 | boundaryCollision = true; | ||
282 | 357 | ||
283 | break; | 358 | break; |
284 | } | 359 | } |
360 | } | ||
285 | 361 | ||
286 | case Collision::Type::danger: | 362 | // Find a list of potential colliders, sorted so that the closest is |
363 | // first. | ||
364 | std::vector<id_type> colliders; | ||
365 | |||
366 | for (id_type collider : entities) | ||
367 | { | ||
368 | // Can't collide with self. | ||
369 | if (collider == entity) | ||
287 | { | 370 | { |
288 | if (game_.getEntityManager(). | 371 | continue; |
289 | hasComponent<PlayableComponent>(entity)) | 372 | } |
290 | { | ||
291 | game_.getSystemManager().getSystem<PlayingSystem>().die(entity); | ||
292 | 373 | ||
293 | adjacentlyWarping = false; | 374 | auto& colliderPonder = game_.getEntityManager(). |
294 | } | 375 | getComponent<PonderableComponent>(collider); |
295 | 376 | ||
296 | stopProcessing = true; | 377 | // Only check objects that are active. |
378 | if (!colliderPonder.active) | ||
379 | { | ||
380 | continue; | ||
381 | } | ||
297 | 382 | ||
298 | break; | 383 | auto& colliderTrans = game_.getEntityManager(). |
384 | getComponent<TransformableComponent>(collider); | ||
385 | |||
386 | // Check if the entity would move into the potential collider, | ||
387 | if ((colliderTrans.y + colliderTrans.h > result.newY) && | ||
388 | // that it wasn't already colliding, | ||
389 | (colliderTrans.y + colliderTrans.h <= oldY) && | ||
390 | // that the position on the other axis is in range, | ||
391 | (colliderTrans.x + colliderTrans.w > result.newX) && | ||
392 | (colliderTrans.x < result.newX + transformable.w) && | ||
393 | // and that the collider is not farther away than the environmental | ||
394 | // boundary. | ||
395 | (!boundaryCollision || | ||
396 | (colliderTrans.y + colliderTrans.h >= it->first))) | ||
397 | { | ||
398 | colliders.push_back(collider); | ||
299 | } | 399 | } |
400 | } | ||
401 | |||
402 | std::sort( | ||
403 | std::begin(colliders), | ||
404 | std::end(colliders), | ||
405 | [&] (id_type left, id_type right) { | ||
406 | auto& leftTrans = game_.getEntityManager(). | ||
407 | getComponent<TransformableComponent>(left); | ||
408 | |||
409 | auto& rightTrans = game_.getEntityManager(). | ||
410 | getComponent<TransformableComponent>(right); | ||
300 | 411 | ||
301 | default: | 412 | return (rightTrans.y < leftTrans.y); |
413 | }); | ||
414 | |||
415 | for (id_type collider : colliders) | ||
416 | { | ||
417 | auto& colliderTrans = game_.getEntityManager(). | ||
418 | getComponent<TransformableComponent>(collider); | ||
419 | |||
420 | // Check if the entity would still move into the potential collider. | ||
421 | if (colliderTrans.y + colliderTrans.h <= result.newY) | ||
302 | { | 422 | { |
303 | // Not yet implemented. | 423 | break; |
424 | } | ||
425 | |||
426 | auto& colliderPonder = game_.getEntityManager(). | ||
427 | getComponent<PonderableComponent>(collider); | ||
304 | 428 | ||
429 | processCollision( | ||
430 | entity, | ||
431 | collider, | ||
432 | Direction::up, | ||
433 | colliderPonder.colliderType, | ||
434 | colliderTrans.y + colliderTrans.h, | ||
435 | colliderTrans.x, | ||
436 | colliderTrans.x + colliderTrans.w, | ||
437 | result); | ||
438 | |||
439 | if (result.stopProcessing) | ||
440 | { | ||
305 | break; | 441 | break; |
306 | } | 442 | } |
307 | } | 443 | } |
308 | 444 | ||
309 | if (stopProcessing) | 445 | // If movement hasn't been stopped by an intermediary object, and |
446 | // collision checking hasn't been stopped, process the environmental | ||
447 | // boundaries closest to the entity. | ||
448 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
310 | { | 449 | { |
311 | break; | 450 | double boundaryAxis = it->first; |
451 | |||
452 | for (; | ||
453 | (it != std::end(mappable.upBoundaries)) && | ||
454 | (it->first == boundaryAxis); | ||
455 | it++) | ||
456 | { | ||
457 | if ((result.newX + transformable.w > it->second.lower) && | ||
458 | (result.newX < it->second.upper)) | ||
459 | { | ||
460 | processCollision( | ||
461 | entity, | ||
462 | mapEntity, | ||
463 | Direction::up, | ||
464 | it->second.type, | ||
465 | it->first, | ||
466 | it->second.lower, | ||
467 | it->second.upper, | ||
468 | result); | ||
469 | |||
470 | if (result.stopProcessing) | ||
471 | { | ||
472 | break; | ||
473 | } | ||
474 | } | ||
475 | } | ||
312 | } | 476 | } |
477 | } else if ((!result.stopProcessing) && (result.newY > oldY)) | ||
478 | { | ||
479 | bool boundaryCollision = false; | ||
480 | auto it = mappable.downBoundaries.lower_bound(oldBottom); | ||
481 | |||
482 | // Find the axis distance of the closest environmental boundary. | ||
483 | for (; | ||
484 | (it != std::end(mappable.downBoundaries)) | ||
485 | && (it->first <= (result.newY + transformable.h)); | ||
486 | it++) | ||
487 | { | ||
488 | // Check that the boundary is in range for the other axis. | ||
489 | if ((result.newX + transformable.w > it->second.lower) && | ||
490 | (result.newX < it->second.upper)) | ||
491 | { | ||
492 | // We have a collision! | ||
493 | boundaryCollision = true; | ||
313 | 494 | ||
314 | if (touchedWall) | 495 | break; |
496 | } | ||
497 | } | ||
498 | |||
499 | // Find a list of potential colliders, sorted so that the closest is | ||
500 | // first. | ||
501 | std::vector<id_type> colliders; | ||
502 | |||
503 | for (id_type collider : entities) | ||
315 | { | 504 | { |
316 | switch (collision.getDirection()) | 505 | // Can't collide with self. |
506 | if (collider == entity) | ||
317 | { | 507 | { |
318 | case Direction::left: | 508 | continue; |
319 | { | 509 | } |
320 | newX = collision.getAxis(); | ||
321 | ponderable.velX = 0.0; | ||
322 | 510 | ||
323 | break; | 511 | auto& colliderPonder = game_.getEntityManager(). |
324 | } | 512 | getComponent<PonderableComponent>(collider); |
325 | 513 | ||
326 | case Direction::right: | 514 | // Only check objects that are active. |
327 | { | 515 | if (!colliderPonder.active) |
328 | newX = collision.getAxis() - transformable.w; | 516 | { |
329 | ponderable.velX = 0.0; | 517 | continue; |
518 | } | ||
330 | 519 | ||
331 | break; | 520 | auto& colliderTrans = game_.getEntityManager(). |
332 | } | 521 | getComponent<TransformableComponent>(collider); |
522 | |||
523 | // Check if the entity would move into the potential collider, | ||
524 | if ((colliderTrans.y < result.newY + transformable.h) && | ||
525 | // that it wasn't already colliding, | ||
526 | (colliderTrans.y >= oldBottom) && | ||
527 | // that the position on the other axis is in range, | ||
528 | (colliderTrans.x + colliderTrans.w > result.newX) && | ||
529 | (colliderTrans.x < result.newX + transformable.w) && | ||
530 | // and that the collider is not farther away than the environmental | ||
531 | // boundary. | ||
532 | (!boundaryCollision || (colliderTrans.y <= it->first))) | ||
533 | { | ||
534 | colliders.push_back(collider); | ||
535 | } | ||
536 | } | ||
333 | 537 | ||
334 | case Direction::up: | 538 | std::sort( |
335 | { | 539 | std::begin(colliders), |
336 | newY = collision.getAxis(); | 540 | std::end(colliders), |
337 | ponderable.velY = 0.0; | 541 | [&] (id_type left, id_type right) { |
542 | auto& leftTrans = game_.getEntityManager(). | ||
543 | getComponent<TransformableComponent>(left); | ||
338 | 544 | ||
339 | break; | 545 | auto& rightTrans = game_.getEntityManager(). |
340 | } | 546 | getComponent<TransformableComponent>(right); |
341 | 547 | ||
342 | case Direction::down: | 548 | return (leftTrans.y < rightTrans.y); |
343 | { | 549 | }); |
344 | newY = collision.getAxis() - transformable.h; | 550 | |
345 | ponderable.velY = 0.0; | 551 | for (id_type collider : colliders) |
346 | ponderable.grounded = true; | 552 | { |
553 | auto& colliderTrans = game_.getEntityManager(). | ||
554 | getComponent<TransformableComponent>(collider); | ||
555 | |||
556 | // Check if the entity would still move into the potential collider. | ||
557 | if (colliderTrans.y >= result.newY + transformable.h) | ||
558 | { | ||
559 | break; | ||
560 | } | ||
561 | |||
562 | auto& colliderPonder = game_.getEntityManager(). | ||
563 | getComponent<PonderableComponent>(collider); | ||
564 | |||
565 | processCollision( | ||
566 | entity, | ||
567 | collider, | ||
568 | Direction::down, | ||
569 | colliderPonder.colliderType, | ||
570 | colliderTrans.y, | ||
571 | colliderTrans.x, | ||
572 | colliderTrans.x + colliderTrans.w, | ||
573 | result); | ||
574 | |||
575 | if (result.stopProcessing) | ||
576 | { | ||
577 | break; | ||
578 | } | ||
579 | } | ||
580 | |||
581 | // If movement hasn't been stopped by an intermediary object, and | ||
582 | // collision checking hasn't been stopped, process the environmental | ||
583 | // boundaries closest to the entity. | ||
584 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
585 | { | ||
586 | double boundaryAxis = it->first; | ||
347 | 587 | ||
348 | break; | 588 | for (; |
589 | (it != std::end(mappable.downBoundaries)) && | ||
590 | (it->first == boundaryAxis); | ||
591 | it++) | ||
592 | { | ||
593 | if ((result.newX + transformable.w > it->second.lower) && | ||
594 | (result.newX < it->second.upper)) | ||
595 | { | ||
596 | processCollision( | ||
597 | entity, | ||
598 | mapEntity, | ||
599 | Direction::down, | ||
600 | it->second.type, | ||
601 | it->first, | ||
602 | it->second.lower, | ||
603 | it->second.upper, | ||
604 | result); | ||
605 | |||
606 | if (result.stopProcessing) | ||
607 | { | ||
608 | break; | ||
609 | } | ||
349 | } | 610 | } |
350 | } | 611 | } |
351 | } | 612 | } |
352 | } | 613 | } |
353 | 614 | ||
354 | // Move | 615 | // Move |
355 | transformable.x = newX; | 616 | transformable.x = result.newX; |
356 | transformable.y = newY; | 617 | transformable.y = result.newY; |
357 | 618 | ||
358 | // Perform cleanup for orientable entites | 619 | // Perform cleanup for orientable entites |
359 | if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) | 620 | if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) |
@@ -381,12 +642,12 @@ void PonderingSystem::tick(double dt) | |||
381 | } | 642 | } |
382 | 643 | ||
383 | // Move to an adjacent map, if necessary | 644 | // Move to an adjacent map, if necessary |
384 | if (adjacentlyWarping) | 645 | if (result.adjacentlyWarping) |
385 | { | 646 | { |
386 | double warpX = newX; | 647 | double warpX = result.newX; |
387 | double warpY = newY; | 648 | double warpY = result.newY; |
388 | 649 | ||
389 | switch (adjWarpDir) | 650 | switch (result.adjWarpDir) |
390 | { | 651 | { |
391 | case Direction::left: | 652 | case Direction::left: |
392 | { | 653 | { |
@@ -420,7 +681,7 @@ void PonderingSystem::tick(double dt) | |||
420 | game_.getSystemManager().getSystem<PlayingSystem>(). | 681 | game_.getSystemManager().getSystem<PlayingSystem>(). |
421 | changeMap( | 682 | changeMap( |
422 | entity, | 683 | entity, |
423 | adjWarpMapId, | 684 | result.adjWarpMapId, |
424 | warpX, | 685 | warpX, |
425 | warpY); | 686 | warpY); |
426 | } | 687 | } |
@@ -453,3 +714,196 @@ void PonderingSystem::initPrototype(id_type prototype) | |||
453 | ponderable.frozen = false; | 714 | ponderable.frozen = false; |
454 | ponderable.collidable = true; | 715 | ponderable.collidable = true; |
455 | } | 716 | } |
717 | |||
718 | void PonderingSystem::processCollision( | ||
719 | id_type entity, | ||
720 | id_type collider, | ||
721 | Direction dir, | ||
722 | PonderableComponent::Collision type, | ||
723 | double axis, | ||
724 | double lower, | ||
725 | double upper, | ||
726 | CollisionResult& result) | ||
727 | { | ||
728 | auto& ponderable = game_.getEntityManager(). | ||
729 | getComponent<PonderableComponent>(entity); | ||
730 | |||
731 | auto& transformable = game_.getEntityManager(). | ||
732 | getComponent<TransformableComponent>(entity); | ||
733 | |||
734 | switch (type) | ||
735 | { | ||
736 | case PonderableComponent::Collision::wall: | ||
737 | { | ||
738 | result.touchedWall = true; | ||
739 | |||
740 | break; | ||
741 | } | ||
742 | |||
743 | case PonderableComponent::Collision::platform: | ||
744 | { | ||
745 | if (game_.getEntityManager(). | ||
746 | hasComponent<OrientableComponent>(entity)) | ||
747 | { | ||
748 | auto& orientable = game_.getEntityManager(). | ||
749 | getComponent<OrientableComponent>(entity); | ||
750 | |||
751 | if (orientable.getDropState() != | ||
752 | OrientableComponent::DropState::none) | ||
753 | { | ||
754 | orientable.setDropState(OrientableComponent::DropState::active); | ||
755 | } else { | ||
756 | result.touchedWall = true; | ||
757 | } | ||
758 | } else { | ||
759 | result.touchedWall = true; | ||
760 | } | ||
761 | |||
762 | break; | ||
763 | } | ||
764 | |||
765 | case PonderableComponent::Collision::adjacency: | ||
766 | { | ||
767 | auto& mappable = game_.getEntityManager(). | ||
768 | getComponent<MappableComponent>(collider); | ||
769 | |||
770 | auto& adj = [&] () -> const MappableComponent::Adjacent& { | ||
771 | switch (dir) | ||
772 | { | ||
773 | case Direction::left: return mappable.leftAdjacent; | ||
774 | case Direction::right: return mappable.rightAdjacent; | ||
775 | case Direction::up: return mappable.upAdjacent; | ||
776 | case Direction::down: return mappable.downAdjacent; | ||
777 | } | ||
778 | }(); | ||
779 | |||
780 | switch (adj.type) | ||
781 | { | ||
782 | case MappableComponent::Adjacent::Type::wall: | ||
783 | { | ||
784 | result.touchedWall = true; | ||
785 | |||
786 | break; | ||
787 | } | ||
788 | |||
789 | case MappableComponent::Adjacent::Type::wrap: | ||
790 | { | ||
791 | switch (dir) | ||
792 | { | ||
793 | case Direction::left: | ||
794 | { | ||
795 | result.newX = GAME_WIDTH + WALL_GAP - transformable.w; | ||
796 | |||
797 | break; | ||
798 | } | ||
799 | |||
800 | case Direction::right: | ||
801 | { | ||
802 | result.newX = -WALL_GAP; | ||
803 | |||
804 | break; | ||
805 | } | ||
806 | |||
807 | case Direction::up: | ||
808 | { | ||
809 | result.newY = | ||
810 | MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h; | ||
811 | |||
812 | break; | ||
813 | } | ||
814 | |||
815 | case Direction::down: | ||
816 | { | ||
817 | result.newY = -WALL_GAP; | ||
818 | |||
819 | break; | ||
820 | } | ||
821 | } | ||
822 | } | ||
823 | |||
824 | case MappableComponent::Adjacent::Type::warp: | ||
825 | { | ||
826 | if (game_.getEntityManager(). | ||
827 | hasComponent<PlayableComponent>(entity)) | ||
828 | { | ||
829 | result.adjacentlyWarping = true; | ||
830 | result.adjWarpDir = dir; | ||
831 | result.adjWarpMapId = adj.mapId; | ||
832 | } | ||
833 | |||
834 | break; | ||
835 | } | ||
836 | |||
837 | case MappableComponent::Adjacent::Type::reverse: | ||
838 | { | ||
839 | // TODO: not yet implemented. | ||
840 | |||
841 | break; | ||
842 | } | ||
843 | } | ||
844 | |||
845 | break; | ||
846 | } | ||
847 | |||
848 | case PonderableComponent::Collision::danger: | ||
849 | { | ||
850 | if (game_.getEntityManager(). | ||
851 | hasComponent<PlayableComponent>(entity)) | ||
852 | { | ||
853 | game_.getSystemManager().getSystem<PlayingSystem>().die(entity); | ||
854 | |||
855 | result.adjacentlyWarping = false; | ||
856 | } | ||
857 | |||
858 | result.stopProcessing = true; | ||
859 | |||
860 | break; | ||
861 | } | ||
862 | |||
863 | default: | ||
864 | { | ||
865 | // Not yet implemented. | ||
866 | |||
867 | break; | ||
868 | } | ||
869 | } | ||
870 | |||
871 | if (!result.stopProcessing && result.touchedWall) | ||
872 | { | ||
873 | switch (dir) | ||
874 | { | ||
875 | case Direction::left: | ||
876 | { | ||
877 | result.newX = axis; | ||
878 | ponderable.velX = 0.0; | ||
879 | |||
880 | break; | ||
881 | } | ||
882 | |||
883 | case Direction::right: | ||
884 | { | ||
885 | result.newX = axis - transformable.w; | ||
886 | ponderable.velX = 0.0; | ||
887 | |||
888 | break; | ||
889 | } | ||
890 | |||
891 | case Direction::up: | ||
892 | { | ||
893 | result.newY = axis; | ||
894 | ponderable.velY = 0.0; | ||
895 | |||
896 | break; | ||
897 | } | ||
898 | |||
899 | case Direction::down: | ||
900 | { | ||
901 | result.newY = axis - transformable.h; | ||
902 | ponderable.velY = 0.0; | ||
903 | ponderable.grounded = true; | ||
904 | |||
905 | break; | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | } | ||
diff --git a/src/systems/pondering.h b/src/systems/pondering.h index 58e6496..aa430db 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h | |||
@@ -18,6 +18,29 @@ public: | |||
18 | 18 | ||
19 | void initPrototype(id_type prototype); | 19 | void initPrototype(id_type prototype); |
20 | 20 | ||
21 | private: | ||
22 | |||
23 | struct CollisionResult | ||
24 | { | ||
25 | double newX; | ||
26 | double newY; | ||
27 | bool stopProcessing = false; | ||
28 | bool touchedWall = false; | ||
29 | bool adjacentlyWarping = false; | ||
30 | Direction adjWarpDir; | ||
31 | size_t adjWarpMapId; | ||
32 | }; | ||
33 | |||
34 | void processCollision( | ||
35 | id_type entity, | ||
36 | id_type collider, | ||
37 | Direction dir, | ||
38 | PonderableComponent::Collision type, | ||
39 | double axis, | ||
40 | double lower, | ||
41 | double upper, | ||
42 | CollisionResult& result); | ||
43 | |||
21 | }; | 44 | }; |
22 | 45 | ||
23 | #endif /* end of include guard: PONDERING_H_F2530E0E */ | 46 | #endif /* end of include guard: PONDERING_H_F2530E0E */ |
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index c86dd5e..3656acb 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp | |||
@@ -207,6 +207,10 @@ EntityManager::id_type RealizingSystem::initSingleton( | |||
207 | 207 | ||
208 | animatable.origAnimation = "static"; | 208 | animatable.origAnimation = "static"; |
209 | 209 | ||
210 | // Create a physics body. | ||
211 | game_.getSystemManager().getSystem<PonderingSystem>(). | ||
212 | initializeBody(mapObject, PonderableComponent::Type::vacuumed); | ||
213 | |||
210 | mappable.objects.push_back(mapObject); | 214 | mappable.objects.push_back(mapObject); |
211 | } else if (!xmlStrcmp( | 215 | } else if (!xmlStrcmp( |
212 | mapNode->name, | 216 | mapNode->name, |