diff options
| author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-06-05 18:18:17 -0400 |
|---|---|---|
| committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-06-05 18:18:17 -0400 |
| commit | f838a08ef531ad643e28ced2c9b1ae5bd9d57765 (patch) | |
| tree | 3c2527f673118014da72c1703000c8dec3e24e06 /src | |
| parent | 8c9d046123f0978ea3f770838a2321d85ffda977 (diff) | |
| parent | 47c584d8a583bab3a71333e9993b72cf481bca2f (diff) | |
| download | ether-f838a08ef531ad643e28ced2c9b1ae5bd9d57765.tar.gz ether-f838a08ef531ad643e28ced2c9b1ae5bd9d57765.tar.bz2 ether-f838a08ef531ad643e28ced2c9b1ae5bd9d57765.zip | |
merge master into colorfade and improved it
overlapping colors look weird though
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 967 | ||||
| -rw-r--r-- | src/map.h | 127 | ||||
| -rw-r--r-- | src/untitled.txt | 0 | ||||
| -rw-r--r-- | src/util.h | 20 |
4 files changed, 858 insertions, 256 deletions
| diff --git a/src/main.cpp b/src/main.cpp index c02fdd5..dacc646 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -3,10 +3,14 @@ | |||
| 3 | #include <stdexcept> | 3 | #include <stdexcept> |
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | #include <list> | ||
| 6 | #include <random> | 7 | #include <random> |
| 7 | #include <fov.h> | 8 | #include <fov.h> |
| 8 | #include <deque> | ||
| 9 | #include <iostream> | 9 | #include <iostream> |
| 10 | #include <tuple> | ||
| 11 | #include <set> | ||
| 12 | #include "util.h" | ||
| 13 | #include "map.h" | ||
| 10 | 14 | ||
| 11 | class sdl_error : public std::logic_error { | 15 | class sdl_error : public std::logic_error { |
| 12 | public: | 16 | public: |
| @@ -82,12 +86,20 @@ enum class Source { | |||
| 82 | Player | 86 | Player |
| 83 | }; | 87 | }; |
| 84 | 88 | ||
| 89 | enum class LoseState { | ||
| 90 | None, | ||
| 91 | PoppingLamps, | ||
| 92 | PoppingPlayer, | ||
| 93 | Outro | ||
| 94 | }; | ||
| 95 | |||
| 85 | const int GAME_WIDTH = 640*2; | 96 | const int GAME_WIDTH = 640*2; |
| 86 | const int GAME_HEIGHT = 480*2; | 97 | const int GAME_HEIGHT = 480*2; |
| 87 | const int TILE_WIDTH = 8*2; | 98 | const int TILE_WIDTH = 8*2; |
| 88 | const int TILE_HEIGHT = 8*2; | 99 | const int TILE_HEIGHT = TILE_WIDTH; |
| 89 | const int VIEW_WIDTH = GAME_WIDTH / TILE_WIDTH; | 100 | const int INIT_ZOOM = 10; |
| 90 | const int VIEW_HEIGHT = GAME_HEIGHT / TILE_HEIGHT; | 101 | const int ZOOM_X_FACTOR = 8; |
| 102 | const int ZOOM_Y_FACTOR = 6; | ||
| 91 | const int RADIUS = 8; | 103 | const int RADIUS = 8; |
| 92 | 104 | ||
| 93 | struct Input { | 105 | struct Input { |
| @@ -97,34 +109,70 @@ struct Input { | |||
| 97 | bool down = false; | 109 | bool down = false; |
| 98 | }; | 110 | }; |
| 99 | 111 | ||
| 100 | class Map { | 112 | using coord = std::tuple<int, int>; |
| 113 | |||
| 114 | struct Kickup { | ||
| 115 | int x; | ||
| 116 | int y; | ||
| 117 | size_t cur; | ||
| 118 | size_t radius; | ||
| 119 | size_t chain; | ||
| 120 | std::set<coord> done; | ||
| 121 | std::set<coord> front; | ||
| 122 | }; | ||
| 123 | |||
| 124 | struct MapData { | ||
| 125 | Tile tile = Tile::Floor; | ||
| 126 | bool lit = false; | ||
| 127 | bool wasLit = false; | ||
| 128 | double visibility = 0.0; | ||
| 129 | size_t dustLife = 0; | ||
| 130 | Source lightType = Source::None; | ||
| 131 | int lightRadius = 0; | ||
| 132 | std::set<coord> litTiles; | ||
| 133 | }; | ||
| 134 | |||
| 135 | class Game { | ||
| 101 | public: | 136 | public: |
| 102 | 137 | ||
| 103 | Map() : | 138 | Game(std::mt19937& rng) : |
| 104 | tiles(VIEW_WIDTH*VIEW_HEIGHT, Tile::Floor), | 139 | rng(rng), |
| 105 | lighting(VIEW_WIDTH*VIEW_HEIGHT, false), | 140 | map( |
| 106 | lightStrength(VIEW_WIDTH*VIEW_HEIGHT, 0.0), | 141 | -INIT_ZOOM * ZOOM_X_FACTOR / 2, |
| 107 | lightSource(VIEW_WIDTH*VIEW_HEIGHT, Source::None) | 142 | -INIT_ZOOM * ZOOM_Y_FACTOR / 2, |
| 143 | INIT_ZOOM * ZOOM_X_FACTOR, | ||
| 144 | INIT_ZOOM * ZOOM_Y_FACTOR) | ||
| 108 | { | 145 | { |
| 109 | } | 146 | } |
| 110 | 147 | ||
| 111 | std::vector<Tile> tiles; | 148 | std::mt19937& rng; |
| 112 | std::vector<bool> lighting; | 149 | |
| 113 | std::vector<bool> oldLighting; | 150 | Map<MapData> map; |
| 114 | std::vector<double> lightStrength; | 151 | std::list<Kickup> kickups; |
| 115 | std::vector<Source> lightSource; | 152 | int litSpots = 0; |
| 116 | int lightedSpots = 0; | 153 | bool dirtyLighting = true; |
| 117 | }; | 154 | size_t numLamps = 0; |
| 155 | size_t numDust = 0; | ||
| 156 | |||
| 157 | int player_x = 0; | ||
| 158 | int player_y = 0; | ||
| 159 | bool renderPlayer = true; | ||
| 160 | |||
| 161 | int curZoom = INIT_ZOOM; | ||
| 162 | int maxZoom = INIT_ZOOM; | ||
| 118 | 163 | ||
| 119 | int player_x = VIEW_WIDTH / 2; | 164 | bool zooming = false; |
| 120 | int player_y = VIEW_HEIGHT / 2; | 165 | //size_t oldZoom; |
| 166 | int zoomProgress = 0; | ||
| 167 | |||
| 168 | }; | ||
| 121 | 169 | ||
| 122 | void render( | 170 | void render( |
| 123 | SDL_Renderer* ren, | 171 | SDL_Renderer* ren, |
| 124 | const Map& map, | 172 | const Game& game, |
| 125 | bool drawDark = true) | 173 | bool drawDark = true) |
| 126 | { | 174 | { |
| 127 | texture_ptr playerFade; | 175 | texture_ptr origFade; |
| 128 | { | 176 | { |
| 129 | surface_ptr pfs(IMG_Load("../res/lighting.png")); | 177 | surface_ptr pfs(IMG_Load("../res/lighting.png")); |
| 130 | if (!pfs) | 178 | if (!pfs) |
| @@ -132,9 +180,29 @@ void render( | |||
| 132 | throw img_error(); | 180 | throw img_error(); |
| 133 | } | 181 | } |
| 134 | 182 | ||
| 135 | playerFade = texture_ptr(SDL_CreateTextureFromSurface(ren, pfs.get())); | 183 | origFade = texture_ptr(SDL_CreateTextureFromSurface(ren, pfs.get())); |
| 184 | } | ||
| 185 | |||
| 186 | SDL_SetTextureBlendMode(origFade.get(), SDL_BLENDMODE_BLEND); | ||
| 187 | |||
| 188 | texture_ptr playerFade( | ||
| 189 | SDL_CreateTexture( | ||
| 190 | ren, | ||
| 191 | SDL_PIXELFORMAT_RGBA4444, | ||
| 192 | SDL_TEXTUREACCESS_TARGET, | ||
| 193 | 144, | ||
| 194 | 144)); | ||
| 195 | |||
| 196 | if (!playerFade) | ||
| 197 | { | ||
| 198 | throw sdl_error(); | ||
| 136 | } | 199 | } |
| 137 | 200 | ||
| 201 | SDL_SetRenderTarget(ren, playerFade.get()); | ||
| 202 | SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); | ||
| 203 | SDL_RenderClear(ren); | ||
| 204 | SDL_RenderCopy(ren, origFade.get(), nullptr, nullptr); | ||
| 205 | |||
| 138 | texture_ptr lampFade( | 206 | texture_ptr lampFade( |
| 139 | SDL_CreateTexture( | 207 | SDL_CreateTexture( |
| 140 | ren, | 208 | ren, |
| @@ -143,59 +211,83 @@ void render( | |||
| 143 | 144, | 211 | 144, |
| 144 | 144)); | 212 | 144)); |
| 145 | 213 | ||
| 214 | if (!lampFade) | ||
| 146 | { | 215 | { |
| 147 | SDL_SetRenderTarget(ren, lampFade.get()); | 216 | throw sdl_error(); |
| 217 | } | ||
| 148 | 218 | ||
| 149 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE); | 219 | SDL_SetRenderTarget(ren, lampFade.get()); |
| 150 | SDL_SetRenderDrawColor(ren, 255, 255, 255, 0); | ||
| 151 | SDL_RenderFillRect(ren, nullptr); | ||
| 152 | 220 | ||
| 153 | SDL_RenderCopy(ren, playerFade.get(), nullptr, nullptr); | 221 | SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); |
| 222 | SDL_RenderClear(ren); | ||
| 223 | SDL_RenderCopy(ren, origFade.get(), nullptr, nullptr); | ||
| 224 | |||
| 225 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_MOD); | ||
| 226 | SDL_SetRenderDrawColor(ren, 255, 204, 58, 255); | ||
| 227 | SDL_RenderFillRect(ren, nullptr); | ||
| 154 | 228 | ||
| 155 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_MOD); | 229 | texture_ptr dustFade( |
| 156 | SDL_SetRenderDrawColor(ren, 255, 180, 0, 255); | 230 | SDL_CreateTexture( |
| 157 | SDL_RenderFillRect(ren, nullptr); | 231 | ren, |
| 232 | SDL_PIXELFORMAT_RGBA4444, | ||
| 233 | SDL_TEXTUREACCESS_TARGET, | ||
| 234 | 144, | ||
| 235 | 144)); | ||
| 158 | 236 | ||
| 159 | SDL_SetRenderTarget(ren, nullptr); | 237 | if (!dustFade) |
| 238 | { | ||
| 239 | throw sdl_error(); | ||
| 160 | } | 240 | } |
| 161 | 241 | ||
| 162 | int darkR = 40; | 242 | SDL_SetRenderTarget(ren, dustFade.get()); |
| 163 | int darkG = 40; | 243 | |
| 164 | int darkB = 40; | 244 | SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); |
| 245 | SDL_RenderClear(ren); | ||
| 246 | SDL_RenderCopy(ren, origFade.get(), nullptr, nullptr); | ||
| 247 | |||
| 248 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_MOD); | ||
| 249 | SDL_SetRenderDrawColor(ren, 255, 150, 255, 255); | ||
| 250 | SDL_RenderFillRect(ren, nullptr); | ||
| 165 | 251 | ||
| 166 | if (!drawDark) | 252 | texture_ptr canvas( |
| 253 | SDL_CreateTexture( | ||
| 254 | ren, | ||
| 255 | SDL_PIXELFORMAT_RGBA8888, | ||
| 256 | SDL_TEXTUREACCESS_TARGET, | ||
| 257 | TILE_WIDTH * game.map.getWidth(), | ||
| 258 | TILE_HEIGHT * game.map.getHeight())); | ||
| 259 | |||
| 260 | if (!canvas) | ||
| 167 | { | 261 | { |
| 168 | darkR = rand() % 255; | 262 | throw sdl_error(); |
| 169 | darkG = rand() % 255; | ||
| 170 | darkB = rand() % 255; | ||
| 171 | } | 263 | } |
| 172 | 264 | ||
| 173 | SDL_SetRenderDrawColor(ren, darkR, darkG, darkB, 255); | 265 | SDL_SetRenderTarget(ren, canvas.get()); |
| 266 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE); | ||
| 267 | SDL_SetRenderDrawColor(ren, rand() % 255, rand() % 255, rand() % 255, 255); | ||
| 174 | SDL_RenderClear(ren); | 268 | SDL_RenderClear(ren); |
| 175 | 269 | ||
| 176 | for (int y = 0; y < VIEW_HEIGHT; y++) | 270 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) |
| 177 | { | 271 | { |
| 178 | for (int x = 0; x < VIEW_WIDTH; x++) | 272 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) |
| 179 | { | 273 | { |
| 180 | bool draw = true; | 274 | bool draw = true; |
| 181 | 275 | ||
| 182 | if (player_x == x && player_y == y) | 276 | if ((game.player_x == x && game.player_y == y) && game.renderPlayer) |
| 183 | { | 277 | { |
| 184 | SDL_SetRenderDrawColor(ren, 255, 255, 0, 255); | 278 | SDL_SetRenderDrawColor(ren, 255, 255, 0, 255); |
| 185 | } else if (!map.lighting.at(x+VIEW_WIDTH*y)) | 279 | /*} else if (!game.map.at(x,y).lit) |
| 186 | { | 280 | { |
| 187 | /*if (drawDark) | 281 | if (drawDark) |
| 188 | { | 282 | { |
| 189 | SDL_SetRenderDrawColor(ren, 40, 40, 40, 255); | 283 | SDL_SetRenderDrawColor(ren, 40, 40, 40, 255); |
| 190 | } else { | 284 | } else { |
| 191 | draw = false; | 285 | draw = false; |
| 192 | }*/ | 286 | }*/ |
| 193 | draw = false; | ||
| 194 | } else { | 287 | } else { |
| 195 | int alpha = map.lightStrength.at(x+y*VIEW_WIDTH) * 255; | 288 | int alpha = 255; |
| 196 | alpha = 255; | ||
| 197 | 289 | ||
| 198 | switch (map.tiles.at(x+y*VIEW_WIDTH)) | 290 | switch (game.map.at(x,y).tile) |
| 199 | { | 291 | { |
| 200 | case Tile::Floor: | 292 | case Tile::Floor: |
| 201 | { | 293 | { |
| @@ -225,216 +317,322 @@ void render( | |||
| 225 | 317 | ||
| 226 | if (draw) | 318 | if (draw) |
| 227 | { | 319 | { |
| 228 | SDL_Rect rect{x*TILE_WIDTH, y*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT}; | 320 | SDL_Rect rect { |
| 229 | 321 | game.map.getTrueX(x) * TILE_WIDTH, | |
| 230 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); | 322 | game.map.getTrueY(y) * TILE_HEIGHT, |
| 231 | SDL_RenderFillRect(ren, &rect); | 323 | TILE_WIDTH, |
| 324 | TILE_HEIGHT}; | ||
| 232 | 325 | ||
| 326 | //SDL_RenderFillRect(ren, &rect); | ||
| 233 | 327 | ||
| 328 | //int alpha = (1.0 - game.map.at(x,y).visibility) * 255; | ||
| 329 | //SDL_SetRenderDrawColor(ren, 40, 40, 40, 255); | ||
| 330 | SDL_RenderFillRect(ren, &rect); | ||
| 234 | } | 331 | } |
| 235 | } | 332 | } |
| 236 | } | 333 | } |
| 237 | 334 | ||
| 238 | for (int y = 0; y < VIEW_HEIGHT; y++) | 335 | texture_ptr mask( |
| 336 | SDL_CreateTexture( | ||
| 337 | ren, | ||
| 338 | SDL_PIXELFORMAT_RGBA8888, | ||
| 339 | SDL_TEXTUREACCESS_TARGET, | ||
| 340 | TILE_WIDTH * game.map.getWidth(), | ||
| 341 | TILE_HEIGHT * game.map.getHeight())); | ||
| 342 | |||
| 343 | if (!mask) | ||
| 344 | { | ||
| 345 | throw sdl_error(); | ||
| 346 | } | ||
| 347 | |||
| 348 | SDL_SetRenderTarget(ren, mask.get()); | ||
| 349 | SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); | ||
| 350 | SDL_RenderClear(ren); | ||
| 351 | |||
| 352 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) | ||
| 239 | { | 353 | { |
| 240 | for (int x = 0; x < VIEW_WIDTH; x++) | 354 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) |
| 241 | { | 355 | { |
| 242 | if (map.lightSource.at(x+VIEW_WIDTH*y) != Source::None) | 356 | if (game.map.at(x,y).lightType != Source::None) |
| 243 | { | 357 | { |
| 244 | SDL_Rect fadeRect{x*TILE_WIDTH + (TILE_WIDTH/2) - (144/2), y*TILE_HEIGHT + (TILE_HEIGHT/2) - (144/2), 144, 144}; | 358 | texture_ptr sourceMask( |
| 245 | if (map.lightSource.at(x+VIEW_WIDTH*y) == Source::Lamp) | 359 | SDL_CreateTexture( |
| 360 | ren, | ||
| 361 | SDL_PIXELFORMAT_RGBA8888, | ||
| 362 | SDL_TEXTUREACCESS_TARGET, | ||
| 363 | TILE_WIDTH * game.map.getWidth(), | ||
| 364 | TILE_HEIGHT * game.map.getHeight())); | ||
| 365 | |||
| 366 | if (!sourceMask) | ||
| 246 | { | 367 | { |
| 247 | //SDL_SetTextureBlendMode(lampFade.get(), SDL_BLENDMODE_MOD); | 368 | throw sdl_error(); |
| 248 | SDL_RenderCopy(ren, lampFade.get(), nullptr, &fadeRect); | 369 | } |
| 249 | //SDL_SetRenderDrawColor(ren, 255, 180, 0, 50); | 370 | |
| 250 | //SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); | 371 | SDL_SetRenderTarget(ren, sourceMask.get()); |
| 251 | //SDL_RenderFillRect(ren, &rect); | 372 | SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); |
| 252 | } else if (map.lightSource.at(x+VIEW_WIDTH*y) == Source::Player) | 373 | SDL_RenderClear(ren); |
| 374 | |||
| 375 | SDL_SetRenderDrawColor(ren, 255, 255, 255, 255); | ||
| 376 | |||
| 377 | for (const coord& xy : game.map.at(x,y).litTiles) | ||
| 253 | { | 378 | { |
| 254 | //SDL_SetTextureBlendMode(playerFade.get(), SDL_BLENDMODE_MOD); | 379 | SDL_Rect rect { |
| 380 | game.map.getTrueX(std::get<0>(xy)) * TILE_WIDTH, | ||
| 381 | game.map.getTrueY(std::get<1>(xy)) * TILE_HEIGHT, | ||
| 382 | TILE_WIDTH, | ||
| 383 | TILE_HEIGHT}; | ||
| 384 | |||
| 385 | SDL_RenderFillRect(ren, &rect); | ||
| 386 | } | ||
| 387 | |||
| 388 | SDL_Rect fadeRect { | ||
| 389 | (game.map.getTrueX(x) - game.map.at(x,y).lightRadius) * TILE_WIDTH, | ||
| 390 | (game.map.getTrueY(y) - game.map.at(x,y).lightRadius) * TILE_HEIGHT, | ||
| 391 | (game.map.at(x,y).lightRadius * 2 + 1) * TILE_WIDTH, | ||
| 392 | (game.map.at(x,y).lightRadius * 2 + 1) * TILE_HEIGHT}; | ||
| 393 | |||
| 394 | if (game.map.at(x,y).lightType == Source::Lamp) | ||
| 395 | { | ||
| 396 | SDL_SetTextureBlendMode(lampFade.get(), SDL_BLENDMODE_MOD); | ||
| 397 | SDL_RenderCopy(ren, lampFade.get(), nullptr, &fadeRect); | ||
| 398 | } else if (game.map.at(x,y).lightType == Source::Player) { | ||
| 399 | SDL_SetTextureBlendMode(playerFade.get(), SDL_BLENDMODE_MOD); | ||
| 255 | SDL_RenderCopy(ren, playerFade.get(), nullptr, &fadeRect); | 400 | SDL_RenderCopy(ren, playerFade.get(), nullptr, &fadeRect); |
| 401 | } else if (game.map.at(x,y).lightType == Source::Dust) { | ||
| 402 | SDL_SetTextureBlendMode(dustFade.get(), SDL_BLENDMODE_MOD); | ||
| 403 | SDL_RenderCopy(ren, dustFade.get(), nullptr, &fadeRect); | ||
| 256 | } | 404 | } |
| 257 | 405 | ||
| 258 | /*SDL_SetRenderDrawColor(ren, 40, 40, 40, alpha); | 406 | SDL_SetRenderTarget(ren, mask.get()); |
| 259 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); | 407 | SDL_SetTextureBlendMode(sourceMask.get(), SDL_BLENDMODE_ADD); |
| 260 | SDL_RenderFillRect(ren, &rect);*/ | 408 | SDL_RenderCopy(ren, sourceMask.get(), nullptr, nullptr); |
| 261 | } | 409 | } |
| 262 | } | 410 | } |
| 263 | } | 411 | } |
| 264 | 412 | ||
| 265 | SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE); | 413 | SDL_SetRenderTarget(ren, canvas.get()); |
| 266 | SDL_SetRenderDrawColor(ren, darkR, darkG, darkB, 255); | 414 | SDL_SetTextureBlendMode(mask.get(), SDL_BLENDMODE_MOD); |
| 415 | SDL_RenderCopy(ren, mask.get(), nullptr, nullptr); | ||
| 267 | 416 | ||
| 268 | for (int y = 0; y < VIEW_HEIGHT; y++) | 417 | SDL_SetRenderTarget(ren, nullptr); |
| 269 | { | ||
| 270 | for (int x = 0; x < VIEW_WIDTH; x++) | ||
| 271 | { | ||
| 272 | if (!map.lighting.at(x+VIEW_WIDTH*y)) | ||
| 273 | { | ||
| 274 | SDL_Rect rect{x*TILE_WIDTH, y*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT}; | ||
| 275 | 418 | ||
| 276 | SDL_RenderFillRect(ren, &rect); | 419 | if (!game.zooming) |
| 277 | } | 420 | { |
| 278 | } | 421 | SDL_RenderCopy(ren, canvas.get(), nullptr, nullptr); |
| 422 | } else { | ||
| 423 | // TODO: zooming back in to the player | ||
| 424 | SDL_Rect zoomRect { | ||
| 425 | ((game.maxZoom - game.curZoom) * TILE_WIDTH + game.zoomProgress) | ||
| 426 | * ZOOM_X_FACTOR / 2, | ||
| 427 | ((game.maxZoom - game.curZoom) * TILE_HEIGHT + game.zoomProgress) | ||
| 428 | * ZOOM_Y_FACTOR / 2, | ||
| 429 | (game.curZoom * TILE_WIDTH - game.zoomProgress) * ZOOM_X_FACTOR, | ||
| 430 | (game.curZoom * TILE_HEIGHT - game.zoomProgress) * ZOOM_Y_FACTOR | ||
| 431 | }; | ||
| 432 | |||
| 433 | SDL_RenderCopy(ren, canvas.get(), &zoomRect, nullptr); | ||
| 279 | } | 434 | } |
| 280 | 435 | ||
| 281 | SDL_RenderPresent(ren); | 436 | SDL_RenderPresent(ren); |
| 282 | } | 437 | } |
| 283 | 438 | ||
| 284 | void incrementIfSet(Map& map, int& count, int x, int y, int w, int h, Tile val = Tile::Wall) | 439 | void incrementIfSet(Game& game, int& count, int x, int y, Tile val = Tile::Wall) |
| 285 | { | 440 | { |
| 286 | if ((x >= 0) && (x < w) && (y >= 0) && (y < h) && (map.tiles[x+w*y] == val)) | 441 | if (game.map.inBounds(x, y) && game.map.at(x,y).tile == val) |
| 287 | { | 442 | { |
| 288 | count++; | 443 | count++; |
| 289 | } | 444 | } |
| 290 | } | 445 | } |
| 291 | 446 | ||
| 292 | void tick(Map& map, int x1 = 0, int y1 = 0, int x2 = VIEW_WIDTH, int y2 = VIEW_HEIGHT, bool onlyDark = false) | 447 | void tick( |
| 448 | Game& game, | ||
| 449 | int x1, | ||
| 450 | int y1, | ||
| 451 | int x2, | ||
| 452 | int y2, | ||
| 453 | bool invert = false, | ||
| 454 | bool onlyDark = false) | ||
| 293 | { | 455 | { |
| 294 | std::vector<Tile> temp(map.tiles); | 456 | Map<MapData> temp(game.map); |
| 295 | 457 | ||
| 296 | for (int y = std::max(y1, 0); y < std::min(y2, VIEW_HEIGHT); y++) | 458 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) |
| 297 | { | 459 | { |
| 298 | for (int x = std::max(x1, 0); x < std::min(x2, VIEW_WIDTH); x++) | 460 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) |
| 299 | { | 461 | { |
| 300 | if (onlyDark && map.lighting[x+y*VIEW_WIDTH]) | 462 | if (invert == (x >= x1 && x < x2 && y >= y1 && y < y2)) |
| 463 | { | ||
| 464 | continue; | ||
| 465 | } | ||
| 466 | |||
| 467 | if (onlyDark && game.map.at(x,y).lit) | ||
| 301 | { | 468 | { |
| 302 | continue; | 469 | continue; |
| 303 | } | 470 | } |
| 304 | 471 | ||
| 305 | if (map.tiles[x+y*VIEW_WIDTH] == Tile::Lamp) | 472 | if (game.map.at(x,y).tile == Tile::Lamp) |
| 306 | { | 473 | { |
| 307 | continue; | 474 | continue; |
| 308 | } | 475 | } |
| 309 | 476 | ||
| 310 | int count = 0; | 477 | int count = 0; |
| 311 | 478 | ||
| 312 | incrementIfSet(map, count, x-1, y-1, VIEW_WIDTH, VIEW_HEIGHT); | 479 | incrementIfSet(game, count, x-1, y-1); |
| 313 | incrementIfSet(map, count, x-1, y , VIEW_WIDTH, VIEW_HEIGHT); | 480 | incrementIfSet(game, count, x-1, y ); |
| 314 | incrementIfSet(map, count, x-1, y+1, VIEW_WIDTH, VIEW_HEIGHT); | 481 | incrementIfSet(game, count, x-1, y+1); |
| 315 | incrementIfSet(map, count, x , y-1, VIEW_WIDTH, VIEW_HEIGHT); | 482 | incrementIfSet(game, count, x , y-1); |
| 316 | incrementIfSet(map, count, x , y , VIEW_WIDTH, VIEW_HEIGHT); | 483 | incrementIfSet(game, count, x , y ); |
| 317 | incrementIfSet(map, count, x , y+1, VIEW_WIDTH, VIEW_HEIGHT); | 484 | incrementIfSet(game, count, x , y+1); |
| 318 | incrementIfSet(map, count, x+1, y-1, VIEW_WIDTH, VIEW_HEIGHT); | 485 | incrementIfSet(game, count, x+1, y-1); |
| 319 | incrementIfSet(map, count, x+1, y , VIEW_WIDTH, VIEW_HEIGHT); | 486 | incrementIfSet(game, count, x+1, y ); |
| 320 | incrementIfSet(map, count, x+1, y+1, VIEW_WIDTH, VIEW_HEIGHT); | 487 | incrementIfSet(game, count, x+1, y+1); |
| 321 | 488 | ||
| 322 | if (count >= 5) | 489 | if (count >= 5) |
| 323 | { | 490 | { |
| 324 | temp[x+VIEW_WIDTH*y] = Tile::Wall; | 491 | temp.at(x,y).tile = Tile::Wall; |
| 325 | } else { | 492 | } else { |
| 326 | temp[x+VIEW_WIDTH*y] = Tile::Floor; | 493 | temp.at(x,y).tile = Tile::Floor; |
| 327 | } | 494 | } |
| 328 | } | 495 | } |
| 329 | } | 496 | } |
| 330 | 497 | ||
| 331 | map.tiles = temp; | 498 | game.map = std::move(temp); |
| 332 | } | 499 | } |
| 333 | 500 | ||
| 334 | void movePlayer(int x, int y, Map& map) | 501 | void tick(Game& game, bool onlyDark = false) |
| 335 | { | 502 | { |
| 336 | if ((x >= 0) && (x < VIEW_WIDTH) && (y >= 0) && (y < VIEW_HEIGHT) && | 503 | tick( |
| 337 | map.tiles[x+VIEW_WIDTH*y] == Tile::Floor) | 504 | game, |
| 505 | game.map.getLeft(), | ||
| 506 | game.map.getTop(), | ||
| 507 | game.map.getRight(), | ||
| 508 | game.map.getBottom(), | ||
| 509 | false, | ||
| 510 | onlyDark); | ||
| 511 | } | ||
| 512 | |||
| 513 | void movePlayer(Game& game, int x, int y) | ||
| 514 | { | ||
| 515 | if (game.map.inBounds(x, y) && game.map.at(x,y).tile == Tile::Floor) | ||
| 338 | { | 516 | { |
| 339 | if (map.tiles[player_x+player_y*VIEW_WIDTH] == Tile::Floor) | 517 | if (game.map.at(game.player_x, game.player_y).tile == Tile::Floor) |
| 340 | { | 518 | { |
| 341 | map.tiles[player_x+player_y*VIEW_WIDTH] = Tile::Dust; | 519 | game.map.at(game.player_x, game.player_y).tile = Tile::Dust; |
| 520 | game.map.at(game.player_x, game.player_y).dustLife = 1; | ||
| 521 | game.numDust++; | ||
| 342 | } | 522 | } |
| 343 | 523 | ||
| 344 | player_x = x; | 524 | game.player_x = x; |
| 345 | player_y = y; | 525 | game.player_y = y; |
| 526 | |||
| 527 | game.dirtyLighting = true; | ||
| 346 | } | 528 | } |
| 347 | } | 529 | } |
| 348 | 530 | ||
| 349 | void setIfValid(Map& map, int x, int y, Tile val) | 531 | void recalculateLighting(Game& game, fov_settings_type* fov) |
| 350 | { | 532 | { |
| 351 | if ((x >= 0) && (x < VIEW_WIDTH) && (y >= 0) && (y < VIEW_HEIGHT)) | 533 | game.litSpots = 0; |
| 534 | |||
| 535 | for (MapData& md : game.map.data()) | ||
| 352 | { | 536 | { |
| 353 | map.tiles[x+VIEW_WIDTH*y] = val; | 537 | md.wasLit = md.lit; |
| 538 | md.lit = false; | ||
| 539 | md.visibility = 0.0; | ||
| 540 | md.litTiles.clear(); | ||
| 354 | } | 541 | } |
| 355 | } | ||
| 356 | |||
| 357 | void recalculateLighting(Map& map, fov_settings_type* fov) | ||
| 358 | { | ||
| 359 | map.oldLighting = map.lighting; | ||
| 360 | map.lighting = std::vector<bool>(VIEW_WIDTH*VIEW_HEIGHT, false); | ||
| 361 | map.lightStrength = std::vector<double>(VIEW_WIDTH*VIEW_HEIGHT, 0.0); | ||
| 362 | map.lightedSpots = 0; | ||
| 363 | map.lightSource = std::vector<Source>(VIEW_WIDTH*VIEW_HEIGHT, Source::None); | ||
| 364 | 542 | ||
| 365 | fov_settings_set_opacity_test_function( | 543 | fov_settings_set_opacity_test_function( |
| 366 | fov, | 544 | fov, |
| 367 | [] (void* map, int x, int y) { | 545 | [] (void* data, int x, int y) { |
| 368 | return | 546 | Game& game = *static_cast<Game*>(data); |
| 369 | x >= 0 && | 547 | |
| 370 | x < VIEW_WIDTH && | 548 | return game.map.inBounds(x,y) && game.map.at(x,y).tile == Tile::Wall; |
| 371 | y >= 0 && | ||
| 372 | y < VIEW_HEIGHT && | ||
| 373 | static_cast<Map*>(map)->tiles.at(x+VIEW_WIDTH*y) == Tile::Wall; | ||
| 374 | }); | 549 | }); |
| 375 | 550 | ||
| 376 | fov_settings_set_apply_lighting_function( | 551 | fov_settings_set_apply_lighting_function( |
| 377 | fov, | 552 | fov, |
| 378 | [] (void* map, int x, int y, int dx, int dy, void* source) { | 553 | [] (void* data, int x, int y, int dx, int dy, void* source) { |
| 379 | if ((x >= 0) && (x < VIEW_WIDTH) && (y >= 0) && (y < VIEW_HEIGHT)) | 554 | Game& game = *static_cast<Game*>(data); |
| 555 | |||
| 556 | if (game.map.inBounds(x, y)) | ||
| 380 | { | 557 | { |
| 381 | Map& m = *static_cast<Map*>(map); | 558 | MapData& sourceData = *static_cast<MapData*>(source); |
| 382 | if (!m.lighting[x+VIEW_WIDTH*y]) | 559 | double lightRadius = static_cast<double>(sourceData.lightRadius); |
| 560 | |||
| 561 | if (!game.map.at(x,y).lit) | ||
| 383 | { | 562 | { |
| 384 | m.lightedSpots++; | 563 | game.litSpots++; |
| 385 | } | 564 | } |
| 386 | 565 | ||
| 387 | m.lighting[x+VIEW_WIDTH*y] = true; | 566 | game.map.at(x,y).lit = true; |
| 388 | 567 | ||
| 389 | m.lightStrength[x+VIEW_WIDTH*y] = std::max( | 568 | /*game.map.at(x,y).visibility = std::max( |
| 390 | m.lightStrength[x+VIEW_WIDTH*y], | 569 | game.map.at(x,y).visibility, |
| 391 | std::pow( | 570 | std::pow( |
| 392 | std::max( | 571 | std::max( |
| 393 | 0.0, | 572 | 0.0, |
| 394 | 1.0 - std::sqrt(dx * dx + dy * dy) / static_cast<double>(RADIUS)), | 573 | 1.0 - std::sqrt(dx * dx + dy * dy) / lightRadius), |
| 395 | 1.0/3.0)); | 574 | 1.0/3.0));*/ |
| 575 | |||
| 576 | sourceData.litTiles.emplace(x,y); | ||
| 396 | 577 | ||
| 397 | Source ls = *static_cast<Source*>(source); | 578 | //Source ls = *static_cast<Source*>(source); |
| 398 | if (static_cast<size_t>(ls) > static_cast<size_t>(m.lightSource[x+VIEW_WIDTH*y])) | 579 | //if (static_cast<size_t>(ls) > static_cast<size_t>(m.lightSource[x+VIEW_WIDTH*y])) |
| 399 | { | 580 | { |
| 400 | //m.lightSource[x+VIEW_WIDTH*y] = ls; | 581 | //m.lightSource[x+VIEW_WIDTH*y] = ls; |
| 401 | } | 582 | } |
| 402 | } | 583 | } |
| 403 | }); | 584 | }); |
| 404 | 585 | ||
| 405 | for (int y = 0; y < VIEW_HEIGHT; y++) | 586 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) |
| 406 | { | 587 | { |
| 407 | for (int x = 0; x < VIEW_WIDTH; x++) | 588 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) |
| 408 | { | 589 | { |
| 409 | Source ls = Source::None; | 590 | Source ls = Source::None; |
| 591 | int lightRadius; | ||
| 410 | 592 | ||
| 411 | if (player_x == x && player_y == y) | 593 | if (game.renderPlayer && game.player_x == x && game.player_y == y) |
| 412 | { | 594 | { |
| 413 | ls = Source::Player; | 595 | ls = Source::Player; |
| 414 | } else if (map.tiles[x+VIEW_WIDTH*y] == Tile::Dust) | 596 | lightRadius = RADIUS; |
| 597 | } else if (game.map.at(x,y).tile == Tile::Dust) | ||
| 415 | { | 598 | { |
| 416 | ls = Source::Dust; | 599 | ls = Source::Dust; |
| 417 | } else if (map.tiles[x+VIEW_WIDTH*y] == Tile::Lamp) | 600 | lightRadius = 2; |
| 601 | } else if (game.map.at(x,y).tile == Tile::Lamp) | ||
| 418 | { | 602 | { |
| 419 | ls = Source::Lamp; | 603 | ls = Source::Lamp; |
| 604 | lightRadius = RADIUS; | ||
| 420 | } | 605 | } |
| 421 | 606 | ||
| 607 | game.map.at(x,y).lightType = ls; | ||
| 608 | //game.map.at(x,y).litTiles.clear(); | ||
| 609 | |||
| 422 | if (ls != Source::None) | 610 | if (ls != Source::None) |
| 423 | { | 611 | { |
| 424 | fov_circle(fov, static_cast<void*>(&map), static_cast<void*>(&ls), x, y, RADIUS); | 612 | game.map.at(x,y).lightRadius = lightRadius; |
| 425 | 613 | game.map.at(x,y).litTiles.emplace(x,y); | |
| 426 | map.lighting[x+VIEW_WIDTH*y] = true; | 614 | |
| 427 | map.lightStrength[x+VIEW_WIDTH*y] = 1.0; | 615 | fov_circle( |
| 428 | map.lightSource[x+VIEW_WIDTH*y] = ls; | 616 | fov, |
| 617 | static_cast<void*>(&game), | ||
| 618 | static_cast<void*>(&game.map.at(x,y)), | ||
| 619 | x, | ||
| 620 | y, | ||
| 621 | lightRadius); | ||
| 622 | |||
| 623 | game.map.at(x,y).lit = true; | ||
| 624 | game.map.at(x,y).visibility = 1.0; | ||
| 429 | } | 625 | } |
| 430 | } | 626 | } |
| 431 | } | 627 | } |
| 628 | |||
| 629 | game.dirtyLighting = false; | ||
| 432 | } | 630 | } |
| 433 | 631 | ||
| 434 | void processKeys(Map& map, const Input& keystate) | 632 | void processKeys(Game& game, const Input& keystate) |
| 435 | { | 633 | { |
| 436 | int px = player_x; | 634 | int px = game.player_x; |
| 437 | int py = player_y; | 635 | int py = game.player_y; |
| 438 | 636 | ||
| 439 | if (keystate.up) | 637 | if (keystate.up) |
| 440 | { | 638 | { |
| @@ -456,12 +654,164 @@ void processKeys(Map& map, const Input& keystate) | |||
| 456 | px++; | 654 | px++; |
| 457 | } | 655 | } |
| 458 | 656 | ||
| 459 | if (!(player_x == px && player_y == py)) | 657 | if (!(game.player_x == px && game.player_y == py)) |
| 460 | { | 658 | { |
| 461 | movePlayer(px, py, map); | 659 | movePlayer(game, px, py); |
| 462 | } | 660 | } |
| 463 | } | 661 | } |
| 464 | 662 | ||
| 663 | void kickUpDust(Game& game, int x, int y, size_t chain) | ||
| 664 | { | ||
| 665 | Kickup dk; | ||
| 666 | dk.x = x; | ||
| 667 | dk.y = y; | ||
| 668 | dk.chain = chain; | ||
| 669 | dk.cur = 0; | ||
| 670 | dk.radius = RADIUS + (chain + 1) * (chain + 1); | ||
| 671 | dk.front.emplace(x, y); | ||
| 672 | dk.done.emplace(x, y); | ||
| 673 | |||
| 674 | game.kickups.push_back(dk); | ||
| 675 | } | ||
| 676 | |||
| 677 | void popLamp(Game& game, int x, int y, size_t chain) | ||
| 678 | { | ||
| 679 | if (game.map.at(x,y).tile == Tile::Lamp) | ||
| 680 | { | ||
| 681 | game.numLamps--; | ||
| 682 | } | ||
| 683 | |||
| 684 | game.map.at(x,y).tile = Tile::Dust; | ||
| 685 | game.map.at(x,y).dustLife = 2; | ||
| 686 | game.numDust++; | ||
| 687 | game.dirtyLighting = true; | ||
| 688 | |||
| 689 | kickUpDust(game, x, y, chain); | ||
| 690 | } | ||
| 691 | |||
| 692 | void processKickup(Game& game) | ||
| 693 | { | ||
| 694 | for (Kickup& kickup : game.kickups) | ||
| 695 | { | ||
| 696 | kickup.cur++; | ||
| 697 | |||
| 698 | std::set<coord> newFront; | ||
| 699 | for (const coord& xy : kickup.front) | ||
| 700 | { | ||
| 701 | auto processDir = [&] (int x, int y) { | ||
| 702 | coord c {x,y}; | ||
| 703 | |||
| 704 | if (game.map.inBounds(x,y) && | ||
| 705 | (game.map.at(x,y).tile == Tile::Floor || | ||
| 706 | game.map.at(x,y).tile == Tile::Lamp) && | ||
| 707 | !kickup.done.count(c)) | ||
| 708 | { | ||
| 709 | newFront.insert(c); | ||
| 710 | kickup.done.insert(c); | ||
| 711 | |||
| 712 | if (game.map.at(x,y).tile == Tile::Floor) | ||
| 713 | { | ||
| 714 | game.map.at(x,y).tile = Tile::Dust; | ||
| 715 | game.map.at(x,y).dustLife = 2; | ||
| 716 | game.numDust++; | ||
| 717 | game.dirtyLighting = true; | ||
| 718 | } else if (game.map.at(x,y).tile == Tile::Lamp) | ||
| 719 | { | ||
| 720 | popLamp(game, x, y, kickup.chain + 1); | ||
| 721 | } | ||
| 722 | } | ||
| 723 | }; | ||
| 724 | |||
| 725 | processDir(std::get<0>(xy) - 1, std::get<1>(xy) ); | ||
| 726 | processDir(std::get<0>(xy) + 1, std::get<1>(xy) ); | ||
| 727 | processDir(std::get<0>(xy) , std::get<1>(xy) - 1); | ||
| 728 | processDir(std::get<0>(xy) , std::get<1>(xy) + 1); | ||
| 729 | |||
| 730 | if (std::bernoulli_distribution(0.5)(game.rng)) | ||
| 731 | { | ||
| 732 | processDir(std::get<0>(xy) - 1, std::get<1>(xy) - 1); | ||
| 733 | } | ||
| 734 | |||
| 735 | if (std::bernoulli_distribution(0.5)(game.rng)) | ||
| 736 | { | ||
| 737 | processDir(std::get<0>(xy) - 1, std::get<1>(xy) + 1); | ||
| 738 | } | ||
| 739 | |||
| 740 | if (std::bernoulli_distribution(0.5)(game.rng)) | ||
| 741 | { | ||
| 742 | processDir(std::get<0>(xy) + 1, std::get<1>(xy) - 1); | ||
| 743 | } | ||
| 744 | |||
| 745 | if (std::bernoulli_distribution(0.5)(game.rng)) | ||
| 746 | { | ||
| 747 | processDir(std::get<0>(xy) + 1, std::get<1>(xy) + 1); | ||
| 748 | } | ||
| 749 | } | ||
| 750 | |||
| 751 | kickup.front.swap(newFront); | ||
| 752 | } | ||
| 753 | |||
| 754 | erase_if( | ||
| 755 | game.kickups, | ||
| 756 | [] (const Kickup& kickup) { | ||
| 757 | return kickup.cur == kickup.radius; | ||
| 758 | }); | ||
| 759 | } | ||
| 760 | |||
| 761 | void growMap(Game& game, size_t zoom) | ||
| 762 | { | ||
| 763 | int ol = game.map.getLeft(); | ||
| 764 | int ot = game.map.getTop(); | ||
| 765 | int ow = game.map.getWidth(); | ||
| 766 | int oh = game.map.getHeight(); | ||
| 767 | |||
| 768 | game.map.resize( | ||
| 769 | -zoom * ZOOM_X_FACTOR / 2, | ||
| 770 | -zoom * ZOOM_Y_FACTOR / 2, | ||
| 771 | zoom * ZOOM_X_FACTOR, | ||
| 772 | zoom * ZOOM_Y_FACTOR); | ||
| 773 | |||
| 774 | game.maxZoom = zoom; | ||
| 775 | |||
| 776 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) | ||
| 777 | { | ||
| 778 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) | ||
| 779 | { | ||
| 780 | if (!(x >= ol && x < (ol + ow) && y >= ot && y < (ot + oh))) | ||
| 781 | { | ||
| 782 | if (std::bernoulli_distribution(0.5)(game.rng)) | ||
| 783 | { | ||
| 784 | game.map.at(x,y).tile = Tile::Wall; | ||
| 785 | } | ||
| 786 | } | ||
| 787 | } | ||
| 788 | } | ||
| 789 | |||
| 790 | for (int i = 0; i < 3; i++) | ||
| 791 | { | ||
| 792 | tick(game, ol, ot, ol + ow, ot + oh, true); | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | void setZoom(Game& game, size_t zoom) | ||
| 797 | { | ||
| 798 | if (zoom == game.curZoom) | ||
| 799 | { | ||
| 800 | return; | ||
| 801 | } | ||
| 802 | |||
| 803 | if (zoom > game.maxZoom) | ||
| 804 | { | ||
| 805 | growMap(game, zoom); | ||
| 806 | } | ||
| 807 | |||
| 808 | // TODO: don't think this works well with rapid zoom changes | ||
| 809 | game.zoomProgress += (zoom - game.curZoom) * TILE_WIDTH; | ||
| 810 | //game.oldZoom = game.curZoom; | ||
| 811 | game.curZoom = zoom; | ||
| 812 | game.zooming = true; | ||
| 813 | } | ||
| 814 | |||
| 465 | int main(int, char**) | 815 | int main(int, char**) |
| 466 | { | 816 | { |
| 467 | std::random_device randomEngine; | 817 | std::random_device randomEngine; |
| @@ -498,121 +848,112 @@ int main(int, char**) | |||
| 498 | throw sdl_error(); | 848 | throw sdl_error(); |
| 499 | } | 849 | } |
| 500 | 850 | ||
| 501 | Map map; | 851 | SDL_SetRenderDrawBlendMode(ren.get(), SDL_BLENDMODE_BLEND); |
| 852 | |||
| 853 | Game game(rng); | ||
| 502 | 854 | ||
| 503 | std::unique_ptr<fov_settings_type> fov(new fov_settings_type()); | 855 | std::unique_ptr<fov_settings_type> fov(new fov_settings_type()); |
| 504 | fov_settings_init(fov.get()); | 856 | fov_settings_init(fov.get()); |
| 505 | 857 | ||
| 506 | for (int y = 0; y < VIEW_HEIGHT; y++) | 858 | for (MapData& md : game.map.data()) |
| 507 | { | 859 | { |
| 508 | for (int x = 0; x < VIEW_WIDTH; x++) | 860 | if (std::bernoulli_distribution(0.5)(rng)) |
| 509 | { | 861 | { |
| 510 | if (std::bernoulli_distribution(0.5)(rng)) | 862 | md.tile = Tile::Wall; |
| 511 | { | 863 | } |
| 512 | map.tiles[x+y*VIEW_WIDTH] = Tile::Wall; | 864 | } |
| 513 | } | 865 | |
| 866 | tick(game); | ||
| 867 | tick(game); | ||
| 868 | |||
| 869 | for (int y = -1; y <= 1; y++) | ||
| 870 | { | ||
| 871 | for (int x = -1; x <= 1; x++) | ||
| 872 | { | ||
| 873 | game.map.at(x,y).tile = Tile::Floor; | ||
| 514 | } | 874 | } |
| 515 | } | 875 | } |
| 516 | 876 | ||
| 517 | tick(map); | 877 | tick(game); |
| 518 | tick(map); | ||
| 519 | tick(map); | ||
| 520 | 878 | ||
| 521 | bool quit = false; | 879 | bool quit = false; |
| 880 | LoseState losing = LoseState::None; | ||
| 522 | Input keystate; | 881 | Input keystate; |
| 523 | SDL_Event e; | 882 | SDL_Event e; |
| 883 | |||
| 884 | size_t dustDt = 40; | ||
| 885 | size_t dustAcc = 0; | ||
| 886 | |||
| 887 | size_t inputDt = 50; | ||
| 888 | size_t inputAcc = 0; | ||
| 889 | |||
| 890 | size_t losePopLampDt = 800; | ||
| 891 | size_t losePopLampAcc = losePopLampDt; | ||
| 892 | |||
| 893 | size_t losePopPlayerDt = 3000; | ||
| 894 | size_t losePopPlayerAcc = 0; | ||
| 895 | |||
| 896 | size_t zoomDt = 62; | ||
| 897 | size_t zoomAcc = 0; | ||
| 898 | |||
| 899 | size_t lastTime = SDL_GetTicks(); | ||
| 900 | |||
| 524 | while (!quit) | 901 | while (!quit) |
| 525 | { | 902 | { |
| 526 | //bool input = false; | 903 | size_t currentTime = SDL_GetTicks(); |
| 527 | //int presses = 0; | 904 | size_t frameTime = currentTime - lastTime; |
| 528 | bool pressedSpace = false; | 905 | lastTime = currentTime; |
| 906 | |||
| 529 | while (SDL_PollEvent(&e)) | 907 | while (SDL_PollEvent(&e)) |
| 530 | { | 908 | { |
| 531 | if (e.type == SDL_QUIT) | 909 | if (e.type == SDL_QUIT) |
| 532 | { | 910 | { |
| 533 | quit = true; | 911 | if (losing != LoseState::None) |
| 912 | { | ||
| 913 | quit = true; | ||
| 914 | } else { | ||
| 915 | losing = LoseState::PoppingLamps; | ||
| 916 | } | ||
| 534 | } else if (e.type == SDL_KEYDOWN) | 917 | } else if (e.type == SDL_KEYDOWN) |
| 535 | { | 918 | { |
| 536 | //presses++; | ||
| 537 | |||
| 538 | switch (e.key.keysym.sym) | 919 | switch (e.key.keysym.sym) |
| 539 | { | 920 | { |
| 540 | case SDLK_ESCAPE: | 921 | case SDLK_ESCAPE: |
| 541 | { | 922 | { |
| 542 | quit = true; | 923 | if (losing != LoseState::None) |
| 924 | { | ||
| 925 | quit = true; | ||
| 926 | } else { | ||
| 927 | losing = LoseState::PoppingLamps; | ||
| 928 | } | ||
| 929 | |||
| 543 | break; | 930 | break; |
| 544 | } | 931 | } |
| 545 | 932 | ||
| 546 | case SDLK_SPACE: | 933 | case SDLK_SPACE: |
| 547 | { | 934 | { |
| 548 | pressedSpace = true; | 935 | if (losing == LoseState::None) |
| 549 | //input = true; | ||
| 550 | |||
| 551 | std::deque<std::tuple<int, int>> lamps; | ||
| 552 | lamps.emplace_back(player_x, player_y); | ||
| 553 | |||
| 554 | setIfValid(map, player_x , player_y , Tile::Lamp); | ||
| 555 | |||
| 556 | for (int i = 0; i < 5; i++) | ||
| 557 | { | ||
| 558 | processKeys(map, keystate); | ||
| 559 | |||
| 560 | tick( | ||
| 561 | map, | ||
| 562 | player_x - (RADIUS - 1), | ||
| 563 | player_y - (RADIUS - 1), | ||
| 564 | player_x + RADIUS, | ||
| 565 | player_y + RADIUS); | ||
| 566 | |||
| 567 | render(ren.get(), map, false); | ||
| 568 | SDL_Delay(30); | ||
| 569 | } | ||
| 570 | |||
| 571 | int lamped = 0; | ||
| 572 | while (!lamps.empty()) | ||
| 573 | { | 936 | { |
| 574 | lamped++; | 937 | if (game.map.at(game.player_x, game.player_y).tile == |
| 575 | 938 | Tile::Floor) | |
| 576 | int px, py; | 939 | { |
| 577 | std::tie(px, py) = lamps.front(); | 940 | game.map.at(game.player_x, game.player_y).tile = Tile::Lamp; |
| 578 | lamps.pop_front(); | 941 | game.numLamps++; |
| 579 | 942 | game.dirtyLighting = true; | |
| 580 | std::unique_ptr<fov_settings_type> dusty(new fov_settings_type); | 943 | kickUpDust(game, game.player_x, game.player_y, 0); |
| 581 | fov_settings_set_opacity_test_function( | 944 | |
| 582 | dusty.get(), | 945 | for (int i = 0; i < 5; i++) |
| 583 | [] (void* map, int x, int y) { | 946 | { |
| 584 | return | 947 | processKeys(game, keystate); |
| 585 | x >= 0 && | 948 | |
| 586 | x < VIEW_WIDTH && | 949 | tick( |
| 587 | y >= 0 && | 950 | game, |
| 588 | y < VIEW_HEIGHT && | 951 | game.player_x - (RADIUS - 1), |
| 589 | static_cast<Map*>(map)->tiles.at(x+VIEW_WIDTH*y) == Tile::Wall; | 952 | game.player_y - (RADIUS - 1), |
| 590 | }); | 953 | game.player_x + RADIUS, |
| 591 | 954 | game.player_y + RADIUS); | |
| 592 | fov_settings_set_apply_lighting_function( | 955 | } |
| 593 | dusty.get(), | 956 | } |
| 594 | [] (void* map, int x, int y, int, int, void* source) { | ||
| 595 | Map& m = *static_cast<Map*>(map); | ||
| 596 | auto& lamps = *static_cast<std::deque<std::pair<int, int>>*>(source); | ||
| 597 | |||
| 598 | if ((x >= 0) && (x < VIEW_WIDTH) && | ||
| 599 | (y >= 0) && (y < VIEW_HEIGHT)) | ||
| 600 | { | ||
| 601 | if (m.tiles[x+VIEW_WIDTH*y] == Tile::Floor) | ||
| 602 | { | ||
| 603 | m.tiles[x+VIEW_WIDTH*y] = Tile::Dust; | ||
| 604 | } else if (m.tiles[x+VIEW_WIDTH*y] == Tile::Lamp) | ||
| 605 | { | ||
| 606 | m.tiles[x+VIEW_WIDTH*y] = Tile::Dust; | ||
| 607 | lamps.emplace_back(x, y); | ||
| 608 | } | ||
| 609 | } | ||
| 610 | }); | ||
| 611 | |||
| 612 | fov_circle(dusty.get(), static_cast<void*>(&map), static_cast<void*>(&lamps), px, py, RADIUS+lamped*lamped); | ||
| 613 | |||
| 614 | render(ren.get(), map, false); | ||
| 615 | SDL_Delay(50); | ||
| 616 | } | 957 | } |
| 617 | 958 | ||
| 618 | break; | 959 | break; |
| @@ -627,50 +968,164 @@ int main(int, char**) | |||
| 627 | keystate.up = state[SDL_SCANCODE_UP]; | 968 | keystate.up = state[SDL_SCANCODE_UP]; |
| 628 | keystate.down = state[SDL_SCANCODE_DOWN]; | 969 | keystate.down = state[SDL_SCANCODE_DOWN]; |
| 629 | 970 | ||
| 630 | bool input = keystate.left || keystate.right || keystate.up || keystate.down || pressedSpace; | 971 | dustAcc += frameTime; |
| 972 | inputAcc += frameTime; | ||
| 631 | 973 | ||
| 632 | if (input) | 974 | while (dustAcc >= dustDt) |
| 633 | { | 975 | { |
| 634 | for (int y = 0; y < VIEW_HEIGHT; y++) | 976 | game.numDust = 0; |
| 977 | |||
| 978 | for (MapData& md : game.map.data()) | ||
| 635 | { | 979 | { |
| 636 | for (int x = 0; x < VIEW_WIDTH; x++) | 980 | if (md.tile == Tile::Dust) |
| 637 | { | 981 | { |
| 638 | if (map.tiles[x+y*VIEW_WIDTH] == Tile::Dust) | 982 | md.dustLife--; |
| 983 | |||
| 984 | if (md.dustLife <= 0) | ||
| 639 | { | 985 | { |
| 640 | map.tiles[x+y*VIEW_WIDTH] = Tile::Floor; | 986 | md.tile = Tile::Floor; |
| 987 | game.dirtyLighting = true; | ||
| 988 | } else { | ||
| 989 | game.numDust++; | ||
| 641 | } | 990 | } |
| 642 | } | 991 | } |
| 643 | } | 992 | } |
| 993 | |||
| 994 | processKickup(game); | ||
| 995 | |||
| 996 | dustAcc -= dustDt; | ||
| 644 | } | 997 | } |
| 645 | 998 | ||
| 646 | processKeys(map, keystate); | 999 | switch (losing) |
| 647 | recalculateLighting(map, fov.get()); | 1000 | { |
| 1001 | case LoseState::None: | ||
| 1002 | { | ||
| 1003 | while (inputAcc >= inputDt) | ||
| 1004 | { | ||
| 1005 | processKeys(game, keystate); | ||
| 1006 | |||
| 1007 | inputAcc -= inputDt; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | break; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | case LoseState::PoppingLamps: | ||
| 1014 | { | ||
| 1015 | if (game.numLamps == 0) | ||
| 1016 | { | ||
| 1017 | if (game.numDust == 0) | ||
| 1018 | { | ||
| 1019 | losing = LoseState::PoppingPlayer; | ||
| 1020 | } | ||
| 1021 | } else { | ||
| 1022 | losePopLampAcc += frameTime; | ||
| 1023 | |||
| 1024 | while (losePopLampAcc >= losePopLampDt) | ||
| 1025 | { | ||
| 1026 | std::vector<std::tuple<int, int>> lamps; | ||
| 1027 | |||
| 1028 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) | ||
| 1029 | { | ||
| 1030 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) | ||
| 1031 | { | ||
| 1032 | if (game.map.at(x,y).tile == Tile::Lamp) | ||
| 1033 | { | ||
| 1034 | lamps.emplace_back(x, y); | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | std::uniform_int_distribution<int> lampDist(0, lamps.size() - 1); | ||
| 1040 | std::tuple<int, int> popPos = lamps[lampDist(rng)]; | ||
| 1041 | |||
| 1042 | popLamp(game, std::get<0>(popPos), std::get<1>(popPos), 1); | ||
| 1043 | |||
| 1044 | losePopLampAcc -= losePopLampDt; | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | break; | ||
| 1049 | } | ||
| 648 | 1050 | ||
| 649 | if (input) | 1051 | case LoseState::PoppingPlayer: |
| 1052 | { | ||
| 1053 | losePopPlayerAcc += frameTime; | ||
| 1054 | |||
| 1055 | if (losePopPlayerAcc >= losePopPlayerDt) | ||
| 1056 | { | ||
| 1057 | popLamp(game, game.player_x, game.player_y, 10); | ||
| 1058 | game.renderPlayer = false; | ||
| 1059 | |||
| 1060 | losing = LoseState::Outro; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | break; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | case LoseState::Outro: | ||
| 1067 | { | ||
| 1068 | if (game.numDust == 0) | ||
| 1069 | { | ||
| 1070 | quit = true; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | break; | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | if (game.dirtyLighting) | ||
| 650 | { | 1078 | { |
| 651 | for (int y = 0; y < VIEW_HEIGHT; y++) | 1079 | recalculateLighting(game, fov.get()); |
| 1080 | |||
| 1081 | for (int y = game.map.getTop(); y < game.map.getBottom(); y++) | ||
| 652 | { | 1082 | { |
| 653 | for (int x = 0; x < VIEW_WIDTH; x++) | 1083 | for (int x = game.map.getLeft(); x < game.map.getRight(); x++) |
| 654 | { | 1084 | { |
| 655 | if (!map.lighting[x+y*VIEW_WIDTH] && map.oldLighting[x+y*VIEW_WIDTH]) | 1085 | if (!game.map.at(x,y).lit && game.map.at(x,y).wasLit) |
| 656 | { | 1086 | { |
| 657 | if (std::bernoulli_distribution(0.5)(rng)) | 1087 | if (std::bernoulli_distribution(0.5)(rng)) |
| 658 | { | 1088 | { |
| 659 | map.tiles[x+y*VIEW_WIDTH] = Tile::Wall; | 1089 | game.map.at(x,y).tile = Tile::Wall; |
| 660 | } else { | 1090 | } else { |
| 661 | map.tiles[x+y*VIEW_WIDTH] = Tile::Floor; | 1091 | game.map.at(x,y).tile = Tile::Floor; |
| 662 | } | 1092 | } |
| 663 | } | 1093 | } |
| 664 | } | 1094 | } |
| 665 | } | 1095 | } |
| 666 | 1096 | ||
| 667 | tick(map, 0, 0, VIEW_WIDTH, VIEW_HEIGHT, true); | 1097 | tick(game, true); |
| 668 | tick(map, 0, 0, VIEW_WIDTH, VIEW_HEIGHT, true); | 1098 | tick(game, true); |
| 669 | tick(map, 0, 0, VIEW_WIDTH, VIEW_HEIGHT, true); | 1099 | tick(game, true); |
| 1100 | |||
| 1101 | // TODO: better zoom algorithm | ||
| 1102 | setZoom(game, game.litSpots / 1500 * 2 + INIT_ZOOM); | ||
| 670 | } | 1103 | } |
| 671 | 1104 | ||
| 672 | render(ren.get(), map, true); | 1105 | zoomAcc += frameTime; |
| 673 | SDL_Delay(50); | 1106 | |
| 1107 | while (zoomAcc >= zoomDt) | ||
| 1108 | { | ||
| 1109 | if (game.zooming) | ||
| 1110 | { | ||
| 1111 | if (game.zoomProgress > 0) | ||
| 1112 | { | ||
| 1113 | game.zoomProgress--; | ||
| 1114 | } else if (game.zoomProgress < 0) | ||
| 1115 | { | ||
| 1116 | game.zoomProgress++; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | if (game.zoomProgress == 0) | ||
| 1120 | { | ||
| 1121 | game.zooming = false; | ||
| 1122 | } | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | zoomAcc -= zoomDt; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | render(ren.get(), game, true); | ||
| 674 | } | 1129 | } |
| 675 | } catch (const sdl_error& ex) | 1130 | } catch (const sdl_error& ex) |
| 676 | { | 1131 | { |
| @@ -681,4 +1136,4 @@ int main(int, char**) | |||
| 681 | SDL_Quit(); | 1136 | SDL_Quit(); |
| 682 | 1137 | ||
| 683 | return 0; | 1138 | return 0; |
| 684 | } \ No newline at end of file | 1139 | } |
| diff --git a/src/map.h b/src/map.h new file mode 100644 index 0000000..329553c --- /dev/null +++ b/src/map.h | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | #ifndef MAP_H_3AB00D12 | ||
| 2 | #define MAP_H_3AB00D12 | ||
| 3 | |||
| 4 | #include <vector> | ||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | template <typename T> | ||
| 8 | class Map { | ||
| 9 | public: | ||
| 10 | |||
| 11 | Map( | ||
| 12 | int left, | ||
| 13 | int top, | ||
| 14 | int width, | ||
| 15 | int height) : | ||
| 16 | left_(left), | ||
| 17 | top_(top), | ||
| 18 | width_(width), | ||
| 19 | height_(height), | ||
| 20 | data_(width_*height_) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | inline int getLeft() const | ||
| 25 | { | ||
| 26 | return left_; | ||
| 27 | } | ||
| 28 | |||
| 29 | inline int getRight() const | ||
| 30 | { | ||
| 31 | return left_ + width_; | ||
| 32 | } | ||
| 33 | |||
| 34 | inline int getTop() const | ||
| 35 | { | ||
| 36 | return top_; | ||
| 37 | } | ||
| 38 | |||
| 39 | inline int getBottom() const | ||
| 40 | { | ||
| 41 | return top_ + height_; | ||
| 42 | } | ||
| 43 | |||
| 44 | inline int getWidth() const | ||
| 45 | { | ||
| 46 | return width_; | ||
| 47 | } | ||
| 48 | |||
| 49 | inline int getHeight() const | ||
| 50 | { | ||
| 51 | return height_; | ||
| 52 | } | ||
| 53 | |||
| 54 | inline int getTrueX(int x) const | ||
| 55 | { | ||
| 56 | return (x - left_); | ||
| 57 | } | ||
| 58 | |||
| 59 | inline int getTrueY(int y) const | ||
| 60 | { | ||
| 61 | return (y - top_); | ||
| 62 | } | ||
| 63 | |||
| 64 | inline bool inBounds(int x, int y) const | ||
| 65 | { | ||
| 66 | return (x >= left_) && | ||
| 67 | (x < left_ + width_) && | ||
| 68 | (y >= top_) && | ||
| 69 | (y < top_ + height_); | ||
| 70 | } | ||
| 71 | |||
| 72 | inline const T& at(int x, int y) const | ||
| 73 | { | ||
| 74 | return data_.at((x - left_) + width_ * (y - top_)); | ||
| 75 | } | ||
| 76 | |||
| 77 | inline T& at(int x, int y) | ||
| 78 | { | ||
| 79 | return const_cast<T&>(static_cast<const Map&>(*this).at(x, y)); | ||
| 80 | } | ||
| 81 | |||
| 82 | inline const std::vector<T>& data() const | ||
| 83 | { | ||
| 84 | return data_; | ||
| 85 | } | ||
| 86 | |||
| 87 | inline std::vector<T>& data() | ||
| 88 | { | ||
| 89 | return data_; | ||
| 90 | } | ||
| 91 | |||
| 92 | void resize(int newLeft, int newTop, int newWidth, int newHeight) | ||
| 93 | { | ||
| 94 | std::vector<T> newData(newWidth * newHeight); | ||
| 95 | |||
| 96 | int winTop = std::max(top_, newTop); | ||
| 97 | int winBottom = std::min(top_ + height_, newTop + newHeight); | ||
| 98 | int winLeft = std::max(left_, newLeft); | ||
| 99 | int winRight = std::min(left_ + width_, newLeft + newWidth); | ||
| 100 | |||
| 101 | for (int y = winTop; y < winBottom; y++) | ||
| 102 | { | ||
| 103 | std::move( | ||
| 104 | std::next(std::begin(data_), (winLeft - left_) + width_ * (y - top_)), | ||
| 105 | std::next(std::begin(data_), (winRight - left_) + width_ * (y - top_)), | ||
| 106 | std::next(std::begin(newData), | ||
| 107 | (winLeft - newLeft) + newWidth * (y - newTop))); | ||
| 108 | } | ||
| 109 | |||
| 110 | left_ = newLeft; | ||
| 111 | top_ = newTop; | ||
| 112 | width_ = newWidth; | ||
| 113 | height_ = newHeight; | ||
| 114 | data_.swap(newData); | ||
| 115 | } | ||
| 116 | |||
| 117 | private: | ||
| 118 | |||
| 119 | int left_; | ||
| 120 | int top_; | ||
| 121 | int width_; | ||
| 122 | int height_; | ||
| 123 | |||
| 124 | std::vector<T> data_; | ||
| 125 | }; | ||
| 126 | |||
| 127 | #endif /* end of include guard: MAP_H_3AB00D12 */ | ||
| diff --git a/src/untitled.txt b/src/untitled.txt deleted file mode 100644 index e69de29..0000000 --- a/src/untitled.txt +++ /dev/null | |||
| diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..150a6a5 --- /dev/null +++ b/src/util.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef UTIL_H_E9110D4C | ||
| 2 | #define UTIL_H_E9110D4C | ||
| 3 | |||
| 4 | template <typename Container, typename Predicate> | ||
| 5 | void erase_if(Container& items, const Predicate& predicate) | ||
| 6 | { | ||
| 7 | for (auto it = std::begin(items); it != std::end(items);) | ||
| 8 | { | ||
| 9 | if (predicate(*it)) | ||
| 10 | { | ||
| 11 | it = items.erase(it); | ||
| 12 | } | ||
| 13 | else | ||
| 14 | { | ||
| 15 | ++it; | ||
| 16 | } | ||
| 17 | } | ||
| 18 | }; | ||
| 19 | |||
| 20 | #endif /* end of include guard: UTIL_H_E9110D4C */ | ||
