diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-02-18 12:35:45 -0500 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-02-18 12:35:45 -0500 |
commit | e16fb5be90c889c371cbb0ca2444735c2e12073c (patch) | |
tree | cbaa20e14a34c460b6c9886f266c4b4b6f62ae87 | |
parent | ed08b673c50b076042d8f0c49501372168142764 (diff) | |
download | therapy-e16fb5be90c889c371cbb0ca2444735c2e12073c.tar.gz therapy-e16fb5be90c889c371cbb0ca2444735c2e12073c.tar.bz2 therapy-e16fb5be90c889c371cbb0ca2444735c2e12073c.zip |
Implemented map adjacency
This brings along with it the ability to move to different maps, for which the PlayingSystem and PlayableComponent were introduced. The PlayingSystem is a general overseer system that handles big picture stuff like initializing the player and changing maps. The PlayableComponent represents the player. While the ControllableComponent is also likely to always only be on the player entity, the two are distinct by separation of concerns. This also required a refactoring of how collisions are processed, because of a bug where the player can move to a new map when horizontal collisions are checked, and vertical collisions are skipped, causing the player to clip through the ground because the normal force was never handled.
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/collision.cpp | 97 | ||||
-rw-r--r-- | src/collision.h | 81 | ||||
-rw-r--r-- | src/components/mappable.h | 10 | ||||
-rw-r--r-- | src/components/playable.h | 15 | ||||
-rw-r--r-- | src/consts.h | 2 | ||||
-rw-r--r-- | src/entity_manager.h | 10 | ||||
-rw-r--r-- | src/game.cpp | 27 | ||||
-rw-r--r-- | src/map.h | 70 | ||||
-rw-r--r-- | src/systems/mapping.cpp | 28 | ||||
-rw-r--r-- | src/systems/playing.cpp | 98 | ||||
-rw-r--r-- | src/systems/playing.h | 21 | ||||
-rw-r--r-- | src/systems/pondering.cpp | 347 | ||||
-rw-r--r-- | src/systems/pondering.h | 11 | ||||
-rw-r--r-- | src/world.cpp | 58 |
15 files changed, 717 insertions, 160 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e7bcb8..155063e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -55,6 +55,7 @@ add_executable(Aromatherapy | |||
55 | src/animation.cpp | 55 | src/animation.cpp |
56 | src/world.cpp | 56 | src/world.cpp |
57 | src/util.cpp | 57 | src/util.cpp |
58 | src/collision.cpp | ||
58 | src/renderer/renderer.cpp | 59 | src/renderer/renderer.cpp |
59 | src/renderer/mesh.cpp | 60 | src/renderer/mesh.cpp |
60 | src/renderer/shader.cpp | 61 | src/renderer/shader.cpp |
@@ -64,6 +65,7 @@ add_executable(Aromatherapy | |||
64 | src/systems/animating.cpp | 65 | src/systems/animating.cpp |
65 | src/systems/mapping.cpp | 66 | src/systems/mapping.cpp |
66 | src/systems/orienting.cpp | 67 | src/systems/orienting.cpp |
68 | src/systems/playing.cpp | ||
67 | ) | 69 | ) |
68 | 70 | ||
69 | set_property(TARGET Aromatherapy PROPERTY CXX_STANDARD 11) | 71 | set_property(TARGET Aromatherapy PROPERTY CXX_STANDARD 11) |
diff --git a/src/collision.cpp b/src/collision.cpp new file mode 100644 index 0000000..b747a90 --- /dev/null +++ b/src/collision.cpp | |||
@@ -0,0 +1,97 @@ | |||
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 new file mode 100644 index 0000000..e5371f8 --- /dev/null +++ b/src/collision.h | |||
@@ -0,0 +1,81 @@ | |||
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 2dbab77..633cdf4 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <map> | 4 | #include <map> |
5 | #include "component.h" | 5 | #include "component.h" |
6 | #include "renderer/texture.h" | 6 | #include "renderer/texture.h" |
7 | #include "collision.h" | ||
7 | #include "map.h" | 8 | #include "map.h" |
8 | 9 | ||
9 | class MappableComponent : public Component { | 10 | class MappableComponent : public Component { |
@@ -12,14 +13,7 @@ public: | |||
12 | class Boundary { | 13 | class Boundary { |
13 | public: | 14 | public: |
14 | 15 | ||
15 | enum class Type { | 16 | using Type = Collision::Type; |
16 | wall, | ||
17 | wrap, | ||
18 | teleport, | ||
19 | reverse, | ||
20 | platform, | ||
21 | danger | ||
22 | }; | ||
23 | 17 | ||
24 | Boundary( | 18 | Boundary( |
25 | double axis, | 19 | double axis, |
diff --git a/src/components/playable.h b/src/components/playable.h new file mode 100644 index 0000000..a6e71b0 --- /dev/null +++ b/src/components/playable.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef PLAYABLE_H_DDC566C3 | ||
2 | #define PLAYABLE_H_DDC566C3 | ||
3 | |||
4 | #include "component.h" | ||
5 | |||
6 | class PlayableComponent : public Component { | ||
7 | public: | ||
8 | |||
9 | bool changingMap = false; | ||
10 | int newMapId = -1; | ||
11 | double newMapX = 0; | ||
12 | double newMapY = 0; | ||
13 | }; | ||
14 | |||
15 | #endif /* end of include guard: PLAYABLE_H_DDC566C3 */ | ||
diff --git a/src/consts.h b/src/consts.h index 581018d..a065159 100644 --- a/src/consts.h +++ b/src/consts.h | |||
@@ -7,7 +7,7 @@ const int GAME_WIDTH = 320; | |||
7 | const int GAME_HEIGHT = 200; | 7 | const int GAME_HEIGHT = 200; |
8 | const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; | 8 | const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; |
9 | const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT - 1; | 9 | const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT - 1; |
10 | const int WALL_GAP = 6; | 10 | const int WALL_GAP = 5; |
11 | const int TILESET_COLS = 8; | 11 | const int TILESET_COLS = 8; |
12 | const int FONT_COLS = 16; | 12 | const int FONT_COLS = 16; |
13 | 13 | ||
diff --git a/src/entity_manager.h b/src/entity_manager.h index 65fa6ca..1e8d31c 100644 --- a/src/entity_manager.h +++ b/src/entity_manager.h | |||
@@ -26,6 +26,7 @@ private: | |||
26 | 26 | ||
27 | database_type entities; | 27 | database_type entities; |
28 | std::vector<bool> slotAvailable; | 28 | std::vector<bool> slotAvailable; |
29 | std::set<id_type> allEntities; | ||
29 | std::map<std::set<std::type_index>, std::set<id_type>> cachedComponents; | 30 | std::map<std::set<std::type_index>, std::set<id_type>> cachedComponents; |
30 | 31 | ||
31 | id_type nextEntityID = 0; | 32 | id_type nextEntityID = 0; |
@@ -59,12 +60,14 @@ public: | |||
59 | // If the database is saturated, add a new element for the new entity. | 60 | // If the database is saturated, add a new element for the new entity. |
60 | entities.emplace_back(); | 61 | entities.emplace_back(); |
61 | slotAvailable.push_back(false); | 62 | slotAvailable.push_back(false); |
63 | allEntities.insert(nextEntityID); | ||
62 | 64 | ||
63 | return nextEntityID++; | 65 | return nextEntityID++; |
64 | } else { | 66 | } else { |
65 | // If there is an available slot in the database, use it. | 67 | // If there is an available slot in the database, use it. |
66 | id_type id = nextEntityID++; | 68 | id_type id = nextEntityID++; |
67 | slotAvailable[id] = false; | 69 | slotAvailable[id] = false; |
70 | allEntities.insert(id); | ||
68 | 71 | ||
69 | // Fast forward the next available slot pointer to an available slot. | 72 | // Fast forward the next available slot pointer to an available slot. |
70 | while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID]) | 73 | while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID]) |
@@ -89,6 +92,8 @@ public: | |||
89 | cache.second.erase(entity); | 92 | cache.second.erase(entity); |
90 | } | 93 | } |
91 | 94 | ||
95 | allEntities.erase(entity); | ||
96 | |||
92 | // Destroy the data | 97 | // Destroy the data |
93 | entities[entity].components.clear(); | 98 | entities[entity].components.clear(); |
94 | 99 | ||
@@ -202,6 +207,11 @@ public: | |||
202 | 207 | ||
203 | return getEntitiesWithComponentsHelper<R...>(componentTypes); | 208 | return getEntitiesWithComponentsHelper<R...>(componentTypes); |
204 | } | 209 | } |
210 | |||
211 | const std::set<id_type>& getEntities() const | ||
212 | { | ||
213 | return allEntities; | ||
214 | } | ||
205 | }; | 215 | }; |
206 | 216 | ||
207 | template <> | 217 | template <> |
diff --git a/src/game.cpp b/src/game.cpp index 228ff23..f245e7c 100644 --- a/src/game.cpp +++ b/src/game.cpp | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "systems/animating.h" | 9 | #include "systems/animating.h" |
10 | #include "systems/mapping.h" | 10 | #include "systems/mapping.h" |
11 | #include "systems/orienting.h" | 11 | #include "systems/orienting.h" |
12 | #include "systems/playing.h" | ||
12 | #include "animation.h" | 13 | #include "animation.h" |
13 | #include "consts.h" | 14 | #include "consts.h" |
14 | 15 | ||
@@ -28,36 +29,14 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) | |||
28 | 29 | ||
29 | Game::Game() : world_("res/maps.xml") | 30 | Game::Game() : world_("res/maps.xml") |
30 | { | 31 | { |
32 | systemManager_.emplaceSystem<PlayingSystem>(*this); | ||
31 | systemManager_.emplaceSystem<ControllingSystem>(*this); | 33 | systemManager_.emplaceSystem<ControllingSystem>(*this); |
32 | systemManager_.emplaceSystem<OrientingSystem>(*this); | 34 | systemManager_.emplaceSystem<OrientingSystem>(*this); |
33 | systemManager_.emplaceSystem<PonderingSystem>(*this); | 35 | systemManager_.emplaceSystem<PonderingSystem>(*this); |
34 | systemManager_.emplaceSystem<MappingSystem>(*this); | 36 | systemManager_.emplaceSystem<MappingSystem>(*this); |
35 | systemManager_.emplaceSystem<AnimatingSystem>(*this); | 37 | systemManager_.emplaceSystem<AnimatingSystem>(*this); |
36 | 38 | ||
37 | int player = entityManager_.emplaceEntity(); | 39 | systemManager_.getSystem<PlayingSystem>().initPlayer(); |
38 | |||
39 | AnimationSet playerGraphics {"res/Starla.png", 10, 12, 6}; | ||
40 | playerGraphics.emplaceAnimation("stillLeft", 3, 1, 1); | ||
41 | playerGraphics.emplaceAnimation("stillRight", 0, 1, 1); | ||
42 | playerGraphics.emplaceAnimation("walkingLeft", 4, 2, 10); | ||
43 | playerGraphics.emplaceAnimation("walkingRight", 1, 2, 10); | ||
44 | |||
45 | entityManager_.emplaceComponent<AnimatableComponent>( | ||
46 | player, | ||
47 | std::move(playerGraphics), | ||
48 | "stillLeft"); | ||
49 | |||
50 | entityManager_.emplaceComponent<TransformableComponent>( | ||
51 | player, | ||
52 | 203, 44, 10, 12); | ||
53 | |||
54 | systemManager_.getSystem<PonderingSystem>().initializeBody( | ||
55 | player, | ||
56 | PonderableComponent::Type::freefalling); | ||
57 | |||
58 | entityManager_.emplaceComponent<ControllableComponent>(player); | ||
59 | entityManager_.emplaceComponent<OrientableComponent>(player); | ||
60 | |||
61 | systemManager_.getSystem<MappingSystem>().loadMap(world_.getStartingMapId()); | 40 | systemManager_.getSystem<MappingSystem>().loadMap(world_.getStartingMapId()); |
62 | 41 | ||
63 | glfwSwapInterval(1); | 42 | glfwSwapInterval(1); |
diff --git a/src/map.h b/src/map.h index 9177870..6fe1e62 100644 --- a/src/map.h +++ b/src/map.h | |||
@@ -10,13 +10,55 @@ | |||
10 | class Map { | 10 | class Map { |
11 | public: | 11 | public: |
12 | 12 | ||
13 | class Adjacent { | ||
14 | public: | ||
15 | |||
16 | enum class Type { | ||
17 | wall, | ||
18 | wrap, | ||
19 | warp, | ||
20 | reverse | ||
21 | }; | ||
22 | |||
23 | Adjacent( | ||
24 | Type type = Type::wall, | ||
25 | int mapId = -1) : | ||
26 | type_(type), | ||
27 | mapId_(mapId) | ||
28 | { | ||
29 | } | ||
30 | |||
31 | inline Type getType() const | ||
32 | { | ||
33 | return type_; | ||
34 | } | ||
35 | |||
36 | inline int getMapId() const | ||
37 | { | ||
38 | return mapId_; | ||
39 | } | ||
40 | |||
41 | private: | ||
42 | |||
43 | Type type_; | ||
44 | int mapId_; | ||
45 | }; | ||
46 | |||
13 | Map( | 47 | Map( |
14 | int id, | 48 | int id, |
15 | std::vector<int> tiles, | 49 | std::vector<int> tiles, |
16 | std::string title) : | 50 | std::string title, |
51 | Adjacent leftAdjacent, | ||
52 | Adjacent rightAdjacent, | ||
53 | Adjacent upAdjacent, | ||
54 | Adjacent downAdjacent) : | ||
17 | id_(id), | 55 | id_(id), |
18 | tiles_(std::move(tiles)), | 56 | tiles_(std::move(tiles)), |
19 | title_(std::move(title)) | 57 | title_(std::move(title)), |
58 | leftAdjacent_(std::move(leftAdjacent)), | ||
59 | rightAdjacent_(std::move(rightAdjacent)), | ||
60 | upAdjacent_(std::move(upAdjacent)), | ||
61 | downAdjacent_(std::move(downAdjacent)) | ||
20 | { | 62 | { |
21 | } | 63 | } |
22 | 64 | ||
@@ -35,11 +77,35 @@ public: | |||
35 | return title_; | 77 | return title_; |
36 | } | 78 | } |
37 | 79 | ||
80 | inline const Adjacent& getLeftAdjacent() const | ||
81 | { | ||
82 | return leftAdjacent_; | ||
83 | } | ||
84 | |||
85 | inline const Adjacent& getRightAdjacent() const | ||
86 | { | ||
87 | return rightAdjacent_; | ||
88 | } | ||
89 | |||
90 | inline const Adjacent& getUpAdjacent() const | ||
91 | { | ||
92 | return upAdjacent_; | ||
93 | } | ||
94 | |||
95 | inline const Adjacent& getDownAdjacent() const | ||
96 | { | ||
97 | return downAdjacent_; | ||
98 | } | ||
99 | |||
38 | private: | 100 | private: |
39 | 101 | ||
40 | int id_; | 102 | int id_; |
41 | std::vector<int> tiles_; | 103 | std::vector<int> tiles_; |
42 | std::string title_; | 104 | std::string title_; |
105 | Adjacent leftAdjacent_; | ||
106 | Adjacent rightAdjacent_; | ||
107 | Adjacent upAdjacent_; | ||
108 | Adjacent downAdjacent_; | ||
43 | }; | 109 | }; |
44 | 110 | ||
45 | #endif /* end of include guard: MAP_H_74055FC0 */ | 111 | #endif /* end of include guard: MAP_H_74055FC0 */ |
diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index 120a27a..05167c1 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp | |||
@@ -93,6 +93,34 @@ void MappingSystem::loadMap(size_t mapId) | |||
93 | 93 | ||
94 | const Map& map = game_.getWorld().getMap(mappable.getMapId()); | 94 | const Map& map = game_.getWorld().getMap(mappable.getMapId()); |
95 | 95 | ||
96 | addBoundary( | ||
97 | mappable.getLeftBoundaries(), | ||
98 | -WALL_GAP, | ||
99 | 0, | ||
100 | MAP_HEIGHT * TILE_HEIGHT, | ||
101 | MappableComponent::Boundary::Type::adjacency); | ||
102 | |||
103 | addBoundary( | ||
104 | mappable.getRightBoundaries(), | ||
105 | GAME_WIDTH + WALL_GAP, | ||
106 | 0, | ||
107 | MAP_HEIGHT * TILE_HEIGHT, | ||
108 | MappableComponent::Boundary::Type::adjacency); | ||
109 | |||
110 | addBoundary( | ||
111 | mappable.getUpBoundaries(), | ||
112 | -WALL_GAP, | ||
113 | 0, | ||
114 | GAME_WIDTH, | ||
115 | MappableComponent::Boundary::Type::adjacency); | ||
116 | |||
117 | addBoundary( | ||
118 | mappable.getDownBoundaries(), | ||
119 | MAP_HEIGHT * TILE_HEIGHT + WALL_GAP, | ||
120 | 0, | ||
121 | GAME_WIDTH, | ||
122 | MappableComponent::Boundary::Type::adjacency); | ||
123 | |||
96 | for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) | 124 | for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) |
97 | { | 125 | { |
98 | size_t x = i % MAP_WIDTH; | 126 | size_t x = i % MAP_WIDTH; |
diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp new file mode 100644 index 0000000..2c6a419 --- /dev/null +++ b/src/systems/playing.cpp | |||
@@ -0,0 +1,98 @@ | |||
1 | #include "playing.h" | ||
2 | #include "game.h" | ||
3 | #include "components/transformable.h" | ||
4 | #include "components/animatable.h" | ||
5 | #include "components/playable.h" | ||
6 | #include "components/controllable.h" | ||
7 | #include "components/orientable.h" | ||
8 | #include "systems/mapping.h" | ||
9 | #include "systems/pondering.h" | ||
10 | #include "animation.h" | ||
11 | |||
12 | void PlayingSystem::tick(double) | ||
13 | { | ||
14 | // Check if we need to change the map | ||
15 | auto players = game_.getEntityManager().getEntitiesWithComponents< | ||
16 | PlayableComponent>(); | ||
17 | |||
18 | for (id_type player : players) | ||
19 | { | ||
20 | auto& playable = game_.getEntityManager(). | ||
21 | getComponent<PlayableComponent>(player); | ||
22 | |||
23 | if (playable.changingMap) | ||
24 | { | ||
25 | // Change the map! | ||
26 | auto entities = game_.getEntityManager().getEntities(); | ||
27 | |||
28 | for (id_type entity : entities) | ||
29 | { | ||
30 | if (entity != player) | ||
31 | { | ||
32 | game_.getEntityManager().deleteEntity(entity); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | game_.getSystemManager().getSystem<MappingSystem>(). | ||
37 | loadMap(playable.newMapId); | ||
38 | |||
39 | auto& transformable = game_.getEntityManager(). | ||
40 | getComponent<TransformableComponent>(player); | ||
41 | |||
42 | transformable.setX(playable.newMapX); | ||
43 | transformable.setY(playable.newMapY); | ||
44 | |||
45 | playable.changingMap = false; | ||
46 | |||
47 | break; | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | void PlayingSystem::initPlayer() | ||
53 | { | ||
54 | id_type player = game_.getEntityManager().emplaceEntity(); | ||
55 | |||
56 | AnimationSet playerGraphics {"res/Starla.png", 10, 12, 6}; | ||
57 | playerGraphics.emplaceAnimation("stillLeft", 3, 1, 1); | ||
58 | playerGraphics.emplaceAnimation("stillRight", 0, 1, 1); | ||
59 | playerGraphics.emplaceAnimation("walkingLeft", 4, 2, 10); | ||
60 | playerGraphics.emplaceAnimation("walkingRight", 1, 2, 10); | ||
61 | |||
62 | game_.getEntityManager().emplaceComponent<AnimatableComponent>( | ||
63 | player, | ||
64 | std::move(playerGraphics), | ||
65 | "stillLeft"); | ||
66 | |||
67 | game_.getEntityManager().emplaceComponent<TransformableComponent>( | ||
68 | player, | ||
69 | 203, 44, 10, 12); | ||
70 | |||
71 | game_.getSystemManager().getSystem<PonderingSystem>().initializeBody( | ||
72 | player, | ||
73 | PonderableComponent::Type::freefalling); | ||
74 | |||
75 | game_.getEntityManager().emplaceComponent<ControllableComponent>(player); | ||
76 | game_.getEntityManager().emplaceComponent<OrientableComponent>(player); | ||
77 | game_.getEntityManager().emplaceComponent<PlayableComponent>(player); | ||
78 | } | ||
79 | |||
80 | void PlayingSystem::changeMap( | ||
81 | size_t mapId, | ||
82 | double x, | ||
83 | double y) | ||
84 | { | ||
85 | auto players = game_.getEntityManager().getEntitiesWithComponents< | ||
86 | PlayableComponent>(); | ||
87 | |||
88 | for (id_type player : players) | ||
89 | { | ||
90 | auto& playable = game_.getEntityManager(). | ||
91 | getComponent<PlayableComponent>(player); | ||
92 | |||
93 | playable.changingMap = true; | ||
94 | playable.newMapId = mapId; | ||
95 | playable.newMapX = x; | ||
96 | playable.newMapY = y; | ||
97 | } | ||
98 | } | ||
diff --git a/src/systems/playing.h b/src/systems/playing.h new file mode 100644 index 0000000..c98a464 --- /dev/null +++ b/src/systems/playing.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef PLAYING_H_70A54F7D | ||
2 | #define PLAYING_H_70A54F7D | ||
3 | |||
4 | #include "system.h" | ||
5 | |||
6 | class PlayingSystem : public System { | ||
7 | public: | ||
8 | |||
9 | PlayingSystem(Game& game) : System(game) | ||
10 | { | ||
11 | } | ||
12 | |||
13 | void tick(double dt); | ||
14 | |||
15 | void initPlayer(); | ||
16 | |||
17 | void changeMap(size_t mapId, double x, double y); | ||
18 | |||
19 | }; | ||
20 | |||
21 | #endif /* end of include guard: PLAYING_H_70A54F7D */ | ||
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4a165b1..2490dc9 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp | |||
@@ -1,11 +1,14 @@ | |||
1 | #include "pondering.h" | 1 | #include "pondering.h" |
2 | #include <queue> | ||
2 | #include "game.h" | 3 | #include "game.h" |
3 | #include "components/ponderable.h" | 4 | #include "components/ponderable.h" |
4 | #include "components/transformable.h" | 5 | #include "components/transformable.h" |
5 | #include "components/orientable.h" | 6 | #include "components/orientable.h" |
6 | #include "components/mappable.h" | 7 | #include "components/mappable.h" |
7 | #include "systems/orienting.h" | 8 | #include "systems/orienting.h" |
9 | #include "systems/playing.h" | ||
8 | #include "consts.h" | 10 | #include "consts.h" |
11 | #include "collision.h" | ||
9 | 12 | ||
10 | void PonderingSystem::tick(double dt) | 13 | void PonderingSystem::tick(double dt) |
11 | { | 14 | { |
@@ -42,6 +45,9 @@ void PonderingSystem::tick(double dt) | |||
42 | bool oldGrounded = ponderable.isGrounded(); | 45 | bool oldGrounded = ponderable.isGrounded(); |
43 | ponderable.setGrounded(false); | 46 | ponderable.setGrounded(false); |
44 | 47 | ||
48 | std::priority_queue<Collision> collisions; | ||
49 | |||
50 | // Find collisions | ||
45 | for (id_type mapEntity : maps) | 51 | for (id_type mapEntity : maps) |
46 | { | 52 | { |
47 | auto& mappable = game_.getEntityManager(). | 53 | auto& mappable = game_.getEntityManager(). |
@@ -57,13 +63,13 @@ void PonderingSystem::tick(double dt) | |||
57 | && (oldY < it->second.getUpper())) | 63 | && (oldY < it->second.getUpper())) |
58 | { | 64 | { |
59 | // We have a collision! | 65 | // We have a collision! |
60 | processCollision( | 66 | collisions.emplace( |
61 | entity, | 67 | mapEntity, |
62 | Direction::left, | 68 | Direction::left, |
63 | newX, | 69 | it->second.getType(), |
64 | newY, | ||
65 | it->first, | 70 | it->first, |
66 | it->second.getType()); | 71 | it->second.getLower(), |
72 | it->second.getUpper()); | ||
67 | } | 73 | } |
68 | } | 74 | } |
69 | } else if (newX > oldX) | 75 | } else if (newX > oldX) |
@@ -77,13 +83,13 @@ void PonderingSystem::tick(double dt) | |||
77 | && (oldY < it->second.getUpper())) | 83 | && (oldY < it->second.getUpper())) |
78 | { | 84 | { |
79 | // We have a collision! | 85 | // We have a collision! |
80 | processCollision( | 86 | collisions.emplace( |
81 | entity, | 87 | mapEntity, |
82 | Direction::right, | 88 | Direction::right, |
83 | newX, | 89 | it->second.getType(), |
84 | newY, | ||
85 | it->first, | 90 | it->first, |
86 | it->second.getType()); | 91 | it->second.getLower(), |
92 | it->second.getUpper()); | ||
87 | } | 93 | } |
88 | } | 94 | } |
89 | } | 95 | } |
@@ -98,13 +104,13 @@ void PonderingSystem::tick(double dt) | |||
98 | && (oldX < it->second.getUpper())) | 104 | && (oldX < it->second.getUpper())) |
99 | { | 105 | { |
100 | // We have a collision! | 106 | // We have a collision! |
101 | processCollision( | 107 | collisions.emplace( |
102 | entity, | 108 | mapEntity, |
103 | Direction::up, | 109 | Direction::up, |
104 | newX, | 110 | it->second.getType(), |
105 | newY, | ||
106 | it->first, | 111 | it->first, |
107 | it->second.getType()); | 112 | it->second.getLower(), |
113 | it->second.getUpper()); | ||
108 | } | 114 | } |
109 | } | 115 | } |
110 | } else if (newY > oldY) | 116 | } else if (newY > oldY) |
@@ -118,13 +124,221 @@ void PonderingSystem::tick(double dt) | |||
118 | && (oldX < it->second.getUpper())) | 124 | && (oldX < it->second.getUpper())) |
119 | { | 125 | { |
120 | // We have a collision! | 126 | // We have a collision! |
121 | processCollision( | 127 | collisions.emplace( |
122 | entity, | 128 | mapEntity, |
123 | Direction::down, | 129 | Direction::down, |
124 | newX, | 130 | it->second.getType(), |
125 | newY, | ||
126 | it->first, | 131 | it->first, |
127 | it->second.getType()); | 132 | it->second.getLower(), |
133 | it->second.getUpper()); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | // Process collisions in order of priority | ||
140 | while (!collisions.empty()) | ||
141 | { | ||
142 | Collision collision = collisions.top(); | ||
143 | collisions.pop(); | ||
144 | |||
145 | // Make sure that they are still colliding | ||
146 | if (!collision.isColliding( | ||
147 | newX, | ||
148 | newY, | ||
149 | transformable.getW(), | ||
150 | transformable.getH())) | ||
151 | { | ||
152 | continue; | ||
153 | } | ||
154 | |||
155 | bool touchedWall = false; | ||
156 | bool stopProcessing = false; | ||
157 | |||
158 | switch (collision.getType()) | ||
159 | { | ||
160 | case Collision::Type::wall: | ||
161 | { | ||
162 | touchedWall = true; | ||
163 | |||
164 | break; | ||
165 | } | ||
166 | |||
167 | case Collision::Type::platform: | ||
168 | { | ||
169 | if (game_.getEntityManager(). | ||
170 | hasComponent<OrientableComponent>(entity)) | ||
171 | { | ||
172 | auto& orientable = game_.getEntityManager(). | ||
173 | getComponent<OrientableComponent>(entity); | ||
174 | |||
175 | if (orientable.getDropState() != | ||
176 | OrientableComponent::DropState::none) | ||
177 | { | ||
178 | orientable.setDropState(OrientableComponent::DropState::active); | ||
179 | } else { | ||
180 | touchedWall = true; | ||
181 | } | ||
182 | } else { | ||
183 | touchedWall = true; | ||
184 | } | ||
185 | |||
186 | break; | ||
187 | } | ||
188 | |||
189 | case Collision::Type::adjacency: | ||
190 | { | ||
191 | auto& mappable = game_.getEntityManager(). | ||
192 | getComponent<MappableComponent>(collision.getCollider()); | ||
193 | const Map& map = game_.getWorld().getMap(mappable.getMapId()); | ||
194 | auto& adj = [&] () -> const Map::Adjacent& { | ||
195 | switch (collision.getDirection()) | ||
196 | { | ||
197 | case Direction::left: return map.getLeftAdjacent(); | ||
198 | case Direction::right: return map.getRightAdjacent(); | ||
199 | case Direction::up: return map.getUpAdjacent(); | ||
200 | case Direction::down: return map.getDownAdjacent(); | ||
201 | } | ||
202 | }(); | ||
203 | |||
204 | switch (adj.getType()) | ||
205 | { | ||
206 | case Map::Adjacent::Type::wall: | ||
207 | { | ||
208 | touchedWall = true; | ||
209 | |||
210 | break; | ||
211 | } | ||
212 | |||
213 | case Map::Adjacent::Type::wrap: | ||
214 | { | ||
215 | switch (collision.getDirection()) | ||
216 | { | ||
217 | case Direction::left: | ||
218 | { | ||
219 | newX = GAME_WIDTH + WALL_GAP - transformable.getW(); | ||
220 | |||
221 | break; | ||
222 | } | ||
223 | |||
224 | case Direction::right: | ||
225 | { | ||
226 | newX = -WALL_GAP; | ||
227 | |||
228 | break; | ||
229 | } | ||
230 | |||
231 | case Direction::up: | ||
232 | { | ||
233 | newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - | ||
234 | transformable.getH(); | ||
235 | |||
236 | break; | ||
237 | } | ||
238 | |||
239 | case Direction::down: | ||
240 | { | ||
241 | newY = -WALL_GAP; | ||
242 | |||
243 | break; | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | |||
248 | case Map::Adjacent::Type::warp: | ||
249 | { | ||
250 | double warpX = newX; | ||
251 | double warpY = newY; | ||
252 | |||
253 | switch (collision.getDirection()) | ||
254 | { | ||
255 | case Direction::left: | ||
256 | { | ||
257 | warpX = GAME_WIDTH + WALL_GAP - transformable.getW(); | ||
258 | |||
259 | break; | ||
260 | } | ||
261 | |||
262 | case Direction::right: | ||
263 | { | ||
264 | warpX = -WALL_GAP; | ||
265 | |||
266 | break; | ||
267 | } | ||
268 | |||
269 | case Direction::up: | ||
270 | { | ||
271 | warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.getH(); | ||
272 | |||
273 | break; | ||
274 | } | ||
275 | |||
276 | case Direction::down: | ||
277 | { | ||
278 | warpY = -WALL_GAP; | ||
279 | |||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | game_.getSystemManager().getSystem<PlayingSystem>(). | ||
285 | changeMap(adj.getMapId(), warpX, warpY); | ||
286 | |||
287 | stopProcessing = true; | ||
288 | |||
289 | break; | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | default: | ||
295 | { | ||
296 | // Not yet implemented. | ||
297 | |||
298 | break; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | if (stopProcessing) | ||
303 | { | ||
304 | break; | ||
305 | } | ||
306 | |||
307 | if (touchedWall) | ||
308 | { | ||
309 | switch (collision.getDirection()) | ||
310 | { | ||
311 | case Direction::left: | ||
312 | { | ||
313 | newX = collision.getAxis(); | ||
314 | ponderable.setVelocityX(0.0); | ||
315 | |||
316 | break; | ||
317 | } | ||
318 | |||
319 | case Direction::right: | ||
320 | { | ||
321 | newX = collision.getAxis() - transformable.getW(); | ||
322 | ponderable.setVelocityX(0.0); | ||
323 | |||
324 | break; | ||
325 | } | ||
326 | |||
327 | case Direction::up: | ||
328 | { | ||
329 | newY = collision.getAxis(); | ||
330 | ponderable.setVelocityY(0.0); | ||
331 | |||
332 | break; | ||
333 | } | ||
334 | |||
335 | case Direction::down: | ||
336 | { | ||
337 | newY = collision.getAxis() - transformable.getH(); | ||
338 | ponderable.setVelocityY(0.0); | ||
339 | ponderable.setGrounded(true); | ||
340 | |||
341 | break; | ||
128 | } | 342 | } |
129 | } | 343 | } |
130 | } | 344 | } |
@@ -173,96 +387,3 @@ void PonderingSystem::initializeBody( | |||
173 | ponderable.setAccelY(NORMAL_GRAVITY); | 387 | ponderable.setAccelY(NORMAL_GRAVITY); |
174 | } | 388 | } |
175 | } | 389 | } |
176 | |||
177 | void PonderingSystem::processCollision( | ||
178 | id_type entity, | ||
179 | Direction dir, | ||
180 | double& newX, | ||
181 | double& newY, | ||
182 | int axis, | ||
183 | MappableComponent::Boundary::Type type) | ||
184 | { | ||
185 | auto& ponderable = game_.getEntityManager(). | ||
186 | getComponent<PonderableComponent>(entity); | ||
187 | |||
188 | auto& transformable = game_.getEntityManager(). | ||
189 | getComponent<TransformableComponent>(entity); | ||
190 | |||
191 | bool touchedGround = false; | ||
192 | |||
193 | switch (type) | ||
194 | { | ||
195 | case MappableComponent::Boundary::Type::wall: | ||
196 | { | ||
197 | switch (dir) | ||
198 | { | ||
199 | case Direction::left: | ||
200 | { | ||
201 | newX = axis; | ||
202 | ponderable.setVelocityX(0.0); | ||
203 | |||
204 | break; | ||
205 | } | ||
206 | |||
207 | case Direction::right: | ||
208 | { | ||
209 | newX = axis - transformable.getW(); | ||
210 | ponderable.setVelocityX(0.0); | ||
211 | |||
212 | break; | ||
213 | } | ||
214 | |||
215 | case Direction::up: | ||
216 | { | ||
217 | newY = axis; | ||
218 | ponderable.setVelocityY(0.0); | ||
219 | |||
220 | break; | ||
221 | } | ||
222 | |||
223 | case Direction::down: | ||
224 | { | ||
225 | touchedGround = true; | ||
226 | |||
227 | break; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | break; | ||
232 | } | ||
233 | |||
234 | case MappableComponent::Boundary::Type::platform: | ||
235 | { | ||
236 | if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) | ||
237 | { | ||
238 | auto& orientable = game_.getEntityManager(). | ||
239 | getComponent<OrientableComponent>(entity); | ||
240 | |||
241 | if (orientable.getDropState() != OrientableComponent::DropState::none) | ||
242 | { | ||
243 | orientable.setDropState(OrientableComponent::DropState::active); | ||
244 | } else { | ||
245 | touchedGround = true; | ||
246 | } | ||
247 | } else { | ||
248 | touchedGround = true; | ||
249 | } | ||
250 | |||
251 | break; | ||
252 | } | ||
253 | |||
254 | default: | ||
255 | { | ||
256 | // Not yet implemented. | ||
257 | |||
258 | break; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | if (touchedGround) | ||
263 | { | ||
264 | newY = axis - transformable.getH(); | ||
265 | ponderable.setVelocityY(0.0); | ||
266 | ponderable.setGrounded(true); | ||
267 | } | ||
268 | } | ||
diff --git a/src/systems/pondering.h b/src/systems/pondering.h index a16622b..d70525b 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h | |||
@@ -2,7 +2,6 @@ | |||
2 | #define PONDERING_H_F2530E0E | 2 | #define PONDERING_H_F2530E0E |
3 | 3 | ||
4 | #include "system.h" | 4 | #include "system.h" |
5 | #include "components/mappable.h" | ||
6 | #include "components/ponderable.h" | 5 | #include "components/ponderable.h" |
7 | #include "direction.h" | 6 | #include "direction.h" |
8 | 7 | ||
@@ -17,16 +16,6 @@ public: | |||
17 | 16 | ||
18 | void initializeBody(id_type entity, PonderableComponent::Type type); | 17 | void initializeBody(id_type entity, PonderableComponent::Type type); |
19 | 18 | ||
20 | private: | ||
21 | |||
22 | void processCollision( | ||
23 | id_type entity, | ||
24 | Direction dir, | ||
25 | double& newX, | ||
26 | double& newY, | ||
27 | int axis, | ||
28 | MappableComponent::Boundary::Type type); | ||
29 | |||
30 | }; | 19 | }; |
31 | 20 | ||
32 | #endif /* end of include guard: PONDERING_H_F2530E0E */ | 21 | #endif /* end of include guard: PONDERING_H_F2530E0E */ |
diff --git a/src/world.cpp b/src/world.cpp index 9b1e4f6..3b6bd41 100644 --- a/src/world.cpp +++ b/src/world.cpp | |||
@@ -63,6 +63,10 @@ World::World(std::string filename) | |||
63 | xmlFree(key); | 63 | xmlFree(key); |
64 | 64 | ||
65 | std::vector<int> mapTiles; | 65 | std::vector<int> mapTiles; |
66 | Map::Adjacent leftAdj; | ||
67 | Map::Adjacent rightAdj; | ||
68 | Map::Adjacent upAdj; | ||
69 | Map::Adjacent downAdj; | ||
66 | 70 | ||
67 | for (xmlNodePtr mapNode = node->xmlChildrenNode; | 71 | for (xmlNodePtr mapNode = node->xmlChildrenNode; |
68 | mapNode != nullptr; | 72 | mapNode != nullptr; |
@@ -82,6 +86,54 @@ World::World(std::string filename) | |||
82 | } | 86 | } |
83 | 87 | ||
84 | xmlFree(key); | 88 | xmlFree(key); |
89 | } else if (!xmlStrcmp( | ||
90 | mapNode->name, | ||
91 | reinterpret_cast<const xmlChar*>("adjacent"))) | ||
92 | { | ||
93 | key = getProp(mapNode, "type"); | ||
94 | std::string adjTypeStr(reinterpret_cast<char*>(key)); | ||
95 | xmlFree(key); | ||
96 | |||
97 | Map::Adjacent::Type adjType; | ||
98 | if (adjTypeStr == "wall") | ||
99 | { | ||
100 | adjType = Map::Adjacent::Type::wall; | ||
101 | } else if (adjTypeStr == "wrap") | ||
102 | { | ||
103 | adjType = Map::Adjacent::Type::wrap; | ||
104 | } else if (adjTypeStr == "warp") | ||
105 | { | ||
106 | adjType = Map::Adjacent::Type::warp; | ||
107 | } else if (adjTypeStr == "reverseWarp") | ||
108 | { | ||
109 | adjType = Map::Adjacent::Type::reverse; | ||
110 | } else { | ||
111 | throw std::logic_error("Invalid adjacency type"); | ||
112 | } | ||
113 | |||
114 | key = getProp(mapNode, "map"); | ||
115 | int adjMapId = atoi(reinterpret_cast<char*>(key)); | ||
116 | xmlFree(key); | ||
117 | |||
118 | key = getProp(mapNode, "dir"); | ||
119 | std::string adjDir(reinterpret_cast<char*>(key)); | ||
120 | xmlFree(key); | ||
121 | |||
122 | if (adjDir == "left") | ||
123 | { | ||
124 | leftAdj = {adjType, adjMapId}; | ||
125 | } else if (adjDir == "right") | ||
126 | { | ||
127 | rightAdj = {adjType, adjMapId}; | ||
128 | } else if (adjDir == "up") | ||
129 | { | ||
130 | upAdj = {adjType, adjMapId}; | ||
131 | } else if (adjDir == "down") | ||
132 | { | ||
133 | downAdj = {adjType, adjMapId}; | ||
134 | } else { | ||
135 | throw std::logic_error("Invalid adjacency direction"); | ||
136 | } | ||
85 | } | 137 | } |
86 | } | 138 | } |
87 | 139 | ||
@@ -91,7 +143,11 @@ World::World(std::string filename) | |||
91 | std::forward_as_tuple( | 143 | std::forward_as_tuple( |
92 | mapId, | 144 | mapId, |
93 | std::move(mapTiles), | 145 | std::move(mapTiles), |
94 | std::move(mapTitle))); | 146 | std::move(mapTitle), |
147 | leftAdj, | ||
148 | rightAdj, | ||
149 | upAdj, | ||
150 | downAdj)); | ||
95 | } | 151 | } |
96 | } | 152 | } |
97 | 153 | ||