From 2a9f262e6db5906db445d465e500d7ba8c90fab3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 5 May 2021 09:44:50 -0400 Subject: Implemented additional commands --- src/dmtool.cc | 249 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 192 insertions(+), 57 deletions(-) (limited to 'src/dmtool.cc') diff --git a/src/dmtool.cc b/src/dmtool.cc index 8ade820..ba47616 100644 --- a/src/dmtool.cc +++ b/src/dmtool.cc @@ -3,6 +3,8 @@ #include "creature.h" #include "dice.h" #include "weapon.h" +#include "spell.h" +#include "spellcasting.h" #include #include #include @@ -30,7 +32,7 @@ void usage(const std::string& exename) { 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 << indOpt << "damage path amount [type] [--magical,-m] [--silvered,-s] [--adamantine,-a]" << 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; @@ -40,18 +42,22 @@ void usage(const std::string& exename) { 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 << " proficiency; value is new proficency bonus." << std::endl; std::cout << indDesc << " name; value is new given name." << std::endl; std::cout << indOpt << "edit path" << std::endl; - std::cout << indDesc << "Edit notes associated to creature." << std::endl; + std::cout << indDesc << "Edit notes associated with creature." << 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 << "del path entry" << std::endl; + std::cout << indDesc << "Delete entry from 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) { +template std::shared_ptr instantiate(const fs::path& path) { + std::shared_ptr ent; try { - return entry::Entry::create(utils::loadJson(path)); + ent = entry::Entry::create(utils::loadJson(path)); } catch(std::exception& e) { if(fs::directory_entry(path).exists()) { throw std::runtime_error("Invalid json: " + path.string()); @@ -59,6 +65,19 @@ std::shared_ptr instantiate(const fs::path& path) { throw std::runtime_error("No such file nor directory: " + path.string()); } } + std::shared_ptr t = std::dynamic_pointer_cast(ent); + if(! t) { + throw std::runtime_error("Wrong instance type: " + ent->getType()); + } + return t; +} + +int parseInt(const std::string& s) { + try { + return std::stoi(s); + } catch(std::exception& e) { + throw std::runtime_error("An integer was expected but " + s + " was given"); + } } void save(const std::shared_ptr& e, const fs::path& path) { @@ -66,7 +85,7 @@ void save(const std::shared_ptr& e, const fs::path& path) { } void print(const fs::path& path) { - std::cout << instantiate(path)->getText() << std::endl; + std::cout << instantiate(path)->getText() << std::endl; } fs::path getBaseDir() { @@ -150,7 +169,7 @@ void mkdir(std::vector args) { void cp(fs::path src, fs::path dest) { if(fs::directory_entry(src).is_regular_file()) { - save(instantiate(src), dest); + save(instantiate(src), dest); } else { mkdir({dest}); for(fs::directory_entry de : fs::directory_iterator(src)) { @@ -176,88 +195,203 @@ void rm(std::vector args) { } } +template T tryGetAbilityOrSkill(std::string src) { + try { + return T::fromString(src); + } catch(std::exception& e) {} // eat. + return T(); +} + 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()); - } + auto c = instantiate(getTruePath(args[0])); args.erase(args.begin()); std::string rollName = utils::join(args, " "); - std::transform(rollName.begin(), rollName.end(), rollName.begin(), ::tolower); + utils::lower(rollName); 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); + rules::Skill skill = tryGetAbilityOrSkill(rollName); + rules::Ability ability = tryGetAbilityOrSkill(rollName); + if(skill) { printResults(skill.getName(), "check", rolled, c->getSkillBonus(skill)); - return; - } catch(std::exception& e) {} // eat. - try { - rules::Ability ability = rules::Ability::string2ability(rollName); + } else if(ability) { 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; + } else { + 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; + } } } } -std::tuple, int> getDmgHealAmnt(const std::string& subcommand, std::vector args) { - fs::path p = getTruePath(args[0]); - std::shared_ptr e = instantiate(p); - std::shared_ptr c = std::dynamic_pointer_cast(e); - if(! c) { - throw std::runtime_error("Subcommand '" + subcommand + "' expected a creature but was given an instance of " + e->getType()); - } - int amnt = 0; - try { - amnt = std::stoi(args[1]); - } catch(std::exception& e) { - throw std::runtime_error("Subcommand '" + subcommand + "' expected an integer but was given " + args[1]); - } - return {p, c, amnt}; -} - void healOrDamage(bool heal, std::vector args) { - fs::path p; - std::shared_ptr c; - int amnt; - std::string healOrDamage = heal? "heal" : "damage"; - std::tie(p, c, amnt) = getDmgHealAmnt(healOrDamage, args); + std::vector qualifiers; + if(! heal) { + auto it = args.begin(); + while(it != args.end()) { + if(*it == "-m" || *it == "--magical") { + qualifiers.push_back(rules::Qualifier::Magical()); + args.erase(it); + } else if(*it == "-s" || *it == "--silvered") { + qualifiers.push_back(rules::Qualifier::Silvered()); + args.erase(it); + } else if(*it == "-a" || *it == "--adamantine") { + qualifiers.push_back(rules::Qualifier::Adamantine()); + args.erase(it); + } else { + it++; + } + } + } + fs::path p = getTruePath(args[0]); + auto c = instantiate(p); + int amnt = parseInt(args[1]); int initHP = c->getHP(); + std::string dmgType = "force"; if(heal) { c->applyHealing(amnt); } else { - std::string dmgType = "force"; if(args.size() == 3) { dmgType = args[2]; } - std::vector qualifiers; // TODO c->applyDamage(amnt, dmgType, qualifiers); } - std::cout << (heal? "Healing " : "Damaging ") << c->getGivenName() << " the " << c->getCreatureName() << " by " << amnt << ". HP: " << initHP << " -> " << c->getHP() << "." << std::endl; + std::cout << (heal? "Healing " : "Damaging ") << c->getGivenName() << " the " << c->getCreatureName() << " by " << amnt; + if(! heal) { + std::vector positiveQuals; + for(auto qual : qualifiers) { + positiveQuals.push_back(qual.getPositive()); + } + std::cout << " " << utils::join(positiveQuals, ", ") << " " << dmgType << " damage"; + } + std::cout << ". HP: " << initHP << " -> " << c->getHP() << "." << std::endl; save(c, p); } -void reset(std::vector args) {} -void set(std::vector args) {} -void add(std::vector args) {} +void reset(std::vector args) { + for(std::string s : args) { + fs::path p = getTruePath(s); + auto c = instantiate(p); + c->longRest(); + save(c, p); + } + +} + +void set(std::vector args) { + if(args.size() < 3) { + throw std::runtime_error("Subcommand 'set' requires at least 3 arguments"); + } + fs::path p = getTruePath(args[0]); + args.erase(args.begin()); // remove path from args + auto c = instantiate(p); + if(args[0] == "name") { + args.erase(args.begin()); // remove "name" from args + c->setGivenName(utils::join(args, " ")); + } else if(args[0] == "proficiency") { + c->setProficiency(parseInt(args[1])); + } else { + // Either an ability or a skill. If skill, then it could be multiple words long. + std::string toSet = args.back(); + args.erase(--args.end()); + std::string abilityOrSkill = utils::join(args, " "); + rules::Skill skill = tryGetAbilityOrSkill(abilityOrSkill); + rules::Ability ability = tryGetAbilityOrSkill(abilityOrSkill); + if(skill) { + // ensure lower case + utils::lower(toSet); + int level = -1; + if(toSet == "none") level = 0; + else if(toSet == "proficient") level = 1; + else if(toSet == "expert") level = 2; + if(level == -1) { + throw std::runtime_error("Skill levels can be set to none, proficient, or expert, but " + toSet + " was given."); + } + c->setProfLevel(skill, level); + } else if(ability) { + c->setScore(ability, parseInt(toSet)); + } else { + throw std::runtime_error("Subcommand 'set' expected an ability, skill, proficiency, or name field to set, but was given " + abilityOrSkill); + } + } + save(c, p); +} + +void add(std::vector args) { + fs::path p = getTruePath(args[0]); + args.erase(args.begin()); // remove path from args + auto c = instantiate(p); + std::string addName = utils::join(args, " "); + std::shared_ptr ent; + fs::path path = getTruePath(addName); + if(fs::directory_entry(path).exists()) { + ent = instantiate(path); + } else { + ent = entry::Entry::create(utils::findByName(addName)); + } + // Determine if it is an item or a spell + auto i = std::dynamic_pointer_cast(ent); + if(i) { + c->addInventoryItem(i); + } else { + auto s = std::dynamic_pointer_cast(ent); + if(s) { + c->addSpell(s); + } else { + throw std::runtime_error("Could not add the " + ent->getType() + " " + ent->getName() + " to " + c->getGivenName() + " the " + c->getName() + ": Requires a weapon, armor, or spell, but received object of type " + ent->getType()); + } + } + save(c, p); + std::cout << "Added the " << ent->getType() << " " << ent->getName() << " to " << c->getGivenName() << " the " << c->getName() << std::endl; +} + +void del(std::vector args) { + fs::path p = getTruePath(args[0]); + args.erase(args.begin()); // remove path from args + auto c = instantiate(p); + std::string itemName = utils::join(args, " "); + utils::lower(itemName); + // Loop through all of c's stuff, searching for itemName + std::shared_ptr removed; + for(auto item : c->getInventory()) { + std::string name = item->getName(); + if(utils::lower(name) == itemName) { + c->removeInventoryItem(item); + removed = item; + break; + } + } + if(! removed) { + for(auto spell : c->getSpellcasting()->getSpells()) { + std::string name = spell->getName(); + if(utils::lower(name) == itemName) { + c->removeSpell(spell); + removed = spell; + break; + } + } + } + save(c, p); + if(removed) { + std::cout << "Successfully removed the " << removed->Entry::getType() << " " << removed->getName() << std::endl; + } else { + std::cout << "Could not find any inventory item nor spell by that name" << std::endl; + } +} const std::map> nargs({ {"cp", {2}}, {"mv", {2}}, - {"damage", {2, 3}}, - {"heal", {2}} + {"damage", {2, 3, 4, 5, 6}}, + {"heal", {2}}, }); void checkArgs(std::string cmd, std::vector args) { @@ -298,6 +432,7 @@ int main(int argc, char *argv[]) { else if(cmd == "reset") reset(args); else if(cmd == "set") set(args); else if(cmd == "add") add(args); + else if(cmd == "del") del(args); else if(cmd == "help") usage(exename); else list(argsOrig); } catch(std::exception& e) { -- cgit v1.2.3