Advertisement
Guest User

Untitled

a guest
Apr 17th, 2020
636
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 106.43 KB | None | 0 0
  1. #include "actors.hpp"
  2.  
  3. #include <components/esm/esmreader.hpp>
  4. #include <components/esm/esmwriter.hpp>
  5.  
  6. #include <components/sceneutil/positionattitudetransform.hpp>
  7. #include <components/sceneutil/vismask.hpp>
  8. #include <components/debug/debuglog.hpp>
  9. #include <components/misc/rng.hpp>
  10. #include <components/settings/settings.hpp>
  11.  
  12. #include "../mwworld/esmstore.hpp"
  13. #include "../mwworld/class.hpp"
  14. #include "../mwworld/inventorystore.hpp"
  15. #include "../mwworld/actionequip.hpp"
  16. #include "../mwworld/player.hpp"
  17.  
  18. #include "../mwbase/world.hpp"
  19. #include "../mwbase/environment.hpp"
  20. #include "../mwbase/windowmanager.hpp"
  21. #include "../mwbase/dialoguemanager.hpp"
  22. #include "../mwbase/soundmanager.hpp"
  23. #include "../mwbase/mechanicsmanager.hpp"
  24. #include "../mwbase/statemanager.hpp"
  25.  
  26. #include "../mwmechanics/aibreathe.hpp"
  27.  
  28. #include "spellcasting.hpp"
  29. #include "steering.hpp"
  30. #include "npcstats.hpp"
  31. #include "creaturestats.hpp"
  32. #include "movement.hpp"
  33. #include "character.hpp"
  34. #include "aicombat.hpp"
  35. #include "aicombataction.hpp"
  36. #include "aifollow.hpp"
  37. #include "aipursue.hpp"
  38. #include "actor.hpp"
  39. #include "summoning.hpp"
  40. #include "combat.hpp"
  41. #include "actorutil.hpp"
  42.  
  43. namespace
  44. {
  45.  
  46. bool isConscious(const MWWorld::Ptr& ptr)
  47. {
  48.     const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
  49.     return !stats.isDead() && !stats.getKnockedDown();
  50. }
  51.  
  52. int getBoundItemSlot (const std::string& itemId)
  53. {
  54.     static std::map<std::string, int> boundItemsMap;
  55.     if (boundItemsMap.empty())
  56.     {
  57.         std::string boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundBootsID")->mValue.getString();
  58.         boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots;
  59.  
  60.         boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundCuirassID")->mValue.getString();
  61.         boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass;
  62.  
  63.         boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundLeftGauntletID")->mValue.getString();
  64.         boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet;
  65.  
  66.         boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundRightGauntletID")->mValue.getString();
  67.         boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet;
  68.  
  69.         boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundHelmID")->mValue.getString();
  70.         boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet;
  71.  
  72.         boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundShieldID")->mValue.getString();
  73.         boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft;
  74.     }
  75.  
  76.     int slot = MWWorld::InventoryStore::Slot_CarriedRight;
  77.     std::map<std::string, int>::iterator it = boundItemsMap.find(itemId);
  78.     if (it != boundItemsMap.end())
  79.         slot = it->second;
  80.  
  81.     return slot;
  82. }
  83.  
  84. class CheckActorCommanded : public MWMechanics::EffectSourceVisitor
  85. {
  86.     MWWorld::Ptr mActor;
  87. public:
  88.     bool mCommanded;
  89.     CheckActorCommanded(const MWWorld::Ptr& actor)
  90.         : mActor(actor)
  91.     , mCommanded(false){}
  92.  
  93.     virtual void visit (MWMechanics::EffectKey key,
  94.                              const std::string& sourceName, const std::string& sourceId, int casterActorId,
  95.                         float magnitude, float remainingTime = -1, float totalTime = -1)
  96.     {
  97.         if (((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc())
  98.             || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name()))
  99.             && magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel())
  100.                 mCommanded = true;
  101.     }
  102. };
  103.  
  104. // Check for command effects having ended and remove package if necessary
  105. void adjustCommandedActor (const MWWorld::Ptr& actor)
  106. {
  107.     CheckActorCommanded check(actor);
  108.     MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
  109.     stats.getActiveSpells().visitEffectSources(check);
  110.  
  111.     bool hasCommandPackage = false;
  112.  
  113.     std::list<MWMechanics::AiPackage*>::const_iterator it;
  114.     for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
  115.     {
  116.         if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
  117.                 static_cast<MWMechanics::AiFollow*>(*it)->isCommanded())
  118.         {
  119.             hasCommandPackage = true;
  120.             break;
  121.         }
  122.     }
  123.  
  124.     if (!check.mCommanded && hasCommandPackage)
  125.         stats.getAiSequence().erase(it);
  126. }
  127.  
  128. void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka)
  129. {
  130.     MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
  131.     const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
  132.  
  133.     int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
  134.     health = 0.1f * endurance;
  135.  
  136.     float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
  137.     magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
  138. }
  139.  
  140. }
  141.  
  142. namespace MWMechanics
  143. {
  144.     static const int GREETING_SHOULD_START = 4; // how many updates should pass before NPC can greet player
  145.     static const int GREETING_SHOULD_END = 20;  // how many updates should pass before NPC stops turning to player
  146.     static const int GREETING_COOLDOWN = 40;    // how many updates should pass before NPC can continue movement
  147.     static const float DECELERATE_DISTANCE = 512.f;
  148.  
  149.     class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
  150.     {
  151.     public:
  152.         float mRemainingTime;
  153.  
  154.         GetStuntedMagickaDuration(const MWWorld::Ptr& actor)
  155.             : mRemainingTime(0.f){}
  156.  
  157.         virtual void visit (MWMechanics::EffectKey key,
  158.                                 const std::string& sourceName, const std::string& sourceId, int casterActorId,
  159.                             float magnitude, float remainingTime = -1, float totalTime = -1)
  160.         {
  161.             if (mRemainingTime == -1) return;
  162.  
  163.             if (key.mId == ESM::MagicEffect::StuntedMagicka)
  164.             {
  165.                 if (totalTime == -1)
  166.                 {
  167.                     mRemainingTime = -1;
  168.                     return;
  169.                 }
  170.  
  171.                 if (remainingTime > mRemainingTime)
  172.                     mRemainingTime = remainingTime;
  173.             }
  174.         }
  175.     };
  176.  
  177.     class SoulTrap : public MWMechanics::EffectSourceVisitor
  178.     {
  179.         MWWorld::Ptr mCreature;
  180.         MWWorld::Ptr mActor;
  181.         bool mTrapped;
  182.     public:
  183.         SoulTrap(const MWWorld::Ptr& trappedCreature)
  184.             : mCreature(trappedCreature)
  185.             , mTrapped(false)
  186.         {
  187.         }
  188.  
  189.         virtual void visit (MWMechanics::EffectKey key,
  190.                                  const std::string& sourceName, const std::string& sourceId, int casterActorId,
  191.                             float magnitude, float remainingTime = -1, float totalTime = -1)
  192.         {
  193.             if (mTrapped)
  194.                 return;
  195.             if (key.mId != ESM::MagicEffect::Soultrap)
  196.                 return;
  197.             if (magnitude <= 0)
  198.                 return;
  199.  
  200.             MWBase::World* world = MWBase::Environment::get().getWorld();
  201.  
  202.             MWWorld::Ptr caster = world->searchPtrViaActorId(casterActorId);
  203.             if (caster.isEmpty() || !caster.getClass().isActor())
  204.                 return;
  205.  
  206.             static const float fSoulgemMult = world->getStore().get<ESM::GameSetting>().find("fSoulgemMult")->mValue.getFloat();;
  207.  
  208.         int creatureSoulValue = 0;
  209.         if (mCreature.getTypeName() == typeid(ESM::Creature).name())
  210.         {    creatureSoulValue = mCreature.get<ESM::Creature>()->mBase->mData.mSoul;
  211.  
  212.         }
  213.         else
  214.         {creatureSoulValue = 400;}
  215.          
  216.             if (creatureSoulValue == 0)
  217.                 return;
  218.  
  219.             // Use the smallest soulgem that is large enough to hold the soul
  220.             MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster);
  221.             MWWorld::ContainerStoreIterator gem = container.end();
  222.             float gemCapacity = std::numeric_limits<float>::max();
  223.             std::string soulgemFilter = "misc_soulgem"; // no other way to check for soulgems? :/
  224.  
  225.             for (MWWorld::ContainerStoreIterator it = container.begin(MWWorld::ContainerStore::Type_Miscellaneous);
  226.                  it != container.end(); ++it)
  227.             {
  228.                 const std::string& id = it->getCellRef().getRefId();
  229.                 float thisGemCapacity = it->get<ESM::Miscellaneous>()->mBase->mData.mValue * fSoulgemMult;
  230.                 if (mCreature.getTypeName() != typeid(ESM::Creature).name() )//if it's not a creature
  231.                 {
  232.                     if ( id == "misc_soulgem_black"&& it->getCellRef().getSoul().empty())//only use black, empty soul gems
  233.                     {
  234.                                 gemCapacity = thisGemCapacity;
  235.                                 gem = it;
  236.  
  237.                     }
  238.  
  239.                 }
  240.                 else
  241.                 {
  242.                         if (id.size() >= soulgemFilter.size() && id.substr(0,soulgemFilter.size()) == soulgemFilter)
  243.                         {
  244.                             if (thisGemCapacity >= creatureSoulValue && thisGemCapacity < gemCapacity
  245.                                     && it->getCellRef().getSoul().empty()&& id != "misc_soulgem_black" )//don't use black soul gems for creatures
  246.                             {
  247.                                 gem = it;
  248.                                 gemCapacity = thisGemCapacity;
  249.                             }
  250.                         }
  251.                     }
  252.                 }
  253.                 if (mCreature.getTypeName() != typeid(ESM::Creature).name())
  254.                 {
  255.               //  const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
  256.                //     store.get<ESM::NPC>().find(mCreature.getCellRef().getRefId());
  257.                         // MWBase::Environment::get().getWindowManager()->messageBox ("soul_" + mCreature.getCellRef().getRefId());
  258.                 }
  259.                 if (gem == container.end())
  260.                     return;
  261.  
  262.             // Set the soul on just one of the gems, not the whole stack
  263.             gem->getContainerStore()->unstack(*gem, caster);
  264.                         gem->getCellRef().setSoul(mCreature.getCellRef().getRefId());
  265.        /*    if (mCreature.getTypeName() == typeid(ESM::Creature).name())
  266.             {  
  267.                         gem->getCellRef().setSoul(mCreature.getCellRef().getRefId());
  268.  
  269.             }
  270.             else
  271.             {
  272.  
  273.                     //  gem->getCellRef().setName("Black Soul Gem (" + mCreature.getCellRef().getName() + ")");
  274.                             MWBase::Environment::get().getWindowManager()->messageBox(gem->getCellRef().getSoul());
  275.                            
  276.             }//*/
  277.  
  278.             // Restack the gem with other gems with the same soul
  279.             gem->getContainerStore()->restack(*gem);
  280.  
  281.             mTrapped = true;
  282.  
  283.             if (caster == getPlayer())
  284.                 MWBase::Environment::get().getWindowManager()->messageBox("#{sSoultrapSuccess}");
  285.  
  286.             const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
  287.                     .search("VFX_Soul_Trap");
  288.             if (fx)
  289.                 MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
  290.                     "", mCreature.getRefData().getPosition().asVec3());
  291.  
  292.             MWBase::Environment::get().getSoundManager()->playSound3D(
  293.                 mCreature.getRefData().getPosition().asVec3(), "conjuration hit", 1.f, 1.f
  294.             );
  295.         }
  296.     };
  297.  
  298.     void Actors::addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor)
  299.     {
  300.         MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
  301.         int slot = getBoundItemSlot(itemId);
  302.  
  303.         if (actor.getClass().getContainerStore(actor).count(itemId) != 0)
  304.             return;
  305.  
  306.         MWWorld::ContainerStoreIterator prevItem = store.getSlot(slot);
  307.  
  308.         MWWorld::Ptr boundPtr = *store.MWWorld::ContainerStore::add(itemId, 1, actor);
  309.         MWWorld::ActionEquip action(boundPtr);
  310.         action.execute(actor);
  311.  
  312.         if (actor != MWMechanics::getPlayer())
  313.             return;
  314.  
  315.         MWWorld::Ptr newItem = *store.getSlot(slot);
  316.  
  317.         if (newItem.isEmpty() || boundPtr != newItem)
  318.             return;
  319.  
  320.         MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
  321.  
  322.         // change draw state only if the item is in player's right hand
  323.         if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
  324.             player.setDrawState(MWMechanics::DrawState_Weapon);
  325.  
  326.         if (prevItem != store.end())
  327.             player.setPreviousItem(itemId, prevItem->getCellRef().getRefId());
  328.     }
  329.  
  330.     void Actors::removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor)
  331.     {
  332.         MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
  333.         int slot = getBoundItemSlot(itemId);
  334.  
  335.         MWWorld::ContainerStoreIterator currentItem = store.getSlot(slot);
  336.  
  337.         bool wasEquipped = currentItem != store.end() && Misc::StringUtils::ciEqual(currentItem->getCellRef().getRefId(), itemId);
  338.  
  339.         if (actor != MWMechanics::getPlayer())
  340.         {
  341.             store.remove(itemId, 1, actor);
  342.  
  343.             // Equip a replacement
  344.             if (!wasEquipped)
  345.                 return;
  346.  
  347.             std::string type = currentItem->getTypeName();
  348.             if (type != typeid(ESM::Weapon).name() && type != typeid(ESM::Armor).name() && type != typeid(ESM::Clothing).name())
  349.                 return;
  350.  
  351.             if (actor.getClass().getCreatureStats(actor).isDead())
  352.                 return;
  353.  
  354.             if (!actor.getClass().hasInventoryStore(actor))
  355.                 return;
  356.  
  357.             if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
  358.                 return;
  359.  
  360.             actor.getClass().getInventoryStore(actor).autoEquip(actor);
  361.  
  362.             return;
  363.         }
  364.  
  365.         MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
  366.         std::string prevItemId = player.getPreviousItem(itemId);
  367.         player.erasePreviousItem(itemId);
  368.  
  369.         if (!prevItemId.empty())
  370.         {
  371.             // Find previous item (or its replacement) by id.
  372.             // we should equip previous item only if expired bound item was equipped.
  373.             MWWorld::Ptr item = store.findReplacement(prevItemId);
  374.             if (!item.isEmpty() && wasEquipped)
  375.             {
  376.                 MWWorld::ActionEquip action(item);
  377.                 action.execute(actor);
  378.             }
  379.         }
  380.  
  381.         store.remove(itemId, 1, actor);
  382.     }
  383.  
  384.     void Actors::updateActor (const MWWorld::Ptr& ptr, float duration)
  385.     {
  386.         // magic effects
  387.         adjustMagicEffects (ptr);
  388.         if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats())
  389.             calculateDynamicStats (ptr);
  390.  
  391.         calculateCreatureStatModifiers (ptr, duration);
  392.         // fatigue restoration
  393.         calculateRestoration(ptr, duration);
  394.     }
  395.  
  396.     void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
  397.                                     MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance)
  398.     {
  399.         if (!actor.getRefData().getBaseNode())
  400.             return;
  401.  
  402.         if (targetActor.getClass().getCreatureStats(targetActor).isDead())
  403.             return;
  404.  
  405.         static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
  406.                 .find("fMaxHeadTrackDistance")->mValue.getFloat();
  407.         static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
  408.                 .find("fInteriorHeadTrackMult")->mValue.getFloat();
  409.         float maxDistance = fMaxHeadTrackDistance;
  410.         const ESM::Cell* currentCell = actor.getCell()->getCell();
  411.         if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx))
  412.             maxDistance *= fInteriorHeadTrackMult;
  413.  
  414.         const osg::Vec3f actor1Pos(actor.getRefData().getPosition().asVec3());
  415.         const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
  416.         float sqrDist = (actor1Pos - actor2Pos).length2();
  417.  
  418.         if (sqrDist > maxDistance*maxDistance)
  419.             return;
  420.  
  421.         // stop tracking when target is behind the actor
  422.         osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
  423.         osg::Vec3f targetDirection(actor2Pos - actor1Pos);
  424.         actorDirection.z() = 0;
  425.         targetDirection.z() = 0;
  426.         actorDirection.normalize();
  427.         targetDirection.normalize();
  428.         if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f)
  429.             && sqrDist <= sqrHeadTrackDistance
  430.             && MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function
  431.             && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))
  432.         {
  433.             sqrHeadTrackDistance = sqrDist;
  434.             headTrackTarget = targetActor;
  435.         }
  436.     }
  437.  
  438.     void Actors::playIdleDialogue(const MWWorld::Ptr& actor)
  439.     {
  440.         if (!actor.getClass().isActor() || actor == getPlayer() || MWBase::Environment::get().getSoundManager()->sayActive(actor))
  441.             return;
  442.  
  443.         const CreatureStats &stats = actor.getClass().getCreatureStats(actor);
  444.         if (stats.getAiSetting(CreatureStats::AI_Hello).getModified() == 0)
  445.             return;
  446.  
  447.         const MWMechanics::AiSequence& seq = stats.getAiSequence();
  448.         if (seq.isInCombat() || seq.hasPackage(AiPackage::TypeIdFollow) || seq.hasPackage(AiPackage::TypeIdEscort))
  449.             return;
  450.  
  451.         const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3());
  452.         const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
  453.         MWBase::World* world = MWBase::Environment::get().getWorld();
  454.         if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000)
  455.             return;
  456.  
  457.         // Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated.
  458.         // We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST.
  459.         const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;
  460.         static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat();
  461.         if (Misc::Rng::rollProbability() * 10000.f < fVoiceIdleOdds * delta && world->getLOS(getPlayer(), actor))
  462.             MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
  463.     }
  464.  
  465.     void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
  466.     {
  467.         float previousSpeedFactor = actor.getClass().getMovementSettings(actor).mSpeedFactor;
  468.         float newSpeedFactor = 1.f;
  469.  
  470.         CreatureStats &stats = actor.getClass().getCreatureStats(actor);
  471.         MWMechanics::AiSequence& seq = stats.getAiSequence();
  472.  
  473.         if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed())
  474.         {
  475.             osg::Vec3f targetPos = seq.getActivePackage()->getDestination();
  476.             osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
  477.             float distance = (targetPos - actorPos).length();
  478.             if (distance < DECELERATE_DISTANCE)
  479.                 newSpeedFactor = std::max(0.7f, 0.1f * previousSpeedFactor * (distance/64.f + 2.f));
  480.         }
  481.  
  482.         actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor;
  483.     }
  484.  
  485.     void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly)
  486.     {
  487.         if (!actor.getClass().isActor() || actor == getPlayer())
  488.             return;
  489.  
  490.         CreatureStats &stats = actor.getClass().getCreatureStats(actor);
  491.         const MWMechanics::AiSequence& seq = stats.getAiSequence();
  492.         int packageId = seq.getTypeId();
  493.  
  494.         if (seq.isInCombat() ||
  495.             MWBase::Environment::get().getWorld()->isSwimming(actor) ||
  496.             (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
  497.         {
  498.             stats.setTurningToPlayer(false);
  499.             stats.setGreetingTimer(0);
  500.             stats.setGreetingState(Greet_None);
  501.             return;
  502.         }
  503.  
  504.         MWWorld::Ptr player = getPlayer();
  505.         osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
  506.         osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
  507.         osg::Vec3f dir = playerPos - actorPos;
  508.  
  509.         if (stats.isTurningToPlayer())
  510.         {
  511.             // Reduce the turning animation glitch by using a *HUGE* value of
  512.             // epsilon...  TODO: a proper fix might be in either the physics or the
  513.             // animation subsystem
  514.             if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f)))
  515.             {
  516.                 stats.setTurningToPlayer(false);
  517.                 // An original engine launches an endless idle2 when an actor greets player.
  518.                 playAnimationGroup (actor, "idle2", 0, std::numeric_limits<int>::max(), false);
  519.             }
  520.         }
  521.  
  522.         if (turnOnly)
  523.             return;
  524.  
  525.         // Play a random voice greeting if the player gets too close
  526.         static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
  527.             .get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
  528.  
  529.         float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);
  530.  
  531.         int greetingTimer = stats.getGreetingTimer();
  532.         GreetingState greetingState = stats.getGreetingState();
  533.         if (greetingState == Greet_None)
  534.         {
  535.             if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
  536.                 !player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
  537.                 && MWBase::Environment::get().getWorld()->getLOS(player, actor)
  538.                 && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
  539.                 greetingTimer++;
  540.  
  541.             if (greetingTimer >= GREETING_SHOULD_START)
  542.             {
  543.                 greetingState = Greet_InProgress;
  544.                 MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
  545.                 greetingTimer = 0;
  546.             }
  547.         }
  548.  
  549.         if (greetingState == Greet_InProgress)
  550.         {
  551.             greetingTimer++;
  552.  
  553.             if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor))
  554.                 turnActorToFacePlayer(actor, dir);
  555.  
  556.             if (greetingTimer >= GREETING_COOLDOWN)
  557.             {
  558.                 greetingState = Greet_Done;
  559.                 greetingTimer = 0;
  560.             }
  561.         }
  562.  
  563.         if (greetingState == Greet_Done)
  564.         {
  565.             float resetDist = 2 * helloDistance;
  566.             if ((playerPos - actorPos).length2() >= resetDist*resetDist)
  567.                 greetingState = Greet_None;
  568.         }
  569.  
  570.         stats.setGreetingTimer(greetingTimer);
  571.         stats.setGreetingState(greetingState);
  572.     }
  573.  
  574.     void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir)
  575.     {
  576.         actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
  577.         actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
  578.  
  579.         CreatureStats &stats = actor.getClass().getCreatureStats(actor);
  580.         if (!stats.isTurningToPlayer())
  581.         {
  582.             stats.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
  583.             stats.setTurningToPlayer(true);
  584.         }
  585.     }
  586.  
  587.     void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
  588.     {
  589.         // No combat for totally static creatures
  590.         if (!actor1.getClass().isMobile(actor1))
  591.             return;
  592.  
  593.         CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1);
  594.         if (creatureStats1.isDead() || creatureStats1.getAiSequence().isInCombat(actor2))
  595.             return;
  596.  
  597.         const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);
  598.         if (creatureStats2.isDead())
  599.             return;
  600.  
  601.         const osg::Vec3f actor1Pos(actor1.getRefData().getPosition().asVec3());
  602.         const osg::Vec3f actor2Pos(actor2.getRefData().getPosition().asVec3());
  603.         float sqrDist = (actor1Pos - actor2Pos).length2();
  604.  
  605.         if (sqrDist > mActorsProcessingRange*mActorsProcessingRange)
  606.             return;
  607.  
  608.         // If this is set to true, actor1 will start combat with actor2 if the awareness check at the end of the method returns true
  609.         bool aggressive = false;
  610.  
  611.         // Get actors allied with actor1. Includes those following or escorting actor1, actors following or escorting those actors, (recursive)
  612.         // and any actor currently being followed or escorted by actor1
  613.         std::set<MWWorld::Ptr> allies1;
  614.  
  615.         getActorsSidingWith(actor1, allies1, cachedAllies);
  616.  
  617.         // If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and actor2
  618.         for (const MWWorld::Ptr &ally : allies1)
  619.         {
  620.             if (creatureStats1.getAiSequence().isInCombat(ally))
  621.                 continue;
  622.  
  623.             if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))
  624.             {
  625.                 MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
  626.                 // Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat
  627.                 // if the player gets out of reach, while the ally would continue combat with the player
  628.                 creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());
  629.                 return;
  630.             }
  631.  
  632.             // If there's been no attack attempt yet but an ally of actor1 is in combat with actor2, become aggressive to actor2
  633.             if (ally.getClass().getCreatureStats(ally).getAiSequence().isInCombat(actor2))
  634.                 aggressive = true;
  635.         }
  636.  
  637.         std::set<MWWorld::Ptr> playerAllies;
  638.         getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies);
  639.  
  640.         bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();
  641.  
  642.         // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them
  643.         // Doesn't apply for player followers/escorters        
  644.         if (!aggressive && !isPlayerFollowerOrEscorter)
  645.         {
  646.             // Check that actor2 is in combat with actor1
  647.             if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1))
  648.             {
  649.                 std::set<MWWorld::Ptr> allies2;
  650.  
  651.                 getActorsSidingWith(actor2, allies2, cachedAllies);
  652.  
  653.                 // Check that an ally of actor2 is also in combat with actor1
  654.                 for (const MWWorld::Ptr &ally2 : allies2)
  655.                 {
  656.                     if (ally2.getClass().getCreatureStats(ally2).getAiSequence().isInCombat(actor1))
  657.                     {
  658.                         MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
  659.                         // Also have actor1's allies start combat
  660.                         for (const MWWorld::Ptr& ally1 : allies1)
  661.                             MWBase::Environment::get().getMechanicsManager()->startCombat(ally1, actor2);
  662.                         return;
  663.                     }
  664.                 }
  665.             }
  666.         }
  667.  
  668.         // Stop here if target is unreachable
  669.         if (!canFight(actor1, actor2))
  670.             return;
  671.  
  672.         // If set in the settings file, player followers and escorters will become aggressive toward enemies in combat with them or the player
  673.         static const bool followersAttackOnSight = Settings::Manager::getBool("followers attack on sight", "Game");
  674.         if (!aggressive && isPlayerFollowerOrEscorter && followersAttackOnSight)
  675.         {
  676.             if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1))
  677.                 aggressive = true;
  678.             else
  679.             {
  680.                 for (const MWWorld::Ptr &ally : allies1)
  681.                 {
  682.                     if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(ally))
  683.                     {
  684.                         aggressive = true;
  685.                         break;
  686.                     }
  687.                 }
  688.             }
  689.         }
  690.  
  691.         // Do aggression check if actor2 is the player or a player follower or escorter
  692.         if (!aggressive)
  693.         {
  694.             if (againstPlayer || playerAllies.find(actor2) != playerAllies.end())
  695.             {
  696.                 // Player followers and escorters with high fight should not initiate combat with the player or with
  697.                 // other player followers or escorters
  698.                 if (!isPlayerFollowerOrEscorter)
  699.                     aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
  700.             }
  701.         }
  702.  
  703.         // Make guards go aggressive with creatures that are in combat, unless the creature is a follower or escorter
  704.         if (!aggressive && actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc() && creatureStats2.getAiSequence().isInCombat())
  705.         {
  706.             // Check if the creature is too far
  707.             static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fAlarmRadius")->mValue.getFloat();
  708.             if (sqrDist > fAlarmRadius * fAlarmRadius)
  709.                 return;
  710.  
  711.             bool followerOrEscorter = false;
  712.             for (const AiPackage* package : creatureStats2.getAiSequence())
  713.             {
  714.                 // The follow package must be first or have nothing but combat before it
  715.                 if (package->sideWithTarget())
  716.                 {
  717.                     followerOrEscorter = true;
  718.                     break;
  719.                 }
  720.                 else if (package->getTypeId() != MWMechanics::AiPackage::TypeIdCombat)
  721.                     break;
  722.             }
  723.             if (!followerOrEscorter)
  724.                 aggressive = true;
  725.         }
  726.  
  727.         // If any of the above conditions turned actor1 aggressive towards actor2, do an awareness check. If it passes, start combat with actor2.
  728.         if (aggressive)
  729.         {
  730.             bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2)
  731.                     && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
  732.  
  733.             if (LOS)
  734.                 MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
  735.         }
  736.     }
  737.  
  738.     void Actors::adjustMagicEffects (const MWWorld::Ptr& creature)
  739.     {
  740.         CreatureStats& creatureStats =  creature.getClass().getCreatureStats (creature);
  741.         if (creatureStats.isDeathAnimationFinished())
  742.             return;
  743.  
  744.         MagicEffects now = creatureStats.getSpells().getMagicEffects();
  745.  
  746.         if (creature.getClass().hasInventoryStore(creature))
  747.         {
  748.             MWWorld::InventoryStore& store = creature.getClass().getInventoryStore (creature);
  749.             now += store.getMagicEffects();
  750.         }
  751.  
  752.         now += creatureStats.getActiveSpells().getMagicEffects();
  753.  
  754.         creatureStats.modifyMagicEffects(now);
  755.     }
  756.  
  757.     void Actors::calculateDynamicStats (const MWWorld::Ptr& ptr)
  758.     {
  759.         CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
  760.  
  761.         int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
  762.  
  763.         float base = 1.f;
  764.         if (ptr == getPlayer())
  765.             base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat();
  766.         else
  767.             base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCbaseMagickaMult")->mValue.getFloat();
  768.  
  769.         double magickaFactor = base +
  770.             creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1;
  771.  
  772.         DynamicStat<float> magicka = creatureStats.getMagicka();
  773.         float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase();
  774.         float currentToBaseRatio = (magicka.getCurrent() / magicka.getBase());
  775.         magicka.setModified(magicka.getModified() + diff, 0);
  776.         magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true);
  777.         creatureStats.setMagicka(magicka);
  778.     }
  779.  
  780.     void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, double hours, bool sleep)
  781.     {
  782.         MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
  783.         if (stats.isDead())
  784.             return;
  785.  
  786.         const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
  787.  
  788.         if (sleep)
  789.         {
  790.             float health, magicka;
  791.             getRestorationPerHourOfSleep(ptr, health, magicka);
  792.  
  793.             DynamicStat<float> stat = stats.getHealth();
  794.             stat.setCurrent(stat.getCurrent() + health * hours);
  795.             stats.setHealth(stat);
  796.  
  797.             double restoreHours = hours;
  798.             bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;
  799.             if (stunted)
  800.             {
  801.                 // Stunted Magicka effect should be taken into account.
  802.                 GetStuntedMagickaDuration visitor(ptr);
  803.                 stats.getActiveSpells().visitEffectSources(visitor);
  804.                 stats.getSpells().visitEffectSources(visitor);
  805.                 if (ptr.getClass().hasInventoryStore(ptr))
  806.                     ptr.getClass().getInventoryStore(ptr).visitEffectSources(visitor);
  807.  
  808.                 // Take a maximum remaining duration of Stunted Magicka effects (-1 is a constant one) in game hours.
  809.                 if (visitor.mRemainingTime > 0)
  810.                 {
  811.                     double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
  812.                     restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f);
  813.                 }
  814.                 else if (visitor.mRemainingTime == -1)
  815.                     restoreHours = 0;
  816.             }
  817.  
  818.             if (restoreHours > 0)
  819.             {
  820.                 stat = stats.getMagicka();
  821.                 stat.setCurrent(stat.getCurrent() + magicka * restoreHours);
  822.                 stats.setMagicka(stat);
  823.             }
  824.         }
  825.  
  826.         // Current fatigue can be above base value due to a fortify effect.
  827.         // In that case stop here and don't try to restore.
  828.         DynamicStat<float> fatigue = stats.getFatigue();
  829.         if (fatigue.getCurrent() >= fatigue.getBase())
  830.             return;
  831.  
  832.         // Restore fatigue
  833.         float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
  834.         float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
  835.         float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat ();
  836.  
  837.         int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
  838.  
  839.         float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
  840.         if (normalizedEncumbrance > 1)
  841.             normalizedEncumbrance = 1;
  842.  
  843.         float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);
  844.         x *= fEndFatigueMult * endurance;
  845.  
  846.         fatigue.setCurrent (fatigue.getCurrent() + 3600 * x * hours);
  847.         stats.setFatigue (fatigue);
  848.     }
  849.  
  850.     void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration)
  851.     {
  852.         if (ptr.getClass().getCreatureStats(ptr).isDead())
  853.             return;
  854.  
  855.         MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
  856.  
  857.         // Current fatigue can be above base value due to a fortify effect.
  858.         // In that case stop here and don't try to restore.
  859.         DynamicStat<float> fatigue = stats.getFatigue();
  860.         if (fatigue.getCurrent() >= fatigue.getBase())
  861.             return;
  862.  
  863.         // Restore fatigue
  864.         int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
  865.         const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
  866.         static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
  867.         static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
  868.  
  869.         float x = fFatigueReturnBase + fFatigueReturnMult * endurance;
  870.  
  871.         fatigue.setCurrent (fatigue.getCurrent() + duration * x);
  872.         stats.setFatigue (fatigue);
  873.     }
  874.  
  875.     class ExpiryVisitor : public EffectSourceVisitor
  876.     {
  877.         private:
  878.             MWWorld::Ptr mActor;
  879.             float mDuration;
  880.  
  881.         public:
  882.             ExpiryVisitor(const MWWorld::Ptr& actor, float duration)
  883.                 : mActor(actor), mDuration(duration)
  884.             {
  885.             }
  886.  
  887.             virtual void visit (MWMechanics::EffectKey key,
  888.                                 const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,
  889.                                 float magnitude, float remainingTime = -1, float /*totalTime*/ = -1)
  890.             {
  891.                 if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration)
  892.                 {
  893.                     CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);
  894.                     if (effectTick(creatureStats, mActor, key, magnitude * remainingTime))
  895.                         creatureStats.getMagicEffects().add(key, -magnitude);
  896.                 }
  897.             }
  898.     };
  899.  
  900.     void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)
  901.     {
  902.         CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
  903.         const MagicEffects &effects = creatureStats.getMagicEffects();
  904.  
  905.         bool wasDead = creatureStats.isDead();
  906.  
  907.         if (duration > 0)
  908.         {
  909.             // Apply correct magnitude for tickable effects that have just expired,
  910.             // in case duration > remaining time of effect.
  911.             // One case where this will happen is when the player uses the rest/wait command
  912.             // while there is a tickable effect active that should expire before the end of the rest/wait.
  913.             ExpiryVisitor visitor(ptr, duration);
  914.             creatureStats.getActiveSpells().visitEffectSources(visitor);
  915.  
  916.             for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it)
  917.             {
  918.                 // tickable effects (i.e. effects having a lasting impact after expiry)
  919.                 effectTick(creatureStats, ptr, it->first, it->second.getMagnitude() * duration);
  920.  
  921.                 // instant effects are already applied on spell impact in spellcasting.cpp, but may also come from permanent abilities
  922.                 if (it->second.getMagnitude() > 0)
  923.                 {
  924.                     CastSpell cast(ptr, ptr);
  925.                     if (cast.applyInstantEffect(ptr, ptr, it->first, it->second.getMagnitude()))
  926.                     {
  927.                         creatureStats.getSpells().purgeEffect(it->first.mId);
  928.                         creatureStats.getActiveSpells().purgeEffect(it->first.mId);
  929.                         if (ptr.getClass().hasInventoryStore(ptr))
  930.                             ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first.mId);
  931.                     }
  932.                 }
  933.             }
  934.         }
  935.  
  936.         // purge levitate effect if levitation is disabled
  937.         // check only modifier, because base value can be setted from SetFlying console command.
  938.         if (MWBase::Environment::get().getWorld()->isLevitationEnabled() == false && effects.get(ESM::MagicEffect::Levitate).getModifier() > 0)
  939.         {
  940.             creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Levitate);
  941.             creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Levitate);
  942.             if (ptr.getClass().hasInventoryStore(ptr))
  943.                 ptr.getClass().getInventoryStore(ptr).purgeEffect(ESM::MagicEffect::Levitate);
  944.  
  945.             if (ptr == getPlayer())
  946.             {
  947.                 MWBase::Environment::get().getWindowManager()->messageBox ("#{sLevitateDisabled}");
  948.             }
  949.         }
  950.  
  951.         // dynamic stats
  952.         for (int i = 0; i < 3; ++i)
  953.         {
  954.             DynamicStat<float> stat = creatureStats.getDynamic(i);
  955.             stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth + i).getMagnitude() -
  956.                 effects.get(ESM::MagicEffect::DrainHealth + i).getMagnitude(),
  957.                 // Magicka can be decreased below zero due to a fortify effect wearing off
  958.                 // Fatigue can be decreased below zero meaning the actor will be knocked out
  959.                 i == 1 || i == 2);
  960.  
  961.             creatureStats.setDynamic(i, stat);
  962.         }
  963.  
  964.         // attributes
  965.         for(int i = 0;i < ESM::Attribute::Length;++i)
  966.         {
  967.             AttributeValue stat = creatureStats.getAttribute(i);
  968.             stat.setModifier(static_cast<int>(effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).getMagnitude() -
  969.                              effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).getMagnitude() -
  970.                              effects.get(EffectKey(ESM::MagicEffect::AbsorbAttribute, i)).getMagnitude()));
  971.  
  972.             creatureStats.setAttribute(i, stat);
  973.         }
  974.  
  975.         if (creatureStats.needToRecalcDynamicStats())
  976.             calculateDynamicStats(ptr);
  977.  
  978.         {
  979.             Spells & spells = creatureStats.getSpells();
  980.             for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
  981.             {
  982.                 if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end())
  983.                 {
  984.                     if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening)
  985.                     {
  986.                         spells.worsenCorprus(it->first);
  987.  
  988.                         if (ptr == getPlayer())
  989.                             MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
  990.                     }
  991.                 }
  992.             }
  993.         }
  994.  
  995.         // AI setting modifiers
  996.         int creature = !ptr.getClass().isNpc();
  997.         if (creature && ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Humanoid)
  998.             creature = false;
  999.         // Note: the Creature variants only work on normal creatures, not on daedra or undead creatures.
  1000.         if (!creature || ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Creatures)
  1001.         {
  1002.             Stat<int> stat = creatureStats.getAiSetting(CreatureStats::AI_Fight);
  1003.             stat.setModifier(static_cast<int>(effects.get(ESM::MagicEffect::FrenzyHumanoid + creature).getMagnitude()
  1004.                 - effects.get(ESM::MagicEffect::CalmHumanoid+creature).getMagnitude()));
  1005.             creatureStats.setAiSetting(CreatureStats::AI_Fight, stat);
  1006.  
  1007.             stat = creatureStats.getAiSetting(CreatureStats::AI_Flee);
  1008.             stat.setModifier(static_cast<int>(effects.get(ESM::MagicEffect::DemoralizeHumanoid + creature).getMagnitude()
  1009.                 - effects.get(ESM::MagicEffect::RallyHumanoid+creature).getMagnitude()));
  1010.             creatureStats.setAiSetting(CreatureStats::AI_Flee, stat);
  1011.         }
  1012.         if (creature && ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Undead)
  1013.         {
  1014.             Stat<int> stat = creatureStats.getAiSetting(CreatureStats::AI_Flee);
  1015.             stat.setModifier(static_cast<int>(effects.get(ESM::MagicEffect::TurnUndead).getMagnitude()));
  1016.             creatureStats.setAiSetting(CreatureStats::AI_Flee, stat);
  1017.         }
  1018.  
  1019.         if (!wasDead && creatureStats.isDead())
  1020.         {
  1021.             // The actor was killed by a magic effect. Figure out if the player was responsible for it.
  1022.             const ActiveSpells& spells = creatureStats.getActiveSpells();
  1023.             MWWorld::Ptr player = getPlayer();
  1024.             std::set<MWWorld::Ptr> playerFollowers;
  1025.             getActorsSidingWith(player, playerFollowers);
  1026.  
  1027.             for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
  1028.             {
  1029.                 bool actorKilled = false;
  1030.  
  1031.                 const ActiveSpells::ActiveSpellParams& spell = it->second;
  1032.                 MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
  1033.                 for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
  1034.                      effectIt != spell.mEffects.end(); ++effectIt)
  1035.                 {
  1036.                     int effectId = effectIt->mEffectId;
  1037.                     bool isDamageEffect = false;
  1038.  
  1039.                     int damageEffects[] = {
  1040.                         ESM::MagicEffect::FireDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::FrostDamage, ESM::MagicEffect::Poison,
  1041.                         ESM::MagicEffect::SunDamage, ESM::MagicEffect::DamageHealth, ESM::MagicEffect::AbsorbHealth
  1042.                     };
  1043.  
  1044.                     for (unsigned int i=0; i<sizeof(damageEffects)/sizeof(int); ++i)
  1045.                     {
  1046.                         if (damageEffects[i] == effectId)
  1047.                             isDamageEffect = true;
  1048.                     }
  1049.  
  1050.                     if (isDamageEffect)
  1051.                     {
  1052.                         if (caster == player || playerFollowers.find(caster) != playerFollowers.end())
  1053.                         {
  1054.                             if (caster.getClass().isNpc() && caster.getClass().getNpcStats(caster).isWerewolf())
  1055.                                 caster.getClass().getNpcStats(caster).addWerewolfKill();
  1056.  
  1057.                             MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
  1058.                             actorKilled = true;
  1059.                             break;
  1060.                         }
  1061.                     }
  1062.                 }
  1063.  
  1064.                 if (actorKilled)
  1065.                     break;
  1066.             }
  1067.         }
  1068.  
  1069.         // TODO: dirty flag for magic effects to avoid some unnecessary work below?
  1070.  
  1071.         // any value of calm > 0 will stop the actor from fighting
  1072.         if ((effects.get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0 && ptr.getClass().isNpc())
  1073.             || (effects.get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0 && !ptr.getClass().isNpc()))
  1074.             creatureStats.getAiSequence().stopCombat();
  1075.  
  1076.         // Update bound effects
  1077.         // Note: in vanilla MW multiple bound items of the same type can be created by different spells.
  1078.         // As these extra copies are kinda useless this may or may not be important.
  1079.         static std::map<int, std::string> boundItemsMap;
  1080.         if (boundItemsMap.empty())
  1081.         {
  1082.             boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "sMagicBoundBattleAxeID";
  1083.             boundItemsMap[ESM::MagicEffect::BoundBoots] = "sMagicBoundBootsID";
  1084.             boundItemsMap[ESM::MagicEffect::BoundCuirass] = "sMagicBoundCuirassID";
  1085.             boundItemsMap[ESM::MagicEffect::BoundDagger] = "sMagicBoundDaggerID";
  1086.             boundItemsMap[ESM::MagicEffect::BoundGloves] = "sMagicBoundLeftGauntletID"; // Note: needs RightGauntlet variant too (see below)
  1087.             boundItemsMap[ESM::MagicEffect::BoundHelm] = "sMagicBoundHelmID";
  1088.             boundItemsMap[ESM::MagicEffect::BoundLongbow] = "sMagicBoundLongbowID";
  1089.             boundItemsMap[ESM::MagicEffect::BoundLongsword] = "sMagicBoundLongswordID";
  1090.             boundItemsMap[ESM::MagicEffect::BoundMace] = "sMagicBoundMaceID";
  1091.             boundItemsMap[ESM::MagicEffect::BoundShield] = "sMagicBoundShieldID";
  1092.             boundItemsMap[ESM::MagicEffect::BoundSpear] = "sMagicBoundSpearID";
  1093.         }
  1094.  
  1095.         for (std::map<int, std::string>::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it)
  1096.         {
  1097.             bool found = creatureStats.mBoundItems.find(it->first) != creatureStats.mBoundItems.end();
  1098.             float magnitude = effects.get(it->first).getMagnitude();
  1099.             if (found != (magnitude > 0))
  1100.             {
  1101.                 if (magnitude > 0)
  1102.                     creatureStats.mBoundItems.insert(it->first);
  1103.                 else
  1104.                     creatureStats.mBoundItems.erase(it->first);
  1105.  
  1106.                 std::string itemGmst = it->second;
  1107.                 std::string item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
  1108.                             itemGmst)->mValue.getString();
  1109.  
  1110.                 magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);
  1111.  
  1112.                 if (it->first == ESM::MagicEffect::BoundGloves)
  1113.                 {
  1114.                     item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
  1115.                                 "sMagicBoundRightGauntletID")->mValue.getString();
  1116.                     magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);
  1117.                 }
  1118.             }
  1119.         }
  1120.  
  1121.         bool hasSummonEffect = false;
  1122.         for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it)
  1123.         {
  1124.             if (isSummoningEffect(it->first.mId))
  1125.             {
  1126.                 hasSummonEffect = true;
  1127.                 break;
  1128.             }
  1129.         }
  1130.  
  1131.         if (!creatureStats.getSummonedCreatureMap().empty() || !creatureStats.getSummonedCreatureGraveyard().empty() || hasSummonEffect)
  1132.         {
  1133.             UpdateSummonedCreatures updateSummonedCreatures(ptr);
  1134.             creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
  1135.             if (ptr.getClass().hasInventoryStore(ptr))
  1136.                 ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
  1137.             updateSummonedCreatures.process(mTimerDisposeSummonsCorpses == 0.f);
  1138.         }
  1139.     }
  1140.  
  1141.     void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration)
  1142.     {
  1143.         NpcStats &npcStats = ptr.getClass().getNpcStats(ptr);
  1144.         const MagicEffects &effects = npcStats.getMagicEffects();
  1145.  
  1146.         // skills
  1147.         for(int i = 0;i < ESM::Skill::Length;++i)
  1148.         {
  1149.             SkillValue& skill = npcStats.getSkill(i);
  1150.             skill.setModifier(static_cast<int>(effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).getMagnitude() -
  1151.                              effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).getMagnitude() -
  1152.                              effects.get(EffectKey(ESM::MagicEffect::AbsorbSkill, i)).getMagnitude()));
  1153.         }
  1154.     }
  1155.  
  1156.     bool Actors::isAttackPreparing(const MWWorld::Ptr& ptr)
  1157.     {
  1158.         PtrActorMap::iterator it = mActors.find(ptr);
  1159.         if (it == mActors.end())
  1160.             return false;
  1161.         CharacterController* ctrl = it->second->getCharacterController();
  1162.  
  1163.         return ctrl->isAttackPreparing();
  1164.     }
  1165.  
  1166.     bool Actors::isRunning(const MWWorld::Ptr& ptr)
  1167.     {
  1168.         PtrActorMap::iterator it = mActors.find(ptr);
  1169.         if (it == mActors.end())
  1170.             return false;
  1171.         CharacterController* ctrl = it->second->getCharacterController();
  1172.  
  1173.         return ctrl->isRunning();
  1174.     }
  1175.  
  1176.     bool Actors::isSneaking(const MWWorld::Ptr& ptr)
  1177.     {
  1178.         PtrActorMap::iterator it = mActors.find(ptr);
  1179.         if (it == mActors.end())
  1180.             return false;
  1181.         CharacterController* ctrl = it->second->getCharacterController();
  1182.  
  1183.         return ctrl->isSneaking();
  1184.     }
  1185.  
  1186.     void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer)
  1187.     {
  1188.         NpcStats &stats = ptr.getClass().getNpcStats(ptr);
  1189.  
  1190.         // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST
  1191.         static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->mValue.getFloat();
  1192.         if (stats.getTimeToStartDrowning() == -1.f)
  1193.             stats.setTimeToStartDrowning(fHoldBreathTime);
  1194.  
  1195.         if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2)
  1196.         {
  1197.             AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
  1198.             if (seq.getTypeId() != AiPackage::TypeIdBreathe) //Only add it once
  1199.                 seq.stack(AiBreathe(), ptr);
  1200.         }
  1201.  
  1202.         MWBase::World *world = MWBase::Environment::get().getWorld();
  1203.         bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3())));
  1204.         if((world->isSubmerged(ptr) || knockedOutUnderwater)
  1205.            && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0)
  1206.         {
  1207.             float timeLeft = 0.0f;
  1208.             if(knockedOutUnderwater)
  1209.                 stats.setTimeToStartDrowning(0);
  1210.             else
  1211.             {
  1212.                 timeLeft = stats.getTimeToStartDrowning() - duration;
  1213.                 if(timeLeft < 0.0f)
  1214.                     timeLeft = 0.0f;
  1215.                 stats.setTimeToStartDrowning(timeLeft);
  1216.             }
  1217.  
  1218.             bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState();
  1219.  
  1220.             if(timeLeft == 0.0f && !godmode)
  1221.             {
  1222.                 // If drowning, apply 3 points of damage per second
  1223.                 static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->mValue.getFloat();
  1224.                 DynamicStat<float> health = stats.getHealth();
  1225.                 health.setCurrent(health.getCurrent() - fSuffocationDamage*duration);
  1226.                 stats.setHealth(health);
  1227.  
  1228.                 // Play a drowning sound
  1229.                 MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager();
  1230.                 if(!sndmgr->getSoundPlaying(ptr, "drown"))
  1231.                     sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f);
  1232.  
  1233.                 if(isPlayer)
  1234.                     MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
  1235.             }
  1236.         }
  1237.         else
  1238.             stats.setTimeToStartDrowning(fHoldBreathTime);
  1239.     }
  1240.  
  1241.     void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip)
  1242.     {
  1243.         bool isPlayer = (ptr == getPlayer());
  1244.  
  1245.         MWWorld::InventoryStore &inventoryStore = ptr.getClass().getInventoryStore(ptr);
  1246.         MWWorld::ContainerStoreIterator heldIter =
  1247.                 inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
  1248.         /**
  1249.          * Automatically equip NPCs torches at night and unequip them at day
  1250.          */
  1251.         if (!isPlayer)
  1252.         {
  1253.             MWWorld::ContainerStoreIterator torch = inventoryStore.end();
  1254.             for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it)
  1255.             {
  1256.                 if (it->getTypeName() == typeid(ESM::Light).name() &&
  1257.                     it->getClass().canBeEquipped(*it, ptr).first)
  1258.                 {
  1259.                     torch = it;
  1260.                     break;
  1261.                 }
  1262.             }
  1263.  
  1264.             if (mayEquip)
  1265.             {
  1266.                 if (torch != inventoryStore.end())
  1267.                 {
  1268.                     if (!ptr.getClass().getCreatureStats (ptr).getAiSequence().isInCombat())
  1269.                     {
  1270.                         // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.
  1271.                         if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())
  1272.                             inventoryStore.unequipItem(*heldIter, ptr);
  1273.                     }
  1274.                     else if (heldIter == inventoryStore.end() || heldIter->getTypeName() == typeid(ESM::Light).name())
  1275.                     {
  1276.                         // For hostile NPCs, see if they have anything better to equip first
  1277.                         inventoryStore.autoEquip(ptr);
  1278.                     }
  1279.  
  1280.                     heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
  1281.  
  1282.                     // If we have a torch and can equip it, then equip it now.
  1283.                     if (heldIter == inventoryStore.end())
  1284.                     {
  1285.                         inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
  1286.                     }
  1287.                 }
  1288.             }
  1289.             else
  1290.             {
  1291.                 if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name())
  1292.                 {
  1293.                     // At day, unequip lights and auto equip shields or other suitable items
  1294.                     // (Note: autoEquip will ignore lights)
  1295.                     inventoryStore.autoEquip(ptr);
  1296.                 }
  1297.             }
  1298.         }
  1299.  
  1300.         heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
  1301.  
  1302.         //If holding a light...
  1303.         if(heldIter.getType() == MWWorld::ContainerStore::Type_Light)
  1304.         {
  1305.             // Use time from the player's light
  1306.             if(isPlayer)
  1307.             {
  1308.                 float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter);
  1309.  
  1310.                 // -1 is infinite light source. Other negative values are treated as 0.
  1311.                 if(timeRemaining != -1.0f)
  1312.                 {
  1313.                     timeRemaining -= duration;
  1314.  
  1315.                     if(timeRemaining > 0.0f)
  1316.                         heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining);
  1317.                     else
  1318.                     {
  1319.                         inventoryStore.remove(*heldIter, 1, ptr); // remove it
  1320.                         return;
  1321.                     }
  1322.                 }
  1323.             }
  1324.  
  1325.             // Both NPC and player lights extinguish in water.
  1326.             if(MWBase::Environment::get().getWorld()->isSwimming(ptr))
  1327.             {
  1328.                 inventoryStore.remove(*heldIter, 1, ptr); // remove it
  1329.  
  1330.                 // ...But, only the player makes a sound.
  1331.                 if(isPlayer)
  1332.                     MWBase::Environment::get().getSoundManager()->playSound("torch out",
  1333.                             1.0, 1.0, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);
  1334.             }
  1335.         }
  1336.     }
  1337.  
  1338.     void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration)
  1339.     {
  1340.         MWWorld::Ptr player = getPlayer();
  1341.         if (ptr != player && ptr.getClass().isNpc())
  1342.         {
  1343.             // get stats of witness
  1344.             CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
  1345.             NpcStats& npcStats = ptr.getClass().getNpcStats(ptr);
  1346.  
  1347.             if (player.getClass().getNpcStats(player).isWerewolf())
  1348.                 return;
  1349.  
  1350.             if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()
  1351.                 && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)
  1352.             {
  1353.                 const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
  1354.                 static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->mValue.getInteger();
  1355.                 // Force dialogue on sight if bounty is greater than the cutoff
  1356.                 // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)
  1357.                 if (   player.getClass().getNpcStats(player).getBounty() >= cutoff
  1358.                        // TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?
  1359.                     && MWBase::Environment::get().getWorld()->getLOS(ptr, player)
  1360.                     && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
  1361.                 {
  1362.                     static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
  1363.                     if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier)
  1364.                     {
  1365.                         MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
  1366.                         creatureStats.setHitAttemptActorId(player.getClass().getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable
  1367.                     }
  1368.                     else
  1369.                         creatureStats.getAiSequence().stack(AiPursue(player), ptr);
  1370.                     creatureStats.setAlarmed(true);
  1371.                     npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
  1372.                 }
  1373.             }
  1374.  
  1375.             // if I was a witness to a crime
  1376.             if (npcStats.getCrimeId() != -1)
  1377.             {
  1378.                 // if you've paid for your crimes and I havent noticed
  1379.                 if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() )
  1380.                 {
  1381.                     // Calm witness down
  1382.                     if (ptr.getClass().isClass(ptr, "Guard"))
  1383.                         creatureStats.getAiSequence().stopPursuit();
  1384.                     creatureStats.getAiSequence().stopCombat();
  1385.  
  1386.                     // Reset factors to attack
  1387.                     creatureStats.setAttacked(false);
  1388.                     creatureStats.setAlarmed(false);
  1389.                     creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr));
  1390.  
  1391.                     // Update witness crime id
  1392.                     npcStats.setCrimeId(-1);
  1393.                 }
  1394.             }
  1395.         }
  1396.     }
  1397.  
  1398.     Actors::Actors()
  1399.     {
  1400.         mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
  1401.  
  1402.         updateProcessingRange();
  1403.     }
  1404.  
  1405.     Actors::~Actors()
  1406.     {
  1407.         clear();
  1408.     }
  1409.  
  1410.     float Actors::getProcessingRange() const
  1411.     {
  1412.         return mActorsProcessingRange;
  1413.     }
  1414.  
  1415.     void Actors::updateProcessingRange()
  1416.     {
  1417.         // We have to cap it since using high values (larger than 7168) will make some quests harder or impossible to complete (bug #1876)
  1418.         static const float maxProcessingRange = 7168.f;
  1419.         static const float minProcessingRange = maxProcessingRange / 2.f;
  1420.  
  1421.         float actorsProcessingRange = Settings::Manager::getFloat("actors processing range", "Game");
  1422.         actorsProcessingRange = std::min(actorsProcessingRange, maxProcessingRange);
  1423.         actorsProcessingRange = std::max(actorsProcessingRange, minProcessingRange);
  1424.         mActorsProcessingRange = actorsProcessingRange;
  1425.     }
  1426.  
  1427.     void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately)
  1428.     {
  1429.         removeActor(ptr);
  1430.  
  1431.         MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
  1432.         if (!anim)
  1433.             return;
  1434.         mActors.insert(std::make_pair(ptr, new Actor(ptr, anim)));
  1435.  
  1436.         CharacterController* ctrl = mActors[ptr]->getCharacterController();
  1437.         if (updateImmediately)
  1438.             ctrl->update(0);
  1439.  
  1440.         // We should initially hide actors outside of processing range.
  1441.         // Note: since we update player after other actors, distance will be incorrect during teleportation.
  1442.         // Do not update visibility if player was teleported, so actors will be visible during teleportation frame.
  1443.         if (MWBase::Environment::get().getWorld()->getPlayer().wasTeleported())
  1444.             return;
  1445.  
  1446.         updateVisibility(ptr, ctrl);
  1447.     }
  1448.  
  1449.     void Actors::updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl)
  1450.     {
  1451.         MWWorld::Ptr player = MWMechanics::getPlayer();
  1452.         if (ptr == player)
  1453.             return;
  1454.  
  1455.         const float dist = (player.getRefData().getPosition().asVec3() - ptr.getRefData().getPosition().asVec3()).length();
  1456.         if (dist > mActorsProcessingRange)
  1457.         {
  1458.             ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled);
  1459.             return;
  1460.         }
  1461.         else
  1462.             ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor);
  1463.  
  1464.         // Fade away actors on large distance (>90% of actor's processing distance)
  1465.         float visibilityRatio = 1.0;
  1466.         float fadeStartDistance = mActorsProcessingRange*0.9f;
  1467.         float fadeEndDistance = mActorsProcessingRange;
  1468.         float fadeRatio = (dist - fadeStartDistance)/(fadeEndDistance - fadeStartDistance);
  1469.         if (fadeRatio > 0)
  1470.             visibilityRatio -= std::max(0.f, fadeRatio);
  1471.  
  1472.         visibilityRatio = std::min(1.f, visibilityRatio);
  1473.  
  1474.         ctrl->setVisibility(visibilityRatio);
  1475.     }
  1476.  
  1477.     void Actors::removeActor (const MWWorld::Ptr& ptr)
  1478.     {
  1479.         PtrActorMap::iterator iter = mActors.find(ptr);
  1480.         if(iter != mActors.end())
  1481.         {
  1482.             delete iter->second;
  1483.             mActors.erase(iter);
  1484.         }
  1485.     }
  1486.  
  1487.     void Actors::castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell)
  1488.     {
  1489.         PtrActorMap::iterator iter = mActors.find(ptr);
  1490.         if(iter != mActors.end())
  1491.             iter->second->getCharacterController()->castSpell(spellId, manualSpell);
  1492.     }
  1493.  
  1494.     bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
  1495.     {
  1496.         if (!actor.getClass().isActor())
  1497.             return false;
  1498.  
  1499.         // If an observer is NPC, check if he detected an actor
  1500.         if (!observer.isEmpty() && observer.getClass().isNpc())
  1501.         {
  1502.             return
  1503.                 MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
  1504.                 MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
  1505.         }
  1506.  
  1507.         // Otherwise check if any actor in AI processing range sees the target actor
  1508.         std::vector<MWWorld::Ptr> neighbors;
  1509.         osg::Vec3f position (actor.getRefData().getPosition().asVec3());
  1510.         getObjectsInRange(position, mActorsProcessingRange, neighbors);
  1511.         for (const MWWorld::Ptr &neighbor : neighbors)
  1512.         {
  1513.             if (neighbor == actor)
  1514.                 continue;
  1515.  
  1516.             bool result = MWBase::Environment::get().getWorld()->getLOS(neighbor, actor)
  1517.                        && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, neighbor);
  1518.  
  1519.             if (result)
  1520.                 return true;
  1521.         }
  1522.  
  1523.         return false;
  1524.     }
  1525.  
  1526.     void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
  1527.     {
  1528.         PtrActorMap::iterator iter = mActors.find(old);
  1529.         if(iter != mActors.end())
  1530.         {
  1531.             Actor *actor = iter->second;
  1532.             mActors.erase(iter);
  1533.  
  1534.             actor->updatePtr(ptr);
  1535.             mActors.insert(std::make_pair(ptr, actor));
  1536.         }
  1537.     }
  1538.  
  1539.     void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore)
  1540.     {
  1541.         PtrActorMap::iterator iter = mActors.begin();
  1542.         while(iter != mActors.end())
  1543.         {
  1544.             if((iter->first.isInCell() && iter->first.getCell()==cellStore) && iter->first != ignore)
  1545.             {
  1546.                 delete iter->second;
  1547.                 mActors.erase(iter++);
  1548.             }
  1549.             else
  1550.                 ++iter;
  1551.         }
  1552.     }
  1553.  
  1554.     void Actors::updateCombatMusic ()
  1555.     {
  1556.         MWWorld::Ptr player = getPlayer();
  1557.         const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
  1558.         bool hasHostiles = false; // need to know this to play Battle music
  1559.         bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive();
  1560.  
  1561.         if (aiActive)
  1562.         {
  1563.             for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
  1564.             {
  1565.                 if (iter->first == player) continue;
  1566.  
  1567.                 bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= mActorsProcessingRange*mActorsProcessingRange;
  1568.                 if (inProcessingRange)
  1569.                 {
  1570.                     MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
  1571.                     if (!stats.isDead() && stats.getAiSequence().isInCombat())
  1572.                     {
  1573.                         hasHostiles = true;
  1574.                         break;
  1575.                     }
  1576.                 }
  1577.             }
  1578.         }
  1579.  
  1580.         // check if we still have any player enemies to switch music
  1581.         static int currentMusic = 0;
  1582.  
  1583.         if (currentMusic != 1 && !hasHostiles && !(player.getClass().getCreatureStats(player).isDead() &&
  1584.         MWBase::Environment::get().getSoundManager()->isMusicPlaying()))
  1585.         {
  1586.             MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
  1587.             currentMusic = 1;
  1588.         }
  1589.         else if (currentMusic != 2 && hasHostiles)
  1590.         {
  1591.             MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle"));
  1592.             currentMusic = 2;
  1593.         }
  1594.  
  1595.     }
  1596.  
  1597.     void Actors::update (float duration, bool paused)
  1598.     {
  1599.         if(!paused)
  1600.         {
  1601.             static float timerUpdateAITargets = 0;
  1602.             static float timerUpdateHeadTrack = 0;
  1603.             static float timerUpdateEquippedLight = 0;
  1604.             static float timerUpdateHello = 0;
  1605.             const float updateEquippedLightInterval = 1.0f;
  1606.  
  1607.             // target lists get updated once every 1.0 sec
  1608.             if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
  1609.             if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
  1610.             if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
  1611.             if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
  1612.             if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
  1613.  
  1614.             // show torches only when there are darkness and no precipitations
  1615.             MWBase::World* world = MWBase::Environment::get().getWorld();
  1616.             bool showTorches = world->useTorches();
  1617.  
  1618.             MWWorld::Ptr player = getPlayer();
  1619.             const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
  1620.  
  1621.             /// \todo move update logic to Actor class where appropriate
  1622.  
  1623.             std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> > cachedAllies; // will be filled as engageCombat iterates
  1624.  
  1625.             bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive();
  1626.             int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId();
  1627.             if (attackedByPlayerId != -1)
  1628.             {
  1629.                 const MWWorld::Ptr playerHitAttemptActor = world->searchPtrViaActorId(attackedByPlayerId);
  1630.  
  1631.                 if (!playerHitAttemptActor.isInCell())
  1632.                     player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);
  1633.             }
  1634.  
  1635.              // AI and magic effects update
  1636.             for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
  1637.             {
  1638.                 bool isPlayer = iter->first == player;
  1639.                 CharacterController* ctrl = iter->second->getCharacterController();
  1640.  
  1641.                 float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2();
  1642.                 // AI processing is only done within given distance to the player.
  1643.                 bool inProcessingRange = distSqr <= mActorsProcessingRange*mActorsProcessingRange;
  1644.  
  1645.                 if (isPlayer)
  1646.                     ctrl->setAttackingOrSpell(world->getPlayer().getAttackingOrSpell());
  1647.  
  1648.                 // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player.
  1649.                 if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead()
  1650.                     || !iter->first.getClass().getCreatureStats(iter->first).getAiSequence().isInCombat()
  1651.                     || !inProcessingRange))
  1652.                 {
  1653.                     iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(-1);
  1654.                     if (player.getClass().getCreatureStats(player).getHitAttemptActorId() == iter->first.getClass().getCreatureStats(iter->first).getActorId())
  1655.                         player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);
  1656.                 }
  1657.  
  1658.                 // For dead actors we need to remove looping spell particles
  1659.                 if (iter->first.getClass().getCreatureStats(iter->first).isDead())
  1660.                     ctrl->updateContinuousVfx();
  1661.                 else
  1662.                 {
  1663.                     bool cellChanged = world->hasCellChanged();
  1664.                     MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports
  1665.                     updateActor(actor, duration);
  1666.  
  1667.                     // Looping magic VFX update
  1668.                     // Note: we need to do this before any of the animations are updated.
  1669.                     // Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
  1670.                     // so updating VFX immediately after that would just remove the particle effects instantly.
  1671.                     // There needs to be a magic effect update in between.
  1672.                     ctrl->updateContinuousVfx();
  1673.  
  1674.                     if (!cellChanged && world->hasCellChanged())
  1675.                     {
  1676.                         return; // for now abort update of the old cell when cell changes by teleportation magic effect
  1677.                                 // a better solution might be to apply cell changes at the end of the frame
  1678.                     }
  1679.                     if (aiActive && inProcessingRange)
  1680.                     {
  1681.                         if (timerUpdateAITargets == 0)
  1682.                         {
  1683.                             if (!isPlayer)
  1684.                                 adjustCommandedActor(iter->first);
  1685.  
  1686.                             for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
  1687.                             {
  1688.                                 if (it->first == iter->first || isPlayer) // player is not AI-controlled
  1689.                                     continue;
  1690.                                 engageCombat(iter->first, it->first, cachedAllies, it->first == player);
  1691.                             }
  1692.                         }
  1693.                         if (timerUpdateHeadTrack == 0)
  1694.                         {
  1695.                             float sqrHeadTrackDistance = std::numeric_limits<float>::max();
  1696.                             MWWorld::Ptr headTrackTarget;
  1697.  
  1698.                             MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
  1699.                             bool firstPersonPlayer = isPlayer && world->isFirstPerson();
  1700.  
  1701.                             // 1. Unconsious actor can not track target
  1702.                             // 2. Actors in combat and pursue mode do not bother to headtrack
  1703.                             // 3. Player character does not use headtracking in the 1st-person view
  1704.                             if (!stats.getKnockedDown() &&
  1705.                                 !stats.getAiSequence().isInCombat() &&
  1706.                                 !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) &&
  1707.                                 !firstPersonPlayer)
  1708.                             {
  1709.                                 for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
  1710.                                 {
  1711.                                     if (it->first == iter->first)
  1712.                                         continue;
  1713.                                     updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance);
  1714.                                 }
  1715.                             }
  1716.  
  1717.                             ctrl->setHeadTrackTarget(headTrackTarget);
  1718.                         }
  1719.  
  1720.                         if (iter->first.getClass().isNpc() && iter->first != player)
  1721.                             updateCrimePursuit(iter->first, duration);
  1722.  
  1723.                         if (iter->first != player)
  1724.                         {
  1725.                             CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
  1726.                             if (isConscious(iter->first))
  1727.                             {
  1728.                                 stats.getAiSequence().execute(iter->first, *ctrl, duration);
  1729.                                 updateGreetingState(iter->first, timerUpdateHello > 0);
  1730.                                 playIdleDialogue(iter->first);
  1731.                                 updateMovementSpeed(iter->first);
  1732.                             }
  1733.                         }
  1734.                     }
  1735.                     else if (aiActive && iter->first != player && isConscious(iter->first))
  1736.                     {
  1737.                         CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
  1738.                         stats.getAiSequence().execute(iter->first, *ctrl, duration, /*outOfRange*/true);
  1739.                     }
  1740.  
  1741.                     if(iter->first.getClass().isNpc())
  1742.                     {
  1743.                         // We can not update drowning state for actors outside of AI distance - they can not resurface to breathe
  1744.                         if (inProcessingRange)
  1745.                             updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer);
  1746.  
  1747.                         calculateNpcStatModifiers(iter->first, duration);
  1748.  
  1749.                         if (timerUpdateEquippedLight == 0)
  1750.                             updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches);
  1751.                     }
  1752.                 }
  1753.             }
  1754.  
  1755.             timerUpdateAITargets += duration;
  1756.             timerUpdateHeadTrack += duration;
  1757.             timerUpdateEquippedLight += duration;
  1758.             timerUpdateHello += duration;
  1759.             mTimerDisposeSummonsCorpses += duration;
  1760.  
  1761.             // Animation/movement update
  1762.             CharacterController* playerCharacter = nullptr;
  1763.             for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
  1764.             {
  1765.                 const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length();
  1766.                 bool isPlayer = iter->first == player;
  1767.                 CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
  1768.                 // Actors with active AI should be able to move.
  1769.                 bool alwaysActive = false;
  1770.                 if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
  1771.                 {
  1772.                     MWMechanics::AiSequence& seq = stats.getAiSequence();
  1773.                     alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive();
  1774.                 }
  1775.                 bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive;
  1776.                 int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
  1777.                 if (isPlayer)
  1778.                     activeFlag = 2;
  1779.                 int active = inRange ? activeFlag : 0;
  1780.  
  1781.                 CharacterController* ctrl = iter->second->getCharacterController();
  1782.                 ctrl->setActive(active);
  1783.  
  1784.                 if (!inRange)
  1785.                 {
  1786.                     iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled);
  1787.                     world->setActorCollisionMode(iter->first, false, false);
  1788.                     continue;
  1789.                 }
  1790.                 else if (!isPlayer)
  1791.                     iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor);
  1792.  
  1793.                 const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead();
  1794.                 if (!isDead && iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
  1795.                     ctrl->skipAnim();
  1796.  
  1797.                 // Handle player last, in case a cell transition occurs by casting a teleportation spell
  1798.                 // (would invalidate the iterator)
  1799.                 if (iter->first == getPlayer())
  1800.                 {
  1801.                     playerCharacter = ctrl;
  1802.                     continue;
  1803.                 }
  1804.  
  1805.                 world->setActorCollisionMode(iter->first, true, !iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished());
  1806.                 ctrl->update(duration);
  1807.  
  1808.                 updateVisibility(iter->first, ctrl);
  1809.             }
  1810.  
  1811.             if (playerCharacter)
  1812.             {
  1813.                 playerCharacter->update(duration);
  1814.                 playerCharacter->setVisibility(1.f);
  1815.             }
  1816.  
  1817.             for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
  1818.             {
  1819.                 const MWWorld::Class &cls = iter->first.getClass();
  1820.                 CreatureStats &stats = cls.getCreatureStats(iter->first);
  1821.  
  1822.                 //KnockedOutOneFrameLogic
  1823.                 //Used for "OnKnockedOut" command
  1824.                 //Put here to ensure that it's run for PRECISELY one frame.
  1825.                 if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())
  1826.                 { //Start it for one frame if nessesary
  1827.                     stats.setKnockedDownOneFrame(true);
  1828.                 }
  1829.                 else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())
  1830.                 { //Turn off KnockedOutOneframe
  1831.                     stats.setKnockedDownOneFrame(false);
  1832.                     stats.setKnockedDownOverOneFrame(true);
  1833.                 }
  1834.             }
  1835.  
  1836.             killDeadActors();
  1837.             updateSneaking(playerCharacter, duration);
  1838.         }
  1839.  
  1840.         updateCombatMusic();
  1841.     }
  1842.  
  1843.     void Actors::notifyDied(const MWWorld::Ptr &actor)
  1844.     {
  1845.         actor.getClass().getCreatureStats(actor).notifyDied();
  1846.  
  1847.         ++mDeathCount[Misc::StringUtils::lowerCase(actor.getCellRef().getRefId())];
  1848.     }
  1849.  
  1850.     void Actors::resurrect(const MWWorld::Ptr &ptr)
  1851.     {
  1852.         PtrActorMap::iterator iter = mActors.find(ptr);
  1853.         if(iter != mActors.end())
  1854.         {
  1855.             if(iter->second->getCharacterController()->isDead())
  1856.             {
  1857.                 // Actor has been resurrected. Notify the CharacterController and re-enable collision.
  1858.                 MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
  1859.                 iter->second->getCharacterController()->resurrect();
  1860.             }
  1861.         }
  1862.     }
  1863.  
  1864.     void Actors::killDeadActors()
  1865.     {
  1866.         for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
  1867.         {
  1868.             const MWWorld::Class &cls = iter->first.getClass();
  1869.             CreatureStats &stats = cls.getCreatureStats(iter->first);
  1870.  
  1871.             if(!stats.isDead())
  1872.                 continue;
  1873.  
  1874.             MWBase::Environment::get().getWorld()->removeActorPath(iter->first);
  1875.             CharacterController::KillResult killResult = iter->second->getCharacterController()->kill();
  1876.             if (killResult == CharacterController::Result_DeathAnimStarted)
  1877.             {
  1878.                 // Play dying words
  1879.                 // Note: It's not known whether the soundgen tags scream, roar, and moan are reliable
  1880.                 // for NPCs since some of the npc death animation files are missing them.
  1881.                 MWBase::Environment::get().getDialogueManager()->say(iter->first, "hit");
  1882.  
  1883.                 // Apply soultrap
  1884.                 //if (iter->first.getTypeName() == typeid(ESM::Creature).name())
  1885.                 {
  1886.                     SoulTrap soulTrap (iter->first);
  1887.                     stats.getActiveSpells().visitEffectSources(soulTrap);
  1888.                 }
  1889.         //else
  1890.         {
  1891.        
  1892. }
  1893.  
  1894.                 // Magic effects will be reset later, and the magic effect that could kill the actor
  1895.                 // needs to be determined now
  1896.                 calculateCreatureStatModifiers(iter->first, 0);
  1897.  
  1898.                 if (cls.isEssential(iter->first))
  1899.                     MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
  1900.             }
  1901.             else if (killResult == CharacterController::Result_DeathAnimJustFinished)
  1902.             {
  1903.                 notifyDied(iter->first);
  1904.  
  1905.                 // Reset magic effects and recalculate derived effects
  1906.                 // One case where we need this is to make sure bound items are removed upon death
  1907.                 stats.modifyMagicEffects(MWMechanics::MagicEffects());
  1908.                 stats.getActiveSpells().clear();
  1909.                 stats.getSpells().clear();
  1910.                 // Make sure spell effects are removed
  1911.                 purgeSpellEffects(stats.getActorId());
  1912.  
  1913.                 // Reset dynamic stats, attributes and skills
  1914.                 calculateCreatureStatModifiers(iter->first, 0);
  1915.                 if (iter->first.getClass().isNpc())
  1916.                     calculateNpcStatModifiers(iter->first, 0);
  1917.  
  1918.                 if( iter->first == getPlayer())
  1919.                 {
  1920.                     //player's death animation is over
  1921.                     MWBase::Environment::get().getStateManager()->askLoadRecent();
  1922.                 }
  1923.                 else
  1924.                 {
  1925.                     // NPC death animation is over, disable actor collision
  1926.                     MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
  1927.                 }
  1928.  
  1929.                 // Play Death Music if it was the player dying
  1930.                 if(iter->first == getPlayer())
  1931.                     MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Death.mp3");
  1932.             }
  1933.         }
  1934.     }
  1935.  
  1936.     void Actors::cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId)
  1937.     {
  1938.         MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);
  1939.         if (!ptr.isEmpty())
  1940.         {
  1941.             MWBase::Environment::get().getWorld()->deleteObject(ptr);
  1942.  
  1943.             const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
  1944.                     .search("VFX_Summon_End");
  1945.             if (fx)
  1946.                 MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
  1947.                     "", ptr.getRefData().getPosition().asVec3());
  1948.  
  1949.             // Remove the summoned creature's summoned creatures as well
  1950.             MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
  1951.             std::map<CreatureStats::SummonKey, int>& creatureMap = stats.getSummonedCreatureMap();
  1952.             for (const auto& creature : creatureMap)
  1953.                 cleanupSummonedCreature(stats, creature.second);
  1954.             creatureMap.clear();
  1955.         }
  1956.         else if (creatureActorId != -1)
  1957.         {
  1958.             // We didn't find the creature. It's probably in an inactive cell.
  1959.             // Add to graveyard so we can delete it when the cell becomes active.
  1960.             std::vector<int>& graveyard = casterStats.getSummonedCreatureGraveyard();
  1961.             graveyard.push_back(creatureActorId);
  1962.         }
  1963.  
  1964.         purgeSpellEffects(creatureActorId);
  1965.     }
  1966.  
  1967.     void Actors::purgeSpellEffects(int casterActorId)
  1968.     {
  1969.         for (PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
  1970.         {
  1971.             MWMechanics::ActiveSpells& spells = iter->first.getClass().getCreatureStats(iter->first).getActiveSpells();
  1972.             spells.purge(casterActorId);
  1973.         }
  1974.     }
  1975.  
  1976.     void Actors::rest(double hours, bool sleep)
  1977.     {
  1978.         float duration = hours * 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor();
  1979.         const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
  1980.         const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
  1981.  
  1982.         for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
  1983.         {
  1984.             if (iter->first.getClass().getCreatureStats(iter->first).isDead())
  1985.                 continue;
  1986.  
  1987.             if (!sleep || iter->first == player)
  1988.                 restoreDynamicStats(iter->first, hours, sleep);
  1989.  
  1990.             if ((!iter->first.getRefData().getBaseNode()) ||
  1991.                     (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)
  1992.                 continue;
  1993.  
  1994.             adjustMagicEffects (iter->first);
  1995.             if (iter->first.getClass().getCreatureStats(iter->first).needToRecalcDynamicStats())
  1996.                 calculateDynamicStats (iter->first);
  1997.  
  1998.             calculateCreatureStatModifiers (iter->first, duration);
  1999.             if (iter->first.getClass().isNpc())
  2000.                 calculateNpcStatModifiers(iter->first, duration);
  2001.  
  2002.             MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
  2003.             if (animation)
  2004.             {
  2005.                 animation->removeEffects();
  2006.                 MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first);
  2007.             }
  2008.  
  2009.         }
  2010.  
  2011.         fastForwardAi();
  2012.     }
  2013.  
  2014.     void Actors::updateSneaking(CharacterController* ctrl, float duration)
  2015.     {
  2016.         static float sneakTimer = 0.f; // Times update of sneak icon
  2017.  
  2018.         if (!ctrl)
  2019.         {
  2020.             MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
  2021.             return;
  2022.         }
  2023.  
  2024.         MWWorld::Ptr player = getPlayer();
  2025.  
  2026.         if (!MWBase::Environment::get().getMechanicsManager()->isSneaking(player))
  2027.         {
  2028.             MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
  2029.             return;
  2030.         }
  2031.  
  2032.         static float sneakSkillTimer = 0.f; // Times sneak skill progress from "avoid notice"
  2033.  
  2034.         MWBase::World* world = MWBase::Environment::get().getWorld();
  2035.         const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();
  2036.         static const float fSneakUseDist = gmst.find("fSneakUseDist")->mValue.getFloat();
  2037.         static const float fSneakUseDelay = gmst.find("fSneakUseDelay")->mValue.getFloat();
  2038.  
  2039.         if (sneakTimer >= fSneakUseDelay)
  2040.             sneakTimer = 0.f;
  2041.  
  2042.         if (sneakTimer == 0.f)
  2043.         {
  2044.             // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress.
  2045.             bool avoidedNotice = false;
  2046.             bool detected = false;
  2047.  
  2048.             std::vector<MWWorld::Ptr> observers;
  2049.             osg::Vec3f position(player.getRefData().getPosition().asVec3());
  2050.             float radius = std::min(fSneakUseDist, mActorsProcessingRange);
  2051.             getObjectsInRange(position, radius, observers);
  2052.  
  2053.             for (const MWWorld::Ptr &observer : observers)
  2054.             {
  2055.                 if (observer == player || observer.getClass().getCreatureStats(observer).isDead())
  2056.                     continue;
  2057.  
  2058.                 if (world->getLOS(player, observer))
  2059.                 {
  2060.                     if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer))
  2061.                     {
  2062.                         detected = true;
  2063.                         avoidedNotice = false;
  2064.                         MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
  2065.                         break;
  2066.                     }
  2067.                     else
  2068.                     {
  2069.                         avoidedNotice = true;
  2070.                     }
  2071.                 }
  2072.             }
  2073.  
  2074.             if (sneakSkillTimer >= fSneakUseDelay)
  2075.                 sneakSkillTimer = 0.f;
  2076.  
  2077.             if (avoidedNotice && sneakSkillTimer == 0.f)
  2078.                 player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0);
  2079.  
  2080.             if (!detected)
  2081.                 MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);
  2082.         }
  2083.  
  2084.         sneakTimer += duration;
  2085.         sneakSkillTimer += duration;
  2086.     }
  2087.  
  2088.     int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const
  2089.     {
  2090.         float healthPerHour, magickaPerHour;
  2091.         getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour);
  2092.  
  2093.         CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
  2094.         bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;
  2095.  
  2096.         float healthHours  = healthPerHour > 0
  2097.                              ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour
  2098.                              : 1.0f;
  2099.         float magickaHours = magickaPerHour > 0 && !stunted
  2100.                               ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour
  2101.                               : 1.0f;
  2102.  
  2103.         int autoHours = static_cast<int>(std::ceil(std::max(1.f, std::max(healthHours, magickaHours))));
  2104.         return autoHours;
  2105.     }
  2106.  
  2107.     int Actors::countDeaths (const std::string& id) const
  2108.     {
  2109.         std::map<std::string, int>::const_iterator iter = mDeathCount.find(id);
  2110.         if(iter != mDeathCount.end())
  2111.             return iter->second;
  2112.         return 0;
  2113.     }
  2114.  
  2115.     void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
  2116.     {
  2117.         PtrActorMap::iterator iter = mActors.find(ptr);
  2118.         if(iter != mActors.end())
  2119.             iter->second->getCharacterController()->forceStateUpdate();
  2120.     }
  2121.  
  2122.     bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)
  2123.     {
  2124.         PtrActorMap::iterator iter = mActors.find(ptr);
  2125.         if(iter != mActors.end())
  2126.         {
  2127.             return iter->second->getCharacterController()->playGroup(groupName, mode, number, persist);
  2128.         }
  2129.         else
  2130.         {
  2131.             Log(Debug::Warning) << "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId();
  2132.             return false;
  2133.         }
  2134.     }
  2135.     void Actors::skipAnimation(const MWWorld::Ptr& ptr)
  2136.     {
  2137.         PtrActorMap::iterator iter = mActors.find(ptr);
  2138.         if(iter != mActors.end())
  2139.             iter->second->getCharacterController()->skipAnim();
  2140.     }
  2141.  
  2142.     bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)
  2143.     {
  2144.         PtrActorMap::iterator iter = mActors.find(ptr);
  2145.         if(iter != mActors.end())
  2146.             return iter->second->getCharacterController()->isAnimPlaying(groupName);
  2147.         return false;
  2148.     }
  2149.  
  2150.     void Actors::persistAnimationStates()
  2151.     {
  2152.         for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
  2153.             iter->second->getCharacterController()->persistAnimationState();
  2154.     }
  2155.  
  2156.     void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)
  2157.     {
  2158.         for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
  2159.         {
  2160.             if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius)
  2161.                 out.push_back(iter->first);
  2162.         }
  2163.     }
  2164.  
  2165.     bool Actors::isAnyObjectInRange(const osg::Vec3f& position, float radius)
  2166.     {
  2167.         for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
  2168.         {
  2169.             if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius)
  2170.                 return true;
  2171.         }
  2172.  
  2173.         return false;
  2174.     }
  2175.  
  2176.     std::list<MWWorld::Ptr> Actors::getActorsSidingWith(const MWWorld::Ptr& actor)
  2177.     {
  2178.         std::list<MWWorld::Ptr> list;
  2179.         for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
  2180.         {
  2181.             const MWWorld::Ptr &iteratedActor = iter->first;
  2182.             if (iteratedActor == getPlayer())
  2183.                 continue;
  2184.  
  2185.             const bool sameActor = (iteratedActor == actor);
  2186.  
  2187.             const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
  2188.             if (stats.isDead())
  2189.                 continue;
  2190.  
  2191.             // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package
  2192.             // Actors that are targeted by this actor's Follow or Escort packages also side with them
  2193.             for (const AiPackage* package : stats.getAiSequence())
  2194.             {
  2195.                 if (package->sideWithTarget() && !package->getTarget().isEmpty())
  2196.                 {
  2197.                     if (sameActor)
  2198.                     {
  2199.                         list.push_back(package->getTarget());
  2200.                     }
  2201.                     else if (package->getTarget() == actor)
  2202.                     {
  2203.                         list.push_back(iteratedActor);
  2204.                     }
  2205.                     break;
  2206.                 }
  2207.                 else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
  2208.                     break;
  2209.             }
  2210.         }
  2211.         return list;
  2212.     }
  2213.  
  2214.     std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
  2215.     {
  2216.         std::list<MWWorld::Ptr> list;
  2217.         for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
  2218.         {
  2219.             const MWWorld::Ptr &iteratedActor = iter->first;
  2220.             if (iteratedActor == getPlayer() || iteratedActor == actor)
  2221.                 continue;
  2222.  
  2223.             const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
  2224.             if (stats.isDead())
  2225.                 continue;
  2226.  
  2227.             // An actor counts as following if AiFollow is the current AiPackage,
  2228.             // or there are only Combat and Wander packages before the AiFollow package
  2229.             for (const AiPackage* package : stats.getAiSequence())
  2230.             {
  2231.                 if (package->followTargetThroughDoors() && package->getTarget() == actor)
  2232.                     list.push_back(iteratedActor);
  2233.                 else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
  2234.                     break;
  2235.             }
  2236.         }
  2237.         return list;
  2238.     }
  2239.  
  2240.     void Actors::getActorsFollowing(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out) {
  2241.         std::list<MWWorld::Ptr> followers = getActorsFollowing(actor);
  2242.         for(const MWWorld::Ptr &follower : followers)
  2243.             if (out.insert(follower).second)
  2244.                 getActorsFollowing(follower, out);
  2245.     }
  2246.  
  2247.     void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out) {
  2248.         std::list<MWWorld::Ptr> followers = getActorsSidingWith(actor);
  2249.         for(const MWWorld::Ptr &follower : followers)
  2250.             if (out.insert(follower).second)
  2251.                 getActorsSidingWith(follower, out);
  2252.     }
  2253.  
  2254.     void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies) {
  2255.         // If we have already found actor's allies, use the cache
  2256.         std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >::const_iterator search = cachedAllies.find(actor);
  2257.         if (search != cachedAllies.end())
  2258.             out.insert(search->second.begin(), search->second.end());
  2259.         else
  2260.         {
  2261.             std::list<MWWorld::Ptr> followers = getActorsSidingWith(actor);
  2262.             for (const MWWorld::Ptr &follower : followers)
  2263.                 if (out.insert(follower).second)
  2264.                     getActorsSidingWith(follower, out, cachedAllies);
  2265.  
  2266.             // Cache ptrs and their sets of allies
  2267.             cachedAllies.insert(std::make_pair(actor, out));
  2268.             for (const MWWorld::Ptr &iter : out)
  2269.             {
  2270.                 search = cachedAllies.find(iter);
  2271.                 if (search == cachedAllies.end())
  2272.                     cachedAllies.insert(std::make_pair(iter, out));
  2273.             }
  2274.         }
  2275.     }
  2276.  
  2277.     std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
  2278.     {
  2279.         std::list<int> list;
  2280.         for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
  2281.         {
  2282.             const MWWorld::Ptr &iteratedActor = iter->first;
  2283.             if (iteratedActor == getPlayer() || iteratedActor == actor)
  2284.                 continue;
  2285.  
  2286.             const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
  2287.             if (stats.isDead())
  2288.                 continue;
  2289.  
  2290.             // An actor counts as following if AiFollow is the current AiPackage,
  2291.             // or there are only Combat and Wander packages before the AiFollow package
  2292.             for (AiPackage* package : stats.getAiSequence())
  2293.             {
  2294.                 if (package->followTargetThroughDoors() && package->getTarget() == actor)
  2295.                 {
  2296.                     list.push_back(static_cast<AiFollow*>(package)->getFollowIndex());
  2297.                     break;
  2298.                 }
  2299.                 else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
  2300.                     break;
  2301.             }
  2302.         }
  2303.         return list;
  2304.     }
  2305.  
  2306.     std::list<MWWorld::Ptr> Actors::getActorsFighting(const MWWorld::Ptr& actor) {
  2307.         std::list<MWWorld::Ptr> list;
  2308.         std::vector<MWWorld::Ptr> neighbors;
  2309.         osg::Vec3f position (actor.getRefData().getPosition().asVec3());
  2310.         getObjectsInRange(position, mActorsProcessingRange, neighbors);
  2311.         for(const MWWorld::Ptr& neighbor : neighbors)
  2312.         {
  2313.             if (neighbor == actor)
  2314.                 continue;
  2315.  
  2316.             const CreatureStats &stats = neighbor.getClass().getCreatureStats(neighbor);
  2317.             if (stats.isDead())
  2318.                 continue;
  2319.  
  2320.             if (stats.getAiSequence().isInCombat(actor))
  2321.                 list.push_front(neighbor);
  2322.         }
  2323.         return list;
  2324.     }
  2325.  
  2326.     std::list<MWWorld::Ptr> Actors::getEnemiesNearby(const MWWorld::Ptr& actor)
  2327.     {
  2328.         std::list<MWWorld::Ptr> list;
  2329.         std::vector<MWWorld::Ptr> neighbors;
  2330.         osg::Vec3f position (actor.getRefData().getPosition().asVec3());
  2331.         getObjectsInRange(position, mActorsProcessingRange, neighbors);
  2332.  
  2333.         std::set<MWWorld::Ptr> followers;
  2334.         getActorsFollowing(actor, followers);
  2335.         for (auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor)
  2336.         {
  2337.             const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor);
  2338.             if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor))
  2339.                 continue;
  2340.  
  2341.             const bool isFollower = followers.find(*neighbor) != followers.end();
  2342.  
  2343.             if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower))
  2344.                 list.push_back(*neighbor);
  2345.         }
  2346.         return list;
  2347.     }
  2348.  
  2349.  
  2350.     void Actors::write (ESM::ESMWriter& writer, Loading::Listener& listener) const
  2351.     {
  2352.         writer.startRecord(ESM::REC_DCOU);
  2353.         for (std::map<std::string, int>::const_iterator it = mDeathCount.begin(); it != mDeathCount.end(); ++it)
  2354.         {
  2355.             writer.writeHNString("ID__", it->first);
  2356.             writer.writeHNT ("COUN", it->second);
  2357.         }
  2358.         writer.endRecord(ESM::REC_DCOU);
  2359.     }
  2360.  
  2361.     void Actors::readRecord (ESM::ESMReader& reader, uint32_t type)
  2362.     {
  2363.         if (type == ESM::REC_DCOU)
  2364.         {
  2365.             while (reader.isNextSub("ID__"))
  2366.             {
  2367.                 std::string id = reader.getHString();
  2368.                 int count;
  2369.                 reader.getHNT(count, "COUN");
  2370.                 if (MWBase::Environment::get().getWorld()->getStore().find(id))
  2371.                     mDeathCount[id] = count;
  2372.             }
  2373.         }
  2374.     }
  2375.  
  2376.     void Actors::clear()
  2377.     {
  2378.         PtrActorMap::iterator it(mActors.begin());
  2379.         for (; it != mActors.end(); ++it)
  2380.         {
  2381.             delete it->second;
  2382.             it->second = nullptr;
  2383.         }
  2384.         mActors.clear();
  2385.         mDeathCount.clear();
  2386.     }
  2387.  
  2388.     void Actors::updateMagicEffects(const MWWorld::Ptr &ptr)
  2389.     {
  2390.         adjustMagicEffects(ptr);
  2391.         calculateCreatureStatModifiers(ptr, 0.f);
  2392.         if (ptr.getClass().isNpc())
  2393.             calculateNpcStatModifiers(ptr, 0.f);
  2394.     }
  2395.  
  2396.     bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const
  2397.     {
  2398.         PtrActorMap::const_iterator it = mActors.find(ptr);
  2399.         if (it == mActors.end())
  2400.             return false;
  2401.  
  2402.         return it->second->getCharacterController()->isReadyToBlock();
  2403.     }
  2404.  
  2405.     bool Actors::isCastingSpell(const MWWorld::Ptr &ptr) const
  2406.     {
  2407.         PtrActorMap::const_iterator it = mActors.find(ptr);
  2408.         if (it == mActors.end())
  2409.             return false;
  2410.  
  2411.         return it->second->getCharacterController()->isCastingSpell();
  2412.     }
  2413.  
  2414.     bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
  2415.     {
  2416.         PtrActorMap::const_iterator it = mActors.find(ptr);
  2417.         if (it == mActors.end())
  2418.             return false;
  2419.         CharacterController* ctrl = it->second->getCharacterController();
  2420.  
  2421.         return ctrl->isAttackingOrSpell();
  2422.     }
  2423.  
  2424.     void Actors::fastForwardAi()
  2425.     {
  2426.         if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
  2427.             return;
  2428.  
  2429.         // making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator
  2430.         PtrActorMap map = mActors;
  2431.         for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it)
  2432.         {
  2433.             MWWorld::Ptr ptr = it->first;
  2434.             if (ptr == getPlayer()
  2435.                     || !isConscious(ptr)
  2436.                     || ptr.getClass().getCreatureStats(ptr).isParalyzed())
  2437.                 continue;
  2438.             MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
  2439.             seq.fastForward(ptr);
  2440.         }
  2441.     }
  2442. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement