#pragma once #include "utils.h" #include #include #include #include #include #include #include namespace rules { class RuleItem { public: friend void to_json(nlohmann::json& j, const RuleItem& ri) { j = ri.payload; } friend void from_json(const nlohmann::json& j, RuleItem& ri) { ri.payload = j; } std::string getPayload(void) const {return payload;} bool operator==(const RuleItem& rhs) const {return getPayload() == rhs.getPayload();} bool operator<(const RuleItem& rhs) const {return getPayload() < rhs.getPayload();} operator std::string() const {return getPayload();} operator bool() const {return ! getPayload().empty();} protected: std::string payload; }; class Ability : public RuleItem { public: std::string getFull() const {return abilities.at(getAbbrev());} std::string getAbbrev() const {return getPayload();} Ability() {} Ability(const std::string& abbrev) { if(! abilities.contains(abbrev)) { throw std::invalid_argument("No such ability: " + abbrev); } payload = abbrev; } virtual ~Ability() {} static Ability Str() {return Ability("str");} static Ability Dex() {return Ability("dex");} static Ability Con() {return Ability("con");} static Ability Int() {return Ability("int");} static Ability Wis() {return Ability("wis");} static Ability Cha() {return Ability("cha");} static Ability fromString(std::string s) { utils::lower(s); for(auto [abbrev, full] : abilities) { utils::lower(full); if(s == abbrev || s == full) { return Ability(abbrev); } } throw std::invalid_argument("Cannot find an ability for input: \"" + s + "\""); } private: static const std::map abilities; }; class Skill : public RuleItem { public: std::string getName() const {return getPayload();} Ability getAbility() const {return Ability(skill2ability.at(getName()));} virtual ~Skill() {} static Skill Athletics() {return Skill("Athletics");} static Skill Acrobatics() {return Skill("Acrobatics");} static Skill SleightOfHand() {return Skill("Sleight of Hand");} static Skill Stealth() {return Skill("Stealth");} static Skill Arcana() {return Skill("Arcana");} static Skill History() {return Skill("History");} static Skill Investigation() {return Skill("Investigation");} static Skill Nature() {return Skill("Nature");} static Skill Religion() {return Skill("Religion");} static Skill AnimalHandling() {return Skill("Animal Handling");} static Skill Insight() {return Skill("Insight");} static Skill Medicine() {return Skill("Medicine");} static Skill Perception() {return Skill("Perception");} static Skill Survival() {return Skill("Survival");} static Skill Deception() {return Skill("Deception");} static Skill Intimidation() {return Skill("Intimidation");} static Skill Performance() {return Skill("Performance");} static Skill Persuasion() {return Skill("Persuasion");} Skill() {}; Skill(const std::string& name) { if(! skill2ability.contains(name)) { throw std::invalid_argument("No such skill: " + name); } payload = name; } static Skill fromString(std::string s) { utils::lower(s); for(auto& [name, _] : skill2ability) { std::string n = name; utils::lower(n); if(s == n) { return Skill(name); } } throw std::invalid_argument("Cannot find a skill for input: \"" + s + "\""); } private: static const std::map skill2ability; }; class Qualifier : public RuleItem { public: Qualifier() {} Qualifier(const std::string& negative) { if(! negative2positive.contains(negative)) { throw std::invalid_argument("No such qualifier: " + negative); } payload = negative; } std::string getNegative() const {return getPayload();} std::string getPositive() const {return negative2positive.at(getNegative());} virtual ~Qualifier() {} static Qualifier Magical() {return Qualifier("nonmagical");} static Qualifier Silvered() {return Qualifier("non-silvered");} static Qualifier Adamantine() {return Qualifier("non-adamantine");} private: static const std::map negative2positive; }; class Condition : public RuleItem { public: Condition() {} Condition(std::string name) { utils::lower(name); if(conditions.count(name) == 0) { throw std::invalid_argument("No such condition: " + name); } payload = name; } virtual ~Condition() {} void incExhaustion() { if(payload.find("exhausted") == std::string::npos) { throw std::invalid_argument("Cannot increment exhaustion on condition " + payload); } int exhLvl = utils::parseInt(std::string(1, payload.back())); if(exhLvl >= 6) { throw std::invalid_argument("Cannot increment exhaustion beyond level 6"); } payload.back() = '0'+exhLvl+1; } private: static const std::set conditions; }; std::ostream& operator<<(std::ostream& os, const Ability& a); std::ostream& operator<<(std::ostream& os, const Skill& s); std::ostream& operator<<(std::ostream& os, const Qualifier& q); std::ostream& operator<<(std::ostream& os, const Condition& c); template T tryGetAbilityOrSkill(std::string src) { try { return T::fromString(src); } catch(std::exception& e) {} // eat. return T(); } }