aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYour Name <you@example.com>2021-04-29 14:17:08 -0400
committerYour Name <you@example.com>2021-04-29 14:17:08 -0400
commit5a813a75412ac9b8fadb90c9abd46dd95aee8e9b (patch)
tree75c5466d459c793430a6481cd276a624cd843794 /src
parentcd57ad6e208728bafcbc8c7d7b85d88603706978 (diff)
downloaddmtool-5a813a75412ac9b8fadb90c9abd46dd95aee8e9b.tar.gz
dmtool-5a813a75412ac9b8fadb90c9abd46dd95aee8e9b.tar.bz2
dmtool-5a813a75412ac9b8fadb90c9abd46dd95aee8e9b.zip
Removed data files from repo
Diffstat (limited to 'src')
-rw-r--r--src/armor.cc31
-rw-r--r--src/armor.h11
-rw-r--r--src/creature.cc99
-rw-r--r--src/creature.h49
-rw-r--r--src/dmtool.cc140
-rw-r--r--src/entry.cc29
-rw-r--r--src/entry.h16
-rw-r--r--src/jsonable.h3
-rw-r--r--src/rules.h1
-rw-r--r--src/settings.cc7
-rw-r--r--src/spell.cc17
-rw-r--r--src/spell.h41
-rw-r--r--src/spellcasting.cc32
-rw-r--r--src/spellcasting.h10
-rw-r--r--src/utils.cc18
-rw-r--r--src/utils.h7
-rw-r--r--src/weapon.cc60
-rw-r--r--src/weapon.h13
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);
}