diff options
author | Your Name <you@example.com> | 2021-04-29 14:17:08 -0400 |
---|---|---|
committer | Your Name <you@example.com> | 2021-04-29 14:17:08 -0400 |
commit | 5a813a75412ac9b8fadb90c9abd46dd95aee8e9b (patch) | |
tree | 75c5466d459c793430a6481cd276a624cd843794 /src | |
parent | cd57ad6e208728bafcbc8c7d7b85d88603706978 (diff) | |
download | dmtool-5a813a75412ac9b8fadb90c9abd46dd95aee8e9b.tar.gz dmtool-5a813a75412ac9b8fadb90c9abd46dd95aee8e9b.tar.bz2 dmtool-5a813a75412ac9b8fadb90c9abd46dd95aee8e9b.zip |
Removed data files from repo
Diffstat (limited to 'src')
-rw-r--r-- | src/armor.cc | 31 | ||||
-rw-r--r-- | src/armor.h | 11 | ||||
-rw-r--r-- | src/creature.cc | 99 | ||||
-rw-r--r-- | src/creature.h | 49 | ||||
-rw-r--r-- | src/dmtool.cc | 140 | ||||
-rw-r--r-- | src/entry.cc | 29 | ||||
-rw-r--r-- | src/entry.h | 16 | ||||
-rw-r--r-- | src/jsonable.h | 3 | ||||
-rw-r--r-- | src/rules.h | 1 | ||||
-rw-r--r-- | src/settings.cc | 7 | ||||
-rw-r--r-- | src/spell.cc | 17 | ||||
-rw-r--r-- | src/spell.h | 41 | ||||
-rw-r--r-- | src/spellcasting.cc | 32 | ||||
-rw-r--r-- | src/spellcasting.h | 10 | ||||
-rw-r--r-- | src/utils.cc | 18 | ||||
-rw-r--r-- | src/utils.h | 7 | ||||
-rw-r--r-- | src/weapon.cc | 60 | ||||
-rw-r--r-- | src/weapon.h | 13 |
18 files changed, 368 insertions, 216 deletions
diff --git a/src/armor.cc b/src/armor.cc index 54284c7..85c5bc2 100644 --- a/src/armor.cc +++ b/src/armor.cc @@ -8,21 +8,13 @@ using namespace std; namespace entry{ - string Armor::getText(const creature::Creature& c) const { - return genText(*this, c); - } - - string genText(const Armor& a, const creature::Creature& c) { + string getTextHelper(const Armor& a, string dexBonusLight, string dexBonusMedium) { stringstream text; - text << genText(static_cast<const Item&>(a), c); - text << ": AC: " << a.getACBonus(); + text << "AC: " << a.getACBonus(); if(a.getArmorType() == "light") { - text << " + dex (i.e., " << (a.getACBonus() + c.getBonus(rules::Ability::Dex())) << ")"; + text << " + dex" << dexBonusLight; } else if(a.getArmorType() == "medium") { - int actualBonus = a.getACBonus(); - int dex = c.getBonus(rules::Ability::Dex()); - actualBonus += (dex > 2)? 2 : dex; - text << " + dex max 2 (i.e., " << actualBonus << ")"; + text << " + dex max 2" << dexBonusMedium; } if(a.getStrRequirement() > 0) { text << ", Mininum str: " << a.getStrRequirement(); @@ -33,4 +25,19 @@ namespace entry{ text << ". " << genText(static_cast<const Substantial&>(a)); return text.str(); } + + string Armor::getText() const { + return getTextHelper(*this, "", ""); + } + + + string genText(const Armor& a, const creature::Creature& c) { + stringstream text; + text << genText(static_cast<const Item&>(a), c); + int dex = c.getBonus(rules::Ability::Dex()); + string dexBonusLight = " (i.e., " + to_string(a.getACBonus() + dex) + ")"; + string dexBonusMedium = " (i.e., " + to_string(a.getACBonus() + ((dex > 2)? 2 : dex)) + ")"; + text << ": " << getTextHelper(a, dexBonusLight, dexBonusMedium); + return text.str(); + } } diff --git a/src/armor.h b/src/armor.h index 57402cb..0d148b7 100644 --- a/src/armor.h +++ b/src/armor.h @@ -3,9 +3,13 @@ #include "json.hpp" namespace entry { + class Armor; + + std::string genText(const Armor& a, const creature::Creature& c); + class Armor : public Item , public Substantial { public: - Armor(const nlohmann::json& data, const nlohmann::json& base) : Item(base), acBonus(data["ac"]), armorType(data["type"]), strRequirement(data["strength"]), stealthDis(data["disadvantage"]), cost(data["cost"]), weight(data["weight"]) {} + Armor(const nlohmann::json& data, const nlohmann::json& base) : Item(base), acBonus(data["ac"]), armorType(data["armor_type"]), strRequirement(data["strength"]), stealthDis(data["disadvantage"]), cost(data["cost"]), weight(data["weight"]) {} int getACBonus(void) const {return acBonus;} std::string getArmorType(void) const {return armorType;} @@ -14,7 +18,8 @@ namespace entry { int getCost(void) const {return cost;} double getWeight(void) const {return weight;} - virtual std::string getText(const creature::Creature& c) const; + virtual std::string getText() const override; + virtual std::string getText(const creature::Creature& c) const override {return genText(*this, c);} /*virtual nlohmann::json toJson(void) const { auto data = Item::toJson(); @@ -35,6 +40,4 @@ namespace entry { const int cost; const double weight; }; - - std::string genText(const Armor& a, const creature::Creature& c); } diff --git a/src/creature.cc b/src/creature.cc index ac616ac..ee735d7 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -15,19 +15,16 @@ using namespace std; namespace creature { template<typename T> map<T, int> makeMap(map<string, int> src) { - //cout << "Got here!\n"; map<T, int> ret; for(auto& [abilityStr, val] : src) { ret.insert({T(abilityStr), val}); } - //cout << "And here!\n"; return ret; } - Creature::Creature(const json& data) - : inventory(json2ptrvec<entry::Item>(data["inventory"])), stats(makeMap<rules::Ability>(data["stats"])), skills(makeMap<rules::Skill>(data["skills"])), creatureName(data["name"]), size(data["size"]), type(data["type"]), alignment(data["alignment"]), hdCount(data["hit_die_count"]), hdSides(data["hit_die_sides"]), speed(data["speed"]), saves(json2vec<rules::Ability>(data["saves"])), langs(data["langs"]), cr(data["cr"]), proficiency(data["prof"]), natArmorName(data["natural_armor"]["name"]), natArmorBonus(data["natural_armor"]["bonus"]), dmgImmunities(json2vec<dmgType>(data["d_immunities"])), dmgResistances(json2vec<dmgType>(data["d_resistances"])), dmgVulnerabilities(json2vec<dmgType>(data["d_vulnerabilities"])), condImmunities(json2vec<dmgType>(data["c_immunities"])), features(json2ptrvec<entry::Feature>(data["features"])) + Creature::Creature(const json& data, const json& base) + : Entry(base), inventory(json2ptrvec<entry::Item>(data["inventory"])), stats(makeMap<rules::Ability>(data["stats"])), skills(makeMap<rules::Skill>(data["skills"])), size(data["size"]), alignment(data["alignment"]), hdCount(data["hit_die_count"]), hdSides(data["hit_die_sides"]), speed(data["speed"]), saves(json2vec<rules::Ability>(data["saves"])), langs(data["langs"]), cr(data["cr"]), proficiency(data["prof"]), natArmorName(data["natural_armor"]["name"]), natArmorBonus(data["natural_armor"]["bonus"]), dmgImmunities(json2vec<dmgType>(data["d_immunities"])), dmgResistances(json2vec<dmgType>(data["d_resistances"])), dmgVulnerabilities(json2vec<dmgType>(data["d_vulnerabilities"])), condImmunities(json2vec<dmgType>(data["c_immunities"])), features(json2ptrvec<entry::Feature>(data["features"])) { - //cout << "...And here!\n"; // Initialize names and hp if(((map<string, json>) data).contains("givenName")) { givenName = data["givenName"]; @@ -52,30 +49,28 @@ namespace creature { } nlohmann::json Creature::toJson() const { - return nlohmann::json({ - {"name", creatureName}, - {"size", size}, - {"type", type}, - {"alignment", alignment}, - {"hit_die_count", hdCount}, - {"hit_die_sides", hdSides}, - {"speed", speed}, - {"stats", stats}, - {"skills", skills}, - {"saves", saves}, - {"langs", langs}, - {"cr", cr}, - {"prof", proficiency}, - {"d_immunities", dmgImmunities}, - {"d_resistances", dmgResistances}, - {"d_vulnerabilities", dmgVulnerabilities}, - {"c_immunities", condImmunities}, - {"givenName", givenName}, - {"hpMax", hpMax}, - {"hp", hp}, - {"inventory", getJsonVectP(inventory)}, - {"features", getJsonVectP(features)} - }); + nlohmann::json data = Entry::toJson(); + data["size"] = size; + data["alignment"] = alignment; + data["hit_die_count"] = hdCount; + data["hit_die_sides"] = hdSides; + data["speed"] = speed; + data["stats"] = stats; + data["skills"] = skills; + data["saves"] = saves; + data["langs"] = langs; + data["cr"] = cr; + data["prof"] = proficiency; + data["d_immunities"] = dmgImmunities; + data["d_resistances"] = dmgResistances; + data["d_vulnerabilities"] = dmgVulnerabilities; + data["c_immunities"] = condImmunities; + data["givenName"] = givenName; + data["hpMax"] = hpMax; + data["hp"] = hp; + data["inventory"] = getJsonVectP(inventory); + data["features"] = getJsonVectP(features); + return data; } // True if type without matching qualifiers is in subdata @@ -194,6 +189,10 @@ namespace creature { return out; } + string formatDmgTypeVector(string title, vector<dmgType> dmg) { + return title + ": " + utils::join(dmg, "; "); + } + string genText(const Creature& c) { stringstream text; text << c.getGivenName() << " (" << c.getCreatureName() << "): " << c.getHP() << "/" << c.getHPMax() << " hp, " << getAC(c) << " ac"; @@ -205,49 +204,61 @@ namespace creature { text << " (" << armor << ")"; } } - text << ", speed " << c.getSpeed() << "\n"; - text << "A cr " << c.getCR() << " " << c.getAlignment() << " " << c.getSize() << " " << c.getType() << ".\n"; - text << "Stats:\n"; + text << ", speed " << c.getSpeed() << endl; + text << "A cr " << c.getCR() << " " << c.getAlignment() << " " << c.getSize() << " " << c.getType() << "." << endl; + text << "Stats:" << endl; using namespace rules; vector<rules::Ability> abilities {Ability::Str(), Ability::Dex(), Ability::Con(), Ability::Int(), Ability::Wis(), Ability::Cha()}; for(auto ability : abilities) { text << " " << setw(6) << std::left << ability.getAbbrev(); } - text << "\n"; + text << endl; for(auto ability : abilities) { text << setw(7) << std::left << (to_string(c.getScore(ability)) + "(" + to_string(c.getBonus(ability)) + ")"); } - text << "\n"; + text << endl; text << "Senses: "; if(! c.getSenses().empty()) { text << utils::join(c.getSenses(), ", ") << ". "; } - text << "Passive Perception " << 10 + c.getSkillBonus(rules::Skill::Perception()) << "\n"; + text << "Passive Perception " << 10 + c.getSkillBonus(rules::Skill::Perception()) << endl; if(! c.getLanguages().empty()) { - text << "Languages: " << c.getLanguages() << "\n"; + text << "Languages: " << c.getLanguages() << endl; } if(! c.getSkills().empty()) { - text << "\nSkills:\n"; + text << endl << "Skills:" << endl; for(auto skill : c.getSkills()) { - text << skill.first.getName() << " (+" << skill.second << ")\n"; + text << " * " << skill.first.getName() << " (+" << skill.second << ")" << endl; } } if(! c.getSaves().empty()) { - text << "\nSaves:\n"; + text << endl << "Saves:" << endl; for(auto save : c.getSaves()) { - text << save.first.getAbbrev() << " (+" << save.second << ")\n"; + text << " * " << save.first.getAbbrev() << " (+" << save.second << ")" << endl; } } + if(! c.getDmgImmunities().empty()) { + text << formatDmgTypeVector("\nDamage Immunities", c.getDmgImmunities()) << endl; + } + if(! c.getDmgResistances().empty()) { + text << formatDmgTypeVector("\nDamage Resistances", c.getDmgResistances()) << endl; + } + if(! c.getDmgVulnerabilities().empty()) { + text << formatDmgTypeVector("\nDamage Vulnerabilities", c.getDmgVulnerabilities()) << endl; + } + if(! c.getCondImmunities().empty()) { + text << formatDmgTypeVector("\nCondition Immunities", c.getCondImmunities()) << endl; + } if(! c.getFeatures().empty()) { - text << "\nFeatures:\n"; + text << endl << "Features:" << endl; for(auto f: c.getFeatures()) { - text << f->getText(c) << "\n"; + text << " * " << f->getText(c) << endl; } } if(! c.getInventory().empty()) { - text << "\nInventory:\n"; + text << endl << "Inventory:" << endl; for(auto i : c.getInventory()) { - text << i->getText(c) << "\n"; + text << " * " << i->getText(c) << endl; } } diff --git a/src/creature.h b/src/creature.h index 1c8db21..c3ce5ae 100644 --- a/src/creature.h +++ b/src/creature.h @@ -1,25 +1,31 @@ #pragma once #include "json.hpp" -#include "jsonable.h" #include "rules.h" +#include "utils.h" +#include "entry.h" +#include <cmath> namespace entry { class Feature; -} -namespace entry { class Item; } -class Armor; -class Weapon; typedef nlohmann::json json; namespace creature { + class Creature; + class dmgType : public Jsonable { public: dmgType(const json& data) : type(data["type"]), qualifiers(data["qualifiers"]) {} std::string type; std::vector<std::string> qualifiers; + operator string() const { + if(qualifiers.empty()) { + return type; + } + return utils::join(qualifiers, ", ") + " " + type; + } json toJson(void) const { return json({ {"type", type}, @@ -29,9 +35,15 @@ namespace creature { }; - class Creature : public Jsonable { + // Convenience function to calculate AC + const int getAC(const Creature& c); + + // Convenience function to spit out everything about it + std::string genText(const Creature& c); + + class Creature : public entry::Entry { public: - Creature(const json& data); + Creature(const json& data, const json& base); virtual ~Creature() {}; // Getters @@ -40,10 +52,13 @@ namespace creature { std::map<rules::Skill, int> getSkills(void) const; std::map<rules::Ability, int> getSaves(void) const; + //Override getText + virtual std::string getText() const override {return genText(*this);} + // Inline getters - std::string getCreatureName(void) const {return creatureName;} + std::string getCreatureName(void) const {return getName();} std::string getGivenName(void) const {return givenName;} - std::string getType(void) const {return type;} + //std::string getType(void) const {return getType();} std::string getSize(void) const {return size;} std::string getAlignment(void) const {return alignment;} double getCR(void) const {return cr;} @@ -54,10 +69,14 @@ namespace creature { std::vector<std::string> getSenses(void) const {return senses;} std::string getSpeed(void) const {return speed;} int getScore(const rules::Ability& ability) const {return stats.at(ability);} - int getBonus(const rules::Ability& ability) const {return (int) (getScore(ability) - 10) / 2;} + int getBonus(const rules::Ability& ability) const {return std::floor((getScore(ability) - 10) / 2.0);} int getProficiency(void) const {return proficiency;} std::vector<std::shared_ptr<entry::Feature>> getFeatures(void) const {return features;} std::vector<std::shared_ptr<entry::Item>> getInventory(void) const {return inventory;} + std::vector<dmgType> getDmgImmunities(void) const {return dmgImmunities;} + std::vector<dmgType> getDmgResistances(void) const {return dmgResistances;} + std::vector<dmgType> getDmgVulnerabilities(void) const {return dmgVulnerabilities;} + std::vector<dmgType> getCondImmunities(void) const {return condImmunities;} // Setters (mutators) @@ -80,9 +99,9 @@ namespace creature { std::map<rules::Skill, int> skills; //Immutable variables - const std::string creatureName; + //const std::string creatureName; const std::string size; - const std::string type; + //const std::string type; const std::string alignment; const int hdCount; const int hdSides; @@ -113,10 +132,4 @@ namespace creature { } return Ts; } - - // Convenience function to calculate AC - const int getAC(const Creature& c); - - // Convenience function to spit out everything about it - std::string genText(const Creature& c); } diff --git a/src/dmtool.cc b/src/dmtool.cc index f9d32ab..4aee1cf 100644 --- a/src/dmtool.cc +++ b/src/dmtool.cc @@ -1,78 +1,112 @@ -#include "creature.h" +#include "entry.h" #include "settings.h" #include <iostream> #include <vector> #include <string> #include <filesystem> +#include <system_error> using namespace std; namespace fs = std::filesystem; void usage(string exename) { - cout << "Usage:\n"; + cout << "Usage:" << endl; string indOpt = " " + exename + " "; string indDesc = " "; - cout << indOpt << "[ls] [subfolder]\n"; - cout << indDesc << "List creatures and objects.\n"; - cout << indOpt << "cp old-path new-path\n"; - cout << indDesc << "Copy old-path to new-path.\n"; - cout << indOpt << "mv old-path new-path\n"; - cout << indDesc << "Move old-path to new-path.\n"; - cout << indOpt << "rm path\n"; - cout << indDesc << "Remove existing creature, object, or directory.\n"; - cout << indOpt << "roll path name\n"; - cout << indDesc << "Roll a skill check, save, or attack.\n"; - cout << indOpt << "damage path amount [type]\n"; - cout << indDesc << "Damage creature by amount. Type defaults to \"force\".\n"; - cout << indOpt << "heal path amount\n"; - cout << indDesc << "Heal creature by amount.\n"; - cout << indOpt << "reset path\n"; - cout << indDesc << "Reset creature to full health (as if completing a long rest).\n"; - cout << indOpt << "set path field value\n"; - cout << indDesc << "Set a field to a new value, where field is any of:\n"; - cout << indDesc << " ability (str, dex, con, int, wis, cha); value is new ability score\n"; - cout << indDesc << " skill (athletics, \"sleight of hand\", etc.); value is (none|proficient|expert)\n"; - cout << indDesc << " name; value is new given name.\n"; - cout << indOpt << "add path entry\n"; - cout << indDesc << "Add entry to creature, where entry is an item or spell.\n"; - cout << indOpt << "help\n"; - cout << indDesc << "Show this help.\n"; + cout << indOpt << "[ls] [subfolder]" << endl; + cout << indDesc << "List creatures and objects." << endl; + cout << indOpt << "cp old-path new-path" << endl; + cout << indDesc << "Copy old-path to new-path." << endl; + cout << indOpt << "mv old-path new-path" << endl; + cout << indDesc << "Move old-path to new-path." << endl; + cout << indOpt << "rm path" << endl; + cout << indDesc << "Remove existing creature, object, or directory." << endl; + cout << indOpt << "roll path name" << endl; + cout << indDesc << "Roll a skill check, save, or attack." << endl; + cout << indOpt << "damage path amount [type]" << endl; + cout << indDesc << "Damage creature by amount. Type defaults to \"force\"." << endl; + cout << indOpt << "heal path amount" << endl; + cout << indDesc << "Heal creature by amount." << endl; + cout << indOpt << "reset path" << endl; + cout << indDesc << "Reset creature to full health (as if completing a long rest)." << endl; + cout << indOpt << "set path field value" << endl; + cout << indDesc << "Set a field to a new value, where field is any of:" << endl; + cout << indDesc << " ability (str, dex, con, int, wis, cha); value is new ability score" << endl; + cout << indDesc << " skill (athletics, \"sleight of hand\", etc.); value is (none|proficient|expert)" << endl; + cout << indDesc << " name; value is new given name." << endl; + cout << indOpt << "add path entry" << endl; + cout << indDesc << "Add entry to creature, where entry is an item or spell." << endl; + cout << indOpt << "help" << endl; + cout << indDesc << "Show this help." << endl; } -void print(string path) { - creature::Creature c(utils::loadJson(path)); - cout << genText(c); +void print(const fs::path& path) { + auto e = entry::Entry::create(utils::loadJson(path)); + cout << e->getText() << endl; +} + +fs::path getBaseDir() { + return settings::getString("savedir"); +} + +fs::path getTruePath(const fs::path& virtPath) { + fs::path p = getBaseDir() / virtPath; + if(fs::directory_entry(p.string() + ".json").is_regular_file()) return p.string() + ".json"; + return p; +} + +// Ensure the system is set up correctly +void initFS() { + fs::directory_entry de = fs::directory_entry(getBaseDir()); + if(! de.exists()) { + fs::create_directories(de); + fs::copy(settings::getString("weapon"), de.path() / "weapons"); + fs::copy(settings::getString("armor"), de.path() / "armor"); + fs::copy(settings::getString("spellcasting"), de.path() / "spells"); + fs::copy(settings::getString("monsters"), de.path() / "creatures"); + } +} + +void list(const fs::path& p) { + fs::path truePath = getTruePath(p); + if(fs::directory_entry(truePath).is_regular_file()) { + print(truePath); + } + else if(fs::directory_entry(truePath).is_directory()) { + for(fs::directory_entry de : filesystem::directory_iterator(truePath)) { + if(de.is_directory()) { + cout << de.path().filename().string() << "/"; + } else { + cout << de.path().stem().string(); + } + cout << "" << endl; + } + } + else { + cerr << "Unknown path " << p << endl; + } } void list(vector<string> args) { - string baseDir = settings::getString("savedir"); - vector<string> listPaths; if(args.empty()) { - listPaths.push_back(baseDir); + list(""); } else { - for(auto dir : args) { - listPaths.push_back(baseDir + "/" + dir); + for(string dir : args) { + list(dir); } } - for(auto listPath : listPaths) { - if(fs::is_regular_file(fs::status(listPath))) { - // Try loading and printing stuff about it - print(listPath); - } - else if(fs::is_directory(fs::status(listPath))) { - for(fs::directory_entry path : filesystem::directory_iterator(listPath)) { - cout << path.path().filename().string(); - if(path.is_directory()) { - cout << "/"; - } - cout << "\n"; - } - } +} + +void cp(vector<string> args) { + if(args.size() != 2) { + cerr << "Subcommand 'cp' expected 2 arguments but got " << args.size() << endl; } + fs::path src = getTruePath(args[0]); + fs::path dest = getTruePath(args[1]); + fs::copy(src, dest); } -void cp(vector<string> args) {} void mv(vector<string> args) {} void rm(vector<string> args) {} void roll(vector<string> args) {} @@ -85,6 +119,12 @@ void add(vector<string> args) {} int main(int argc, char *argv[]) { string exename = argv[0]; vector<string> args(&argv[1], &argv[argc]); + try { + initFS(); + } catch (fs::filesystem_error& e) { + cerr << e.what() << endl; + return 1; + } if(args.empty()) { list(args); return 0; diff --git a/src/entry.cc b/src/entry.cc index b6431f0..a3b4133 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -1,15 +1,28 @@ #include "entry.h" - -namespace creature { - class Creature; -} +#include "utils.h" +#include "feature.h" +#include "item.h" +#include "spell.h" +#include "creature.h" +#include <stdexcept> namespace entry { - std::string genText(const Entry& e, const creature::Creature& c) { - return e.getName() + " (" + e.getType() + ")"; + // Returns either a feature, an item, a creature, or a spell + std::shared_ptr<Entry> Entry::create(const nlohmann::json& data) { + if(data["entry"] == "feature") { + return Feature::create(data); + } else if(data["entry"] == "item") { + return Item::create(data); + } else if(data["entry"] == "creature") { + return utils::loadDFromJson<Entry, creature::Creature>(data); + } else if(data["entry"] == "spell") { + return utils::loadDFromJson<Entry, Spell>(data); + } + throw std::invalid_argument("Invalid entry: " + std::string(data["entry"])); } - std::string Entry::getText(const creature::Creature& c) const { - return genText(*this, c); + + std::string genText(const Entry& e, const creature::Creature& c) { + return e.getName() + " (" + e.getType() + ")"; } } diff --git a/src/entry.h b/src/entry.h index 20f38e0..1badd40 100644 --- a/src/entry.h +++ b/src/entry.h @@ -8,14 +8,23 @@ namespace creature { } namespace entry { + class Entry; + + // Set up default text generation + std::string genText(const Entry& e, const creature::Creature& c); + class Entry : public Jsonable { public: + static std::shared_ptr<Entry> create(const nlohmann::json& data); virtual ~Entry() {} std::string getName(void) const {return name;} std::string getType(void) const {return type;} - std::string getText(void) const {return text;} - virtual std::string getText(const creature::Creature& c) const; + virtual std::string getText(void) const {return text;} + virtual std::string getText(const creature::Creature& c) const { + //return genText(*this, c); + return genText(*this, c) + ": " + getText(); + } virtual nlohmann::json toJson(void) const { return nlohmann::json({ @@ -33,7 +42,4 @@ namespace entry { const std::string type; const std::string text; }; - - // Set up default text generation - std::string genText(const Entry& e, const creature::Creature& c); } diff --git a/src/jsonable.h b/src/jsonable.h index d6866b8..2c0da6b 100644 --- a/src/jsonable.h +++ b/src/jsonable.h @@ -19,7 +19,8 @@ template<typename T> std::vector<T> json2vec(const nlohmann::json& data) { template<typename T> std::vector<T> jsonList2vec(const std::string& type, const std::vector<std::string>& names) { std::vector<T> ret; for(auto name : names) { - ret.push_back(utils::loadJson(type, name)); + auto j = utils::loadJson(type, name); + ret.push_back(T(j, j)); } return ret; } diff --git a/src/rules.h b/src/rules.h index 8b54e8d..3a3ffbc 100644 --- a/src/rules.h +++ b/src/rules.h @@ -13,6 +13,7 @@ namespace rules { public: string getFull() const {return abilities.at(getAbbrev());} string getAbbrev() const {return abbrev;} + operator string() const {return getAbbrev();} virtual nlohmann::json toJson(void) const { return getAbbrev(); } diff --git a/src/settings.cc b/src/settings.cc index e9a6165..56c26b3 100644 --- a/src/settings.cc +++ b/src/settings.cc @@ -5,9 +5,10 @@ namespace settings { const std::map<std::string, std::string> dummySettings { - {"weapon", "parser/items/weapons/"}, - {"armor", "parser/items/armor/"}, - {"spellcasting", "parser/spells/"}, + {"weapon", "/usr/share/dmtool/items/weapons/"}, + {"armor", "/usr/share/dmtool/items/armor/"}, + {"spellcasting", "/usr/share/dmtool/spells/"}, + {"monsters", "/usr/share/dmtool/monsters/"}, {"savedir", "~/.dmtool/"} }; diff --git a/src/spell.cc b/src/spell.cc new file mode 100644 index 0000000..0294956 --- /dev/null +++ b/src/spell.cc @@ -0,0 +1,17 @@ +#include "spell.h" +#include "utils.h" +#include <string> +#include <sstream> + +using namespace std; + +namespace entry { + string Spell::getText() const { + stringstream text; + text << utils::toOrdinal(getLevel()) << " level " << getSchool() << " spell." << endl; + text << "Casting time: " << getCastingTime() << ", Duration: " << getDuration() << ", Range: " << getRange() << ", Components: " << getComponents() << "." << endl; + text << Entry::getText() << endl; + text << "Available for: " << utils::join(getClasses(), ", ") << "." << endl; + return text.str(); + } +} diff --git a/src/spell.h b/src/spell.h index d493d5d..31b7d72 100644 --- a/src/spell.h +++ b/src/spell.h @@ -1,47 +1,44 @@ #pragma once #include "json.hpp" -#include "jsonable.h" +#include "entry.h" -namespace spell { - class Spell : public Jsonable { +namespace entry { + class Spell : public Entry { public: - Spell(const nlohmann::json& data) : name(data["name"]), level(data["level"]), school(data["school"]), classes(data["classes"]), castingTime(data["casting_time"]), range(data["range"]), components(data["components"]), duration(data["duration"]), text(data["text"]) {}; + Spell(const nlohmann::json& data, const nlohmann::json& base) : Entry(base), level(data["level"]), classes(data["classes"]), castingTime(data["casting_time"]), range(data["range"]), components(data["components"]), duration(data["duration"]) {}; virtual ~Spell() {}; - std::string getName(void) const {return name;} + //std::string getName(void) const {return name;} int getLevel(void) const {return level;} - std::string getSchool(void) const {return school;} + std::string getSchool(void) const {return getType();} std::vector<std::string> getClasses(void) const {return classes;} std::string getCastingTime(void) const {return castingTime;} std::string getRange(void) const {return range;} std::string getComponents(void) const {return components;} std::string getDuration(void) const {return duration;} - std::string getText(void) const {return text;} + + std::string getText(void) const override; virtual nlohmann::json toJson(void) const { - /*return nlohmann::json({ - {"name", name}, - {"level", level}, - {"school", school}, - {"classes", classes}, - {"casting_time", castingTime}, - {"range", range}, - {"components", components}, - {"duration", duration}, - {"text", text} - });*/ - return nlohmann::json(name); + auto data = Entry::toJson(); + data["level"] = level; + data["classes"] = classes; + data["casting_time"] = castingTime; + data["range"] = range; + data["components"] = components; + data["duration"] = duration; + return data; } private: - const std::string name; + //const std::string name; const int level; - const std::string school; + //const std::string school; const std::vector<std::string> classes; const std::string castingTime; const std::string range; const std::string components; const std::string duration; - const std::string text; + //const std::string text; }; } diff --git a/src/spellcasting.cc b/src/spellcasting.cc index e36fd5b..9b39741 100644 --- a/src/spellcasting.cc +++ b/src/spellcasting.cc @@ -12,47 +12,29 @@ namespace entry { return genText(*this, c); } - string toOrdinal(size_t number) { - string suffix = "th"; - if (number % 100 < 11 || number % 100 > 13) { - switch (number % 10) { - case 1: - suffix = "st"; - break; - case 2: - suffix = "nd"; - break; - case 3: - suffix = "rd"; - break; - } - } - return to_string(number) + suffix; - } - string genText(const Spellcasting& s, const creature::Creature& c) { stringstream text; text << genText(static_cast<const Feature&>(s), c); - text << ": Spellcasting ability: " << s.getAbility().getFull(); + text << ": The " << c.getCreatureName() << "'s spellcasting ability is " << s.getAbility().getFull(); text << " (spell save DC " << 8 + c.getBonus(s.getAbility()) + c.getProficiency(); text << ", +" << c.getBonus(s.getAbility()) + c.getProficiency() << " to hit with spell attacks)."; if(s.isInnate()) { - text << " (spellcasting is innate)."; + text << " Spellcasting is innate."; } int levelNumber = 0; for(auto level : s.getSpellsBySlot()) { - text << "\n"; + text << endl; if(level.numSlots == 0) { if(s.isInnate()) { - text << "At will: "; + text << " At will: "; } else { - text << "Cantrips: "; + text << " Cantrips: "; } } else { if(s.isInnate()) { - text << level.numSlots << "/day each: "; + text << " " << level.numSlots << "/day each: "; } else { - text << toOrdinal(levelNumber) << " level (" << level.numSlots << " slots): "; + text << " " << utils::toOrdinal(levelNumber) << " level (" << level.numSlots << " slots): "; } } vector<string> names; diff --git a/src/spellcasting.h b/src/spellcasting.h index 654a386..3a55277 100644 --- a/src/spellcasting.h +++ b/src/spellcasting.h @@ -9,15 +9,19 @@ typedef nlohmann::json json; namespace entry { struct SlotLevel : public Jsonable { - SlotLevel(const json& data) : numSlots(data["slots"]), spells(jsonList2vec<spell::Spell>("spellcasting", data["spells"])) {} + SlotLevel(const json& data) : numSlots(data["slots"]), spells(jsonList2vec<Spell>("spellcasting", data["spells"])) {} virtual ~SlotLevel() {} const int numSlots; - const std::vector<spell::Spell> spells; + const std::vector<Spell> spells; json toJson(void) const { + std::vector<string> s; + for(auto spell : spells) { + s.push_back(spell.getName()); + } return json({ {"slots", numSlots}, - {"spells", spells} + {"spells", s} }); } }; diff --git a/src/utils.cc b/src/utils.cc index d071fe6..a44eced 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -82,3 +82,21 @@ std::string utils::getCostString(int coppers) { } return utils::join(parts, ", "); } + +std::string utils::toOrdinal(std::size_t number) { + std::string suffix = "th"; + if (number % 100 < 11 || number % 100 > 13) { + switch (number % 10) { + case 1: + suffix = "st"; + break; + case 2: + suffix = "nd"; + break; + case 3: + suffix = "rd"; + break; + } + } + return std::to_string(number) + suffix; +} diff --git a/src/utils.h b/src/utils.h index 156c88b..bdf7ddc 100644 --- a/src/utils.h +++ b/src/utils.h @@ -22,8 +22,9 @@ namespace utils { template<typename S, typename D> std::shared_ptr<S> loadDFromJson(const nlohmann::json& data) { try { return std::shared_ptr<S>(new D(loadJson(data["type"], data["name"]), data)); - } catch(std::invalid_argument& e) { - // Fall back on the data passed in + } catch(std::exception& e) { + // Covers errors in building the creature or fs traversal. + // Fall back on the data passed in. return std::shared_ptr<S>(new D(data, data)); } } @@ -57,4 +58,6 @@ namespace utils { std::vector<std::pair<std::string, int>> copper2coins(int coppers); std::string getCostString(int coppers); + + std::string toOrdinal(std::size_t number); } diff --git a/src/weapon.cc b/src/weapon.cc index af75736..b3dad1d 100644 --- a/src/weapon.cc +++ b/src/weapon.cc @@ -15,19 +15,9 @@ namespace entry { return damageDieSides; } - string Weapon::getText(const creature::Creature& c) const { - return genText(*this, c); - } - - string genText(const Weapon& w, const creature::Creature& c) { + string getTextHelper(const Weapon& w, string toHitBonus, string damageBonus) { stringstream text; - text << genText(static_cast<const Item&>(w), c); - // Determine best ability bonus - int abilityBonus = c.getBonus(rules::Ability::Str()); - if(w.getProperties().count("finesse")) { - abilityBonus = max(abilityBonus, c.getBonus(rules::Ability::Dex())); - } - text << ": +" << abilityBonus + c.getProficiency() << " to hit, "; + text << "+" << toHitBonus << " to hit, "; if(w.getReach() > 0) { text << "reach " << w.getReach() << " ft."; if(w.getRange().second > 0) { @@ -37,9 +27,9 @@ namespace entry { if(w.getRange().second > 0) { text << "range " << w.getRange().first << "/" << w.getRange().second << " ft."; } - text << " Hit: " << w.getDamageDieCount() << "d" << w.getDamageDieSides() << " + " << abilityBonus << " " << w.getDamageType() << " damage"; + text << " Hit: " << w.getDamageDieCount() << "d" << w.getDamageDieSides() << " + " << damageBonus << " " << w.getDamageType() << " damage"; if(w.getProperties().count("versatile")) { - text << " (or " << w.getDamageDieCount() << "d" << w.getDamageDieSides(true) << " + " << abilityBonus << " " << w.getDamageType() << " damage if two-handed)"; + text << " (or " << w.getDamageDieCount() << "d" << w.getDamageDieSides(true) << " + " << damageBonus << " " << w.getDamageType() << " damage if two-handed)"; } text << "."; auto props = w.getProperties(); @@ -52,4 +42,46 @@ namespace entry { text << " " << genText(static_cast<const Substantial&>(w)); return text.str(); } + + vector<rules::Ability> getAbilityOptions(const Weapon& w) { + // Do finesse + if(w.getProperties().count("finesse")) { + return {rules::Ability::Str(), rules::Ability::Dex()}; + } + // Do melee weapons + if(w.getReach() > 0) { + return {rules::Ability::Str()}; + } + // Do range weapons (thrown melee were done above) + if(w.getRange().second > 0) { + return {rules::Ability::Dex()}; + } + cerr << "Error processing weapon!" << endl; + return {rules::Ability::Str()}; + } + + string Weapon::getText() const { + auto abilities = getAbilityOptions(*this); + string abilityString; + if(abilities.size() == 1) { + abilityString = abilities[0]; + } else { + abilityString = "max(" + utils::join(abilities, ", ") + ")"; + } + return getTextHelper(*this, "(" + abilityString + " + prof)", abilityString); + + } + + string genText(const Weapon& w, const creature::Creature& c) { + stringstream text; + text << genText(static_cast<const Item&>(w), c); + // Determine best ability bonus + auto abilities = getAbilityOptions(w); + int abilityBonus = c.getBonus(abilities[0]); + if(abilities.size() == 2) { + abilityBonus = max(abilityBonus, c.getBonus(abilities[1])); + } + text << ": " << getTextHelper(w, to_string(abilityBonus + c.getProficiency()), to_string(abilityBonus)); + return text.str(); + } } diff --git a/src/weapon.h b/src/weapon.h index 9ee2e8a..7a8c0ba 100644 --- a/src/weapon.h +++ b/src/weapon.h @@ -8,9 +8,13 @@ namespace creature { } namespace entry { + class Weapon; + + std::string genText(const Weapon& w, const creature::Creature& c); + class Weapon : public Item, public Substantial { public: - Weapon(const nlohmann::json& data, const nlohmann::json& base) : Item(base), damageType(data["damage"]["dmg_type"]), damageDieCount(data["damage"]["dmg_die_count"]), damageDieSides(data["damage"]["dmg_die_sides"]), properties(data["properties"]), weaponType(data["type"]), range(data["range"][0], data["range"][1]), reach(data["reach"]), cost(data["cost"]), weight(data["weight"]) {} + Weapon(const nlohmann::json& data, const nlohmann::json& base) : Item(base), damageType(data["damage"]["dmg_type"]), damageDieCount(data["damage"]["dmg_die_count"]), damageDieSides(data["damage"]["dmg_die_sides"]), properties(data["properties"]), weaponType(data["weapon_type"]), range(data["range"][0], data["range"][1]), reach(data["reach"]), cost(data["cost"]), weight(data["weight"]) {} std::string getDamageType(void) const {return damageType;} int getDamageDieCount(void) const {return damageDieCount;} @@ -21,8 +25,9 @@ namespace entry { int getReach(void) const {return reach;} int getCost(void) const {return cost;} double getWeight(void) const {return weight;} - - virtual std::string getText(const creature::Creature& c) const; + + virtual std::string getText() const override; + virtual std::string getText(const creature::Creature& c) const override {return genText(*this, c);} /*virtual nlohmann::json toJson(void) const { auto data = Item::toJson(); @@ -49,6 +54,4 @@ namespace entry { const int cost; const double weight; }; - - std::string genText(const Weapon& w, const creature::Creature& c); } |