Merge branch 'refactor/6677-1' into 'master'

#6677: Reduce some calls and re-structure functions

See merge request OpenMW/openmw!1729
pull/3226/head
psi29a 3 years ago
commit 9ff34da59e

@ -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 &&
it->getClass().canBeEquipped(*it, ptr).first)
{ {
torch = it; return entry.getType() == ESM::Light::sRecordId &&
break; entry.getClass().canBeEquipped(entry, ptr).first;
} });
}
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,59 +958,68 @@ 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;
// get stats of witness
CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
NpcStats& npcStats = ptr.getClass().getNpcStats(ptr);
if (player.getClass().getNpcStats(player).isWerewolf()) auto& actorClass = ptr.getClass();
return; 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() const auto& playerClass = player.getClass();
&& creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) 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 iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->mValue.getInteger(); if (playerStats.getBounty() >= cutoff * iCrimeThresholdMultiplier)
// 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(); mechanicsManager->startCombat(ptr, player);
if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) creatureStats.setHitAttemptActorId(playerClass.getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable
{
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());
} }
else
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
creatureStats.setAlarmed(true);
npcStats.setCrimeId(world->getPlayer().getNewCrimeId());
} }
}
// if I was a witness to a crime // if I was a witness to a crime
if (npcStats.getCrimeId() != -1) 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 // Calm witness down
if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ) if (ptr.getClass().isClass(ptr, "Guard"))
{ creatureStats.getAiSequence().stopPursuit();
// Calm witness down stopCombat(ptr);
if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stopPursuit(); // Reset factors to attack
stopCombat(ptr); creatureStats.setAttacked(false);
creatureStats.setAlarmed(false);
// Reset factors to attack creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr));
creatureStats.setAttacked(false);
creatureStats.setAlarmed(false); // Update witness crime id
creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr)); npcStats.setCrimeId(-1);
// Update witness crime id
npcStats.setCrimeId(-1);
}
} }
} }
} }

@ -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…
Cancel
Save