Mutate base records when adding/removing spells

pull/581/head
Evil Eye 4 years ago
parent ae1af0e103
commit 6ad20ec9c7

@ -82,7 +82,7 @@ add_openmw_dir (mwclass
)
add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil spelllist
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning

@ -141,14 +141,9 @@ namespace MWClass
data->mCreatureStats.setDeathAnimationFinished(isPersistent(ptr));
// spells
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
iter!=ref->mBase->mSpells.mList.end(); ++iter)
{
if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter))
data->mCreatureStats.getSpells().add (spell);
else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility
Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'";
}
bool spellsInitialised = data->mCreatureStats.getSpells().setSpells(ref->mBase->mId);
if (!spellsInitialised)
data->mCreatureStats.getSpells().addAllToInstance(ref->mBase->mSpells.mList);
// inventory
bool hasInventory = hasInventoryStore(ptr);
@ -781,6 +776,9 @@ namespace MWClass
CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
const ESM::CreatureState& creatureState = state.asCreatureState();
customData.mContainerStore->readState (creatureState.mInventory);
bool spellsInitialised = customData.mCreatureStats.getSpells().setSpells(ptr.get<ESM::Creature>()->mBase->mId);
if(spellsInitialised)
customData.mCreatureStats.getSpells().clear();
customData.mCreatureStats.readState (creatureState.mCreatureStats);
}

@ -158,7 +158,7 @@ namespace
*
* and by adding class, race, specialization bonus.
*/
void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr)
void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr, bool spellsInitialised)
{
const ESM::Class *class_ =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);
@ -235,9 +235,11 @@ namespace
for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
std::vector<std::string> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
for (std::vector<std::string>::iterator it = spells.begin(); it != spells.end(); ++it)
npcStats.getSpells().add(*it);
if (!spellsInitialised)
{
std::vector<std::string> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
npcStats.getSpells().addAllToInstance(spells);
}
}
}
@ -311,6 +313,8 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
bool spellsInitialised = data->mNpcStats.getSpells().setSpells(ref->mBase->mId);
// creature stats
int gold=0;
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
@ -351,7 +355,7 @@ namespace MWClass
data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);
autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr);
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr, spellsInitialised);
data->mNpcStats.setNeedRecalcDynamicStats(true);
}
@ -362,14 +366,7 @@ namespace MWClass
// race powers
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
for (std::vector<std::string>::const_iterator iter (race->mPowers.mList.begin());
iter!=race->mPowers.mList.end(); ++iter)
{
if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter))
data->mNpcStats.getSpells().add (spell);
else
Log(Debug::Warning) << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'";
}
data->mNpcStats.getSpells().addAllToInstance(race->mPowers.mList);
if (!ref->mBase->mFaction.empty())
{
@ -390,17 +387,8 @@ namespace MWClass
data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm);
// spells
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
iter!=ref->mBase->mSpells.mList.end(); ++iter)
{
if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter))
data->mNpcStats.getSpells().add (spell);
else
{
/// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility
Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'";
}
}
if (!spellsInitialised)
data->mNpcStats.getSpells().addAllToInstance(ref->mBase->mSpells.mList);
// inventory
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
@ -1326,6 +1314,9 @@ namespace MWClass
const ESM::NpcState& npcState = state.asNpcState();
customData.mInventoryStore.readState (npcState.mInventory);
customData.mNpcStats.readState (npcState.mNpcStats);
bool spellsInitialised = customData.mNpcStats.getSpells().setSpells(ptr.get<ESM::NPC>()->mBase->mId);
if(spellsInitialised)
customData.mNpcStats.getSpells().clear();
customData.mNpcStats.readState (npcState.mCreatureStats);
}

@ -1973,10 +1973,6 @@ namespace MWMechanics
// One case where we need this is to make sure bound items are removed upon death
stats.modifyMagicEffects(MWMechanics::MagicEffects());
stats.getActiveSpells().clear();
if (!isPlayer)
stats.getSpells().clear();
// Make sure spell effects are removed
purgeSpellEffects(stats.getActorId());

@ -40,7 +40,7 @@ namespace MWMechanics
continue;
float resist = 0.f;
if (spells.hasCorprusEffect(spell))
if (Spells::hasCorprusEffect(spell))
resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistCorprusDisease).getMagnitude()
- actorEffects.get(ESM::MagicEffect::WeaknessToCorprusDisease).getMagnitude());
else if (spell->mData.mType == ESM::Spell::ST_Disease)

