Files
brandling-safari/shared/Roster.cpp
Gabe 238cd9c6c3 Brandling Safari: LAN safari-zone creature catching on the HD-2D engine
- Authoritative 30 Hz server (ENet, SQLite): accounts, characters, wild AI, tall grass /
  water / wandering spawns, fishing, captures, follower chains, clock + weather, LAN beacon
- Client: title / join / character creator / play screens, prediction + reconciliation,
  interpolated players and parties, HD-2D UI (dialogue with Professor Henry, HUD, collection)
- Shared deterministic zone generator, movement, protocol, roster, appearance
- Paper-doll character art pipeline (PixelLab base walk + per-item animation edits)
- Brandling roster from the company list (logo colors, jev types), Blender safari props
- Headless test bot
2026-10-02 15:20:27 -05:00

97 lines
3.5 KiB
C++

#include "Roster.h"
#include "Appearance.h"
#include <nlohmann/json.hpp>
#include <algorithm>
#include <fstream>
#include <map>
namespace bs {
namespace {
std::string str(const nlohmann::json& j, const char* key, const std::string& def = {}) {
auto it = j.find(key);
return it != j.end() && it->is_string() ? it->get<std::string>() : def;
}
float num(const nlohmann::json& j, const char* key, float def) {
auto it = j.find(key);
return it != j.end() && it->is_number() ? it->get<float>() : def;
}
} // namespace
bool Roster::load(const std::string& path, std::string* error) {
std::ifstream f(path);
if (!f) {
if (error) *error = "cannot open " + path;
return false;
}
try {
nlohmann::json j = nlohmann::json::parse(f);
species_.clear();
for (const auto& s : j.at("species")) {
Species sp;
sp.id = s.at("id").get<uint16_t>();
sp.slug = str(s, "slug");
sp.name = str(s, "name", sp.slug);
sp.domain = str(s, "domain");
sp.type = str(s, "type", "normal");
sp.type2 = str(s, "type2");
sp.body = str(s, "body");
std::string r = str(s, "rarity", "common");
sp.rarity = r == "legendary" ? Rarity::Legendary : r == "rare" ? Rarity::Rare
: r == "uncommon" ? Rarity::Uncommon : Rarity::Common;
std::string h = str(s, "habitat", "grass");
sp.habitat = h == "water" ? Habitat::Water : h == "both" ? Habitat::Both : Habitat::Grass;
sp.catchRate = num(s, "catchRate", 0.5f);
sp.fleeRate = num(s, "fleeRate", 0.1f);
if (s.contains("colors") && s["colors"].is_object()) {
sp.mainColor = parseHexColor(str(s["colors"], "main", "#888888"));
sp.accentColor = parseHexColor(str(s["colors"], "accent", "#cccccc"));
}
sp.sprite = str(s, "sprite");
sp.anchorRow = int(num(s, "anchorRow", 63.0f));
sp.retired = s.contains("retired") && s["retired"].is_boolean() && s["retired"].get<bool>();
species_.push_back(sp);
}
// ids index the vector directly
std::sort(species_.begin(), species_.end(), [](const Species& a, const Species& b) { return a.id < b.id; });
for (size_t i = 0; i < species_.size(); ++i)
if (species_[i].id != i) {
if (error) *error = path + ": species ids must be 0..N-1";
return false;
}
} catch (const std::exception& e) {
if (error) *error = path + ": " + e.what();
return false;
}
if (species_.empty()) {
if (error) *error = path + ": no species";
return false;
}
return true;
}
const char* rarityName(Rarity r) {
switch (r) {
case Rarity::Uncommon: return "Uncommon";
case Rarity::Rare: return "Rare";
case Rarity::Legendary: return "Legendary";
default: return "Common";
}
}
uint32_t typeColor(const std::string& type) {
static const std::map<std::string, uint32_t> colors = {
{"normal", 0xa8a878}, {"fire", 0xf08030}, {"water", 0x6890f0}, {"grass", 0x78c850},
{"electric", 0xf8d030}, {"ice", 0x98d8d8}, {"fighting", 0xc03028}, {"poison", 0xa040a0},
{"ground", 0xe0c068}, {"flying", 0xa890f0}, {"psychic", 0xf85888}, {"bug", 0xa8b820},
{"rock", 0xb8a038}, {"ghost", 0x705898}, {"dragon", 0x7038f8},
};
auto it = colors.find(type);
return it == colors.end() ? 0xa8a878 : it->second;
}
} // namespace bs