aboutsummaryrefslogtreecommitdiff
path: root/src/dmtool.cc
diff options
context:
space:
mode:
authorYour Name <you@example.com>2021-05-01 15:10:54 -0400
committerYour Name <you@example.com>2021-05-01 15:10:54 -0400
commit7b5d1e3d46e94262a9c0fd3a01ab4685aea9d12d (patch)
treed9b808542216f71dbab053ad23145903e96c6401 /src/dmtool.cc
parent5a813a75412ac9b8fadb90c9abd46dd95aee8e9b (diff)
downloaddmtool-7b5d1e3d46e94262a9c0fd3a01ab4685aea9d12d.tar.gz
dmtool-7b5d1e3d46e94262a9c0fd3a01ab4685aea9d12d.tar.bz2
dmtool-7b5d1e3d46e94262a9c0fd3a01ab4685aea9d12d.zip
Added bash completion, amongst others
Diffstat (limited to 'src/dmtool.cc')
-rw-r--r--src/dmtool.cc266
1 files changed, 189 insertions, 77 deletions
diff --git a/src/dmtool.cc b/src/dmtool.cc
index 4aee1cf..cf05938 100644
--- a/src/dmtool.cc
+++ b/src/dmtool.cc
@@ -1,57 +1,98 @@
#include "entry.h"
#include "settings.h"
+#include "creature.h"
+#include "dice.h"
+#include "weapon.h"
#include <iostream>
#include <vector>
#include <string>
#include <filesystem>
#include <system_error>
-
-using namespace std;
+#include <exception>
+#include <memory>
+#include <algorithm>
namespace fs = std::filesystem;
-void usage(string exename) {
- cout << "Usage:" << endl;
- string indOpt = " " + exename + " ";
- string indDesc = " ";
- 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 usage(const std::string& exename) {
+ std::cout << "Usage:" << std::endl;
+ std::string indOpt = " " + exename + " ";
+ std::string indDesc = " ";
+ std::cout << indOpt << "[ls] [subfolder]" << std::endl;
+ std::cout << indDesc << "List creatures and objects." << std::endl;
+ std::cout << indOpt << "cp old-path new-path" << std::endl;
+ std::cout << indDesc << "Copy old-path to new-path, instantiating it." << std::endl;
+ std::cout << indOpt << "mkdir path" << std::endl;
+ std::cout << indDesc << "Make a new directory for holding creatures and objects." << std::endl;
+ std::cout << indOpt << "mv old-path new-path" << std::endl;
+ std::cout << indDesc << "Move old-path to new-path." << std::endl;
+ std::cout << indOpt << "rm path" << std::endl;
+ std::cout << indDesc << "Remove existing creature, object, or directory." << std::endl;
+ std::cout << indOpt << "roll path name" << std::endl;
+ std::cout << indDesc << "Roll a skill check, save, or attack." << std::endl;
+ std::cout << indOpt << "damage path amount [type]" << std::endl;
+ std::cout << indDesc << "Damage creature by amount. Type defaults to \"force\"." << std::endl;
+ std::cout << indOpt << "heal path amount" << std::endl;
+ std::cout << indDesc << "Heal creature by amount." << std::endl;
+ std::cout << indOpt << "reset path" << std::endl;
+ std::cout << indDesc << "Reset creature to full health (as if completing a long rest)." << std::endl;
+ std::cout << indOpt << "set path field value" << std::endl;
+ std::cout << indDesc << "Set a field to a new value, where field is any of:" << std::endl;
+ std::cout << indDesc << " ability (str, dex, con, int, wis, cha); value is new ability score" << std::endl;
+ std::cout << indDesc << " skill (athletics, \"sleight of hand\", etc.); value is (none|proficient|expert)" << std::endl;
+ std::cout << indDesc << " name; value is new given name." << std::endl;
+ std::cout << indOpt << "add path entry" << std::endl;
+ std::cout << indDesc << "Add entry to creature, where entry is an item or spell." << std::endl;
+ std::cout << indOpt << "help" << std::endl;
+ std::cout << indDesc << "Show this help." << std::endl;
+}
+
+std::shared_ptr<entry::Entry> instantiate(const fs::path& path) {
+ try {
+ return entry::Entry::create(utils::loadJson(path));
+ } catch(std::exception& e) {
+ if(fs::directory_entry(path).exists()) {
+ throw std::runtime_error("Invalid json: " + path.string());
+ } else {
+ throw std::runtime_error("No such file nor directory: " + path.string());
+ }
+ }
+}
+
+void save(const std::shared_ptr<entry::Entry>& e, const fs::path& path) {
+ utils::saveJson(*e, path);
}
void print(const fs::path& path) {
- auto e = entry::Entry::create(utils::loadJson(path));
- cout << e->getText() << endl;
+ std::cout << instantiate(path)->getText() << std::endl;
}
fs::path getBaseDir() {
return settings::getString("savedir");
}
-fs::path getTruePath(const fs::path& virtPath) {
- fs::path p = getBaseDir() / virtPath;
+fs::path eraseRoot(fs::path& src) {
+ if(src.empty()) return src;
+ fs::path tmp;
+ auto it = src.begin();
+ while(++it != src.end()) {
+ tmp /= *it;
+ }
+ src = tmp;
+ return src;
+}
+
+const std::vector<std::string> virtPaths({"weapons", "armor", "spells", "creatures"});
+
+fs::path getTruePath(fs::path virtPath) {
+ fs::path p;
+ if(std::find(virtPaths.begin(), virtPaths.end(), *virtPath.begin()) != virtPaths.end()) {
+ p = settings::getString(*virtPath.begin());
+ eraseRoot(virtPath);
+ } else {
+ p = getBaseDir();
+ }
+ p /= virtPath;
if(fs::directory_entry(p.string() + ".json").is_regular_file()) return p.string() + ".json";
return p;
}
@@ -61,88 +102,159 @@ 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) {
+ if(p.empty()) {
+ // Print read-only dirs
+ for(std::string name : virtPaths) {
+ std::cout << name << "/ (read only)" << std::endl;
+ }
+ }
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)) {
+ for(fs::directory_entry de : fs::directory_iterator(truePath)) {
if(de.is_directory()) {
- cout << de.path().filename().string() << "/";
+ std::cout << de.path().filename().string() << "/" << std::endl;
} else {
- cout << de.path().stem().string();
+ std::cout << de.path().stem().string() << std::endl;
}
- cout << "" << endl;
}
}
else {
- cerr << "Unknown path " << p << endl;
+ std::cerr << "Unknown path " << p << std::endl;
}
}
-void list(vector<string> args) {
+void list(std::vector<std::string> args) {
if(args.empty()) {
list("");
} else {
- for(string dir : args) {
+ for(std::string dir : args) {
list(dir);
}
}
}
-void cp(vector<string> args) {
+void mkdir(std::vector<std::string> args) {
+ for(std::string s : args) {
+ fs::create_directories(getTruePath(s));
+ }
+}
+
+void cp(fs::path src, fs::path dest) {
+ if(fs::directory_entry(src).is_regular_file()) {
+ save(instantiate(src), dest);
+ } else {
+ mkdir({dest});
+ for(fs::directory_entry de : fs::directory_iterator(src)) {
+ cp(de.path(), dest / de.path().filename());
+ }
+ }
+
+}
+
+void cp(std::vector<std::string> args) {
if(args.size() != 2) {
- cerr << "Subcommand 'cp' expected 2 arguments but got " << args.size() << endl;
+ throw std::runtime_error("Subcommand 'cp' expected 2 arguments but got " + std::to_string(args.size()));
}
- fs::path src = getTruePath(args[0]);
- fs::path dest = getTruePath(args[1]);
- fs::copy(src, dest);
+ // Operate by intantiating and saving
+ // We do recursive!
+ cp(getTruePath(args[0]), getTruePath(args[1]));
}
-void mv(vector<string> args) {}
-void rm(vector<string> args) {}
-void roll(vector<string> args) {}
-void damage(vector<string> args) {}
-void heal(vector<string> args) {}
-void reset(vector<string> args) {}
-void set(vector<string> args) {}
-void add(vector<string> args) {}
+void mv(std::vector<std::string> args) {
+ if(args.size() != 2) {
+ throw std::runtime_error("Subcommand 'mv' expected 2 arguments but got " + std::to_string(args.size()));
+ }
+ fs::rename(getTruePath(args[0]), getTruePath(args[1]));
+}
+
+void rm(std::vector<std::string> args) {
+ for(std::string s : args) {
+ fs::remove_all(getTruePath(s));
+ }
+}
+
+void roll(std::vector<std::string> args) {
+ std::shared_ptr<entry::Entry> e = instantiate(getTruePath(args[0]));
+ std::shared_ptr<creature::Creature> c = std::dynamic_pointer_cast<creature::Creature>(e);
+ if(! c) {
+ throw std::runtime_error("Subcommand 'roll' expected a creature but was given an instance of " + e->getType());
+ }
+ args.erase(args.begin());
+ std::string rollName = utils::join(args, " ");
+ std::transform(rollName.begin(), rollName.end(), rollName.begin(), ::tolower);
+ int rolled = dice::roll(20);
+ auto printResults = [](std::string name, std::string type, int rolled, int bonus) {
+ std::cout << name << " " << type << ": " << rolled << " (d20) + " << bonus << " (" << name << " " << type << " bonus) = " << rolled + bonus << std::endl;
+ };
+ // Search through skills, saves, and attacks to roll
+ try {
+ rules::Skill skill = rules::Skill::string2skill(rollName);
+ printResults(skill.getName(), "check", rolled, c->getSkillBonus(skill));
+ return;
+ } catch(std::exception& e) {} // eat.
+ try {
+ rules::Ability ability = rules::Ability::string2ability(rollName);
+ printResults(ability.getFull(), "save", rolled, c->getAbilitySaveBonus(ability));
+ return;
+ } catch(std::exception& e) {} // eat.
+ for(auto w : creature::getAttacks(*c)) {
+ if(w->getName() == rollName) {
+ std::cout << w->getText(*c) << std::endl;
+ int abilityBonus = c->getBonus(creature::getBestAbility(getAbilityOptions(*w), *c));
+ int bonus = abilityBonus + c->getProficiency();
+ printResults(w->getName(), "attack", rolled, bonus);
+ std::cout << " on hit: " << entry::formatDmg(*w, *c) << std::endl;
+ return;
+ }
+ }
+}
+
+void damage(std::vector<std::string> args) {}
+void heal(std::vector<std::string> args) {}
+void reset(std::vector<std::string> args) {}
+void set(std::vector<std::string> args) {}
+void add(std::vector<std::string> args) {}
int main(int argc, char *argv[]) {
- string exename = argv[0];
- vector<string> args(&argv[1], &argv[argc]);
+ std::string exename = argv[0];
+ std::vector<std::string> args(&argv[1], &argv[argc]);
try {
initFS();
- } catch (fs::filesystem_error& e) {
- cerr << e.what() << endl;
+ } catch(fs::filesystem_error& e) {
+ std::cerr << e.what() << std::endl;
return 1;
}
if(args.empty()) {
list(args);
return 0;
}
- string cmd = args[0];
- vector<string> argsOrig(args);
+ std::string cmd = args[0];
+ std::vector<std::string> argsOrig(args);
args.erase(args.begin());
- if(cmd == "ls") list(args);
- else if(cmd == "cp") cp(args);
- else if(cmd == "mv") mv(args);
- else if(cmd == "rm") rm(args);
- else if(cmd == "roll") roll(args);
- else if(cmd == "damage") damage(args);
- else if(cmd == "heal") heal(args);
- else if(cmd == "reset") reset(args);
- else if(cmd == "set") set(args);
- else if(cmd == "add") add(args);
- else if(cmd == "help") usage(exename);
- else list(argsOrig);
+ try {
+ if(cmd == "ls") list(args);
+ else if(cmd == "cp") cp(args);
+ else if(cmd == "mkdir") mkdir(args);
+ else if(cmd == "mv") mv(args);
+ else if(cmd == "rm") rm(args);
+ else if(cmd == "roll") roll(args);
+ else if(cmd == "damage") damage(args);
+ else if(cmd == "heal") heal(args);
+ else if(cmd == "reset") reset(args);
+ else if(cmd == "set") set(args);
+ else if(cmd == "add") add(args);
+ else if(cmd == "help") usage(exename);
+ else list(argsOrig);
+ } catch(std::exception& e) {
+ std::cerr << e.what() << std::endl;
+ return 1;
+ }
return 0;
}