2012-04-11 18:29:36 +02:00
# include "spells.hpp"
2020-07-28 08:33:28 +02:00
# include <components/debug/debuglog.hpp>
2022-01-22 15:58:41 +01:00
# include <components/esm3/loadspel.hpp>
# include <components/esm3/spellstate.hpp>
2015-07-09 19:22:04 +02:00
# include <components/misc/rng.hpp>
2020-07-28 08:33:28 +02:00
# include <components/misc/stringops.hpp>
2012-04-11 18:29:36 +02:00
2012-04-23 15:27:03 +02:00
# include "../mwbase/environment.hpp"
2012-07-03 12:30:50 +02:00
# include "../mwbase/world.hpp"
2012-04-11 18:29:36 +02:00
2019-09-30 20:27:42 +04:00
# include "../mwworld/class.hpp"
2013-01-13 14:52:55 +01:00
# include "../mwworld/esmstore.hpp"
2019-09-30 20:27:42 +04:00
# include "actorutil.hpp"
# include "creaturestats.hpp"
2012-04-11 18:29:36 +02:00
# include "magiceffects.hpp"
2019-09-30 20:27:42 +04:00
# include "stat.hpp"
2012-04-11 18:29:36 +02:00
namespace MWMechanics
{
2016-07-02 12:50:00 +02:00
Spells : : Spells ( )
{
}
2021-03-14 19:32:03 +01:00
Spells : : Spells ( const Spells & spells ) : mSpellList ( spells . mSpellList ) , mSpells ( spells . mSpells ) ,
2021-08-27 20:07:50 +02:00
mSelectedSpell ( spells . mSelectedSpell ) , mUsedPowers ( spells . mUsedPowers )
2021-03-14 19:32:03 +01:00
{
if ( mSpellList )
mSpellList - > addListener ( this ) ;
}
2021-04-23 23:48:55 +02:00
Spells : : Spells ( Spells & & spells ) : mSpellList ( std : : move ( spells . mSpellList ) ) , mSpells ( std : : move ( spells . mSpells ) ) ,
2021-08-27 20:07:50 +02:00
mSelectedSpell ( std : : move ( spells . mSelectedSpell ) ) , mUsedPowers ( std : : move ( spells . mUsedPowers ) )
2021-04-23 23:48:55 +02:00
{
if ( mSpellList )
mSpellList - > updateListener ( & spells , this ) ;
}
2021-08-27 20:07:50 +02:00
std : : vector < const ESM : : Spell * > : : const_iterator Spells : : begin ( ) const
2012-04-11 18:29:36 +02:00
{
2012-04-11 19:40:42 +02:00
return mSpells . begin ( ) ;
2012-04-11 18:29:36 +02:00
}
2021-08-27 20:07:50 +02:00
std : : vector < const ESM : : Spell * > : : const_iterator Spells : : end ( ) const
2012-04-11 18:29:36 +02:00
{
2012-04-11 19:40:42 +02:00
return mSpells . end ( ) ;
2012-04-11 18:29:36 +02:00
}
2015-11-27 01:02:29 +01:00
bool Spells : : hasSpell ( const std : : string & spell ) const
{
2020-07-28 08:33:28 +02:00
return hasSpell ( SpellList : : getSpell ( spell ) ) ;
2015-11-27 01:02:29 +01:00
}
bool Spells : : hasSpell ( const ESM : : Spell * spell ) const
{
2021-08-27 20:07:50 +02:00
return std : : find ( mSpells . begin ( ) , mSpells . end ( ) , spell ) ! = mSpells . end ( ) ;
2015-11-27 01:02:29 +01:00
}
2014-12-14 01:53:15 +01:00
void Spells : : add ( const ESM : : Spell * spell )
2020-07-28 08:33:28 +02:00
{
mSpellList - > add ( spell ) ;
}
void Spells : : add ( const std : : string & spellId )
{
add ( SpellList : : getSpell ( spellId ) ) ;
}
void Spells : : addSpell ( const ESM : : Spell * spell )
2012-04-11 18:29:36 +02:00
{
2021-08-27 20:07:50 +02:00
if ( ! hasSpell ( spell ) )
mSpells . emplace_back ( spell ) ;
2012-04-11 18:29:36 +02:00
}
2020-07-28 08:33:28 +02:00
void Spells : : remove ( const std : : string & spellId )
2014-12-14 01:53:15 +01:00
{
2020-07-28 08:33:28 +02:00
const auto spell = SpellList : : getSpell ( spellId ) ;
removeSpell ( spell ) ;
mSpellList - > remove ( spell ) ;
if ( spellId = = mSelectedSpell )
mSelectedSpell . clear ( ) ;
2014-12-14 01:53:15 +01:00
}
2020-07-28 08:33:28 +02:00
void Spells : : removeSpell ( const ESM : : Spell * spell )
2012-04-11 18:29:36 +02:00
{
2021-08-27 20:07:50 +02:00
const auto it = std : : find ( mSpells . begin ( ) , mSpells . end ( ) , spell ) ;
2020-07-28 08:33:28 +02:00
if ( it ! = mSpells . end ( ) )
mSpells . erase ( it ) ;
2012-04-11 18:29:36 +02:00
}
2020-07-28 08:33:28 +02:00
void Spells : : removeAllSpells ( )
2012-04-11 18:29:36 +02:00
{
2012-04-11 19:40:42 +02:00
mSpells . clear ( ) ;
2012-04-11 18:29:36 +02:00
}
2012-04-13 10:49:45 +02:00
2020-07-28 08:33:28 +02:00
void Spells : : clear ( bool modifyBase )
{
removeAllSpells ( ) ;
if ( modifyBase )
mSpellList - > clear ( ) ;
}
2012-04-13 10:49:45 +02:00
void Spells : : setSelectedSpell ( const std : : string & spellId )
{
mSelectedSpell = spellId ;
}
const std : : string Spells : : getSelectedSpell ( ) const
{
return mSelectedSpell ;
}
2013-01-12 13:10:20 +01:00
2022-03-25 20:47:43 +02:00
bool Spells : : hasSpellType ( const ESM : : Spell : : SpellType type ) const
2012-11-09 18:16:29 +01:00
{
2022-03-25 20:47:43 +02:00
auto it = std : : find_if ( std : : begin ( mSpells ) , std : : end ( mSpells ) , [ = ] ( const ESM : : Spell * spell )
{
return spell - > mData . mType = = type ;
} ) ;
return it ! = std : : end ( mSpells ) ;
2012-11-09 18:16:29 +01:00
}
2020-07-28 08:33:28 +02:00
bool Spells : : hasCommonDisease ( ) const
2012-11-09 18:16:29 +01:00
{
2022-03-25 20:47:43 +02:00
return hasSpellType ( ESM : : Spell : : ST_Disease ) ;
2020-07-28 08:33:28 +02:00
}
2013-01-12 13:10:20 +01:00
2020-07-28 08:33:28 +02:00
bool Spells : : hasBlightDisease ( ) const
{
2022-03-25 20:47:43 +02:00
return hasSpellType ( ESM : : Spell : : ST_Blight ) ;
2012-11-09 18:16:29 +01:00
}
2013-11-15 20:29:47 +01:00
2020-07-28 08:33:28 +02:00
void Spells : : purge ( const SpellFilter & filter )
2013-11-17 23:15:57 +01:00
{
2020-07-28 08:33:28 +02:00
std : : vector < std : : string > purged ;
for ( auto iter = mSpells . begin ( ) ; iter ! = mSpells . end ( ) ; )
2013-11-17 23:15:57 +01:00
{
2021-08-27 20:07:50 +02:00
const ESM : : Spell * spell = * iter ;
2020-07-28 08:33:28 +02:00
if ( filter ( spell ) )
2016-07-02 12:50:00 +02:00
{
2021-08-31 19:59:55 +02:00
iter = mSpells . erase ( iter ) ;
2020-07-28 08:33:28 +02:00
purged . push_back ( spell - > mId ) ;
2016-07-02 12:50:00 +02:00
}
2013-11-17 23:15:57 +01:00
else
2014-04-27 19:03:33 +02:00
+ + iter ;
2013-11-17 23:15:57 +01:00
}
2020-07-28 08:33:28 +02:00
if ( ! purged . empty ( ) )
mSpellList - > removeAll ( purged ) ;
}
void Spells : : purgeCommonDisease ( )
{
purge ( [ ] ( auto spell ) { return spell - > mData . mType = = ESM : : Spell : : ST_Disease ; } ) ;
2013-11-17 23:15:57 +01:00
}
void Spells : : purgeBlightDisease ( )
{
2020-07-28 08:33:28 +02:00
purge ( [ ] ( auto spell ) { return spell - > mData . mType = = ESM : : Spell : : ST_Blight & & ! hasCorprusEffect ( spell ) ; } ) ;
2013-11-17 23:15:57 +01:00
}
void Spells : : purgeCorprusDisease ( )
{
2020-07-28 08:33:28 +02:00
purge ( & hasCorprusEffect ) ;
2013-11-17 23:15:57 +01:00
}
void Spells : : purgeCurses ( )
{
2020-07-28 08:33:28 +02:00
purge ( [ ] ( auto spell ) { return spell - > mData . mType = = ESM : : Spell : : ST_Curse ; } ) ;
2013-11-17 23:15:57 +01:00
}
2014-08-19 03:17:31 +02:00
bool Spells : : hasCorprusEffect ( const ESM : : Spell * spell )
{
2020-07-28 08:33:28 +02:00
for ( const auto & effectIt : spell - > mEffects . mList )
2014-08-19 03:17:31 +02:00
{
2020-07-28 08:33:28 +02:00
if ( effectIt . mEffectID = = ESM : : MagicEffect : : Corprus )
2014-08-19 03:17:31 +02:00
{
return true ;
}
}
return false ;
}
2015-11-27 01:02:29 +01:00
bool Spells : : canUsePower ( const ESM : : Spell * spell ) const
2014-05-12 21:04:02 +02:00
{
2022-03-25 20:42:01 +02:00
const auto it = std : : find_if ( std : : begin ( mUsedPowers ) , std : : end ( mUsedPowers ) , [ & ] ( auto & pair ) { return pair . first = = spell ; } ) ;
2020-07-28 08:33:28 +02:00
return it = = mUsedPowers . end ( ) | | it - > second + 24 < = MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeStamp ( ) ;
2014-05-12 21:04:02 +02:00
}
2015-11-27 01:02:29 +01:00
void Spells : : usePower ( const ESM : : Spell * spell )
2014-05-12 21:04:02 +02:00
{
2022-03-25 20:42:01 +02:00
// Updates or inserts a new entry with the current timestamp.
const auto it = std : : find_if ( std : : begin ( mUsedPowers ) , std : : end ( mUsedPowers ) , [ & ] ( auto & pair ) { return pair . first = = spell ; } ) ;
const auto timestamp = MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeStamp ( ) ;
if ( it = = mUsedPowers . end ( ) )
mUsedPowers . emplace_back ( spell , timestamp ) ;
else
it - > second = timestamp ;
2014-05-12 21:04:02 +02:00
}
2019-09-30 20:27:42 +04:00
void Spells : : readState ( const ESM : : SpellState & state , CreatureStats * creatureStats )
2014-05-12 21:04:02 +02:00
{
2020-07-28 08:33:28 +02:00
const auto & baseSpells = mSpellList - > getSpells ( ) ;
2021-08-27 20:07:50 +02:00
for ( const std : : string & id : state . mSpells )
2014-05-12 21:04:02 +02:00
{
2015-01-21 21:24:25 +01:00
// Discard spells that are no longer available due to changed content files
2021-08-27 20:07:50 +02:00
const ESM : : Spell * spell = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Spell > ( ) . search ( id ) ;
2015-01-21 21:24:25 +01:00
if ( spell )
2014-05-12 21:04:02 +02:00
{
2021-08-31 19:59:55 +02:00
addSpell ( spell ) ;
2015-01-21 21:24:25 +01:00
2021-08-27 20:07:50 +02:00
if ( id = = state . mSelectedSpell )
mSelectedSpell = id ;
2014-05-12 21:04:02 +02:00
}
}
2020-07-28 08:33:28 +02:00
// 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 ) ;
}
2014-05-12 21:04:02 +02:00
for ( std : : map < std : : string , ESM : : TimeStamp > : : const_iterator it = state . mUsedPowers . begin ( ) ; it ! = state . mUsedPowers . end ( ) ; + + it )
2015-11-27 01:02:29 +01:00
{
const ESM : : Spell * spell = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Spell > ( ) . search ( it - > first ) ;
if ( ! spell )
continue ;
2022-03-25 20:42:01 +02:00
mUsedPowers . emplace_back ( spell , MWWorld : : TimeStamp ( it - > second ) ) ;
2015-11-27 01:02:29 +01:00
}
2014-08-19 03:17:31 +02:00
2019-09-30 20:27:42 +04:00
// Permanent effects are used only to keep the custom magnitude of corprus spells effects (after cure too), and only in old saves. Convert data to the new approach.
for ( std : : map < std : : string , std : : vector < ESM : : SpellState : : PermanentSpellEffectInfo > > : : const_iterator it =
state . mPermanentSpellEffects . begin ( ) ; it ! = state . mPermanentSpellEffects . end ( ) ; + + it )
2014-08-19 03:17:31 +02:00
{
2019-09-30 20:27:42 +04:00
const ESM : : Spell * spell = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Spell > ( ) . search ( it - > first ) ;
if ( ! spell )
2015-11-27 01:02:29 +01:00
continue ;
2016-07-02 12:50:00 +02:00
2019-09-30 20:27:42 +04:00
// Import data only for player, other actors should not suffer from corprus worsening.
MWWorld : : Ptr player = getPlayer ( ) ;
if ( creatureStats - > getActorId ( ) ! = player . getClass ( ) . getCreatureStats ( player ) . getActorId ( ) )
return ;
// Note: if target actor has the Restore attirbute effects, stats will be restored.
for ( std : : vector < ESM : : SpellState : : PermanentSpellEffectInfo > : : const_iterator effectIt = it - > second . begin ( ) ; effectIt ! = it - > second . end ( ) ; + + effectIt )
{
// Applied corprus effects are already in loaded stats modifiers
if ( effectIt - > mId = = ESM : : MagicEffect : : FortifyAttribute )
{
AttributeValue attr = creatureStats - > getAttribute ( effectIt - > mArg ) ;
attr . setModifier ( attr . getModifier ( ) - effectIt - > mMagnitude ) ;
attr . damage ( - effectIt - > mMagnitude ) ;
creatureStats - > setAttribute ( effectIt - > mArg , attr ) ;
}
else if ( effectIt - > mId = = ESM : : MagicEffect : : DrainAttribute )
{
AttributeValue attr = creatureStats - > getAttribute ( effectIt - > mArg ) ;
attr . setModifier ( attr . getModifier ( ) + effectIt - > mMagnitude ) ;
attr . damage ( effectIt - > mMagnitude ) ;
creatureStats - > setAttribute ( effectIt - > mArg , attr ) ;
}
}
}
2014-05-12 21:04:02 +02:00
}
void Spells : : writeState ( ESM : : SpellState & state ) const
{
2020-07-28 08:33:28 +02:00
const auto & baseSpells = mSpellList - > getSpells ( ) ;
2021-08-27 20:07:50 +02:00
for ( const auto spell : mSpells )
2016-07-01 18:50:28 +02:00
{
2020-08-07 16:36:59 +02:00
// Don't save spells and powers stored in the base record
2021-08-27 20:07:50 +02:00
if ( ( spell - > mData . mType ! = ESM : : Spell : : ST_Spell & & spell - > mData . mType ! = ESM : : Spell : : ST_Power ) | |
std : : find ( baseSpells . begin ( ) , baseSpells . end ( ) , spell - > mId ) = = baseSpells . end ( ) )
2020-07-28 08:33:28 +02:00
{
2021-08-27 20:07:50 +02:00
state . mSpells . emplace_back ( spell - > mId ) ;
2020-07-28 08:33:28 +02:00
}
2016-07-01 18:50:28 +02:00
}
2015-11-27 01:02:29 +01:00
2014-05-12 21:04:02 +02:00
state . mSelectedSpell = mSelectedSpell ;
2020-07-28 08:33:28 +02:00
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 ) ;
2020-08-04 18:28:10 +02:00
addAllToInstance ( mSpellList - > getSpells ( ) ) ;
2020-07-28 08:33:28 +02:00
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 ) ;
2014-05-12 21:04:02 +02:00
}
2012-04-11 18:29:36 +02:00
}