From 4b6e5eb5d3e0733c239a7a231da91a9977c38246 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 12 Mar 2022 14:12:42 -0500 Subject: dynamic wall and floor textures --- res/runninbloods.png | Bin 0 -> 34239 bytes src/game.h | 13 +++ src/interpolation.h | 38 +++++++ src/main.cpp | 298 ++++++++++++++++++++++++++++++++++++++------------- src/renderer.cpp | 80 +++++++++++--- src/renderer.h | 1 + 6 files changed, 342 insertions(+), 88 deletions(-) create mode 100755 res/runninbloods.png create mode 100644 src/interpolation.h diff --git a/res/runninbloods.png b/res/runninbloods.png new file mode 100755 index 0000000..5d18f4c Binary files /dev/null and b/res/runninbloods.png differ diff --git a/src/game.h b/src/game.h index 2fd0f05..c91a001 100644 --- a/src/game.h +++ b/src/game.h @@ -9,6 +9,7 @@ #include "muxer.h" #include "timer.h" #include "animation.h" +#include "interpolation.h" const int GAME_WIDTH = 640*2; const int GAME_HEIGHT = 480*2; @@ -19,6 +20,10 @@ const int ZOOM_X_FACTOR = 8; const int ZOOM_Y_FACTOR = 6; const int RADIUS = 8; +constexpr int TilesetIndex(int x, int y) { + return x + y * 24; +} + enum class Tile { Floor, Wall, @@ -75,6 +80,8 @@ struct MapData { Source lightType = Source::None; int lightRadius = 0; std::set litTiles; + int renderId = -1; + bool dirtyRender = true; }; class Game { @@ -97,11 +104,14 @@ public: std::list kickups; int litSpots = 0; bool dirtyLighting = true; + bool dirtyRender = true; size_t numLamps = 0; size_t numDust = 0; int player_x = 0; int player_y = 0; + int player_oldx = 0; + int player_oldy = 0; bool renderPlayer = true; Animation playerAnim {"../res/player_anim.txt"}; @@ -123,6 +133,9 @@ public: Input lastInput; bool alreadyBumped = false; Timer bumpCooldown = {500}; + Interpolation moveProgress; + bool moving = false; + bool queueDash = false; }; diff --git a/src/interpolation.h b/src/interpolation.h new file mode 100644 index 0000000..68a6221 --- /dev/null +++ b/src/interpolation.h @@ -0,0 +1,38 @@ +#ifndef INTERPOLATION_H_861230A8 +#define INTERPOLATION_H_861230A8 + +class Interpolation { +public: + + void start(int length) { + length_ = length; + thus_ = 0; + } + + void tick(double dt) { + if (thus_ < length_) { + thus_ += dt; + if (thus_ >= length_) { + thus_ = length_; + } + } + } + + // Info + + double getProgress(double from, double to) const { + return (to - from) / length_ * thus_ + from; + } + + bool isComplete() const { return thus_ == length_; } + +private: + + double progress_ = 0.0; + double dest_ = 0.0; + double start_ = 0.0; + double length_ = 0.0; + double thus_ = 0.0; +}; + +#endif /* end of include guard: INTERPOLATION_H_861230A8 */ diff --git a/src/main.cpp b/src/main.cpp index 5178110..149e1cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,15 +5,25 @@ #include "game.h" #include "renderer.h" -void incrementIfSet(Game& game, int& count, int x, int y, Tile val = Tile::Wall) +inline bool isTileSetOrNotLit(const Map& map, int x, int y) { - if (game.map.inBounds(x, y) && game.map.at(x,y).tile == val) + return (map.inBounds(x, y) && (map.at(x,y).tile == Tile::Wall || !map.at(x,y).lit)); +} + +inline bool isTileSet(const Map& map, int x, int y, Tile val = Tile::Wall) +{ + return (map.inBounds(x, y) && map.at(x,y).tile == val); +} + +inline void incrementIfSet(const Game& game, int& count, int x, int y, Tile val = Tile::Wall) +{ + if (isTileSet(game.map, x, y, val)) { count++; } } -int getZoomLevel(const Game& game) { +inline int getZoomLevel(const Game& game) { return (game.curZoom - INIT_ZOOM) / 2; } @@ -65,6 +75,11 @@ void tick( } else { temp.at(x,y).tile = Tile::Floor; } + + if (temp.at(x,y).tile != game.map.at(x,y).tile) { + temp.at(x,y).dirtyRender = true; + game.dirtyRender = true; + } } } @@ -98,9 +113,13 @@ bool movePlayer(Game& game, int x, int y) game.numDust++; } + game.player_oldx = game.player_x; + game.player_oldy = game.player_y; game.player_x = x; game.player_y = y; game.muxer.setPlayerLoc(x, y); + game.moving = true; + game.moveProgress.start(33); game.dirtyLighting = true; @@ -119,12 +138,17 @@ bool movePlayer(Game& game, int x, int y) void recalculateLighting(Game& game) { game.litSpots = 0; + game.dirtyRender = true; for (MapData& md : game.map.data()) { md.wasLit = md.lit; md.lit = false; md.litTiles.clear(); + + if (md.tile == Tile::Wall) { + md.dirtyRender = true; + } } fov_settings_type fov; @@ -203,6 +227,103 @@ void recalculateLighting(Game& game) game.dirtyLighting = false; } +void recalculateRender(Game& game) { + for (int y = game.map.getTop(); y < game.map.getBottom(); y++) + { + for (int x = game.map.getLeft(); x < game.map.getRight(); x++) + { + if (game.map.at(x,y).dirtyRender) { + game.map.at(x,y).dirtyRender = false; + + if (game.map.at(x,y).tile == Tile::Floor) { + if (std::bernoulli_distribution(0.05)(game.rng)) { + static const std::vector furnishings { + TilesetIndex(20, 16), + TilesetIndex(21, 2), + TilesetIndex(22, 2), + TilesetIndex(21, 3), + TilesetIndex(22, 3)}; + game.map.at(x,y).renderId = furnishings.at(std::uniform_int_distribution(0, furnishings.size()-1)(game.rng)); + } else { + game.map.at(x,y).renderId = -1; + } + } else if (game.map.at(x,y).tile == Tile::Wall) { + static bool initWalls = false; + static std::vector wallRenders(256, TilesetIndex(0, 0)); + if (!initWalls) { + initWalls = true; + + // Start in top left and go clockwise. + wallRenders[0b00011100] = TilesetIndex(16, 14); + wallRenders[0b00111100] = TilesetIndex(16, 14); + wallRenders[0b00011110] = TilesetIndex(16, 14); + wallRenders[0b00111110] = TilesetIndex(16, 14); + + wallRenders[0b00011111] = TilesetIndex(17, 14); + wallRenders[0b10011111] = TilesetIndex(17, 14); + wallRenders[0b00111111] = TilesetIndex(17, 14); + wallRenders[0b10111111] = TilesetIndex(17, 14); + + wallRenders[0b00000111] = TilesetIndex(18, 14); + wallRenders[0b00001111] = TilesetIndex(18, 14); + wallRenders[0b10000111] = TilesetIndex(18, 14); + wallRenders[0b10001111] = TilesetIndex(18, 14); + + wallRenders[0b01111100] = TilesetIndex(16, 15); + wallRenders[0b11111100] = TilesetIndex(16, 15); + wallRenders[0b01111110] = TilesetIndex(16, 15); + wallRenders[0b11111110] = TilesetIndex(16, 15); + + wallRenders[0b11000111] = TilesetIndex(18, 15); + wallRenders[0b11001111] = TilesetIndex(18, 15); + wallRenders[0b11100111] = TilesetIndex(18, 15); + wallRenders[0b11101111] = TilesetIndex(18, 15); + + wallRenders[0b01110000] = TilesetIndex(16, 16); + wallRenders[0b01111000] = TilesetIndex(16, 16); + wallRenders[0b11110000] = TilesetIndex(16, 16); + wallRenders[0b11111000] = TilesetIndex(16, 16); + + wallRenders[0b11110001] = TilesetIndex(17, 16); + wallRenders[0b11110011] = TilesetIndex(17, 16); + wallRenders[0b11111001] = TilesetIndex(17, 16); + wallRenders[0b11111011] = TilesetIndex(17, 16); + + wallRenders[0b11000001] = TilesetIndex(18, 16); + wallRenders[0b11000011] = TilesetIndex(18, 16); + wallRenders[0b11100001] = TilesetIndex(18, 16); + wallRenders[0b11100011] = TilesetIndex(18, 16); + + + wallRenders[0b11110111] = TilesetIndex(21, 14); + wallRenders[0b11111101] = TilesetIndex(22, 14); + wallRenders[0b11011111] = TilesetIndex(21, 15); + wallRenders[0b01111111] = TilesetIndex(22, 15); + } + + int renderDesc = 0; + if (isTileSetOrNotLit(game.map, x-1, y-1)) renderDesc |= (1 << 7); + if (isTileSetOrNotLit(game.map, x , y-1)) renderDesc |= (1 << 6); + if (isTileSetOrNotLit(game.map, x+1, y-1)) renderDesc |= (1 << 5); + if (isTileSetOrNotLit(game.map, x+1, y )) renderDesc |= (1 << 4); + if (isTileSetOrNotLit(game.map, x+1, y+1)) renderDesc |= (1 << 3); + if (isTileSetOrNotLit(game.map, x , y+1)) renderDesc |= (1 << 2); + if (isTileSetOrNotLit(game.map, x-1, y+1)) renderDesc |= (1 << 1); + if (isTileSetOrNotLit(game.map, x-1, y )) renderDesc |= (1 << 0); + + game.map.at(x,y).renderId = wallRenders.at(renderDesc); + + if (wallRenders.at(renderDesc) == 0 && renderDesc != 255) { + std::cout << renderDesc << std::endl; + } + } + } + } + } + + game.dirtyRender = false; +} + bool processKeys(Game& game, const Input& keystate) { int px = game.player_x; @@ -435,6 +556,80 @@ void setZoom(Game& game, size_t zoom) } } +void performDash(Game& game, std::mt19937& rng) { + if (game.map.at(game.player_x, game.player_y).tile == + Tile::Floor) + { + std::vector freeSpaces; + + auto addIfFree = [&] (int x, int y) { + if (game.map.inBounds(x,y) && + game.map.at(x,y).tile == Tile::Floor) + { + freeSpaces.emplace_back(x, y); + } + }; + + addIfFree(game.player_x - 1, game.player_y - 1); + addIfFree(game.player_x , game.player_y - 1); + addIfFree(game.player_x + 1, game.player_y - 1); + addIfFree(game.player_x - 1, game.player_y ); + addIfFree(game.player_x + 1, game.player_y ); + addIfFree(game.player_x - 1, game.player_y + 1); + addIfFree(game.player_x , game.player_y + 1); + addIfFree(game.player_x + 1, game.player_y + 1); + + if (!freeSpaces.empty()) + { + game.map.at(game.player_x, game.player_y).tile = Tile::Lamp; + game.numLamps++; + game.dirtyLighting = true; + kickUpDust(game, game.player_x, game.player_y, 0); + game.muxer.playSoundAtPosition("drop", game.player_x, game.player_y); + + if (game.firstInput) + { + for (int i = 0; i < 5; i++) + { + if (!processKeys(game, game.lastInput)) + { + std::uniform_int_distribution freeDist( + 0, freeSpaces.size() - 1); + + int freeIndex = freeDist(rng); + coord& moveTo = freeSpaces[freeIndex]; + + movePlayer( + game, + std::get<0>(moveTo), + std::get<1>(moveTo)); + } + + tick( + game, + game.player_x - (RADIUS - 1), + game.player_y - (RADIUS - 1), + game.player_x + RADIUS, + game.player_y + RADIUS); + } + } else { + std::uniform_int_distribution freeDist( + 0, freeSpaces.size() - 1); + + int freeIndex = freeDist(rng); + coord& moveTo = freeSpaces[freeIndex]; + + movePlayer( + game, + std::get<0>(moveTo), + std::get<1>(moveTo)); + } + + //game.muxer.playSoundAtPosition("dash", game.player_x, game.player_y); + } + } +} + int main(int, char**) { std::random_device randomEngine; @@ -527,76 +722,10 @@ int main(int, char**) { if (losing == LoseState::None) { - if (game.map.at(game.player_x, game.player_y).tile == - Tile::Floor) - { - std::vector freeSpaces; - - auto addIfFree = [&] (int x, int y) { - if (game.map.inBounds(x,y) && - game.map.at(x,y).tile == Tile::Floor) - { - freeSpaces.emplace_back(x, y); - } - }; - - addIfFree(game.player_x - 1, game.player_y - 1); - addIfFree(game.player_x , game.player_y - 1); - addIfFree(game.player_x + 1, game.player_y - 1); - addIfFree(game.player_x - 1, game.player_y ); - addIfFree(game.player_x + 1, game.player_y ); - addIfFree(game.player_x - 1, game.player_y + 1); - addIfFree(game.player_x , game.player_y + 1); - addIfFree(game.player_x + 1, game.player_y + 1); - - if (!freeSpaces.empty()) - { - game.map.at(game.player_x, game.player_y).tile = Tile::Lamp; - game.numLamps++; - game.dirtyLighting = true; - kickUpDust(game, game.player_x, game.player_y, 0); - game.muxer.playSoundAtPosition("drop", game.player_x, game.player_y); - - if (game.firstInput) - { - for (int i = 0; i < 5; i++) - { - if (!processKeys(game, game.lastInput)) - { - std::uniform_int_distribution freeDist( - 0, freeSpaces.size() - 1); - - int freeIndex = freeDist(rng); - coord& moveTo = freeSpaces[freeIndex]; - - movePlayer( - game, - std::get<0>(moveTo), - std::get<1>(moveTo)); - } - - tick( - game, - game.player_x - (RADIUS - 1), - game.player_y - (RADIUS - 1), - game.player_x + RADIUS, - game.player_y + RADIUS); - } - } else { - std::uniform_int_distribution freeDist( - 0, freeSpaces.size() - 1); - - int freeIndex = freeDist(rng); - coord& moveTo = freeSpaces[freeIndex]; - - movePlayer( - game, - std::get<0>(moveTo), - std::get<1>(moveTo)); - } - - //game.muxer.playSoundAtPosition("dash", game.player_x, game.player_y); - } + if (game.moving) { + game.queueDash = true; + } else { + performDash(game, rng); } } @@ -617,6 +746,13 @@ int main(int, char**) game.alreadyBumped = false; } + if (game.queueDash && !game.moving) { + game.queueDash = false; + if (state[SDL_SCANCODE_SPACE]) { + performDash(game, rng); + } + } + if (keystate.left || keystate.right || keystate.up || keystate.down) { game.firstInput = true; @@ -657,9 +793,18 @@ int main(int, char**) { case LoseState::None: { + if (game.moving) { + game.moveProgress.tick(frameTime); + if (game.moveProgress.isComplete()) { + game.moving = false; + } + } + while (inputAcc >= inputDt) { - processKeys(game, keystate); + if (!game.moving) { + processKeys(game, keystate); + } inputAcc -= inputDt; } @@ -747,6 +892,7 @@ int main(int, char**) } else { game.map.at(x,y).tile = Tile::Floor; } + game.map.at(x,y).dirtyRender = true; } } } @@ -759,6 +905,10 @@ int main(int, char**) setZoom(game, game.litSpots / 1500 * 2 + INIT_ZOOM); } + if (game.dirtyRender) { + recalculateRender(game); + } + zoomAcc += frameTime; while (zoomAcc >= zoomDt) diff --git a/src/renderer.cpp b/src/renderer.cpp index 0aaa14a..00b557a 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -122,6 +122,18 @@ Renderer::Renderer() } SDL_SetTextureBlendMode(playerSheet_.get(), SDL_BLENDMODE_BLEND); + + { + surface_ptr pfs(IMG_Load("../res/runninbloods.png")); + if (!pfs) + { + throw img_error(); + } + + tileset_ = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), pfs.get())); + } + + SDL_SetTextureBlendMode(tileset_.get(), SDL_BLENDMODE_BLEND); } void Renderer::render( @@ -151,12 +163,19 @@ void Renderer::render( for (int x = game.map.getLeft(); x < game.map.getRight(); x++) { bool draw = true; + bool drawColour = false; + SDL_Rect rect { + game.map.getTrueX(x) * TILE_WIDTH, + game.map.getTrueY(y) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; if (!game.map.at(x,y).lit) { if (drawDark) { SDL_SetRenderDrawColor(ren_.get(), 40, 40, 40, 255); + drawColour = true; } else { draw = false; } @@ -186,6 +205,7 @@ void Renderer::render( case Tile::Lamp: { SDL_SetRenderDrawColor(ren_.get(), 0, 255, 255, alpha); + drawColour = true; break; } } @@ -193,22 +213,47 @@ void Renderer::render( if (draw) { - SDL_Rect rect { - game.map.getTrueX(x) * TILE_WIDTH, - game.map.getTrueY(y) * TILE_HEIGHT, - TILE_WIDTH, - TILE_HEIGHT}; - - SDL_RenderFillRect(ren_.get(), &rect); + if (game.map.at(x,y).tile == Tile::Floor || game.map.at(x,y).tile == Tile::Dust) { + SDL_Rect tileRect {17 * 16, 15 * 16, 16, 16}; + SDL_RenderCopy(ren_.get(), tileset_.get(), &tileRect, &rect); + + if (game.map.at(x,y).renderId != -1) { + tileRect.x = game.map.at(x,y).renderId % 24 * 16; + tileRect.y = game.map.at(x,y).renderId / 24 * 16; + SDL_RenderCopy(ren_.get(), tileset_.get(), &tileRect, &rect); + } + } else if (game.map.at(x,y).tile == Tile::Wall) { + SDL_Rect tileRect { + game.map.at(x,y).renderId % 24 * 16, + game.map.at(x,y).renderId / 24 * 16, + 16, + 16}; + + SDL_RenderCopy(ren_.get(), tileset_.get(), &tileRect, &rect); + } - if ((game.player_x == x && game.player_y == y) && game.renderPlayer) - { - SDL_RenderCopy(ren_.get(), playerSheet_.get(), &game.playerAnim.getRenderRect(), &rect); + if (drawColour) { + SDL_RenderFillRect(ren_.get(), &rect); } } } } + if (game.renderPlayer) { + SDL_Rect rect { + game.map.getTrueX(game.player_x) * TILE_WIDTH, + game.map.getTrueY(game.player_y) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + if (game.moving) { + rect.x = game.moveProgress.getProgress(game.map.getTrueX(game.player_oldx) * TILE_WIDTH, rect.x); + rect.y = game.moveProgress.getProgress(game.map.getTrueY(game.player_oldy) * TILE_HEIGHT, rect.y); + } + + SDL_RenderCopy(ren_.get(), playerSheet_.get(), &game.playerAnim.getRenderRect(), &rect); + } + texture_ptr mask( SDL_CreateTexture( ren_.get(), @@ -250,10 +295,17 @@ void Renderer::render( SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0); SDL_RenderClear(ren_.get()); - int fadeX = game.map.getTrueX(x) - game.map.at(x,y).lightRadius; - int fadeY = game.map.getTrueY(y) - game.map.at(x,y).lightRadius; - int fadeRight = game.map.getTrueX(x) + game.map.at(x,y).lightRadius; - int fadeBottom = game.map.getTrueY(y) + game.map.at(x,y).lightRadius; + int posToUseX = game.map.getTrueX(x); + int posToUseY = game.map.getTrueY(y); + if (game.map.at(x,y).lightType == Source::Player && game.moving) { + posToUseX = game.moveProgress.getProgress(game.map.getTrueX(game.player_oldx), posToUseX); + posToUseY = game.moveProgress.getProgress(game.map.getTrueY(game.player_oldy), posToUseY); + } + + int fadeX = posToUseX - game.map.at(x,y).lightRadius; + int fadeY = posToUseY - game.map.at(x,y).lightRadius; + int fadeRight = posToUseX + game.map.at(x,y).lightRadius; + int fadeBottom = posToUseY + game.map.at(x,y).lightRadius; SDL_Rect fadeRect { fadeX * TILE_WIDTH, diff --git a/src/renderer.h b/src/renderer.h index 0416c9e..8551e26 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -130,6 +130,7 @@ private: texture_ptr lampFade_; texture_ptr dustFade_; texture_ptr playerSheet_; + texture_ptr tileset_; }; #endif /* end of include guard: RENDERER_H_6A58EC30 */ -- cgit 1.4.1