#ifndef MAP_H_3AB00D12 #define MAP_H_3AB00D12 #include #include #include #include #include #include #include "consts.h" #include "direction.h" enum class Tile { Floor, Wall, Dust, Lamp }; enum class Source { None, Dust, Lamp, Player }; struct MapData { bool lit = false; bool wasLit = false; size_t dustLife = 0; Source lightType = Source::None; int lightRadius = 0; std::set litTiles; int renderId = -1; bool dirtyRender = true; bool sign = false; std::string text; }; struct Chunk { std::array tiles; std::array data; int litTiles = 0; int x; int y; }; inline void toChunkPos(int x, int y, int& cx, int& cy) { cx = std::floor(static_cast(x) / CHUNK_WIDTH); cy = std::floor(static_cast(y) / CHUNK_HEIGHT); } class Map { public: inline int getLeft() const { return left_; } inline int getRight() const { return left_ + width_; } inline int getTop() const { return top_; } inline int getBottom() const { return top_ + height_; } inline int getWidth() const { return width_; } inline int getHeight() const { return height_; } inline int getTrueX(int x) const { return (x - left_); } inline int getTrueY(int y) const { return (y - top_); } inline bool inBounds(int x, int y) const { return (x >= left_) && (x < left_ + width_) && (y >= top_) && (y < top_ + height_); } inline const Tile& tile(int x, int y) const { return loadedTiles_[(x - left_) + width_ * (y - top_)]; } inline Tile& tile(int x, int y) { return const_cast(static_cast(*this).tile(x, y)); } inline const MapData& at(int x, int y) const { return loaded_[(x - left_) + width_ * (y - top_)]; } inline MapData& at(int x, int y) { return const_cast(static_cast(*this).at(x, y)); } inline const std::vector& tiles() const { return loadedTiles_; } inline std::vector& tiles() { return loadedTiles_; } inline const std::vector& data() const { return loaded_; } inline std::vector& data() { return loaded_; } inline int getUnloadedLitTiles() const { return unloadedLitTiles_; } void load(int newLeftChunk, int newTopChunk, int newWidthChunks, int newHeightChunks, std::mt19937& rng) { // Flush the currently loaded data as long as there is any (this isn't the first load). if (!loaded_.empty()) { std::vector touchedChunks; for (int chunkY = 0; chunkY < chunksVert_; chunkY++) { for (int chunkX = 0; chunkX < chunksHoriz_; chunkX++) { size_t chunkIndex = chunkByPos_.at(leftmostChunk_ + chunkX).at(topmostChunk_ + chunkY); touchedChunks.push_back(chunkIndex); Chunk& chunk = chunks_.at(chunkIndex); chunk.litTiles = 0; int startX = chunkX * CHUNK_WIDTH; int startY = chunkY * CHUNK_HEIGHT; for (int y=0; y(width_ * height_); loadedTiles_ = std::vector(width_ * height_, Tile::Floor); dirtyTiles_.clear(); dirtySet_ = std::vector(width_ * height_, false); // Load in the requested chunks. for (int chunkY = 0; chunkY < chunksVert_; chunkY++) { for (int chunkX = 0; chunkX < chunksHoriz_; chunkX++) { // Instantiate a new chunk if necessary. if (!chunkByPos_[leftmostChunk_ + chunkX].count(topmostChunk_ + chunkY)) { size_t chunkIndex; if (!freeList_.empty()) { chunkIndex = freeList_.front(); freeList_.pop_front(); } else { chunkIndex = chunks_.size(); chunks_.emplace_back(); } chunkByPos_[leftmostChunk_ + chunkX][topmostChunk_ + chunkY] = chunkIndex; Chunk& chunk = chunks_[chunkIndex]; chunk.x = leftmostChunk_ + chunkX; chunk.y = topmostChunk_ + chunkY; chunk.litTiles = 0; for (Tile& tile : chunk.tiles) { if (std::bernoulli_distribution(0.5)(rng)) { tile = Tile::Wall; } else { tile = Tile::Floor; } } for (MapData& md : chunk.data) { md.dirtyRender = true; } } size_t chunkIndex = chunkByPos_[leftmostChunk_ + chunkX][topmostChunk_ + chunkY]; Chunk& chunk = chunks_[chunkIndex]; for (int y = 0; y < CHUNK_HEIGHT; y++) { std::copy( std::next(std::begin(chunk.tiles), y*CHUNK_WIDTH), std::next(std::begin(chunk.tiles), (y+1)*CHUNK_WIDTH), std::next(std::begin(loadedTiles_), (chunkY*CHUNK_HEIGHT + y)*width_ + chunkX*CHUNK_WIDTH)); std::copy( std::next(std::begin(chunk.data), y*CHUNK_WIDTH), std::next(std::begin(chunk.data), (y+1)*CHUNK_WIDTH), std::next(std::begin(loaded_), (chunkY*CHUNK_HEIGHT + y)*width_ + chunkX*CHUNK_WIDTH)); } unloadedLitTiles_ -= chunk.litTiles; } } } void markDirtyAround(int x, int y) { if (x > left_) { if (y > top_) { markDirty(x-1, y-1); } markDirty(x-1, y ); if (y < top_+height_-1) { markDirty(x-1, y+1); } } if (y > top_) { markDirty(x , y-1); } markDirty(x , y ); if (y < top_+height_-1) { markDirty(x , y+1); } if (x < left_+width_-1) { if (y > top_) { markDirty(x+1, y-1); } markDirty(x+1, y ); if (y < top_+height_-1) { markDirty(x+1, y+1); } } } void markDirty(int x, int y) { size_t index = (x-left_)+(y-top_)*width_; if (!dirtySet_[index]) { dirtySet_[index] = true; dirtyTiles_.emplace_back(x,y); } } const std::vector getDirty() const { return dirtyTiles_; } void resetDirty() { for (auto [x,y] : dirtyTiles_) { dirtySet_[(x-left_)+(y-top_)*width_] = false; } dirtyTiles_.clear(); } private: int left_; int top_; int width_; int height_; int leftmostChunk_; int topmostChunk_; int chunksHoriz_; int chunksVert_; std::vector loadedTiles_; std::vector loaded_; std::vector chunks_; std::list freeList_; std::map> chunkByPos_; // chunkByPos_[X][Y] int unloadedLitTiles_ = 0; std::vector dirtyTiles_; std::vector dirtySet_; }; #endif /* end of include guard: MAP_H_3AB00D12 */