#include "spellcasting.h" #include "creature.h" #include "utils.h" #include #include #include using namespace std; namespace entry { // Slot level serialization void to_json(nlohmann::json& j, const SlotLevel& sl) { std::vector s; for(auto spell : sl.spells) { s.push_back(spell->getName()); } j = {{"slots", sl.numSlots}, {"spells", s}}; } void from_json(const nlohmann::json& j, SlotLevel& sl) { j.at("slots").get_to(sl.numSlots); sl.spells = utils::instantiateNames("spells", j["spells"]); } shared_ptr SlotLevel::create(const json& data) { return shared_ptr(new SlotLevel(data)); } struct spellcastingImpl { bool innate; rules::Ability spellcasting_ability; std::vector> levels; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(spellcastingImpl, innate, spellcasting_ability, levels); NLOHMANN_FRIEND_DEFS(Feature, Spellcasting, data); Spellcasting::Spellcasting() : Feature("spellcasting", "spells", ""), data(new spellcastingImpl()) { data->innate = false; data->spellcasting_ability = rules::Ability::Int(); } bool Spellcasting::isInnate(void) const {return data->innate;} rules::Ability Spellcasting::getAbility(void) const {return data->spellcasting_ability;} void Spellcasting::setAbility(const rules::Ability& ability) {data->spellcasting_ability = ability;} const std::vector>& Spellcasting::getSlotLevels(void) const {return data->levels;} void Spellcasting::addSlotLevel(void) {data->levels.push_back(std::shared_ptr(new SlotLevel()));} vector> Spellcasting::getSpells() const { vector> ret; for(auto sl : getSlotLevels()) { for(auto spell : sl->spells) { ret.push_back(spell); } } return ret; } string Spellcasting::getText(const creature::Creature& c) const { stringstream text; text << getName() << " (" << getType() << "): "; text << "The " << c.getCreatureName() << "'s spellcasting ability is " << getAbility().getFull(); text << " (spell save DC " << 8 + c.getBonus(getAbility()) + c.getProficiency(); text << ", +" << c.getBonus(getAbility()) + c.getProficiency() << " to hit with spell attacks)."; if(isInnate()) { text << " Spellcasting is innate."; } int levelNumber = -1; for(auto level : getSlotLevels()) { levelNumber++; // Skip if it's empty if(level->numSlots == 0 && level->spells.empty()) { continue; } text << endl; if(levelNumber == 0) { if(isInnate()) { text << " At will: "; } else { text << " Cantrips: "; } } else { if(isInnate()) { text << " " << level->numSlots << "/day each: "; } else { text << " " << utils::toOrdinal(levelNumber) << " level (" << level->numSlots << " slots): "; } } vector names; for(auto spell : level->spells) { names.push_back(spell->getName()); } text << utils::join(names, ", "); } return text.str(); } }