diff options
Diffstat (limited to 'src/systems')
-rw-r--r-- | src/systems/animating.cpp | 103 | ||||
-rw-r--r-- | src/systems/animating.h | 25 | ||||
-rw-r--r-- | src/systems/controlling.cpp | 136 | ||||
-rw-r--r-- | src/systems/controlling.h | 27 | ||||
-rw-r--r-- | src/systems/mapping.cpp | 187 | ||||
-rw-r--r-- | src/systems/mapping.h | 19 | ||||
-rw-r--r-- | src/systems/orienting.cpp | 230 | ||||
-rw-r--r-- | src/systems/orienting.h | 35 | ||||
-rw-r--r-- | src/systems/playing.cpp | 144 | ||||
-rw-r--r-- | src/systems/playing.h | 25 | ||||
-rw-r--r-- | src/systems/pondering.cpp | 916 | ||||
-rw-r--r-- | src/systems/pondering.h | 81 | ||||
-rw-r--r-- | src/systems/realizing.cpp | 438 | ||||
-rw-r--r-- | src/systems/realizing.h | 86 | ||||
-rw-r--r-- | src/systems/scheduling.cpp | 54 | ||||
-rw-r--r-- | src/systems/scheduling.h | 22 | ||||
-rw-r--r-- | src/systems/scripting.cpp | 257 | ||||
-rw-r--r-- | src/systems/scripting.h | 34 |
18 files changed, 2819 insertions, 0 deletions
diff --git a/src/systems/animating.cpp b/src/systems/animating.cpp new file mode 100644 index 0000000..50a32fc --- /dev/null +++ b/src/systems/animating.cpp | |||
@@ -0,0 +1,103 @@ | |||
1 | #include "animating.h" | ||
2 | #include "game.h" | ||
3 | #include "components/animatable.h" | ||
4 | #include "components/transformable.h" | ||
5 | |||
6 | void AnimatingSystem::tick(double) | ||
7 | { | ||
8 | std::set<id_type> spriteEntities = | ||
9 | game_.getEntityManager().getEntitiesWithComponents<AnimatableComponent>(); | ||
10 | |||
11 | for (id_type entity : spriteEntities) | ||
12 | { | ||
13 | auto& sprite = game_.getEntityManager(). | ||
14 | getComponent<AnimatableComponent>(entity); | ||
15 | |||
16 | if (sprite.active) | ||
17 | { | ||
18 | if (!sprite.frozen) | ||
19 | { | ||
20 | sprite.countdown++; | ||
21 | } | ||
22 | |||
23 | const Animation& anim = sprite.getAnimation(); | ||
24 | if (sprite.countdown >= anim.getDelay()) | ||
25 | { | ||
26 | sprite.frame++; | ||
27 | sprite.countdown = 0; | ||
28 | |||
29 | if (sprite.frame >= anim.getFirstFrame() + anim.getNumFrames()) | ||
30 | { | ||
31 | sprite.frame = anim.getFirstFrame(); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | if (sprite.flickering) | ||
36 | { | ||
37 | sprite.flickerTimer = (sprite.flickerTimer + 1) % 6; | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | void AnimatingSystem::render(Texture& texture) | ||
44 | { | ||
45 | std::set<id_type> spriteEntities = | ||
46 | game_.getEntityManager().getEntitiesWithComponents< | ||
47 | AnimatableComponent, | ||
48 | TransformableComponent>(); | ||
49 | |||
50 | for (id_type entity : spriteEntities) | ||
51 | { | ||
52 | auto& sprite = game_.getEntityManager(). | ||
53 | getComponent<AnimatableComponent>(entity); | ||
54 | |||
55 | if (sprite.active) | ||
56 | { | ||
57 | auto& transform = game_.getEntityManager(). | ||
58 | getComponent<TransformableComponent>(entity); | ||
59 | |||
60 | double alpha = 1.0; | ||
61 | if (sprite.flickering && (sprite.flickerTimer < 3)) | ||
62 | { | ||
63 | alpha = 0.0; | ||
64 | } | ||
65 | |||
66 | Rectangle dstrect { | ||
67 | static_cast<int>(transform.pos.x()), | ||
68 | static_cast<int>(transform.pos.y()), | ||
69 | transform.size.w(), | ||
70 | transform.size.h()}; | ||
71 | |||
72 | const AnimationSet& aset = sprite.animationSet; | ||
73 | game_.getRenderer().blit( | ||
74 | aset.getTexture(), | ||
75 | texture, | ||
76 | aset.getFrameRect(sprite.frame), | ||
77 | dstrect, | ||
78 | alpha); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | void AnimatingSystem::initPrototype(id_type entity) | ||
84 | { | ||
85 | auto& sprite = game_.getEntityManager(). | ||
86 | getComponent<AnimatableComponent>(entity); | ||
87 | |||
88 | startAnimation(entity, sprite.origAnimation); | ||
89 | |||
90 | sprite.countdown = 0; | ||
91 | sprite.flickering = false; | ||
92 | sprite.flickerTimer = 0; | ||
93 | sprite.frozen = false; | ||
94 | } | ||
95 | |||
96 | void AnimatingSystem::startAnimation(id_type entity, std::string animation) | ||
97 | { | ||
98 | auto& sprite = game_.getEntityManager(). | ||
99 | getComponent<AnimatableComponent>(entity); | ||
100 | |||
101 | sprite.animation = std::move(animation); | ||
102 | sprite.frame = sprite.getAnimation().getFirstFrame(); | ||
103 | } | ||
diff --git a/src/systems/animating.h b/src/systems/animating.h new file mode 100644 index 0000000..acc6191 --- /dev/null +++ b/src/systems/animating.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef ANIMATING_H_5BBF0094 | ||
2 | #define ANIMATING_H_5BBF0094 | ||
3 | |||
4 | #include "system.h" | ||
5 | #include <string> | ||
6 | #include "renderer/texture.h" | ||
7 | |||
8 | class AnimatingSystem : public System { | ||
9 | public: | ||
10 | |||
11 | AnimatingSystem(Game& game) : System(game) | ||
12 | { | ||
13 | } | ||
14 | |||
15 | void tick(double dt); | ||
16 | |||
17 | void render(Texture& texture); | ||
18 | |||
19 | void initPrototype(id_type entity); | ||
20 | |||
21 | void startAnimation(id_type entity, std::string animation); | ||
22 | |||
23 | }; | ||
24 | |||
25 | #endif /* end of include guard: ANIMATING_H_5BBF0094 */ | ||
diff --git a/src/systems/controlling.cpp b/src/systems/controlling.cpp new file mode 100644 index 0000000..e65c09a --- /dev/null +++ b/src/systems/controlling.cpp | |||
@@ -0,0 +1,136 @@ | |||
1 | #include "controlling.h" | ||
2 | #include "game.h" | ||
3 | #include "components/controllable.h" | ||
4 | #include "components/orientable.h" | ||
5 | #include "systems/orienting.h" | ||
6 | |||
7 | void ControllingSystem::tick(double) | ||
8 | { | ||
9 | while (!actions_.empty()) | ||
10 | { | ||
11 | int key = actions_.front().first; | ||
12 | int action = actions_.front().second; | ||
13 | |||
14 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
15 | ControllableComponent, | ||
16 | OrientableComponent>(); | ||
17 | |||
18 | for (auto entity : entities) | ||
19 | { | ||
20 | auto& controllable = game_.getEntityManager(). | ||
21 | getComponent<ControllableComponent>(entity); | ||
22 | |||
23 | auto& orienting = game_.getSystemManager().getSystem<OrientingSystem>(); | ||
24 | |||
25 | if (action == GLFW_PRESS) | ||
26 | { | ||
27 | if (key == controllable.getLeftKey()) | ||
28 | { | ||
29 | controllable.setHoldingLeft(true); | ||
30 | |||
31 | if (!controllable.isFrozen()) | ||
32 | { | ||
33 | orienting.moveLeft(entity); | ||
34 | } | ||
35 | } else if (key == controllable.getRightKey()) | ||
36 | { | ||
37 | controllable.setHoldingRight(true); | ||
38 | |||
39 | if (!controllable.isFrozen()) | ||
40 | { | ||
41 | orienting.moveRight(entity); | ||
42 | } | ||
43 | } else if (key == controllable.getJumpKey()) | ||
44 | { | ||
45 | if (!controllable.isFrozen()) | ||
46 | { | ||
47 | orienting.jump(entity); | ||
48 | } | ||
49 | } else if (key == controllable.getDropKey()) | ||
50 | { | ||
51 | if (!controllable.isFrozen()) | ||
52 | { | ||
53 | orienting.drop(entity); | ||
54 | } | ||
55 | } | ||
56 | } else if (action == GLFW_RELEASE) | ||
57 | { | ||
58 | if (key == controllable.getLeftKey()) | ||
59 | { | ||
60 | controllable.setHoldingLeft(false); | ||
61 | |||
62 | if (!controllable.isFrozen()) | ||
63 | { | ||
64 | if (controllable.isHoldingRight()) | ||
65 | { | ||
66 | orienting.moveRight(entity); | ||
67 | } else { | ||
68 | orienting.stopWalking(entity); | ||
69 | } | ||
70 | } | ||
71 | } else if (key == controllable.getRightKey()) | ||
72 | { | ||
73 | controllable.setHoldingRight(false); | ||
74 | |||
75 | if (!controllable.isFrozen()) | ||
76 | { | ||
77 | if (controllable.isHoldingLeft()) | ||
78 | { | ||
79 | orienting.moveLeft(entity); | ||
80 | } else { | ||
81 | orienting.stopWalking(entity); | ||
82 | } | ||
83 | } | ||
84 | } else if (key == controllable.getDropKey()) | ||
85 | { | ||
86 | if (!controllable.isFrozen()) | ||
87 | { | ||
88 | orienting.stopDropping(entity); | ||
89 | } | ||
90 | } else if (key == controllable.getJumpKey()) | ||
91 | { | ||
92 | if (!controllable.isFrozen()) | ||
93 | { | ||
94 | orienting.stopJumping(entity); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | actions_.pop(); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void ControllingSystem::input(int key, int action) | ||
105 | { | ||
106 | actions_.push(std::make_pair(key, action)); | ||
107 | } | ||
108 | |||
109 | void ControllingSystem::freeze(id_type entity) | ||
110 | { | ||
111 | auto& controllable = game_.getEntityManager(). | ||
112 | getComponent<ControllableComponent>(entity); | ||
113 | |||
114 | controllable.setFrozen(true); | ||
115 | } | ||
116 | |||
117 | void ControllingSystem::unfreeze(id_type entity) | ||
118 | { | ||
119 | auto& controllable = game_.getEntityManager(). | ||
120 | getComponent<ControllableComponent>(entity); | ||
121 | |||
122 | if (controllable.isFrozen()) | ||
123 | { | ||
124 | controllable.setFrozen(false); | ||
125 | |||
126 | auto& orienting = game_.getSystemManager().getSystem<OrientingSystem>(); | ||
127 | |||
128 | if (controllable.isHoldingLeft()) | ||
129 | { | ||
130 | orienting.moveLeft(entity); | ||
131 | } else if (controllable.isHoldingRight()) | ||
132 | { | ||
133 | orienting.moveRight(entity); | ||
134 | } | ||
135 | } | ||
136 | } | ||
diff --git a/src/systems/controlling.h b/src/systems/controlling.h new file mode 100644 index 0000000..d6f0789 --- /dev/null +++ b/src/systems/controlling.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef CONTROLLING_H_80B1BB8D | ||
2 | #define CONTROLLING_H_80B1BB8D | ||
3 | |||
4 | #include "system.h" | ||
5 | #include <queue> | ||
6 | |||
7 | class ControllingSystem : public System { | ||
8 | public: | ||
9 | |||
10 | ControllingSystem(Game& game) : System(game) | ||
11 | { | ||
12 | } | ||
13 | |||
14 | void tick(double dt); | ||
15 | |||
16 | void input(int key, int action); | ||
17 | |||
18 | void freeze(id_type entity); | ||
19 | |||
20 | void unfreeze(id_type entity); | ||
21 | |||
22 | private: | ||
23 | |||
24 | std::queue<std::pair<int,int>> actions_; | ||
25 | }; | ||
26 | |||
27 | #endif /* end of include guard: CONTROLLING_H_80B1BB8D */ | ||
diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp new file mode 100644 index 0000000..1275e11 --- /dev/null +++ b/src/systems/mapping.cpp | |||
@@ -0,0 +1,187 @@ | |||
1 | #include "mapping.h" | ||
2 | #include "components/mappable.h" | ||
3 | #include "systems/realizing.h" | ||
4 | #include "game.h" | ||
5 | #include "consts.h" | ||
6 | |||
7 | template <typename Storage> | ||
8 | inline void addBoundary( | ||
9 | Storage& boundaries, | ||
10 | int axis, | ||
11 | int lower, | ||
12 | int upper, | ||
13 | MappableComponent::Boundary::Type type) | ||
14 | { | ||
15 | boundaries.emplace(std::piecewise_construct, | ||
16 | std::tie(axis), | ||
17 | std::tie(axis, lower, upper, type)); | ||
18 | } | ||
19 | |||
20 | void MappingSystem::render(Texture& texture) | ||
21 | { | ||
22 | id_type map = | ||
23 | game_.getSystemManager().getSystem<RealizingSystem>().getActiveMap(); | ||
24 | |||
25 | auto& mappable = game_.getEntityManager(). | ||
26 | getComponent<MappableComponent>(map); | ||
27 | |||
28 | for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) | ||
29 | { | ||
30 | int x = i % MAP_WIDTH; | ||
31 | int y = i / MAP_WIDTH; | ||
32 | int tile = mappable.tiles[i]; | ||
33 | |||
34 | if (tile > 0) | ||
35 | { | ||
36 | Rectangle dst { | ||
37 | x * TILE_WIDTH, | ||
38 | y * TILE_HEIGHT, | ||
39 | TILE_WIDTH, | ||
40 | TILE_HEIGHT}; | ||
41 | |||
42 | Rectangle src { | ||
43 | (tile % TILESET_COLS) * TILE_WIDTH, | ||
44 | (tile / TILESET_COLS) * TILE_HEIGHT, | ||
45 | TILE_WIDTH, | ||
46 | TILE_HEIGHT}; | ||
47 | |||
48 | game_.getRenderer().blit( | ||
49 | mappable.tileset, | ||
50 | texture, | ||
51 | std::move(src), | ||
52 | std::move(dst)); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | int startX = ((GAME_WIDTH / TILE_WIDTH) / 2) - (mappable.title.size() / 2); | ||
57 | |||
58 | for (size_t i = 0; i < mappable.title.size(); i++) | ||
59 | { | ||
60 | Rectangle src { | ||
61 | (mappable.title[i] % FONT_COLS) * TILE_WIDTH, | ||
62 | (mappable.title[i] / FONT_COLS) * TILE_HEIGHT, | ||
63 | TILE_WIDTH, | ||
64 | TILE_HEIGHT}; | ||
65 | |||
66 | Rectangle dst { | ||
67 | (startX + static_cast<int>(i)) * TILE_WIDTH, | ||
68 | 24 * TILE_HEIGHT, | ||
69 | TILE_WIDTH, | ||
70 | TILE_HEIGHT}; | ||
71 | |||
72 | game_.getRenderer().blit( | ||
73 | mappable.font, | ||
74 | texture, | ||
75 | std::move(src), | ||
76 | std::move(dst)); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | void MappingSystem::generateBoundaries(id_type mapEntity) | ||
81 | { | ||
82 | auto& mappable = game_.getEntityManager(). | ||
83 | getComponent<MappableComponent>(mapEntity); | ||
84 | |||
85 | addBoundary( | ||
86 | mappable.leftBoundaries, | ||
87 | -WALL_GAP, | ||
88 | 0, | ||
89 | MAP_HEIGHT * TILE_HEIGHT, | ||
90 | MappableComponent::Boundary::Type::adjacency); | ||
91 | |||
92 | addBoundary( | ||
93 | mappable.rightBoundaries, | ||
94 | GAME_WIDTH + WALL_GAP, | ||
95 | 0, | ||
96 | MAP_HEIGHT * TILE_HEIGHT, | ||
97 | MappableComponent::Boundary::Type::adjacency); | ||
98 | |||
99 | addBoundary( | ||
100 | mappable.upBoundaries, | ||
101 | -WALL_GAP, | ||
102 | 0, | ||
103 | GAME_WIDTH, | ||
104 | MappableComponent::Boundary::Type::adjacency); | ||
105 | |||
106 | addBoundary( | ||
107 | mappable.downBoundaries, | ||
108 | MAP_HEIGHT * TILE_HEIGHT + WALL_GAP, | ||
109 | 0, | ||
110 | GAME_WIDTH, | ||
111 | MappableComponent::Boundary::Type::adjacency); | ||
112 | |||
113 | for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) | ||
114 | { | ||
115 | size_t x = i % MAP_WIDTH; | ||
116 | size_t y = i / MAP_WIDTH; | ||
117 | int tile = mappable.tiles[i]; | ||
118 | |||
119 | if ((tile >= 5) && (tile <= 7)) | ||
120 | { | ||
121 | addBoundary( | ||
122 | mappable.downBoundaries, | ||
123 | y * TILE_HEIGHT, | ||
124 | x * TILE_WIDTH, | ||
125 | (x + 1) * TILE_WIDTH, | ||
126 | MappableComponent::Boundary::Type::platform); | ||
127 | } else if ((tile > 0) && (tile < 28)) | ||
128 | { | ||
129 | addBoundary( | ||
130 | mappable.rightBoundaries, | ||
131 | x * TILE_WIDTH, | ||
132 | y * TILE_HEIGHT, | ||
133 | (y+1) * TILE_HEIGHT, | ||
134 | MappableComponent::Boundary::Type::wall); | ||
135 | |||
136 | addBoundary( | ||
137 | mappable.leftBoundaries, | ||
138 | (x+1) * TILE_WIDTH, | ||
139 | y * TILE_HEIGHT, | ||
140 | (y+1) * TILE_HEIGHT, | ||
141 | MappableComponent::Boundary::Type::wall); | ||
142 | |||
143 | addBoundary( | ||
144 | mappable.downBoundaries, | ||
145 | y * TILE_HEIGHT, | ||
146 | x * TILE_WIDTH, | ||
147 | (x+1) * TILE_WIDTH, | ||
148 | MappableComponent::Boundary::Type::wall); | ||
149 | |||
150 | addBoundary( | ||
151 | mappable.upBoundaries, | ||
152 | (y+1) * TILE_HEIGHT, | ||
153 | x * TILE_WIDTH, | ||
154 | (x+1) * TILE_WIDTH, | ||
155 | MappableComponent::Boundary::Type::wall); | ||
156 | } else if (tile == 42) | ||
157 | { | ||
158 | addBoundary( | ||
159 | mappable.rightBoundaries, | ||
160 | x * TILE_WIDTH, | ||
161 | y * TILE_HEIGHT, | ||
162 | (y+1) * TILE_HEIGHT, | ||
163 | MappableComponent::Boundary::Type::danger); | ||
164 | |||
165 | addBoundary( | ||
166 | mappable.leftBoundaries, | ||
167 | (x+1) * TILE_WIDTH, | ||
168 | y * TILE_HEIGHT, | ||
169 | (y+1) * TILE_HEIGHT, | ||
170 | MappableComponent::Boundary::Type::danger); | ||
171 | |||
172 | addBoundary( | ||
173 | mappable.downBoundaries, | ||
174 | y * TILE_HEIGHT + 1, | ||
175 | x * TILE_WIDTH, | ||
176 | (x+1) * TILE_WIDTH, | ||
177 | MappableComponent::Boundary::Type::danger); | ||
178 | |||
179 | addBoundary( | ||
180 | mappable.upBoundaries, | ||
181 | (y+1) * TILE_HEIGHT, | ||
182 | x * TILE_WIDTH, | ||
183 | (x+1) * TILE_WIDTH, | ||
184 | MappableComponent::Boundary::Type::danger); | ||
185 | } | ||
186 | } | ||
187 | } | ||
diff --git a/src/systems/mapping.h b/src/systems/mapping.h new file mode 100644 index 0000000..3c47419 --- /dev/null +++ b/src/systems/mapping.h | |||
@@ -0,0 +1,19 @@ | |||
1 | #ifndef MAPPING_H_33FC2294 | ||
2 | #define MAPPING_H_33FC2294 | ||
3 | |||
4 | #include "system.h" | ||
5 | |||
6 | class MappingSystem : public System { | ||
7 | public: | ||
8 | |||
9 | MappingSystem(Game& game) : System(game) | ||
10 | { | ||
11 | } | ||
12 | |||
13 | void render(Texture& texture); | ||
14 | |||
15 | void generateBoundaries(id_type mapEntity); | ||
16 | |||
17 | }; | ||
18 | |||
19 | #endif /* end of include guard: MAPPING_H_33FC2294 */ | ||
diff --git a/src/systems/orienting.cpp b/src/systems/orienting.cpp new file mode 100644 index 0000000..d73ddd2 --- /dev/null +++ b/src/systems/orienting.cpp | |||
@@ -0,0 +1,230 @@ | |||
1 | #include "orienting.h" | ||
2 | #include "game.h" | ||
3 | #include "components/orientable.h" | ||
4 | #include "components/ponderable.h" | ||
5 | #include "systems/animating.h" | ||
6 | #include "consts.h" | ||
7 | #include "muxer.h" | ||
8 | |||
9 | void OrientingSystem::tick(double) | ||
10 | { | ||
11 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
12 | OrientableComponent, | ||
13 | PonderableComponent>(); | ||
14 | |||
15 | for (id_type entity : entities) | ||
16 | { | ||
17 | auto& orientable = game_.getEntityManager(). | ||
18 | getComponent<OrientableComponent>(entity); | ||
19 | |||
20 | auto& ponderable = game_.getEntityManager(). | ||
21 | getComponent<PonderableComponent>(entity); | ||
22 | |||
23 | switch (orientable.getWalkState()) | ||
24 | { | ||
25 | case OrientableComponent::WalkState::still: | ||
26 | { | ||
27 | ponderable.vel.x() = 0.0; | ||
28 | |||
29 | break; | ||
30 | } | ||
31 | |||
32 | case OrientableComponent::WalkState::left: | ||
33 | { | ||
34 | ponderable.vel.x() = -WALK_SPEED; | ||
35 | |||
36 | break; | ||
37 | } | ||
38 | |||
39 | case OrientableComponent::WalkState::right: | ||
40 | { | ||
41 | ponderable.vel.x() = WALK_SPEED; | ||
42 | |||
43 | break; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | if (orientable.isJumping() && (ponderable.vel.y() > 0)) | ||
48 | { | ||
49 | orientable.setJumping(false); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | void OrientingSystem::moveLeft(id_type entity) | ||
55 | { | ||
56 | auto& ponderable = game_.getEntityManager(). | ||
57 | getComponent<PonderableComponent>(entity); | ||
58 | |||
59 | auto& orientable = game_.getEntityManager(). | ||
60 | getComponent<OrientableComponent>(entity); | ||
61 | |||
62 | orientable.setFacingRight(false); | ||
63 | orientable.setWalkState(OrientableComponent::WalkState::left); | ||
64 | |||
65 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
66 | if (ponderable.grounded) | ||
67 | { | ||
68 | animating.startAnimation(entity, "walkingLeft"); | ||
69 | } else { | ||
70 | animating.startAnimation(entity, "stillLeft"); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | void OrientingSystem::moveRight(id_type entity) | ||
75 | { | ||
76 | auto& ponderable = game_.getEntityManager(). | ||
77 | getComponent<PonderableComponent>(entity); | ||
78 | |||
79 | auto& orientable = game_.getEntityManager(). | ||
80 | getComponent<OrientableComponent>(entity); | ||
81 | |||
82 | orientable.setFacingRight(true); | ||
83 | orientable.setWalkState(OrientableComponent::WalkState::right); | ||
84 | |||
85 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
86 | if (ponderable.grounded) | ||
87 | { | ||
88 | animating.startAnimation(entity, "walkingRight"); | ||
89 | } else { | ||
90 | animating.startAnimation(entity, "stillRight"); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | void OrientingSystem::stopWalking(id_type entity) | ||
95 | { | ||
96 | auto& orientable = game_.getEntityManager(). | ||
97 | getComponent<OrientableComponent>(entity); | ||
98 | |||
99 | orientable.setWalkState(OrientableComponent::WalkState::still); | ||
100 | |||
101 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
102 | |||
103 | if (orientable.isFacingRight()) | ||
104 | { | ||
105 | animating.startAnimation(entity, "stillRight"); | ||
106 | } else { | ||
107 | animating.startAnimation(entity, "stillLeft"); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | void OrientingSystem::jump(id_type entity) | ||
112 | { | ||
113 | auto& ponderable = game_.getEntityManager(). | ||
114 | getComponent<PonderableComponent>(entity); | ||
115 | |||
116 | if (ponderable.grounded) | ||
117 | { | ||
118 | auto& orientable = game_.getEntityManager(). | ||
119 | getComponent<OrientableComponent>(entity); | ||
120 | |||
121 | orientable.setJumping(true); | ||
122 | |||
123 | playSound("res/Randomize87.wav", 0.25); | ||
124 | |||
125 | ponderable.vel.y() = JUMP_VELOCITY; | ||
126 | ponderable.accel.y() = JUMP_GRAVITY; | ||
127 | |||
128 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
129 | if (orientable.isFacingRight()) | ||
130 | { | ||
131 | animating.startAnimation(entity, "stillRight"); | ||
132 | } else { | ||
133 | animating.startAnimation(entity, "stillLeft"); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | void OrientingSystem::stopJumping(id_type entity) | ||
139 | { | ||
140 | auto& orientable = game_.getEntityManager(). | ||
141 | getComponent<OrientableComponent>(entity); | ||
142 | |||
143 | if (orientable.isJumping()) | ||
144 | { | ||
145 | orientable.setJumping(false); | ||
146 | |||
147 | auto& ponderable = game_.getEntityManager(). | ||
148 | getComponent<PonderableComponent>(entity); | ||
149 | |||
150 | ponderable.accel.y() = NORMAL_GRAVITY; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | void OrientingSystem::land(id_type entity) | ||
155 | { | ||
156 | auto& orientable = game_.getEntityManager(). | ||
157 | getComponent<OrientableComponent>(entity); | ||
158 | |||
159 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
160 | |||
161 | switch (orientable.getWalkState()) | ||
162 | { | ||
163 | case OrientableComponent::WalkState::still: | ||
164 | { | ||
165 | if (orientable.isFacingRight()) | ||
166 | { | ||
167 | animating.startAnimation(entity, "stillRight"); | ||
168 | } else { | ||
169 | animating.startAnimation(entity, "stillLeft"); | ||
170 | } | ||
171 | |||
172 | break; | ||
173 | } | ||
174 | |||
175 | case OrientableComponent::WalkState::left: | ||
176 | { | ||
177 | animating.startAnimation(entity, "walkingLeft"); | ||
178 | |||
179 | break; | ||
180 | } | ||
181 | |||
182 | case OrientableComponent::WalkState::right: | ||
183 | { | ||
184 | animating.startAnimation(entity, "walkingRight"); | ||
185 | |||
186 | break; | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | void OrientingSystem::startFalling(id_type entity) | ||
192 | { | ||
193 | auto& orientable = game_.getEntityManager(). | ||
194 | getComponent<OrientableComponent>(entity); | ||
195 | |||
196 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
197 | |||
198 | if (orientable.isFacingRight()) | ||
199 | { | ||
200 | animating.startAnimation(entity, "stillRight"); | ||
201 | } else { | ||
202 | animating.startAnimation(entity, "stillLeft"); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | void OrientingSystem::drop(id_type entity) | ||
207 | { | ||
208 | auto& orientable = game_.getEntityManager(). | ||
209 | getComponent<OrientableComponent>(entity); | ||
210 | |||
211 | auto& ponderable = game_.getEntityManager(). | ||
212 | getComponent<PonderableComponent>(entity); | ||
213 | |||
214 | if (ponderable.grounded | ||
215 | && (orientable.getDropState() == OrientableComponent::DropState::none)) | ||
216 | { | ||
217 | orientable.setDropState(OrientableComponent::DropState::ready); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | void OrientingSystem::stopDropping(id_type entity) | ||
222 | { | ||
223 | auto& orientable = game_.getEntityManager(). | ||
224 | getComponent<OrientableComponent>(entity); | ||
225 | |||
226 | if (orientable.getDropState() == OrientableComponent::DropState::ready) | ||
227 | { | ||
228 | orientable.setDropState(OrientableComponent::DropState::none); | ||
229 | } | ||
230 | } | ||
diff --git a/src/systems/orienting.h b/src/systems/orienting.h new file mode 100644 index 0000000..4ded612 --- /dev/null +++ b/src/systems/orienting.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef ORIENTING_H_099F0C23 | ||
2 | #define ORIENTING_H_099F0C23 | ||
3 | |||
4 | #include "system.h" | ||
5 | |||
6 | class OrientingSystem : public System { | ||
7 | public: | ||
8 | |||
9 | OrientingSystem(Game& game) : System(game) | ||
10 | { | ||
11 | } | ||
12 | |||
13 | void tick(double dt); | ||
14 | |||
15 | void moveLeft(id_type entity); | ||
16 | |||
17 | void moveRight(id_type entity); | ||
18 | |||
19 | void stopWalking(id_type entity); | ||
20 | |||
21 | void jump(id_type entity); | ||
22 | |||
23 | void stopJumping(id_type entity); | ||
24 | |||
25 | void land(id_type entity); | ||
26 | |||
27 | void startFalling(id_type entity); | ||
28 | |||
29 | void drop(id_type entity); | ||
30 | |||
31 | void stopDropping(id_type entity); | ||
32 | |||
33 | }; | ||
34 | |||
35 | #endif /* end of include guard: ORIENTING_H_099F0C23 */ | ||
diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp new file mode 100644 index 0000000..6652099 --- /dev/null +++ b/src/systems/playing.cpp | |||
@@ -0,0 +1,144 @@ | |||
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 "systems/orienting.h" | ||
11 | #include "systems/scheduling.h" | ||
12 | #include "systems/controlling.h" | ||
13 | #include "systems/animating.h" | ||
14 | #include "systems/realizing.h" | ||
15 | #include "animation.h" | ||
16 | #include "muxer.h" | ||
17 | |||
18 | void PlayingSystem::initPlayer() | ||
19 | { | ||
20 | id_type player = game_.getEntityManager().emplaceEntity(); | ||
21 | |||
22 | AnimationSet playerGraphics {"res/Starla.png", 10, 12, 6}; | ||
23 | playerGraphics.emplaceAnimation("stillLeft", 3, 1, 1); | ||
24 | playerGraphics.emplaceAnimation("stillRight", 0, 1, 1); | ||
25 | playerGraphics.emplaceAnimation("walkingLeft", 4, 2, 10); | ||
26 | playerGraphics.emplaceAnimation("walkingRight", 1, 2, 10); | ||
27 | |||
28 | game_.getEntityManager().emplaceComponent<AnimatableComponent>( | ||
29 | player, | ||
30 | std::move(playerGraphics)); | ||
31 | |||
32 | game_.getSystemManager().getSystem<AnimatingSystem>().startAnimation( | ||
33 | player, | ||
34 | "stillLeft"); | ||
35 | |||
36 | auto& realizing = game_.getSystemManager().getSystem<RealizingSystem>(); | ||
37 | |||
38 | auto& transformable = game_.getEntityManager(). | ||
39 | emplaceComponent<TransformableComponent>(player); | ||
40 | |||
41 | transformable.pos = realizing.getStartingPos(); | ||
42 | transformable.size.w() = 10; | ||
43 | transformable.size.h() = 12; | ||
44 | |||
45 | game_.getSystemManager().getSystem<PonderingSystem>().initializeBody( | ||
46 | player, | ||
47 | PonderableComponent::Type::freefalling); | ||
48 | |||
49 | game_.getEntityManager().emplaceComponent<ControllableComponent>(player); | ||
50 | game_.getEntityManager().emplaceComponent<OrientableComponent>(player); | ||
51 | |||
52 | auto& playable = game_.getEntityManager(). | ||
53 | emplaceComponent<PlayableComponent>(player); | ||
54 | |||
55 | playable.mapId = realizing.getActiveMap(); | ||
56 | playable.checkpointMapId = realizing.getStartingMapId(); | ||
57 | playable.checkpointPos = realizing.getStartingPos(); | ||
58 | |||
59 | realizing.enterActiveMap(player); | ||
60 | |||
61 | realizing.setActivePlayer(player); | ||
62 | } | ||
63 | |||
64 | void PlayingSystem::changeMap( | ||
65 | id_type player, | ||
66 | size_t mapId, | ||
67 | vec2d warpPos) | ||
68 | { | ||
69 | auto& playable = game_.getEntityManager(). | ||
70 | getComponent<PlayableComponent>(player); | ||
71 | |||
72 | auto& transformable = game_.getEntityManager(). | ||
73 | getComponent<TransformableComponent>(player); | ||
74 | |||
75 | auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>(); | ||
76 | auto& realizing = game_.getSystemManager().getSystem<RealizingSystem>(); | ||
77 | |||
78 | id_type newMapEntity = realizing.getEntityByMapId(mapId); | ||
79 | |||
80 | if (playable.mapId != newMapEntity) | ||
81 | { | ||
82 | if (playable.mapId == realizing.getActiveMap()) | ||
83 | { | ||
84 | realizing.leaveActiveMap(player); | ||
85 | } else if (newMapEntity == realizing.getActiveMap()) | ||
86 | { | ||
87 | realizing.enterActiveMap(player); | ||
88 | } | ||
89 | |||
90 | playable.mapId = newMapEntity; | ||
91 | } | ||
92 | |||
93 | pondering.unferry(player); | ||
94 | |||
95 | transformable.pos = warpPos; | ||
96 | |||
97 | if (realizing.getActivePlayer() == player) | ||
98 | { | ||
99 | realizing.loadMap(newMapEntity); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | void PlayingSystem::die(id_type player) | ||
104 | { | ||
105 | playSound("res/Hit_Hurt5.wav", 0.25); | ||
106 | |||
107 | auto& animatable = game_.getEntityManager(). | ||
108 | getComponent<AnimatableComponent>(player); | ||
109 | |||
110 | auto& ponderable = game_.getEntityManager(). | ||
111 | getComponent<PonderableComponent>(player); | ||
112 | |||
113 | auto& controlling = game_.getSystemManager().getSystem<ControllingSystem>(); | ||
114 | controlling.freeze(player); | ||
115 | |||
116 | animatable.frozen = true; | ||
117 | animatable.flickering = true; | ||
118 | ponderable.frozen = true; | ||
119 | ponderable.collidable = false; | ||
120 | |||
121 | auto& scheduling = game_.getSystemManager().getSystem<SchedulingSystem>(); | ||
122 | |||
123 | scheduling.schedule(player, 0.75, [&] (id_type player) { | ||
124 | auto& playable = game_.getEntityManager(). | ||
125 | getComponent<PlayableComponent>(player); | ||
126 | |||
127 | changeMap( | ||
128 | player, | ||
129 | playable.checkpointMapId, | ||
130 | playable.checkpointPos); | ||
131 | |||
132 | animatable.frozen = false; | ||
133 | animatable.flickering = false; | ||
134 | ponderable.frozen = false; | ||
135 | ponderable.collidable = true; | ||
136 | |||
137 | // Reset the walk state, and then potentially let the | ||
138 | // ControllingSystem set it again. | ||
139 | auto& orienting = game_.getSystemManager().getSystem<OrientingSystem>(); | ||
140 | orienting.stopWalking(player); | ||
141 | |||
142 | controlling.unfreeze(player); | ||
143 | }); | ||
144 | } | ||
diff --git a/src/systems/playing.h b/src/systems/playing.h new file mode 100644 index 0000000..31f79ab --- /dev/null +++ b/src/systems/playing.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef PLAYING_H_70A54F7D | ||
2 | #define PLAYING_H_70A54F7D | ||
3 | |||
4 | #include "system.h" | ||
5 | #include "vector.h" | ||
6 | |||
7 | class PlayingSystem : public System { | ||
8 | public: | ||
9 | |||
10 | PlayingSystem(Game& game) : System(game) | ||
11 | { | ||
12 | } | ||
13 | |||
14 | void initPlayer(); | ||
15 | |||
16 | void changeMap( | ||
17 | id_type player, | ||
18 | size_t mapId, | ||
19 | vec2d warpPos); | ||
20 | |||
21 | void die(id_type player); | ||
22 | |||
23 | }; | ||
24 | |||
25 | #endif /* end of include guard: PLAYING_H_70A54F7D */ | ||
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp new file mode 100644 index 0000000..d841679 --- /dev/null +++ b/src/systems/pondering.cpp | |||
@@ -0,0 +1,916 @@ | |||
1 | #include "pondering.h" | ||
2 | #include <queue> | ||
3 | #include <algorithm> | ||
4 | #include "game.h" | ||
5 | #include "components/ponderable.h" | ||
6 | #include "components/transformable.h" | ||
7 | #include "components/orientable.h" | ||
8 | #include "components/mappable.h" | ||
9 | #include "components/playable.h" | ||
10 | #include "systems/orienting.h" | ||
11 | #include "systems/playing.h" | ||
12 | #include "systems/realizing.h" | ||
13 | #include "systems/scripting.h" | ||
14 | #include "consts.h" | ||
15 | |||
16 | void PonderingSystem::tick(double dt) | ||
17 | { | ||
18 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
19 | PonderableComponent, | ||
20 | TransformableComponent>(); | ||
21 | |||
22 | for (id_type entity : entities) | ||
23 | { | ||
24 | auto& ponderable = game_.getEntityManager(). | ||
25 | getComponent<PonderableComponent>(entity); | ||
26 | |||
27 | // We will recursively process ferried bodies after their ferries have been | ||
28 | // processed, so hold off on processing ferried bodies at the top level. | ||
29 | if (ponderable.ferried) | ||
30 | { | ||
31 | continue; | ||
32 | } | ||
33 | |||
34 | tickBody(entity, dt); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | void PonderingSystem::initializeBody( | ||
39 | id_type entity, | ||
40 | PonderableComponent::Type type) | ||
41 | { | ||
42 | auto& ponderable = game_.getEntityManager(). | ||
43 | emplaceComponent<PonderableComponent>(entity, type); | ||
44 | |||
45 | if (type == PonderableComponent::Type::freefalling) | ||
46 | { | ||
47 | ponderable.accel.y() = NORMAL_GRAVITY; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | void PonderingSystem::initPrototype(id_type prototype) | ||
52 | { | ||
53 | auto& ponderable = game_.getEntityManager(). | ||
54 | getComponent<PonderableComponent>(prototype); | ||
55 | |||
56 | ponderable.vel.x() = 0.0; | ||
57 | ponderable.vel.y() = 0.0; | ||
58 | ponderable.accel.x() = 0.0; | ||
59 | ponderable.accel.y() = 0.0; | ||
60 | ponderable.grounded = false; | ||
61 | ponderable.frozen = false; | ||
62 | ponderable.collidable = true; | ||
63 | ponderable.ferried = false; | ||
64 | ponderable.passengers.clear(); | ||
65 | } | ||
66 | |||
67 | void PonderingSystem::unferry(id_type entity) | ||
68 | { | ||
69 | auto& ponderable = game_.getEntityManager(). | ||
70 | getComponent<PonderableComponent>(entity); | ||
71 | |||
72 | if (ponderable.ferried) | ||
73 | { | ||
74 | ponderable.ferried = false; | ||
75 | |||
76 | auto& ferryPonder = game_.getEntityManager(). | ||
77 | getComponent<PonderableComponent>(ponderable.ferry); | ||
78 | |||
79 | ferryPonder.passengers.erase(entity); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | void PonderingSystem::tickBody( | ||
84 | id_type entity, | ||
85 | double dt) | ||
86 | { | ||
87 | auto& ponderable = game_.getEntityManager(). | ||
88 | getComponent<PonderableComponent>(entity); | ||
89 | |||
90 | if (!ponderable.active) | ||
91 | { | ||
92 | return; | ||
93 | } | ||
94 | |||
95 | auto& transformable = game_.getEntityManager(). | ||
96 | getComponent<TransformableComponent>(entity); | ||
97 | |||
98 | // Accelerate | ||
99 | if (!ponderable.frozen) | ||
100 | { | ||
101 | ponderable.vel += ponderable.accel * dt; | ||
102 | |||
103 | if ((ponderable.type == PonderableComponent::Type::freefalling) | ||
104 | && (ponderable.vel.y() > TERMINAL_VELOCITY)) | ||
105 | { | ||
106 | ponderable.vel.y() = TERMINAL_VELOCITY; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | // Move | ||
111 | vec2d newPos = transformable.pos; | ||
112 | |||
113 | if (!ponderable.frozen) | ||
114 | { | ||
115 | newPos += ponderable.vel * dt; | ||
116 | } | ||
117 | |||
118 | CollisionResult result = moveBody(entity, newPos); | ||
119 | |||
120 | // Perform cleanup for orientable entites | ||
121 | bool groundedChanged = (ponderable.grounded != result.grounded); | ||
122 | ponderable.grounded = result.grounded; | ||
123 | |||
124 | if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) | ||
125 | { | ||
126 | auto& orientable = game_.getEntityManager(). | ||
127 | getComponent<OrientableComponent>(entity); | ||
128 | |||
129 | // Handle changes in groundedness | ||
130 | if (groundedChanged) | ||
131 | { | ||
132 | if (ponderable.grounded) | ||
133 | { | ||
134 | game_.getSystemManager().getSystem<OrientingSystem>().land(entity); | ||
135 | } else { | ||
136 | game_.getSystemManager(). | ||
137 | getSystem<OrientingSystem>().startFalling(entity); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | // Complete dropping, if necessary | ||
142 | if (orientable.getDropState() == OrientableComponent::DropState::active) | ||
143 | { | ||
144 | orientable.setDropState(OrientableComponent::DropState::none); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | // Ferry or unferry as necessary | ||
149 | if ((ponderable.type == PonderableComponent::Type::freefalling) && | ||
150 | groundedChanged) | ||
151 | { | ||
152 | if (ponderable.grounded && | ||
153 | game_.getEntityManager(). | ||
154 | hasComponent<PonderableComponent>(result.groundEntity)) | ||
155 | { | ||
156 | // The body is now being ferried | ||
157 | auto& ferryPonder = game_.getEntityManager(). | ||
158 | getComponent<PonderableComponent>(result.groundEntity); | ||
159 | |||
160 | ponderable.ferried = true; | ||
161 | ponderable.ferry = result.groundEntity; | ||
162 | ponderable.ferrySide = Direction::up; | ||
163 | |||
164 | ferryPonder.passengers.insert(entity); | ||
165 | } else if (ponderable.ferried) | ||
166 | { | ||
167 | // The body is no longer being ferried | ||
168 | unferry(entity); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | // Handle ferry passengers | ||
173 | std::set<id_type> passengers = ponderable.passengers; | ||
174 | |||
175 | for (id_type passenger : passengers) | ||
176 | { | ||
177 | tickBody(passenger, dt); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | PonderingSystem::CollisionResult PonderingSystem::moveBody( | ||
182 | id_type entity, | ||
183 | vec2d newPos) | ||
184 | { | ||
185 | auto& ponderable = game_.getEntityManager(). | ||
186 | getComponent<PonderableComponent>(entity); | ||
187 | |||
188 | CollisionResult result; | ||
189 | |||
190 | if (ponderable.collidable) | ||
191 | { | ||
192 | result = detectCollisions(entity, newPos); | ||
193 | } else { | ||
194 | result.pos = newPos; | ||
195 | } | ||
196 | |||
197 | if (!ponderable.frozen) | ||
198 | { | ||
199 | auto& transformable = game_.getEntityManager(). | ||
200 | getComponent<TransformableComponent>(entity); | ||
201 | |||
202 | vec2d delta = result.pos - transformable.pos; | ||
203 | |||
204 | // Move. | ||
205 | transformable.pos = result.pos; | ||
206 | |||
207 | // Stop if the entity hit a wall. | ||
208 | if (result.blockedHoriz) | ||
209 | { | ||
210 | ponderable.vel.x() = 0.0; | ||
211 | } | ||
212 | |||
213 | if (result.blockedVert) | ||
214 | { | ||
215 | ponderable.vel.y() = 0.0; | ||
216 | } | ||
217 | |||
218 | // Move ferry passengers by the appropriate amount. | ||
219 | auto passengers = ponderable.passengers; | ||
220 | |||
221 | for (id_type passenger : passengers) | ||
222 | { | ||
223 | auto& passTrans = game_.getEntityManager(). | ||
224 | getComponent<TransformableComponent>(passenger); | ||
225 | |||
226 | moveBody(passenger, passTrans.pos + delta); | ||
227 | } | ||
228 | |||
229 | // Move to an adjacent map, if necessary | ||
230 | if (result.adjacentlyWarping) | ||
231 | { | ||
232 | vec2d warpPos = result.pos; | ||
233 | |||
234 | switch (result.adjWarpDir) | ||
235 | { | ||
236 | case Direction::left: | ||
237 | { | ||
238 | warpPos.x() = GAME_WIDTH + WALL_GAP - transformable.size.w(); | ||
239 | |||
240 | break; | ||
241 | } | ||
242 | |||
243 | case Direction::right: | ||
244 | { | ||
245 | warpPos.x() = -WALL_GAP; | ||
246 | |||
247 | break; | ||
248 | } | ||
249 | |||
250 | case Direction::up: | ||
251 | { | ||
252 | warpPos.y() = MAP_HEIGHT * TILE_HEIGHT - transformable.size.h(); | ||
253 | |||
254 | break; | ||
255 | } | ||
256 | |||
257 | case Direction::down: | ||
258 | { | ||
259 | warpPos.y() = -WALL_GAP; | ||
260 | |||
261 | break; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | game_.getSystemManager().getSystem<PlayingSystem>(). | ||
266 | changeMap( | ||
267 | entity, | ||
268 | result.adjWarpMapId, | ||
269 | warpPos); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | return result; | ||
274 | } | ||
275 | |||
276 | namespace CollisionParams { | ||
277 | |||
278 | template <typename HorizVert> | ||
279 | class Desc : public HorizVert { | ||
280 | public: | ||
281 | |||
282 | inline static bool AtLeastInAxisSweep( | ||
283 | double boundaryAxis, | ||
284 | double entityAxis) | ||
285 | { | ||
286 | return (boundaryAxis >= entityAxis); | ||
287 | } | ||
288 | |||
289 | inline static bool IsPastAxis( | ||
290 | double colliderAxis, | ||
291 | double entityAxis) | ||
292 | { | ||
293 | return (colliderAxis > entityAxis); | ||
294 | } | ||
295 | |||
296 | inline static double EntityAxis(const vec2d& pos, const vec2i& size) | ||
297 | { | ||
298 | return HorizVert::AxisLower(pos); | ||
299 | } | ||
300 | |||
301 | inline static double ObjectAxis(const vec2d& pos, const vec2i& size) | ||
302 | { | ||
303 | return HorizVert::AxisUpper(pos, size); | ||
304 | } | ||
305 | |||
306 | inline static bool Closer(double left, double right) | ||
307 | { | ||
308 | return right < left; | ||
309 | } | ||
310 | }; | ||
311 | |||
312 | template <typename HorizVert> | ||
313 | class Asc : public HorizVert { | ||
314 | public: | ||
315 | |||
316 | inline static bool AtLeastInAxisSweep( | ||
317 | double boundaryAxis, | ||
318 | double entityAxis) | ||
319 | { | ||
320 | return (boundaryAxis <= entityAxis); | ||
321 | } | ||
322 | |||
323 | inline static bool IsPastAxis( | ||
324 | double colliderAxis, | ||
325 | double entityAxis) | ||
326 | { | ||
327 | return (colliderAxis < entityAxis); | ||
328 | } | ||
329 | |||
330 | inline static double EntityAxis(const vec2d& pos, const vec2i& size) | ||
331 | { | ||
332 | return HorizVert::AxisUpper(pos, size); | ||
333 | } | ||
334 | |||
335 | inline static double ObjectAxis(const vec2d& pos, const vec2i& size) | ||
336 | { | ||
337 | return HorizVert::AxisLower(pos); | ||
338 | } | ||
339 | |||
340 | inline static bool Closer(double left, double right) | ||
341 | { | ||
342 | return left < right; | ||
343 | } | ||
344 | }; | ||
345 | |||
346 | template <size_t Axis, size_t NonAxis> | ||
347 | class HorizVert { | ||
348 | public: | ||
349 | |||
350 | inline static double AxisLower(const vec2d& pos) | ||
351 | { | ||
352 | return pos.coords[Axis]; | ||
353 | } | ||
354 | |||
355 | inline static double AxisUpper(const vec2d& pos, const vec2i& size) | ||
356 | { | ||
357 | return pos.coords[Axis] + size.coords[Axis]; | ||
358 | } | ||
359 | |||
360 | inline static double NonAxisLower(const vec2d& pos) | ||
361 | { | ||
362 | return pos.coords[NonAxis]; | ||
363 | } | ||
364 | |||
365 | inline static double NonAxisUpper(const vec2d& pos, const vec2i& size) | ||
366 | { | ||
367 | return pos.coords[NonAxis] + size.coords[NonAxis]; | ||
368 | } | ||
369 | |||
370 | }; | ||
371 | |||
372 | using Horizontal = HorizVert<0, 1>; | ||
373 | using Vertical = HorizVert<1, 0>; | ||
374 | |||
375 | template <Direction dir, typename AscDesc> | ||
376 | class DetectCollisions : public AscDesc { | ||
377 | public: | ||
378 | |||
379 | static const Direction Dir = dir; | ||
380 | |||
381 | inline static double EntityAxis(const vec2d& pos, const vec2i& size) | ||
382 | { | ||
383 | return AscDesc::EntityAxis(pos, size); | ||
384 | } | ||
385 | |||
386 | inline static double ObjectAxis(const vec2d& pos, const vec2i& size) | ||
387 | { | ||
388 | return AscDesc::ObjectAxis(pos, size); | ||
389 | } | ||
390 | |||
391 | inline static double EntityAxis(const TransformableComponent& transformable) | ||
392 | { | ||
393 | return AscDesc::EntityAxis(transformable.pos, transformable.size); | ||
394 | } | ||
395 | |||
396 | inline static double ObjectAxis(const TransformableComponent& transformable) | ||
397 | { | ||
398 | return AscDesc::ObjectAxis(transformable.pos, transformable.size); | ||
399 | } | ||
400 | }; | ||
401 | |||
402 | class Left : public DetectCollisions<Direction::left, Desc<Horizontal>> { | ||
403 | public: | ||
404 | |||
405 | inline static const MappableComponent::desc_boundaries_type& MapBoundaries( | ||
406 | const MappableComponent& mappable) | ||
407 | { | ||
408 | return mappable.leftBoundaries; | ||
409 | } | ||
410 | }; | ||
411 | |||
412 | class Right : public DetectCollisions<Direction::right, Asc<Horizontal>> { | ||
413 | public: | ||
414 | |||
415 | inline static const MappableComponent::asc_boundaries_type& MapBoundaries( | ||
416 | const MappableComponent& mappable) | ||
417 | { | ||
418 | return mappable.rightBoundaries; | ||
419 | } | ||
420 | }; | ||
421 | |||
422 | class Up : public DetectCollisions<Direction::up, Desc<Vertical>> { | ||
423 | public: | ||
424 | |||
425 | inline static const MappableComponent::desc_boundaries_type& MapBoundaries( | ||
426 | const MappableComponent& mappable) | ||
427 | { | ||
428 | return mappable.upBoundaries; | ||
429 | } | ||
430 | }; | ||
431 | |||
432 | class Down : public DetectCollisions<Direction::down, Asc<Vertical>> { | ||
433 | public: | ||
434 | |||
435 | inline static const MappableComponent::asc_boundaries_type& MapBoundaries( | ||
436 | const MappableComponent& mappable) | ||
437 | { | ||
438 | return mappable.downBoundaries; | ||
439 | } | ||
440 | }; | ||
441 | }; | ||
442 | |||
443 | PonderingSystem::CollisionResult PonderingSystem::detectCollisions( | ||
444 | id_type entity, | ||
445 | vec2d newPos) | ||
446 | { | ||
447 | auto& transformable = game_.getEntityManager(). | ||
448 | getComponent<TransformableComponent>(entity); | ||
449 | |||
450 | CollisionResult result; | ||
451 | result.pos.x() = newPos.x(); | ||
452 | result.pos.y() = transformable.pos.y(); | ||
453 | |||
454 | // Find horizontal collisions. | ||
455 | if (result.pos.x() < transformable.pos.x()) | ||
456 | { | ||
457 | detectCollisionsInDirection<CollisionParams::Left>(entity, result); | ||
458 | } else if (result.pos.x() > transformable.pos.x()) | ||
459 | { | ||
460 | detectCollisionsInDirection<CollisionParams::Right>(entity, result); | ||
461 | } | ||
462 | |||
463 | // Find vertical collisions | ||
464 | if (!result.stopProcessing) | ||
465 | { | ||
466 | result.pos.y() = newPos.y(); | ||
467 | result.touchedWall = false; | ||
468 | |||
469 | if (result.pos.y() < transformable.pos.y()) | ||
470 | { | ||
471 | detectCollisionsInDirection<CollisionParams::Up>(entity, result); | ||
472 | } else if (result.pos.y() > transformable.pos.y()) | ||
473 | { | ||
474 | detectCollisionsInDirection<CollisionParams::Down>(entity, result); | ||
475 | } | ||
476 | } | ||
477 | |||
478 | return result; | ||
479 | } | ||
480 | |||
481 | template <typename Param> | ||
482 | void PonderingSystem::detectCollisionsInDirection( | ||
483 | id_type entity, | ||
484 | CollisionResult& result) | ||
485 | { | ||
486 | // Get map data. | ||
487 | id_type mapEntity = | ||
488 | game_.getSystemManager().getSystem<RealizingSystem>().getActiveMap(); | ||
489 | |||
490 | auto& mappable = game_.getEntityManager(). | ||
491 | getComponent<MappableComponent>(mapEntity); | ||
492 | |||
493 | // Get old location. | ||
494 | auto& transform = game_.getEntityManager(). | ||
495 | getComponent<TransformableComponent>(entity); | ||
496 | |||
497 | auto& ponderable = game_.getEntityManager(). | ||
498 | getComponent<PonderableComponent>(entity); | ||
499 | |||
500 | bool boundaryCollision = false; | ||
501 | |||
502 | auto boundaries = Param::MapBoundaries(mappable); | ||
503 | auto it = boundaries.lower_bound(Param::EntityAxis(transform)); | ||
504 | |||
505 | // Find the axis distance of the closest environmental boundary. | ||
506 | for (; | ||
507 | (it != std::end(boundaries)) && | ||
508 | Param::AtLeastInAxisSweep( | ||
509 | it->first, | ||
510 | Param::EntityAxis(result.pos, transform.size)); | ||
511 | it++) | ||
512 | { | ||
513 | // Check that the boundary is in range for the other axis. | ||
514 | if ((Param::NonAxisUpper(result.pos, transform.size) > it->second.lower) && | ||
515 | (Param::NonAxisLower(result.pos) < it->second.upper)) | ||
516 | { | ||
517 | // We have a collision! | ||
518 | boundaryCollision = true; | ||
519 | |||
520 | break; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | // Find the results of pretending to move the entity's passengers, if there | ||
525 | // are any. | ||
526 | vec2d delta = result.pos - transform.pos; | ||
527 | std::map<id_type, CollisionResult> passResults; | ||
528 | |||
529 | for (id_type passenger : ponderable.passengers) | ||
530 | { | ||
531 | auto& passPonder = game_.getEntityManager(). | ||
532 | getComponent<PonderableComponent>(passenger); | ||
533 | |||
534 | if (passPonder.ferrySide == Param::Dir) | ||
535 | { | ||
536 | auto& passTrans = game_.getEntityManager(). | ||
537 | getComponent<TransformableComponent>(passenger); | ||
538 | |||
539 | passResults[passenger] = | ||
540 | detectCollisions(passenger, passTrans.pos + delta); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | // Find a list of potential colliders, sorted so that the closest is | ||
545 | // first. | ||
546 | std::vector<id_type> colliders; | ||
547 | |||
548 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
549 | PonderableComponent, | ||
550 | TransformableComponent>(); | ||
551 | |||
552 | for (id_type collider : entities) | ||
553 | { | ||
554 | // Can't collide with self. | ||
555 | if (collider == entity) | ||
556 | { | ||
557 | continue; | ||
558 | } | ||
559 | |||
560 | auto& colliderPonder = game_.getEntityManager(). | ||
561 | getComponent<PonderableComponent>(collider); | ||
562 | |||
563 | // Only check objects that are active and collidable. | ||
564 | if (!colliderPonder.active || !colliderPonder.collidable) | ||
565 | { | ||
566 | continue; | ||
567 | } | ||
568 | |||
569 | // If the collider is a passenger of the entity, pretend that it has already | ||
570 | // moved. | ||
571 | auto& colliderTrans = game_.getEntityManager(). | ||
572 | getComponent<TransformableComponent>(collider); | ||
573 | |||
574 | vec2d colliderPos = colliderTrans.pos; | ||
575 | vec2i colliderSize = colliderTrans.size; | ||
576 | |||
577 | if (passResults.count(collider)) | ||
578 | { | ||
579 | colliderPos = passResults[collider].pos; | ||
580 | } | ||
581 | |||
582 | // Check if the entity would move into the potential collider, | ||
583 | if (Param::IsPastAxis( | ||
584 | Param::ObjectAxis(colliderPos, colliderSize), | ||
585 | Param::EntityAxis(result.pos, transform.size)) && | ||
586 | // that it wasn't already colliding, | ||
587 | !Param::IsPastAxis( | ||
588 | Param::ObjectAxis(colliderPos, colliderSize), | ||
589 | Param::EntityAxis(transform)) && | ||
590 | // that the position on the non-axis is in range, | ||
591 | (Param::NonAxisUpper(colliderPos, colliderSize) > | ||
592 | Param::NonAxisLower(result.pos)) && | ||
593 | (Param::NonAxisLower(colliderPos) < | ||
594 | Param::NonAxisUpper(result.pos, transform.size)) && | ||
595 | // and that the collider is not farther away than the environmental | ||
596 | // boundary. | ||
597 | (!boundaryCollision || | ||
598 | Param::AtLeastInAxisSweep( | ||
599 | Param::ObjectAxis(colliderPos, colliderSize), | ||
600 | it->first))) | ||
601 | { | ||
602 | colliders.push_back(collider); | ||
603 | } | ||
604 | } | ||
605 | |||
606 | // Sort the potential colliders such that the closest to the axis of movement | ||
607 | // is first. When sorting, treat passengers of the entity as having already | ||
608 | // moved. | ||
609 | std::sort( | ||
610 | std::begin(colliders), | ||
611 | std::end(colliders), | ||
612 | [&] (id_type left, id_type right) { | ||
613 | auto& leftTrans = game_.getEntityManager(). | ||
614 | getComponent<TransformableComponent>(left); | ||
615 | |||
616 | vec2d leftPos = leftTrans.pos; | ||
617 | |||
618 | if (passResults.count(left)) | ||
619 | { | ||
620 | leftPos = passResults[left].pos; | ||
621 | } | ||
622 | |||
623 | auto& rightTrans = game_.getEntityManager(). | ||
624 | getComponent<TransformableComponent>(right); | ||
625 | |||
626 | vec2d rightPos = rightTrans.pos; | ||
627 | |||
628 | if (passResults.count(right)) | ||
629 | { | ||
630 | rightPos = passResults[right].pos; | ||
631 | } | ||
632 | |||
633 | return Param::Closer( | ||
634 | Param::ObjectAxis(leftPos, leftTrans.size), | ||
635 | Param::ObjectAxis(rightPos, rightTrans.size)); | ||
636 | }); | ||
637 | |||
638 | for (id_type collider : colliders) | ||
639 | { | ||
640 | auto& colliderTrans = game_.getEntityManager(). | ||
641 | getComponent<TransformableComponent>(collider); | ||
642 | |||
643 | // If the collider is a passenger of the entity, pretend that it has already | ||
644 | // moved. | ||
645 | vec2d colliderPos = colliderTrans.pos; | ||
646 | vec2i colliderSize = colliderTrans.size; | ||
647 | |||
648 | if (passResults.count(collider)) | ||
649 | { | ||
650 | colliderPos = passResults[collider].pos; | ||
651 | } | ||
652 | |||
653 | // Check if the entity would still move into the potential collider. | ||
654 | if (!Param::IsPastAxis( | ||
655 | Param::ObjectAxis(colliderPos, colliderSize), | ||
656 | Param::EntityAxis(result.pos, transform.size))) | ||
657 | { | ||
658 | break; | ||
659 | } | ||
660 | |||
661 | // TODO: Check if the entity is moving into one of its passengers. | ||
662 | auto& colliderPonder = game_.getEntityManager(). | ||
663 | getComponent<PonderableComponent>(collider); | ||
664 | |||
665 | processCollision( | ||
666 | entity, | ||
667 | collider, | ||
668 | Param::Dir, | ||
669 | colliderPonder.colliderType, | ||
670 | Param::ObjectAxis(colliderPos, colliderSize), | ||
671 | Param::NonAxisLower(colliderPos), | ||
672 | Param::NonAxisUpper(colliderPos, colliderSize), | ||
673 | result); | ||
674 | |||
675 | if (result.stopProcessing) | ||
676 | { | ||
677 | break; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | // If movement hasn't been stopped by an intermediary object, and | ||
682 | // collision checking hasn't been stopped, process the environmental | ||
683 | // boundaries closest to the entity. | ||
684 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
685 | { | ||
686 | double boundaryAxis = it->first; | ||
687 | |||
688 | for (; | ||
689 | (it != std::end(boundaries)) && | ||
690 | (it->first == boundaryAxis); | ||
691 | it++) | ||
692 | { | ||
693 | if ((Param::NonAxisLower(result.pos) < it->second.upper) && | ||
694 | (Param::NonAxisUpper(result.pos, transform.size) > it->second.lower)) | ||
695 | { | ||
696 | processCollision( | ||
697 | entity, | ||
698 | mapEntity, | ||
699 | Param::Dir, | ||
700 | it->second.type, | ||
701 | it->first, | ||
702 | it->second.lower, | ||
703 | it->second.upper, | ||
704 | result); | ||
705 | |||
706 | if (result.stopProcessing) | ||
707 | { | ||
708 | break; | ||
709 | } | ||
710 | } | ||
711 | } | ||
712 | } | ||
713 | } | ||
714 | |||
715 | void PonderingSystem::processCollision( | ||
716 | id_type entity, | ||
717 | id_type collider, | ||
718 | Direction dir, | ||
719 | PonderableComponent::Collision type, | ||
720 | double axis, | ||
721 | double lower, | ||
722 | double upper, | ||
723 | CollisionResult& result) | ||
724 | { | ||
725 | auto& transformable = game_.getEntityManager(). | ||
726 | getComponent<TransformableComponent>(entity); | ||
727 | |||
728 | switch (type) | ||
729 | { | ||
730 | case PonderableComponent::Collision::wall: | ||
731 | { | ||
732 | result.touchedWall = true; | ||
733 | |||
734 | break; | ||
735 | } | ||
736 | |||
737 | case PonderableComponent::Collision::platform: | ||
738 | { | ||
739 | if (game_.getEntityManager(). | ||
740 | hasComponent<OrientableComponent>(entity)) | ||
741 | { | ||
742 | auto& orientable = game_.getEntityManager(). | ||
743 | getComponent<OrientableComponent>(entity); | ||
744 | |||
745 | if (orientable.getDropState() != | ||
746 | OrientableComponent::DropState::none) | ||
747 | { | ||
748 | orientable.setDropState(OrientableComponent::DropState::active); | ||
749 | } else { | ||
750 | result.touchedWall = true; | ||
751 | } | ||
752 | } else { | ||
753 | result.touchedWall = true; | ||
754 | } | ||
755 | |||
756 | break; | ||
757 | } | ||
758 | |||
759 | case PonderableComponent::Collision::adjacency: | ||
760 | { | ||
761 | auto& mappable = game_.getEntityManager(). | ||
762 | getComponent<MappableComponent>(collider); | ||
763 | |||
764 | auto& adj = [&] () -> const MappableComponent::Adjacent& { | ||
765 | switch (dir) | ||
766 | { | ||
767 | case Direction::left: return mappable.leftAdjacent; | ||
768 | case Direction::right: return mappable.rightAdjacent; | ||
769 | case Direction::up: return mappable.upAdjacent; | ||
770 | case Direction::down: return mappable.downAdjacent; | ||
771 | } | ||
772 | }(); | ||
773 | |||
774 | switch (adj.type) | ||
775 | { | ||
776 | case MappableComponent::Adjacent::Type::wall: | ||
777 | { | ||
778 | result.touchedWall = true; | ||
779 | |||
780 | break; | ||
781 | } | ||
782 | |||
783 | case MappableComponent::Adjacent::Type::wrap: | ||
784 | { | ||
785 | switch (dir) | ||
786 | { | ||
787 | case Direction::left: | ||
788 | { | ||
789 | result.pos.x() = GAME_WIDTH + WALL_GAP - transformable.size.w(); | ||
790 | |||
791 | break; | ||
792 | } | ||
793 | |||
794 | case Direction::right: | ||
795 | { | ||
796 | result.pos.x() = -WALL_GAP; | ||
797 | |||
798 | break; | ||
799 | } | ||
800 | |||
801 | case Direction::up: | ||
802 | { | ||
803 | result.pos.y() = | ||
804 | MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.pos.h(); | ||
805 | |||
806 | break; | ||
807 | } | ||
808 | |||
809 | case Direction::down: | ||
810 | { | ||
811 | result.pos.y() = -WALL_GAP; | ||
812 | |||
813 | break; | ||
814 | } | ||
815 | } | ||
816 | } | ||
817 | |||
818 | case MappableComponent::Adjacent::Type::warp: | ||
819 | { | ||
820 | if (game_.getEntityManager(). | ||
821 | hasComponent<PlayableComponent>(entity)) | ||
822 | { | ||
823 | result.adjacentlyWarping = true; | ||
824 | result.adjWarpDir = dir; | ||
825 | result.adjWarpMapId = adj.mapId; | ||
826 | } | ||
827 | |||
828 | break; | ||
829 | } | ||
830 | |||
831 | case MappableComponent::Adjacent::Type::reverse: | ||
832 | { | ||
833 | // TODO: not yet implemented. | ||
834 | |||
835 | break; | ||
836 | } | ||
837 | } | ||
838 | |||
839 | break; | ||
840 | } | ||
841 | |||
842 | case PonderableComponent::Collision::danger: | ||
843 | { | ||
844 | if (game_.getEntityManager(). | ||
845 | hasComponent<PlayableComponent>(entity)) | ||
846 | { | ||
847 | game_.getSystemManager().getSystem<PlayingSystem>().die(entity); | ||
848 | |||
849 | result.adjacentlyWarping = false; | ||
850 | } | ||
851 | |||
852 | result.stopProcessing = true; | ||
853 | |||
854 | break; | ||
855 | } | ||
856 | |||
857 | case PonderableComponent::Collision::event: | ||
858 | { | ||
859 | if (game_.getEntityManager(). | ||
860 | hasComponent<PlayableComponent>(entity)) | ||
861 | { | ||
862 | game_.getSystemManager().getSystem<ScriptingSystem>(). | ||
863 | onTouch(collider, entity); | ||
864 | } | ||
865 | |||
866 | break; | ||
867 | } | ||
868 | |||
869 | default: | ||
870 | { | ||
871 | // Not yet implemented. | ||
872 | |||
873 | break; | ||
874 | } | ||
875 | } | ||
876 | |||
877 | if (!result.stopProcessing && result.touchedWall) | ||
878 | { | ||
879 | switch (dir) | ||
880 | { | ||
881 | case Direction::left: | ||
882 | { | ||
883 | result.pos.x() = axis; | ||
884 | result.blockedHoriz = true; | ||
885 | |||
886 | break; | ||
887 | } | ||
888 | |||
889 | case Direction::right: | ||
890 | { | ||
891 | result.pos.x() = axis - transformable.size.w(); | ||
892 | result.blockedHoriz = true; | ||
893 | |||
894 | break; | ||
895 | } | ||
896 | |||
897 | case Direction::up: | ||
898 | { | ||
899 | result.pos.y() = axis; | ||
900 | result.blockedVert = true; | ||
901 | |||
902 | break; | ||
903 | } | ||
904 | |||
905 | case Direction::down: | ||
906 | { | ||
907 | result.pos.y() = axis - transformable.size.h(); | ||
908 | result.blockedVert = true; | ||
909 | result.groundEntity = collider; | ||
910 | result.grounded = true; | ||
911 | |||
912 | break; | ||
913 | } | ||
914 | } | ||
915 | } | ||
916 | } | ||
diff --git a/src/systems/pondering.h b/src/systems/pondering.h new file mode 100644 index 0000000..c79bbf6 --- /dev/null +++ b/src/systems/pondering.h | |||
@@ -0,0 +1,81 @@ | |||
1 | #ifndef PONDERING_H_F2530E0E | ||
2 | #define PONDERING_H_F2530E0E | ||
3 | |||
4 | #include "system.h" | ||
5 | #include "components/ponderable.h" | ||
6 | #include "direction.h" | ||
7 | #include "vector.h" | ||
8 | |||
9 | class PonderingSystem : public System { | ||
10 | public: | ||
11 | |||
12 | PonderingSystem(Game& game) : System(game) | ||
13 | { | ||
14 | } | ||
15 | |||
16 | void tick(double dt); | ||
17 | |||
18 | void initializeBody(id_type entity, PonderableComponent::Type type); | ||
19 | |||
20 | /** | ||
21 | * Initializes a ponderable map object from its prototype data. | ||
22 | * | ||
23 | * @requires entity is ponderable | ||
24 | * @requires entity is a map object | ||
25 | */ | ||
26 | void initPrototype(id_type prototype); | ||
27 | |||
28 | /** | ||
29 | * Unferries an entity if it is a passenger of another entity. Use before | ||
30 | * moving a ponderable entity outside the PonderingSystem. | ||
31 | * | ||
32 | * @requires entity is ponderable | ||
33 | */ | ||
34 | void unferry(id_type entity); | ||
35 | |||
36 | private: | ||
37 | |||
38 | struct CollisionResult | ||
39 | { | ||
40 | vec2d pos; | ||
41 | bool stopProcessing = false; | ||
42 | bool touchedWall = false; | ||
43 | bool blockedHoriz = false; | ||
44 | bool blockedVert = false; | ||
45 | bool adjacentlyWarping = false; | ||
46 | Direction adjWarpDir; | ||
47 | size_t adjWarpMapId; | ||
48 | bool grounded = false; | ||
49 | id_type groundEntity; | ||
50 | }; | ||
51 | |||
52 | void tickBody( | ||
53 | id_type entity, | ||
54 | double dt); | ||
55 | |||
56 | CollisionResult moveBody( | ||
57 | id_type entity, | ||
58 | vec2d newPos); | ||
59 | |||
60 | CollisionResult detectCollisions( | ||
61 | id_type entity, | ||
62 | vec2d newPos); | ||
63 | |||
64 | template <typename Param> | ||
65 | void detectCollisionsInDirection( | ||
66 | id_type entity, | ||
67 | CollisionResult& result); | ||
68 | |||
69 | void processCollision( | ||
70 | id_type entity, | ||
71 | id_type collider, | ||
72 | Direction dir, | ||
73 | PonderableComponent::Collision type, | ||
74 | double axis, | ||
75 | double lower, | ||
76 | double upper, | ||
77 | CollisionResult& result); | ||
78 | |||
79 | }; | ||
80 | |||
81 | #endif /* end of include guard: PONDERING_H_F2530E0E */ | ||
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp new file mode 100644 index 0000000..baacf5a --- /dev/null +++ b/src/systems/realizing.cpp | |||
@@ -0,0 +1,438 @@ | |||
1 | #include "realizing.h" | ||
2 | #include <stdexcept> | ||
3 | #include <libxml/parser.h> | ||
4 | #include <cstring> | ||
5 | #include <map> | ||
6 | #include "game.h" | ||
7 | #include "consts.h" | ||
8 | #include "animation.h" | ||
9 | #include "components/mappable.h" | ||
10 | #include "components/animatable.h" | ||
11 | #include "components/playable.h" | ||
12 | #include "components/ponderable.h" | ||
13 | #include "components/transformable.h" | ||
14 | #include "components/prototypable.h" | ||
15 | #include "components/automatable.h" | ||
16 | #include "systems/mapping.h" | ||
17 | #include "systems/animating.h" | ||
18 | #include "systems/pondering.h" | ||
19 | #include "systems/scripting.h" | ||
20 | |||
21 | inline xmlChar* getProp(xmlNodePtr node, const char* attr) | ||
22 | { | ||
23 | xmlChar* key = xmlGetProp(node, reinterpret_cast<const xmlChar*>(attr)); | ||
24 | if (key == nullptr) | ||
25 | { | ||
26 | throw std::invalid_argument("Error parsing world file"); | ||
27 | } | ||
28 | |||
29 | return key; | ||
30 | } | ||
31 | |||
32 | // TODO: neither the XML doc nor any of the emplaced entities are properly | ||
33 | // destroyed if this constructor throws an exception. | ||
34 | RealizingSystem::RealizingSystem( | ||
35 | Game& game, | ||
36 | std::string worldFile, | ||
37 | std::string prototypeFile) : | ||
38 | System(game), | ||
39 | worldFile_(std::move(worldFile)), | ||
40 | prototypeFile_(std::move(prototypeFile)) | ||
41 | { | ||
42 | auto& mapping = game_.getSystemManager().getSystem<MappingSystem>(); | ||
43 | |||
44 | xmlChar* key = nullptr; | ||
45 | |||
46 | // Create a mapping between prototype names and the XML trees defining them. | ||
47 | xmlDocPtr protoXml = xmlParseFile(prototypeFile_.c_str()); | ||
48 | if (protoXml == nullptr) | ||
49 | { | ||
50 | throw std::invalid_argument("Cannot find prototypes file"); | ||
51 | } | ||
52 | |||
53 | xmlNodePtr protoTop = xmlDocGetRootElement(protoXml); | ||
54 | if (protoTop == nullptr) | ||
55 | { | ||
56 | throw std::invalid_argument("Error parsing prototypes file"); | ||
57 | } | ||
58 | |||
59 | if (xmlStrcmp(protoTop->name, reinterpret_cast<const xmlChar*>("entities"))) | ||
60 | { | ||
61 | throw std::invalid_argument("Error parsing prototypes file"); | ||
62 | } | ||
63 | |||
64 | std::map<std::string, xmlNodePtr> prototypes; | ||
65 | |||
66 | for (xmlNodePtr node = protoTop->xmlChildrenNode; | ||
67 | node != nullptr; | ||
68 | node = node->next) | ||
69 | { | ||
70 | if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("entity"))) | ||
71 | { | ||
72 | key = getProp(node, "id"); | ||
73 | std::string prototypeId = reinterpret_cast<char*>(key); | ||
74 | xmlFree(key); | ||
75 | |||
76 | prototypes[prototypeId] = node; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | // Create entities from the world definition. | ||
81 | xmlDocPtr doc = xmlParseFile(worldFile_.c_str()); | ||
82 | if (doc == nullptr) | ||
83 | { | ||
84 | throw std::invalid_argument("Cannot find world file"); | ||
85 | } | ||
86 | |||
87 | xmlNodePtr top = xmlDocGetRootElement(doc); | ||
88 | if (top == nullptr) | ||
89 | { | ||
90 | throw std::invalid_argument("Error parsing world file"); | ||
91 | } | ||
92 | |||
93 | if (xmlStrcmp(top->name, reinterpret_cast<const xmlChar*>("world"))) | ||
94 | { | ||
95 | throw std::invalid_argument("Error parsing world file"); | ||
96 | } | ||
97 | |||
98 | key = getProp(top, "startx"); | ||
99 | startingPos_.x() = atoi(reinterpret_cast<char*>(key)); | ||
100 | xmlFree(key); | ||
101 | |||
102 | key = getProp(top, "starty"); | ||
103 | startingPos_.y() = atoi(reinterpret_cast<char*>(key)); | ||
104 | xmlFree(key); | ||
105 | |||
106 | key = getProp(top, "startmap"); | ||
107 | startingMapId_ = atoi(reinterpret_cast<char*>(key)); | ||
108 | xmlFree(key); | ||
109 | |||
110 | for (xmlNodePtr node = top->xmlChildrenNode; | ||
111 | node != nullptr; | ||
112 | node = node->next) | ||
113 | { | ||
114 | if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("map"))) | ||
115 | { | ||
116 | id_type map = game_.getEntityManager().emplaceEntity(); | ||
117 | |||
118 | auto& mappable = game_.getEntityManager(). | ||
119 | emplaceComponent<MappableComponent>(map, | ||
120 | Texture("res/tiles.png"), | ||
121 | Texture("res/font.bmp")); | ||
122 | |||
123 | key = getProp(node, "id"); | ||
124 | mappable.mapId = atoi(reinterpret_cast<char*>(key)); | ||
125 | xmlFree(key); | ||
126 | |||
127 | key = getProp(node, "title"); | ||
128 | mappable.title = reinterpret_cast<char*>(key); | ||
129 | xmlFree(key); | ||
130 | |||
131 | for (xmlNodePtr mapNode = node->xmlChildrenNode; | ||
132 | mapNode != nullptr; | ||
133 | mapNode = mapNode->next) | ||
134 | { | ||
135 | if (!xmlStrcmp( | ||
136 | mapNode->name, | ||
137 | reinterpret_cast<const xmlChar*>("environment"))) | ||
138 | { | ||
139 | key = xmlNodeGetContent(mapNode); | ||
140 | if (key == nullptr) | ||
141 | { | ||
142 | throw std::invalid_argument("Error parsing world file"); | ||
143 | } | ||
144 | |||
145 | mappable.tiles.clear(); | ||
146 | mappable.tiles.push_back(atoi(strtok( | ||
147 | reinterpret_cast<char*>(key), | ||
148 | ",\n"))); | ||
149 | |||
150 | for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++) | ||
151 | { | ||
152 | mappable.tiles.push_back(atoi(strtok(nullptr, ",\n"))); | ||
153 | } | ||
154 | |||
155 | xmlFree(key); | ||
156 | } else if (!xmlStrcmp( | ||
157 | mapNode->name, | ||
158 | reinterpret_cast<const xmlChar*>("entity"))) | ||
159 | { | ||
160 | id_type mapObject = game_.getEntityManager().emplaceEntity(); | ||
161 | |||
162 | key = getProp(mapNode, "type"); | ||
163 | std::string prototypeId = reinterpret_cast<char*>(key); | ||
164 | xmlFree(key); | ||
165 | |||
166 | xmlNodePtr prototypeNode = prototypes[prototypeId]; | ||
167 | |||
168 | // Set the coordinates from the object definition. | ||
169 | auto& transformable = game_.getEntityManager(). | ||
170 | emplaceComponent<TransformableComponent>(mapObject); | ||
171 | |||
172 | key = getProp(mapNode, "x"); | ||
173 | transformable.origPos.x() = atoi(reinterpret_cast<char*>(key)); | ||
174 | xmlFree(key); | ||
175 | |||
176 | key = getProp(mapNode, "y"); | ||
177 | transformable.origPos.y() = atoi(reinterpret_cast<char*>(key)); | ||
178 | xmlFree(key); | ||
179 | |||
180 | // Set the sprite and size using the prototype definition. | ||
181 | key = getProp(prototypeNode, "sprite"); | ||
182 | std::string spritePath = reinterpret_cast<char*>(key); | ||
183 | xmlFree(key); | ||
184 | |||
185 | key = getProp(prototypeNode, "width"); | ||
186 | transformable.origSize.w() = atoi(reinterpret_cast<char*>(key)); | ||
187 | xmlFree(key); | ||
188 | |||
189 | key = getProp(prototypeNode, "height"); | ||
190 | transformable.origSize.h() = atoi(reinterpret_cast<char*>(key)); | ||
191 | xmlFree(key); | ||
192 | |||
193 | AnimationSet objectAnim( | ||
194 | spritePath.c_str(), | ||
195 | transformable.origSize.w(), | ||
196 | transformable.origSize.h(), | ||
197 | 1); | ||
198 | |||
199 | objectAnim.emplaceAnimation("static", 0, 1, 1); | ||
200 | |||
201 | auto& animatable = game_.getEntityManager(). | ||
202 | emplaceComponent<AnimatableComponent>( | ||
203 | mapObject, | ||
204 | std::move(objectAnim)); | ||
205 | |||
206 | animatable.origAnimation = "static"; | ||
207 | |||
208 | // Create a physics body. | ||
209 | game_.getSystemManager().getSystem<PonderingSystem>(). | ||
210 | initializeBody(mapObject, PonderableComponent::Type::vacuumed); | ||
211 | |||
212 | |||
213 | |||
214 | |||
215 | |||
216 | auto& prototypable = game_.getEntityManager(). | ||
217 | emplaceComponent<PrototypableComponent>(mapObject); | ||
218 | |||
219 | prototypable.prototypeId = prototypeId; | ||
220 | |||
221 | key = getProp(mapNode, "index"); | ||
222 | prototypable.mapObjectIndex = atoi(reinterpret_cast<char*>(key)); | ||
223 | xmlFree(key); | ||
224 | |||
225 | if (prototypeId == "movplat") | ||
226 | { | ||
227 | auto& automatable = game_.getEntityManager(). | ||
228 | emplaceComponent<AutomatableComponent>(mapObject); | ||
229 | |||
230 | automatable.table = prototypeId; | ||
231 | } else if (prototypeId == "checkpoint") | ||
232 | { | ||
233 | auto& ponderable = game_.getEntityManager(). | ||
234 | getComponent<PonderableComponent>(mapObject); | ||
235 | |||
236 | ponderable.colliderType = PonderableComponent::Collision::event; | ||
237 | } | ||
238 | |||
239 | mappable.objects.push_back(mapObject); | ||
240 | } else if (!xmlStrcmp( | ||
241 | mapNode->name, | ||
242 | reinterpret_cast<const xmlChar*>("adjacent"))) | ||
243 | { | ||
244 | key = getProp(mapNode, "type"); | ||
245 | std::string adjTypeStr(reinterpret_cast<char*>(key)); | ||
246 | xmlFree(key); | ||
247 | |||
248 | MappableComponent::Adjacent::Type adjType; | ||
249 | if (adjTypeStr == "wall") | ||
250 | { | ||
251 | adjType = MappableComponent::Adjacent::Type::wall; | ||
252 | } else if (adjTypeStr == "wrap") | ||
253 | { | ||
254 | adjType = MappableComponent::Adjacent::Type::wrap; | ||
255 | } else if (adjTypeStr == "warp") | ||
256 | { | ||
257 | adjType = MappableComponent::Adjacent::Type::warp; | ||
258 | } else if (adjTypeStr == "reverseWarp") | ||
259 | { | ||
260 | adjType = MappableComponent::Adjacent::Type::reverse; | ||
261 | } else { | ||
262 | throw std::logic_error("Invalid adjacency type"); | ||
263 | } | ||
264 | |||
265 | key = getProp(mapNode, "map"); | ||
266 | size_t adjMapId = atoi(reinterpret_cast<char*>(key)); | ||
267 | xmlFree(key); | ||
268 | |||
269 | key = getProp(mapNode, "dir"); | ||
270 | std::string adjDir(reinterpret_cast<char*>(key)); | ||
271 | xmlFree(key); | ||
272 | |||
273 | if (adjDir == "left") | ||
274 | { | ||
275 | mappable.leftAdjacent = {adjType, adjMapId}; | ||
276 | } else if (adjDir == "right") | ||
277 | { | ||
278 | mappable.rightAdjacent = {adjType, adjMapId}; | ||
279 | } else if (adjDir == "up") | ||
280 | { | ||
281 | mappable.upAdjacent = {adjType, adjMapId}; | ||
282 | } else if (adjDir == "down") | ||
283 | { | ||
284 | mappable.downAdjacent = {adjType, adjMapId}; | ||
285 | } else { | ||
286 | throw std::logic_error("Invalid adjacency direction"); | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | mapping.generateBoundaries(map); | ||
292 | |||
293 | entityByMapId_[mappable.mapId] = map; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | xmlFreeDoc(doc); | ||
298 | xmlFreeDoc(protoXml); | ||
299 | |||
300 | activateMap(entityByMapId_[startingMapId_]); | ||
301 | } | ||
302 | |||
303 | void RealizingSystem::loadMap(id_type mapEntity) | ||
304 | { | ||
305 | deactivateMap(); | ||
306 | activateMap(mapEntity); | ||
307 | } | ||
308 | |||
309 | void RealizingSystem::deactivateMap() | ||
310 | { | ||
311 | id_type oldMap = activeMap_; | ||
312 | |||
313 | auto& oldMappable = game_.getEntityManager(). | ||
314 | getComponent<MappableComponent>(oldMap); | ||
315 | |||
316 | // Deactivate any map objects from the old map. | ||
317 | for (id_type prototype : oldMappable.objects) | ||
318 | { | ||
319 | leaveActiveMap(prototype); | ||
320 | } | ||
321 | |||
322 | // Deactivate players that were on the old map. | ||
323 | std::set<id_type> players = | ||
324 | game_.getEntityManager().getEntitiesWithComponents< | ||
325 | PlayableComponent>(); | ||
326 | |||
327 | for (id_type player : players) | ||
328 | { | ||
329 | auto& playable = game_.getEntityManager(). | ||
330 | getComponent<PlayableComponent>(player); | ||
331 | |||
332 | if (playable.mapId == oldMap) | ||
333 | { | ||
334 | leaveActiveMap(player); | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | void RealizingSystem::activateMap(id_type mapEntity) | ||
340 | { | ||
341 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
342 | auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>(); | ||
343 | |||
344 | std::set<id_type> players = | ||
345 | game_.getEntityManager().getEntitiesWithComponents< | ||
346 | PlayableComponent>(); | ||
347 | |||
348 | activeMap_ = mapEntity; | ||
349 | |||
350 | auto& mappable = game_.getEntityManager(). | ||
351 | getComponent<MappableComponent>(mapEntity); | ||
352 | |||
353 | // Initialize the new map's objects. | ||
354 | for (id_type prototype : mappable.objects) | ||
355 | { | ||
356 | if (game_.getEntityManager(). | ||
357 | hasComponent<TransformableComponent>(prototype)) | ||
358 | { | ||
359 | auto& transformable = game_.getEntityManager(). | ||
360 | getComponent<TransformableComponent>(prototype); | ||
361 | |||
362 | transformable.pos = transformable.origPos; | ||
363 | transformable.size = transformable.origSize; | ||
364 | } | ||
365 | |||
366 | if (game_.getEntityManager().hasComponent<AnimatableComponent>(prototype)) | ||
367 | { | ||
368 | animating.initPrototype(prototype); | ||
369 | } | ||
370 | |||
371 | if (game_.getEntityManager().hasComponent<PonderableComponent>(prototype)) | ||
372 | { | ||
373 | pondering.initPrototype(prototype); | ||
374 | } | ||
375 | |||
376 | enterActiveMap(prototype); | ||
377 | } | ||
378 | |||
379 | // Activate any players on the map. | ||
380 | for (id_type player : players) | ||
381 | { | ||
382 | auto& playable = game_.getEntityManager(). | ||
383 | getComponent<PlayableComponent>(player); | ||
384 | |||
385 | if (playable.mapId == mapEntity) | ||
386 | { | ||
387 | enterActiveMap(player); | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | void RealizingSystem::enterActiveMap(id_type entity) | ||
393 | { | ||
394 | if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity)) | ||
395 | { | ||
396 | auto& animatable = game_.getEntityManager(). | ||
397 | getComponent<AnimatableComponent>(entity); | ||
398 | |||
399 | animatable.active = true; | ||
400 | } | ||
401 | |||
402 | if (game_.getEntityManager().hasComponent<PonderableComponent>(entity)) | ||
403 | { | ||
404 | auto& ponderable = game_.getEntityManager(). | ||
405 | getComponent<PonderableComponent>(entity); | ||
406 | |||
407 | ponderable.active = true; | ||
408 | } | ||
409 | |||
410 | if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity)) | ||
411 | { | ||
412 | game_.getSystemManager().getSystem<ScriptingSystem>().startBehavior(entity); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | void RealizingSystem::leaveActiveMap(id_type entity) | ||
417 | { | ||
418 | if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity)) | ||
419 | { | ||
420 | auto& animatable = game_.getEntityManager(). | ||
421 | getComponent<AnimatableComponent>(entity); | ||
422 | |||
423 | animatable.active = false; | ||
424 | } | ||
425 | |||
426 | if (game_.getEntityManager().hasComponent<PonderableComponent>(entity)) | ||
427 | { | ||
428 | auto& ponderable = game_.getEntityManager(). | ||
429 | getComponent<PonderableComponent>(entity); | ||
430 | |||
431 | ponderable.active = false; | ||
432 | } | ||
433 | |||
434 | if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity)) | ||
435 | { | ||
436 | game_.getSystemManager().getSystem<ScriptingSystem>().stopBehavior(entity); | ||
437 | } | ||
438 | } | ||
diff --git a/src/systems/realizing.h b/src/systems/realizing.h new file mode 100644 index 0000000..ab5a150 --- /dev/null +++ b/src/systems/realizing.h | |||
@@ -0,0 +1,86 @@ | |||
1 | #ifndef REALIZING_H_6853748C | ||
2 | #define REALIZING_H_6853748C | ||
3 | |||
4 | #include <string> | ||
5 | #include <map> | ||
6 | #include "system.h" | ||
7 | #include "vector.h" | ||
8 | |||
9 | class RealizingSystem : public System { | ||
10 | public: | ||
11 | |||
12 | /** | ||
13 | * Constructs the realizing system. | ||
14 | * | ||
15 | * Note that this must be constructed after the following system: | ||
16 | * - Mapping | ||
17 | * - Animating | ||
18 | * - Pondering | ||
19 | * - Scripting | ||
20 | */ | ||
21 | RealizingSystem( | ||
22 | Game& game, | ||
23 | std::string worldFile, | ||
24 | std::string prototypeFile); | ||
25 | |||
26 | id_type getActiveMap() const | ||
27 | { | ||
28 | return activeMap_; | ||
29 | } | ||
30 | |||
31 | int getStartingMapId() const | ||
32 | { | ||
33 | return startingMapId_; | ||
34 | } | ||
35 | |||
36 | vec2i getStartingPos() const | ||
37 | { | ||
38 | return startingPos_; | ||
39 | } | ||
40 | |||
41 | id_type getEntityByMapId(size_t mapId) const | ||
42 | { | ||
43 | return entityByMapId_.at(mapId); | ||
44 | } | ||
45 | |||
46 | id_type getActivePlayer() const | ||
47 | { | ||
48 | return activePlayer_; | ||
49 | } | ||
50 | |||
51 | void setActivePlayer(id_type entity) | ||
52 | { | ||
53 | activePlayer_ = entity; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * Loads the given map. | ||
58 | */ | ||
59 | void loadMap(id_type mapEntity); | ||
60 | |||
61 | /** | ||
62 | * Treats the given entity as part of the active map. | ||
63 | */ | ||
64 | void enterActiveMap(id_type entity); | ||
65 | |||
66 | /** | ||
67 | * Stops treating the given entity as part of the active map. | ||
68 | */ | ||
69 | void leaveActiveMap(id_type entity); | ||
70 | |||
71 | private: | ||
72 | |||
73 | void deactivateMap(); | ||
74 | |||
75 | void activateMap(id_type mapEntity); | ||
76 | |||
77 | std::string worldFile_; | ||
78 | std::string prototypeFile_; | ||
79 | int startingMapId_; | ||
80 | vec2i startingPos_; | ||
81 | std::map<size_t, id_type> entityByMapId_; | ||
82 | id_type activeMap_; | ||
83 | id_type activePlayer_; | ||
84 | }; | ||
85 | |||
86 | #endif /* end of include guard: REALIZING_H_6853748C */ | ||
diff --git a/src/systems/scheduling.cpp b/src/systems/scheduling.cpp new file mode 100644 index 0000000..220efae --- /dev/null +++ b/src/systems/scheduling.cpp | |||
@@ -0,0 +1,54 @@ | |||
1 | #include "scheduling.h" | ||
2 | #include "game.h" | ||
3 | #include "components/schedulable.h" | ||
4 | #include "util.h" | ||
5 | |||
6 | void SchedulingSystem::tick(double dt) | ||
7 | { | ||
8 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
9 | SchedulableComponent>(); | ||
10 | |||
11 | for (id_type entity : entities) | ||
12 | { | ||
13 | auto& schedulable = game_.getEntityManager(). | ||
14 | getComponent<SchedulableComponent>(entity); | ||
15 | |||
16 | for (auto& action : schedulable.actions) | ||
17 | { | ||
18 | std::get<0>(action) -= dt; | ||
19 | |||
20 | if (std::get<0>(action) < 0) | ||
21 | { | ||
22 | std::get<1>(action)(entity); | ||
23 | } | ||
24 | } | ||
25 | |||
26 | erase_if(schedulable.actions, | ||
27 | [] (const SchedulableComponent::Action& action) { | ||
28 | return (std::get<0>(action) < 0); | ||
29 | }); | ||
30 | |||
31 | if (schedulable.actions.empty()) | ||
32 | { | ||
33 | game_.getEntityManager().removeComponent<SchedulableComponent>(entity); | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | void SchedulingSystem::schedule( | ||
39 | id_type entity, | ||
40 | double length, | ||
41 | std::function<void(id_type)> action) | ||
42 | { | ||
43 | if (!game_.getEntityManager().hasComponent<SchedulableComponent>(entity)) | ||
44 | { | ||
45 | game_.getEntityManager().emplaceComponent<SchedulableComponent>(entity); | ||
46 | } | ||
47 | |||
48 | auto& schedulable = game_.getEntityManager(). | ||
49 | getComponent<SchedulableComponent>(entity); | ||
50 | |||
51 | schedulable.actions.emplace_back( | ||
52 | length, | ||
53 | std::move(action)); | ||
54 | } | ||
diff --git a/src/systems/scheduling.h b/src/systems/scheduling.h new file mode 100644 index 0000000..7950d62 --- /dev/null +++ b/src/systems/scheduling.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef SCHEDULING_H_7B02E3E3 | ||
2 | #define SCHEDULING_H_7B02E3E3 | ||
3 | |||
4 | #include "system.h" | ||
5 | |||
6 | class SchedulingSystem : public System { | ||
7 | public: | ||
8 | |||
9 | SchedulingSystem(Game& game) : System(game) | ||
10 | { | ||
11 | } | ||
12 | |||
13 | void tick(double dt); | ||
14 | |||
15 | void schedule( | ||
16 | id_type entity, | ||
17 | double length, | ||
18 | std::function<void(id_type)> action); | ||
19 | |||
20 | }; | ||
21 | |||
22 | #endif /* end of include guard: SCHEDULING_H_7B02E3E3 */ | ||
diff --git a/src/systems/scripting.cpp b/src/systems/scripting.cpp new file mode 100644 index 0000000..504224c --- /dev/null +++ b/src/systems/scripting.cpp | |||
@@ -0,0 +1,257 @@ | |||
1 | #include "scripting.h" | ||
2 | #include "game.h" | ||
3 | #include "components/runnable.h" | ||
4 | #include "components/ponderable.h" | ||
5 | #include "components/transformable.h" | ||
6 | #include "components/playable.h" | ||
7 | #include "components/mappable.h" | ||
8 | #include "components/prototypable.h" | ||
9 | #include "components/automatable.h" | ||
10 | #include "systems/realizing.h" | ||
11 | #include "vector.h" | ||
12 | #include "muxer.h" | ||
13 | |||
14 | struct script_entity { | ||
15 | using id_type = EntityManager::id_type; | ||
16 | |||
17 | id_type id; | ||
18 | |||
19 | script_entity(id_type id) : id(id) | ||
20 | { | ||
21 | } | ||
22 | }; | ||
23 | |||
24 | ScriptingSystem::ScriptingSystem(Game& game) : System(game) | ||
25 | { | ||
26 | id_type entity = game_.getEntityManager().emplaceEntity(); | ||
27 | |||
28 | engine_.open_libraries(sol::lib::base, sol::lib::coroutine); | ||
29 | |||
30 | engine_.new_usertype<vec2d>( | ||
31 | "vec2d", | ||
32 | sol::constructors<vec2d(), vec2d(double, double)>(), | ||
33 | "x", sol::property( | ||
34 | [] (vec2d& v) -> double { return v.x(); }, | ||
35 | [] (vec2d& v, double x) { v.x() = x; }), | ||
36 | "y", sol::property( | ||
37 | [] (vec2d& v) -> double { return v.y(); }, | ||
38 | [] (vec2d& v, double y) { v.y() = y; })); | ||
39 | |||
40 | engine_.new_usertype<vec2i>( | ||
41 | "vec2i", | ||
42 | sol::constructors<vec2i(), vec2i(int, int)>(), | ||
43 | "x", [] (vec2i& v) -> int& { return v.x(); }, | ||
44 | "y", [] (vec2i& v) -> int& { return v.y(); }); | ||
45 | |||
46 | engine_.new_usertype<script_entity>( | ||
47 | "entity", | ||
48 | sol::constructors<script_entity(id_type)>(), | ||
49 | "id", &script_entity::id, | ||
50 | "transformable", | ||
51 | [&] (script_entity& entity) -> TransformableComponent& { | ||
52 | return game_.getEntityManager(). | ||
53 | getComponent<TransformableComponent>(entity.id); | ||
54 | }, | ||
55 | "ponderable", | ||
56 | [&] (script_entity& entity) -> PonderableComponent& { | ||
57 | return game_.getEntityManager(). | ||
58 | getComponent<PonderableComponent>(entity.id); | ||
59 | }, | ||
60 | "mappable", | ||
61 | [&] (script_entity& entity) -> MappableComponent& { | ||
62 | return game_.getEntityManager(). | ||
63 | getComponent<MappableComponent>(entity.id); | ||
64 | }, | ||
65 | "playable", | ||
66 | [&] (script_entity& entity) -> PlayableComponent& { | ||
67 | return game_.getEntityManager(). | ||
68 | getComponent<PlayableComponent>(entity.id); | ||
69 | }, | ||
70 | "prototypable", | ||
71 | [&] (script_entity& entity) -> PrototypableComponent& { | ||
72 | return game_.getEntityManager(). | ||
73 | getComponent<PrototypableComponent>(entity.id); | ||
74 | }); | ||
75 | |||
76 | engine_.new_usertype<TransformableComponent>( | ||
77 | "transformable", | ||
78 | "pos", &TransformableComponent::pos); | ||
79 | |||
80 | engine_.new_usertype<PonderableComponent>( | ||
81 | "ponderable", | ||
82 | "vel", &PonderableComponent::vel, | ||
83 | "accel", &PonderableComponent::accel); | ||
84 | |||
85 | engine_.new_usertype<MappableComponent>( | ||
86 | "mappable", | ||
87 | "mapId", &MappableComponent::mapId); | ||
88 | |||
89 | engine_.new_usertype<PlayableComponent>( | ||
90 | "playable", | ||
91 | "checkpointPos", &PlayableComponent::checkpointPos, | ||
92 | "checkpointMapId", &PlayableComponent::checkpointMapId, | ||
93 | "checkpointMapObject", &PlayableComponent::checkpointMapObject, | ||
94 | "checkpointMapObjectIndex", &PlayableComponent::checkpointMapObjectIndex); | ||
95 | |||
96 | engine_.new_usertype<PrototypableComponent>( | ||
97 | "prototypable", | ||
98 | "mapObjectIndex", &PrototypableComponent::mapObjectIndex, | ||
99 | "prototypeId", &PrototypableComponent::prototypeId); | ||
100 | |||
101 | engine_.new_usertype<RealizingSystem>( | ||
102 | "realizing", | ||
103 | "activeMap", sol::property(&RealizingSystem::getActiveMap)); | ||
104 | |||
105 | engine_.set_function( | ||
106 | "realizing", | ||
107 | [&] () -> RealizingSystem& { | ||
108 | return game_.getSystemManager().getSystem<RealizingSystem>(); | ||
109 | }); | ||
110 | |||
111 | engine_.set_function("playSound", playSound); | ||
112 | |||
113 | engine_.script_file("scripts/common.lua"); | ||
114 | engine_.script_file("scripts/movplat.lua"); | ||
115 | engine_.script_file("scripts/checkpoint.lua"); | ||
116 | } | ||
117 | |||
118 | void ScriptingSystem::tick(double dt) | ||
119 | { | ||
120 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
121 | RunnableComponent>(); | ||
122 | |||
123 | for (id_type entity : entities) | ||
124 | { | ||
125 | auto& runnable = game_.getEntityManager(). | ||
126 | getComponent<RunnableComponent>(entity); | ||
127 | |||
128 | if (*runnable.callable) | ||
129 | { | ||
130 | auto result = (*runnable.callable)(dt); | ||
131 | if (!result.valid()) | ||
132 | { | ||
133 | sol::error e = result; | ||
134 | throw std::runtime_error(e.what()); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | if (!*runnable.callable) | ||
139 | { | ||
140 | killScript(entity); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void ScriptingSystem::killScript(id_type entity) | ||
146 | { | ||
147 | auto& runnable = game_.getEntityManager(). | ||
148 | getComponent<RunnableComponent>(entity); | ||
149 | |||
150 | if (runnable.behavior) | ||
151 | { | ||
152 | auto& automatable = game_.getEntityManager(). | ||
153 | getComponent<AutomatableComponent>(runnable.actor); | ||
154 | |||
155 | automatable.running = false; | ||
156 | } | ||
157 | |||
158 | game_.getEntityManager().deleteEntity(entity); | ||
159 | } | ||
160 | |||
161 | template <typename... Args> | ||
162 | sol::optional<EntityManager::id_type> ScriptingSystem::runScript( | ||
163 | std::string table, | ||
164 | std::string event, | ||
165 | id_type entity, | ||
166 | Args&&... args) | ||
167 | { | ||
168 | id_type script = game_.getEntityManager().emplaceEntity(); | ||
169 | |||
170 | auto& runnable = game_.getEntityManager(). | ||
171 | emplaceComponent<RunnableComponent>(script); | ||
172 | |||
173 | runnable.runner = | ||
174 | std::unique_ptr<sol::thread>( | ||
175 | new sol::thread( | ||
176 | sol::thread::create( | ||
177 | engine_.lua_state()))); | ||
178 | |||
179 | runnable.callable = | ||
180 | std::unique_ptr<sol::coroutine>( | ||
181 | new sol::coroutine( | ||
182 | runnable.runner->state(). | ||
183 | traverse_get<sol::function>( | ||
184 | table, | ||
185 | event))); | ||
186 | |||
187 | if (!*runnable.callable) | ||
188 | { | ||
189 | throw std::runtime_error("Error running script"); | ||
190 | } | ||
191 | |||
192 | auto result = (*runnable.callable)( | ||
193 | script_entity(entity), | ||
194 | std::forward<Args>(args)...); | ||
195 | |||
196 | if (!result.valid()) | ||
197 | { | ||
198 | sol::error e = result; | ||
199 | throw std::runtime_error(e.what()); | ||
200 | } | ||
201 | |||
202 | if (*runnable.callable) | ||
203 | { | ||
204 | return { script }; | ||
205 | } else { | ||
206 | killScript(script); | ||
207 | |||
208 | return {}; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | void ScriptingSystem::startBehavior(id_type entity) | ||
213 | { | ||
214 | auto& automatable = game_.getEntityManager(). | ||
215 | getComponent<AutomatableComponent>(entity); | ||
216 | |||
217 | sol::optional<id_type> script = | ||
218 | runScript( | ||
219 | automatable.table, | ||
220 | "Behavior", | ||
221 | entity); | ||
222 | |||
223 | if (script) | ||
224 | { | ||
225 | automatable.script = *script; | ||
226 | automatable.running = true; | ||
227 | |||
228 | auto& runnable = game_.getEntityManager(). | ||
229 | getComponent<RunnableComponent>(automatable.script); | ||
230 | |||
231 | runnable.behavior = true; | ||
232 | runnable.actor = entity; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | void ScriptingSystem::stopBehavior(id_type entity) | ||
237 | { | ||
238 | auto& automatable = game_.getEntityManager(). | ||
239 | getComponent<AutomatableComponent>(entity); | ||
240 | |||
241 | if (automatable.running) | ||
242 | { | ||
243 | killScript(automatable.script); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | void ScriptingSystem::onTouch(id_type entity, id_type player) | ||
248 | { | ||
249 | auto& prototypable = game_.getEntityManager(). | ||
250 | getComponent<PrototypableComponent>(entity); | ||
251 | |||
252 | runScript( | ||
253 | prototypable.prototypeId, | ||
254 | "OnTouch", | ||
255 | entity, | ||
256 | script_entity(player)); | ||
257 | } | ||
diff --git a/src/systems/scripting.h b/src/systems/scripting.h new file mode 100644 index 0000000..b119c3f --- /dev/null +++ b/src/systems/scripting.h | |||
@@ -0,0 +1,34 @@ | |||
1 | #ifndef AUTOMATING_H_E6E5D76E | ||
2 | #define AUTOMATING_H_E6E5D76E | ||
3 | |||
4 | #include "system.h" | ||
5 | #include <sol.hpp> | ||
6 | |||
7 | class ScriptingSystem : public System { | ||
8 | public: | ||
9 | |||
10 | ScriptingSystem(Game& game); | ||
11 | |||
12 | void tick(double dt); | ||
13 | |||
14 | void killScript(id_type entity); | ||
15 | |||
16 | void startBehavior(id_type entity); | ||
17 | |||
18 | void stopBehavior(id_type entity); | ||
19 | |||
20 | void onTouch(id_type entity, id_type player); | ||
21 | |||
22 | private: | ||
23 | |||
24 | template <typename... Args> | ||
25 | sol::optional<id_type> runScript( | ||
26 | std::string table, | ||
27 | std::string event, | ||
28 | id_type entity, | ||
29 | Args&&... args); | ||
30 | |||
31 | sol::state engine_; | ||
32 | }; | ||
33 | |||
34 | #endif /* end of include guard: AUTOMATING_H_E6E5D76E */ | ||