tdgame test branch by gemini.
This commit is contained in:
222
util/tdgame/tile.hpp
Normal file
222
util/tdgame/tile.hpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user