diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rwxr-xr-x | res/player.png | bin | 0 -> 6428 bytes | |||
-rw-r--r-- | res/player_anim.txt | 12 | ||||
-rw-r--r-- | src/animation.cpp | 93 | ||||
-rw-r--r-- | src/animation.h | 39 | ||||
-rw-r--r-- | src/direction.h | 22 | ||||
-rw-r--r-- | src/game.h | 2 | ||||
-rw-r--r-- | src/main.cpp | 11 | ||||
-rw-r--r-- | src/renderer.cpp | 22 | ||||
-rw-r--r-- | src/renderer.h | 1 | ||||
-rw-r--r-- | src/util.h | 35 |
11 files changed, 234 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e499ef2..360d4a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -32,6 +32,7 @@ add_executable(Ether | |||
32 | src/main.cpp | 32 | src/main.cpp |
33 | src/renderer.cpp | 33 | src/renderer.cpp |
34 | src/muxer.cpp | 34 | src/muxer.cpp |
35 | src/animation.cpp | ||
35 | vendor/fov.c | 36 | vendor/fov.c |
36 | ) | 37 | ) |
37 | 38 | ||
diff --git a/res/player.png b/res/player.png new file mode 100755 index 0000000..9f6dbdf --- /dev/null +++ b/res/player.png | |||
Binary files differ | |||
diff --git a/res/player_anim.txt b/res/player_anim.txt new file mode 100644 index 0000000..e1037a0 --- /dev/null +++ b/res/player_anim.txt | |||
@@ -0,0 +1,12 @@ | |||
1 | 16,16 cell size | ||
2 | 8 frames per row | ||
3 | 128 frames | ||
4 | |||
5 | still[down]: 0,1,2,3 | ||
6 | still[left]: 8,9,10,11 | ||
7 | still[right]: 16,17,18,19 | ||
8 | still[up]: 24,25,26,27 | ||
9 | walk[down]: 32,33,34,35 | ||
10 | walk[left]: 40,41,42,43 | ||
11 | walk[right]: 48,49,50,51 | ||
12 | walk[up]: 56,57,58,59 \ No newline at end of file | ||
diff --git a/src/animation.cpp b/src/animation.cpp new file mode 100644 index 0000000..fbf7ccf --- /dev/null +++ b/src/animation.cpp | |||
@@ -0,0 +1,93 @@ | |||
1 | #include "animation.h" | ||
2 | #include <string_view> | ||
3 | #include <fstream> | ||
4 | #include <stdexcept> | ||
5 | #include <string> | ||
6 | #include <regex> | ||
7 | #include <list> | ||
8 | #include "direction.h" | ||
9 | #include "util.h" | ||
10 | |||
11 | Animation::Animation(std::string_view path) { | ||
12 | std::ifstream datafile(path.data()); | ||
13 | if (!datafile.is_open()) { | ||
14 | throw std::invalid_argument(std::string("Could not find sprite datafile: ") + path.data()); | ||
15 | } | ||
16 | |||
17 | std::string animLine; | ||
18 | char ch; | ||
19 | int cellWidth; | ||
20 | int cellHeight; | ||
21 | datafile >> cellWidth; | ||
22 | datafile >> ch; //, | ||
23 | datafile >> cellHeight; | ||
24 | std::getline(datafile, animLine); // cell size | ||
25 | |||
26 | int framesPerRow; | ||
27 | datafile >> framesPerRow; | ||
28 | std::getline(datafile, animLine); // frames per row | ||
29 | |||
30 | int numFrames; | ||
31 | datafile >> numFrames; | ||
32 | std::getline(datafile, animLine); // frames | ||
33 | std::getline(datafile, animLine); // blank | ||
34 | |||
35 | for (int i=0; i<numFrames; i++) { | ||
36 | SDL_Rect srcRect; | ||
37 | srcRect.x = (i % framesPerRow) * cellWidth; | ||
38 | srcRect.y = (i / framesPerRow) * cellHeight; | ||
39 | srcRect.w = cellWidth; | ||
40 | srcRect.h = cellHeight; | ||
41 | frames_.push_back(srcRect); | ||
42 | } | ||
43 | |||
44 | while (std::getline(datafile, animLine)) { | ||
45 | std::regex re(R"(([a-z!._]+)\[([a-z_]+)\]: ([0-9,]+))"); | ||
46 | std::smatch m; | ||
47 | std::regex_match(animLine, m, re); | ||
48 | |||
49 | std::string animName = m[1]; | ||
50 | std::vector<int> anim; | ||
51 | auto framestrs = splitStr<std::list<std::string>>(m[3], ","); | ||
52 | for (const std::string& f : framestrs) { | ||
53 | anim.push_back(std::stoi(f)); | ||
54 | } | ||
55 | |||
56 | int animId = animations_.size(); | ||
57 | animations_.push_back(std::move(anim)); | ||
58 | |||
59 | Direction dir = directionFromString(std::string(m[2])); | ||
60 | nameDirToAnim_[animName][dir] = animId; | ||
61 | } | ||
62 | |||
63 | updateAnim(); | ||
64 | } | ||
65 | |||
66 | void Animation::setDirection(Direction dir) { | ||
67 | dir_ = dir; | ||
68 | updateAnim(); | ||
69 | } | ||
70 | |||
71 | void Animation::setAnimation(std::string_view anim) { | ||
72 | animationName_ = anim; | ||
73 | updateAnim(); | ||
74 | } | ||
75 | |||
76 | void Animation::update(int dt) { | ||
77 | animTimer_.accumulate(dt); | ||
78 | |||
79 | while (animTimer_.step()) { | ||
80 | animationFrame_++; | ||
81 | |||
82 | if (animationFrame_ >= animations_.at(animationId_).size()) { | ||
83 | animationFrame_ = 0; | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | void Animation::updateAnim() { | ||
89 | if (nameDirToAnim_.at(animationName_).at(dir_) != animationId_) { | ||
90 | animationId_ = nameDirToAnim_.at(animationName_).at(dir_); | ||
91 | animationFrame_ = 0; | ||
92 | } | ||
93 | } | ||
diff --git a/src/animation.h b/src/animation.h new file mode 100644 index 0000000..0108c12 --- /dev/null +++ b/src/animation.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #ifndef ANIMATION_H_332518EB | ||
2 | #define ANIMATION_H_332518EB | ||
3 | |||
4 | #include <string> | ||
5 | #include <map> | ||
6 | #include <vector> | ||
7 | #include <string_view> | ||
8 | #include <SDL.h> | ||
9 | #include "direction.h" | ||
10 | #include "timer.h" | ||
11 | |||
12 | class Animation { | ||
13 | public: | ||
14 | explicit Animation(std::string_view path); | ||
15 | |||
16 | void setDirection(Direction dir); | ||
17 | void setAnimation(std::string_view anim); | ||
18 | |||
19 | const SDL_Rect& getRenderRect() const { | ||
20 | return frames_.at(animations_.at(animationId_).at(animationFrame_)); | ||
21 | } | ||
22 | |||
23 | void update(int dt); | ||
24 | |||
25 | private: | ||
26 | |||
27 | void updateAnim(); | ||
28 | |||
29 | std::vector<SDL_Rect> frames_; | ||
30 | std::vector<std::vector<int>> animations_; | ||
31 | std::map<std::string, std::map<Direction, int>> nameDirToAnim_; | ||
32 | Direction dir_ = Direction::down; | ||
33 | std::string animationName_ = "still"; | ||
34 | int animationId_ = 0; | ||
35 | int animationFrame_ = 0; | ||
36 | Timer animTimer_ = {1000/5}; | ||
37 | }; | ||
38 | |||
39 | #endif /* end of include guard: ANIMATION_H_332518EB */ | ||
diff --git a/src/direction.h b/src/direction.h new file mode 100644 index 0000000..0fe3e5a --- /dev/null +++ b/src/direction.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef DIRECTION_H_42BDAFB9 | ||
2 | #define DIRECTION_H_42BDAFB9 | ||
3 | |||
4 | #include <string_view> | ||
5 | #include <stdexcept> | ||
6 | |||
7 | enum class Direction { | ||
8 | up, | ||
9 | down, | ||
10 | left, | ||
11 | right | ||
12 | }; | ||
13 | |||
14 | inline Direction directionFromString(std::string_view str) { | ||
15 | if (str == "up") return Direction::up; | ||
16 | if (str == "right") return Direction::right; | ||
17 | if (str == "down") return Direction::down; | ||
18 | if (str == "left") return Direction::left; | ||
19 | throw std::invalid_argument("Invalid direction: " + std::string(str)); | ||
20 | } | ||
21 | |||
22 | #endif /* end of include guard: DIRECTION_H_42BDAFB9 */ | ||
diff --git a/src/game.h b/src/game.h index c489afc..2fd0f05 100644 --- a/src/game.h +++ b/src/game.h | |||
@@ -8,6 +8,7 @@ | |||
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "muxer.h" | 9 | #include "muxer.h" |
10 | #include "timer.h" | 10 | #include "timer.h" |
11 | #include "animation.h" | ||
11 | 12 | ||
12 | const int GAME_WIDTH = 640*2; | 13 | const int GAME_WIDTH = 640*2; |
13 | const int GAME_HEIGHT = 480*2; | 14 | const int GAME_HEIGHT = 480*2; |
@@ -102,6 +103,7 @@ public: | |||
102 | int player_x = 0; | 103 | int player_x = 0; |
103 | int player_y = 0; | 104 | int player_y = 0; |
104 | bool renderPlayer = true; | 105 | bool renderPlayer = true; |
106 | Animation playerAnim {"../res/player_anim.txt"}; | ||
105 | 107 | ||
106 | int maxZoom = INIT_ZOOM; | 108 | int maxZoom = INIT_ZOOM; |
107 | 109 | ||
diff --git a/src/main.cpp b/src/main.cpp index 1805357..5178110 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
@@ -207,6 +207,7 @@ bool processKeys(Game& game, const Input& keystate) | |||
207 | { | 207 | { |
208 | int px = game.player_x; | 208 | int px = game.player_x; |
209 | int py = game.player_y; | 209 | int py = game.player_y; |
210 | Direction dir = Direction::up; | ||
210 | 211 | ||
211 | if (keystate.up) | 212 | if (keystate.up) |
212 | { | 213 | { |
@@ -216,20 +217,26 @@ bool processKeys(Game& game, const Input& keystate) | |||
216 | if (keystate.down) | 217 | if (keystate.down) |
217 | { | 218 | { |
218 | py++; | 219 | py++; |
220 | dir = Direction::down; | ||
219 | } | 221 | } |
220 | 222 | ||
221 | if (keystate.left) | 223 | if (keystate.left) |
222 | { | 224 | { |
223 | px--; | 225 | px--; |
226 | dir = Direction::left; | ||
224 | } | 227 | } |
225 | 228 | ||
226 | if (keystate.right) | 229 | if (keystate.right) |
227 | { | 230 | { |
228 | px++; | 231 | px++; |
232 | dir = Direction::right; | ||
229 | } | 233 | } |
230 | 234 | ||
231 | if (!(game.player_x == px && game.player_y == py)) | 235 | if (!(game.player_x == px && game.player_y == py)) |
232 | { | 236 | { |
237 | game.playerAnim.setAnimation("walk"); | ||
238 | game.playerAnim.setDirection(dir); | ||
239 | |||
233 | return movePlayer(game, px, py); | 240 | return movePlayer(game, px, py); |
234 | } else { | 241 | } else { |
235 | return false; | 242 | return false; |
@@ -614,6 +621,8 @@ int main(int, char**) | |||
614 | { | 621 | { |
615 | game.firstInput = true; | 622 | game.firstInput = true; |
616 | game.lastInput = keystate; | 623 | game.lastInput = keystate; |
624 | } else if (losing == LoseState::None) { | ||
625 | game.playerAnim.setAnimation("still"); | ||
617 | } | 626 | } |
618 | 627 | ||
619 | dustAcc += frameTime; | 628 | dustAcc += frameTime; |
@@ -767,6 +776,8 @@ int main(int, char**) | |||
767 | zoomAcc -= zoomDt; | 776 | zoomAcc -= zoomDt; |
768 | } | 777 | } |
769 | 778 | ||
779 | game.playerAnim.update(frameTime); | ||
780 | |||
770 | game.muxer.update(); | 781 | game.muxer.update(); |
771 | renderer.render(game, true); | 782 | renderer.render(game, true); |
772 | } | 783 | } |
diff --git a/src/renderer.cpp b/src/renderer.cpp index 2dac07e..0aaa14a 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp | |||
@@ -110,6 +110,18 @@ Renderer::Renderer() | |||
110 | 110 | ||
111 | SDL_SetRenderDrawColor(ren_.get(), 100, 100, 100, 255); | 111 | SDL_SetRenderDrawColor(ren_.get(), 100, 100, 100, 255); |
112 | SDL_RenderFillRect(ren_.get(), nullptr); | 112 | SDL_RenderFillRect(ren_.get(), nullptr); |
113 | |||
114 | { | ||
115 | surface_ptr pfs(IMG_Load("../res/player.png")); | ||
116 | if (!pfs) | ||
117 | { | ||
118 | throw img_error(); | ||
119 | } | ||
120 | |||
121 | playerSheet_ = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), pfs.get())); | ||
122 | } | ||
123 | |||
124 | SDL_SetTextureBlendMode(playerSheet_.get(), SDL_BLENDMODE_BLEND); | ||
113 | } | 125 | } |
114 | 126 | ||
115 | void Renderer::render( | 127 | void Renderer::render( |
@@ -140,10 +152,7 @@ void Renderer::render( | |||
140 | { | 152 | { |
141 | bool draw = true; | 153 | bool draw = true; |
142 | 154 | ||
143 | if ((game.player_x == x && game.player_y == y) && game.renderPlayer) | 155 | if (!game.map.at(x,y).lit) |
144 | { | ||
145 | SDL_SetRenderDrawColor(ren_.get(), 255, 255, 0, 255); | ||
146 | } else if (!game.map.at(x,y).lit) | ||
147 | { | 156 | { |
148 | if (drawDark) | 157 | if (drawDark) |
149 | { | 158 | { |
@@ -191,6 +200,11 @@ void Renderer::render( | |||
191 | TILE_HEIGHT}; | 200 | TILE_HEIGHT}; |
192 | 201 | ||
193 | SDL_RenderFillRect(ren_.get(), &rect); | 202 | SDL_RenderFillRect(ren_.get(), &rect); |
203 | |||
204 | if ((game.player_x == x && game.player_y == y) && game.renderPlayer) | ||
205 | { | ||
206 | SDL_RenderCopy(ren_.get(), playerSheet_.get(), &game.playerAnim.getRenderRect(), &rect); | ||
207 | } | ||
194 | } | 208 | } |
195 | } | 209 | } |
196 | } | 210 | } |
diff --git a/src/renderer.h b/src/renderer.h index a34b231..0416c9e 100644 --- a/src/renderer.h +++ b/src/renderer.h | |||
@@ -129,6 +129,7 @@ private: | |||
129 | texture_ptr playerFade_; | 129 | texture_ptr playerFade_; |
130 | texture_ptr lampFade_; | 130 | texture_ptr lampFade_; |
131 | texture_ptr dustFade_; | 131 | texture_ptr dustFade_; |
132 | texture_ptr playerSheet_; | ||
132 | }; | 133 | }; |
133 | 134 | ||
134 | #endif /* end of include guard: RENDERER_H_6A58EC30 */ | 135 | #endif /* end of include guard: RENDERER_H_6A58EC30 */ |
diff --git a/src/util.h b/src/util.h index 150a6a5..1eb2303 100644 --- a/src/util.h +++ b/src/util.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef UTIL_H_E9110D4C | 1 | #ifndef UTIL_H_E9110D4C |
2 | #define UTIL_H_E9110D4C | 2 | #define UTIL_H_E9110D4C |
3 | 3 | ||
4 | #include <string> | ||
5 | #include <iterator> | ||
6 | |||
4 | template <typename Container, typename Predicate> | 7 | template <typename Container, typename Predicate> |
5 | void erase_if(Container& items, const Predicate& predicate) | 8 | void erase_if(Container& items, const Predicate& predicate) |
6 | { | 9 | { |
@@ -17,4 +20,36 @@ void erase_if(Container& items, const Predicate& predicate) | |||
17 | } | 20 | } |
18 | }; | 21 | }; |
19 | 22 | ||
23 | template <class OutputIterator> | ||
24 | void splitStr( | ||
25 | std::string input, | ||
26 | std::string delimiter, | ||
27 | OutputIterator out) { | ||
28 | while (!input.empty()) { | ||
29 | int divider = input.find(delimiter); | ||
30 | if (divider == std::string::npos) { | ||
31 | *out = input; | ||
32 | out++; | ||
33 | |||
34 | input = ""; | ||
35 | } else { | ||
36 | *out = input.substr(0, divider); | ||
37 | out++; | ||
38 | |||
39 | input = input.substr(divider+delimiter.length()); | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | template <class Container> | ||
45 | Container splitStr( | ||
46 | std::string input, | ||
47 | std::string delimiter) { | ||
48 | Container result; | ||
49 | |||
50 | splitStr(input, delimiter, std::back_inserter(result)); | ||
51 | |||
52 | return result; | ||
53 | } | ||
54 | |||
20 | #endif /* end of include guard: UTIL_H_E9110D4C */ | 55 | #endif /* end of include guard: UTIL_H_E9110D4C */ |