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 /src/game.cpp | |
| 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)
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 | } | ||
