From 7b5d1e3d46e94262a9c0fd3a01ab4685aea9d12d Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 1 May 2021 15:10:54 -0400 Subject: Added bash completion, amongst others --- src/dmtool.cc | 266 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 189 insertions(+), 77 deletions(-) (limited to 'src/dmtool.cc') 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 #include #include #include #include - -using namespace std; +#include +#include +#include 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 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& 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 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 args) { +void list(std::vector args) { if(args.empty()) { list(""); } else { - for(string dir : args) { + for(std::string dir : args) { list(dir); } } } -void cp(vector args) { +void mkdir(std::vector 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 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 args) {} -void rm(vector args) {} -void roll(vector args) {} -void damage(vector args) {} -void heal(vector args) {} -void reset(vector args) {} -void set(vector args) {} -void add(vector args) {} +void mv(std::vector 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 args) { + for(std::string s : args) { + fs::remove_all(getTruePath(s)); + } +} + +void roll(std::vector args) { + std::shared_ptr e = instantiate(getTruePath(args[0])); + std::shared_ptr c = std::dynamic_pointer_cast(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 args) {} +void heal(std::vector args) {} +void reset(std::vector args) {} +void set(std::vector args) {} +void add(std::vector args) {} int main(int argc, char *argv[]) { - string exename = argv[0]; - vector args(&argv[1], &argv[argc]); + std::string exename = argv[0]; + std::vector 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 argsOrig(args); + std::string cmd = args[0]; + std::vector 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; } -- cgit v1.2.3