summary refs log tree commit diff stats
path: root/src/game.cpp
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2022-03-13 13:21:17 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2022-03-13 13:21:17 -0400
commit016d1cdf036039792f50e1ed0431386c7b9e93d7 (patch)
treee5f773f34b05c4fd8c73247eae9d20514fa450df /src/game.cpp
parentb2dfe21775a55e815b2572d844c749c5108671fe (diff)
downloadether-016d1cdf036039792f50e1ed0431386c7b9e93d7.tar.gz
ether-016d1cdf036039792f50e1ed0431386c7b9e93d7.tar.bz2
ether-016d1cdf036039792f50e1ed0431386c7b9e93d7.zip
refactored game code into the game class (for titles / multiple games)
Diffstat (limited to 'src/game.cpp')
-rw-r--r--src/game.cpp885
1 files changed, 885 insertions, 0 deletions
diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..8de9b7b --- /dev/null +++ b/src/game.cpp
@@ -0,0 +1,885 @@
1#include "game.h"
2#include <vector>
3#include <fov.h>
4#include <iostream>
5#include "util.h"
6#include "renderer.h"
7
8Game::Game(std::mt19937& rng, Muxer& muxer) :
9 rng(rng),
10 muxer(muxer),
11 map(
12 -INIT_ZOOM * ZOOM_X_FACTOR / 2,
13 -INIT_ZOOM * ZOOM_Y_FACTOR / 2,
14 INIT_ZOOM * ZOOM_X_FACTOR,
15 INIT_ZOOM * ZOOM_Y_FACTOR)
16{
17 losePopLampTimer.accumulate(losePopLampTimer.getDt());
18
19 for (MapData& md : map.data())
20 {
21 if (std::bernoulli_distribution(0.5)(rng))
22 {
23 md.tile = Tile::Wall;
24 }
25 }
26
27 tick();
28 tick();
29
30 for (int y = -1; y <= 1; y++)
31 {
32 for (int x = -1; x <= 1; x++)
33 {
34 map.at(x,y).tile = Tile::Floor;
35 }
36 }
37
38 tick();
39}
40
41inline bool isTileSetOrNotLit(const Map<MapData>& map, int x, int y)
42{
43 return (map.inBounds(x, y) && (map.at(x,y).tile == Tile::Wall || !map.at(x,y).lit));
44}
45
46inline bool isTileSet(const Map<MapData>& map, int x, int y, Tile val = Tile::Wall)
47{
48 return (map.inBounds(x, y) && map.at(x,y).tile == val);
49}
50
51inline void incrementIfSet(const Game& game, int& count, int x, int y, Tile val = Tile::Wall)
52{
53 if (isTileSet(game.map, x, y, val))
54 {
55 count++;
56 }
57}
58
59inline int getZoomLevel(const Game& game) {
60 return game.curZoom - INIT_ZOOM;
61}
62
63void Game::tick(
64 int x1,
65 int y1,
66 int x2,
67 int y2,
68 bool invert,
69 bool onlyDark)
70{
71 Map<MapData> temp(map);
72
73 for (int y = map.getTop(); y < map.getBottom(); y++)
74 {
75 for (int x = map.getLeft(); x < map.getRight(); x++)
76 {
77 if (invert == (x >= x1 && x < x2 && y >= y1 && y < y2))
78 {
79 continue;
80 }
81
82 if (onlyDark && map.at(x,y).lit)
83 {
84 continue;
85 }
86
87 if (map.at(x,y).tile == Tile::Lamp)
88 {
89 continue;
90 }
91
92 int count = 0;
93
94 incrementIfSet(*this, count, x-1, y-1);
95 incrementIfSet(*this, count, x-1, y );
96 incrementIfSet(*this, count, x-1, y+1);
97 incrementIfSet(*this, count, x , y-1);
98 incrementIfSet(*this, count, x , y );
99 incrementIfSet(*this, count, x , y+1);
100 incrementIfSet(*this, count, x+1, y-1);
101 incrementIfSet(*this, count, x+1, y );
102 incrementIfSet(*this, count, x+1, y+1);
103
104 if (count >= 5)
105 {
106 temp.at(x,y).tile = Tile::Wall;
107 } else {
108 temp.at(x,y).tile = Tile::Floor;
109 }
110
111 if (temp.at(x,y).tile != map.at(x,y).tile) {
112 temp.at(x,y).dirtyRender = true;
113 dirtyRender = true;
114 }
115 }
116 }
117
118 map = std::move(temp);
119}
120
121void Game::tick(bool onlyDark)
122{
123 tick(
124 map.getLeft(),
125 map.getTop(),
126 map.getRight(),
127 map.getBottom(),
128 false,
129 onlyDark);
130}
131
132bool Game::movePlayer(int x, int y)
133{
134 if (x >= curBoundX &&
135 y >= curBoundY &&
136 x < curBoundX + curZoom * ZOOM_X_FACTOR &&
137 y < curBoundY + curZoom * ZOOM_Y_FACTOR &&
138 map.at(x,y).tile == Tile::Floor)
139 {
140 if (map.at(player_x, player_y).tile == Tile::Floor)
141 {
142 map.at(player_x, player_y).tile = Tile::Dust;
143 map.at(player_x, player_y).dustLife = 1;
144 numDust++;
145 }
146
147 player_oldx = player_x;
148 player_oldy = player_y;
149 player_x = x;
150 player_y = y;
151 muxer.setPlayerLoc(x, y);
152 moving = true;
153 moveProgress.start(66);
154 dirtyLighting = true;
155
156 return true;
157 } else {
158 if (!alreadyBumped) {
159 muxer.playSoundAtPosition("bump", x, y);
160 alreadyBumped = true;
161 bumpCooldown.reset();
162 }
163
164 return false;
165 }
166}
167
168void Game::recalculateLighting()
169{
170 litSpots = 0;
171 dirtyRender = true;
172
173 for (MapData& md : map.data())
174 {
175 md.wasLit = md.lit;
176 md.lit = false;
177 md.litTiles.clear();
178
179 if (md.tile == Tile::Wall) {
180 md.dirtyRender = true;
181 }
182 }
183
184 fov_settings_type fov;
185 fov_settings_init(&fov);
186
187 fov_settings_set_opacity_test_function(
188 &fov,
189 [] (void* data, int x, int y) {
190 Game& game = *static_cast<Game*>(data);
191
192 return game.map.inBounds(x,y) && game.map.at(x,y).tile == Tile::Wall;
193 });
194
195 fov_settings_set_apply_lighting_function(
196 &fov,
197 [] (void* data, int x, int y, int dx, int dy, void* source) {
198 Game& game = *static_cast<Game*>(data);
199
200 if (game.map.inBounds(x, y))
201 {
202 MapData& sourceData = *static_cast<MapData*>(source);
203 double lightRadius = static_cast<double>(sourceData.lightRadius);
204
205 if (!game.map.at(x,y).lit)
206 {
207 game.litSpots++;
208 }
209
210 game.map.at(x,y).lit = true;
211
212 sourceData.litTiles.emplace(x,y);
213 }
214 });
215
216 for (int y = map.getTop(); y < map.getBottom(); y++)
217 {
218 for (int x = map.getLeft(); x < map.getRight(); x++)
219 {
220 Source ls = Source::None;
221 int lightRadius;
222
223 if (renderPlayer && player_x == x && player_y == y)
224 {
225 ls = Source::Player;
226 lightRadius = RADIUS;
227 } else if (map.at(x,y).tile == Tile::Dust)
228 {
229 ls = Source::Dust;
230 lightRadius = 2;
231 } else if (map.at(x,y).tile == Tile::Lamp)
232 {
233 ls = Source::Lamp;
234 lightRadius = RADIUS;
235 }
236
237 map.at(x,y).lightType = ls;
238
239 if (ls != Source::None)
240 {
241 map.at(x,y).lightRadius = lightRadius;
242 map.at(x,y).litTiles.emplace(x,y);
243
244 fov_circle(
245 &fov,
246 static_cast<void*>(this),
247 static_cast<void*>(&map.at(x,y)),
248 x,
249 y,
250 lightRadius);
251
252 map.at(x,y).lit = true;
253 }
254 }
255 }
256
257 dirtyLighting = false;
258}
259
260void Game::recalculateRender() {
261 for (int y = map.getTop(); y < map.getBottom(); y++)
262 {
263 for (int x = map.getLeft(); x < map.getRight(); x++)
264 {
265 if (map.at(x,y).dirtyRender) {
266 map.at(x,y).dirtyRender = false;
267
268 if (map.at(x,y).tile == Tile::Floor) {
269 if (std::bernoulli_distribution(0.05)(rng)) {
270 static const std::vector<int> furnishings {
271 TilesetIndex(20, 16),
272 TilesetIndex(21, 2),
273 TilesetIndex(22, 2),
274 TilesetIndex(21, 3),
275 TilesetIndex(22, 3)};
276 map.at(x,y).renderId = furnishings.at(std::uniform_int_distribution<int>(0, furnishings.size()-1)(rng));
277 } else {
278 map.at(x,y).renderId = -1;
279 }
280 } else if (map.at(x,y).tile == Tile::Wall) {
281 static bool initWalls = false;
282 static std::vector<int> wallRenders(256, TilesetIndex(0, 0));
283 if (!initWalls) {
284 initWalls = true;
285
286 // Start in top left and go clockwise.
287 wallRenders[0b00011100] = TilesetIndex(16, 14);
288 wallRenders[0b00111100] = TilesetIndex(16, 14);
289 wallRenders[0b00011110] = TilesetIndex(16, 14);
290 wallRenders[0b00111110] = TilesetIndex(16, 14);
291
292 wallRenders[0b00011111] = TilesetIndex(17, 14);
293 wallRenders[0b10011111] = TilesetIndex(17, 14);
294 wallRenders[0b00111111] = TilesetIndex(17, 14);
295 wallRenders[0b10111111] = TilesetIndex(17, 14);
296
297 wallRenders[0b00000111] = TilesetIndex(18, 14);
298 wallRenders[0b00001111] = TilesetIndex(18, 14);
299 wallRenders[0b10000111] = TilesetIndex(18, 14);
300 wallRenders[0b10001111] = TilesetIndex(18, 14);
301
302 wallRenders[0b01111100] = TilesetIndex(16, 15);
303 wallRenders[0b11111100] = TilesetIndex(16, 15);
304 wallRenders[0b01111110] = TilesetIndex(16, 15);
305 wallRenders[0b11111110] = TilesetIndex(16, 15);
306
307 wallRenders[0b11000111] = TilesetIndex(18, 15);
308 wallRenders[0b11001111] = TilesetIndex(18, 15);
309 wallRenders[0b11100111] = TilesetIndex(18, 15);
310 wallRenders[0b11101111] = TilesetIndex(18, 15);
311
312 wallRenders[0b01110000] = TilesetIndex(16, 16);
313 wallRenders[0b01111000] = TilesetIndex(16, 16);
314 wallRenders[0b11110000] = TilesetIndex(16, 16);
315 wallRenders[0b11111000] = TilesetIndex(16, 16);
316
317 wallRenders[0b11110001] = TilesetIndex(17, 16);
318 wallRenders[0b11110011] = TilesetIndex(17, 16);
319 wallRenders[0b11111001] = TilesetIndex(17, 16);
320 wallRenders[0b11111011] = TilesetIndex(17, 16);
321
322 wallRenders[0b11000001] = TilesetIndex(18, 16);
323 wallRenders[0b11000011] = TilesetIndex(18, 16);
324 wallRenders[0b11100001] = TilesetIndex(18, 16);
325 wallRenders[0b11100011] = TilesetIndex(18, 16);
326
327
328 wallRenders[0b11110111] = TilesetIndex(21, 14);
329 wallRenders[0b11111101] = TilesetIndex(22, 14);
330 wallRenders[0b11011111] = TilesetIndex(21, 15);
331 wallRenders[0b01111111] = TilesetIndex(22, 15);
332 }
333
334 int renderDesc = 0;
335 if (isTileSetOrNotLit(map, x-1, y-1)) renderDesc |= (1 << 7);
336 if (isTileSetOrNotLit(map, x , y-1)) renderDesc |= (1 << 6);
337 if (isTileSetOrNotLit(map, x+1, y-1)) renderDesc |= (1 << 5);
338 if (isTileSetOrNotLit(map, x+1, y )) renderDesc |= (1 << 4);
339 if (isTileSetOrNotLit(map, x+1, y+1)) renderDesc |= (1 << 3);
340 if (isTileSetOrNotLit(map, x , y+1)) renderDesc |= (1 << 2);
341 if (isTileSetOrNotLit(map, x-1, y+1)) renderDesc |= (1 << 1);
342 if (isTileSetOrNotLit(map, x-1, y )) renderDesc |= (1 << 0);
343
344 map.at(x,y).renderId = wallRenders.at(renderDesc);
345
346 if (wallRenders.at(renderDesc) == 0 && renderDesc != 255) {
347 std::cout << renderDesc << std::endl;
348 }
349 }
350 }
351 }
352 }
353
354 dirtyRender = false;
355}
356
357bool Game::processKeys(const Input& keystate)
358{
359 int px = player_x;
360 int py = player_y;
361 Direction dir = Direction::up;
362
363 if (keystate.up)
364 {
365 py--;
366 }
367
368 if (keystate.down)
369 {
370 py++;
371 dir = Direction::down;
372 }
373
374 if (keystate.left)
375 {
376 px--;
377 dir = Direction::left;
378 }
379
380 if (keystate.right)
381 {
382 px++;
383 dir = Direction::right;
384 }
385
386 if (!(player_x == px && player_y == py))
387 {
388 playerAnim.setAnimation("walk");
389 playerAnim.setDirection(dir);
390
391 bool succeeds = movePlayer(px, py);
392 if (!succeeds && px != player_x) {
393 succeeds = movePlayer(px, player_y);
394 }
395 if (!succeeds && py != player_y) {
396 succeeds = movePlayer(player_x, py);
397 }
398 return succeeds;
399 } else {
400 return false;
401 }
402}
403
404void Game::kickUpDust(int x, int y, size_t chain)
405{
406 Kickup dk;
407 dk.x = x;
408 dk.y = y;
409 dk.chain = chain;
410 dk.cur = 0;
411 dk.radius = RADIUS + (chain + 1) * (chain + 1);
412 dk.front.emplace(x, y);
413 dk.done.emplace(x, y);
414
415 kickups.push_back(dk);
416}
417
418void Game::popLamp(int x, int y, size_t chain)
419{
420 muxer.playSoundAtPosition("pop", x, y);
421
422 if (map.at(x,y).tile == Tile::Lamp)
423 {
424 numLamps--;
425 }
426
427 map.at(x,y).tile = Tile::Dust;
428 map.at(x,y).dustLife = 2;
429 numDust++;
430 dirtyLighting = true;
431
432 kickUpDust(x, y, chain);
433}
434
435void Game::processKickup()
436{
437 for (Kickup& kickup : kickups)
438 {
439 kickup.cur++;
440
441 std::set<coord> newFront;
442 for (const coord& xy : kickup.front)
443 {
444 auto processDir = [&] (int x, int y) {
445 coord c {x,y};
446
447 if (map.inBounds(x,y) &&
448 (map.at(x,y).tile == Tile::Floor || map.at(x,y).tile == Tile::Lamp) &&
449 !kickup.done.count(c))
450 {
451 newFront.insert(c);
452 kickup.done.insert(c);
453
454 if (map.at(x,y).tile == Tile::Floor)
455 {
456 map.at(x,y).tile = Tile::Dust;
457 map.at(x,y).dustLife = 2;
458 numDust++;
459 dirtyLighting = true;
460 } else if (map.at(x,y).tile == Tile::Lamp)
461 {
462 popLamp(x, y, kickup.chain + 1);
463 }
464 }
465 };
466
467 processDir(std::get<0>(xy) - 1, std::get<1>(xy) );
468 processDir(std::get<0>(xy) + 1, std::get<1>(xy) );
469 processDir(std::get<0>(xy) , std::get<1>(xy) - 1);
470 processDir(std::get<0>(xy) , std::get<1>(xy) + 1);
471
472 if (std::bernoulli_distribution(0.5)(rng))
473 {
474 processDir(std::get<0>(xy) - 1, std::get<1>(xy) - 1);
475 }
476
477 if (std::bernoulli_distribution(0.5)(rng))
478 {
479 processDir(std::get<0>(xy) - 1, std::get<1>(xy) + 1);
480 }
481
482 if (std::bernoulli_distribution(0.5)(rng))
483 {
484 processDir(std::get<0>(xy) + 1, std::get<1>(xy) - 1);
485 }
486
487 if (std::bernoulli_distribution(0.5)(rng))
488 {
489 processDir(std::get<0>(xy) + 1, std::get<1>(xy) + 1);
490 }
491 }
492
493 kickup.front.swap(newFront);
494 }
495
496 erase_if(
497 kickups,
498 [] (const Kickup& kickup) {
499 return kickup.cur == kickup.radius;
500 });
501}
502
503void Game::growMap(size_t zoom)
504{
505 int ol = map.getLeft();
506 int ot = map.getTop();
507 int ow = map.getWidth();
508 int oh = map.getHeight();
509
510 map.resize(
511 -zoom * ZOOM_X_FACTOR / 2,
512 -zoom * ZOOM_Y_FACTOR / 2,
513 zoom * ZOOM_X_FACTOR,
514 zoom * ZOOM_Y_FACTOR);
515
516 maxZoom = zoom;
517
518 for (int y = map.getTop(); y < map.getBottom(); y++)
519 {
520 for (int x = map.getLeft(); x < map.getRight(); x++)
521 {
522 if (!(x >= ol && x < (ol + ow) && y >= ot && y < (ot + oh)))
523 {
524 if (std::bernoulli_distribution(0.5)(rng))
525 {
526 map.at(x,y).tile = Tile::Wall;
527 }
528 }
529 }
530 }
531
532 for (int i = 0; i < 3; i++)
533 {
534 tick(ol, ot, ol + ow, ot + oh, true);
535 }
536}
537
538void Game::setZoom(size_t zoom)
539{
540 if (zoom == curZoom)
541 {
542 return;
543 }
544
545 if (zoom > maxZoom)
546 {
547 growMap(zoom);
548 }
549
550 std::tie(
551 lastZoomLeft,
552 lastZoomTop,
553 lastZoomWidth,
554 lastZoomHeight) =
555 Renderer::calculateZoomRect(*this);
556
557 zoomProgress = 0;
558 zoomLength = std::abs(static_cast<long>(zoom - curZoom)) * TILE_WIDTH;
559 curZoom = zoom;
560 zooming = true;
561
562 curBoundX = player_x - zoom * ZOOM_X_FACTOR / 2;
563 if (curBoundX < map.getLeft())
564 {
565 curBoundX = map.getLeft();
566 } else if (curBoundX + zoom * ZOOM_X_FACTOR >= map.getRight())
567 {
568 curBoundX = map.getRight() - zoom * ZOOM_X_FACTOR;
569 }
570
571 curBoundY = player_y - zoom * ZOOM_Y_FACTOR / 2;
572 if (curBoundY < map.getTop())
573 {
574 curBoundY = map.getTop();
575 } else if (curBoundY + zoom * ZOOM_Y_FACTOR >= map.getBottom())
576 {
577 curBoundY = map.getBottom() - zoom * ZOOM_Y_FACTOR;
578 }
579
580 int zoomLevel = getZoomLevel(*this);
581 if (zoomLevel == 0) {
582 muxer.setMusicLevel(0);
583 } else if (zoomLevel < 3) {
584 muxer.setMusicLevel(1);
585 } else if (zoomLevel < 5) {
586 muxer.setMusicLevel(2);
587 } else if (zoomLevel < 7) {
588 muxer.setMusicLevel(3);
589 } else {
590 muxer.setMusicLevel(4);
591 }
592}
593
594void Game::performDash() {
595 if (map.at(player_x, player_y).tile == Tile::Floor) {
596 std::vector<coord> freeSpaces;
597
598 auto addIfFree = [&] (int x, int y) {
599 if (map.inBounds(x,y) && map.at(x,y).tile == Tile::Floor)
600 {
601 freeSpaces.emplace_back(x, y);
602 }
603 };
604
605 addIfFree(player_x - 1, player_y - 1);
606 addIfFree(player_x , player_y - 1);
607 addIfFree(player_x + 1, player_y - 1);
608 addIfFree(player_x - 1, player_y );
609 addIfFree(player_x + 1, player_y );
610 addIfFree(player_x - 1, player_y + 1);
611 addIfFree(player_x , player_y + 1);
612 addIfFree(player_x + 1, player_y + 1);
613
614 if (!freeSpaces.empty())
615 {
616 map.at(player_x, player_y).tile = Tile::Lamp;
617 numLamps++;
618 dirtyLighting = true;
619 kickUpDust(player_x, player_y, 0);
620 muxer.playSoundAtPosition("drop", player_x, player_y);
621
622 if (firstInput)
623 {
624 for (int i = 0; i < 5; i++)
625 {
626 if (!processKeys(lastInput))
627 {
628 std::uniform_int_distribution<int> freeDist(0, freeSpaces.size() - 1);
629
630 int freeIndex = freeDist(rng);
631 coord& moveTo = freeSpaces[freeIndex];
632
633 movePlayer(std::get<0>(moveTo), std::get<1>(moveTo));
634 }
635
636 tick(
637 player_x - (RADIUS - 1),
638 player_y - (RADIUS - 1),
639 player_x + RADIUS,
640 player_y + RADIUS);
641 }
642 } else {
643 std::uniform_int_distribution<int> freeDist(0, freeSpaces.size() - 1);
644
645 int freeIndex = freeDist(rng);
646 coord& moveTo = freeSpaces[freeIndex];
647
648 movePlayer(std::get<0>(moveTo), std::get<1>(moveTo));
649 }
650
651 //muxer.playSoundAtPosition("dash", player_x, player_y);
652 }
653 }
654}
655
656void Game::update(size_t frameTime) {
657 SDL_Event e;
658
659 while (SDL_PollEvent(&e))
660 {
661 if (e.type == SDL_QUIT)
662 {
663 if (losing != LoseState::None)
664 {
665 quit = true;
666 } else {
667 losing = LoseState::PoppingLamps;
668 muxer.stopMusic();
669 }
670 } else if (e.type == SDL_KEYDOWN)
671 {
672 switch (e.key.keysym.sym)
673 {
674 case SDLK_ESCAPE:
675 {
676 if (losing != LoseState::None)
677 {
678 quit = true;
679 } else {
680 losing = LoseState::PoppingLamps;
681 muxer.stopMusic();
682 }
683
684 break;
685 }
686
687 case SDLK_SPACE:
688 {
689 if (losing == LoseState::None)
690 {
691 if (moving) {
692 queueDash = true;
693 } else {
694 performDash();
695 }
696 }
697
698 break;
699 }
700 }
701 }
702 }
703
704 const Uint8* state = SDL_GetKeyboardState(NULL);
705 keystate.left = state[SDL_SCANCODE_LEFT];
706 keystate.right = state[SDL_SCANCODE_RIGHT];
707 keystate.up = state[SDL_SCANCODE_UP];
708 keystate.down = state[SDL_SCANCODE_DOWN];
709
710 bumpCooldown.accumulate(frameTime);
711 if (alreadyBumped && keystate != lastInput && bumpCooldown.step()) {
712 alreadyBumped = false;
713 }
714
715 if (queueDash && !moving) {
716 queueDash = false;
717 performDash();
718 }
719
720 if (keystate.left || keystate.right || keystate.up || keystate.down)
721 {
722 firstInput = true;
723 lastInput = keystate;
724 } else if (losing == LoseState::None) {
725 playerAnim.setAnimation("still");
726 }
727
728 dustTimer.accumulate(frameTime);
729 inputTimer.accumulate(frameTime);
730
731 while (dustTimer.step())
732 {
733 numDust = 0;
734
735 for (MapData& md : map.data())
736 {
737 if (md.tile == Tile::Dust)
738 {
739 md.dustLife--;
740
741 if (md.dustLife <= 0)
742 {
743 md.tile = Tile::Floor;
744 dirtyLighting = true;
745 } else {
746 numDust++;
747 }
748 }
749 }
750
751 processKickup();
752 }
753
754 switch (losing)
755 {
756 case LoseState::None:
757 {
758 if (moving) {
759 moveProgress.tick(frameTime);
760 if (moveProgress.isComplete()) {
761 moving = false;
762 }
763 }
764
765 while (inputTimer.step())
766 {
767 if (!moving) {
768 processKeys(keystate);
769 }
770 }
771
772 break;
773 }
774
775 case LoseState::PoppingLamps:
776 {
777 if (numLamps == 0)
778 {
779 if (numDust == 0)
780 {
781 losing = LoseState::PoppingPlayer;
782 }
783 } else {
784 losePopLampTimer.accumulate(frameTime);
785
786 while (losePopLampTimer.step())
787 {
788 std::vector<std::tuple<int, int>> lamps;
789
790 for (int y = map.getTop(); y < map.getBottom(); y++)
791 {
792 for (int x = map.getLeft(); x < map.getRight(); x++)
793 {
794 if (map.at(x,y).tile == Tile::Lamp)
795 {
796 lamps.emplace_back(x, y);
797 }
798 }
799 }
800
801 std::uniform_int_distribution<int> lampDist(0, lamps.size() - 1);
802 std::tuple<int, int> popPos = lamps[lampDist(rng)];
803
804 popLamp(std::get<0>(popPos), std::get<1>(popPos), 1);
805 }
806 }
807
808 break;
809 }
810
811 case LoseState::PoppingPlayer:
812 {
813 losePopPlayerTimer.accumulate(frameTime);
814
815 if (losePopPlayerTimer.step())
816 {
817 popLamp(player_x, player_y, 10);
818 renderPlayer = false;
819
820 losing = LoseState::Outro;
821 }
822
823 break;
824 }
825
826 case LoseState::Outro:
827 {
828 if (numDust == 0)
829 {
830 quit = true;
831 }
832
833 break;
834 }
835 }
836
837 if (dirtyLighting)
838 {
839 recalculateLighting();
840
841 for (int y = map.getTop(); y < map.getBottom(); y++)
842 {
843 for (int x = map.getLeft(); x < map.getRight(); x++)
844 {
845 if (!map.at(x,y).lit && map.at(x,y).wasLit)
846 {
847 if (std::bernoulli_distribution(0.5)(rng))
848 {
849 map.at(x,y).tile = Tile::Wall;
850 } else {
851 map.at(x,y).tile = Tile::Floor;
852 }
853 map.at(x,y).dirtyRender = true;
854 }
855 }
856 }
857
858 tick(true);
859 tick(true);
860 tick(true);
861
862 // TODO: better zoom algorithm
863 setZoom(litSpots / 1500 + INIT_ZOOM);
864 }
865
866 if (dirtyRender) {
867 recalculateRender();
868 }
869
870 zoomTimer.accumulate(frameTime);
871 while (zoomTimer.step())
872 {
873 if (zooming)
874 {
875 zoomProgress++;
876
877 if (zoomProgress == zoomLength)
878 {
879 zooming = false;
880 }
881 }
882 }
883
884 playerAnim.update(frameTime);
885}