implement SurroundMap & update AreaMap2D

This commit is contained in:
MihailRis 2024-09-10 20:34:26 +03:00
parent dc79c150da
commit fdefbda49f
5 changed files with 100 additions and 22 deletions

View File

@ -10,9 +10,9 @@ namespace util {
template<class T, typename TCoord=int>
class AreaMap2D {
public:
using OutCallback = std::function<void(const T&)>;
using OutCallback = std::function<void(TCoord, TCoord, const T&)>;
private:
TCoord offsetX, offsetY;
TCoord offsetX = 0, offsetY = 0;
TCoord sizeX, sizeY;
std::vector<T> firstBuffer;
std::vector<T> secondBuffer;
@ -35,7 +35,7 @@ namespace util {
}
if (nx < 0 || ny < 0 || nx >= sizeX || ny >= sizeY) {
if (outCallback) {
outCallback(value);
outCallback(x + offsetX, y + offsetY, value);
}
valuesCount--;
continue;
@ -71,6 +71,23 @@ namespace util {
return firstBuffer[ly * sizeX + lx];
}
T get(TCoord x, TCoord y, const T& def) const {
if (auto ptr = getIf(x, y)) {
const auto& value = *ptr;
if (value == T{}) {
return def;
}
return value;
}
return def;
}
bool isInside(TCoord x, TCoord y) const {
auto lx = x - offsetX;
auto ly = y - offsetY;
return !(lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY);
}
const T& require(TCoord x, TCoord y) const {
auto lx = x - offsetX;
auto ly = y - offsetY;
@ -134,11 +151,14 @@ namespace util {
}
void clear() {
for (TCoord i = 0; i < sizeX * sizeY; i++) {
auto value = firstBuffer[i];
firstBuffer[i] = {};
if (outCallback) {
outCallback(value);
for (TCoord y = 0; y < sizeY; y++) {
for (TCoord x = 0; x < sizeX; x++) {
auto i = y * sizeX + x;
auto value = firstBuffer[i];
firstBuffer[i] = {};
if (outCallback) {
outCallback(x + offsetX, y + offsetY, value);
}
}
}
valuesCount = 0;

View File

@ -35,7 +35,7 @@ Chunks::Chunks(
areaMap(w, d),
worldFiles(wfile) {
areaMap.setCenter(ox-w/2, oz-d/2);
areaMap.setOutCallback([this](const auto& chunk) {
areaMap.setOutCallback([this](int, int, const auto& chunk) {
save(chunk.get());
});
}

View File

@ -4,25 +4,63 @@
#include <cstring>
#include <stdexcept>
SurroundMap::SurroundMap(int loadDistance, ubyte maxLevel)
SurroundMap::SurroundMap(int loadDistance, int8_t maxLevel)
: areaMap((loadDistance + maxLevel) * 2 + 1,
(loadDistance + maxLevel) * 2 + 1),
levelCallbacks(maxLevel),
maxLevel(maxLevel)
{}
void SurroundMap::setLevelCallback(int level, LevelCallback callback) {
levelCallbacks.at(level) = callback;
void SurroundMap::setLevelCallback(int8_t level, LevelCallback callback) {
auto& wrapper = levelCallbacks.at(level);
wrapper.callback = callback;
wrapper.active = callback != nullptr;
}
void SurroundMap::setOutCallback(util::AreaMap2D<ubyte>::OutCallback callback) {
void SurroundMap::setOutCallback(util::AreaMap2D<int8_t>::OutCallback callback) {
areaMap.setOutCallback(callback);
}
void SurroundMap::upgrade(int x, int y, int8_t level) {
auto& callback = levelCallbacks[level];
int size = maxLevel - level + 1;
for (int ly = -size+1; ly < size; ly++) {
for (int lx = -size+1; lx < size; lx++) {
int posX = lx + x;
int posY = ly + y;
int8_t sourceLevel = areaMap.get(posX, posY, 0);
if (sourceLevel < level-1) {
throw std::runtime_error("invalid map state");
}
if (sourceLevel >= level) {
continue;
}
areaMap.set(posX, posY, level);
if (callback.active) {
callback.callback(posX, posY);
}
}
}
}
void SurroundMap::completeAt(int x, int y) {
// TODO
if (!areaMap.isInside(x - maxLevel + 1, y - maxLevel + 1) ||
!areaMap.isInside(x + maxLevel - 1, y + maxLevel - 1)) {
throw std::invalid_argument(
"upgrade square is not fully inside of area");
}
for (int8_t level = 1; level <= maxLevel; level++) {
upgrade(x, y, level);
}
}
void SurroundMap::setCenter(int x, int y) {
areaMap.setCenter(x, y);
}
int8_t SurroundMap::at(int x, int y) {
if (auto ptr = areaMap.getIf(x, y)) {
return *ptr;
}
throw std::invalid_argument("position is out of area");
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <unordered_map>
#include <functional>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>
@ -10,18 +11,34 @@
class SurroundMap {
public:
using LevelCallback = std::function<void(ubyte)>;
using LevelCallback = std::function<void(int, int)>;
struct LevelCallbackWrapper {
LevelCallback callback;
bool active;
};
private:
util::AreaMap2D<ubyte> areaMap;
std::vector<LevelCallback> levelCallbacks;
ubyte maxLevel;
util::AreaMap2D<int8_t> areaMap;
std::vector<LevelCallbackWrapper> levelCallbacks;
int8_t maxLevel;
void upgrade(int x, int y, int8_t level);
public:
SurroundMap(int loadDistance, ubyte maxLevel);
SurroundMap(int loadDistance, int8_t maxLevel);
void setLevelCallback(int level, LevelCallback callback);
void setOutCallback(util::AreaMap2D<ubyte>::OutCallback callback);
/// @brief Callback called on point level increments
void setLevelCallback(int8_t level, LevelCallback callback);
/// @brief Callback called when non-zero value moves out of area
void setOutCallback(util::AreaMap2D<int8_t>::OutCallback callback);
/// @brief Upgrade point to maxLevel
/// @throws std::invalid_argument - upgrade square is not fully inside
void completeAt(int x, int y);
/// @brief Set map area center
void setCenter(int x, int y);
/// @brief Get level at position
/// @throws std::invalid_argument - position is out of area
int8_t at(int x, int y);
};

View File

@ -3,5 +3,8 @@
#include "world/generator/SurroundMap.hpp"
TEST(SurroundMap, InitTest) {
SurroundMap map(50, 8);
int8_t maxLevel = 2;
SurroundMap map(50, maxLevel);
map.completeAt(25, 25);
EXPECT_EQ(map.at(25, 25), maxLevel);
}