mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
Merge pull request #1350 from akortunov/deathanimationfix
Do not allow to loot fighting actors during death animation (bug #3528)
This commit is contained in:
commit
2611377081
8 changed files with 111 additions and 40 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/magiceffects.hpp"
|
||||
|
@ -448,10 +449,27 @@ namespace MWClass
|
|||
return action;
|
||||
}
|
||||
|
||||
if(getCreatureStats(ptr).isDead())
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
|
||||
if(stats.isDead())
|
||||
{
|
||||
bool canLoot = Settings::Manager::getBool ("can loot during death animation", "Game");
|
||||
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (canLoot && !stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
|
||||
|
||||
// otherwise wait until death animation
|
||||
if(stats.isDeathAnimationFinished())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
|
||||
// death animation is not finished, do nothing
|
||||
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
|
||||
}
|
||||
|
||||
if(stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
|
||||
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
|
||||
}
|
||||
|
||||
|
@ -558,7 +576,11 @@ namespace MWClass
|
|||
return true;
|
||||
|
||||
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
|
||||
return !customData.mCreatureStats.getAiSequence().isInCombat() || customData.mCreatureStats.isDead();
|
||||
|
||||
if (customData.mCreatureStats.isDead() && customData.mCreatureStats.isDeathAnimationFinished())
|
||||
return true;
|
||||
|
||||
return !customData.mCreatureStats.getAiSequence().isInCombat();
|
||||
}
|
||||
|
||||
MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/npcstate.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -863,16 +864,35 @@ namespace MWClass
|
|||
return action;
|
||||
}
|
||||
|
||||
if(getCreatureStats(ptr).isDead())
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
|
||||
if(stats.isDead())
|
||||
{
|
||||
bool canLoot = Settings::Manager::getBool ("can loot during death animation", "Game");
|
||||
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (canLoot && !stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
|
||||
|
||||
// otherwise wait until death animation
|
||||
if(stats.isDeathAnimationFinished())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
|
||||
// death animation is not finished, do nothing
|
||||
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
|
||||
}
|
||||
|
||||
if(stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
|
||||
|
||||
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)
|
||||
|| ptr.getClass().getCreatureStats(ptr).getKnockedDown())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
|
||||
|
||||
// Can't talk to werewolfs
|
||||
if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf())
|
||||
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
|
||||
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1039,11 @@ namespace MWClass
|
|||
return true;
|
||||
|
||||
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
|
||||
return !customData.mNpcStats.getAiSequence().isInCombat() || customData.mNpcStats.isDead();
|
||||
|
||||
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
|
||||
return true;
|
||||
|
||||
return !customData.mNpcStats.getAiSequence().isInCombat();
|
||||
}
|
||||
|
||||
MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
|
||||
|
|
|
@ -783,7 +783,7 @@ namespace MWMechanics
|
|||
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
|
||||
if (ptr.getClass().hasInventoryStore(ptr))
|
||||
ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
|
||||
updateSummonedCreatures.process();
|
||||
updateSummonedCreatures.process(mTimerDisposeSummonsCorpses == 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1040,7 +1040,9 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
Actors::Actors() {}
|
||||
Actors::Actors() {
|
||||
mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
|
||||
}
|
||||
|
||||
Actors::~Actors()
|
||||
{
|
||||
|
@ -1109,6 +1111,7 @@ namespace MWMechanics
|
|||
// target lists get updated once every 1.0 sec
|
||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
||||
|
||||
MWWorld::Ptr player = getPlayer();
|
||||
|
@ -1213,6 +1216,7 @@ namespace MWMechanics
|
|||
timerUpdateAITargets += duration;
|
||||
timerUpdateHeadTrack += duration;
|
||||
timerUpdateEquippedLight += duration;
|
||||
mTimerDisposeSummonsCorpses += duration;
|
||||
|
||||
// Looping magic VFX update
|
||||
// Note: we need to do this before any of the animations are updated.
|
||||
|
|
|
@ -153,6 +153,7 @@ namespace MWMechanics
|
|||
|
||||
private:
|
||||
PtrActorMap mActors;
|
||||
float mTimerDisposeSummonsCorpses;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -38,26 +38,10 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void UpdateSummonedCreatures::process()
|
||||
void UpdateSummonedCreatures::process(bool cleanup)
|
||||
{
|
||||
|
||||
|
||||
MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);
|
||||
|
||||
// Update summon effects
|
||||
std::map<CreatureStats::SummonKey, int>& creatureMap = creatureStats.getSummonedCreatureMap();
|
||||
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||
{
|
||||
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
|
||||
if (!found)
|
||||
{
|
||||
// Effect has ended
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
|
||||
creatureMap.erase(it++);
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
for (std::set<std::pair<int, std::string> >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it)
|
||||
{
|
||||
|
@ -101,20 +85,17 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
// Update summon effects
|
||||
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||
{
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
|
||||
if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())
|
||||
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
|
||||
if (!found)
|
||||
{
|
||||
// Purge the magic effect so a new creature can be summoned if desired
|
||||
creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second);
|
||||
if (mActor.getClass().hasInventoryStore(ptr))
|
||||
mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second);
|
||||
|
||||
// Effect has ended
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
|
||||
creatureMap.erase(it++);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
|
@ -137,6 +118,26 @@ namespace MWMechanics
|
|||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
if (!cleanup)
|
||||
return;
|
||||
|
||||
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||
{
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
|
||||
if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()))
|
||||
{
|
||||
// Purge the magic effect so a new creature can be summoned if desired
|
||||
creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second);
|
||||
if (mActor.getClass().hasInventoryStore(mActor))
|
||||
mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second);
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second);
|
||||
creatureMap.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace MWMechanics
|
|||
float magnitude, float remainingTime = -1, float totalTime = -1);
|
||||
|
||||
/// To call after all effect sources have been visited
|
||||
void process();
|
||||
void process(bool cleanup);
|
||||
|
||||
private:
|
||||
MWWorld::Ptr mActor;
|
||||
|
|
|
@ -65,6 +65,22 @@ the type of attack is determined by the direction that the character is moving a
|
|||
The default value is false.
|
||||
This setting can be toggled with the Always Use Best Attack button in the Prefs panel of the Options menu.
|
||||
|
||||
can loot during death animation
|
||||
-------------------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
:Default: True
|
||||
|
||||
If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat.
|
||||
However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests.
|
||||
This is how original Morrowind behaves.
|
||||
|
||||
If this setting is false, player has to wait until end of death animation in all cases.
|
||||
This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder.
|
||||
|
||||
The default value is true. This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
difficulty
|
||||
----------
|
||||
|
||||
|
|
|
@ -180,6 +180,9 @@ prevent merchant equipping = false
|
|||
# or the player. Otherwise they wait for the enemies or the player to do an attack first.
|
||||
followers attack on sight = false
|
||||
|
||||
# Can loot non-fighting actors during death animation
|
||||
can loot during death animation = true
|
||||
|
||||
[General]
|
||||
|
||||
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
||||
|
|
Loading…
Reference in a new issue