diff options
-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 */ | ||