diff options
| author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-05-17 15:55:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-17 15:55:37 -0400 |
| commit | 90aadf3844386824140a20d7fbb847bc16009a94 (patch) | |
| tree | 6f83fce90e71abb22b1a8f3e09c79963b2a34d5d /src/systems | |
| parent | bc63fa57ced1c7329f7fdcfd168eaf7e290158bc (diff) | |
| parent | 86f0106d0523825549f1e74b835688c78a10cf6c (diff) | |
| download | therapy-90aadf3844386824140a20d7fbb847bc16009a94.tar.gz therapy-90aadf3844386824140a20d7fbb847bc16009a94.tar.bz2 therapy-90aadf3844386824140a20d7fbb847bc16009a94.zip | |
Merge pull request #7 from hatkirby/es-rewrite
The ECS rewrite exceeds the original branch in functionality, so it is time to merge it in.
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 */ | ||
