#pragma once
#include <string>
#include <memory>
#include <vector>
#include <map>
#include <stdexcept>
#include <cstdint>
#include <cstring>
extern "C" {
typedef struct PraedaGeneratorHandle PraedaGeneratorHandle;
typedef struct CItemArrayHandle CItemArrayHandle;
typedef struct {
char* name;
double initial_value;
double min;
double max;
uint8_t required;
double scaling_factor;
double chance;
} CItemAttribute;
typedef struct {
char* name;
CItemAttribute* attributes;
uint32_t attributes_count;
} CAffix;
typedef struct {
char* name;
char* quality;
char* item_type;
char* subtype;
CAffix prefix;
CAffix suffix;
CItemAttribute* attributes;
uint32_t attributes_count;
} CItem;
PraedaGeneratorHandle* praeda_generator_new(void);
void praeda_generator_free(PraedaGeneratorHandle* handle);
void praeda_string_free(char* ptr);
void praeda_error_free(char* ptr);
void praeda_item_array_free(CItemArrayHandle* handle);
int praeda_generator_load_toml(
PraedaGeneratorHandle* handle,
const char* toml_str,
char** error_out
);
int praeda_generator_set_quality_data(
PraedaGeneratorHandle* handle,
const char* quality_name,
int weight
);
int praeda_generator_set_item_type(
PraedaGeneratorHandle* handle,
const char* type_name,
int weight
);
int praeda_generator_set_item_subtype(
PraedaGeneratorHandle* handle,
const char* type_name,
const char* subtype_name,
int weight
);
int praeda_generator_set_attribute(
PraedaGeneratorHandle* handle,
const char* type_name,
const char* subtype_name,
const char* attr_name,
double initial_value,
double min_value,
double max_value,
int required
);
int praeda_generator_set_item_names(
PraedaGeneratorHandle* handle,
const char* type_name,
const char* subtype_name,
const char** names,
uint32_t names_count
);
int praeda_generator_set_prefix_attribute(
PraedaGeneratorHandle* handle,
const char* type_name,
const char* subtype_name,
const char* affix_name,
const char* attr_name,
double initial_value,
double min_value,
double max_value,
int required
);
int praeda_generator_set_suffix_attribute(
PraedaGeneratorHandle* handle,
const char* type_name,
const char* subtype_name,
const char* affix_name,
const char* attr_name,
double initial_value,
double min_value,
double max_value,
int required
);
CItemArrayHandle* praeda_generator_generate_loot(
PraedaGeneratorHandle* handle,
uint32_t number_of_items,
double base_level,
double level_variance,
double affix_chance,
uint8_t linear,
double scaling_factor,
char** error_out
);
uint32_t praeda_item_array_count(const CItemArrayHandle* handle);
const CItem* praeda_item_array_get(const CItemArrayHandle* handle, uint32_t index);
int praeda_generator_has_quality(const PraedaGeneratorHandle* handle, const char* quality);
char* praeda_version(void);
}
namespace praeda {
class CStringWrapper {
public:
explicit CStringWrapper(char* ptr) : ptr_(ptr) {}
~CStringWrapper() {
if (ptr_) {
praeda_error_free(ptr_);
}
}
CStringWrapper(const CStringWrapper&) = delete;
CStringWrapper& operator=(const CStringWrapper&) = delete;
CStringWrapper(CStringWrapper&& other) noexcept : ptr_(other.release()) {}
CStringWrapper& operator=(CStringWrapper&& other) noexcept {
if (ptr_) praeda_error_free(ptr_);
ptr_ = other.release();
return *this;
}
const char* c_str() const { return ptr_ ? ptr_ : ""; }
std::string str() const { return std::string(ptr_ ? ptr_ : ""); }
char* release() {
char* temp = ptr_;
ptr_ = nullptr;
return temp;
}
private:
char* ptr_;
};
class Exception : public std::runtime_error {
public:
explicit Exception(const std::string& what) : std::runtime_error(what) {}
};
class ItemAttribute {
public:
ItemAttribute() : initial_value(0.0), min(0.0), max(0.0), required(false),
scaling_factor(1.0), chance(0.0) {}
ItemAttribute(const std::string& n, double iv, double min_val, double max_val, bool req)
: name(n), initial_value(iv), min(min_val), max(max_val), required(req),
scaling_factor(1.0), chance(0.0) {}
std::string name;
double initial_value;
double min;
double max;
bool required;
double scaling_factor;
double chance;
static ItemAttribute from_c(const CItemAttribute& c_attr) {
ItemAttribute attr;
attr.name = std::string(c_attr.name ? c_attr.name : "");
attr.initial_value = c_attr.initial_value;
attr.min = c_attr.min;
attr.max = c_attr.max;
attr.required = c_attr.required != 0;
attr.scaling_factor = c_attr.scaling_factor;
attr.chance = c_attr.chance;
return attr;
}
};
class Affix {
public:
Affix() = default;
Affix(const std::string& n, const std::vector<ItemAttribute>& attrs)
: name(n), attributes(attrs) {}
std::string name;
std::vector<ItemAttribute> attributes;
static Affix from_c(const CAffix& c_affix) {
Affix affix;
affix.name = std::string(c_affix.name ? c_affix.name : "");
for (uint32_t i = 0; i < c_affix.attributes_count; ++i) {
affix.attributes.push_back(ItemAttribute::from_c(c_affix.attributes[i]));
}
return affix;
}
};
class Item {
public:
Item() = default;
Item(const std::string& n, const std::string& q, const std::string& t,
const std::string& st, const Affix& pre, const Affix& suf,
const std::map<std::string, ItemAttribute>& attrs)
: name(n), quality(q), type(t), subtype(st),
prefix(pre), suffix(suf), attributes(attrs) {}
std::string name;
std::string quality;
std::string type;
std::string subtype;
Affix prefix;
Affix suffix;
std::map<std::string, ItemAttribute> attributes;
private:
friend class Generator;
static Item from_c(const CItem& c_item) {
Item item;
item.name = std::string(c_item.name ? c_item.name : "");
item.quality = std::string(c_item.quality ? c_item.quality : "");
item.type = std::string(c_item.item_type ? c_item.item_type : "");
item.subtype = std::string(c_item.subtype ? c_item.subtype : "");
item.prefix = Affix::from_c(c_item.prefix);
item.suffix = Affix::from_c(c_item.suffix);
for (uint32_t i = 0; i < c_item.attributes_count; ++i) {
const CItemAttribute& c_attr = c_item.attributes[i];
ItemAttribute attr = ItemAttribute::from_c(c_attr);
item.attributes[attr.name] = attr;
}
return item;
}
};
struct GenerationOptions {
uint32_t number_of_items = 1;
double base_level = 1.0;
double level_variance = 1.0;
double affix_chance = 0.25;
bool linear = true;
double scaling_factor = 1.0;
};
class Generator {
public:
static std::unique_ptr<Generator> create() {
PraedaGeneratorHandle* handle = praeda_generator_new();
if (!handle) {
throw Exception("Failed to create generator");
}
return std::unique_ptr<Generator>(new Generator(handle));
}
~Generator() {
if (handle_) {
praeda_generator_free(handle_);
}
}
Generator(const Generator&) = delete;
Generator& operator=(const Generator&) = delete;
Generator(Generator&& other) noexcept : handle_(other.release()) {}
Generator& operator=(Generator&& other) noexcept {
if (handle_) praeda_generator_free(handle_);
handle_ = other.release();
return *this;
}
void load_toml_string(const std::string& toml_content) {
char* error = nullptr;
int result = praeda_generator_load_toml(handle_, toml_content.c_str(), &error);
if (result != 0) {
if (error) {
CStringWrapper error_wrapper(error);
throw Exception(error_wrapper.str());
}
throw Exception("Failed to load TOML");
}
}
void set_quality_data(const std::string& quality_name, int weight) {
int result = praeda_generator_set_quality_data(handle_, quality_name.c_str(), weight);
if (result != 0) {
throw Exception("Failed to set quality data");
}
}
void set_item_type(const std::string& type_name, int weight) {
int result = praeda_generator_set_item_type(handle_, type_name.c_str(), weight);
if (result != 0) {
throw Exception("Failed to set item type");
}
}
void set_item_subtype(const std::string& type_name, const std::string& subtype_name, int weight) {
int result = praeda_generator_set_item_subtype(
handle_,
type_name.c_str(),
subtype_name.c_str(),
weight
);
if (result != 0) {
throw Exception("Failed to set item subtype");
}
}
void set_attribute(const std::string& type_name, const std::string& subtype_name,
const ItemAttribute& attribute) {
int result = praeda_generator_set_attribute(
handle_,
type_name.c_str(),
subtype_name.c_str(),
attribute.name.c_str(),
attribute.initial_value,
attribute.min,
attribute.max,
attribute.required ? 1 : 0
);
if (result != 0) {
throw Exception("Failed to set attribute");
}
}
void set_item_names(const std::string& type_name, const std::string& subtype_name,
const std::vector<std::string>& names) {
std::vector<const char*> c_names;
for (const auto& name : names) {
c_names.push_back(name.c_str());
}
int result = praeda_generator_set_item_names(
handle_,
type_name.c_str(),
subtype_name.c_str(),
c_names.data(),
static_cast<uint32_t>(c_names.size())
);
if (result != 0) {
throw Exception("Failed to set item names");
}
}
void set_prefix_attribute(const std::string& type_name, const std::string& subtype_name,
const std::string& affix_name, const ItemAttribute& attribute) {
int result = praeda_generator_set_prefix_attribute(
handle_,
type_name.c_str(),
subtype_name.c_str(),
affix_name.c_str(),
attribute.name.c_str(),
attribute.initial_value,
attribute.min,
attribute.max,
attribute.required ? 1 : 0
);
if (result != 0) {
throw Exception("Failed to set prefix attribute");
}
}
void set_suffix_attribute(const std::string& type_name, const std::string& subtype_name,
const std::string& affix_name, const ItemAttribute& attribute) {
int result = praeda_generator_set_suffix_attribute(
handle_,
type_name.c_str(),
subtype_name.c_str(),
affix_name.c_str(),
attribute.name.c_str(),
attribute.initial_value,
attribute.min,
attribute.max,
attribute.required ? 1 : 0
);
if (result != 0) {
throw Exception("Failed to set suffix attribute");
}
}
std::vector<Item> generate_loot(const GenerationOptions& options) {
char* error = nullptr;
CItemArrayHandle* array_handle = praeda_generator_generate_loot(
handle_,
options.number_of_items,
options.base_level,
options.level_variance,
options.affix_chance,
options.linear ? 1 : 0,
options.scaling_factor,
&error
);
if (!array_handle) {
if (error) {
CStringWrapper error_wrapper(error);
throw Exception(error_wrapper.str());
}
throw Exception("Failed to generate loot");
}
std::vector<Item> items;
uint32_t count = praeda_item_array_count(array_handle);
for (uint32_t i = 0; i < count; ++i) {
const CItem* c_item = praeda_item_array_get(array_handle, i);
if (c_item) {
items.push_back(Item::from_c(*c_item));
}
}
praeda_item_array_free(array_handle);
return items;
}
bool has_quality(const std::string& quality) {
int result = praeda_generator_has_quality(handle_, quality.c_str());
if (result < 0) {
throw Exception("Error checking quality");
}
return result == 1;
}
std::string info() {
char* v = praeda_version();
CStringWrapper wrapper(v);
return wrapper.str();
}
PraedaGeneratorHandle* native_handle() const { return handle_; }
private:
PraedaGeneratorHandle* handle_;
explicit Generator(PraedaGeneratorHandle* handle) : handle_(handle) {}
PraedaGeneratorHandle* release() {
PraedaGeneratorHandle* temp = handle_;
handle_ = nullptr;
return temp;
}
};
inline std::string version() {
char* v = praeda_version();
CStringWrapper wrapper(v);
return wrapper.str();
}
}