From ae68e84557a7e56fd7210c1009aa1313dcc78adf Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Nov 2023 15:28:00 -0500 Subject: Increased capacity for creating items from command line --- src/cmd/cmd_manipulate.cc | 41 ++++------------------ src/creature.cc | 86 +++++++++++++++++++++++++++++++++++++++++++++-- src/creature.h | 10 ++++++ src/entry.cc | 5 ++- src/item.cc | 5 ++- 5 files changed, 108 insertions(+), 39 deletions(-) diff --git a/src/cmd/cmd_manipulate.cc b/src/cmd/cmd_manipulate.cc index f0d3fc4..e29fd34 100644 --- a/src/cmd/cmd_manipulate.cc +++ b/src/cmd/cmd_manipulate.cc @@ -224,7 +224,9 @@ namespace cmd { } c->setProfLevel(skill, level); } else if(ability) { - c->setScore(ability, utils::parseInt(toSet)); + if(toSet == "proficient") c->setProficientSave(ability); + else if(toSet == "none") c->removeProficientSave(ability); + else c->setScore(ability, utils::parseInt(toSet)); } else { throw std::runtime_error("Subcommand 'set' expected an ability, skill, proficiency, or name field to set, but was given " + abilityOrSkill); } @@ -246,17 +248,8 @@ namespace cmd { } 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()); - } + if(! creature::addEntry(ent, *c)) { + 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()); } utils::saveJson(c->serialize(), p); text << "Added the " << ent->getType() << " " << ent->getName() << " to " << c->getGivenName() << " the " << c->getName() << std::endl; @@ -276,32 +269,12 @@ namespace cmd { itemName = i->getName(); } } catch(std::exception& e) {} // eat. - 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; - } - } - } + auto removed = creature::removeEntry(itemName, *c); utils::saveJson(c->serialize(), p); if(removed) { text << "Successfully removed the " << removed->Entry::getType() << " " << removed->getName() << std::endl; } else { - text << "Could not find any inventory item nor spell by that name" << std::endl; + text << "Could not find any entry by that name" << std::endl; } return text.str(); } diff --git a/src/creature.cc b/src/creature.cc index 4343b6d..955da4d 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -152,6 +152,19 @@ namespace creature { return bonus; } + void Creature::setProficientSave(const rules::Ability& ability) { + if(find(data->saves.begin(), data->saves.end(), ability) == data->saves.end()) { + data->saves.push_back(ability); + } + } + + void Creature::removeProficientSave(const rules::Ability& ability) { + for(auto it = data->saves.begin(); it != data->saves.end();) { + if(*it == ability) it = data->saves.erase(it); + else ++it; + } + } + void Creature::addInventoryItem(std::shared_ptr item) { data->inventory.push_back(item); } @@ -209,6 +222,19 @@ namespace creature { } } + void Creature::addFeature(std::shared_ptr feature) { + data->features.push_back(feature); + } + + void Creature::removeFeature(std::shared_ptr feature) { + for(auto it = data->features.begin(); it != data->features.end(); it++) { + if(*it == feature) { + data->features.erase(it); + break; + } + } + } + std::map Creature::getSkills() const { std::map s; for(auto skill : data->skills) { @@ -309,6 +335,46 @@ namespace creature { return out; } + bool addEntry(std::shared_ptr entry, Creature& c) { + auto i = std::dynamic_pointer_cast(entry); + if(i) { + c.addInventoryItem(i); + return true; + } + auto s = std::dynamic_pointer_cast(entry); + if(s) { + c.addSpell(s); + return true; + } + auto f = std::dynamic_pointer_cast(entry); + if(f) { + c.addFeature(f); + return true; + } + return false; + } + + template std::shared_ptr tryRemove(std::string entryName, F remover, std::vector entries) { + for(auto e : entries) { + std::string name = e->getName(); + if(utils::lower(name) == entryName) { + remover(e); + return e; + } + } + return std::shared_ptr(); + } + + std::shared_ptr removeEntry(std::string entryName, Creature& c) { + entryName = utils::lower(entryName); + if(auto e = tryRemove(entryName, [c](auto e) mutable {c.removeInventoryItem(e);}, c.getInventory())) return e; + if(auto e = tryRemove(entryName, [c](auto e) mutable {c.removeFeature(e);}, c.getFeatures())) return e; + if(c.getSpellcasting()) { + if(auto e = tryRemove(entryName, [c](auto e) mutable {c.removeSpell(e);}, c.getSpellcasting()->getSpells())) return e; + } + return std::shared_ptr(); + } + std::vector dmgTypes2text(std::vector dmg) { std::vector ret; for(dmgType t : dmg) { @@ -361,7 +427,9 @@ namespace creature { if(! getSkills().empty()) { text << std::endl << "Skills:" << std::endl; for(auto skill : getSkills()) { - text << " * " << skill.first.getName() << " (+" << skill.second << ")" << std::endl; + text << " * " << skill.first.getName(); + if(data->skills.at(skill.first) == 2) text << " Expert"; + text << " (+" << skill.second << ")" << std::endl; } } if(! getSaves().empty()) { @@ -389,9 +457,21 @@ namespace creature { } } if(! getInventory().empty()) { - text << std::endl << "Inventory:" << std::endl; + std::map inventoryText; + double totalWeight = 0; + int totalCost = 0; for(auto i : getInventory()) { - text << " * " << i->getText(*this) << std::endl; + totalWeight += i->getWeight(); + totalCost += i->getCost(); + auto t = i->getText(*this); + if(inventoryText.contains(t)) inventoryText[t]++; + else inventoryText.insert({t, 1}); + } + text << std::endl << "Inventory (" << totalWeight << " lbs, value " << utils::getCostString(totalCost) << "):" << std::endl; + for(auto const& [t, count] : inventoryText) { + text << " * "; + if(count > 1) text << count << "x "; + text << t << std::endl; } } if(! Entry::getText().empty()) { diff --git a/src/creature.h b/src/creature.h index de93b0e..621b397 100644 --- a/src/creature.h +++ b/src/creature.h @@ -80,11 +80,15 @@ namespace creature { void setScore(const rules::Ability& ability, int score); void setProfLevel(const rules::Skill& skill, int level); void setProficiency(int p); + void setProficientSave(const rules::Ability& ability); + void removeProficientSave(const rules::Ability& ability); void addInventoryItem(std::shared_ptr item); void addSpellcasting(void); void addSpell(std::shared_ptr spell); void removeSpell(std::shared_ptr spell); void removeInventoryItem(std::shared_ptr item); + void addFeature(std::shared_ptr feature); + void removeFeature(std::shared_ptr feature); // Events void longRest(void); @@ -107,4 +111,10 @@ namespace creature { // Helper function to get the best ability for this creature (chooses arbitrarily in case of ties) rules::Ability getBestAbility(const std::vector& abilities, const Creature& c); + + // Helper function to smartly add an entry. Returns true on success. + bool addEntry(std::shared_ptr entry, Creature& c); + + // Helper function to smartly remove an entry. Returns the removed entry on success, else a null shared_ptr + std::shared_ptr removeEntry(std::string entryName, Creature& c); } diff --git a/src/entry.cc b/src/entry.cc index f958e17..249382f 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -44,7 +44,10 @@ namespace entry { void Entry::setName(const std::string& name) {data->name = name;} std::string Entry::getEntryType(void) const {return data->entry;} std::string Entry::getType(void) const {return data->type;} - std::string Entry::getText(void) const {return data->text;} + std::string Entry::getText(void) const { + if(!data->text.empty() and data->text.back() == '\n') return data->text.substr(0, data->text.length()-1); // Strip final newline + return data->text; + } std::string Entry::getText(const creature::Creature& c) const { return getName() + " (" + getType() + "): " + getText(); } diff --git a/src/item.cc b/src/item.cc index cf55d4a..8d5d9a3 100644 --- a/src/item.cc +++ b/src/item.cc @@ -59,7 +59,10 @@ namespace entry { } string Item::getText() const { - return Entry::getText() + " " + getCostWeightText(); + stringstream text; + text << getName() << " (" << getType() << "): "; + text << Entry::getText() << " " << getCostWeightText(); + return text.str(); } string Item::getText(const creature::Creature& c) const { -- cgit v1.2.3