@ -83,7 +83,7 @@ namespace MWMechanics
// reset
creatureStats.setLevel(player->mNpdt.mLevel);
creatureStats.getSpells().clear();
creatureStats.getSpells().clear(true);
creatureStats.modifyMagicEffects(MagicEffects());
for (int i=0; i<27; ++i)

@ -1,8 +1,10 @@
#include "spells.hpp"
#include <components/debug/debuglog.hpp>
#include <components/esm/loadspel.hpp>
#include <components/esm/spellstate.hpp>
#include <components/misc/rng.hpp>
#include <components/misc/stringops.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -22,46 +24,41 @@ namespace MWMechanics
{
}
Spells::TIterator Spells::begin() const
std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::begin() const
{
return mSpells.begin();
}
Spells::TIterator Spells::end() const
std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::end() const
{
return mSpells.end();
}
const ESM::Spell* Spells::getSpell(const std::string& id) const
{
return MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(id);
}
void Spells::rebuildEffects() const
{
mEffects = MagicEffects();
mSourcedEffects.clear();
for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter)
for (const auto& iter : mSpells)
{
const ESM::Spell *spell = iter->first;
const ESM::Spell *spell = iter.first;
if (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight ||
spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse)
{
int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
for (const auto& effect : spell->mEffects.mList)
{
if (iter->second.mPurgedEffects.find(i) != iter->second.mPurgedEffects.end())
if (iter.second.mPurgedEffects.find(i) != iter.second.mPurgedEffects.end())
continue; // effect was purged
float random = 1.f;
if (iter->second.mEffectRands.find(i) != iter->second.mEffectRands.end())
random = iter->second.mEffectRands.at(i);
if (iter.second.mEffectRands.find(i) != iter.second.mEffectRands.end())
random = iter.second.mEffectRands.at(i);
float magnitude = it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random;
mEffects.add (*it, magnitude);
mSourcedEffects[spell].add(MWMechanics::EffectKey(*it), magnitude);
float magnitude = effect.mMagnMin + (effect.mMagnMax - effect.mMagnMin) * random;
mEffects.add (effect, magnitude);
mSourcedEffects[spell].add(MWMechanics::EffectKey(effect), magnitude);
++i;
}
@ -71,7 +68,7 @@ namespace MWMechanics
bool Spells::hasSpell(const std::string &spell) const
{
return hasSpell(getSpell(spell));
return hasSpell(SpellList::getSpell(spell));
}
bool Spells::hasSpell(const ESM::Spell *spell) const
@ -80,6 +77,16 @@ namespace MWMechanics
}
void Spells::add (const ESM::Spell* spell)
{
mSpellList->add(spell);
}
void Spells::add (const std::string& spellId)
{
add(SpellList::getSpell(spellId));
}
void Spells::addSpell(const ESM::Spell* spell)
{
if (mSpells.find (spell)==mSpells.end())
{
@ -106,24 +113,24 @@ namespace MWMechanics
}
}
void Spells::add (const std::string& spellId)
void Spells::remove (const std::string& spellId)
{
add(getSpell(spellId));
const auto spell = SpellList::getSpell(spellId);
removeSpell(spell);
mSpellList->remove(spell);
if (spellId==mSelectedSpell)
mSelectedSpell.clear();
}
void Spells::remove (const std::string& spellId)
void Spells::removeSpell(const ESM::Spell* spell)
{
const ESM::Spell* spell = getSpell(spellId);
TContainer::iterator iter = mSpells.find (spell);
if (iter!=mSpells.end())
const auto it = mSpells.find(spell);
if(it != mSpells.end())
{
mSpells.erase (iter);
mSpells.erase(it);
mSpellsChanged = true;
}
if (spellId==mSelectedSpell)
mSelectedSpell.clear();
}
MagicEffects Spells::getMagicEffects() const
@ -135,12 +142,19 @@ namespace MWMechanics
return mEffects;
}
void Spells::clear()
void Spells::removeAllSpells()
{
mSpells.clear();
mSpellsChanged = true;
}
void Spells::clear(bool modifyBase)
{
removeAllSpells();
if(modifyBase)
mSpellList->clear();
}
void Spells::setSelectedSpell (const std::string& spellId)
{
mSelectedSpell = spellId;
@ -166,101 +180,78 @@ namespace MWMechanics
return false;
}
bool Spells::hasCommonDisease() const
bool Spells::hasDisease(const ESM::Spell::SpellType type) const
{
for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter)
for (const auto& iter : mSpells)
{
const ESM::Spell *spell = iter->first;
if (spell->mData.mType == ESM::Spell::ST_Disease)
const ESM::Spell *spell = iter.first;
if (spell->mData.mType == type)
return true;
}
return false;
}
bool Spells::hasBlightDisease() const
bool Spells::hasCommonDisease() const
{
for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter)
{
const ESM::Spell *spell = iter->first;
if (spell->mData.mType == ESM::Spell::ST_Blight)
return true;
}
return hasDisease(ESM::Spell::ST_Disease);
}
return false;
bool Spells::hasBlightDisease() const
{
return hasDisease(ESM::Spell::ST_Blight);
}
void Spells::purgeCommonDisease()
void Spells::purge(const SpellFilter& filter)
{
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
std::vector<std::string> purged;
for (auto iter = mSpells.begin(); iter!=mSpells.end();)
{
const ESM::Spell *spell = iter->first;
if (spell->mData.mType == ESM::Spell::ST_Disease)
if (filter(spell))
{
mSpells.erase(iter++);
purged.push_back(spell->mId);
mSpellsChanged = true;
}
else
++iter;
}
if(!purged.empty())
mSpellList->removeAll(purged);
}
void Spells::purgeCommonDisease()
{
purge([](auto spell) { return spell->mData.mType == ESM::Spell::ST_Disease; });
}
void Spells::purgeBlightDisease()
{
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
const ESM::Spell *spell = iter->first;
if (spell->mData.mType == ESM::Spell::ST_Blight && !hasCorprusEffect(spell))
{
mSpells.erase(iter++);
mSpellsChanged = true;
}
else
++iter;
}
purge([](auto spell) { return spell->mData.mType == ESM::Spell::ST_Blight && !hasCorprusEffect(spell); });
}
void Spells::purgeCorprusDisease()
{
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
const ESM::Spell *spell = iter->first;
if (hasCorprusEffect(spell))
{
mSpells.erase(iter++);
mSpellsChanged = true;
}
else
++iter;
}
purge(&hasCorprusEffect);
}
void Spells::purgeCurses()
{
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
const ESM::Spell *spell = iter->first;
if (spell->mData.mType == ESM::Spell::ST_Curse)
{
mSpells.erase(iter++);
mSpellsChanged = true;
}
else
++iter;
}
purge([](auto spell) { return spell->mData.mType == ESM::Spell::ST_Curse; });
}
void Spells::removeEffects(const std::string &id)
{
if (isSpellActive(id))
{
for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell)
for (auto& spell : mSpells)
{
if (spell->first == getSpell(id))
if (spell.first == SpellList::getSpell(id))
{
for (long unsigned int i = 0; i != spell->first->mEffects.mList.size(); i++)
for (long unsigned int i = 0; i != spell.first->mEffects.mList.size(); i++)
{
spell->second.mPurgedEffects.insert(i);
spell.second.mPurgedEffects.insert(i);
}
}
}
@ -276,23 +267,21 @@ namespace MWMechanics
mSpellsChanged = false;
}
for (std::map<SpellKey, MagicEffects>::const_iterator it = mSourcedEffects.begin();
it != mSourcedEffects.end(); ++it)
for (const auto& it : mSourcedEffects)
{
const ESM::Spell * spell = it->first;
for (MagicEffects::Collection::const_iterator effectIt = it->second.begin();
effectIt != it->second.end(); ++effectIt)
const ESM::Spell * spell = it.first;
for (const auto& effectIt : it.second)
{
visitor.visit(effectIt->first, spell->mName, spell->mId, -1, effectIt->second.getMagnitude());
visitor.visit(effectIt.first, spell->mName, spell->mId, -1, effectIt.second.getMagnitude());
}
}
}
bool Spells::hasCorprusEffect(const ESM::Spell *spell)
{
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
for (const auto& effectIt : spell->mEffects.mList)
{
if (effectIt->mEffectID == ESM::MagicEffect::Corprus)
if (effectIt.mEffectID == ESM::MagicEffect::Corprus)
{
return true;
}
@ -302,14 +291,14 @@ namespace MWMechanics
void Spells::purgeEffect(int effectId)
{
for (TContainer::iterator spellIt = mSpells.begin(); spellIt != mSpells.end(); ++spellIt)
for (auto& spellIt : mSpells)
{
int i = 0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spellIt->first->mEffects.mList.begin(); effectIt != spellIt->first->mEffects.mList.end(); ++effectIt)
for (auto& effectIt : spellIt.first->mEffects.mList)
{
if (effectIt->mEffectID == effectId)
if (effectIt.mEffectID == effectId)
{
spellIt->second.mPurgedEffects.insert(i);
spellIt.second.mPurgedEffects.insert(i);
mSpellsChanged = true;
}
++i;
@ -319,15 +308,15 @@ namespace MWMechanics
void Spells::purgeEffect(int effectId, const std::string & sourceId)
{
const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(sourceId);
TContainer::iterator spellIt = mSpells.find(spell);
const ESM::Spell * spell = SpellList::getSpell(sourceId);
auto spellIt = mSpells.find(spell);
if (spellIt == mSpells.end())
return;
int i = 0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spellIt->first->mEffects.mList.begin(); effectIt != spellIt->first->mEffects.mList.end(); ++effectIt)
for (auto& effectIt : spellIt->first->mEffects.mList)
{
if (effectIt->mEffectID == effectId)
if (effectIt.mEffectID == effectId)
{
spellIt->second.mPurgedEffects.insert(i);
mSpellsChanged = true;
@ -338,11 +327,8 @@ namespace MWMechanics
bool Spells::canUsePower(const ESM::Spell* spell) const
{
std::map<SpellKey, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(spell);
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true;
else
return false;
const auto it = mUsedPowers.find(spell);
return it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp();
}
void Spells::usePower(const ESM::Spell* spell)
@ -352,6 +338,8 @@ namespace MWMechanics
void Spells::readState(const ESM::SpellState &state, CreatureStats* creatureStats)
{
const auto& baseSpells = mSpellList->getSpells();
for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)
{
// Discard spells that are no longer available due to changed content files
@ -365,6 +353,13 @@ namespace MWMechanics
mSelectedSpell = it->first;
}
}
// Add spells from the base record
for(const std::string& id : baseSpells)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id);
if(spell)
addSpell(spell);
}
for (std::map<std::string, ESM::TimeStamp>::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it)
{
@ -436,17 +431,50 @@ namespace MWMechanics
void Spells::writeState(ESM::SpellState &state) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
const auto& baseSpells = mSpellList->getSpells();
for (const auto& it : mSpells)
{
ESM::SpellState::SpellParams params;
params.mEffectRands = it->second.mEffectRands;
params.mPurgedEffects = it->second.mPurgedEffects;
state.mSpells.insert(std::make_pair(it->first->mId, params));
//Don't save spells stored in the base record
if(std::find(baseSpells.begin(), baseSpells.end(), it.first->mId) == baseSpells.end())
{
ESM::SpellState::SpellParams params;
params.mEffectRands = it.second.mEffectRands;
params.mPurgedEffects = it.second.mPurgedEffects;
state.mSpells.insert(std::make_pair(it.first->mId, params));
}
}
state.mSelectedSpell = mSelectedSpell;
for (std::map<SpellKey, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
state.mUsedPowers[it->first->mId] = it->second.toEsm();
for (const auto& it : mUsedPowers)
state.mUsedPowers[it.first->mId] = it.second.toEsm();
}
bool Spells::setSpells(const std::string& actorId)
{
bool result;
std::tie(mSpellList, result) = MWBase::Environment::get().getWorld()->getStore().getSpellList(actorId);
mSpellList->addListener(this);
for(const auto& id : mSpellList->getSpells())
addSpell(SpellList::getSpell(id));
return result;
}
void Spells::addAllToInstance(const std::vector<std::string>& spells)
{
for(const std::string& id : spells)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id);
if(spell)
addSpell(spell);
else
Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << id << "'";
}
}
Spells::~Spells()
{
if(mSpellList)
mSpellList->removeListener(this);
}
}

@ -1,22 +1,19 @@
#ifndef GAME_MWMECHANICS_SPELLS_H
#define GAME_MWMECHANICS_SPELLS_H
#include <memory>
#include <map>
#include <string>
#include <set>
#include <vector>
#include <components/misc/stringops.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/timestamp.hpp"
#include "magiceffects.hpp"
#include "spelllist.hpp"
namespace ESM
{
struct Spell;
struct SpellState;
}
@ -32,37 +29,36 @@ namespace MWMechanics
/// diseases. It also keeps track of used powers (which can only be used every 24h).
class Spells
{
public:
typedef const ESM::Spell* SpellKey;
struct SpellParams
{
std::map<int, float> mEffectRands; // <effect index, normalised random magnitude>
std::set<int> mPurgedEffects; // indices of purged effects
};
typedef std::map<SpellKey, SpellParams> TContainer;
typedef TContainer::const_iterator TIterator;
private:
TContainer mSpells;
std::shared_ptr<SpellList> mSpellList;
std::map<const ESM::Spell*, SpellParams> mSpells;
// Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)
std::string mSelectedSpell;
std::map<SpellKey, MWWorld::TimeStamp> mUsedPowers;
std::map<const ESM::Spell*, MWWorld::TimeStamp> mUsedPowers;
mutable bool mSpellsChanged;
mutable MagicEffects mEffects;
mutable std::map<SpellKey, MagicEffects> mSourcedEffects;
mutable std::map<const ESM::Spell*, MagicEffects> mSourcedEffects;
void rebuildEffects() const;
/// Get spell from ID, throws exception if not found
const ESM::Spell* getSpell(const std::string& id) const;
bool hasDisease(const ESM::Spell::SpellType type) const;
using SpellFilter = bool (*)(const ESM::Spell*);
void purge(const SpellFilter& filter);
void addSpell(const ESM::Spell* spell);
void removeSpell(const ESM::Spell* spell);
void removeAllSpells();
friend class SpellList;
public:
using TIterator = std::map<const ESM::Spell*, SpellParams>::const_iterator;
Spells();
~Spells();
static bool hasCorprusEffect(const ESM::Spell *spell);
void purgeEffect(int effectId);
@ -96,7 +92,7 @@ namespace MWMechanics
MagicEffects getMagicEffects() const;
///< Return sum of magic effects resulting from abilities, blights, deseases and curses.
void clear();
void clear(bool modifyBase = false);
///< Remove all spells of al types.
void setSelectedSpell (const std::string& spellId);
@ -118,6 +114,10 @@ namespace MWMechanics
void readState (const ESM::SpellState& state, CreatureStats* creatureStats);
void writeState (ESM::SpellState& state) const;
bool setSpells(const std::string& id);
void addAllToInstance(const std::vector<std::string>& spells);
};
}

