mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 07:53:53 +00:00
Merge branch 'refactor/6677-1' into 'master'
#6677: Reduce some calls and re-structure functions See merge request OpenMW/openmw!1729
This commit is contained in:
commit
9ff34da59e
3 changed files with 137 additions and 120 deletions
|
@ -56,14 +56,17 @@ bool isConscious(const MWWorld::Ptr& ptr)
|
||||||
|
|
||||||
bool isCommanded(const MWWorld::Ptr& actor)
|
bool isCommanded(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
const auto& stats = actor.getClass().getCreatureStats(actor);
|
const auto& actorClass = actor.getClass();
|
||||||
for(const auto& params : stats.getActiveSpells())
|
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())
|
if (((effect.mEffectId == ESM::MagicEffect::CommandHumanoid && isActorNpc)
|
||||||
|| (effect.mEffectId == ESM::MagicEffect::CommandCreature && !actor.getClass().isNpc()))
|
|| (effect.mEffectId == ESM::MagicEffect::CommandCreature && !isActorNpc))
|
||||||
&& effect.mMagnitude >= stats.getLevel())
|
&& effect.mMagnitude >= level)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,7 +249,8 @@ namespace MWMechanics
|
||||||
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,
|
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,
|
||||||
bool inCombatOrPursue)
|
bool inCombatOrPursue)
|
||||||
{
|
{
|
||||||
if (!actor.getRefData().getBaseNode())
|
const auto& actorRefData = actor.getRefData();
|
||||||
|
if (!actorRefData.getBaseNode())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (targetActor.getClass().getCreatureStats(targetActor).isDead())
|
if (targetActor.getClass().getCreatureStats(targetActor).isDead())
|
||||||
|
@ -261,7 +265,7 @@ namespace MWMechanics
|
||||||
if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx))
|
if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx))
|
||||||
maxDistance *= fInteriorHeadTrackMult;
|
maxDistance *= fInteriorHeadTrackMult;
|
||||||
|
|
||||||
const osg::Vec3f actor1Pos(actor.getRefData().getPosition().asVec3());
|
const osg::Vec3f actor1Pos(actorRefData.getPosition().asVec3());
|
||||||
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
|
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
|
||||||
float sqrDist = (actor1Pos - actor2Pos).length2();
|
float sqrDist = (actor1Pos - actor2Pos).length2();
|
||||||
|
|
||||||
|
@ -269,7 +273,7 @@ namespace MWMechanics
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// stop tracking when target is behind the actor
|
// stop tracking when target is behind the actor
|
||||||
osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
|
osg::Vec3f actorDirection = actorRefData.getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
|
||||||
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
|
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
|
||||||
actorDirection.z() = 0;
|
actorDirection.z() = 0;
|
||||||
targetDirection.z() = 0;
|
targetDirection.z() = 0;
|
||||||
|
@ -314,8 +318,9 @@ namespace MWMechanics
|
||||||
if (mSmoothMovement)
|
if (mSmoothMovement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
const auto& actorClass = actor.getClass();
|
||||||
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
const CreatureStats &stats = actorClass.getCreatureStats(actor);
|
||||||
|
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||||
|
|
||||||
if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed())
|
if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed())
|
||||||
{
|
{
|
||||||
|
@ -326,7 +331,7 @@ namespace MWMechanics
|
||||||
if (distance < DECELERATE_DISTANCE)
|
if (distance < DECELERATE_DISTANCE)
|
||||||
{
|
{
|
||||||
float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE);
|
float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE);
|
||||||
auto& movement = actor.getClass().getMovementSettings(actor);
|
auto& movement = actorClass.getMovementSettings(actor);
|
||||||
movement.mPosition[0] *= speedCoef;
|
movement.mPosition[0] *= speedCoef;
|
||||||
movement.mPosition[1] *= speedCoef;
|
movement.mPosition[1] *= speedCoef;
|
||||||
}
|
}
|
||||||
|
@ -335,11 +340,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly)
|
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;
|
return;
|
||||||
|
|
||||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
const CreatureStats& actorStats = actorClass.getCreatureStats(actor);
|
||||||
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
const MWMechanics::AiSequence& seq = actorStats.getAiSequence();
|
||||||
const auto packageId = seq.getTypeId();
|
const auto packageId = seq.getTypeId();
|
||||||
|
|
||||||
if (seq.isInCombat() ||
|
if (seq.isInCombat() ||
|
||||||
|
@ -366,7 +372,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
actorState.setTurningToPlayer(false);
|
actorState.setTurningToPlayer(false);
|
||||||
// An original engine launches an endless idle2 when an actor greets player.
|
// 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()
|
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
||||||
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
.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();
|
int greetingTimer = actorState.getGreetingTimer();
|
||||||
GreetingState greetingState = actorState.getGreetingState();
|
GreetingState greetingState = actorState.getGreetingState();
|
||||||
if (greetingState == Greet_None)
|
if (greetingState == Greet_None)
|
||||||
{
|
{
|
||||||
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
if ((playerPos - actorPos).length2() <= helloDistance * helloDistance &&
|
||||||
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
!playerStats.isDead() && !actorStats.isParalyzed()
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
||||||
greetingTimer++;
|
greetingTimer++;
|
||||||
|
@ -401,7 +408,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
greetingTimer++;
|
greetingTimer++;
|
||||||
|
|
||||||
if (!stats.getMovementFlag(CreatureStats::Flag_ForceJump) && !stats.getMovementFlag(CreatureStats::Flag_ForceSneak)
|
if (!actorStats.getMovementFlag(CreatureStats::Flag_ForceJump) && !actorStats.getMovementFlag(CreatureStats::Flag_ForceSneak)
|
||||||
&& (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)))
|
&& (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)))
|
||||||
turnActorToFacePlayer(actor, actorState, dir);
|
turnActorToFacePlayer(actor, actorState, dir);
|
||||||
|
|
||||||
|
@ -415,7 +422,7 @@ namespace MWMechanics
|
||||||
if (greetingState == Greet_Done)
|
if (greetingState == Greet_Done)
|
||||||
{
|
{
|
||||||
float resetDist = 2 * helloDistance;
|
float resetDist = 2 * helloDistance;
|
||||||
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
if ((playerPos - actorPos).length2() >= resetDist * resetDist)
|
||||||
greetingState = Greet_None;
|
greetingState = Greet_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,8 +432,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir)
|
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir)
|
||||||
{
|
{
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
auto& movementSettings = actor.getClass().getMovementSettings(actor);
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
movementSettings.mPosition[1] = 0;
|
||||||
|
movementSettings.mPosition[0] = 0;
|
||||||
|
|
||||||
if (!actorState.isTurningToPlayer())
|
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
|
// No combat for totally static creatures
|
||||||
if (!actor1.getClass().isMobile(actor1))
|
if (!actor1.getClass().isMobile(actor1))
|
||||||
|
@ -473,9 +481,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
const osg::Vec3f actor1Pos(actor1.getRefData().getPosition().asVec3());
|
const osg::Vec3f actor1Pos(actor1.getRefData().getPosition().asVec3());
|
||||||
const osg::Vec3f actor2Pos(actor2.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;
|
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
|
// 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);
|
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
|
// 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))
|
if (creatureStats1.getAiSequence().isInCombat(ally))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))
|
if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
mechanicsManager->startCombat(actor1, actor2);
|
||||||
// Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat
|
// 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
|
// if the player gets out of reach, while the ally would continue combat with the player
|
||||||
creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());
|
creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());
|
||||||
|
@ -517,21 +526,21 @@ namespace MWMechanics
|
||||||
if (!aggressive && !isPlayerFollowerOrEscorter)
|
if (!aggressive && !isPlayerFollowerOrEscorter)
|
||||||
{
|
{
|
||||||
// Check that actor2 is in combat with actor1
|
// Check that actor2 is in combat with actor1
|
||||||
if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1))
|
if (creatureStats2.getAiSequence().isInCombat(actor1))
|
||||||
{
|
{
|
||||||
std::set<MWWorld::Ptr> allies2;
|
std::set<MWWorld::Ptr> allies2;
|
||||||
|
|
||||||
getActorsSidingWith(actor2, allies2, cachedAllies);
|
getActorsSidingWith(actor2, allies2, cachedAllies);
|
||||||
|
|
||||||
// Check that an ally of actor2 is also in combat with actor1
|
// 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))
|
if (ally2.getClass().getCreatureStats(ally2).getAiSequence().isInCombat(actor1))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
mechanicsManager->startCombat(actor1, actor2);
|
||||||
// Also have actor1's allies start combat
|
// Also have actor1's allies start combat
|
||||||
for (const MWWorld::Ptr& ally1 : allies1)
|
for (const MWWorld::Ptr& ally1 : allies1)
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ally1, actor2);
|
mechanicsManager->startCombat(ally1, actor2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,13 +555,13 @@ namespace MWMechanics
|
||||||
static const bool followersAttackOnSight = Settings::Manager::getBool("followers attack on sight", "Game");
|
static const bool followersAttackOnSight = Settings::Manager::getBool("followers attack on sight", "Game");
|
||||||
if (!aggressive && isPlayerFollowerOrEscorter && followersAttackOnSight)
|
if (!aggressive && isPlayerFollowerOrEscorter && followersAttackOnSight)
|
||||||
{
|
{
|
||||||
if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1))
|
if (creatureStats2.getAiSequence().isInCombat(actor1))
|
||||||
aggressive = true;
|
aggressive = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const MWWorld::Ptr &ally : allies1)
|
for (const MWWorld::Ptr& ally : allies1)
|
||||||
{
|
{
|
||||||
if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(ally))
|
if (creatureStats2.getAiSequence().isInCombat(ally))
|
||||||
{
|
{
|
||||||
aggressive = true;
|
aggressive = true;
|
||||||
break;
|
break;
|
||||||
|
@ -569,15 +578,16 @@ namespace MWMechanics
|
||||||
// Player followers and escorters with high fight should not initiate combat with the player or with
|
// Player followers and escorters with high fight should not initiate combat with the player or with
|
||||||
// other player followers or escorters
|
// other player followers or escorters
|
||||||
if (!isPlayerFollowerOrEscorter)
|
if (!isPlayerFollowerOrEscorter)
|
||||||
aggressive = MWBase::Environment::get().getMechanicsManager()->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
|
// 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())
|
if (!aggressive && actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc() && creatureStats2.getAiSequence().isInCombat())
|
||||||
{
|
{
|
||||||
// Check if the creature is too far
|
// 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)
|
if (sqrDist > fAlarmRadius * fAlarmRadius)
|
||||||
return;
|
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 any of the above conditions turned actor1 aggressive towards actor2, do an awareness check. If it passes, start combat with actor2.
|
||||||
if (aggressive)
|
if (aggressive)
|
||||||
{
|
{
|
||||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2)
|
bool LOS = world->getLOS(actor1, actor2) &&
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
|
mechanicsManager->awarenessCheck(actor2, actor1);
|
||||||
|
|
||||||
if (LOS)
|
if (LOS)
|
||||||
MWBase::Environment::get().getMechanicsManager()->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)
|
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
|
// 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();
|
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)
|
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
|
if (seq.getTypeId() != AiPackageTypeId::Breathe) //Only add it once
|
||||||
seq.stack(AiBreathe(), ptr);
|
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())));
|
bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3())));
|
||||||
if((world->isSubmerged(ptr) || knockedOutUnderwater)
|
if ((world->isSubmerged(ptr) || knockedOutUnderwater)
|
||||||
&& stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0)
|
&& stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0)
|
||||||
{
|
{
|
||||||
float timeLeft = 0.0f;
|
float timeLeft = 0.0f;
|
||||||
if(knockedOutUnderwater)
|
if (knockedOutUnderwater)
|
||||||
stats.setTimeToStartDrowning(0);
|
stats.setTimeToStartDrowning(0);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
timeLeft = stats.getTimeToStartDrowning() - duration;
|
timeLeft = stats.getTimeToStartDrowning() - duration;
|
||||||
if(timeLeft < 0.0f)
|
if (timeLeft < 0.0f)
|
||||||
timeLeft = 0.0f;
|
timeLeft = 0.0f;
|
||||||
stats.setTimeToStartDrowning(timeLeft);
|
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
|
// If drowning, apply 3 points of damage per second
|
||||||
static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->mValue.getFloat();
|
static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->mValue.getFloat();
|
||||||
DynamicStat<float> health = stats.getHealth();
|
DynamicStat<float> health = stats.getHealth();
|
||||||
health.setCurrent(health.getCurrent() - fSuffocationDamage*duration);
|
health.setCurrent(health.getCurrent() - fSuffocationDamage * duration);
|
||||||
stats.setHealth(health);
|
stats.setHealth(health);
|
||||||
|
|
||||||
// Play a drowning sound
|
// Play a drowning sound
|
||||||
MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager* sndmgr = MWBase::Environment::get().getSoundManager();
|
||||||
if(!sndmgr->getSoundPlaying(ptr, "drown"))
|
if (!sndmgr->getSoundPlaying(ptr, "drown"))
|
||||||
sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f);
|
sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f);
|
||||||
|
|
||||||
if(isPlayer)
|
if (isPlayer)
|
||||||
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
|
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,34 +855,30 @@ namespace MWMechanics
|
||||||
stats.setTimeToStartDrowning(fHoldBreathTime);
|
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
|
* Automatically equip NPCs torches at night and unequip them at day
|
||||||
*/
|
*/
|
||||||
if (!isPlayer)
|
if (!isPlayer)
|
||||||
{
|
{
|
||||||
MWWorld::ContainerStoreIterator torch = inventoryStore.end();
|
auto torchIter = std::find_if(std::begin(inventoryStore), std::end(inventoryStore), [&](auto entry)
|
||||||
for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getType() == ESM::Light::sRecordId &&
|
return entry.getType() == ESM::Light::sRecordId &&
|
||||||
it->getClass().canBeEquipped(*it, ptr).first)
|
entry.getClass().canBeEquipped(entry, ptr).first;
|
||||||
{
|
});
|
||||||
torch = it;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mayEquip)
|
if (mayEquip)
|
||||||
{
|
{
|
||||||
if (torch != inventoryStore.end())
|
if (torchIter != 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.
|
// 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)
|
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
|
// For hostile NPCs, see if they have anything better to equip first
|
||||||
auto shield = inventoryStore.getPreferredShield(ptr);
|
auto shield = inventoryStore.getPreferredShield(ptr);
|
||||||
if(shield != inventoryStore.end())
|
if (shield != inventoryStore.end())
|
||||||
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, shield, ptr);
|
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 we have a torch and can equip it, then equip it now.
|
||||||
if (heldIter == inventoryStore.end())
|
if (heldIter == inventoryStore.end())
|
||||||
{
|
{
|
||||||
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
|
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torchIter, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -910,11 +917,12 @@ namespace MWMechanics
|
||||||
//If holding a light...
|
//If holding a light...
|
||||||
if(heldIter.getType() == MWWorld::ContainerStore::Type_Light)
|
if(heldIter.getType() == MWWorld::ContainerStore::Type_Light)
|
||||||
{
|
{
|
||||||
|
auto* world = MWBase::Environment::get().getWorld();
|
||||||
// Use time from the player's light
|
// Use time from the player's light
|
||||||
if(isPlayer)
|
if(isPlayer)
|
||||||
{
|
{
|
||||||
// But avoid using it up if the light source is hidden
|
// 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())
|
if (anim && anim->getCarriedLeftShown())
|
||||||
{
|
{
|
||||||
float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter);
|
float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter);
|
||||||
|
@ -935,7 +943,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both NPC and player lights extinguish in water.
|
// 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
|
inventoryStore.remove(*heldIter, 1, ptr); // remove it
|
||||||
|
|
||||||
|
@ -950,37 +958,47 @@ namespace MWMechanics
|
||||||
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration)
|
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr player = getPlayer();
|
MWWorld::Ptr player = getPlayer();
|
||||||
if (ptr != player && ptr.getClass().isNpc())
|
if (ptr == player)
|
||||||
{
|
return;
|
||||||
|
|
||||||
|
auto& actorClass = ptr.getClass();
|
||||||
|
if (!actorClass.isNpc())
|
||||||
|
return;
|
||||||
|
|
||||||
// get stats of witness
|
// get stats of witness
|
||||||
CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
NpcStats& npcStats = ptr.getClass().getNpcStats(ptr);
|
NpcStats& npcStats = ptr.getClass().getNpcStats(ptr);
|
||||||
|
|
||||||
if (player.getClass().getNpcStats(player).isWerewolf())
|
const auto& playerClass = player.getClass();
|
||||||
|
const auto& playerStats = playerClass.getNpcStats(player);
|
||||||
|
if (playerStats.isWerewolf())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackageTypeId::Pursue && !creatureStats.getAiSequence().isInCombat()
|
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)
|
&& creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& esmStore = world->getStore();
|
||||||
static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->mValue.getInteger();
|
static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->mValue.getInteger();
|
||||||
// Force dialogue on sight if bounty is greater than the cutoff
|
// 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)
|
// 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
|
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?
|
// 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)
|
&& world->getLOS(ptr, player)
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
|
&& mechanicsManager->awarenessCheck(player, ptr))
|
||||||
{
|
{
|
||||||
static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
|
static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
|
||||||
if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier)
|
if (playerStats.getBounty() >= cutoff * iCrimeThresholdMultiplier)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
|
mechanicsManager->startCombat(ptr, player);
|
||||||
creatureStats.setHitAttemptActorId(player.getClass().getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable
|
creatureStats.setHitAttemptActorId(playerClass.getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
|
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
|
||||||
creatureStats.setAlarmed(true);
|
creatureStats.setAlarmed(true);
|
||||||
npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
|
npcStats.setCrimeId(world->getPlayer().getNewCrimeId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -988,7 +1006,7 @@ namespace MWMechanics
|
||||||
if (npcStats.getCrimeId() != -1)
|
if (npcStats.getCrimeId() != -1)
|
||||||
{
|
{
|
||||||
// if you've paid for your crimes and I havent noticed
|
// if you've paid for your crimes and I havent noticed
|
||||||
if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() )
|
if (npcStats.getCrimeId() <= world->getPlayer().getCrimeId())
|
||||||
{
|
{
|
||||||
// Calm witness down
|
// Calm witness down
|
||||||
if (ptr.getClass().isClass(ptr, "Guard"))
|
if (ptr.getClass().isClass(ptr, "Guard"))
|
||||||
|
@ -1005,7 +1023,6 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Actors::Actors() : mSmoothMovement(Settings::Manager::getBool("smooth movement", "Game"))
|
Actors::Actors() : mSmoothMovement(Settings::Manager::getBool("smooth movement", "Game"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -430,7 +430,7 @@ bool MWMechanics::AiSequence::isEmpty() const
|
||||||
return mPackages.empty();
|
return mPackages.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
const AiPackage& MWMechanics::AiSequence::getActivePackage()
|
const AiPackage& MWMechanics::AiSequence::getActivePackage() const
|
||||||
{
|
{
|
||||||
if(mPackages.empty())
|
if(mPackages.empty())
|
||||||
throw std::runtime_error(std::string("No AI Package!"));
|
throw std::runtime_error(std::string("No AI Package!"));
|
||||||
|
|
|
@ -163,7 +163,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
/// Return the current active package.
|
/// Return the current active package.
|
||||||
/** If there is no active package, it will throw an exception **/
|
/** If there is no active package, it will throw an exception **/
|
||||||
const AiPackage& getActivePackage();
|
const AiPackage& getActivePackage() const;
|
||||||
|
|
||||||
/// Fills the AiSequence with packages
|
/// Fills the AiSequence with packages
|
||||||
/** Typically used for loading from the ESM
|
/** Typically used for loading from the ESM
|
||||||
|
|
Loading…
Reference in a new issue