2012-03-30 14:18:58 +00:00
# include "actors.hpp"
2012-03-30 15:01:55 +00:00
# include <typeinfo>
2015-07-29 18:15:06 +00:00
# include <iostream>
2012-03-30 15:01:55 +00:00
2015-07-07 17:16:32 +00:00
# include <components/esm/esmreader.hpp>
# include <components/esm/esmwriter.hpp>
2012-03-30 15:01:55 +00:00
# include <components/esm/loadnpc.hpp>
2016-06-17 14:07:16 +00:00
2015-11-20 20:57:04 +00:00
# include <components/sceneutil/positionattitudetransform.hpp>
2016-11-17 15:16:25 +00:00
# include "../mwmp/Main.hpp"
# include "../mwmp/DedicatedPlayer.hpp"
2016-12-16 08:59:15 +00:00
# include "../mwmp/LocalPlayer.hpp"
2016-01-12 03:41:44 +00:00
2012-03-30 15:01:55 +00:00
2012-10-01 15:17:04 +00:00
# include "../mwworld/esmstore.hpp"
2012-03-30 14:18:58 +00:00
# include "../mwworld/class.hpp"
2012-03-31 15:26:15 +00:00
# include "../mwworld/inventorystore.hpp"
2013-11-21 02:39:55 +00:00
# include "../mwworld/actionequip.hpp"
2014-01-15 20:56:55 +00:00
# include "../mwworld/player.hpp"
2012-03-30 14:18:58 +00:00
2012-09-21 15:53:16 +00:00
# include "../mwbase/world.hpp"
# include "../mwbase/environment.hpp"
# include "../mwbase/windowmanager.hpp"
2015-08-19 13:51:04 +00:00
# include "../mwbase/dialoguemanager.hpp"
2013-08-28 00:08:23 +00:00
# include "../mwbase/soundmanager.hpp"
2015-05-31 16:04:14 +00:00
# include "../mwbase/mechanicsmanager.hpp"
2016-06-11 22:04:50 +00:00
# include "../mwbase/statemanager.hpp"
2012-09-21 15:53:16 +00:00
2016-06-17 14:07:16 +00:00
# include "spellcasting.hpp"
2013-08-07 13:34:11 +00:00
# include "npcstats.hpp"
2012-05-17 11:15:31 +00:00
# include "creaturestats.hpp"
2013-03-31 07:13:56 +00:00
# include "movement.hpp"
2014-10-08 08:58:52 +00:00
# include "character.hpp"
2013-11-18 11:33:09 +00:00
# include "aicombat.hpp"
2016-12-25 14:31:44 +00:00
# include "aicombataction.hpp"
2014-01-12 13:02:15 +00:00
# include "aifollow.hpp"
2014-05-03 10:23:22 +00:00
# include "aipursue.hpp"
2014-12-21 15:45:30 +00:00
# include "actor.hpp"
2015-01-05 17:52:37 +00:00
# include "summoning.hpp"
2015-01-11 01:25:46 +00:00
# include "combat.hpp"
2015-08-21 09:12:39 +00:00
# include "actorutil.hpp"
2014-12-21 15:45:30 +00:00
2013-11-21 02:39:55 +00:00
namespace
{
2015-01-13 17:09:10 +00:00
bool isConscious ( const MWWorld : : Ptr & ptr )
{
const MWMechanics : : CreatureStats & stats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
return ! stats . isDead ( ) & & ! stats . getKnockedDown ( ) ;
}
2013-11-21 03:11:06 +00:00
void adjustBoundItem ( const std : : string & item , bool bound , const MWWorld : : Ptr & actor )
2013-11-21 02:39:55 +00:00
{
if ( bound )
{
2014-05-14 04:37:31 +00:00
if ( actor . getClass ( ) . getContainerStore ( actor ) . count ( item ) = = 0 )
{
2015-03-11 20:12:08 +00:00
MWWorld : : InventoryStore & store = actor . getClass ( ) . getInventoryStore ( actor ) ;
MWWorld : : Ptr newPtr = * store . MWWorld : : ContainerStore : : add ( item , 1 , actor ) ;
2014-05-14 04:37:31 +00:00
MWWorld : : ActionEquip action ( newPtr ) ;
action . execute ( actor ) ;
2015-03-11 20:12:08 +00:00
MWWorld : : ContainerStoreIterator rightHand = store . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedRight ) ;
// change draw state only if the item is in player's right hand
2015-08-21 09:12:39 +00:00
if ( actor = = MWMechanics : : getPlayer ( )
2015-03-11 20:12:08 +00:00
& & rightHand ! = store . end ( ) & & newPtr = = * rightHand )
{
MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayer ( ) . setDrawState ( MWMechanics : : DrawState_Weapon ) ;
}
2014-05-14 04:37:31 +00:00
}
2013-11-21 02:39:55 +00:00
}
else
{
2013-11-21 03:11:06 +00:00
actor . getClass ( ) . getContainerStore ( actor ) . remove ( item , 1 , actor ) ;
2013-11-21 02:39:55 +00:00
}
}
2014-08-06 19:16:14 +00:00
class CheckActorCommanded : public MWMechanics : : EffectSourceVisitor
{
MWWorld : : Ptr mActor ;
public :
bool mCommanded ;
CheckActorCommanded ( MWWorld : : Ptr actor )
: mActor ( actor )
, mCommanded ( false ) { }
virtual void visit ( MWMechanics : : EffectKey key ,
2015-01-05 17:52:37 +00:00
const std : : string & sourceName , const std : : string & sourceId , int casterActorId ,
2014-12-20 19:56:44 +00:00
float magnitude , float remainingTime = - 1 , float totalTime = - 1 )
2014-08-06 19:16:14 +00:00
{
2017-01-11 18:11:45 +00:00
if ( ( ( key . mId = = ESM : : MagicEffect : : CommandHumanoid & & mActor . getClass ( ) . isNpc ( ) )
| | ( key . mId = = ESM : : MagicEffect : : CommandCreature & & mActor . getTypeName ( ) = = typeid ( ESM : : Creature ) . name ( ) ) )
2014-08-06 19:16:14 +00:00
& & magnitude > = mActor . getClass ( ) . getCreatureStats ( mActor ) . getLevel ( ) )
mCommanded = true ;
}
} ;
2017-01-11 17:23:03 +00:00
// Check for command effects having ended and remove package if necessary
2014-08-06 19:16:14 +00:00
void adjustCommandedActor ( const MWWorld : : Ptr & actor )
{
CheckActorCommanded check ( actor ) ;
MWMechanics : : CreatureStats & stats = actor . getClass ( ) . getCreatureStats ( actor ) ;
stats . getActiveSpells ( ) . visitEffectSources ( check ) ;
bool hasCommandPackage = false ;
std : : list < MWMechanics : : AiPackage * > : : const_iterator it ;
for ( it = stats . getAiSequence ( ) . begin ( ) ; it ! = stats . getAiSequence ( ) . end ( ) ; + + it )
{
if ( ( * it ) - > getTypeId ( ) = = MWMechanics : : AiPackage : : TypeIdFollow & &
2015-01-12 18:57:54 +00:00
static_cast < MWMechanics : : AiFollow * > ( * it ) - > isCommanded ( ) )
2014-08-06 19:16:14 +00:00
{
hasCommandPackage = true ;
break ;
}
}
2017-01-11 17:23:03 +00:00
if ( ! check . mCommanded & & hasCommandPackage )
2014-08-06 19:16:14 +00:00
{
stats . getAiSequence ( ) . erase ( it ) ;
}
}
2014-01-14 01:52:34 +00:00
void getRestorationPerHourOfSleep ( const MWWorld : : Ptr & ptr , float & health , float & magicka )
{
MWMechanics : : CreatureStats & stats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
const MWWorld : : Store < ESM : : GameSetting > & settings = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) ;
2014-08-16 20:38:22 +00:00
bool stunted = stats . getMagicEffects ( ) . get ( ESM : : MagicEffect : : StuntedMagicka ) . getMagnitude ( ) > 0 ;
2014-01-14 01:52:34 +00:00
int endurance = stats . getAttribute ( ESM : : Attribute : : Endurance ) . getModified ( ) ;
2015-03-08 04:42:07 +00:00
health = 0.1f * endurance ;
2014-01-14 01:52:34 +00:00
magicka = 0 ;
if ( ! stunted )
{
float fRestMagicMult = settings . find ( " fRestMagicMult " ) - > getFloat ( ) ;
magicka = fRestMagicMult * stats . getAttribute ( ESM : : Attribute : : Intelligence ) . getModified ( ) ;
}
}
2014-01-02 00:03:44 +00:00
2013-11-21 02:39:55 +00:00
}
2012-03-30 14:18:58 +00:00
namespace MWMechanics
{
2014-01-02 20:21:28 +00:00
2016-06-06 23:53:16 +00:00
const float aiProcessingDistance = 7168 ;
const float sqrAiProcessingDistance = aiProcessingDistance * aiProcessingDistance ;
2014-01-02 20:21:28 +00:00
class SoulTrap : public MWMechanics : : EffectSourceVisitor
{
MWWorld : : Ptr mCreature ;
MWWorld : : Ptr mActor ;
2015-12-26 17:47:03 +00:00
bool mTrapped ;
2014-01-02 20:21:28 +00:00
public :
SoulTrap ( MWWorld : : Ptr trappedCreature )
2015-12-26 17:47:03 +00:00
: mCreature ( trappedCreature )
, mTrapped ( false )
{
}
2014-01-02 20:21:28 +00:00
virtual void visit ( MWMechanics : : EffectKey key ,
2015-01-05 17:52:37 +00:00
const std : : string & sourceName , const std : : string & sourceId , int casterActorId ,
2014-12-20 19:56:44 +00:00
float magnitude , float remainingTime = - 1 , float totalTime = - 1 )
2014-01-02 20:21:28 +00:00
{
2015-12-26 17:47:03 +00:00
if ( mTrapped )
return ;
2014-01-02 20:21:28 +00:00
if ( key . mId ! = ESM : : MagicEffect : : Soultrap )
return ;
if ( magnitude < = 0 )
return ;
MWBase : : World * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
2014-05-14 05:14:08 +00:00
MWWorld : : Ptr caster = world - > searchPtrViaActorId ( casterActorId ) ;
2014-01-02 20:21:28 +00:00
if ( caster . isEmpty ( ) | | ! caster . getClass ( ) . isActor ( ) )
return ;
static const float fSoulgemMult = world - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fSoulgemMult " ) - > getFloat ( ) ;
2015-03-08 04:42:07 +00:00
int creatureSoulValue = mCreature . get < ESM : : Creature > ( ) - > mBase - > mData . mSoul ;
2014-01-17 17:43:45 +00:00
if ( creatureSoulValue = = 0 )
return ;
2014-01-02 20:21:28 +00:00
// Use the smallest soulgem that is large enough to hold the soul
MWWorld : : ContainerStore & container = caster . getClass ( ) . getContainerStore ( caster ) ;
MWWorld : : ContainerStoreIterator gem = container . end ( ) ;
2014-10-12 21:26:03 +00:00
float gemCapacity = std : : numeric_limits < float > : : max ( ) ;
2014-01-02 20:21:28 +00:00
std : : string soulgemFilter = " misc_soulgem " ; // no other way to check for soulgems? :/
for ( MWWorld : : ContainerStoreIterator it = container . begin ( MWWorld : : ContainerStore : : Type_Miscellaneous ) ;
it ! = container . end ( ) ; + + it )
{
2014-05-25 12:13:07 +00:00
const std : : string & id = it - > getCellRef ( ) . getRefId ( ) ;
2014-01-02 20:21:28 +00:00
if ( id . size ( ) > = soulgemFilter . size ( )
& & id . substr ( 0 , soulgemFilter . size ( ) ) = = soulgemFilter )
{
float thisGemCapacity = it - > get < ESM : : Miscellaneous > ( ) - > mBase - > mData . mValue * fSoulgemMult ;
if ( thisGemCapacity > = creatureSoulValue & & thisGemCapacity < gemCapacity
2014-05-25 12:13:07 +00:00
& & it - > getCellRef ( ) . getSoul ( ) . empty ( ) )
2014-01-02 20:21:28 +00:00
{
gem = it ;
gemCapacity = thisGemCapacity ;
}
}
}
if ( gem = = container . end ( ) )
return ;
// Set the soul on just one of the gems, not the whole stack
gem - > getContainerStore ( ) - > unstack ( * gem , caster ) ;
2014-05-25 12:13:07 +00:00
gem - > getCellRef ( ) . setSoul ( mCreature . getCellRef ( ) . getRefId ( ) ) ;
2014-01-02 20:21:28 +00:00
2015-12-26 17:47:03 +00:00
mTrapped = true ;
2015-08-21 09:12:39 +00:00
if ( caster = = getPlayer ( ) )
2014-01-02 20:21:28 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > messageBox ( " #{sSoultrapSuccess} " ) ;
2014-08-28 00:55:22 +00:00
const ESM : : Static * fx = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Static > ( )
. search ( " VFX_Soul_Trap " ) ;
if ( fx )
MWBase : : Environment : : get ( ) . getWorld ( ) - > spawnEffect ( " meshes \\ " + fx - > mModel ,
2015-04-24 23:20:07 +00:00
" " , mCreature . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
2014-08-28 14:30:42 +00:00
2015-11-25 07:24:42 +00:00
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > playSound3D (
mCreature . getRefData ( ) . getPosition ( ) . asVec3 ( ) , " conjuration hit " , 1.f , 1.f
) ;
2014-01-02 20:21:28 +00:00
}
} ;
2012-03-30 15:01:55 +00:00
void Actors : : updateActor ( const MWWorld : : Ptr & ptr , float duration )
{
2012-05-17 11:21:49 +00:00
// magic effects
adjustMagicEffects ( ptr ) ;
2013-12-28 16:19:35 +00:00
if ( ptr . getClass ( ) . getCreatureStats ( ptr ) . needToRecalcDynamicStats ( ) )
calculateDynamicStats ( ptr ) ;
2014-04-25 02:47:45 +00:00
2013-11-16 02:16:21 +00:00
calculateCreatureStatModifiers ( ptr , duration ) ;
2014-05-15 20:03:48 +00:00
// fatigue restoration
2014-07-23 22:25:02 +00:00
calculateRestoration ( ptr , duration ) ;
2014-05-15 20:03:48 +00:00
}
2014-12-16 19:47:45 +00:00
void Actors : : updateHeadTracking ( const MWWorld : : Ptr & actor , const MWWorld : : Ptr & targetActor ,
MWWorld : : Ptr & headTrackTarget , float & sqrHeadTrackDistance )
{
static const float fMaxHeadTrackDistance = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( )
. find ( " fMaxHeadTrackDistance " ) - > getFloat ( ) ;
static const float fInteriorHeadTrackMult = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( )
. find ( " fInteriorHeadTrackMult " ) - > getFloat ( ) ;
float maxDistance = fMaxHeadTrackDistance ;
const ESM : : Cell * currentCell = actor . getCell ( ) - > getCell ( ) ;
if ( ! currentCell - > isExterior ( ) & & ! ( currentCell - > mData . mFlags & ESM : : Cell : : QuasiEx ) )
maxDistance * = fInteriorHeadTrackMult ;
const ESM : : Position & actor1Pos = actor . getRefData ( ) . getPosition ( ) ;
const ESM : : Position & actor2Pos = targetActor . getRefData ( ) . getPosition ( ) ;
2015-05-31 16:04:14 +00:00
float sqrDist = ( actor1Pos . asVec3 ( ) - actor2Pos . asVec3 ( ) ) . length2 ( ) ;
2014-12-16 19:47:45 +00:00
if ( sqrDist > maxDistance * maxDistance )
return ;
2014-12-18 16:36:38 +00:00
if ( targetActor . getClass ( ) . getCreatureStats ( targetActor ) . isDead ( ) )
return ;
2014-12-16 19:47:45 +00:00
2015-05-31 16:04:14 +00:00
if ( ! actor . getRefData ( ) . getBaseNode ( ) )
return ;
2014-12-16 19:47:45 +00:00
// stop tracking when target is behind the actor
2015-05-31 16:04:14 +00:00
osg : : Vec3f actorDirection = actor . getRefData ( ) . getBaseNode ( ) - > getAttitude ( ) * osg : : Vec3f ( 0 , 1 , 0 ) ;
osg : : Vec3f targetDirection ( actor2Pos . asVec3 ( ) - actor1Pos . asVec3 ( ) ) ;
actorDirection . z ( ) = 0 ;
targetDirection . z ( ) = 0 ;
actorDirection . normalize ( ) ;
targetDirection . normalize ( ) ;
if ( std : : acos ( actorDirection * targetDirection ) < osg : : DegreesToRadians ( 90.f )
2014-12-16 19:47:45 +00:00
& & sqrDist < = sqrHeadTrackDistance
& & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor , targetActor ) // check LOS and awareness last as it's the most expensive function
& & MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > awarenessCheck ( targetActor , actor ) )
{
sqrHeadTrackDistance = sqrDist ;
headTrackTarget = targetActor ;
}
}
2014-05-15 20:03:48 +00:00
void Actors : : engageCombat ( const MWWorld : : Ptr & actor1 , const MWWorld : : Ptr & actor2 , bool againstPlayer )
{
2016-12-25 17:03:17 +00:00
const CreatureStats & creatureStats1 = actor1 . getClass ( ) . getCreatureStats ( actor1 ) ;
if ( creatureStats1 . getAiSequence ( ) . isInCombat ( actor2 ) )
return ;
2014-09-14 20:29:06 +00:00
2016-12-25 17:03:17 +00:00
const CreatureStats & creatureStats2 = actor2 . getClass ( ) . getCreatureStats ( actor2 ) ;
if ( creatureStats1 . isDead ( ) | | creatureStats2 . isDead ( ) )
2014-07-27 18:30:52 +00:00
return ;
2014-08-30 16:37:51 +00:00
const ESM : : Position & actor1Pos = actor1 . getRefData ( ) . getPosition ( ) ;
2014-07-27 18:30:52 +00:00
const ESM : : Position & actor2Pos = actor2 . getRefData ( ) . getPosition ( ) ;
2015-06-03 17:41:19 +00:00
float sqrDist = ( actor1Pos . asVec3 ( ) - actor2Pos . asVec3 ( ) ) . length2 ( ) ;
2016-06-06 23:53:16 +00:00
if ( sqrDist > sqrAiProcessingDistance )
2014-07-27 18:30:52 +00:00
return ;
2012-09-21 15:53:16 +00:00
2016-12-25 17:03:17 +00:00
// No combat for totally static creatures
2015-07-30 23:26:26 +00:00
if ( ! actor1 . getClass ( ) . isMobile ( actor1 ) )
return ;
2016-12-25 17:03:17 +00:00
// Start combat if target actor is in combat with one of our followers or escorters
const std : : list < MWWorld : : Ptr > & followersAndEscorters = getActorsSidingWith ( actor1 ) ;
for ( std : : list < MWWorld : : Ptr > : : const_iterator it = followersAndEscorters . begin ( ) ; it ! = followersAndEscorters . end ( ) ; + + it )
2014-07-27 18:30:52 +00:00
{
2016-12-25 17:03:17 +00:00
// Need to check both ways since player doesn't use AI packages
2014-09-26 20:08:07 +00:00
if ( ( creatureStats2 . getAiSequence ( ) . isInCombat ( * it )
2014-08-13 23:08:09 +00:00
| | it - > getClass ( ) . getCreatureStats ( * it ) . getAiSequence ( ) . isInCombat ( actor2 ) )
2016-12-25 17:03:17 +00:00
& & ! creatureStats1 . getAiSequence ( ) . isInCombat ( * it ) )
2016-12-25 20:11:06 +00:00
{
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( actor1 , actor2 ) ;
return ;
}
2014-07-27 18:30:52 +00:00
}
2014-08-13 23:08:09 +00:00
2016-12-25 17:03:17 +00:00
// Start combat if target actor is in combat with someone we are following through a follow package
for ( std : : list < MWMechanics : : AiPackage * > : : const_iterator it = creatureStats1 . getAiSequence ( ) . begin ( ) ; it ! = creatureStats1 . getAiSequence ( ) . end ( ) ; + + it )
2014-07-27 18:30:52 +00:00
{
2015-12-06 22:32:49 +00:00
if ( ! ( * it ) - > sideWithTarget ( ) )
continue ;
2014-08-13 23:08:09 +00:00
2015-12-06 22:32:49 +00:00
MWWorld : : Ptr followTarget = ( * it ) - > getTarget ( ) ;
2014-09-26 20:08:07 +00:00
2015-12-06 22:32:49 +00:00
if ( followTarget . isEmpty ( ) )
continue ;
2016-12-25 17:03:17 +00:00
if ( creatureStats1 . getAiSequence ( ) . isInCombat ( followTarget ) )
2015-12-06 22:32:49 +00:00
continue ;
2016-12-25 17:03:17 +00:00
// Need to check both ways since player doesn't use AI packages
2015-12-06 22:32:49 +00:00
if ( creatureStats2 . getAiSequence ( ) . isInCombat ( followTarget )
| | followTarget . getClass ( ) . getCreatureStats ( followTarget ) . getAiSequence ( ) . isInCombat ( actor2 ) )
2016-12-25 20:11:06 +00:00
{
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( actor1 , actor2 ) ;
return ;
}
2014-07-27 18:30:52 +00:00
}
2016-12-25 20:11:06 +00:00
// Start combat with the player if we are already in combat with a player follower or escorter
2016-12-25 19:50:22 +00:00
const std : : list < MWWorld : : Ptr > & playerFollowersAndEscorters = getActorsSidingWith ( getPlayer ( ) ) ;
2016-12-25 20:11:06 +00:00
if ( againstPlayer )
2016-12-25 19:50:22 +00:00
{
for ( std : : list < MWWorld : : Ptr > : : const_iterator it = playerFollowersAndEscorters . begin ( ) ; it ! = playerFollowersAndEscorters . end ( ) ; + + it )
{
if ( creatureStats1 . getAiSequence ( ) . isInCombat ( * it ) )
{
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( actor1 , actor2 ) ;
return ;
}
}
}
2016-12-25 17:03:17 +00:00
// Otherwise, don't initiate combat with an unreachable target
2016-12-25 20:11:06 +00:00
if ( ! MWMechanics : : canFight ( actor1 , actor2 ) )
2016-12-25 14:31:44 +00:00
return ;
2016-12-25 20:11:06 +00:00
bool aggressive = false ;
if ( againstPlayer | | std : : find ( playerFollowersAndEscorters . begin ( ) , playerFollowersAndEscorters . end ( ) , actor2 ) ! = playerFollowersAndEscorters . end ( ) )
2016-12-25 14:31:44 +00:00
{
2016-12-25 17:03:17 +00:00
// Player followers and escorters with high fight should not initiate combat here with the player or with
2016-12-25 14:31:44 +00:00
// other player followers or escorters
if ( std : : find ( playerFollowersAndEscorters . begin ( ) , playerFollowersAndEscorters . end ( ) , actor1 ) ! = playerFollowersAndEscorters . end ( ) )
return ;
aggressive = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > isAggressive ( actor1 , actor2 ) ;
}
else
{
// Make guards fight aggressive creatures
if ( ! actor1 . getClass ( ) . isNpc ( ) & & actor2 . getClass ( ) . isClass ( actor2 , " Guard " ) )
{
2016-12-25 17:03:17 +00:00
if ( creatureStats1 . getAiSequence ( ) . isInCombat ( ) & & MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > isAggressive ( actor1 , actor2 ) )
2016-12-25 14:31:44 +00:00
aggressive = true ;
}
2014-07-27 18:30:52 +00:00
}
2016-12-25 17:03:17 +00:00
if ( aggressive )
2014-05-15 20:03:48 +00:00
{
2014-07-27 18:30:52 +00:00
bool LOS = MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor1 , actor2 ) ;
2014-03-30 15:01:59 +00:00
2016-12-25 11:14:21 +00:00
if ( againstPlayer | | std : : find ( playerFollowersAndEscorters . begin ( ) , playerFollowersAndEscorters . end ( ) , actor2 ) ! = playerFollowersAndEscorters . end ( ) )
LOS & = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > awarenessCheck ( actor2 , actor1 ) ;
2014-05-15 20:03:48 +00:00
2014-07-27 18:30:52 +00:00
if ( LOS )
{
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( actor1 , actor2 ) ;
if ( ! againstPlayer ) // start combat between each other
2014-05-15 20:03:48 +00:00
{
2014-07-27 18:30:52 +00:00
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( actor2 , actor1 ) ;
2013-11-18 11:33:09 +00:00
}
}
2013-05-27 02:54:59 +00:00
}
2012-03-30 15:01:55 +00:00
}
2014-05-15 20:03:48 +00:00
void Actors : : updateNpc ( const MWWorld : : Ptr & ptr , float duration )
2012-03-30 15:01:55 +00:00
{
2014-05-15 20:03:48 +00:00
updateDrowning ( ptr , duration ) ;
2014-11-02 17:01:12 +00:00
calculateNpcStatModifiers ( ptr , duration ) ;
2012-03-30 15:01:55 +00:00
}
2012-05-17 11:21:49 +00:00
void Actors : : adjustMagicEffects ( const MWWorld : : Ptr & creature )
{
2014-05-22 18:37:22 +00:00
CreatureStats & creatureStats = creature . getClass ( ) . getCreatureStats ( creature ) ;
2014-08-10 21:52:32 +00:00
if ( creatureStats . isDead ( ) )
return ;
2014-08-17 01:57:26 +00:00
2012-07-22 14:29:54 +00:00
MagicEffects now = creatureStats . getSpells ( ) . getMagicEffects ( ) ;
2012-05-17 11:21:49 +00:00
2012-05-18 13:48:55 +00:00
if ( creature . getTypeName ( ) = = typeid ( ESM : : NPC ) . name ( ) )
{
2014-05-22 18:37:22 +00:00
MWWorld : : InventoryStore & store = creature . getClass ( ) . getInventoryStore ( creature ) ;
2012-05-18 13:48:55 +00:00
now + = store . getMagicEffects ( ) ;
}
2012-07-22 14:29:54 +00:00
now + = creatureStats . getActiveSpells ( ) . getMagicEffects ( ) ;
2012-05-17 11:21:49 +00:00
2014-08-17 01:57:26 +00:00
creatureStats . modifyMagicEffects ( now ) ;
2012-05-17 11:21:49 +00:00
}
2012-07-17 10:18:43 +00:00
void Actors : : calculateDynamicStats ( const MWWorld : : Ptr & ptr )
{
2014-05-22 18:37:22 +00:00
CreatureStats & creatureStats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
2012-07-17 10:18:43 +00:00
2014-08-06 00:19:32 +00:00
int intelligence = creatureStats . getAttribute ( ESM : : Attribute : : Intelligence ) . getModified ( ) ;
2012-07-17 10:18:43 +00:00
2014-09-18 12:56:43 +00:00
float base = 1.f ;
2015-08-21 09:12:39 +00:00
if ( ptr = = getPlayer ( ) )
2014-09-18 12:56:43 +00:00
base = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fPCbaseMagickaMult " ) - > getFloat ( ) ;
else
base = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fNPCbaseMagickaMult " ) - > getFloat ( ) ;
double magickaFactor = base +
creatureStats . getMagicEffects ( ) . get ( EffectKey ( ESM : : MagicEffect : : FortifyMaximumMagicka ) ) . getMagnitude ( ) * 0.1 ;
2012-07-17 10:18:43 +00:00
2012-10-19 11:10:06 +00:00
DynamicStat < float > magicka = creatureStats . getMagicka ( ) ;
2014-08-11 17:07:14 +00:00
float diff = ( static_cast < int > ( magickaFactor * intelligence ) ) - magicka . getBase ( ) ;
2016-06-24 15:38:59 +00:00
float currentToBaseRatio = ( magicka . getCurrent ( ) / magicka . getBase ( ) ) ;
magicka . setModified ( magicka . getModified ( ) + diff , 0 ) ;
magicka . setCurrent ( magicka . getBase ( ) * currentToBaseRatio ) ;
2013-08-28 05:44:52 +00:00
creatureStats . setMagicka ( magicka ) ;
2012-07-17 10:18:43 +00:00
}
2014-07-23 22:25:02 +00:00
void Actors : : restoreDynamicStats ( const MWWorld : : Ptr & ptr , bool sleep )
2012-09-21 15:53:16 +00:00
{
2013-11-29 19:21:57 +00:00
if ( ptr . getClass ( ) . getCreatureStats ( ptr ) . isDead ( ) )
return ;
2013-10-12 22:32:28 +00:00
2014-01-14 01:52:34 +00:00
MWMechanics : : CreatureStats & stats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
const MWWorld : : Store < ESM : : GameSetting > & settings = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) ;
2012-09-21 15:53:16 +00:00
2014-01-14 01:20:13 +00:00
if ( sleep )
2012-09-21 15:53:16 +00:00
{
2014-01-14 01:52:34 +00:00
float health , magicka ;
getRestorationPerHourOfSleep ( ptr , health , magicka ) ;
2012-09-21 15:53:16 +00:00
2014-01-14 01:52:34 +00:00
DynamicStat < float > stat = stats . getHealth ( ) ;
stat . setCurrent ( stat . getCurrent ( ) + health ) ;
stats . setHealth ( stat ) ;
2013-03-18 09:54:47 +00:00
2014-01-14 01:52:34 +00:00
stat = stats . getMagicka ( ) ;
stat . setCurrent ( stat . getCurrent ( ) + magicka ) ;
stats . setMagicka ( stat ) ;
2012-09-21 15:53:16 +00:00
}
2014-01-14 01:52:34 +00:00
int endurance = stats . getAttribute ( ESM : : Attribute : : Endurance ) . getModified ( ) ;
2013-03-18 09:54:47 +00:00
2014-10-05 13:50:01 +00:00
float normalizedEncumbrance = ptr . getClass ( ) . getNormalizedEncumbrance ( ptr ) ;
2014-01-14 01:52:34 +00:00
if ( normalizedEncumbrance > 1 )
normalizedEncumbrance = 1 ;
2013-10-12 22:32:28 +00:00
// restore fatigue
float fFatigueReturnBase = settings . find ( " fFatigueReturnBase " ) - > getFloat ( ) ;
float fFatigueReturnMult = settings . find ( " fFatigueReturnMult " ) - > getFloat ( ) ;
float fEndFatigueMult = settings . find ( " fEndFatigueMult " ) - > getFloat ( ) ;
float x = fFatigueReturnBase + fFatigueReturnMult * ( 1 - normalizedEncumbrance ) ;
x * = fEndFatigueMult * endurance ;
DynamicStat < float > fatigue = stats . getFatigue ( ) ;
2014-07-23 22:25:02 +00:00
fatigue . setCurrent ( fatigue . getCurrent ( ) + 3600 * x ) ;
2013-10-12 22:32:28 +00:00
stats . setFatigue ( fatigue ) ;
2014-07-23 22:25:02 +00:00
}
void Actors : : calculateRestoration ( const MWWorld : : Ptr & ptr , float duration )
{
if ( ptr . getClass ( ) . getCreatureStats ( ptr ) . isDead ( ) )
return ;
2014-01-14 01:52:34 +00:00
2014-07-23 22:25:02 +00:00
MWMechanics : : CreatureStats & stats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
int endurance = stats . getAttribute ( ESM : : Attribute : : Endurance ) . getModified ( ) ;
// restore fatigue
2014-09-27 13:19:15 +00:00
const MWWorld : : Store < ESM : : GameSetting > & settings = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) ;
2014-09-29 10:04:19 +00:00
static const float fFatigueReturnBase = settings . find ( " fFatigueReturnBase " ) - > getFloat ( ) ;
static const float fFatigueReturnMult = settings . find ( " fFatigueReturnMult " ) - > getFloat ( ) ;
2014-07-23 22:25:02 +00:00
float x = fFatigueReturnBase + fFatigueReturnMult * endurance ;
DynamicStat < float > fatigue = stats . getFatigue ( ) ;
fatigue . setCurrent ( fatigue . getCurrent ( ) + duration * x ) ;
stats . setFatigue ( fatigue ) ;
2012-09-21 15:53:16 +00:00
}
2016-07-05 22:20:23 +00:00
class ExpiryVisitor : public EffectSourceVisitor
{
private :
MWWorld : : Ptr mActor ;
float mDuration ;
public :
ExpiryVisitor ( const MWWorld : : Ptr & actor , float duration )
: mActor ( actor ) , mDuration ( duration )
{
}
virtual void visit ( MWMechanics : : EffectKey key ,
const std : : string & /*sourceName*/ , const std : : string & /*sourceId*/ , int /*casterActorId*/ ,
float magnitude , float remainingTime = - 1 , float /*totalTime*/ = - 1 )
{
if ( magnitude > 0 & & remainingTime > 0 & & remainingTime < mDuration )
{
CreatureStats & creatureStats = mActor . getClass ( ) . getCreatureStats ( mActor ) ;
2016-12-05 11:12:13 +00:00
if ( effectTick ( creatureStats , mActor , key , magnitude * remainingTime ) )
creatureStats . getMagicEffects ( ) . add ( key , - magnitude ) ;
2016-07-05 22:20:23 +00:00
}
}
} ;
2013-11-16 02:16:21 +00:00
void Actors : : calculateCreatureStatModifiers ( const MWWorld : : Ptr & ptr , float duration )
2012-07-17 13:49:37 +00:00
{
2014-05-22 18:37:22 +00:00
CreatureStats & creatureStats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
2013-08-28 04:40:31 +00:00
const MagicEffects & effects = creatureStats . getMagicEffects ( ) ;
2012-07-17 13:49:37 +00:00
2014-06-17 01:54:41 +00:00
bool wasDead = creatureStats . isDead ( ) ;
2015-12-15 19:41:00 +00:00
if ( duration > 0 )
2015-07-18 18:39:45 +00:00
{
2016-12-05 11:12:13 +00:00
// Apply correct magnitude for tickable effects that have just expired,
// in case duration > remaining time of effect.
// One case where this will happen is when the player uses the rest/wait command
// while there is a tickable effect active that should expire before the end of the rest/wait.
2016-07-05 22:20:23 +00:00
ExpiryVisitor visitor ( ptr , duration ) ;
creatureStats . getActiveSpells ( ) . visitEffectSources ( visitor ) ;
2015-12-15 19:41:00 +00:00
for ( MagicEffects : : Collection : : const_iterator it = effects . begin ( ) ; it ! = effects . end ( ) ; + + it )
{
// tickable effects (i.e. effects having a lasting impact after expiry)
effectTick ( creatureStats , ptr , it - > first , it - > second . getMagnitude ( ) * duration ) ;
2015-12-14 02:27:49 +00:00
2015-12-15 19:41:00 +00:00
// instant effects are already applied on spell impact in spellcasting.cpp, but may also come from permanent abilities
2015-12-15 19:46:05 +00:00
if ( it - > second . getMagnitude ( ) > 0 )
{
CastSpell cast ( ptr , ptr ) ;
2015-12-16 18:33:39 +00:00
if ( cast . applyInstantEffect ( ptr , ptr , it - > first , it - > second . getMagnitude ( ) ) )
{
2016-07-01 16:50:28 +00:00
creatureStats . getSpells ( ) . purgeEffect ( it - > first . mId ) ;
2015-12-16 18:33:39 +00:00
creatureStats . getActiveSpells ( ) . purgeEffect ( it - > first . mId ) ;
if ( ptr . getClass ( ) . hasInventoryStore ( ptr ) )
ptr . getClass ( ) . getInventoryStore ( ptr ) . purgeEffect ( it - > first . mId ) ;
}
2015-12-15 19:46:05 +00:00
}
2015-12-15 19:41:00 +00:00
}
2015-07-18 18:39:45 +00:00
}
2015-01-01 01:46:18 +00:00
2012-07-17 13:49:37 +00:00
// attributes
2013-08-28 04:40:31 +00:00
for ( int i = 0 ; i < ESM : : Attribute : : Length ; + + i )
2012-07-17 13:49:37 +00:00
{
2014-01-03 00:59:15 +00:00
AttributeValue stat = creatureStats . getAttribute ( i ) ;
2015-03-14 19:49:03 +00:00
stat . setModifier ( static_cast < int > ( effects . get ( EffectKey ( ESM : : MagicEffect : : FortifyAttribute , i ) ) . getMagnitude ( ) -
2015-03-07 15:23:02 +00:00
effects . get ( EffectKey ( ESM : : MagicEffect : : DrainAttribute , i ) ) . getMagnitude ( ) -
2015-03-14 19:49:03 +00:00
effects . get ( EffectKey ( ESM : : MagicEffect : : AbsorbAttribute , i ) ) . getMagnitude ( ) ) ) ;
2012-07-17 13:49:37 +00:00
2013-08-28 04:40:31 +00:00
creatureStats . setAttribute ( i , stat ) ;
2012-07-17 13:49:37 +00:00
}
2016-06-29 17:20:27 +00:00
if ( creatureStats . needToRecalcDynamicStats ( ) )
calculateDynamicStats ( ptr ) ;
2014-08-18 13:33:12 +00:00
{
Spells & spells = creatureStats . getSpells ( ) ;
for ( Spells : : TIterator it = spells . begin ( ) ; it ! = spells . end ( ) ; + + it )
{
2014-08-20 10:40:38 +00:00
if ( spells . getCorprusSpells ( ) . find ( it - > first ) ! = spells . getCorprusSpells ( ) . end ( ) )
2014-08-18 13:33:12 +00:00
{
2014-08-20 10:40:38 +00:00
if ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeStamp ( ) > = spells . getCorprusSpells ( ) . at ( it - > first ) . mNextWorsening )
2014-08-18 13:33:12 +00:00
{
spells . worsenCorprus ( it - > first ) ;
2015-08-21 09:12:39 +00:00
if ( ptr = = getPlayer ( ) )
2014-08-18 13:33:12 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > messageBox ( " #{sMagicCorprusWorsens} " ) ;
}
}
}
}
2012-07-17 13:49:37 +00:00
// dynamic stats
2013-08-28 04:40:31 +00:00
for ( int i = 0 ; i < 3 ; + + i )
2012-10-19 11:10:06 +00:00
{
2013-08-28 04:40:31 +00:00
DynamicStat < float > stat = creatureStats . getDynamic ( i ) ;
2014-08-16 20:38:22 +00:00
stat . setModifier ( effects . get ( ESM : : MagicEffect : : FortifyHealth + i ) . getMagnitude ( ) -
2015-03-07 15:30:41 +00:00
effects . get ( ESM : : MagicEffect : : DrainHealth + i ) . getMagnitude ( ) ,
2016-06-24 15:38:59 +00:00
// Magicka can be decreased below zero due to a fortify effect wearing off
2015-03-07 15:30:41 +00:00
// Fatigue can be decreased below zero meaning the actor will be knocked out
2016-06-24 15:38:59 +00:00
i = = 1 | | i = = 2 ) ;
2013-03-18 09:54:47 +00:00
2013-08-28 04:40:31 +00:00
creatureStats . setDynamic ( i , stat ) ;
2013-03-18 09:54:47 +00:00
}
2013-11-10 14:28:26 +00:00
2014-01-05 00:34:35 +00:00
// AI setting modifiers
int creature = ! ptr . getClass ( ) . isNpc ( ) ;
if ( creature & & ptr . get < ESM : : Creature > ( ) - > mBase - > mData . mType = = ESM : : Creature : : Humanoid )
creature = false ;
// Note: the Creature variants only work on normal creatures, not on daedra or undead creatures.
if ( ! creature | | ptr . get < ESM : : Creature > ( ) - > mBase - > mData . mType = = ESM : : Creature : : Creatures )
{
Stat < int > stat = creatureStats . getAiSetting ( CreatureStats : : AI_Fight ) ;
2015-08-20 06:17:02 +00:00
stat . setModifier ( static_cast < int > ( effects . get ( ESM : : MagicEffect : : FrenzyHumanoid + creature ) . getMagnitude ( )
- effects . get ( ESM : : MagicEffect : : CalmHumanoid + creature ) . getMagnitude ( ) ) ) ;
2014-01-05 00:34:35 +00:00
creatureStats . setAiSetting ( CreatureStats : : AI_Fight , stat ) ;
stat = creatureStats . getAiSetting ( CreatureStats : : AI_Flee ) ;
2015-08-20 06:17:02 +00:00
stat . setModifier ( static_cast < int > ( effects . get ( ESM : : MagicEffect : : DemoralizeHumanoid + creature ) . getMagnitude ( )
- effects . get ( ESM : : MagicEffect : : RallyHumanoid + creature ) . getMagnitude ( ) ) ) ;
2014-01-05 00:34:35 +00:00
creatureStats . setAiSetting ( CreatureStats : : AI_Flee , stat ) ;
}
if ( creature & & ptr . get < ESM : : Creature > ( ) - > mBase - > mData . mType = = ESM : : Creature : : Undead )
{
Stat < int > stat = creatureStats . getAiSetting ( CreatureStats : : AI_Flee ) ;
2015-08-20 06:17:02 +00:00
stat . setModifier ( static_cast < int > ( effects . get ( ESM : : MagicEffect : : TurnUndead ) . getMagnitude ( ) ) ) ;
2014-01-05 00:34:35 +00:00
creatureStats . setAiSetting ( CreatureStats : : AI_Flee , stat ) ;
}
2014-06-17 01:54:41 +00:00
if ( ! wasDead & & creatureStats . isDead ( ) )
{
// The actor was killed by a magic effect. Figure out if the player was responsible for it.
const ActiveSpells & spells = creatureStats . getActiveSpells ( ) ;
2014-06-17 14:51:59 +00:00
bool killedByPlayer = false ;
2015-08-21 09:12:39 +00:00
MWWorld : : Ptr player = getPlayer ( ) ;
2014-06-17 01:54:41 +00:00
for ( ActiveSpells : : TIterator it = spells . begin ( ) ; it ! = spells . end ( ) ; + + it )
{
const ActiveSpells : : ActiveSpellParams & spell = it - > second ;
for ( std : : vector < ActiveSpells : : ActiveEffect > : : const_iterator effectIt = spell . mEffects . begin ( ) ;
effectIt ! = spell . mEffects . end ( ) ; + + effectIt )
{
int effectId = effectIt - > mEffectId ;
bool isDamageEffect = false ;
2015-07-18 18:39:45 +00:00
int damageEffects [ ] = {
ESM : : MagicEffect : : FireDamage , ESM : : MagicEffect : : ShockDamage , ESM : : MagicEffect : : FrostDamage , ESM : : MagicEffect : : Poison ,
ESM : : MagicEffect : : SunDamage , ESM : : MagicEffect : : DamageHealth , ESM : : MagicEffect : : AbsorbHealth
} ;
2014-06-17 01:54:41 +00:00
for ( unsigned int i = 0 ; i < sizeof ( damageEffects ) / sizeof ( int ) ; + + i )
{
if ( damageEffects [ i ] = = effectId )
isDamageEffect = true ;
}
MWWorld : : Ptr caster = MWBase : : Environment : : get ( ) . getWorld ( ) - > searchPtrViaActorId ( spell . mCasterActorId ) ;
if ( isDamageEffect & & caster = = player )
2014-06-17 14:51:59 +00:00
killedByPlayer = true ;
2014-06-17 01:54:41 +00:00
}
}
2015-07-18 18:39:45 +00:00
if ( killedByPlayer )
{
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > actorKilled ( ptr , player ) ;
if ( player . getClass ( ) . getNpcStats ( player ) . isWerewolf ( ) )
player . getClass ( ) . getNpcStats ( player ) . addWerewolfKill ( ) ;
}
2014-06-17 01:54:41 +00:00
}
2013-11-21 02:39:55 +00:00
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
2014-12-14 18:35:34 +00:00
// any value of calm > 0 will stop the actor from fighting
2015-08-20 06:17:02 +00:00
if ( ( effects . get ( ESM : : MagicEffect : : CalmHumanoid ) . getMagnitude ( ) > 0 & & ptr . getClass ( ) . isNpc ( ) )
| | ( effects . get ( ESM : : MagicEffect : : CalmCreature ) . getMagnitude ( ) > 0 & & ! ptr . getClass ( ) . isNpc ( ) ) )
2014-12-14 18:35:34 +00:00
{
for ( std : : list < AiPackage * > : : const_iterator it = creatureStats . getAiSequence ( ) . begin ( ) ; it ! = creatureStats . getAiSequence ( ) . end ( ) ; )
{
if ( ( * it ) - > getTypeId ( ) = = AiPackage : : TypeIdCombat )
it = creatureStats . getAiSequence ( ) . erase ( it ) ;
else
+ + it ;
}
}
2013-11-21 02:39:55 +00:00
// Update bound effects
2015-01-01 01:46:18 +00:00
// Note: in vanilla MW multiple bound items of the same type can be created by different spells.
// As these extra copies are kinda useless this may or may not be important.
2013-11-21 02:39:55 +00:00
static std : : map < int , std : : string > boundItemsMap ;
if ( boundItemsMap . empty ( ) )
{
2014-01-03 19:54:14 +00:00
boundItemsMap [ ESM : : MagicEffect : : BoundBattleAxe ] = " sMagicBoundBattleAxeID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundBoots ] = " sMagicBoundBootsID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundCuirass ] = " sMagicBoundCuirassID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundDagger ] = " sMagicBoundDaggerID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundGloves ] = " sMagicBoundLeftGauntletID " ; // Note: needs RightGauntlet variant too (see below)
boundItemsMap [ ESM : : MagicEffect : : BoundHelm ] = " sMagicBoundHelmID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundLongbow ] = " sMagicBoundLongbowID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundLongsword ] = " sMagicBoundLongswordID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundMace ] = " sMagicBoundMaceID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundShield ] = " sMagicBoundShieldID " ;
boundItemsMap [ ESM : : MagicEffect : : BoundSpear ] = " sMagicBoundSpearID " ;
2013-11-21 02:39:55 +00:00
}
for ( std : : map < int , std : : string > : : iterator it = boundItemsMap . begin ( ) ; it ! = boundItemsMap . end ( ) ; + + it )
{
bool found = creatureStats . mBoundItems . find ( it - > first ) ! = creatureStats . mBoundItems . end ( ) ;
2015-08-20 06:17:02 +00:00
float magnitude = effects . get ( it - > first ) . getMagnitude ( ) ;
2013-11-21 02:39:55 +00:00
if ( found ! = ( magnitude > 0 ) )
{
2014-01-03 19:54:14 +00:00
std : : string itemGmst = it - > second ;
std : : string item = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find (
itemGmst ) - > getString ( ) ;
2013-11-21 02:39:55 +00:00
if ( it - > first = = ESM : : MagicEffect : : BoundGloves )
{
2014-06-19 14:00:27 +00:00
item = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find (
" sMagicBoundLeftGauntletID " ) - > getString ( ) ;
adjustBoundItem ( item , magnitude > 0 , ptr ) ;
item = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find (
" sMagicBoundRightGauntletID " ) - > getString ( ) ;
adjustBoundItem ( item , magnitude > 0 , ptr ) ;
2013-11-21 02:39:55 +00:00
}
else
adjustBoundItem ( item , magnitude > 0 , ptr ) ;
if ( magnitude > 0 )
creatureStats . mBoundItems . insert ( it - > first ) ;
else
creatureStats . mBoundItems . erase ( it - > first ) ;
}
}
2017-02-04 16:54:59 +00:00
bool hasSummonEffect = false ;
for ( MagicEffects : : Collection : : const_iterator it = effects . begin ( ) ; it ! = effects . end ( ) ; + + it )
if ( it - > first . mId > = ESM : : MagicEffect : : SummonScamp & & it - > first . mId < = ESM : : MagicEffect : : SummonStormAtronach )
hasSummonEffect = true ;
if ( ! creatureStats . getSummonedCreatureMap ( ) . empty ( ) | | ! creatureStats . getSummonedCreatureGraveyard ( ) . empty ( ) | | hasSummonEffect )
{
UpdateSummonedCreatures updateSummonedCreatures ( ptr ) ;
creatureStats . getActiveSpells ( ) . visitEffectSources ( updateSummonedCreatures ) ;
if ( ptr . getClass ( ) . hasInventoryStore ( ptr ) )
ptr . getClass ( ) . getInventoryStore ( ptr ) . visitEffectSources ( updateSummonedCreatures ) ;
updateSummonedCreatures . process ( ) ;
}
2012-07-17 13:49:37 +00:00
}
2014-11-02 17:01:12 +00:00
void Actors : : calculateNpcStatModifiers ( const MWWorld : : Ptr & ptr , float duration )
2013-11-09 09:49:00 +00:00
{
2014-05-22 18:37:22 +00:00
NpcStats & npcStats = ptr . getClass ( ) . getNpcStats ( ptr ) ;
2013-11-09 09:49:00 +00:00
const MagicEffects & effects = npcStats . getMagicEffects ( ) ;
// skills
for ( int i = 0 ; i < ESM : : Skill : : Length ; + + i )
{
2014-01-03 02:46:30 +00:00
SkillValue & skill = npcStats . getSkill ( i ) ;
2015-03-14 19:49:03 +00:00
skill . setModifier ( static_cast < int > ( effects . get ( EffectKey ( ESM : : MagicEffect : : FortifySkill , i ) ) . getMagnitude ( ) -
2015-03-07 15:23:02 +00:00
effects . get ( EffectKey ( ESM : : MagicEffect : : DrainSkill , i ) ) . getMagnitude ( ) -
2015-03-14 19:49:03 +00:00
effects . get ( EffectKey ( ESM : : MagicEffect : : AbsorbSkill , i ) ) . getMagnitude ( ) ) ) ;
2013-11-09 09:49:00 +00:00
}
}
2013-08-07 13:34:11 +00:00
void Actors : : updateDrowning ( const MWWorld : : Ptr & ptr , float duration )
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator it = mActors . find ( ptr ) ;
2014-12-12 16:39:00 +00:00
if ( it = = mActors . end ( ) )
return ;
2014-12-21 15:45:30 +00:00
CharacterController * ctrl = it - > second - > getCharacterController ( ) ;
2014-12-12 16:39:00 +00:00
2013-08-28 00:08:23 +00:00
NpcStats & stats = ptr . getClass ( ) . getNpcStats ( ptr ) ;
2014-12-12 16:39:00 +00:00
MWBase : : World * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
2015-06-01 19:41:13 +00:00
bool knockedOutUnderwater = ( ctrl - > isKnockedOut ( ) & & world - > isUnderwater ( ptr . getCell ( ) , osg : : Vec3f ( ptr . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ) ) ;
2014-12-12 16:39:00 +00:00
if ( ( world - > isSubmerged ( ptr ) | | knockedOutUnderwater )
& & stats . getMagicEffects ( ) . get ( ESM : : MagicEffect : : WaterBreathing ) . getMagnitude ( ) = = 0 )
2013-08-07 13:34:11 +00:00
{
2013-08-28 00:08:23 +00:00
float timeLeft = 0.0f ;
2014-12-12 16:39:00 +00:00
if ( knockedOutUnderwater )
2013-08-07 13:34:11 +00:00
stats . setTimeToStartDrowning ( 0 ) ;
2013-08-28 00:08:23 +00:00
else
{
timeLeft = stats . getTimeToStartDrowning ( ) - duration ;
if ( timeLeft < 0.0f )
timeLeft = 0.0f ;
stats . setTimeToStartDrowning ( timeLeft ) ;
}
2013-08-09 08:14:08 +00:00
if ( timeLeft = = 0.0f )
2013-08-28 00:08:23 +00:00
{
// If drowning, apply 3 points of damage per second
2014-01-10 21:27:31 +00:00
static const float fSuffocationDamage = world - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fSuffocationDamage " ) - > getFloat ( ) ;
2015-07-24 18:23:27 +00:00
DynamicStat < float > health = stats . getHealth ( ) ;
health . setCurrent ( health . getCurrent ( ) - fSuffocationDamage * duration ) ;
stats . setHealth ( health ) ;
2013-08-28 00:08:23 +00:00
2014-10-11 20:21:48 +00:00
// Play a drowning sound
MWBase : : SoundManager * sndmgr = MWBase : : Environment : : get ( ) . getSoundManager ( ) ;
if ( ! sndmgr - > getSoundPlaying ( ptr , " drown " ) )
sndmgr - > playSound3D ( ptr , " drown " , 1.0f , 1.0f ) ;
2014-01-08 17:39:44 +00:00
if ( ptr = = world - > getPlayerPtr ( ) )
2014-10-11 20:21:48 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > activateHitOverlay ( false ) ;
2013-08-28 00:08:23 +00:00
}
2013-08-07 13:34:11 +00:00
}
else
2014-01-10 21:27:31 +00:00
{
static const float fHoldBreathTime = world - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fHoldBreathTime " ) - > getFloat ( ) ;
stats . setTimeToStartDrowning ( fHoldBreathTime ) ;
}
2013-08-07 13:34:11 +00:00
}
2013-10-15 19:23:42 +00:00
void Actors : : updateEquippedLight ( const MWWorld : : Ptr & ptr , float duration )
{
2015-08-21 09:12:39 +00:00
bool isPlayer = ( ptr = = getPlayer ( ) ) ;
2013-12-20 21:38:23 +00:00
2014-05-22 18:37:22 +00:00
MWWorld : : InventoryStore & inventoryStore = ptr . getClass ( ) . getInventoryStore ( ptr ) ;
2013-10-15 19:23:42 +00:00
MWWorld : : ContainerStoreIterator heldIter =
inventoryStore . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedLeft ) ;
2013-12-20 21:38:23 +00:00
/**
* Automatically equip NPCs torches at night and unequip them at day
*/
2016-11-18 00:47:59 +00:00
// tes3mp needs player-controlled NPCs to not automatically unequip
// their light-emitting items, so a 2nd condition has been added
// that should return false only for them
if ( ! isPlayer & & ptr . getBase ( ) - > canChangeCell )
2013-12-20 21:38:23 +00:00
{
2013-12-30 16:53:02 +00:00
MWWorld : : ContainerStoreIterator torch = inventoryStore . end ( ) ;
for ( MWWorld : : ContainerStoreIterator it = inventoryStore . begin ( ) ; it ! = inventoryStore . end ( ) ; + + it )
2013-12-20 21:38:23 +00:00
{
2013-12-30 16:53:02 +00:00
if ( it - > getTypeName ( ) = = typeid ( ESM : : Light ) . name ( ) )
{
torch = it ;
break ;
}
2013-12-20 21:38:23 +00:00
}
2013-12-30 16:53:02 +00:00
if ( MWBase : : Environment : : get ( ) . getWorld ( ) - > isDark ( ) )
2013-12-20 21:38:23 +00:00
{
2013-12-30 16:53:02 +00:00
if ( torch ! = inventoryStore . end ( ) )
{
2014-07-28 14:41:12 +00:00
if ( ! ptr . getClass ( ) . getCreatureStats ( ptr ) . getAiSequence ( ) . isInCombat ( ) )
2013-12-30 16:53:02 +00:00
{
// For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.
if ( heldIter ! = inventoryStore . end ( ) & & heldIter - > getTypeName ( ) ! = typeid ( ESM : : Light ) . name ( ) )
inventoryStore . unequipItem ( * heldIter , ptr ) ;
// Also unequip twohanded weapons which conflict with anything in CarriedLeft
if ( torch - > getClass ( ) . canBeEquipped ( * torch , ptr ) . first = = 3 )
inventoryStore . unequipSlot ( MWWorld : : InventoryStore : : Slot_CarriedRight , ptr ) ;
}
heldIter = inventoryStore . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedLeft ) ;
// If we have a torch and can equip it (left slot free, no
// twohanded weapon in right slot), then equip it now.
if ( heldIter = = inventoryStore . end ( )
& & torch - > getClass ( ) . canBeEquipped ( * torch , ptr ) . first = = 1 )
{
inventoryStore . equip ( MWWorld : : InventoryStore : : Slot_CarriedLeft , torch , ptr ) ;
}
}
2013-12-20 21:38:23 +00:00
}
2013-12-30 16:53:02 +00:00
else
2013-12-20 21:38:23 +00:00
{
2013-12-30 16:53:02 +00:00
if ( heldIter ! = inventoryStore . end ( ) & & heldIter - > getTypeName ( ) = = typeid ( ESM : : Light ) . name ( ) )
{
// At day, unequip lights and auto equip shields or other suitable items
// (Note: autoEquip will ignore lights)
inventoryStore . autoEquip ( ptr ) ;
}
2013-12-20 21:38:23 +00:00
}
}
2013-10-15 19:23:42 +00:00
2013-12-30 16:53:02 +00:00
heldIter = inventoryStore . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedLeft ) ;
2013-12-20 21:38:23 +00:00
//If holding a light...
2013-10-15 19:23:42 +00:00
if ( heldIter . getType ( ) = = MWWorld : : ContainerStore : : Type_Light )
{
2013-10-16 00:56:42 +00:00
// Use time from the player's light
if ( isPlayer )
{
float timeRemaining = heldIter - > getClass ( ) . getRemainingUsageTime ( * heldIter ) ;
// -1 is infinite light source. Other negative values are treated as 0.
if ( timeRemaining ! = - 1.0f )
{
timeRemaining - = duration ;
if ( timeRemaining > 0.0f )
heldIter - > getClass ( ) . setRemainingUsageTime ( * heldIter , timeRemaining ) ;
else
{
2013-08-12 23:19:33 +00:00
inventoryStore . remove ( * heldIter , 1 , ptr ) ; // remove it
2013-10-16 00:56:42 +00:00
return ;
}
}
}
2013-10-15 19:23:42 +00:00
2013-10-16 00:56:42 +00:00
// Both NPC and player lights extinguish in water.
if ( MWBase : : Environment : : get ( ) . getWorld ( ) - > isSwimming ( ptr ) )
2013-10-15 19:23:42 +00:00
{
2013-08-12 23:19:33 +00:00
inventoryStore . remove ( * heldIter , 1 , ptr ) ; // remove it
2013-10-15 19:23:42 +00:00
2013-10-16 00:56:42 +00:00
// ...But, only the player makes a sound.
if ( isPlayer )
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > playSound ( " torch out " ,
1.0 , 1.0 , MWBase : : SoundManager : : Play_TypeSfx , MWBase : : SoundManager : : Play_NoEnv ) ;
2013-10-15 19:23:42 +00:00
}
}
}
2014-04-03 04:50:09 +00:00
void Actors : : updateCrimePersuit ( const MWWorld : : Ptr & ptr , float duration )
{
2015-08-21 09:12:39 +00:00
MWWorld : : Ptr player = getPlayer ( ) ;
2014-04-06 02:45:40 +00:00
if ( ptr ! = player & & ptr . getClass ( ) . isNpc ( ) )
2014-04-03 04:50:09 +00:00
{
2014-04-05 14:26:14 +00:00
// get stats of witness
2014-05-22 18:37:22 +00:00
CreatureStats & creatureStats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
NpcStats & npcStats = ptr . getClass ( ) . getNpcStats ( ptr ) ;
2014-04-05 14:26:14 +00:00
2014-09-09 02:15:54 +00:00
if ( player . getClass ( ) . getNpcStats ( player ) . isWerewolf ( ) )
return ;
2014-07-28 14:41:12 +00:00
if ( ptr . getClass ( ) . isClass ( ptr , " Guard " ) & & creatureStats . getAiSequence ( ) . getTypeId ( ) ! = AiPackage : : TypeIdPursue & & ! creatureStats . getAiSequence ( ) . isInCombat ( ) )
2014-04-03 18:53:31 +00:00
{
2014-04-05 14:26:14 +00:00
const MWWorld : : ESMStore & esmStore = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) ;
2015-11-27 01:10:10 +00:00
static const int cutoff = esmStore . get < ESM : : GameSetting > ( ) . find ( " iCrimeThreshold " ) - > getInt ( ) ;
2014-05-03 10:09:34 +00:00
// Force dialogue on sight if bounty is greater than the cutoff
// In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)
2014-04-06 02:45:40 +00:00
if ( player . getClass ( ) . getNpcStats ( player ) . getBounty ( ) > = cutoff
2014-05-03 10:09:34 +00:00
// TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?
2014-04-06 02:45:40 +00:00
& & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( ptr , player )
& & MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > awarenessCheck ( player , ptr ) )
2014-04-03 18:53:31 +00:00
{
2015-11-27 01:10:10 +00:00
static const int iCrimeThresholdMultiplier = esmStore . get < ESM : : GameSetting > ( ) . find ( " iCrimeThresholdMultiplier " ) - > getInt ( ) ;
2014-07-23 22:59:58 +00:00
if ( player . getClass ( ) . getNpcStats ( player ) . getBounty ( ) > = cutoff * iCrimeThresholdMultiplier )
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( ptr , player ) ;
else
creatureStats . getAiSequence ( ) . stack ( AiPursue ( player ) , ptr ) ;
2014-05-03 10:09:34 +00:00
creatureStats . setAlarmed ( true ) ;
npcStats . setCrimeId ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayer ( ) . getNewCrimeId ( ) ) ;
2014-04-03 18:53:31 +00:00
}
}
// if I was a witness to a crime
2014-04-05 14:26:14 +00:00
if ( npcStats . getCrimeId ( ) ! = - 1 )
2014-04-03 04:50:09 +00:00
{
2014-05-03 10:23:22 +00:00
// if you've paid for your crimes and I havent noticed
2014-04-06 02:45:40 +00:00
if ( npcStats . getCrimeId ( ) < = MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayer ( ) . getCrimeId ( ) )
2014-04-03 04:50:09 +00:00
{
2014-04-05 14:26:14 +00:00
// Calm witness down
2014-04-03 04:50:09 +00:00
if ( ptr . getClass ( ) . isClass ( ptr , " Guard " ) )
2014-05-03 10:23:22 +00:00
creatureStats . getAiSequence ( ) . stopPursuit ( ) ;
2014-04-03 04:50:09 +00:00
creatureStats . getAiSequence ( ) . stopCombat ( ) ;
2014-04-05 14:26:14 +00:00
// Reset factors to attack
creatureStats . setAttacked ( false ) ;
2014-04-18 11:44:09 +00:00
creatureStats . setAlarmed ( false ) ;
2014-12-14 18:35:34 +00:00
creatureStats . setAiSetting ( CreatureStats : : AI_Fight , ptr . getClass ( ) . getBaseFightRating ( ptr ) ) ;
2014-04-25 02:47:45 +00:00
2014-04-05 14:26:14 +00:00
// Update witness crime id
npcStats . setCrimeId ( - 1 ) ;
2014-04-03 04:50:09 +00:00
}
}
}
}
2013-11-20 23:27:22 +00:00
Actors : : Actors ( ) { }
2012-03-30 14:18:58 +00:00
2013-12-30 20:47:06 +00:00
Actors : : ~ Actors ( )
{
2014-07-27 21:10:58 +00:00
clear ( ) ;
2013-12-30 20:47:06 +00:00
}
2014-05-13 17:01:02 +00:00
void Actors : : addActor ( const MWWorld : : Ptr & ptr , bool updateImmediately )
2012-03-30 14:18:58 +00:00
{
2013-07-18 04:39:21 +00:00
removeActor ( ptr ) ;
2013-01-16 18:16:37 +00:00
MWRender : : Animation * anim = MWBase : : Environment : : get ( ) . getWorld ( ) - > getAnimation ( ptr ) ;
2015-04-25 13:19:17 +00:00
if ( ! anim )
return ;
2014-12-21 15:45:30 +00:00
mActors . insert ( std : : make_pair ( ptr , new Actor ( ptr , anim ) ) ) ;
2014-05-13 17:01:02 +00:00
if ( updateImmediately )
2014-12-21 15:45:30 +00:00
mActors [ ptr ] - > getCharacterController ( ) - > update ( 0 ) ;
2012-03-30 14:18:58 +00:00
}
void Actors : : removeActor ( const MWWorld : : Ptr & ptr )
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . find ( ptr ) ;
2013-01-12 15:12:12 +00:00
if ( iter ! = mActors . end ( ) )
2013-07-08 21:05:53 +00:00
{
delete iter - > second ;
2013-01-12 15:12:12 +00:00
mActors . erase ( iter ) ;
2013-07-08 21:05:53 +00:00
}
2012-03-30 14:18:58 +00:00
}
2013-02-25 17:57:34 +00:00
void Actors : : updateActor ( const MWWorld : : Ptr & old , const MWWorld : : Ptr & ptr )
2013-01-29 08:19:24 +00:00
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . find ( old ) ;
2013-01-29 08:19:24 +00:00
if ( iter ! = mActors . end ( ) )
{
2014-12-21 15:45:30 +00:00
Actor * actor = iter - > second ;
2013-01-29 08:19:24 +00:00
mActors . erase ( iter ) ;
2013-02-25 17:57:34 +00:00
2014-12-21 15:45:30 +00:00
actor - > updatePtr ( ptr ) ;
mActors . insert ( std : : make_pair ( ptr , actor ) ) ;
2013-01-29 08:19:24 +00:00
}
}
2013-12-07 11:27:06 +00:00
void Actors : : dropActors ( const MWWorld : : CellStore * cellStore , const MWWorld : : Ptr & ignore )
2012-03-30 14:18:58 +00:00
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . begin ( ) ;
2013-01-12 15:12:12 +00:00
while ( iter ! = mActors . end ( ) )
{
2015-11-27 20:38:57 +00:00
if ( ( iter - > first . isInCell ( ) & & iter - > first . getCell ( ) = = cellStore ) & & iter - > first ! = ignore )
2013-07-08 21:05:53 +00:00
{
delete iter - > second ;
2013-01-12 15:12:12 +00:00
mActors . erase ( iter + + ) ;
2013-07-08 21:05:53 +00:00
}
2012-03-30 14:18:58 +00:00
else
+ + iter ;
2013-01-12 15:12:12 +00:00
}
2012-03-30 14:18:58 +00:00
}
2013-01-16 16:22:38 +00:00
void Actors : : update ( float duration , bool paused )
2012-03-30 14:18:58 +00:00
{
2014-01-25 14:54:24 +00:00
if ( ! paused )
2014-09-14 20:29:06 +00:00
{
2014-05-15 20:03:48 +00:00
static float timerUpdateAITargets = 0 ;
2014-12-16 19:47:45 +00:00
static float timerUpdateHeadTrack = 0 ;
2017-02-04 16:20:47 +00:00
static float timerUpdateEquippedLight = 0 ;
const float updateEquippedLightInterval = 1.0f ;
2014-05-15 20:03:48 +00:00
// target lists get updated once every 1.0 sec
if ( timerUpdateAITargets > = 1.0f ) timerUpdateAITargets = 0 ;
2014-12-16 19:47:45 +00:00
if ( timerUpdateHeadTrack > = 0.3f ) timerUpdateHeadTrack = 0 ;
2017-02-04 16:20:47 +00:00
if ( timerUpdateEquippedLight > = updateEquippedLightInterval ) timerUpdateEquippedLight = 0 ;
2014-05-15 20:03:48 +00:00
2015-08-21 09:12:39 +00:00
MWWorld : : Ptr player = getPlayer ( ) ;
2014-05-15 20:03:48 +00:00
2014-09-20 11:55:57 +00:00
int hostilesCount = 0 ; // need to know this to play Battle music
2014-12-21 15:45:30 +00:00
/// \todo move update logic to Actor class where appropriate
2014-05-15 20:03:48 +00:00
// AI and magic effects update
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-02-05 08:50:21 +00:00
{
2016-06-06 23:53:16 +00:00
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
// This distance could be made configurable later, but the setting must be marked with a big warning:
// using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876)
2015-06-03 17:41:19 +00:00
bool inProcessingRange = ( player . getRefData ( ) . getPosition ( ) . asVec3 ( ) - iter - > first . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) . length2 ( )
2016-06-06 23:53:16 +00:00
< = sqrAiProcessingDistance ;
2015-04-29 21:48:08 +00:00
iter - > second - > getCharacterController ( ) - > setActive ( inProcessingRange ) ;
2015-07-02 17:14:28 +00:00
if ( iter - > first = = player )
2016-01-12 03:41:44 +00:00
{
bool state = MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayer ( ) . getAttackingOrSpell ( ) ;
2016-11-12 11:39:04 +00:00
DrawState_ dstate = player . getClass ( ) . getNpcStats ( player ) . getDrawState ( ) ;
2016-01-12 03:41:44 +00:00
iter - > second - > getCharacterController ( ) - > setAttackingOrSpell ( state ) ;
2016-11-12 11:39:04 +00:00
if ( dstate = = DrawState_Weapon )
mwmp : : Main : : get ( ) . getLocalPlayer ( ) - > prepareAttack ( mwmp : : Attack : : MELEE , state ) ;
2016-01-12 03:41:44 +00:00
}
2016-11-15 19:54:06 +00:00
mwmp : : DedicatedPlayer * dedicatedPlayer = mwmp : : Players : : getPlayer ( iter - > first ) ;
2016-12-16 08:59:15 +00:00
if ( dedicatedPlayer ! = NULL )
2016-11-12 11:39:04 +00:00
dedicatedPlayer - > updateActor ( iter - > second ) ;
2016-01-12 03:41:44 +00:00
2015-07-02 17:14:28 +00:00
2014-02-05 08:50:21 +00:00
if ( ! iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) . isDead ( ) )
{
2016-06-13 00:06:44 +00:00
MWWorld : : Ptr actor = iter - > first ; // make a copy of the map key to avoid it being invalidated when the player teleports
2016-10-13 12:39:58 +00:00
bool cellChanged = MWBase : : Environment : : get ( ) . getWorld ( ) - > hasCellChanged ( ) ;
2016-06-13 00:06:44 +00:00
updateActor ( actor , duration ) ;
2016-10-13 12:39:58 +00:00
if ( ! cellChanged & & MWBase : : Environment : : get ( ) . getWorld ( ) - > hasCellChanged ( ) )
2016-02-01 23:50:21 +00:00
{
return ; // for now abort update of the old cell when cell changes by teleportation magic effect
// a better solution might be to apply cell changes at the end of the frame
}
2015-04-29 21:48:08 +00:00
if ( MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > isAIActive ( ) & & inProcessingRange )
2014-05-15 20:03:48 +00:00
{
2014-07-27 18:30:52 +00:00
if ( timerUpdateAITargets = = 0 )
2014-08-06 19:16:14 +00:00
{
if ( iter - > first ! = player )
adjustCommandedActor ( iter - > first ) ;
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator it ( mActors . begin ( ) ) ; it ! = mActors . end ( ) ; + + it )
2014-05-15 20:03:48 +00:00
{
2014-07-27 18:30:52 +00:00
if ( it - > first = = iter - > first | | iter - > first = = player ) // player is not AI-controlled
continue ;
engageCombat ( iter - > first , it - > first , it - > first = = player ) ;
2014-05-15 20:03:48 +00:00
}
}
2014-12-16 19:47:45 +00:00
if ( timerUpdateHeadTrack = = 0 )
{
float sqrHeadTrackDistance = std : : numeric_limits < float > : : max ( ) ;
MWWorld : : Ptr headTrackTarget ;
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator it ( mActors . begin ( ) ) ; it ! = mActors . end ( ) ; + + it )
2014-12-16 19:47:45 +00:00
{
if ( it - > first = = iter - > first )
continue ;
updateHeadTracking ( iter - > first , it - > first , headTrackTarget , sqrHeadTrackDistance ) ;
}
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > setHeadTrackTarget ( headTrackTarget ) ;
2014-12-16 19:47:45 +00:00
}
2014-05-15 20:03:48 +00:00
if ( iter - > first . getClass ( ) . isNpc ( ) & & iter - > first ! = player )
updateCrimePersuit ( iter - > first , duration ) ;
if ( iter - > first ! = player )
2014-09-20 11:55:57 +00:00
{
2015-01-13 17:09:10 +00:00
CreatureStats & stats = iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) ;
if ( isConscious ( iter - > first ) )
2015-06-26 15:47:04 +00:00
stats . getAiSequence ( ) . execute ( iter - > first , * iter - > second - > getCharacterController ( ) , iter - > second - > getAiState ( ) , duration ) ;
2015-01-13 17:09:10 +00:00
if ( stats . getAiSequence ( ) . isInCombat ( ) & & ! stats . isDead ( ) ) hostilesCount + + ;
2014-09-20 11:55:57 +00:00
}
2014-05-15 20:03:48 +00:00
}
if ( iter - > first . getTypeName ( ) = = typeid ( ESM : : NPC ) . name ( ) )
2017-02-04 16:20:47 +00:00
{
2014-05-15 20:03:48 +00:00
updateNpc ( iter - > first , duration ) ;
2017-02-04 16:20:47 +00:00
if ( timerUpdateEquippedLight = = 0 )
updateEquippedLight ( iter - > first , updateEquippedLightInterval ) ;
}
2014-02-05 08:50:21 +00:00
}
}
2014-05-15 20:03:48 +00:00
timerUpdateAITargets + = duration ;
2014-12-16 19:47:45 +00:00
timerUpdateHeadTrack + = duration ;
2017-02-06 04:10:40 +00:00
timerUpdateEquippedLight + = duration ;
2014-05-15 20:03:48 +00:00
2014-02-05 08:50:21 +00:00
// Looping magic VFX update
2014-01-25 14:54:24 +00:00
// Note: we need to do this before any of the animations are updated.
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
// so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between.
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > updateContinuousVfx ( ) ;
2014-01-25 14:54:24 +00:00
2014-02-05 08:50:21 +00:00
// Animation/movement update
2014-09-25 16:00:32 +00:00
CharacterController * playerCharacter = NULL ;
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-01-25 14:54:24 +00:00
{
2014-10-01 19:54:59 +00:00
if ( iter - > first ! = player & &
2015-06-03 17:41:19 +00:00
( player . getRefData ( ) . getPosition ( ) . asVec3 ( ) - iter - > first . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) . length2 ( )
2016-06-06 23:53:16 +00:00
> sqrAiProcessingDistance )
2014-10-01 19:54:59 +00:00
continue ;
2015-08-20 06:12:37 +00:00
if ( iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) . isParalyzed ( ) )
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > skipAnim ( ) ;
2014-09-25 16:00:32 +00:00
2016-02-02 14:55:19 +00:00
// Handle player last, in case a cell transition occurs by casting a teleportation spell
// (would invalidate the iterator)
if ( iter - > first = = getPlayer ( ) )
{
playerCharacter = iter - > second - > getCharacterController ( ) ;
continue ;
}
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > update ( duration ) ;
2014-01-25 14:54:24 +00:00
}
2014-01-30 23:15:59 +00:00
2016-02-02 14:55:19 +00:00
if ( playerCharacter )
playerCharacter - > update ( duration ) ;
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2012-03-30 15:01:55 +00:00
{
2014-05-22 18:37:22 +00:00
const MWWorld : : Class & cls = iter - > first . getClass ( ) ;
2013-07-26 15:08:52 +00:00
CreatureStats & stats = cls . getCreatureStats ( iter - > first ) ;
2014-04-28 00:54:22 +00:00
//KnockedOutOneFrameLogic
//Used for "OnKnockedOut" command
//Put here to ensure that it's run for PRECISELY one frame.
2014-08-03 05:42:40 +00:00
if ( stats . getKnockedDown ( ) & & ! stats . getKnockedDownOneFrame ( ) & & ! stats . getKnockedDownOverOneFrame ( ) )
{ //Start it for one frame if nessesary
2014-04-28 00:54:22 +00:00
stats . setKnockedDownOneFrame ( true ) ;
}
2014-08-03 05:42:40 +00:00
else if ( stats . getKnockedDownOneFrame ( ) & & ! stats . getKnockedDownOverOneFrame ( ) )
{ //Turn off KnockedOutOneframe
2014-04-28 00:54:22 +00:00
stats . setKnockedDownOneFrame ( false ) ;
stats . setKnockedDownOverOneFrame ( true ) ;
}
2014-07-21 18:37:14 +00:00
}
2014-04-28 00:54:22 +00:00
2014-07-21 18:37:14 +00:00
killDeadActors ( ) ;
2014-06-10 10:20:29 +00:00
// check if we still have any player enemies to switch music
2015-07-19 00:08:24 +00:00
static int currentMusic = 0 ;
2014-06-10 10:20:29 +00:00
2015-07-19 00:08:24 +00:00
if ( currentMusic ! = 1 & & hostilesCount = = 0 & & ! ( player . getClass ( ) . getCreatureStats ( player ) . isDead ( ) & &
2014-09-14 20:29:06 +00:00
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > isMusicPlaying ( ) ) )
2014-06-10 10:20:29 +00:00
{
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > playPlaylist ( std : : string ( " Explore " ) ) ;
2015-07-19 00:08:24 +00:00
currentMusic = 1 ;
2014-06-10 10:20:29 +00:00
}
2015-07-19 00:08:24 +00:00
else if ( currentMusic ! = 2 & & hostilesCount > 0 )
2014-06-10 10:20:29 +00:00
{
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > playPlaylist ( std : : string ( " Battle " ) ) ;
2015-07-19 00:08:24 +00:00
currentMusic = 2 ;
2014-06-10 10:20:29 +00:00
}
2014-06-16 23:53:09 +00:00
static float sneakTimer = 0.f ; // times update of sneak icon
2014-05-04 20:20:09 +00:00
// if player is in sneak state see if anyone detects him
2015-09-16 13:37:36 +00:00
if ( playerCharacter & & playerCharacter - > isSneaking ( ) )
2014-05-04 20:20:09 +00:00
{
2014-09-26 15:12:48 +00:00
static float sneakSkillTimer = 0.f ; // times sneak skill progress from "avoid notice"
2014-05-04 20:20:09 +00:00
const MWWorld : : ESMStore & esmStore = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) ;
const int radius = esmStore . get < ESM : : GameSetting > ( ) . find ( " fSneakUseDist " ) - > getInt ( ) ;
2014-06-16 23:53:09 +00:00
static float fSneakUseDelay = esmStore . get < ESM : : GameSetting > ( ) . find ( " fSneakUseDelay " ) - > getFloat ( ) ;
if ( sneakTimer > = fSneakUseDelay )
sneakTimer = 0.f ;
if ( sneakTimer = = 0.f )
2014-05-04 20:20:09 +00:00
{
2014-06-16 23:53:09 +00:00
// Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress.
bool avoidedNotice = false ;
2014-05-04 20:20:09 +00:00
2014-09-26 15:12:48 +00:00
bool detected = false ;
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-05-04 20:20:09 +00:00
{
2016-06-07 20:01:44 +00:00
MWWorld : : Ptr observer = iter - > first ;
2014-06-16 23:53:09 +00:00
if ( iter - > first = = player ) // not the player
continue ;
2016-06-07 20:01:44 +00:00
if ( observer . getClass ( ) . getCreatureStats ( observer ) . isDead ( ) )
continue ;
2014-06-16 23:53:09 +00:00
// is the player in range and can they be detected
2016-06-07 20:01:44 +00:00
if ( ( observer . getRefData ( ) . getPosition ( ) . asVec3 ( ) - player . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) . length2 ( ) < = radius * radius
& & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( player , observer ) )
2014-06-16 23:53:09 +00:00
{
2016-06-07 20:01:44 +00:00
if ( MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > awarenessCheck ( player , observer ) )
2014-06-16 23:53:09 +00:00
{
detected = true ;
avoidedNotice = false ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > setSneakVisibility ( false ) ;
break ;
}
else if ( ! detected )
avoidedNotice = true ;
}
2014-05-04 20:20:09 +00:00
}
2014-06-16 23:53:09 +00:00
if ( sneakSkillTimer > = fSneakUseDelay )
sneakSkillTimer = 0.f ;
if ( avoidedNotice & & sneakSkillTimer = = 0.f )
player . getClass ( ) . skillUsageSucceeded ( player , ESM : : Skill : : Sneak , 0 ) ;
if ( ! detected )
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > setSneakVisibility ( true ) ;
}
sneakTimer + = duration ;
sneakSkillTimer + = duration ;
2014-05-04 20:20:09 +00:00
}
2014-05-04 21:54:33 +00:00
else
2014-06-16 23:53:09 +00:00
{
sneakTimer = 0.f ;
2014-05-04 21:54:33 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > setSneakVisibility ( false ) ;
2014-06-16 23:53:09 +00:00
}
2013-01-17 02:03:39 +00:00
}
2012-03-30 14:18:58 +00:00
}
2014-07-21 18:37:14 +00:00
void Actors : : killDeadActors ( )
{
2016-02-02 14:55:19 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-07-21 18:37:14 +00:00
{
const MWWorld : : Class & cls = iter - > first . getClass ( ) ;
CreatureStats & stats = cls . getCreatureStats ( iter - > first ) ;
if ( ! stats . isDead ( ) )
{
2014-12-21 15:45:30 +00:00
if ( iter - > second - > getCharacterController ( ) - > isDead ( ) )
2014-07-21 18:37:14 +00:00
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase : : Environment : : get ( ) . getWorld ( ) - > enableActorCollision ( iter - > first , true ) ;
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > resurrect ( ) ;
2014-07-21 18:37:14 +00:00
}
if ( ! stats . isDead ( ) )
continue ;
}
2016-06-11 22:04:50 +00:00
CharacterController : : KillResult killResult = iter - > second - > getCharacterController ( ) - > kill ( ) ;
if ( killResult = = CharacterController : : Result_DeathAnimStarted )
2014-07-21 18:37:14 +00:00
{
2015-08-19 13:51:04 +00:00
// Play dying words
2016-06-11 22:04:50 +00:00
// Note: It's not known whether the soundgen tags scream, roar, and moan are reliable
// for NPCs since some of the npc death animation files are missing them.
2015-08-19 13:51:04 +00:00
MWBase : : Environment : : get ( ) . getDialogueManager ( ) - > say ( iter - > first , " hit " ) ;
2014-07-21 18:37:14 +00:00
// Apply soultrap
if ( iter - > first . getTypeName ( ) = = typeid ( ESM : : Creature ) . name ( ) )
{
SoulTrap soulTrap ( iter - > first ) ;
stats . getActiveSpells ( ) . visitEffectSources ( soulTrap ) ;
}
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
2014-08-17 01:57:26 +00:00
stats . modifyMagicEffects ( MWMechanics : : MagicEffects ( ) ) ;
2014-07-21 18:37:14 +00:00
stats . getActiveSpells ( ) . clear ( ) ;
calculateCreatureStatModifiers ( iter - > first , 0 ) ;
if ( cls . isEssential ( iter - > first ) )
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > messageBox ( " #{sKilledEssential} " ) ;
}
2016-06-11 22:04:50 +00:00
else if ( killResult = = CharacterController : : Result_DeathAnimJustFinished )
{
iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) . notifyDied ( ) ;
+ + mDeathCount [ Misc : : StringUtils : : lowerCase ( iter - > first . getCellRef ( ) . getRefId ( ) ) ] ;
// Make sure spell effects are removed
2016-06-11 22:41:13 +00:00
purgeSpellEffects ( stats . getActorId ( ) ) ;
2016-06-11 22:04:50 +00:00
if ( iter - > first = = getPlayer ( ) )
{
//player's death animation is over
MWBase : : Environment : : get ( ) . getStateManager ( ) - > askLoadRecent ( ) ;
}
2016-12-15 18:29:05 +00:00
else
{
// NPC death animation is over, disable actor collision
MWBase : : Environment : : get ( ) . getWorld ( ) - > enableActorCollision ( iter - > first , false ) ;
}
2016-06-11 22:04:50 +00:00
// Play Death Music if it was the player dying
if ( iter - > first = = getPlayer ( ) )
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > streamMusic ( " Special/MW_Death.mp3 " ) ;
}
2014-07-21 18:37:14 +00:00
}
}
2016-06-12 00:43:33 +00:00
void Actors : : cleanupSummonedCreature ( MWMechanics : : CreatureStats & casterStats , int creatureActorId )
{
MWWorld : : Ptr ptr = MWBase : : Environment : : get ( ) . getWorld ( ) - > searchPtrViaActorId ( creatureActorId ) ;
if ( ! ptr . isEmpty ( ) )
{
MWBase : : Environment : : get ( ) . getWorld ( ) - > deleteObject ( ptr ) ;
const ESM : : Static * fx = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Static > ( )
. search ( " VFX_Summon_End " ) ;
if ( fx )
MWBase : : Environment : : get ( ) . getWorld ( ) - > spawnEffect ( " meshes \\ " + fx - > mModel ,
" " , ptr . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
}
else if ( creatureActorId ! = - 1 )
{
// We didn't find the creature. It's probably in an inactive cell.
// Add to graveyard so we can delete it when the cell becomes active.
std : : vector < int > & graveyard = casterStats . getSummonedCreatureGraveyard ( ) ;
graveyard . push_back ( creatureActorId ) ;
}
purgeSpellEffects ( creatureActorId ) ;
}
2016-06-11 22:41:13 +00:00
void Actors : : purgeSpellEffects ( int casterActorId )
{
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
{
MWMechanics : : ActiveSpells & spells = iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) . getActiveSpells ( ) ;
spells . purge ( casterActorId ) ;
}
}
2016-07-05 22:20:23 +00:00
void Actors : : rest ( bool sleep )
2012-09-21 15:53:16 +00:00
{
2016-07-05 22:20:23 +00:00
float duration = 3600.f / MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeScaleFactor ( ) ;
MWWorld : : Ptr player = MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayerPtr ( ) ;
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
{
if ( iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) . isDead ( ) )
continue ;
2014-07-23 22:25:02 +00:00
restoreDynamicStats ( iter - > first , sleep ) ;
2016-07-05 22:20:23 +00:00
if ( ( ! iter - > first . getRefData ( ) . getBaseNode ( ) ) | |
( player . getRefData ( ) . getPosition ( ) . asVec3 ( ) - iter - > first . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) . length2 ( ) > sqrAiProcessingDistance )
continue ;
adjustMagicEffects ( iter - > first ) ;
if ( iter - > first . getClass ( ) . getCreatureStats ( iter - > first ) . needToRecalcDynamicStats ( ) )
calculateDynamicStats ( iter - > first ) ;
calculateCreatureStatModifiers ( iter - > first , duration ) ;
if ( iter - > first . getClass ( ) . isNpc ( ) )
calculateNpcStatModifiers ( iter - > first , duration ) ;
}
fastForwardAi ( ) ;
2012-09-21 15:53:16 +00:00
}
2013-03-18 09:54:47 +00:00
2014-01-14 01:52:34 +00:00
int Actors : : getHoursToRest ( const MWWorld : : Ptr & ptr ) const
{
float healthPerHour , magickaPerHour ;
getRestorationPerHourOfSleep ( ptr , healthPerHour , magickaPerHour ) ;
CreatureStats & stats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
2014-06-06 13:13:25 +00:00
float healthHours = healthPerHour > 0
2014-01-14 01:52:34 +00:00
? ( stats . getHealth ( ) . getModified ( ) - stats . getHealth ( ) . getCurrent ( ) ) / healthPerHour
: 1.0f ;
2014-06-06 13:13:25 +00:00
float magickaHours = magickaPerHour > 0
2014-01-14 01:52:34 +00:00
? ( stats . getMagicka ( ) . getModified ( ) - stats . getMagicka ( ) . getCurrent ( ) ) / magickaPerHour
: 1.0f ;
2015-03-08 04:42:07 +00:00
int autoHours = static_cast < int > ( std : : ceil ( std : : max ( 1.f , std : : max ( healthHours , magickaHours ) ) ) ) ;
2014-01-14 01:52:34 +00:00
return autoHours ;
2012-09-21 15:53:16 +00:00
}
2013-03-18 09:54:47 +00:00
2012-10-27 09:33:18 +00:00
int Actors : : countDeaths ( const std : : string & id ) const
{
2013-01-12 15:12:12 +00:00
std : : map < std : : string , int > : : const_iterator iter = mDeathCount . find ( id ) ;
if ( iter ! = mDeathCount . end ( ) )
2012-10-27 09:33:18 +00:00
return iter - > second ;
return 0 ;
}
2013-01-17 01:53:18 +00:00
2013-04-25 14:08:11 +00:00
void Actors : : forceStateUpdate ( const MWWorld : : Ptr & ptr )
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . find ( ptr ) ;
2013-04-25 14:08:11 +00:00
if ( iter ! = mActors . end ( ) )
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > forceStateUpdate ( ) ;
2013-04-25 14:08:11 +00:00
}
2016-07-30 17:24:03 +00:00
bool Actors : : playAnimationGroup ( const MWWorld : : Ptr & ptr , const std : : string & groupName , int mode , int number , bool persist )
2013-01-17 01:53:18 +00:00
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . find ( ptr ) ;
2013-01-17 01:53:18 +00:00
if ( iter ! = mActors . end ( ) )
2015-07-29 18:15:06 +00:00
{
2016-07-30 17:24:03 +00:00
return iter - > second - > getCharacterController ( ) - > playGroup ( groupName , mode , number , persist ) ;
2015-07-29 18:15:06 +00:00
}
else
{
2015-08-18 23:24:54 +00:00
std : : cerr < < " Error in Actors::playAnimationGroup: Unable to find " < < ptr . getCellRef ( ) . getRefId ( ) < < std : : endl ;
2015-07-29 18:15:06 +00:00
return false ;
}
2013-01-17 01:53:18 +00:00
}
void Actors : : skipAnimation ( const MWWorld : : Ptr & ptr )
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . find ( ptr ) ;
2013-01-17 01:53:18 +00:00
if ( iter ! = mActors . end ( ) )
2014-12-21 15:45:30 +00:00
iter - > second - > getCharacterController ( ) - > skipAnim ( ) ;
2013-01-17 01:53:18 +00:00
}
2013-05-25 03:10:07 +00:00
bool Actors : : checkAnimationPlaying ( const MWWorld : : Ptr & ptr , const std : : string & groupName )
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator iter = mActors . find ( ptr ) ;
2013-05-25 03:10:07 +00:00
if ( iter ! = mActors . end ( ) )
2014-12-21 15:45:30 +00:00
return iter - > second - > getCharacterController ( ) - > isAnimPlaying ( groupName ) ;
2013-05-25 03:10:07 +00:00
return false ;
}
2014-01-20 12:00:43 +00:00
2016-07-30 17:24:03 +00:00
void Actors : : persistAnimationStates ( )
{
for ( PtrActorMap : : iterator iter = mActors . begin ( ) ; iter ! = mActors . end ( ) ; + + iter )
iter - > second - > getCharacterController ( ) - > persistAnimationState ( ) ;
}
2015-06-01 19:41:13 +00:00
void Actors : : getObjectsInRange ( const osg : : Vec3f & position , float radius , std : : vector < MWWorld : : Ptr > & out )
2014-01-20 12:00:43 +00:00
{
2014-12-21 15:45:30 +00:00
for ( PtrActorMap : : iterator iter = mActors . begin ( ) ; iter ! = mActors . end ( ) ; + + iter )
2014-01-20 12:00:43 +00:00
{
2015-06-01 19:41:13 +00:00
if ( ( iter - > first . getRefData ( ) . getPosition ( ) . asVec3 ( ) - position ) . length2 ( ) < = radius * radius )
2014-01-20 12:00:43 +00:00
out . push_back ( iter - > first ) ;
}
}
2014-01-28 11:33:31 +00:00
2015-12-06 22:32:49 +00:00
std : : list < MWWorld : : Ptr > Actors : : getActorsSidingWith ( const MWWorld : : Ptr & actor )
2014-01-12 13:02:15 +00:00
{
std : : list < MWWorld : : Ptr > list ;
2014-12-21 15:45:30 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-01-12 13:02:15 +00:00
{
2014-05-22 18:37:22 +00:00
const MWWorld : : Class & cls = iter - > first . getClass ( ) ;
2014-01-12 13:02:15 +00:00
CreatureStats & stats = cls . getCreatureStats ( iter - > first ) ;
2014-08-13 23:08:09 +00:00
if ( stats . isDead ( ) )
continue ;
2015-12-06 22:32:49 +00:00
// An actor counts as following if AiFollow or AiEscort is the current AiPackage, or there are only Combat packages before the AiFollow/AiEscort package
2014-08-13 23:08:09 +00:00
for ( std : : list < MWMechanics : : AiPackage * > : : const_iterator it = stats . getAiSequence ( ) . begin ( ) ; it ! = stats . getAiSequence ( ) . end ( ) ; + + it )
2014-01-12 13:02:15 +00:00
{
2015-12-06 22:32:49 +00:00
if ( ( * it ) - > sideWithTarget ( ) & & ( * it ) - > getTarget ( ) = = actor )
list . push_back ( iter - > first ) ;
2014-08-24 20:39:46 +00:00
else if ( ( * it ) - > getTypeId ( ) ! = MWMechanics : : AiPackage : : TypeIdCombat )
break ;
2014-01-12 13:02:15 +00:00
}
}
return list ;
}
2014-04-25 02:47:45 +00:00
2015-12-19 14:11:07 +00:00
std : : list < MWWorld : : Ptr > Actors : : getActorsFollowing ( const MWWorld : : Ptr & actor )
{
std : : list < MWWorld : : Ptr > list ;
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
{
const MWWorld : : Class & cls = iter - > first . getClass ( ) ;
CreatureStats & stats = cls . getCreatureStats ( iter - > first ) ;
if ( stats . isDead ( ) )
continue ;
// An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package
for ( std : : list < MWMechanics : : AiPackage * > : : const_iterator it = stats . getAiSequence ( ) . begin ( ) ; it ! = stats . getAiSequence ( ) . end ( ) ; + + it )
{
if ( ( * it ) - > followTargetThroughDoors ( ) & & ( * it ) - > getTarget ( ) = = actor )
list . push_back ( iter - > first ) ;
else if ( ( * it ) - > getTypeId ( ) ! = MWMechanics : : AiPackage : : TypeIdCombat )
break ;
}
}
return list ;
}
2016-12-20 11:38:51 +00:00
void Actors : : getActorsFollowing ( const MWWorld : : Ptr & actor , std : : set < MWWorld : : Ptr > & out ) {
std : : list < MWWorld : : Ptr > followers = getActorsFollowing ( actor ) ;
for ( std : : list < MWWorld : : Ptr > : : iterator it = followers . begin ( ) ; it ! = followers . end ( ) ; + + it )
if ( out . insert ( * it ) . second )
getActorsFollowing ( * it , out ) ;
}
void Actors : : getActorsSidingWith ( const MWWorld : : Ptr & actor , std : : set < MWWorld : : Ptr > & out ) {
std : : list < MWWorld : : Ptr > followers = getActorsSidingWith ( actor ) ;
for ( std : : list < MWWorld : : Ptr > : : iterator it = followers . begin ( ) ; it ! = followers . end ( ) ; + + it )
if ( out . insert ( * it ) . second )
getActorsSidingWith ( * it , out ) ;
}
2014-12-09 15:02:07 +00:00
std : : list < int > Actors : : getActorsFollowingIndices ( const MWWorld : : Ptr & actor )
{
std : : list < int > list ;
2014-12-21 15:45:30 +00:00
for ( PtrActorMap : : iterator iter ( mActors . begin ( ) ) ; iter ! = mActors . end ( ) ; + + iter )
2014-12-09 15:02:07 +00:00
{
const MWWorld : : Class & cls = iter - > first . getClass ( ) ;
CreatureStats & stats = cls . getCreatureStats ( iter - > first ) ;
if ( stats . isDead ( ) )
continue ;
// An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package
for ( std : : list < MWMechanics : : AiPackage * > : : const_iterator it = stats . getAiSequence ( ) . begin ( ) ; it ! = stats . getAiSequence ( ) . end ( ) ; + + it )
{
if ( ( * it ) - > getTypeId ( ) = = MWMechanics : : AiPackage : : TypeIdFollow )
{
2016-02-16 18:17:04 +00:00
MWWorld : : Ptr followTarget = ( * it ) - > getTarget ( ) ;
2014-12-09 15:02:07 +00:00
if ( followTarget . isEmpty ( ) )
continue ;
if ( followTarget = = actor )
2015-01-12 18:57:54 +00:00
list . push_back ( static_cast < MWMechanics : : AiFollow * > ( * it ) - > getFollowIndex ( ) ) ;
2015-12-13 16:42:11 +00:00
break ;
2014-12-09 15:02:07 +00:00
}
else if ( ( * it ) - > getTypeId ( ) ! = MWMechanics : : AiPackage : : TypeIdCombat )
break ;
}
}
return list ;
}
2016-06-06 23:53:16 +00:00
2014-04-25 02:47:45 +00:00
std : : list < MWWorld : : Ptr > Actors : : getActorsFighting ( const MWWorld : : Ptr & actor ) {
std : : list < MWWorld : : Ptr > list ;
std : : vector < MWWorld : : Ptr > neighbors ;
2015-06-01 19:41:13 +00:00
osg : : Vec3f position ( actor . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
2016-06-06 23:53:16 +00:00
getObjectsInRange ( position , aiProcessingDistance , neighbors ) ;
2016-06-08 20:12:52 +00:00
for ( std : : vector < MWWorld : : Ptr > : : const_iterator iter ( neighbors . begin ( ) ) ; iter ! = neighbors . end ( ) ; + + iter )
2014-04-25 02:47:45 +00:00
{
2014-05-22 18:37:22 +00:00
const MWWorld : : Class & cls = iter - > getClass ( ) ;
2016-06-06 23:53:16 +00:00
const CreatureStats & stats = cls . getCreatureStats ( * iter ) ;
2016-06-08 20:12:52 +00:00
if ( stats . isDead ( ) | | * iter = = actor )
continue ;
if ( stats . getAiSequence ( ) . isInCombat ( actor ) )
2014-07-20 20:34:20 +00:00
list . push_front ( * iter ) ;
2014-04-25 02:47:45 +00:00
}
return list ;
}
2014-06-12 23:24:58 +00:00
2016-06-06 23:53:16 +00:00
std : : list < MWWorld : : Ptr > Actors : : getEnemiesNearby ( const MWWorld : : Ptr & actor )
{
std : : list < MWWorld : : Ptr > list ;
std : : vector < MWWorld : : Ptr > neighbors ;
osg : : Vec3f position ( actor . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
getObjectsInRange ( position , aiProcessingDistance , neighbors ) ;
2016-06-11 15:45:56 +00:00
std : : list < MWWorld : : Ptr > followers = getActorsFollowing ( actor ) ;
2016-06-08 20:12:52 +00:00
for ( std : : vector < MWWorld : : Ptr > : : const_iterator iter ( neighbors . begin ( ) ) ; iter ! = neighbors . end ( ) ; + + iter )
2016-06-06 23:53:16 +00:00
{
const CreatureStats & stats = iter - > getClass ( ) . getCreatureStats ( * iter ) ;
2016-10-01 13:15:31 +00:00
if ( stats . isDead ( ) | | * iter = = actor | | iter - > getClass ( ) . isPureWaterCreature ( * iter ) )
2016-06-06 23:53:16 +00:00
continue ;
2016-06-11 15:45:56 +00:00
const bool isFollower = std : : find ( followers . begin ( ) , followers . end ( ) , * iter ) ! = followers . end ( ) ;
if ( stats . getAiSequence ( ) . isInCombat ( actor ) | | ( MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > isAggressive ( * iter , actor ) & & ! isFollower ) )
2016-06-06 23:53:16 +00:00
list . push_back ( * iter ) ;
}
return list ;
}
2014-06-12 23:24:58 +00:00
void Actors : : write ( ESM : : ESMWriter & writer , Loading : : Listener & listener ) const
{
writer . startRecord ( ESM : : REC_DCOU ) ;
for ( std : : map < std : : string , int > : : const_iterator it = mDeathCount . begin ( ) ; it ! = mDeathCount . end ( ) ; + + it )
{
writer . writeHNString ( " ID__ " , it - > first ) ;
writer . writeHNT ( " COUN " , it - > second ) ;
}
writer . endRecord ( ESM : : REC_DCOU ) ;
}
2015-01-22 18:04:59 +00:00
void Actors : : readRecord ( ESM : : ESMReader & reader , uint32_t type )
2014-06-12 23:24:58 +00:00
{
if ( type = = ESM : : REC_DCOU )
{
while ( reader . isNextSub ( " ID__ " ) )
{
std : : string id = reader . getHString ( ) ;
int count ;
reader . getHNT ( count , " COUN " ) ;
mDeathCount [ id ] = count ;
}
}
}
void Actors : : clear ( )
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : iterator it ( mActors . begin ( ) ) ;
2014-07-27 21:10:58 +00:00
for ( ; it ! = mActors . end ( ) ; + + it )
{
delete it - > second ;
it - > second = NULL ;
}
mActors . clear ( ) ;
2014-06-12 23:24:58 +00:00
mDeathCount . clear ( ) ;
}
2014-08-14 23:13:38 +00:00
void Actors : : updateMagicEffects ( const MWWorld : : Ptr & ptr )
{
adjustMagicEffects ( ptr ) ;
calculateCreatureStatModifiers ( ptr , 0.f ) ;
2014-08-17 03:42:52 +00:00
if ( ptr . getClass ( ) . isNpc ( ) )
2014-11-02 17:01:12 +00:00
calculateNpcStatModifiers ( ptr , 0.f ) ;
2014-08-14 23:13:38 +00:00
}
2014-12-12 15:49:22 +00:00
bool Actors : : isReadyToBlock ( const MWWorld : : Ptr & ptr ) const
{
2014-12-21 15:45:30 +00:00
PtrActorMap : : const_iterator it = mActors . find ( ptr ) ;
2014-12-12 15:49:22 +00:00
if ( it = = mActors . end ( ) )
return false ;
2014-12-21 15:45:30 +00:00
return it - > second - > getCharacterController ( ) - > isReadyToBlock ( ) ;
2014-12-12 15:49:22 +00:00
}
2014-12-31 17:41:57 +00:00
void Actors : : fastForwardAi ( )
{
if ( ! MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > isAIActive ( ) )
return ;
2015-01-01 17:11:37 +00:00
// making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator
PtrActorMap map = mActors ;
for ( PtrActorMap : : iterator it = map . begin ( ) ; it ! = map . end ( ) ; + + it )
2014-12-31 17:41:57 +00:00
{
MWWorld : : Ptr ptr = it - > first ;
2015-08-21 09:12:39 +00:00
if ( ptr = = getPlayer ( )
2015-01-13 17:09:10 +00:00
| | ! isConscious ( ptr )
2015-08-20 06:12:37 +00:00
| | ptr . getClass ( ) . getCreatureStats ( ptr ) . isParalyzed ( ) )
2014-12-31 17:41:57 +00:00
continue ;
MWMechanics : : AiSequence & seq = ptr . getClass ( ) . getCreatureStats ( ptr ) . getAiSequence ( ) ;
seq . fastForward ( ptr , it - > second - > getAiState ( ) ) ;
}
}
2012-03-30 14:18:58 +00:00
}