@ -9,6 +9,8 @@
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include "../mwmechanics/spelllist.hpp"
namespace
{
void readRefs(const ESM::Cell& cell, std::map<ESM::RefNum, std::string>& refs, std::vector<ESM::ESMReader>& readers)
@ -409,4 +411,23 @@ void ESMStore::validate()
throw std::runtime_error ("Invalid player record (race or class unavailable");
}
std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> ESMStore::getSpellList(const std::string& originalId) const
{
const std::string id = Misc::StringUtils::lowerCase(originalId);
auto result = mSpellListCache.find(id);
std::shared_ptr<MWMechanics::SpellList> ptr;
if (result != mSpellListCache.end())
ptr = result->second.lock();
if (!ptr)
{
int type = find(id);
ptr = std::make_shared<MWMechanics::SpellList>(id, type);
if (result != mSpellListCache.end())
result->second = ptr;
else
mSpellListCache.insert({id, ptr});
return {ptr, false};
}
return {ptr, true};
}
} // end namespace

@ -1,6 +1,7 @@
#ifndef OPENMW_MWWORLD_ESMSTORE_H
#define OPENMW_MWWORLD_ESMSTORE_H
#include <memory>
#include <sstream>
#include <stdexcept>
@ -12,6 +13,11 @@ namespace Loading
class Listener;
}
namespace MWMechanics
{
class SpellList;
}
namespace MWWorld
{
class ESMStore
@ -78,6 +84,8 @@ namespace MWWorld
unsigned int mDynamicCount;
mutable std::map<std::string, std::weak_ptr<MWMechanics::SpellList> > mSpellListCache;
/// Validate entries in store after setup
void validate();
@ -257,6 +265,8 @@ namespace MWWorld
void checkPlayer();
int getRefCount(const std::string& id) const;
std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> getSpellList(const std::string& id) const;
};
template <>

@ -8,6 +8,12 @@
#include <components/loadinglistener/loadinglistener.hpp>
#include "apps/openmw/mwworld/esmstore.hpp"
#include "apps/openmw/mwmechanics/spelllist.hpp"
namespace MWMechanics
{
SpellList::SpellList(const std::string& id, int type) : mId(id), mType(type) {}
}
static Loading::Listener dummyListener;

@ -4,7 +4,7 @@
#include "esmwriter.hpp"
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
int ESM::SavedGame::sCurrentFormat = 12;
int ESM::SavedGame::sCurrentFormat = 13;
void ESM::SavedGame::load (ESMReader &esm)
{

Loading…
Cancel
Save