@ -56,14 +56,17 @@ bool isConscious(const MWWorld::Ptr& ptr)
bool isCommanded ( const MWWorld : : Ptr & actor )
{
const auto & stats = actor . getClass ( ) . getCreatureStats ( actor ) ;
for ( const auto & params : stats . getActiveSpells ( ) )
const auto & actorClass = actor . getClass ( ) ;
const auto & stats = actorClass . getCreatureStats ( actor ) ;
const bool isActorNpc = actorClass . isNpc ( ) ;
const auto level = stats . getLevel ( ) ;
for ( const auto & params : stats . getActiveSpells ( ) )
{
for ( const auto & effect : params . getEffects ( ) )
for ( const auto & effect : params . getEffects ( ) )
{
if ( ( ( effect . mEffectId = = ESM : : MagicEffect : : CommandHumanoid & & actor. getClass ( ) . isNpc ( ) )
| | ( effect . mEffectId = = ESM : : MagicEffect : : CommandCreature & & ! actor. getClass ( ) . isNpc ( ) ) )
& & effect . mMagnitude > = stats. getLevel ( ) )
if ( ( ( effect . mEffectId = = ESM : : MagicEffect : : CommandHumanoid & & isActorNpc )
| | ( effect . mEffectId = = ESM : : MagicEffect : : CommandCreature & & ! isActorNpc ) )
& & effect . mMagnitude > = level )
return true ;
}
}
@ -246,7 +249,8 @@ namespace MWMechanics
MWWorld : : Ptr & headTrackTarget , float & sqrHeadTrackDistance ,
bool inCombatOrPursue )
{
if ( ! actor . getRefData ( ) . getBaseNode ( ) )
const auto & actorRefData = actor . getRefData ( ) ;
if ( ! actorRefData . getBaseNode ( ) )
return ;
if ( targetActor . getClass ( ) . getCreatureStats ( targetActor ) . isDead ( ) )
@ -261,7 +265,7 @@ namespace MWMechanics
if ( ! currentCell - > isExterior ( ) & & ! ( currentCell - > mData . mFlags & ESM : : Cell : : QuasiEx ) )
maxDistance * = fInteriorHeadTrackMult ;
const osg : : Vec3f actor1Pos ( actor . get RefData( ) . getPosition ( ) . asVec3 ( ) ) ;
const osg : : Vec3f actor1Pos ( actor RefData. getPosition ( ) . asVec3 ( ) ) ;
const osg : : Vec3f actor2Pos ( targetActor . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
float sqrDist = ( actor1Pos - actor2Pos ) . length2 ( ) ;
@ -269,7 +273,7 @@ namespace MWMechanics
return ;
// stop tracking when target is behind the actor
osg : : Vec3f actorDirection = actor . get RefData( ) . getBaseNode ( ) - > getAttitude ( ) * osg : : Vec3f ( 0 , 1 , 0 ) ;
osg : : Vec3f actorDirection = actor RefData. getBaseNode ( ) - > getAttitude ( ) * osg : : Vec3f ( 0 , 1 , 0 ) ;
osg : : Vec3f targetDirection ( actor2Pos - actor1Pos ) ;
actorDirection . z ( ) = 0 ;
targetDirection . z ( ) = 0 ;
@ -314,8 +318,9 @@ namespace MWMechanics
if ( mSmoothMovement )
return ;
CreatureStats & stats = actor . getClass ( ) . getCreatureStats ( actor ) ;
MWMechanics : : AiSequence & seq = stats . getAiSequence ( ) ;
const auto & actorClass = actor . getClass ( ) ;
const CreatureStats & stats = actorClass . getCreatureStats ( actor ) ;
const MWMechanics : : AiSequence & seq = stats . getAiSequence ( ) ;
if ( ! seq . isEmpty ( ) & & seq . getActivePackage ( ) . useVariableSpeed ( ) )
{
@ -326,7 +331,7 @@ namespace MWMechanics
if ( distance < DECELERATE_DISTANCE )
{
float speedCoef = std : : max ( 0.7f , 0.2f + 0.8f * distance / DECELERATE_DISTANCE ) ;
auto & movement = actor . get Class( ) . getMovementSettings ( actor ) ;
auto & movement = actor Class. getMovementSettings ( actor ) ;
movement . mPosition [ 0 ] * = speedCoef ;
movement . mPosition [ 1 ] * = speedCoef ;
}
@ -335,11 +340,12 @@ namespace MWMechanics
void Actors : : updateGreetingState ( const MWWorld : : Ptr & actor , Actor & actorState , bool turnOnly )
{
if ( ! actor . getClass ( ) . isActor ( ) | | actor = = getPlayer ( ) )
const auto & actorClass = actor . getClass ( ) ;
if ( ! actorClass . isActor ( ) | | actor = = getPlayer ( ) )
return ;
CreatureStats & stats = actor . getClass ( ) . getCreatureStats ( actor ) ;
const MWMechanics : : AiSequence & seq = s tats. getAiSequence ( ) ;
const CreatureStats & actorStats = actorClass . getCreatureStats ( actor ) ;
const MWMechanics : : AiSequence & seq = actorS tats. getAiSequence ( ) ;
const auto packageId = seq . getTypeId ( ) ;
if ( seq . isInCombat ( ) | |
@ -366,7 +372,7 @@ namespace MWMechanics
{
actorState . setTurningToPlayer ( false ) ;
// An original engine launches an endless idle2 when an actor greets player.
playAnimationGroup ( actor , " idle2 " , 0 , std : : numeric_limits < int > : : max ( ) , false ) ;
playAnimationGroup ( actor , " idle2 " , 0 , std : : numeric_limits < int > : : max ( ) , false ) ;
}
}
@ -377,14 +383,15 @@ namespace MWMechanics
static int iGreetDistanceMultiplier = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( )
. get < ESM : : GameSetting > ( ) . find ( " iGreetDistanceMultiplier " ) - > mValue . getInteger ( ) ;
float helloDistance = static_cast < float > ( stats . getAiSetting ( CreatureStats : : AI_Hello ) . getModified ( ) * iGreetDistanceMultiplier ) ;
float helloDistance = static_cast < float > ( actorStats . getAiSetting ( CreatureStats : : AI_Hello ) . getModified ( ) * iGreetDistanceMultiplier ) ;
const auto & playerStats = player . getClass ( ) . getCreatureStats ( player ) ;
int greetingTimer = actorState . getGreetingTimer ( ) ;
GreetingState greetingState = actorState . getGreetingState ( ) ;
if ( greetingState = = Greet_None )
{
if ( ( playerPos - actorPos ) . length2 ( ) < = helloDistance * helloDistance & &
! player . getClass ( ) . getCreature Stats( player ) . isDead ( ) & & ! actor . getClass ( ) . getCreature Stats( actor ) . isParalyzed ( )
if ( ( playerPos - actorPos ) . length2 ( ) < = helloDistance * helloDistance & &
! player Stats. isDead ( ) & & ! actor Stats. isParalyzed ( )
& & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( player , actor )
& & MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > awarenessCheck ( player , actor ) )
greetingTimer + + ;
@ -401,7 +408,7 @@ namespace MWMechanics
{
greetingTimer + + ;
if ( ! s tats. getMovementFlag ( CreatureStats : : Flag_ForceJump ) & & ! s tats. getMovementFlag ( CreatureStats : : Flag_ForceSneak )
if ( ! actorS tats. getMovementFlag ( CreatureStats : : Flag_ForceJump ) & & ! actorS tats. getMovementFlag ( CreatureStats : : Flag_ForceSneak )
& & ( greetingTimer < = GREETING_SHOULD_END | | MWBase : : Environment : : get ( ) . getSoundManager ( ) - > sayActive ( actor ) ) )
turnActorToFacePlayer ( actor , actorState , dir ) ;
@ -415,7 +422,7 @@ namespace MWMechanics
if ( greetingState = = Greet_Done )
{
float resetDist = 2 * helloDistance ;
if ( ( playerPos - actorPos ) . length2 ( ) > = resetDist * resetDist )
if ( ( playerPos - actorPos ) . length2 ( ) > = resetDist * resetDist )
greetingState = Greet_None ;
}
@ -425,8 +432,9 @@ namespace MWMechanics
void Actors : : turnActorToFacePlayer ( const MWWorld : : Ptr & actor , Actor & actorState , const osg : : Vec3f & dir )
{
actor . getClass ( ) . getMovementSettings ( actor ) . mPosition [ 1 ] = 0 ;
actor . getClass ( ) . getMovementSettings ( actor ) . mPosition [ 0 ] = 0 ;
auto & movementSettings = actor . getClass ( ) . getMovementSettings ( actor ) ;
movementSettings . mPosition [ 1 ] = 0 ;
movementSettings . mPosition [ 0 ] = 0 ;
if ( ! actorState . isTurningToPlayer ( ) )
{
@ -457,7 +465,7 @@ namespace MWMechanics
}
}
void Actors : : engageCombat ( const MWWorld : : Ptr & actor1 , const MWWorld : : Ptr & actor2 , std : : map < const MWWorld : : Ptr , const std : : set < MWWorld : : Ptr > > & cachedAllies , bool againstPlayer )
void Actors : : engageCombat ( const MWWorld : : Ptr & actor1 , const MWWorld : : Ptr & actor2 , std : : map < const MWWorld : : Ptr , const std : : set < MWWorld : : Ptr > > & cachedAllies , bool againstPlayer )
{
// No combat for totally static creatures
if ( ! actor1 . getClass ( ) . isMobile ( actor1 ) )
@ -473,9 +481,9 @@ namespace MWMechanics
const osg : : Vec3f actor1Pos ( actor1 . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
const osg : : Vec3f actor2Pos ( actor2 . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
float sqrDist = ( actor1Pos - actor2Pos ) . length2 ( ) ;
const float sqrDist = ( actor1Pos - actor2Pos ) . length2 ( ) ;
if ( sqrDist > mActorsProcessingRange * mActorsProcessingRange )
if ( sqrDist > mActorsProcessingRange * mActorsProcessingRange )
return ;
// If this is set to true, actor1 will start combat with actor2 if the awareness check at the end of the method returns true
@ -487,15 +495,16 @@ namespace MWMechanics
getActorsSidingWith ( actor1 , allies1 , cachedAllies ) ;
auto * mechanicsManager = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) ;
// If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and actor2
for ( const MWWorld : : Ptr & ally : allies1 )
for ( const MWWorld : : Ptr & ally : allies1 )
{
if ( creatureStats1 . getAiSequence ( ) . isInCombat ( ally ) )
continue ;
if ( creatureStats2 . matchesActorId ( ally . getClass ( ) . getCreatureStats ( ally ) . getHitAttemptActorId ( ) ) )
{
MWBase: : Environ ment: : get ( ) . getMe chanicsManager( ) - > startCombat ( actor1 , actor2 ) ;
mechanicsManager- > startCombat ( actor1 , actor2 ) ;
// Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat
// if the player gets out of reach, while the ally would continue combat with the player
creatureStats1 . setHitAttemptActorId ( ally . getClass ( ) . getCreatureStats ( ally ) . getHitAttemptActorId ( ) ) ;
@ -517,21 +526,21 @@ namespace MWMechanics
if ( ! aggressive & & ! isPlayerFollowerOrEscorter )
{
// Check that actor2 is in combat with actor1
if ( a ctor2. getClass ( ) . getC reatureStats( actor 2) . getAiSequence ( ) . isInCombat ( actor1 ) )
if ( creatureStats2. getAiSequence ( ) . isInCombat ( actor1 ) )
{
std : : set < MWWorld : : Ptr > allies2 ;
getActorsSidingWith ( actor2 , allies2 , cachedAllies ) ;
// Check that an ally of actor2 is also in combat with actor1
for ( const MWWorld : : Ptr & ally2 : allies2 )
for ( const MWWorld : : Ptr & ally2 : allies2 )
{
if ( ally2 . getClass ( ) . getCreatureStats ( ally2 ) . getAiSequence ( ) . isInCombat ( actor1 ) )
{
MWBase: : Environ ment: : get ( ) . getMe chanicsManager( ) - > startCombat ( actor1 , actor2 ) ;
mechanicsManager- > startCombat ( actor1 , actor2 ) ;
// Also have actor1's allies start combat
for ( const MWWorld : : Ptr & ally1 : allies1 )
MWBase: : Environ ment: : get ( ) . getMe chanicsManager( ) - > startCombat ( ally1 , actor2 ) ;
mechanicsManager- > startCombat ( ally1 , actor2 ) ;
return ;
}
}
@ -546,13 +555,13 @@ namespace MWMechanics
static const bool followersAttackOnSight = Settings : : Manager : : getBool ( " followers attack on sight " , " Game " ) ;
if ( ! aggressive & & isPlayerFollowerOrEscorter & & followersAttackOnSight )
{
if ( a ctor2. getClass ( ) . getC reatureStats( actor 2) . getAiSequence ( ) . isInCombat ( actor1 ) )
if ( creatureStats2. getAiSequence ( ) . isInCombat ( actor1 ) )
aggressive = true ;
else
{
for ( const MWWorld : : Ptr & ally : allies1 )
for ( const MWWorld : : Ptr & ally : allies1 )
{
if ( a ctor2. getClass ( ) . getC reatureStats( actor 2) . getAiSequence ( ) . isInCombat ( ally ) )
if ( creatureStats2. getAiSequence ( ) . isInCombat ( ally ) )
{
aggressive = true ;
break ;
@ -569,15 +578,16 @@ namespace MWMechanics
// Player followers and escorters with high fight should not initiate combat with the player or with
// other player followers or escorters
if ( ! isPlayerFollowerOrEscorter )
aggressive = MWBase: : Environ ment: : get ( ) . getMe chanicsManager( ) - > isAggressive ( actor1 , actor2 ) ;
aggressive = mechanicsManager- > isAggressive ( actor1 , actor2 ) ;
}
}
// Make guards go aggressive with creatures that are in combat, unless the creature is a follower or escorter
auto * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
if ( ! aggressive & & actor1 . getClass ( ) . isClass ( actor1 , " Guard " ) & & ! actor2 . getClass ( ) . isNpc ( ) & & creatureStats2 . getAiSequence ( ) . isInCombat ( ) )
{
// Check if the creature is too far
static const float fAlarmRadius = MWBase: : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fAlarmRadius " ) - > mValue . getFloat ( ) ;
static const float fAlarmRadius = world - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fAlarmRadius " ) - > mValue . getFloat ( ) ;
if ( sqrDist > fAlarmRadius * fAlarmRadius )
return ;
@ -600,11 +610,11 @@ namespace MWMechanics
// If any of the above conditions turned actor1 aggressive towards actor2, do an awareness check. If it passes, start combat with actor2.
if ( aggressive )
{
bool LOS = MWBase: : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor1 , actor2 )
& & MWBase: : Environ ment: : get ( ) . getMe chanicsManager( ) - > awarenessCheck ( actor2 , actor1 ) ;
bool LOS = world - > getLOS ( actor1 , actor2 ) & &
mechanicsManager- > awarenessCheck ( actor2 , actor1 ) ;
if ( LOS )
MWBase: : Environ ment: : get ( ) . getMe chanicsManager( ) - > startCombat ( actor1 , actor2 ) ;
mechanicsManager- > startCombat ( actor1 , actor2 ) ;
}
}
@ -791,7 +801,8 @@ namespace MWMechanics
void Actors : : updateDrowning ( const MWWorld : : Ptr & ptr , float duration , bool isKnockedOut , bool isPlayer )
{
NpcStats & stats = ptr . getClass ( ) . getNpcStats ( ptr ) ;
auto & actorClass = ptr . getClass ( ) ;
NpcStats & stats = actorClass . getNpcStats ( ptr ) ;
// When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST
static const float fHoldBreathTime = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fHoldBreathTime " ) - > mValue . getFloat ( ) ;
@ -800,43 +811,43 @@ namespace MWMechanics
if ( ! isPlayer & & stats . getTimeToStartDrowning ( ) < fHoldBreathTime / 2 )
{
AiSequence & seq = ptr. getClass ( ) . getCreatureStats ( ptr ) . getAiSequence ( ) ;
AiSequence & seq = actorClass . getCreatureStats ( ptr ) . getAiSequence ( ) ;
if ( seq . getTypeId ( ) ! = AiPackageTypeId : : Breathe ) //Only add it once
seq . stack ( AiBreathe ( ) , ptr ) ;
}
MWBase : : World * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
MWBase : : World * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
bool knockedOutUnderwater = ( isKnockedOut & & world - > isUnderwater ( ptr . getCell ( ) , osg : : Vec3f ( ptr . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ) ) ;
if ( ( world - > isSubmerged ( ptr ) | | knockedOutUnderwater )
& & stats . getMagicEffects ( ) . get ( ESM : : MagicEffect : : WaterBreathing ) . getMagnitude ( ) = = 0 )
if ( ( world - > isSubmerged ( ptr ) | | knockedOutUnderwater )
& & stats . getMagicEffects ( ) . get ( ESM : : MagicEffect : : WaterBreathing ) . getMagnitude ( ) = = 0 )
{
float timeLeft = 0.0f ;
if ( knockedOutUnderwater )
if ( knockedOutUnderwater )
stats . setTimeToStartDrowning ( 0 ) ;
else
{
timeLeft = stats . getTimeToStartDrowning ( ) - duration ;
if ( timeLeft < 0.0f )
if ( timeLeft < 0.0f )
timeLeft = 0.0f ;
stats . setTimeToStartDrowning ( timeLeft ) ;
}
bool godmode = isPlayer & & MWBase: : Environment : : get ( ) . getWorld ( ) - > getGodModeState ( ) ;
bool godmode = isPlayer & & world - > getGodModeState ( ) ;
if ( timeLeft = = 0.0f & & ! godmode )
if ( timeLeft = = 0.0f & & ! godmode )
{
// If drowning, apply 3 points of damage per second
static const float fSuffocationDamage = world - > getStore ( ) . get < ESM : : GameSetting > ( ) . find ( " fSuffocationDamage " ) - > mValue . getFloat ( ) ;
DynamicStat < float > health = stats . getHealth ( ) ;
health . setCurrent ( health . getCurrent ( ) - fSuffocationDamage * duration ) ;
health . setCurrent ( health . getCurrent ( ) - fSuffocationDamage * duration ) ;
stats . setHealth ( health ) ;
// Play a drowning sound
MWBase : : SoundManager * sndmgr = MWBase : : Environment : : get ( ) . getSoundManager ( ) ;
if ( ! sndmgr - > getSoundPlaying ( ptr , " drown " ) )
MWBase : : SoundManager * sndmgr = MWBase : : Environment : : get ( ) . getSoundManager ( ) ;
if ( ! sndmgr - > getSoundPlaying ( ptr , " drown " ) )
sndmgr - > playSound3D ( ptr , " drown " , 1.0f , 1.0f ) ;
if ( isPlayer )
if ( isPlayer )
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > activateHitOverlay ( false ) ;
}
}
@ -844,34 +855,30 @@ namespace MWMechanics
stats . setTimeToStartDrowning ( fHoldBreathTime ) ;
}
void Actors : : updateEquippedLight ( const MWWorld : : Ptr & ptr , float duration , bool mayEquip )
void Actors : : updateEquippedLight ( const MWWorld : : Ptr & ptr , float duration , bool mayEquip )
{
bool isPlayer = ( ptr = = getPlayer ( ) ) ;
const bool isPlayer = ( ptr = = getPlayer ( ) ) ;
auto & actorClass = ptr . getClass ( ) ;
auto & inventoryStore = actorClass . getInventoryStore ( ptr ) ;
auto heldIter = inventoryStore . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedLeft ) ;
MWWorld : : InventoryStore & inventoryStore = ptr . getClass ( ) . getInventoryStore ( ptr ) ;
MWWorld : : ContainerStoreIterator heldIter =
inventoryStore . getSlot ( MWWorld : : InventoryStore : : Slot_CarriedLeft ) ;
/**
* Automatically equip NPCs torches at night and unequip them at day
*/
if ( ! isPlayer )
{
MWWorld : : ContainerStoreIterator torch = inventoryStore . end ( ) ;
for ( MWWorld : : ContainerStoreIterator it = inventoryStore . begin ( ) ; it ! = inventoryStore . end ( ) ; + + it )
{
if ( it - > getType ( ) = = ESM : : Light : : sRecordId & &
it - > getClass ( ) . canBeEquipped ( * it , ptr ) . first )
auto torchIter = std : : find_if ( std : : begin ( inventoryStore ) , std : : end ( inventoryStore ) , [ & ] ( auto entry )
{
torch = it ;
break ;
}
}
return entry . getType ( ) = = ESM : : Light : : sRecordId & &
entry . getClass ( ) . canBeEquipped ( entry , ptr ) . first ;
} ) ;
if ( mayEquip )
{
if ( torch ! = inventoryStore . end ( ) )
if ( torch Iter ! = inventoryStore . end ( ) )
{
if ( ! ptr. getClass ( ) . getCreatureStats ( ptr ) . getAiSequence ( ) . isInCombat ( ) )
if ( ! actorClass. getCreatureStats ( ptr ) . getAiSequence ( ) . isInCombat ( ) )
{
// For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.
if ( heldIter ! = inventoryStore . end ( ) & & heldIter - > getType ( ) ! = ESM : : Light : : sRecordId )
@ -881,7 +888,7 @@ namespace MWMechanics
{
// For hostile NPCs, see if they have anything better to equip first
auto shield = inventoryStore . getPreferredShield ( ptr ) ;
if ( shield ! = inventoryStore . end ( ) )
if ( shield ! = inventoryStore . end ( ) )
inventoryStore . equip ( MWWorld : : InventoryStore : : Slot_CarriedLeft , shield , ptr ) ;
}
@ -890,7 +897,7 @@ namespace MWMechanics
// If we have a torch and can equip it, then equip it now.
if ( heldIter = = inventoryStore . end ( ) )
{
inventoryStore . equip ( MWWorld : : InventoryStore : : Slot_CarriedLeft , torch , ptr ) ;
inventoryStore . equip ( MWWorld : : InventoryStore : : Slot_CarriedLeft , torch Iter , ptr ) ;
}
}
}
@ -910,11 +917,12 @@ namespace MWMechanics
//If holding a light...
if ( heldIter . getType ( ) = = MWWorld : : ContainerStore : : Type_Light )
{
auto * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
// Use time from the player's light
if ( isPlayer )
{
// But avoid using it up if the light source is hidden
MWRender : : Animation * anim = MWBase: : Environment : : get ( ) . getWorld ( ) - > getAnimation ( ptr ) ;
MWRender : : Animation * anim = world - > getAnimation ( ptr ) ;
if ( anim & & anim - > getCarriedLeftShown ( ) )
{
float timeRemaining = heldIter - > getClass ( ) . getRemainingUsageTime ( * heldIter ) ;
@ -935,7 +943,7 @@ namespace MWMechanics
}
// Both NPC and player lights extinguish in water.
if ( MWBase: : Environment : : get ( ) . getWorld ( ) - > isSwimming ( ptr ) )
if ( world - > isSwimming ( ptr ) )
{
inventoryStore . remove ( * heldIter , 1 , ptr ) ; // remove it
@ -950,59 +958,68 @@ namespace MWMechanics
void Actors : : updateCrimePursuit ( const MWWorld : : Ptr & ptr , float duration )
{
MWWorld : : Ptr player = getPlayer ( ) ;
if ( ptr ! = player & & ptr . getClass ( ) . isNpc ( ) )
{
// get stats of witness
CreatureStats & creatureStats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
NpcStats & npcStats = ptr . getClass ( ) . getNpcStats ( ptr ) ;
if ( ptr = = player )
return ;
if ( player . getClass ( ) . getNpcStats ( player ) . isWerewolf ( ) )
return ;
auto & actorClass = ptr . getClass ( ) ;
if ( ! actorClass . isNpc ( ) )
return ;
// get stats of witness
CreatureStats & creatureStats = ptr . getClass ( ) . getCreatureStats ( ptr ) ;
NpcStats & npcStats = ptr . getClass ( ) . getNpcStats ( ptr ) ;
if ( ptr . getClass ( ) . isClass ( ptr , " Guard " ) & & creatureStats . getAiSequence ( ) . getTypeId ( ) ! = AiPackageTypeId : : Pursue & & ! creatureStats . getAiSequence ( ) . isInCombat ( )
& & creatureStats . getMagicEffects ( ) . get ( ESM : : MagicEffect : : CalmHumanoid ) . getMagnitude ( ) = = 0 )
const auto & playerClass = player . getClass ( ) ;
const auto & playerStats = playerClass . getNpcStats ( player ) ;
if ( playerStats . isWerewolf ( ) )
return ;
auto * mechanicsManager = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) ;
auto * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
if ( actorClass . isClass ( ptr , " Guard " ) & & creatureStats . getAiSequence ( ) . isInPursuit ( ) & & ! creatureStats . getAiSequence ( ) . isInCombat ( )
& & creatureStats . getMagicEffects ( ) . get ( ESM : : MagicEffect : : CalmHumanoid ) . getMagnitude ( ) = = 0 )
{
const MWWorld : : ESMStore & esmStore = world - > getStore ( ) ;
static const int cutoff = esmStore . get < ESM : : GameSetting > ( ) . find ( " iCrimeThreshold " ) - > mValue . getInteger ( ) ;
// 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)
if ( playerStats . getBounty ( ) > = cutoff
// TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?
& & world - > getLOS ( ptr , player )
& & mechanicsManager - > awarenessCheck ( player , ptr ) )
{
const MWWorld : : ESMStore & esmStore = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) ;
static const int cutoff = esmStore . get < ESM : : GameSetting > ( ) . find ( " iCrimeThreshold " ) - > mValue . getInteger ( ) ;
// 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)
if ( player . getClass ( ) . getNpcStats ( player ) . getBounty ( ) > = cutoff
// TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?
& & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( ptr , player )
& & MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > awarenessCheck ( player , ptr ) )
static const int iCrimeThresholdMultiplier = esmStore . get < ESM : : GameSetting > ( ) . find ( " iCrimeThresholdMultiplier " ) - > mValue . getInteger ( ) ;
if ( playerStats . getBounty ( ) > = cutoff * iCrimeThresholdMultiplier )
{
static const int iCrimeThresholdMultiplier = esmStore . get < ESM : : GameSetting > ( ) . find ( " iCrimeThresholdMultiplier " ) - > mValue . getInteger ( ) ;
if ( player . getClass ( ) . getNpcStats ( player ) . getBounty ( ) > = cutoff * iCrimeThresholdMultiplier )
{
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > startCombat ( ptr , player ) ;
creatureStats . setHitAttemptActorId ( player . getClass ( ) . getCreatureStats ( player ) . getActorId ( ) ) ; // Stops the guard from quitting combat if player is unreachable
}
else
creatureStats . getAiSequence ( ) . stack ( AiPursue ( player ) , ptr ) ;
creatureStats . setAlarmed ( true ) ;
npcStats . setCrimeId ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayer ( ) . getNewCrimeId ( ) ) ;
mechanicsManager - > startCombat ( ptr , player ) ;
creatureStats . setHitAttemptActorId ( playerClass . getCreatureStats ( player ) . getActorId ( ) ) ; // Stops the guard from quitting combat if player is unreachable
}
else
creatureStats . getAiSequence ( ) . stack ( AiPursue ( player ) , ptr ) ;
creatureStats . setAlarmed ( true ) ;
npcStats . setCrimeId ( world - > getPlayer ( ) . getNewCrimeId ( ) ) ;
}
}
// if I was a witness to a crime
if ( npcStats . getCrimeId ( ) ! = - 1 )
// if I was a witness to a crime
if ( npcStats . getCrimeId ( ) ! = - 1 )
{
// if you've paid for your crimes and I havent noticed
if ( npcStats . getCrimeId ( ) < = world - > getPlayer ( ) . getCrimeId ( ) )
{
// if you've paid for your crimes and I havent noticed
if ( npcStats . getCrimeId ( ) < = MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayer ( ) . getCrimeId ( ) )
{
// Calm witness down
if ( ptr . getClass ( ) . isClass ( ptr , " Guard " ) )
creatureStats . getAiSequence ( ) . stopPursuit ( ) ;
stopCombat ( ptr ) ;
// Reset factors to attack
creatureStats . setAttacked ( false ) ;
creatureStats . setAlarmed ( false ) ;
creatureStats . setAiSetting ( CreatureStats : : AI_Fight , ptr . getClass ( ) . getBaseFightRating ( ptr ) ) ;
// Update witness crime id
npcStats . setCrimeId ( - 1 ) ;
}
// Calm witness down
if ( ptr . getClass ( ) . isClass ( ptr , " Guard " ) )
creatureStats . getAiSequence ( ) . stopPursuit ( ) ;
stopCombat ( ptr ) ;
// Reset factors to attack
creatureStats . setAttacked ( false ) ;
creatureStats . setAlarmed ( false ) ;
creatureStats . setAiSetting ( CreatureStats : : AI_Fight , ptr . getClass ( ) . getBaseFightRating ( ptr ) ) ;
// Update witness crime id
npcStats . setCrimeId ( - 1 ) ;
}
}
}