diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2fc610b --- /dev/null +++ b/src/main.cpp | |||
@@ -0,0 +1,549 @@ | |||
1 | #include <SDL.h> | ||
2 | #include <stdexcept> | ||
3 | #include <memory> | ||
4 | #include <vector> | ||
5 | #include <random> | ||
6 | #include <fov.h> | ||
7 | #include <deque> | ||
8 | |||
9 | class sdl_error : public std::logic_error { | ||
10 | public: | ||
11 | |||
12 | sdl_error() : std::logic_error(SDL_GetError()) | ||
13 | { | ||
14 | } | ||
15 | }; | ||
16 | |||
17 | class window_deleter { | ||
18 | public: | ||
19 | |||
20 | void operator()(SDL_Window* ptr) | ||
21 | { | ||
22 | SDL_DestroyWindow(ptr); | ||
23 | } | ||
24 | }; | ||
25 | |||
26 | using window_ptr = std::unique_ptr<SDL_Window, window_deleter>; | ||
27 | |||
28 | class renderer_deleter { | ||
29 | public: | ||
30 | |||
31 | void operator()(SDL_Renderer* ptr) | ||
32 | { | ||
33 | SDL_DestroyRenderer(ptr); | ||
34 | } | ||
35 | }; | ||
36 | |||
37 | using renderer_ptr = std::unique_ptr<SDL_Renderer, renderer_deleter>; | ||
38 | |||
39 | enum class Tile { | ||
40 | Floor, | ||
41 | Wall, | ||
42 | Dark, | ||
43 | Dust, | ||
44 | Lamp | ||
45 | }; | ||
46 | |||
47 | const int GAME_WIDTH = 640; | ||
48 | const int GAME_HEIGHT = 480; | ||
49 | const int TILE_WIDTH = 8; | ||
50 | const int TILE_HEIGHT = 8; | ||
51 | const int VIEW_WIDTH = GAME_WIDTH / TILE_WIDTH; | ||
52 | const int VIEW_HEIGHT = GAME_HEIGHT / TILE_HEIGHT; | ||
53 | |||
54 | class Map { | ||
55 | public: | ||
56 | |||
57 | Map() : | ||
58 | tiles(VIEW_WIDTH*VIEW_HEIGHT, Tile::Floor), | ||
59 | lighting(VIEW_WIDTH*VIEW_HEIGHT, false) | ||
60 | { | ||
61 | } | ||
62 | |||
63 | std::vector<Tile> tiles; | ||
64 | std::vector<bool> lighting; | ||
65 | std::deque<std::tuple<int, int>> playerLocs; | ||
66 | }; | ||
67 | |||
68 | int player_x = VIEW_WIDTH / 2; | ||
69 | int player_y = VIEW_HEIGHT / 2; | ||
70 | |||
71 | void render( | ||
72 | SDL_Renderer* ren, | ||
73 | const Map& map, | ||
74 | bool drawDark = true) | ||
75 | { | ||
76 | SDL_SetRenderDrawColor(ren, rand() % 255, rand() % 255, rand() % 255, 255); | ||
77 | SDL_RenderClear(ren); | ||
78 | |||
79 | for (int y = 0; y < VIEW_HEIGHT; y++) | ||
80 | { | ||
81 | for (int x = 0; x < VIEW_WIDTH; x++) | ||
82 | { | ||
83 | bool draw = true; | ||
84 | |||
85 | if (player_x == x && player_y == y) | ||
86 | { | ||
87 | SDL_SetRenderDrawColor(ren, 255, 255, 0, 255); | ||
88 | } else if (!map.lighting.at(x+VIEW_WIDTH*y)) | ||
89 | { | ||
90 | if (drawDark) | ||
91 | { | ||
92 | SDL_SetRenderDrawColor(ren, 40, 40, 40, 255); | ||
93 | } else { | ||
94 | draw = false; | ||
95 | } | ||
96 | } else { | ||
97 | switch (map.tiles.at(x+y*VIEW_WIDTH)) | ||
98 | { | ||
99 | case Tile::Floor: | ||
100 | { | ||
101 | SDL_SetRenderDrawColor(ren, 210, 210, 210, 255); | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | case Tile::Wall: | ||
106 | case Tile::Dark: | ||
107 | { | ||
108 | SDL_SetRenderDrawColor(ren, 100, 100, 100, 255); | ||
109 | break; | ||
110 | } | ||
111 | |||
112 | case Tile::Dust: | ||
113 | { | ||
114 | SDL_SetRenderDrawColor(ren, 128, 40, 255, 255); | ||
115 | break; | ||
116 | } | ||
117 | |||
118 | case Tile::Lamp: | ||
119 | { | ||
120 | SDL_SetRenderDrawColor(ren, 0, 255, 255, 255); | ||
121 | break; | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | |||
127 | if (draw) | ||
128 | { | ||
129 | SDL_Rect rect{x*TILE_WIDTH, y*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT}; | ||
130 | SDL_RenderFillRect(ren, &rect); | ||
131 | } | ||
132 | |||
133 | |||
134 | } | ||
135 | } | ||
136 | |||
137 | SDL_RenderPresent(ren); | ||
138 | } | ||
139 | |||
140 | void incrementIfSet(Map& map, int& count, int x, int y, int w, int h, Tile val = Tile::Dark) | ||
141 | { | ||
142 | if ((x >= 0) && (x < w) && (y >= 0) && (y < h) && (map.tiles[x+w*y] == val)) | ||
143 | { | ||
144 | count++; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | void tick(Map& map, int x1 = 0, int y1 = 0, int x2 = VIEW_WIDTH, int y2 = VIEW_HEIGHT) | ||
149 | { | ||
150 | std::vector<Tile> temp(map.tiles); | ||
151 | |||
152 | for (int y = std::max(y1, 0); y < std::min(y2, VIEW_HEIGHT); y++) | ||
153 | { | ||
154 | for (int x = std::max(x1, 0); x < std::min(x2, VIEW_WIDTH); x++) | ||
155 | { | ||
156 | if (map.tiles[x+y*VIEW_WIDTH] == Tile::Lamp) | ||
157 | { | ||
158 | continue; | ||
159 | } | ||
160 | |||
161 | int count = 0; | ||
162 | |||
163 | incrementIfSet(map, count, x-1, y-1, VIEW_WIDTH, VIEW_HEIGHT); | ||
164 | incrementIfSet(map, count, x-1, y , VIEW_WIDTH, VIEW_HEIGHT); | ||
165 | incrementIfSet(map, count, x-1, y+1, VIEW_WIDTH, VIEW_HEIGHT); | ||
166 | incrementIfSet(map, count, x , y-1, VIEW_WIDTH, VIEW_HEIGHT); | ||
167 | incrementIfSet(map, count, x , y , VIEW_WIDTH, VIEW_HEIGHT); | ||
168 | incrementIfSet(map, count, x , y+1, VIEW_WIDTH, VIEW_HEIGHT); | ||
169 | incrementIfSet(map, count, x+1, y-1, VIEW_WIDTH, VIEW_HEIGHT); | ||
170 | incrementIfSet(map, count, x+1, y , VIEW_WIDTH, VIEW_HEIGHT); | ||
171 | incrementIfSet(map, count, x+1, y+1, VIEW_WIDTH, VIEW_HEIGHT); | ||
172 | |||
173 | if (count >= 5) | ||
174 | { | ||
175 | temp[x+VIEW_WIDTH*y] = Tile::Dark; | ||
176 | } else { | ||
177 | temp[x+VIEW_WIDTH*y] = Tile::Floor; | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | |||
182 | map.tiles = temp; | ||
183 | } | ||
184 | |||
185 | void movePlayer(int x, int y, Map& map) | ||
186 | { | ||
187 | if ((x >= 0) && (x < VIEW_WIDTH) && (y >= 0) && (y < VIEW_HEIGHT) && | ||
188 | map.tiles[x+VIEW_WIDTH*y] == Tile::Floor) | ||
189 | { | ||
190 | if (map.tiles[player_x+player_y*VIEW_WIDTH] == Tile::Floor) | ||
191 | { | ||
192 | map.tiles[player_x+player_y*VIEW_WIDTH] = Tile::Dust; | ||
193 | map.playerLocs.emplace_front(player_x, player_y); | ||
194 | |||
195 | if (map.playerLocs.size() > 5) | ||
196 | { | ||
197 | map.playerLocs.pop_back(); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | |||
202 | |||
203 | |||
204 | player_x = x; | ||
205 | player_y = y; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | void setIfValid(Map& map, int x, int y, Tile val) | ||
210 | { | ||
211 | if ((x >= 0) && (x < VIEW_WIDTH) && (y >= 0) && (y < VIEW_HEIGHT)) | ||
212 | { | ||
213 | map.tiles[x+VIEW_WIDTH*y] = val; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | void recalculateLighting(Map& map, fov_settings_type* fov) | ||
218 | { | ||
219 | map.lighting = std::vector<bool>(VIEW_WIDTH*VIEW_HEIGHT, false); | ||
220 | |||
221 | fov_settings_set_opacity_test_function( | ||
222 | fov, | ||
223 | [] (void* map, int x, int y) { | ||
224 | return | ||
225 | x >= 0 && | ||
226 | x < VIEW_WIDTH && | ||
227 | y >= 0 && | ||
228 | y < VIEW_HEIGHT && | ||
229 | static_cast<Map*>(map)->tiles.at(x+VIEW_WIDTH*y) == Tile::Dark; | ||
230 | }); | ||
231 | |||
232 | fov_settings_set_apply_lighting_function( | ||
233 | fov, | ||
234 | [] (void* map, int x, int y, int, int, void*) { | ||
235 | if ((x >= 0) && (x < VIEW_WIDTH) && (y >= 0) && (y < VIEW_HEIGHT)) | ||
236 | { | ||
237 | static_cast<Map*>(map)->lighting[x+VIEW_WIDTH*y] = true; | ||
238 | } | ||
239 | }); | ||
240 | |||
241 | for (int y = 0; y < VIEW_HEIGHT; y++) | ||
242 | { | ||
243 | for (int x = 0; x < VIEW_WIDTH; x++) | ||
244 | { | ||
245 | if ((player_x == x && player_y == y) || map.tiles[x+VIEW_WIDTH*y] == Tile::Dust || map.tiles[x+VIEW_WIDTH*y] == Tile::Lamp) | ||
246 | { | ||
247 | fov_circle(fov, static_cast<void*>(&map), nullptr, x, y, 8); | ||
248 | } | ||
249 | |||
250 | if (map.tiles[x+VIEW_WIDTH*y] == Tile::Lamp) | ||
251 | { | ||
252 | map.lighting[x+VIEW_WIDTH*y] = true; | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | int main(int, char**) | ||
259 | { | ||
260 | std::random_device randomEngine; | ||
261 | std::mt19937 rng(randomEngine()); | ||
262 | |||
263 | if (SDL_Init(SDL_INIT_VIDEO) != 0) | ||
264 | { | ||
265 | throw sdl_error(); | ||
266 | } | ||
267 | |||
268 | try | ||
269 | { | ||
270 | window_ptr win( | ||
271 | SDL_CreateWindow("Ether", 100, 100, GAME_WIDTH, GAME_HEIGHT, SDL_WINDOW_SHOWN)); | ||
272 | |||
273 | if (!win) | ||
274 | { | ||
275 | throw sdl_error(); | ||
276 | } | ||
277 | |||
278 | renderer_ptr ren( | ||
279 | SDL_CreateRenderer( | ||
280 | win.get(), | ||
281 | -1, | ||
282 | SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)); | ||
283 | |||
284 | if (!ren) | ||
285 | { | ||
286 | throw sdl_error(); | ||
287 | } | ||
288 | |||
289 | //std::vector<Tile> tiles(VIEW_WIDTH*VIEW_HEIGHT, Tile::Floor); | ||
290 | //std::vector<bool> lighting(VIEW_WIDTH*VIEW_HEIGHT, false); | ||
291 | Map map; | ||
292 | |||
293 | std::unique_ptr<fov_settings_type> fov(new fov_settings_type()); | ||
294 | fov_settings_init(fov.get()); | ||
295 | |||
296 | |||
297 | for (int y = 0; y < VIEW_HEIGHT; y++) | ||
298 | { | ||
299 | for (int x = 0; x < VIEW_WIDTH; x++) | ||
300 | { | ||
301 | if (std::bernoulli_distribution(0.5)(rng)) | ||
302 | { | ||
303 | map.tiles[x+y*VIEW_WIDTH] = Tile::Dark; | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | tick(map); | ||
309 | tick(map); | ||
310 | tick(map); | ||
311 | |||
312 | bool quit = false; | ||
313 | SDL_Event e; | ||
314 | while (!quit) | ||
315 | { | ||
316 | //SDL_PumpEvents(); | ||
317 | bool input = false; | ||
318 | int presses = 0; | ||
319 | while (SDL_PollEvent(&e)) | ||
320 | { | ||
321 | if (e.type == SDL_QUIT) | ||
322 | { | ||
323 | quit = true; | ||
324 | } else if (e.type == SDL_KEYDOWN) | ||
325 | { | ||
326 | presses++; | ||
327 | |||
328 | switch (e.key.keysym.sym) | ||
329 | { | ||
330 | case SDLK_SPACE: | ||
331 | { | ||
332 | input = true; | ||
333 | |||
334 | setIfValid(map, player_x-1, player_y , Tile::Floor); | ||
335 | setIfValid(map, player_x+1, player_y , Tile::Floor); | ||
336 | setIfValid(map, player_x , player_y , Tile::Lamp); | ||
337 | setIfValid(map, player_x , player_y-1, Tile::Floor); | ||
338 | setIfValid(map, player_x , player_y+1, Tile::Floor); | ||
339 | |||
340 | auto locs = map.playerLocs; | ||
341 | while (!locs.empty()) | ||
342 | { | ||
343 | movePlayer(std::get<0>(locs.front()), std::get<1>(locs.front()), map); | ||
344 | locs.pop_front(); | ||
345 | |||
346 | tick( | ||
347 | map, | ||
348 | player_x - 7, | ||
349 | player_y - 7, | ||
350 | player_x + 8, | ||
351 | player_y + 8); | ||
352 | |||
353 | render(ren.get(), map, false); | ||
354 | SDL_Delay(30); | ||
355 | } | ||
356 | |||
357 | break; | ||
358 | } | ||
359 | } | ||
360 | } else if (e.type == SDL_KEYUP) | ||
361 | { | ||
362 | presses++; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | if (presses > 0) | ||
367 | { | ||
368 | for (int y = 0; y < VIEW_HEIGHT; y++) | ||
369 | { | ||
370 | for (int x = 0; x < VIEW_WIDTH; x++) | ||
371 | { | ||
372 | if (map.tiles[x+y*VIEW_WIDTH] == Tile::Dust) | ||
373 | { | ||
374 | map.tiles[x+y*VIEW_WIDTH] = Tile::Floor; | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | } | ||
379 | |||
380 | const Uint8* state = SDL_GetKeyboardState(NULL); | ||
381 | |||
382 | for (int i = 0; i < presses; i++) | ||
383 | { | ||
384 | //switch (e.key.keysym.sym) | ||
385 | { | ||
386 | //case SDLK_UP: | ||
387 | if (state[SDL_SCANCODE_UP]) | ||
388 | { | ||
389 | movePlayer(player_x, player_y-1, map); | ||
390 | input = true; | ||
391 | //break; | ||
392 | } | ||
393 | |||
394 | //case SDLK_DOWN: | ||
395 | if (state[SDL_SCANCODE_DOWN]) | ||
396 | { | ||
397 | movePlayer(player_x, player_y+1, map); | ||
398 | input = true; | ||
399 | //break; | ||
400 | } | ||
401 | |||
402 | //case SDLK_LEFT: | ||
403 | if (state[SDL_SCANCODE_LEFT]) | ||
404 | { | ||
405 | movePlayer(player_x-1, player_y, map); | ||
406 | input = true; | ||
407 | //break; | ||
408 | } | ||
409 | |||
410 | //case SDLK_RIGHT: | ||
411 | if (state[SDL_SCANCODE_RIGHT]) | ||
412 | { | ||
413 | movePlayer(player_x+1, player_y, map); | ||
414 | input = true; | ||
415 | //break; | ||
416 | } | ||
417 | |||
418 | |||
419 | } | ||
420 | |||
421 | if (input) | ||
422 | { | ||
423 | //render(ren.get(), tiles, false); | ||
424 | //SDL_Delay(1); | ||
425 | } | ||
426 | |||
427 | //} | ||
428 | } | ||
429 | |||
430 | bool checkForDust = true; | ||
431 | |||
432 | while (checkForDust) | ||
433 | { | ||
434 | checkForDust = false; | ||
435 | |||
436 | for (int y = 0; y < VIEW_HEIGHT; y++) | ||
437 | { | ||
438 | for (int x = 0; x < VIEW_WIDTH; x++) | ||
439 | { | ||
440 | if (map.tiles[x+y*VIEW_WIDTH] == Tile::Lamp) | ||
441 | { | ||
442 | int count = 0; | ||
443 | |||
444 | incrementIfSet(map, count, x-1, y , VIEW_WIDTH, VIEW_HEIGHT, Tile::Dust); | ||
445 | incrementIfSet(map, count, x+1, y , VIEW_WIDTH, VIEW_HEIGHT, Tile::Dust); | ||
446 | incrementIfSet(map, count, x , y-1, VIEW_WIDTH, VIEW_HEIGHT, Tile::Dust); | ||
447 | incrementIfSet(map, count, x , y+1, VIEW_WIDTH, VIEW_HEIGHT, Tile::Dust); | ||
448 | |||
449 | if (count > 0) | ||
450 | { | ||
451 | checkForDust = true; | ||
452 | |||
453 | map.tiles[x+y*VIEW_WIDTH] = Tile::Dust; | ||
454 | |||
455 | /*for (int i = 0; i < 4; i++) | ||
456 | { | ||
457 | tick( | ||
458 | map, | ||
459 | x - 7, | ||
460 | y - 7, | ||
461 | x + 8, | ||
462 | y + 8); | ||
463 | |||
464 | for (int l = 0; l < (i*2+1); l++) | ||
465 | { | ||
466 | int px = x - i + l; | ||
467 | int py = y - i + l; | ||
468 | |||
469 | auto fillInDust = [&] (int sx, int sy) { | ||
470 | if (sx > 0 && sx < VIEW_WIDTH && | ||
471 | sy > 0 && sy < VIEW_HEIGHT && | ||
472 | map.tiles[sx+sy*VIEW_WIDTH] == Tile::Floor && | ||
473 | !(player_y == sy && player_x == sx)) | ||
474 | { | ||
475 | map.tiles[sx+sy*VIEW_WIDTH] = Tile::Dust; | ||
476 | } | ||
477 | }; | ||
478 | |||
479 | fillInDust(px , y - i); | ||
480 | fillInDust(px , y + i); | ||
481 | fillInDust(x - i, py ); | ||
482 | fillInDust(x + i, py ); | ||
483 | } | ||
484 | |||
485 | render(ren.get(), map, false); | ||
486 | SDL_Delay(30); | ||
487 | }*/ | ||
488 | |||
489 | |||
490 | /* | ||
491 | |||
492 | for (int py = std::max(0, y - 7); py < std::min(VIEW_HEIGHT, y + 8); py++) | ||
493 | { | ||
494 | for (int px = std::max(0, x - 7); px < std::min(VIEW_WIDTH, x + 8); px++) | ||
495 | { | ||
496 | if ((map.tiles[px+py*VIEW_WIDTH] == Tile::Floor) && | ||
497 | !(player_y == py && player_x == px)) | ||
498 | { | ||
499 | map.tiles[px+py*VIEW_WIDTH] = Tile::Dust; | ||
500 | } | ||
501 | } | ||
502 | }*/ | ||
503 | |||
504 | std::unique_ptr<fov_settings_type> dusty(new fov_settings_type); | ||
505 | fov_settings_set_opacity_test_function( | ||
506 | dusty.get(), | ||
507 | [] (void* map, int x, int y) { | ||
508 | return | ||
509 | x >= 0 && | ||
510 | x < VIEW_WIDTH && | ||
511 | y >= 0 && | ||
512 | y < VIEW_HEIGHT && | ||
513 | static_cast<Map*>(map)->tiles.at(x+VIEW_WIDTH*y) == Tile::Dark; | ||
514 | }); | ||
515 | |||
516 | fov_settings_set_apply_lighting_function( | ||
517 | dusty.get(), | ||
518 | [] (void* map, int x, int y, int, int, void*) { | ||
519 | if ((x >= 0) && (x < VIEW_WIDTH) && | ||
520 | (y >= 0) && (y < VIEW_HEIGHT) && | ||
521 | (static_cast<Map*>(map)->tiles[x+VIEW_WIDTH*y] == Tile::Floor)) | ||
522 | { | ||
523 | static_cast<Map*>(map)->tiles[x+VIEW_WIDTH*y] = Tile::Dust; | ||
524 | } | ||
525 | }); | ||
526 | |||
527 | fov_circle(dusty.get(), static_cast<void*>(&map), nullptr, x, y, 8); | ||
528 | |||
529 | render(ren.get(), map, false); | ||
530 | SDL_Delay(50); | ||
531 | } | ||
532 | } | ||
533 | } | ||
534 | } | ||
535 | } | ||
536 | |||
537 | |||
538 | recalculateLighting(map, fov.get()); | ||
539 | render(ren.get(), map, true); | ||
540 | SDL_Delay(10); | ||
541 | } | ||
542 | } catch (const sdl_error&) | ||
543 | { | ||
544 | } | ||
545 | |||
546 | SDL_Quit(); | ||
547 | |||
548 | return 0; | ||
549 | } \ No newline at end of file | ||