222 lines
7.3 KiB
C++
222 lines
7.3 KiB
C++
#ifndef TDGAME_TILE_HPP
|
|
#define TDGAME_TILE_HPP
|
|
|
|
#include "../grid/mesh.hpp"
|
|
#include "enemy.hpp"
|
|
#include "customjson.hpp"
|
|
#include <vector>
|
|
#include <string>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <memory>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <variant>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
struct PathProperties {
|
|
float speedMultiplier = 1.0f;
|
|
std::vector<std::string> effects;
|
|
bool isFlyingPath = true;
|
|
bool isGroundPath = true;
|
|
};
|
|
|
|
struct WallProperties {
|
|
bool blocksGround = true;
|
|
bool blocksAir = false;
|
|
bool blocksProjectiles = true;
|
|
std::vector<std::string> whitelist;
|
|
};
|
|
|
|
struct BaseProperties {
|
|
float healthBonus = 0.0f;
|
|
float defenseBonus = 0.0f;
|
|
int levelRequired = 0;
|
|
};
|
|
|
|
struct WaveDefinition {
|
|
std::string enemyId;
|
|
int count;
|
|
float interval;
|
|
float healthMult = 1.0f;
|
|
float speedMult = 1.0f;
|
|
float rewardMult = 1.0f;
|
|
};
|
|
|
|
struct SpawnProperties {
|
|
std::vector<WaveDefinition> waves;
|
|
bool loopWaves = true;
|
|
float loopHealthScaler = 0.1f;
|
|
float loopSpeedScaler = 0.05f;
|
|
int currentWaveIndex = 0;
|
|
};
|
|
|
|
struct TowerBaseProperties {
|
|
float rangeMultiplier = 1.0f;
|
|
float damageMultiplier = 1.0f;
|
|
float fireRateMultiplier = 1.0f;
|
|
std::vector<std::string> allowedTowerTypes;
|
|
};
|
|
|
|
enum TileType { EMPTY, PATH, WALL, BASE, SPAWN, TOWER_BASE, MULTI, SPECIAL };
|
|
|
|
struct Tile {
|
|
int x = 0;
|
|
int z = 0;
|
|
std::string id = "void";
|
|
TileType type = TileType::EMPTY;
|
|
std::shared_ptr<Mesh> mesh;
|
|
std::optional<PathProperties> path;
|
|
std::optional<WallProperties> wall;
|
|
std::optional<BaseProperties> base;
|
|
std::optional<SpawnProperties> spawn;
|
|
std::optional<TowerBaseProperties> towerBase;
|
|
std::map<std::string, float> specialParams;
|
|
Tile() = default;
|
|
bool isWalkable() const {
|
|
return path.has_value() && path->isGroundPath;
|
|
}
|
|
bool isBuildable() const {
|
|
return towerBase.has_value() || type == TileType::EMPTY;
|
|
}
|
|
void setMeshColor(Color c) {
|
|
if(mesh) mesh->colors({c});
|
|
}
|
|
};
|
|
|
|
class TileRegistry {
|
|
private:
|
|
std::map<std::string, Tile> _prototypes;
|
|
public:
|
|
static TileRegistry& getInstance() {
|
|
static TileRegistry instance;
|
|
return instance;
|
|
}
|
|
|
|
void loadFromDirectory(const std::string& path) {
|
|
if (!fs::exists(path)) {
|
|
std::cerr << "TileRegistry: Directory " << path << " does not exist." << std::endl;
|
|
return;
|
|
}
|
|
for (const auto& entry : fs::directory_iterator(path)) {
|
|
if (entry.path().extension() == ".json") {
|
|
loadTileFile(entry.path().string());
|
|
}
|
|
}
|
|
std::cout << "TileRegistry: Loaded " << _prototypes.size() << " tile definitions." << std::endl;
|
|
}
|
|
|
|
Tile createTile(const std::string& id, int x, int z) {
|
|
if (_prototypes.count(id)) {
|
|
Tile t = _prototypes.at(id);
|
|
t.x = x;
|
|
t.z = z;
|
|
return t;
|
|
}
|
|
Tile t;
|
|
t.x = x;
|
|
t.z = z;
|
|
t.id = "error";
|
|
std::cerr << "TileRegistry: Warning, requested unknown tile ID: " << id << std::endl;
|
|
return t;
|
|
}
|
|
private:
|
|
void loadTileFile(const std::string& filepath) {
|
|
std::ifstream f(filepath);
|
|
if (!f.is_open()) return;
|
|
|
|
std::stringstream buffer;
|
|
buffer << f.rdbuf();
|
|
std::string content = buffer.str();
|
|
|
|
try {
|
|
customJson::Node root = customJson::parse(content);
|
|
if (const auto* arr = std::get_if<std::vector<customJson::Node>>(&root.value)) {
|
|
for (const auto& item : *arr) {
|
|
parseTileJson(item.as_object());
|
|
}
|
|
} else if (const auto* obj = std::get_if<std::map<std::string, customJson::Node>>(&root.value)) {
|
|
parseTileJson(*obj);
|
|
}
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "JSON Parse error in " << filepath << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
T get_value(const std::map<std::string, customJson::Node>& obj, const std::string& key, T default_val) {
|
|
if (!obj.count(key) || obj.at(key).is_null()) return default_val;
|
|
const auto& node = obj.at(key);
|
|
if constexpr (std::is_same_v<T, bool>) return node.as_bool();
|
|
if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float> || std::is_same_v<T, int>) return static_cast<T>(node.as_double());
|
|
if constexpr (std::is_same_v<T, std::string>) return node.as_string();
|
|
return default_val;
|
|
};
|
|
|
|
void parseTileJson(const std::map<std::string, customJson::Node>& j) {
|
|
Tile t;
|
|
t.id = get_value(j, "id", std::string("unknown"));
|
|
std::string typeStr = get_value(j, "type", std::string("empty"));
|
|
|
|
if (typeStr == "path") t.type = TileType::PATH;
|
|
else if (typeStr == "wall") t.type = TileType::WALL;
|
|
else if (typeStr == "base") t.type = TileType::BASE;
|
|
else if (typeStr == "spawn") t.type = TileType::SPAWN;
|
|
else if (typeStr == "tower_base") t.type = TileType::TOWER_BASE;
|
|
else if (typeStr == "multi") t.type = TileType::MULTI;
|
|
else if (typeStr == "special") t.type = TileType::SPECIAL;
|
|
else t.type = TileType::EMPTY;
|
|
|
|
if (j.count("path")) {
|
|
const auto& p_obj = j.at("path").as_object();
|
|
PathProperties p;
|
|
p.speedMultiplier = get_value<float>(p_obj, "speed_mult", 1.0f);
|
|
p.isGroundPath = get_value<bool>(p_obj, "ground", true);
|
|
p.isFlyingPath = get_value<bool>(p_obj, "air", true);
|
|
if(p_obj.count("effects")) {
|
|
for(const auto& effect_node : p_obj.at("effects").as_array()) {
|
|
p.effects.push_back(effect_node.as_string());
|
|
}
|
|
}
|
|
t.path = p;
|
|
}
|
|
|
|
if (j.count("wall")) {
|
|
const auto& w_obj = j.at("wall").as_object();
|
|
WallProperties w;
|
|
w.blocksGround = get_value<bool>(w_obj, "block_ground", true);
|
|
w.blocksAir = get_value<bool>(w_obj, "block_air", false);
|
|
t.wall = w;
|
|
}
|
|
|
|
if (j.count("spawn")) {
|
|
const auto& s_obj = j.at("spawn").as_object();
|
|
SpawnProperties sp;
|
|
sp.loopWaves = get_value<bool>(s_obj, "loop", true);
|
|
sp.loopHealthScaler = get_value<float>(s_obj, "loop_hp_scale", 0.1f);
|
|
if (s_obj.count("waves")) {
|
|
for (const auto& w_node : s_obj.at("waves").as_array()) {
|
|
const auto& wj = w_node.as_object();
|
|
WaveDefinition wd;
|
|
wd.enemyId = get_value<std::string>(wj, "enemy_id", "grunt");
|
|
wd.count = get_value<int>(wj, "count", 5);
|
|
wd.interval = get_value<float>(wj, "interval", 1.0f);
|
|
wd.healthMult = get_value<float>(wj, "hp_mult", 1.0f);
|
|
sp.waves.push_back(wd);
|
|
}
|
|
}
|
|
t.spawn = sp;
|
|
}
|
|
|
|
if (_prototypes.count(t.id)) {
|
|
std::cerr << "Warning: Duplicate tile ID '" << t.id << "' found. Overwriting." << std::endl;
|
|
}
|
|
_prototypes[t.id] = t;
|
|
}
|
|
};
|
|
|
|
#endif |