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