diff options
Diffstat (limited to 'src/game.cpp')
-rw-r--r-- | src/game.cpp | 885 |
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 | |||
8 | Game::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 | |||
41 | inline 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 | |||
46 | inline 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 | |||
51 | inline 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 | |||
59 | inline int getZoomLevel(const Game& game) { | ||
60 | return game.curZoom - INIT_ZOOM; | ||
61 | } | ||
62 | |||
63 | void 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 | |||
121 | void 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 | |||
132 | bool 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 | |||
168 | void 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 | |||
260 | void 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 | |||
357 | bool 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 | |||
404 | void 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 | |||
418 | void 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 | |||
435 | void 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 | |||
503 | void 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 | |||
538 | void 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 | |||
594 | void 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 | |||
656 | void 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 | } | ||