summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/game.h104
-rw-r--r--src/main.cpp522
-rw-r--r--src/renderer.cpp309
-rw-r--r--src/renderer.h132
5 files changed, 561 insertions, 507 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e3b88fd..b492d05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -23,6 +23,7 @@ include_directories(
23 23
24add_executable(Ether 24add_executable(Ether
25 src/main.cpp 25 src/main.cpp
26 src/renderer.cpp
26 vendor/fov.c 27 vendor/fov.c
27) 28)
28 29
diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..1142efb --- /dev/null +++ b/src/game.h
@@ -0,0 +1,104 @@
1#ifndef GAME_H_7D2B65AE
2#define GAME_H_7D2B65AE
3
4#include <tuple>
5#include <set>
6#include <random>
7#include <list>
8#include "map.h"
9
10const int GAME_WIDTH = 640*2;
11const int GAME_HEIGHT = 480*2;
12const int TILE_WIDTH = 8*2;
13const int TILE_HEIGHT = TILE_WIDTH;
14const int INIT_ZOOM = 10;
15const int ZOOM_X_FACTOR = 8;
16const int ZOOM_Y_FACTOR = 6;
17const int RADIUS = 8;
18
19enum class Tile {
20 Floor,
21 Wall,
22 Dust,
23 Lamp
24};
25
26enum class Source {
27 None,
28 Dust,
29 Lamp,
30 Player
31};
32
33enum class LoseState {
34 None,
35 PoppingLamps,
36 PoppingPlayer,
37 Outro
38};
39
40struct Input {
41 bool left = false;
42 bool right = false;
43 bool up = false;
44 bool down = false;
45};
46
47using coord = std::tuple<int, int>;
48
49struct Kickup {
50 int x;
51 int y;
52 size_t cur;
53 size_t radius;
54 size_t chain;
55 std::set<coord> done;
56 std::set<coord> front;
57};
58
59struct MapData {
60 Tile tile = Tile::Floor;
61 bool lit = false;
62 bool wasLit = false;
63 size_t dustLife = 0;
64 Source lightType = Source::None;
65 int lightRadius = 0;
66 std::set<coord> litTiles;
67};
68
69class Game {
70public:
71
72 Game(std::mt19937& rng) :
73 rng(rng),
74 map(
75 -INIT_ZOOM * ZOOM_X_FACTOR / 2,
76 -INIT_ZOOM * ZOOM_Y_FACTOR / 2,
77 INIT_ZOOM * ZOOM_X_FACTOR,
78 INIT_ZOOM * ZOOM_Y_FACTOR)
79 {
80 }
81
82 std::mt19937& rng;
83
84 Map<MapData> map;
85 std::list<Kickup> kickups;
86 int litSpots = 0;
87 bool dirtyLighting = true;
88 size_t numLamps = 0;
89 size_t numDust = 0;
90
91 int player_x = 0;
92 int player_y = 0;
93 bool renderPlayer = true;
94
95 int curZoom = INIT_ZOOM;
96 int maxZoom = INIT_ZOOM;
97
98 bool zooming = false;
99 //size_t oldZoom;
100 int zoomProgress = 0;
101
102};
103
104#endif /* end of include guard: GAME_H_7D2B65AE */
diff --git a/src/main.cpp b/src/main.cpp index 03e7cda..8f20635 100644 --- a/src/main.cpp +++ b/src/main.cpp
@@ -1,455 +1,9 @@
1#include <SDL.h>
2#include <SDL_image.h>
3#include <stdexcept>
4#include <memory>
5#include <vector> 1#include <vector>
6#include <list>
7#include <random>
8#include <fov.h> 2#include <fov.h>
9#include <iostream> 3#include <iostream>
10#include <tuple>
11#include <set>
12#include "util.h" 4#include "util.h"
13#include "map.h" 5#include "game.h"
14 6#include "renderer.h"
15class sdl_error : public std::logic_error {
16public:
17
18 sdl_error() : std::logic_error(SDL_GetError())
19 {
20 }
21};
22
23class img_error : public std::logic_error {
24public:
25
26 img_error() : std::logic_error(IMG_GetError())
27 {
28 }
29};
30
31class window_deleter {
32public:
33
34 void operator()(SDL_Window* ptr)
35 {
36 SDL_DestroyWindow(ptr);
37 }
38};
39
40using window_ptr = std::unique_ptr<SDL_Window, window_deleter>;
41
42class renderer_deleter {
43public:
44
45 void operator()(SDL_Renderer* ptr)
46 {
47 SDL_DestroyRenderer(ptr);
48 }
49};
50
51using renderer_ptr = std::unique_ptr<SDL_Renderer, renderer_deleter>;
52
53class surface_deleter {
54public:
55
56 void operator()(SDL_Surface* ptr)
57 {
58 SDL_FreeSurface(ptr);
59 }
60};
61
62using surface_ptr = std::unique_ptr<SDL_Surface, surface_deleter>;
63
64class texture_deleter {
65public:
66
67 void operator()(SDL_Texture* ptr)
68 {
69 SDL_DestroyTexture(ptr);
70 }
71};
72
73using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>;
74
75enum class Tile {
76 Floor,
77 Wall,
78 Dust,
79 Lamp
80};
81
82enum class Source {
83 None,
84 Dust,
85 Lamp,
86 Player
87};
88
89enum class LoseState {
90 None,
91 PoppingLamps,
92 PoppingPlayer,
93 Outro
94};
95
96const int GAME_WIDTH = 640*2;
97const int GAME_HEIGHT = 480*2;
98const int TILE_WIDTH = 8*2;
99const int TILE_HEIGHT = TILE_WIDTH;
100const int INIT_ZOOM = 10;
101const int ZOOM_X_FACTOR = 8;
102const int ZOOM_Y_FACTOR = 6;
103const int RADIUS = 8;
104
105struct Input {
106 bool left = false;
107 bool right = false;
108 bool up = false;
109 bool down = false;
110};
111
112using coord = std::tuple<int, int>;
113
114struct Kickup {
115 int x;
116 int y;
117 size_t cur;
118 size_t radius;
119 size_t chain;
120 std::set<coord> done;
121 std::set<coord> front;
122};
123
124struct MapData {
125 Tile tile = Tile::Floor;
126 bool lit = false;
127 bool wasLit = false;
128 double visibility = 0.0;
129 size_t dustLife = 0;
130 Source lightType = Source::None;
131 int lightRadius = 0;
132 std::set<coord> litTiles;
133};
134
135class Game {
136public:
137
138 Game(std::mt19937& rng) :
139 rng(rng),
140 map(
141 -INIT_ZOOM * ZOOM_X_FACTOR / 2,
142 -INIT_ZOOM * ZOOM_Y_FACTOR / 2,
143 INIT_ZOOM * ZOOM_X_FACTOR,
144 INIT_ZOOM * ZOOM_Y_FACTOR)
145 {
146 }
147
148 std::mt19937& rng;
149
150 Map<MapData> map;
151 std::list<Kickup> kickups;
152 int litSpots = 0;
153 bool dirtyLighting = true;
154 size_t numLamps = 0;
155 size_t numDust = 0;
156
157 int player_x = 0;
158 int player_y = 0;
159 bool renderPlayer = true;
160
161 int curZoom = INIT_ZOOM;
162 int maxZoom = INIT_ZOOM;
163
164 bool zooming = false;
165 //size_t oldZoom;
166 int zoomProgress = 0;
167
168};
169
170void render(
171 SDL_Renderer* ren,
172 const Game& game,
173 bool drawDark = true)
174{
175 texture_ptr origFade;
176 {
177 surface_ptr pfs(IMG_Load("../res/lighting.png"));
178 if (!pfs)
179 {
180 throw img_error();
181 }
182
183 origFade = texture_ptr(SDL_CreateTextureFromSurface(ren, pfs.get()));
184 }
185
186 SDL_SetTextureBlendMode(origFade.get(), SDL_BLENDMODE_BLEND);
187
188 texture_ptr playerFade(
189 SDL_CreateTexture(
190 ren,
191 SDL_PIXELFORMAT_RGBA4444,
192 SDL_TEXTUREACCESS_TARGET,
193 144,
194 144));
195
196 if (!playerFade)
197 {
198 throw sdl_error();
199 }
200
201 SDL_SetRenderTarget(ren, playerFade.get());
202 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE);
203 SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
204 SDL_RenderClear(ren);
205 SDL_RenderCopy(ren, origFade.get(), nullptr, nullptr);
206
207 texture_ptr lampFade(
208 SDL_CreateTexture(
209 ren,
210 SDL_PIXELFORMAT_RGBA4444,
211 SDL_TEXTUREACCESS_TARGET,
212 144,
213 144));
214
215 if (!lampFade)
216 {
217 throw sdl_error();
218 }
219
220 SDL_SetRenderTarget(ren, lampFade.get());
221
222 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE);
223 SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
224 SDL_RenderClear(ren);
225 SDL_RenderCopy(ren, origFade.get(), nullptr, nullptr);
226
227 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_MOD);
228 SDL_SetRenderDrawColor(ren, 255, 204, 58, 255);
229 SDL_RenderFillRect(ren, nullptr);
230
231 texture_ptr dustFade(
232 SDL_CreateTexture(
233 ren,
234 SDL_PIXELFORMAT_RGBA4444,
235 SDL_TEXTUREACCESS_TARGET,
236 144,
237 144));
238
239 if (!dustFade)
240 {
241 throw sdl_error();
242 }
243
244 SDL_SetRenderTarget(ren, dustFade.get());
245
246 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE);
247 SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
248 SDL_RenderClear(ren);
249 SDL_RenderCopy(ren, origFade.get(), nullptr, nullptr);
250
251 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_MOD);
252 SDL_SetRenderDrawColor(ren, 255, 150, 255, 255);
253 SDL_RenderFillRect(ren, nullptr);
254
255 texture_ptr canvas(
256 SDL_CreateTexture(
257 ren,
258 SDL_PIXELFORMAT_RGBA8888,
259 SDL_TEXTUREACCESS_TARGET,
260 TILE_WIDTH * game.map.getWidth(),
261 TILE_HEIGHT * game.map.getHeight()));
262
263 if (!canvas)
264 {
265 throw sdl_error();
266 }
267
268 SDL_SetRenderTarget(ren, canvas.get());
269 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE);
270 SDL_SetRenderDrawColor(ren, rand() % 255, rand() % 255, rand() % 255, 255);
271 SDL_RenderClear(ren);
272
273 for (int y = game.map.getTop(); y < game.map.getBottom(); y++)
274 {
275 for (int x = game.map.getLeft(); x < game.map.getRight(); x++)
276 {
277 bool draw = true;
278
279 if ((game.player_x == x && game.player_y == y) && game.renderPlayer)
280 {
281 SDL_SetRenderDrawColor(ren, 255, 255, 0, 255);
282 } else if (!game.map.at(x,y).lit)
283 {
284 if (drawDark)
285 {
286 SDL_SetRenderDrawColor(ren, 40, 40, 40, 255);
287 } else {
288 draw = false;
289 }
290 } else {
291 int alpha = 255;
292
293 switch (game.map.at(x,y).tile)
294 {
295 case Tile::Floor:
296 {
297 SDL_SetRenderDrawColor(ren, 210, 210, 210, alpha);
298 break;
299 }
300
301 case Tile::Wall:
302 {
303 SDL_SetRenderDrawColor(ren, 100, 100, 100, alpha);
304 break;
305 }
306
307 case Tile::Dust:
308 {
309 SDL_SetRenderDrawColor(ren, 128, 40, 255, alpha);
310 break;
311 }
312
313 case Tile::Lamp:
314 {
315 SDL_SetRenderDrawColor(ren, 0, 255, 255, alpha);
316 break;
317 }
318 }
319 }
320
321 if (draw)
322 {
323 SDL_Rect rect {
324 game.map.getTrueX(x) * TILE_WIDTH,
325 game.map.getTrueY(y) * TILE_HEIGHT,
326 TILE_WIDTH,
327 TILE_HEIGHT};
328
329 //SDL_RenderFillRect(ren, &rect);
330
331 //int alpha = (1.0 - game.map.at(x,y).visibility) * 255;
332 //SDL_SetRenderDrawColor(ren, 40, 40, 40, 255);
333 SDL_RenderFillRect(ren, &rect);
334 }
335 }
336 }
337
338 texture_ptr mask(
339 SDL_CreateTexture(
340 ren,
341 SDL_PIXELFORMAT_RGBA8888,
342 SDL_TEXTUREACCESS_TARGET,
343 TILE_WIDTH * game.map.getWidth(),
344 TILE_HEIGHT * game.map.getHeight()));
345
346 if (!mask)
347 {
348 throw sdl_error();
349 }
350
351 SDL_SetRenderTarget(ren, mask.get());
352 SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
353 SDL_RenderClear(ren);
354
355 for (int y = game.map.getTop(); y < game.map.getBottom(); y++)
356 {
357 for (int x = game.map.getLeft(); x < game.map.getRight(); x++)
358 {
359 if (game.map.at(x,y).lightType != Source::None)
360 {
361 texture_ptr sourceMask(
362 SDL_CreateTexture(
363 ren,
364 SDL_PIXELFORMAT_RGBA8888,
365 SDL_TEXTUREACCESS_TARGET,
366 TILE_WIDTH * game.map.getWidth(),
367 TILE_HEIGHT * game.map.getHeight()));
368
369 if (!sourceMask)
370 {
371 throw sdl_error();
372 }
373
374 SDL_SetRenderTarget(ren, sourceMask.get());
375 SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE);
376 SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
377 SDL_RenderClear(ren);
378
379 int fadeX = game.map.getTrueX(x) - game.map.at(x,y).lightRadius;
380 int fadeY = game.map.getTrueY(y) - game.map.at(x,y).lightRadius;
381 int fadeRight = game.map.getTrueX(x) + game.map.at(x,y).lightRadius;
382 int fadeBottom = game.map.getTrueY(y) + game.map.at(x,y).lightRadius;
383
384 SDL_Rect fadeRect {
385 fadeX * TILE_WIDTH,
386 fadeY * TILE_HEIGHT,
387 (game.map.at(x,y).lightRadius * 2 + 1) * TILE_WIDTH,
388 (game.map.at(x,y).lightRadius * 2 + 1) * TILE_HEIGHT};
389
390 if (game.map.at(x,y).lightType == Source::Lamp)
391 {
392 SDL_SetTextureBlendMode(lampFade.get(), SDL_BLENDMODE_NONE);
393 SDL_RenderCopy(ren, lampFade.get(), nullptr, &fadeRect);
394 } else if (game.map.at(x,y).lightType == Source::Player) {
395 SDL_SetTextureBlendMode(playerFade.get(), SDL_BLENDMODE_NONE);
396 SDL_RenderCopy(ren, playerFade.get(), nullptr, &fadeRect);
397 } else if (game.map.at(x,y).lightType == Source::Dust) {
398 SDL_SetTextureBlendMode(dustFade.get(), SDL_BLENDMODE_NONE);
399 SDL_RenderCopy(ren, dustFade.get(), nullptr, &fadeRect);
400 }
401
402 SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
403
404 for (int sy = fadeY; sy < fadeBottom; sy++)
405 {
406 for (int sx = fadeX; sx < fadeRight; sx++)
407 {
408 if (!game.map.at(x,y).litTiles.count({sx, sy}))
409 {
410 SDL_Rect rect {
411 game.map.getTrueX(sx) * TILE_WIDTH,
412 game.map.getTrueY(sy) * TILE_HEIGHT,
413 TILE_WIDTH,
414 TILE_HEIGHT};
415
416 SDL_RenderFillRect(ren, &rect);
417 }
418 }
419 }
420
421 SDL_SetRenderTarget(ren, mask.get());
422 SDL_SetTextureBlendMode(sourceMask.get(), SDL_BLENDMODE_ADD);
423 SDL_RenderCopy(ren, sourceMask.get(), nullptr, nullptr);
424 }
425 }
426 }
427
428 SDL_SetRenderTarget(ren, canvas.get());
429 SDL_SetTextureBlendMode(mask.get(), SDL_BLENDMODE_MOD);
430 SDL_RenderCopy(ren, mask.get(), nullptr, nullptr);
431
432 SDL_SetRenderTarget(ren, nullptr);
433
434 if (!game.zooming)
435 {
436 SDL_RenderCopy(ren, canvas.get(), nullptr, nullptr);
437 } else {
438 // TODO: zooming back in to the player
439 SDL_Rect zoomRect {
440 ((game.maxZoom - game.curZoom) * TILE_WIDTH + game.zoomProgress)
441 * ZOOM_X_FACTOR / 2,
442 ((game.maxZoom - game.curZoom) * TILE_HEIGHT + game.zoomProgress)
443 * ZOOM_Y_FACTOR / 2,
444 (game.curZoom * TILE_WIDTH - game.zoomProgress) * ZOOM_X_FACTOR,
445 (game.curZoom * TILE_HEIGHT - game.zoomProgress) * ZOOM_Y_FACTOR
446 };
447
448 SDL_RenderCopy(ren, canvas.get(), &zoomRect, nullptr);
449 }
450
451 SDL_RenderPresent(ren);
452}
453 7
454void incrementIfSet(Game& game, int& count, int x, int y, Tile val = Tile::Wall) 8void incrementIfSet(Game& game, int& count, int x, int y, Tile val = Tile::Wall)
455{ 9{
@@ -543,7 +97,7 @@ void movePlayer(Game& game, int x, int y)
543 } 97 }
544} 98}
545 99
546void recalculateLighting(Game& game, fov_settings_type* fov) 100void recalculateLighting(Game& game)
547{ 101{
548 game.litSpots = 0; 102 game.litSpots = 0;
549 103
@@ -551,12 +105,14 @@ void recalculateLighting(Game& game, fov_settings_type* fov)
551 { 105 {
552 md.wasLit = md.lit; 106 md.wasLit = md.lit;
553 md.lit = false; 107 md.lit = false;
554 md.visibility = 0.0;
555 md.litTiles.clear(); 108 md.litTiles.clear();
556 } 109 }
557 110
111 fov_settings_type fov;
112 fov_settings_init(&fov);
113
558 fov_settings_set_opacity_test_function( 114 fov_settings_set_opacity_test_function(
559 fov, 115 &fov,
560 [] (void* data, int x, int y) { 116 [] (void* data, int x, int y) {
561 Game& game = *static_cast<Game*>(data); 117 Game& game = *static_cast<Game*>(data);
562 118
@@ -564,7 +120,7 @@ void recalculateLighting(Game& game, fov_settings_type* fov)
564 }); 120 });
565 121
566 fov_settings_set_apply_lighting_function( 122 fov_settings_set_apply_lighting_function(
567 fov, 123 &fov,
568 [] (void* data, int x, int y, int dx, int dy, void* source) { 124 [] (void* data, int x, int y, int dx, int dy, void* source) {
569 Game& game = *static_cast<Game*>(data); 125 Game& game = *static_cast<Game*>(data);
570 126
@@ -580,21 +136,7 @@ void recalculateLighting(Game& game, fov_settings_type* fov)
580 136
581 game.map.at(x,y).lit = true; 137 game.map.at(x,y).lit = true;
582 138
583 /*game.map.at(x,y).visibility = std::max(
584 game.map.at(x,y).visibility,
585 std::pow(
586 std::max(
587 0.0,
588 1.0 - std::sqrt(dx * dx + dy * dy) / lightRadius),
589 1.0/3.0));*/
590
591 sourceData.litTiles.emplace(x,y); 139 sourceData.litTiles.emplace(x,y);
592
593 //Source ls = *static_cast<Source*>(source);
594 //if (static_cast<size_t>(ls) > static_cast<size_t>(m.lightSource[x+VIEW_WIDTH*y]))
595 {
596 //m.lightSource[x+VIEW_WIDTH*y] = ls;
597 }
598 } 140 }
599 }); 141 });
600 142
@@ -620,7 +162,6 @@ void recalculateLighting(Game& game, fov_settings_type* fov)
620 } 162 }
621 163
622 game.map.at(x,y).lightType = ls; 164 game.map.at(x,y).lightType = ls;
623 //game.map.at(x,y).litTiles.clear();
624 165
625 if (ls != Source::None) 166 if (ls != Source::None)
626 { 167 {
@@ -628,7 +169,7 @@ void recalculateLighting(Game& game, fov_settings_type* fov)
628 game.map.at(x,y).litTiles.emplace(x,y); 169 game.map.at(x,y).litTiles.emplace(x,y);
629 170
630 fov_circle( 171 fov_circle(
631 fov, 172 &fov,
632 static_cast<void*>(&game), 173 static_cast<void*>(&game),
633 static_cast<void*>(&game.map.at(x,y)), 174 static_cast<void*>(&game.map.at(x,y)),
634 x, 175 x,
@@ -636,7 +177,6 @@ void recalculateLighting(Game& game, fov_settings_type* fov)
636 lightRadius); 177 lightRadius);
637 178
638 game.map.at(x,y).lit = true; 179 game.map.at(x,y).lit = true;
639 game.map.at(x,y).visibility = 1.0;
640 } 180 }
641 } 181 }
642 } 182 }
@@ -832,44 +372,12 @@ int main(int, char**)
832 std::random_device randomEngine; 372 std::random_device randomEngine;
833 std::mt19937 rng(randomEngine()); 373 std::mt19937 rng(randomEngine());
834 374
835 if (SDL_Init(SDL_INIT_VIDEO) != 0)
836 {
837 throw sdl_error();
838 }
839
840 if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
841 {
842 throw img_error();
843 }
844
845 try 375 try
846 { 376 {
847 window_ptr win( 377 Renderer renderer;
848 SDL_CreateWindow("Ether", 100, 100, GAME_WIDTH, GAME_HEIGHT, SDL_WINDOW_SHOWN));
849
850 if (!win)
851 {
852 throw sdl_error();
853 }
854
855 renderer_ptr ren(
856 SDL_CreateRenderer(
857 win.get(),
858 -1,
859 SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC));
860
861 if (!ren)
862 {
863 throw sdl_error();
864 }
865
866 SDL_SetRenderDrawBlendMode(ren.get(), SDL_BLENDMODE_BLEND);
867 378
868 Game game(rng); 379 Game game(rng);
869 380
870 std::unique_ptr<fov_settings_type> fov(new fov_settings_type());
871 fov_settings_init(fov.get());
872
873 for (MapData& md : game.map.data()) 381 for (MapData& md : game.map.data())
874 { 382 {
875 if (std::bernoulli_distribution(0.5)(rng)) 383 if (std::bernoulli_distribution(0.5)(rng))
@@ -1091,7 +599,7 @@ int main(int, char**)
1091 599
1092 if (game.dirtyLighting) 600 if (game.dirtyLighting)
1093 { 601 {
1094 recalculateLighting(game, fov.get()); 602 recalculateLighting(game);
1095 603
1096 for (int y = game.map.getTop(); y < game.map.getBottom(); y++) 604 for (int y = game.map.getTop(); y < game.map.getBottom(); y++)
1097 { 605 {
@@ -1140,15 +648,15 @@ int main(int, char**)
1140 zoomAcc -= zoomDt; 648 zoomAcc -= zoomDt;
1141 } 649 }
1142 650
1143 render(ren.get(), game, true); 651 renderer.render(game, true);
1144 } 652 }
1145 } catch (const sdl_error& ex) 653 } catch (const sdl_error& ex)
1146 { 654 {
1147 std::cout << "SDL error (" << ex.what() << ")" << std::endl; 655 std::cout << "SDL error (" << ex.what() << ")" << std::endl;
656 } catch (const img_error& ex)
657 {
658 std::cout << "SDL_IMG error (" << ex.what() << ")" << std::endl;
1148 } 659 }
1149 660
1150 IMG_Quit();
1151 SDL_Quit();
1152
1153 return 0; 661 return 0;
1154} 662}
diff --git a/src/renderer.cpp b/src/renderer.cpp new file mode 100644 index 0000000..eddd11d --- /dev/null +++ b/src/renderer.cpp
@@ -0,0 +1,309 @@
1#include "renderer.h"
2#include "game.h"
3
4Renderer::Renderer()
5{
6 win_ = window_ptr(
7 SDL_CreateWindow(
8 "Ether",
9 100,
10 100,
11 GAME_WIDTH,
12 GAME_HEIGHT,
13 SDL_WINDOW_SHOWN));
14
15 if (!win_)
16 {
17 throw sdl_error();
18 }
19
20 ren_ = renderer_ptr(
21 SDL_CreateRenderer(
22 win_.get(),
23 -1,
24 SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC));
25
26 if (!ren_)
27 {
28 throw sdl_error();
29 }
30
31 texture_ptr origFade;
32 {
33 surface_ptr pfs(IMG_Load("../res/lighting.png"));
34 if (!pfs)
35 {
36 throw img_error();
37 }
38
39 origFade = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), pfs.get()));
40 }
41
42 SDL_SetTextureBlendMode(origFade.get(), SDL_BLENDMODE_BLEND);
43
44 playerFade_ = texture_ptr(
45 SDL_CreateTexture(
46 ren_.get(),
47 SDL_PIXELFORMAT_RGBA4444,
48 SDL_TEXTUREACCESS_TARGET,
49 144,
50 144));
51
52 if (!playerFade_)
53 {
54 throw sdl_error();
55 }
56
57 SDL_SetRenderTarget(ren_.get(), playerFade_.get());
58 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_NONE);
59 SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0);
60 SDL_RenderClear(ren_.get());
61 SDL_RenderCopy(ren_.get(), origFade.get(), nullptr, nullptr);
62
63 lampFade_ = texture_ptr(
64 SDL_CreateTexture(
65 ren_.get(),
66 SDL_PIXELFORMAT_RGBA4444,
67 SDL_TEXTUREACCESS_TARGET,
68 144,
69 144));
70
71 if (!lampFade_)
72 {
73 throw sdl_error();
74 }
75
76 SDL_SetRenderTarget(ren_.get(), lampFade_.get());
77
78 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_NONE);
79 SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0);
80 SDL_RenderClear(ren_.get());
81 SDL_RenderCopy(ren_.get(), origFade.get(), nullptr, nullptr);
82
83 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_MOD);
84 SDL_SetRenderDrawColor(ren_.get(), 255, 204, 58, 255);
85 SDL_RenderFillRect(ren_.get(), nullptr);
86
87 dustFade_ = texture_ptr(
88 SDL_CreateTexture(
89 ren_.get(),
90 SDL_PIXELFORMAT_RGBA4444,
91 SDL_TEXTUREACCESS_TARGET,
92 144,
93 144));
94
95 if (!dustFade_)
96 {
97 throw sdl_error();
98 }
99
100 SDL_SetRenderTarget(ren_.get(), dustFade_.get());
101
102 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_NONE);
103 SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0);
104 SDL_RenderClear(ren_.get());
105 SDL_RenderCopy(ren_.get(), origFade.get(), nullptr, nullptr);
106
107 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_MOD);
108 SDL_SetRenderDrawColor(ren_.get(), 255, 150, 255, 255);
109 SDL_RenderFillRect(ren_.get(), nullptr);
110}
111
112void Renderer::render(
113 const Game& game,
114 bool drawDark)
115{
116 texture_ptr canvas(
117 SDL_CreateTexture(
118 ren_.get(),
119 SDL_PIXELFORMAT_RGBA8888,
120 SDL_TEXTUREACCESS_TARGET,
121 TILE_WIDTH * game.map.getWidth(),
122 TILE_HEIGHT * game.map.getHeight()));
123
124 if (!canvas)
125 {
126 throw sdl_error();
127 }
128
129 SDL_SetRenderTarget(ren_.get(), canvas.get());
130 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_NONE);
131 SDL_SetRenderDrawColor(ren_.get(), rand() % 255, rand() % 255, rand() % 255, 255);
132 SDL_RenderClear(ren_.get());
133
134 for (int y = game.map.getTop(); y < game.map.getBottom(); y++)
135 {
136 for (int x = game.map.getLeft(); x < game.map.getRight(); x++)
137 {
138 bool draw = true;
139
140 if ((game.player_x == x && game.player_y == y) && game.renderPlayer)
141 {
142 SDL_SetRenderDrawColor(ren_.get(), 255, 255, 0, 255);
143 } else if (!game.map.at(x,y).lit)
144 {
145 if (drawDark)
146 {
147 SDL_SetRenderDrawColor(ren_.get(), 40, 40, 40, 255);
148 } else {
149 draw = false;
150 }
151 } else {
152 int alpha = 255;
153
154 switch (game.map.at(x,y).tile)
155 {
156 case Tile::Floor:
157 {
158 SDL_SetRenderDrawColor(ren_.get(), 210, 210, 210, alpha);
159 break;
160 }
161
162 case Tile::Wall:
163 {
164 SDL_SetRenderDrawColor(ren_.get(), 100, 100, 100, alpha);
165 break;
166 }
167
168 case Tile::Dust:
169 {
170 SDL_SetRenderDrawColor(ren_.get(), 128, 40, 255, alpha);
171 break;
172 }
173
174 case Tile::Lamp:
175 {
176 SDL_SetRenderDrawColor(ren_.get(), 0, 255, 255, alpha);
177 break;
178 }
179 }
180 }
181
182 if (draw)
183 {
184 SDL_Rect rect {
185 game.map.getTrueX(x) * TILE_WIDTH,
186 game.map.getTrueY(y) * TILE_HEIGHT,
187 TILE_WIDTH,
188 TILE_HEIGHT};
189
190 SDL_RenderFillRect(ren_.get(), &rect);
191 }
192 }
193 }
194
195 texture_ptr mask(
196 SDL_CreateTexture(
197 ren_.get(),
198 SDL_PIXELFORMAT_RGBA8888,
199 SDL_TEXTUREACCESS_TARGET,
200 TILE_WIDTH * game.map.getWidth(),
201 TILE_HEIGHT * game.map.getHeight()));
202
203 if (!mask)
204 {
205 throw sdl_error();
206 }
207
208 SDL_SetRenderTarget(ren_.get(), mask.get());
209 SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0);
210 SDL_RenderClear(ren_.get());
211
212 for (int y = game.map.getTop(); y < game.map.getBottom(); y++)
213 {
214 for (int x = game.map.getLeft(); x < game.map.getRight(); x++)
215 {
216 if (game.map.at(x,y).lightType != Source::None)
217 {
218 texture_ptr sourceMask(
219 SDL_CreateTexture(
220 ren_.get(),
221 SDL_PIXELFORMAT_RGBA8888,
222 SDL_TEXTUREACCESS_TARGET,
223 TILE_WIDTH * game.map.getWidth(),
224 TILE_HEIGHT * game.map.getHeight()));
225
226 if (!sourceMask)
227 {
228 throw sdl_error();
229 }
230
231 SDL_SetRenderTarget(ren_.get(), sourceMask.get());
232 SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_NONE);
233 SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0);
234 SDL_RenderClear(ren_.get());
235
236 int fadeX = game.map.getTrueX(x) - game.map.at(x,y).lightRadius;
237 int fadeY = game.map.getTrueY(y) - game.map.at(x,y).lightRadius;
238 int fadeRight = game.map.getTrueX(x) + game.map.at(x,y).lightRadius;
239 int fadeBottom = game.map.getTrueY(y) + game.map.at(x,y).lightRadius;
240
241 SDL_Rect fadeRect {
242 fadeX * TILE_WIDTH,
243 fadeY * TILE_HEIGHT,
244 (game.map.at(x,y).lightRadius * 2 + 1) * TILE_WIDTH,
245 (game.map.at(x,y).lightRadius * 2 + 1) * TILE_HEIGHT};
246
247 if (game.map.at(x,y).lightType == Source::Lamp)
248 {
249 SDL_SetTextureBlendMode(lampFade_.get(), SDL_BLENDMODE_NONE);
250 SDL_RenderCopy(ren_.get(), lampFade_.get(), nullptr, &fadeRect);
251 } else if (game.map.at(x,y).lightType == Source::Player) {
252 SDL_SetTextureBlendMode(playerFade_.get(), SDL_BLENDMODE_NONE);
253 SDL_RenderCopy(ren_.get(), playerFade_.get(), nullptr, &fadeRect);
254 } else if (game.map.at(x,y).lightType == Source::Dust) {
255 SDL_SetTextureBlendMode(dustFade_.get(), SDL_BLENDMODE_NONE);
256 SDL_RenderCopy(ren_.get(), dustFade_.get(), nullptr, &fadeRect);
257 }
258
259 SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0);
260
261 for (int sy = fadeY; sy < fadeBottom; sy++)
262 {
263 for (int sx = fadeX; sx < fadeRight; sx++)
264 {
265 if (!game.map.at(x,y).litTiles.count({sx, sy}))
266 {
267 SDL_Rect rect {
268 game.map.getTrueX(sx) * TILE_WIDTH,
269 game.map.getTrueY(sy) * TILE_HEIGHT,
270 TILE_WIDTH,
271 TILE_HEIGHT};
272
273 SDL_RenderFillRect(ren_.get(), &rect);
274 }
275 }
276 }
277
278 SDL_SetRenderTarget(ren_.get(), mask.get());
279 SDL_SetTextureBlendMode(sourceMask.get(), SDL_BLENDMODE_ADD);
280 SDL_RenderCopy(ren_.get(), sourceMask.get(), nullptr, nullptr);
281 }
282 }
283 }
284
285 SDL_SetRenderTarget(ren_.get(), canvas.get());
286 SDL_SetTextureBlendMode(mask.get(), SDL_BLENDMODE_MOD);
287 SDL_RenderCopy(ren_.get(), mask.get(), nullptr, nullptr);
288
289 SDL_SetRenderTarget(ren_.get(), nullptr);
290
291 if (!game.zooming)
292 {
293 SDL_RenderCopy(ren_.get(), canvas.get(), nullptr, nullptr);
294 } else {
295 // TODO: zooming back in to the player
296 SDL_Rect zoomRect {
297 ((game.maxZoom - game.curZoom) * TILE_WIDTH + game.zoomProgress)
298 * ZOOM_X_FACTOR / 2,
299 ((game.maxZoom - game.curZoom) * TILE_HEIGHT + game.zoomProgress)
300 * ZOOM_Y_FACTOR / 2,
301 (game.curZoom * TILE_WIDTH - game.zoomProgress) * ZOOM_X_FACTOR,
302 (game.curZoom * TILE_HEIGHT - game.zoomProgress) * ZOOM_Y_FACTOR
303 };
304
305 SDL_RenderCopy(ren_.get(), canvas.get(), &zoomRect, nullptr);
306 }
307
308 SDL_RenderPresent(ren_.get());
309}
diff --git a/src/renderer.h b/src/renderer.h new file mode 100644 index 0000000..4aa27bb --- /dev/null +++ b/src/renderer.h
@@ -0,0 +1,132 @@
1#ifndef RENDERER_H_6A58EC30
2#define RENDERER_H_6A58EC30
3
4#include <SDL.h>
5#include <SDL_image.h>
6#include <stdexcept>
7#include <memory>
8
9class Game;
10
11class sdl_error : public std::logic_error {
12public:
13
14 sdl_error() : std::logic_error(SDL_GetError())
15 {
16 }
17};
18
19class img_error : public std::logic_error {
20public:
21
22 img_error() : std::logic_error(IMG_GetError())
23 {
24 }
25};
26
27class sdl_wrapper {
28public:
29
30 sdl_wrapper()
31 {
32 if (SDL_Init(SDL_INIT_VIDEO) != 0)
33 {
34 sdl_error ex;
35 SDL_Quit();
36
37 throw ex;
38 }
39 }
40
41 ~sdl_wrapper()
42 {
43 SDL_Quit();
44 }
45};
46
47class img_wrapper {
48public:
49
50 img_wrapper()
51 {
52 if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
53 {
54 img_error ex;
55 IMG_Quit();
56
57 throw ex;
58 }
59 }
60
61 ~img_wrapper()
62 {
63 IMG_Quit();
64 }
65};
66
67class window_deleter {
68public:
69
70 void operator()(SDL_Window* ptr)
71 {
72 SDL_DestroyWindow(ptr);
73 }
74};
75
76using window_ptr = std::unique_ptr<SDL_Window, window_deleter>;
77
78class renderer_deleter {
79public:
80
81 void operator()(SDL_Renderer* ptr)
82 {
83 SDL_DestroyRenderer(ptr);
84 }
85};
86
87using renderer_ptr = std::unique_ptr<SDL_Renderer, renderer_deleter>;
88
89class surface_deleter {
90public:
91
92 void operator()(SDL_Surface* ptr)
93 {
94 SDL_FreeSurface(ptr);
95 }
96};
97
98using surface_ptr = std::unique_ptr<SDL_Surface, surface_deleter>;
99
100class texture_deleter {
101public:
102
103 void operator()(SDL_Texture* ptr)
104 {
105 SDL_DestroyTexture(ptr);
106 }
107};
108
109using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>;
110
111class Renderer {
112public:
113
114 Renderer();
115
116 void render(
117 const Game& game,
118 bool drawDark = true);
119
120private:
121
122 sdl_wrapper sdl_;
123 img_wrapper img_;
124 window_ptr win_;
125 renderer_ptr ren_;
126
127 texture_ptr playerFade_;
128 texture_ptr lampFade_;
129 texture_ptr dustFade_;
130};
131
132#endif /* end of include guard: RENDERER_H_6A58EC30 */