#include "creature.h" #include "json.hpp" #include "dice.h" #include "rules.h" #include "feature.h" #include "weapon.h" #include "armor.h" #include #include #include #include typedef nlohmann::json json; using namespace std; namespace creature { Creature::Creature(const json& data) : inventory(json2ptrvec(data["inventory"])), stats(data["stats"]), skills(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(data["saves"]), langs(data["langs"]), cr(data["cr"]), proficiency(data["prof"]), dmgImmunities(json2vec(data["d_immunities"])), dmgResistances(json2vec(data["d_resistances"])), dmgVulnerabilities(json2vec(data["d_vulnerabilities"])), condImmunities(json2vec(data["c_immunities"])), features(json2ptrvec(data["features"])) { // Initialize names and hp if(((map) data).contains("givenName")) { givenName = data["givenName"]; hpMax = data["hpMax"]; hp = data["hp"]; } else { givenName = "Jerry"; //TODO: Autogenerate hpMax = this->getBonus("con") * hdCount; for(int i = 0; i < hdCount; i++) { hpMax += roll(hdSides); } hp = hpMax; } } template vector getJsonVectP(vector src) { vector ret; for(T i : src) { ret.push_back(i->toJson()); } return ret; } 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)} }); } // True if type without matching qualifiers is in subdata bool conditionApplies(const string& type, const vector& qualifiers, const vector subdata) { bool applies = false; for(dmgType con : subdata) { if(con.type == type) { applies = true; for(string qualifier : qualifiers) { if(find(con.qualifiers.begin(), con.qualifiers.end(), qualifier) == con.qualifiers.end()) { applies = false; } } } } return applies; } void Creature::applyDamage(int amount, const string& type, const vector& qualifiers) { if(! conditionApplies(type, qualifiers, dmgImmunities)) { if(conditionApplies(type, qualifiers, dmgResistances)) { hp -= amount / 2; } else if(conditionApplies(type, qualifiers, dmgVulnerabilities)) { hp -= amount * 2; } else { hp -= amount; } } } int Creature::getSkillBonus(const string& skill) const { int bonus = this->getBonus(rules::skill2ability[skill]); if(skills.contains(skill)) { bonus += skills.at(skill) * getProficiency(); } return bonus; } int Creature::getAbilitySaveBonus(const string& ability) const { int bonus = this->getBonus(ability); if(find(saves.begin(), saves.end(), ability) != saves.end()) { bonus += getProficiency(); } return bonus; } void Creature::addInventoryItem(shared_ptr item) { inventory.push_back(item); } void Creature::removeInventoryItem(const string& name) { for(auto it = inventory.begin(); it != inventory.end(); it++) { if((*it)->getName() == name) { inventory.erase(it); break; } } } map Creature::getSkills() const { map s; for(auto skill : skills) { s[skill.first] = getSkillBonus(skill.first); } return s; } map Creature::getSaves() const { map s; for(auto save : saves) { s[save] = this->getBonus(save) + getProficiency(); } return s; } void Creature::setScore(const string& ability, int score) { stats[ability] = score; } void Creature::setProfLevel(const string& skill, int level) { skills[skill] = level; } const int getAC(const Creature& c) { int baseBonus = 10 + c.getBonus("dex"); int miscBonus = 0; for(auto a : getItems(c)) { if(c.getScore("str") < a->getStrRequirement()) { continue; } auto armorType = a->getArmorType(); if(armorType== "misc" || armorType == "shield") { miscBonus += a->getACBonus(); } else { baseBonus = a->getACBonus(); if(armorType == "light") { baseBonus += c.getBonus("dex"); } else if(armorType == "medium") { baseBonus += (c.getBonus("dex") > 2)? 2 : c.getBonus("dex"); } } } return baseBonus + miscBonus; } template vector mapItems(const vector>& items) { vector out; for(auto i : items) { out.push_back(i->getName()); } return out; } string genText(const Creature& c) { stringstream text; text << c.getGivenName() << " (" << c.getCreatureName() << "): " << c.getHP() << "/" << c.getHPMax() << " hp, " << getAC(c) << " ac"; string armor = utils::join(mapItems(creature::getItems(c)), ", "); if(! armor.empty()) { text << " (" << armor << ")"; } text << ", speed " << c.getSpeed() << "\n"; text << "A cr " << c.getCR() << " " << c.getAlignment() << " " << c.getSize() << " " << c.getType() << ".\n"; text << "Stats:\n"; //text << setiosflags(ios::fixed) << setw(6); for(auto ability : rules::abilities) { text << " " << setw(6) << std::left << ability; } text << "\n"; for(auto ability : rules::abilities) { text << setw(7) << std::left << (to_string(c.getScore(ability)) + "(" + to_string(c.getBonus(ability)) + ")"); } text << "\n"; text << "Senses: "; if(! c.getSenses().empty()) { text << utils::join(c.getSenses(), ", ") << ". "; } text << "Passive Perception " << 10 + c.getBonus("wis") << "\n"; if(! c.getLanguages().empty()) { text << "Languages: " << c.getLanguages() << "\n"; } text << "\nSkills:\n"; for(auto skill : c.getSkills()) { text << skill.first << " (+" << skill.second << ")\n"; } text << "\nSaves:\n"; for(auto save : c.getSaves()) { text << save.first << " (+" << save.second << ")\n"; } text << "\nFeatures:\n"; for(auto f: c.getFeatures()) { text << f->getText(c) << "\n"; } text << "\nInventory:\n"; for(auto i : c.getInventory()) { text << i->getText(c) << "\n"; } return text.str(); } }