@ -5,347 +5,442 @@
# include <components/esm/loadmgef.hpp>
# include "creaturestats.hpp"
# include "spellcasting.hpp"
# include "spelleffects.hpp"
# include "../mwbase/environment.hpp"
# include "../mwbase/world.hpp"
# include "../mwworld/esmstore.hpp"
# include "../mwworld/class.hpp"
# include "../mwworld/inventorystore.hpp"
namespace MWMechanics
namespace
{
void ActiveSpells : : update ( float duration ) const
bool merge ( std : : vector < ESM : : ActiveEffect > & present , const std : : vector < ESM : : ActiveEffect > & queued )
{
bool rebuild = false ;
// Erase no longer active spells and effects
if ( duration > 0 )
// Can't merge if we already have an effect with the same effect index
auto problem = std : : find_if ( queued . begin ( ) , queued . end ( ) , [ & ] ( const auto & qEffect )
{
TContainer : : iterator iter ( mSpells . begin ( ) ) ;
while ( iter ! = mSpells . end ( ) )
{
if ( ! timeToExpire ( iter ) )
{
mSpells . erase ( iter + + ) ;
rebuild = true ;
}
else
{
bool interrupt = false ;
std : : vector < ActiveEffect > & effects = iter - > second . mEffects ;
for ( std : : vector < ActiveEffect > : : iterator effectIt = effects . begin ( ) ; effectIt ! = effects . end ( ) ; )
{
if ( effectIt - > mTimeLeft < = 0 )
{
rebuild = true ;
// Note: it we expire a Corprus effect, we should remove the whole spell.
if ( effectIt - > mEffectId = = ESM : : MagicEffect : : Corprus )
{
iter = mSpells . erase ( iter ) ;
interrupt = true ;
break ;
}
effectIt = effects . erase ( effectIt ) ;
}
else
{
effectIt - > mTimeLeft - = duration ;
+ + effectIt ;
}
}
if ( ! interrupt )
+ + iter ;
}
}
}
if ( mSpellsChanged )
{
mSpellsChanged = false ;
rebuild = true ;
}
if ( rebuild )
rebuildEffects ( ) ;
return std : : find_if ( present . begin ( ) , present . end ( ) , [ & ] ( const auto & pEffect ) { return pEffect . mEffectIndex = = qEffect . mEffectIndex ; } ) ! = present . end ( ) ;
} ) ;
if ( problem ! = queued . end ( ) )
return false ;
present . insert ( present . end ( ) , queued . begin ( ) , queued . end ( ) ) ;
return true ;
}
void ActiveSpells: : rebuildEffects ( ) const
void addEffects ( std : : vector < ESM : : ActiveEffect > & effects , const ESM : : EffectList & list , bool ignoreResistances = false )
{
mEffects = MagicEffects ( ) ;
for ( TIterator iter ( begin ( ) ) ; iter ! = end ( ) ; + + iter )
int currentEffectIndex = 0 ;
for ( const auto & enam : list . mList )
{
const std : : vector < ActiveEffect > & effects = iter - > second . mEffects ;
for ( std : : vector < ActiveEffect > : : const_iterator effectIt = effects . begin ( ) ; effectIt ! = effects . end ( ) ; + + effectIt )
{
if ( effectIt - > mTimeLeft > 0 )
mEffects . add ( MWMechanics : : EffectKey ( effectIt - > mEffectId , effectIt - > mArg ) , MWMechanics : : EffectParam ( effectIt - > mMagnitude ) ) ;
}
ESM : : ActiveEffect effect ;
effect . mEffectId = enam . mEffectID ;
effect . mArg = MWMechanics : : EffectKey ( enam ) . mArg ;
effect . mMagnitude = 0.f ;
effect . mMinMagnitude = enam . mMagnMin ;
effect . mMaxMagnitude = enam . mMagnMax ;
effect . mEffectIndex = currentEffectIndex + + ;
effect . mFlags = ESM : : ActiveEffect : : Flag_None ;
if ( ignoreResistances )
effect . mFlags | = ESM : : ActiveEffect : : Flag_Ignore_Resistances ;
effect . mDuration = - 1 ;
effect . mTimeLeft = - 1 ;
effects . emplace_back ( effect ) ;
}
}
}
ActiveSpells : : ActiveSpells ( )
: mSpellsChanged ( false )
{ }
namespace MWMechanics
{
ActiveSpells : : IterationGuard : : IterationGuard ( ActiveSpells & spells ) : mActiveSpells ( spells )
{
mActiveSpells . mIterating = true ;
}
const MagicEffects & ActiveSpells : : getMagicEffects ( ) const
ActiveSpells : : IterationGuard : : ~ IterationGuard ( )
{
update ( 0.f ) ;
return mEffects ;
mActiveSpells . mIterating = false ;
}
ActiveSpells : : TIterator ActiveSpells : : begin ( ) const
ActiveSpells : : ActiveSpellParams : : ActiveSpellParams ( const CastSpell & cast , const MWWorld : : Ptr & caster )
: mId ( cast . mId ) , mDisplayName ( cast . mSourceName ) , mCasterActorId ( - 1 ) , mSlot ( cast . mSlot ) , mType ( cast . mType ) , mWorsenings ( - 1 )
{
return mSpells . begin ( ) ;
if ( ! caster . isEmpty ( ) & & caster . getClass ( ) . isActor ( ) )
mCasterActorId = caster . getClass ( ) . getCreatureStats ( caster ) . getActorId ( ) ;
}
ActiveSpells : : TIterator ActiveSpells : : end ( ) const
ActiveSpells : : ActiveSpellParams : : ActiveSpellParams ( const ESM : : Spell * spell , const MWWorld : : Ptr & actor , bool ignoreResistances )
: mId ( spell - > mId ) , mDisplayName ( spell - > mName ) , mCasterActorId ( actor . getClass ( ) . getCreatureStats ( actor ) . getActorId ( ) ) , mSlot ( 0 )
, mType ( spell - > mData . mType = = ESM : : Spell : : ST_Ability ? ESM : : ActiveSpells : : Type_Ability : ESM : : ActiveSpells : : Type_Permanent ) , mWorsenings ( - 1 )
{
return mSpells . end ( ) ;
assert ( spell - > mData . mType ! = ESM : : Spell : : ST_Spell & & spell - > mData . mType ! = ESM : : Spell : : ST_Power ) ;
addEffects ( mEffects , spell - > mEffects , ignoreResistances ) ;
}
double ActiveSpells : : timeToExpire ( const TIterator & iterator ) const
ActiveSpells : : ActiveSpellParams : : ActiveSpellParams ( const MWWorld : : ConstPtr & item , const ESM : : Enchantment * enchantment , int slotIndex , const MWWorld : : Ptr & actor )
: mId ( item . getCellRef ( ) . getRefId ( ) ) , mDisplayName ( item . getClass ( ) . getName ( item ) ) , mCasterActorId ( actor . getClass ( ) . getCreatureStats ( actor ) . getActorId ( ) )
, mSlot ( slotIndex ) , mType ( ESM : : ActiveSpells : : Type_Enchantment ) , mWorsenings ( - 1 )
{
const std : : vector < ActiveEffect > & effects = iterator - > second . mEffects ;
assert ( enchantment - > mData . mType = = ESM : : Enchantment : : ConstantEffect ) ;
addEffects ( mEffects , enchantment - > mEffects ) ;
}
float duration = 0 ;
ActiveSpells : : ActiveSpellParams : : ActiveSpellParams ( const ESM : : ActiveSpells : : ActiveSpellParams & params )
: mId ( params . mId ) , mEffects ( params . mEffects ) , mDisplayName ( params . mDisplayName ) , mCasterActorId ( params . mCasterActorId )
, mSlot ( params . mItem . isSet ( ) ? params . mItem . mIndex : 0 )
, mType ( params . mType ) , mWorsenings ( params . mWorsenings ) , mNextWorsening ( { params . mNextWorsening } )
{ }
for ( std : : vector < ActiveEffect > : : const_iterator iter ( effects . begin ( ) ) ;
iter ! = effects . end ( ) ; + + iter )
ESM : : ActiveSpells : : ActiveSpellParams ActiveSpells : : ActiveSpellParams : : toEsm ( ) const
{
ESM : : ActiveSpells : : ActiveSpellParams params ;
params . mId = mId ;
params . mEffects = mEffects ;
params . mDisplayName = mDisplayName ;
params . mCasterActorId = mCasterActorId ;
params . mItem . unset ( ) ;
if ( mSlot )
{
if ( iter - > mTimeLeft > duration )
duration = iter - > mTimeLeft ;
// Note that we're storing the inventory slot as a RefNum instead of an int as a matter of future proofing
// mSlot needs to be replaced with a RefNum once inventory items get persistent RefNum (#4508 #6148)
params . mItem = { static_cast < unsigned int > ( mSlot ) , 0 } ;
}
params . mType = mType ;
params . mWorsenings = mWorsenings ;
params . mNextWorsening = mNextWorsening . toEsm ( ) ;
return params ;
}
if ( duration < 0 )
return 0 ;
return duration ;
void ActiveSpells : : ActiveSpellParams : : worsen ( )
{
+ + mWorsenings ;
if ( ! mWorsenings )
mNextWorsening = MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeStamp ( ) ;
mNextWorsening + = CorprusStats : : sWorseningPeriod ;
}
bool ActiveSpells : : isSpellActive( const std : : string & id ) const
bool ActiveSpells : : ActiveSpellParams: : shouldWorsen ( ) const
{
for ( TContainer : : iterator iter = mSpells . begin ( ) ; iter ! = mSpells . end ( ) ; + + iter )
{
if ( Misc : : StringUtils : : ciEqual ( iter - > first , id ) )
return true ;
}
return false ;
return mWorsenings > = 0 & & MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeStamp ( ) > = mNextWorsening ;
}
const ActiveSpells : : TContainer & ActiveSpells : : getActiveSpells ( ) const
void ActiveSpells : : ActiveSpellParams : : resetWorsenings ( )
{
return mSpells ;
mWorsenings = - 1 ;
}
void ActiveSpells : : addSpell ( const std : : string & id , bool stack , const std : : vector < ActiveEffect > & effects ,
const std : : string & displayName , int casterActorId )
void ActiveSpells : : update ( const MWWorld : : Ptr & ptr , float duration )
{
TContainer : : iterator it ( mSpells . find ( id ) ) ;
const auto & creatureStats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
assert ( & creatureStats . getActiveSpells ( ) = = this ) ;
IterationGuard guard { * this } ;
// Erase no longer active spells and effects
for ( auto spellIt = mSpells . begin ( ) ; spellIt ! = mSpells . end ( ) ; )
{
if ( spellIt - > mType ! = ESM : : ActiveSpells : : Type_Temporary & & spellIt - > mType ! = ESM : : ActiveSpells : : Type_Consumable )
{
+ + spellIt ;
continue ;
}
bool removedSpell = false ;
for ( auto effectIt = spellIt - > mEffects . begin ( ) ; effectIt ! = spellIt - > mEffects . end ( ) ; )
{
if ( effectIt - > mFlags & ESM : : ActiveEffect : : Flag_Remove & & effectIt - > mTimeLeft < = 0.f )
{
auto effect = * effectIt ;
effectIt = spellIt - > mEffects . erase ( effectIt ) ;
onMagicEffectRemoved ( ptr , * spellIt , effect ) ;
removedSpell = applyPurges ( ptr , & spellIt , & effectIt ) ;
if ( removedSpell )
break ;
}
else
{
+ + effectIt ;
}
}
if ( removedSpell )
continue ;
if ( spellIt - > mEffects . empty ( ) )
spellIt = mSpells . erase ( spellIt ) ;
else
+ + spellIt ;
}
ActiveSpellParams params ;
params . mEffects = effects ;
params . mDisplayName = displayName ;
params . mCasterActorId = casterActorId ;
for ( const auto & spell : mQueue )
addToSpells ( ptr , spell ) ;
mQueue . clear ( ) ;
if ( it = = end ( ) | | stack )
// Vanilla only does this on cell change I think
const auto & spells = creatureStats . getSpells ( ) ;
for ( const ESM : : Spell * spell : spells )
{
mSpells . insert ( std : : make_pair ( id , params ) ) ;
if ( spell - > mData . mType ! = ESM : : Spell : : ST_Spell & & spell - > mData . mType ! = ESM : : Spell : : ST_Power & & ! isSpellActive ( spell - > mId ) )
mSpells . emplace_back ( ActiveSpellParams { spell , ptr } ) ;
}
else
if ( ptr . getClass ( ) . hasInventoryStore ( ptr ) & & ! ( creatureStats . isDead ( ) & & ! creatureStats . isDeathAnimationFinished ( ) ) )
{
// addSpell() is called with effects for a range.
// but a spell may have effects with different ranges (e.g. Touch & Target)
// so, if we see new effects for same spell assume additional
// spell effects and add to existing effects of spell
mergeEffects ( params . mEffects , it - > second . mEffects ) ;
it - > second = params ;
auto & store = ptr . getClass ( ) . getInventoryStore ( ptr ) ;
if ( store . getInvListener ( ) ! = nullptr )
{
bool playNonLooping = ! store . isFirstEquip ( ) ;
const auto world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
for ( int slotIndex = 0 ; slotIndex < MWWorld : : InventoryStore : : Slots ; slotIndex + + )
{
auto slot = store . getSlot ( slotIndex ) ;
if ( slot = = store . end ( ) )
continue ;
const auto & enchantmentId = slot - > getClass ( ) . getEnchantment ( * slot ) ;
if ( enchantmentId . empty ( ) )
continue ;
const ESM : : Enchantment * enchantment = world - > getStore ( ) . get < ESM : : Enchantment > ( ) . find ( enchantmentId ) ;
if ( enchantment - > mData . mType ! = ESM : : Enchantment : : ConstantEffect )
continue ;
if ( std : : find_if ( mSpells . begin ( ) , mSpells . end ( ) , [ & ] ( const ActiveSpellParams & params )
{
return params . mSlot = = slotIndex & & params . mType = = ESM : : ActiveSpells : : Type_Enchantment & & params . mId = = slot - > getCellRef ( ) . getRefId ( ) ;
} ) ! = mSpells . end ( ) )
continue ;
ActiveSpellParams params ( * slot , enchantment , slotIndex , ptr ) ;
mSpells . emplace_back ( params ) ;
for ( const auto & effect : params . mEffects )
MWMechanics : : playEffects ( ptr , * world - > getStore ( ) . get < ESM : : MagicEffect > ( ) . find ( effect . mEffectId ) , playNonLooping ) ;
}
}
}
mSpellsChanged = true ;
}
void ActiveSpells : : mergeEffects ( std : : vector < ActiveEffect > & addTo , const std : : vector < ActiveEffect > & from )
{
for ( std : : vector < ActiveEffect > : : const_iterator effect ( from . begin ( ) ) ; effect ! = from . end ( ) ; + + effect )
// Update effects
for ( auto spellIt = mSpells . begin ( ) ; spellIt ! = mSpells . end ( ) ; )
{
// if effect is not in addTo, add it
bool missing = true ;
for ( std : : vector < ActiveEffect > : : const_iterator iter ( addTo . begin ( ) ) ; iter ! = addTo . end ( ) ; + + iter )
const auto caster = MWBase : : Environment : : get ( ) . getWorld ( ) - > searchPtrViaActorId ( spellIt - > mCasterActorId ) ; //Maybe make this search outside active grid?
bool removedSpell = false ;
for ( auto it = spellIt - > mEffects . begin ( ) ; it ! = spellIt - > mEffects . end ( ) ; )
{
if ( ( effect - > mEffectId = = iter - > mEffectId ) & & ( effect - > mArg = = iter - > mArg ) )
{
missing = false ;
bool remove = applyMagicEffect ( ptr , caster , * spellIt , * it , duration ) ;
if ( remove )
it = spellIt - > mEffects . erase ( it ) ;
else
+ + it ;
removedSpell = applyPurges ( ptr , & spellIt , & it ) ;
if ( removedSpell )
break ;
}
}
if ( missing )
if ( removedSpell )
continue ;
bool remove = false ;
if ( spellIt - > mType = = ESM : : ActiveSpells : : Type_Ability | | spellIt - > mType = = ESM : : ActiveSpells : : Type_Permanent )
remove = ! spells . hasSpell ( spellIt - > mId ) ;
else if ( spellIt - > mType = = ESM : : ActiveSpells : : Type_Enchantment )
{
addTo . push_back ( * effect ) ;
const auto & store = ptr . getClass ( ) . getInventoryStore ( ptr ) ;
auto slot = store . getSlot ( spellIt - > mSlot ) ;
remove = slot = = store . end ( ) | | slot - > getCellRef ( ) . getRefId ( ) ! = spellIt - > mId ;
}
if ( remove )
{
auto params = * spellIt ;
spellIt = mSpells . erase ( spellIt ) ;
for ( const auto & effect : params . mEffects )
onMagicEffectRemoved ( ptr , params , effect ) ;
applyPurges ( ptr , & spellIt ) ;
continue ;
}
+ + spellIt ;
}
}
void ActiveSpells : : removeEffects ( const std : : string & id )
void ActiveSpells : : addToSpells( const MWWorld : : Ptr & ptr , const ActiveSpellParams & spell )
{
for ( TContainer : : iterator spell = mSpells . begin ( ) ; spell ! = mSpells . end ( ) ; + + spell )
if( spell . mType ! = ESM : : ActiveSpells : : Type_Consumable )
{
if ( spell - > first = = id )
auto found = std : : find_if ( mSpells . begin ( ) , mSpells . end ( ) , [ & ] ( const auto & existing )
{
return spell . mId = = existing . mId & & spell . mCasterActorId = = existing . mCasterActorId & & spell . mSlot = = existing . mSlot ;
} ) ;
if ( found ! = mSpells . end ( ) )
{
spell - > second . mEffects . clear ( ) ;
mSpellsChanged = true ;
if ( merge ( found - > mEffects , spell . mEffects ) )
return ;
auto params = * found ;
mSpells . erase ( found ) ;
for ( const auto & effect : params . mEffects )
onMagicEffectRemoved ( ptr , params , effect ) ;
}
}
mSpells . emplace_back ( spell ) ;
}
void ActiveSpells : : visitEffectSources ( EffectSourceVisitor & visitor ) const
ActiveSpells : : ActiveSpells ( ) : mIterating ( false )
{ }
ActiveSpells : : TIterator ActiveSpells : : begin ( ) const
{
for ( TContainer : : const_iterator it = begin ( ) ; it ! = end ( ) ; + + it )
{
for ( std : : vector < ActiveEffect > : : const_iterator effectIt = it - > second . mEffects . begin ( ) ;
effectIt ! = it - > second . mEffects . end ( ) ; + + effectIt )
{
std : : string name = it - > second . mDisplayName ;
return mSpells . begin ( ) ;
}
float magnitude = effectIt - > mMagnitude ;
if ( magnitude )
visitor . visit ( MWMechanics : : EffectKey ( effectIt - > mEffectId , effectIt - > mArg ) , effectIt - > mEffectIndex , name , it - > first , it - > second . mCasterActorId , magnitude , effectIt - > mTimeLeft , effectIt - > mDuration ) ;
}
}
ActiveSpells : : TIterator ActiveSpells : : end ( ) const
{
return mSpells . end ( ) ;
}
void ActiveSpells : : purgeAll ( float chance , bool spellOnly )
bool ActiveSpells : : isSpellActive ( const std : : string & id ) const
{
for ( TContainer : : iterator it = mSpells . begin ( ) ; it ! = mSpells . end ( ) ; )
return std : : find_if ( mSpells . begin ( ) , mSpells . end ( ) , [ & ] ( const auto & spell )
{
const std : : string spellId = it - > first ;
return Misc : : StringUtils : : ciEqual ( spell . mId , id ) ;
} ) ! = mSpells . end ( ) ;
}
// if spellOnly is true, dispell only spells. Leave potions, enchanted items etc.
if ( spellOnly )
{
const ESM : : Spell * spell = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Spell > ( ) . search ( spellId ) ;
if ( ! spell | | spell - > mData . mType ! = ESM : : Spell : : ST_Spell )
{
+ + it ;
continue ;
}
}
void ActiveSpells : : addSpell ( const ActiveSpellParams & params )
{
mQueue . emplace_back ( params ) ;
}
if ( Misc : : Rng : : roll0to99 ( ) < chance )
mSpells . erase ( it + + ) ;
else
+ + it ;
}
mSpellsChanged = true ;
void ActiveSpells : : addSpell ( const ESM : : Spell * spell , const MWWorld : : Ptr & actor )
{
mQueue . emplace_back ( ActiveSpellParams { spell , actor , true } ) ;
}
void ActiveSpells : : purge Effect( short effectId )
void ActiveSpells : : purge ( ParamsPredicate predicate , const MWWorld : : Ptr & ptr )
{
for ( TContainer : : iterator it = mSpells . begin ( ) ; it ! = mSpells . end ( ) ; + + it )
assert ( & ptr . getClass ( ) . getCreatureStats ( ptr ) . getActiveSpells ( ) = = this ) ;
mPurges . emplace ( predicate ) ;
if ( ! mIterating )
{
for ( std : : vector < ActiveEffect > : : iterator effectIt = it - > second . mEffects . begin ( ) ;
effectIt ! = it - > second . mEffects . end ( ) ; )
{
if ( effectIt - > mEffectId = = effectId )
effectIt = it - > second . mEffects . erase ( effectIt ) ;
else
+ + effectIt ;
}
IterationGuard guard { * this } ;
applyPurges ( ptr ) ;
}
mSpellsChanged = true ;
}
void ActiveSpells : : purge Effect( short effectId , const std : : string & sourceId , int effectIndex )
void ActiveSpells : : purge ( EffectPredicate predicate , const MWWorld : : Ptr & ptr )
{
for ( TContainer : : iterator it = mSpells . begin ( ) ; it ! = mSpells . end ( ) ; + + it )
assert ( & ptr . getClass ( ) . getCreatureStats ( ptr ) . getActiveSpells ( ) = = this ) ;
mPurges . emplace ( predicate ) ;
if ( ! mIterating )
{
for ( std : : vector < ActiveEffect > : : iterator effectIt = it - > second . mEffects . begin ( ) ;
effectIt ! = it - > second . mEffects . end ( ) ; )
{
if ( effectIt - > mEffectId = = effectId & & it - > first = = sourceId & & ( effectIndex < 0 | | effectIndex = = effectIt - > mEffectIndex ) )
effectIt = it - > second . mEffects . erase ( effectIt ) ;
else
+ + effectIt ;
}
IterationGuard guard { * this } ;
applyPurges ( ptr ) ;
}
mSpellsChanged = true ;
}
void ActiveSpells : : purge ( int casterActorId )
bool ActiveSpells : : applyPurges ( const MWWorld : : Ptr & ptr , std : : list < ActiveSpellParams > : : iterator * currentSpell , std : : vector < ActiveEffect > : : iterator * currentEffect )
{
for ( TContainer : : iterator it = mSpells . begin ( ) ; it ! = mSpells . end ( ) ; + + it )
bool removedCurrentSpell = false ;
while ( ! mPurges . empty ( ) )
{
for ( std : : vector < ActiveEffect > : : iterator effectIt = it - > second . mEffects . begin ( ) ;
effectIt ! = it - > second . mEffects . end ( ) ; )
auto predicate = mPurges . front ( ) ;
mPurges . pop ( ) ;
for ( auto spellIt = mSpells . begin ( ) ; spellIt ! = mSpells . end ( ) ; )
{
if ( it - > second . mCasterActorId = = casterActorId )
effectIt = it - > second . mEffects . erase ( effectIt ) ;
else
+ + effectIt ;
bool isCurrentSpell = currentSpell & & * currentSpell = = spellIt ;
std : : visit ( [ & ] ( auto & & variant )
{
using T = std : : decay_t < decltype ( variant ) > ;
if constexpr ( std : : is_same_v < T , ParamsPredicate > )
{
if ( variant ( * spellIt ) )
{
auto params = * spellIt ;
spellIt = mSpells . erase ( spellIt ) ;
if ( isCurrentSpell )
{
* currentSpell = spellIt ;
removedCurrentSpell = true ;
}
for ( const auto & effect : params . mEffects )
onMagicEffectRemoved ( ptr , params , effect ) ;
}
else
+ + spellIt ;
}
else
{
static_assert ( std : : is_same_v < T , EffectPredicate > , " Non-exhaustive visitor " ) ;
for ( auto effectIt = spellIt - > mEffects . begin ( ) ; effectIt ! = spellIt - > mEffects . end ( ) ; )
{
if ( variant ( * spellIt , * effectIt ) )
{
auto effect = * effectIt ;
if ( isCurrentSpell & & currentEffect )
{
auto distance = std : : distance ( spellIt - > mEffects . begin ( ) , * currentEffect ) ;
if ( effectIt < = * currentEffect )
distance - - ;
effectIt = spellIt - > mEffects . erase ( effectIt ) ;
* currentEffect = spellIt - > mEffects . begin ( ) + distance ;
}
else
effectIt = spellIt - > mEffects . erase ( effectIt ) ;
onMagicEffectRemoved ( ptr , * spellIt , effect ) ;
}
else
+ + effectIt ;
}
+ + spellIt ;
}
} , predicate ) ;
}
}
mSpellsChanged = true ;
return re movedCurrent Spell;
}
void ActiveSpells : : purgeCorprusDisease ( )
void ActiveSpells : : removeEffects( const MWWorld : : Ptr & ptr , const std : : string & id )
{
for ( TContainer : : iterator iter = mSpells . begin ( ) ; iter ! = mSpells . end ( ) ; )
purge ( [ = ] ( const ActiveSpellParams & params )
{
bool hasCorprusEffect = false ;
for ( std : : vector < ActiveEffect > : : iterator effectIt = iter - > second . mEffects . begin ( ) ;
effectIt ! = iter - > second . mEffects . end ( ) ; + + effectIt )
{
if ( effectIt - > mEffectId = = ESM : : MagicEffect : : Corprus )
{
hasCorprusEffect = true ;
break ;
}
}
if ( hasCorprusEffect )
{
mSpells . erase ( iter + + ) ;
mSpellsChanged = true ;
}
else
+ + iter ;
}
return params . mId = = id ;
} , ptr ) ;
}
void ActiveSpells : : clear ( )
void ActiveSpells : : purgeEffect ( const MWWorld : : Ptr & ptr , short effectId )
{
mSpells . clear ( ) ;
mSpellsChanged = true ;
purge ( [ = ] ( const ActiveSpellParams & , const ESM : : ActiveEffect & effect )
{
return effect . mEffectId = = effectId ;
} , ptr ) ;
}
void ActiveSpells : : writeState ( ESM : : ActiveSpells & state ) const
void ActiveSpells : : purge ( const MWWorld : : Ptr & ptr , int casterActorId )
{
for ( TContainer : : const_iterator it = mSpells . begin ( ) ; it ! = mSpells . end ( ) ; + + it )
purge ( [ = ] ( const ActiveSpellParams & params )
{
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
ESM : : ActiveSpells : : ActiveSpellParams params ;
params . mEffects = it - > second . mEffects ;
params . mCasterActorId = it - > second . mCasterActorId ;
params . mDisplayName = it - > second . mDisplayName ;
return params . mCasterActorId = = casterActorId ;
} , ptr ) ;
}
state . mSpells . insert ( std : : make_pair ( it - > first , params ) ) ;
}
void ActiveSpells : : clear ( const MWWorld : : Ptr & ptr )
{
mQueue . clear ( ) ;
purge ( [ ] ( const ActiveSpellParams & params ) { return true ; } , ptr ) ;
}
void ActiveSpells : : readState( const ESM : : ActiveSpells & state )
void ActiveSpells : : skipWorsenings( double hours )
{
for ( ESM : : ActiveSpells : : TContainer : : const_iterator it = state . mSpells . begin ( ) ; it ! = state . mSpells . end ( ) ; + + it )
for ( auto & spell : mSpells )
{
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
ActiveSpellParams params ;
params . mEffects = it - > second . mEffects ;
params . mCasterActorId = it - > second . mCasterActorId ;
params . mDisplayName = it - > second . mDisplayName ;
mSpells . insert ( std : : make_pair ( it - > first , params ) ) ;
mSpellsChanged = true ;
if ( spell . mWorsenings > = 0 )
spell . mNextWorsening + = hours ;
}
}
void ActiveSpells : : writeState ( ESM : : ActiveSpells & state ) const
{
for ( const auto & spell : mSpells )
state . mSpells . emplace_back ( spell . toEsm ( ) ) ;
for ( const auto & spell : mQueue )
state . mQueue . emplace_back ( spell . toEsm ( ) ) ;
}
void ActiveSpells : : readState ( const ESM : : ActiveSpells & state )
{
for ( const ESM : : ActiveSpells : : ActiveSpellParams & spell : state . mSpells )
mSpells . emplace_back ( ActiveSpellParams { spell } ) ;
for ( const ESM : : ActiveSpells : : ActiveSpellParams & spell : state . mQueue )
mQueue . emplace_back ( ActiveSpellParams { spell } ) ;
}
}