forked from mirror/openmw-tes3mp
Process death events at the end of the death animation (Fixes #1873)
This commit is contained in:
parent
37afe966cf
commit
a825882c6b
10 changed files with 100 additions and 49 deletions
|
@ -134,6 +134,9 @@ namespace MWClass
|
||||||
data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee);
|
data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee);
|
||||||
data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm);
|
data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm);
|
||||||
|
|
||||||
|
if (data->mCreatureStats.isDead())
|
||||||
|
data->mCreatureStats.setDeathAnimationFinished(true);
|
||||||
|
|
||||||
// spells
|
// spells
|
||||||
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
||||||
iter!=ref->mBase->mSpells.mList.end(); ++iter)
|
iter!=ref->mBase->mSpells.mList.end(); ++iter)
|
||||||
|
|
|
@ -351,6 +351,8 @@ namespace MWClass
|
||||||
|
|
||||||
data->mNpcStats.setNeedRecalcDynamicStats(true);
|
data->mNpcStats.setNeedRecalcDynamicStats(true);
|
||||||
}
|
}
|
||||||
|
if (data->mNpcStats.isDead())
|
||||||
|
data->mNpcStats.setDeathAnimationFinished(true);
|
||||||
|
|
||||||
// race powers
|
// race powers
|
||||||
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "../mwbase/dialoguemanager.hpp"
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
#include "../mwbase/statemanager.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/spellcasting.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
|
@ -1208,25 +1209,14 @@ namespace MWMechanics
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->second->getCharacterController()->kill())
|
CharacterController::KillResult killResult = iter->second->getCharacterController()->kill();
|
||||||
|
if (killResult == CharacterController::Result_DeathAnimStarted)
|
||||||
{
|
{
|
||||||
// TODO: It's not known whether the soundgen tags scream, roar, and moan are reliable
|
|
||||||
// for NPCs since some of the npc death animation files are missing them.
|
|
||||||
|
|
||||||
// Play dying words
|
// Play dying words
|
||||||
|
// Note: It's not known whether the soundgen tags scream, roar, and moan are reliable
|
||||||
|
// for NPCs since some of the npc death animation files are missing them.
|
||||||
MWBase::Environment::get().getDialogueManager()->say(iter->first, "hit");
|
MWBase::Environment::get().getDialogueManager()->say(iter->first, "hit");
|
||||||
|
|
||||||
iter->first.getClass().getCreatureStats(iter->first).notifyDied();
|
|
||||||
|
|
||||||
++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
|
|
||||||
|
|
||||||
// Make sure spell effects are removed
|
|
||||||
for (PtrActorMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
|
|
||||||
{
|
|
||||||
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
|
|
||||||
spells.purge(stats.getActorId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply soultrap
|
// Apply soultrap
|
||||||
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
|
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
|
||||||
{
|
{
|
||||||
|
@ -1245,6 +1235,29 @@ namespace MWMechanics
|
||||||
if (cls.isEssential(iter->first))
|
if (cls.isEssential(iter->first))
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
|
||||||
}
|
}
|
||||||
|
else if (killResult == CharacterController::Result_DeathAnimJustFinished)
|
||||||
|
{
|
||||||
|
iter->first.getClass().getCreatureStats(iter->first).notifyDied();
|
||||||
|
|
||||||
|
++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
|
||||||
|
|
||||||
|
// Make sure spell effects are removed
|
||||||
|
for (PtrActorMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
|
||||||
|
{
|
||||||
|
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
|
||||||
|
spells.purge(stats.getActorId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if( iter->first == getPlayer())
|
||||||
|
{
|
||||||
|
//player's death animation is over
|
||||||
|
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play Death Music if it was the player dying
|
||||||
|
if(iter->first == getPlayer())
|
||||||
|
MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Death.mp3");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/statemanager.hpp"
|
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
@ -719,15 +718,20 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
||||||
mIdleState = CharState_Idle;
|
mIdleState = CharState_Idle;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Set the death state, but don't play it yet
|
const MWMechanics::CreatureStats& cStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||||
// We will play it in the first frame, but only if no script set the skipAnim flag
|
if (cStats.isDeathAnimationFinished())
|
||||||
signed char deathanim = mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation();
|
{
|
||||||
if (deathanim == -1)
|
// Set the death state, but don't play it yet
|
||||||
mDeathState = chooseRandomDeathState();
|
// We will play it in the first frame, but only if no script set the skipAnim flag
|
||||||
else
|
signed char deathanim = cStats.getDeathAnimation();
|
||||||
mDeathState = static_cast<CharacterState>(CharState_Death1 + deathanim);
|
if (deathanim == -1)
|
||||||
|
mDeathState = chooseRandomDeathState();
|
||||||
|
else
|
||||||
|
mDeathState = static_cast<CharacterState>(CharState_Death1 + deathanim);
|
||||||
|
|
||||||
mFloatToSurface = false;
|
mFloatToSurface = false;
|
||||||
|
}
|
||||||
|
// else: nothing to do, will detect death in the next frame and start playing death animation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2000,30 +2004,28 @@ void CharacterController::forceStateUpdate()
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::kill()
|
CharacterController::KillResult CharacterController::kill()
|
||||||
{
|
{
|
||||||
if( isDead() )
|
if (mDeathState == CharState_None)
|
||||||
{
|
{
|
||||||
if( mPtr == getPlayer() && !isAnimPlaying(mCurrentDeath) )
|
playRandomDeath();
|
||||||
{
|
|
||||||
//player's death animation is over
|
mAnimation->disable(mCurrentIdle);
|
||||||
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
|
||||||
}
|
mIdleState = CharState_None;
|
||||||
return false;
|
mCurrentIdle.clear();
|
||||||
|
return Result_DeathAnimStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
playRandomDeath();
|
MWMechanics::CreatureStats& cStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||||
|
if (isAnimPlaying(mCurrentDeath))
|
||||||
mAnimation->disable(mCurrentIdle);
|
return Result_DeathAnimPlaying;
|
||||||
|
if (!cStats.isDeathAnimationFinished())
|
||||||
mIdleState = CharState_None;
|
{
|
||||||
mCurrentIdle.clear();
|
cStats.setDeathAnimationFinished(true);
|
||||||
|
return Result_DeathAnimJustFinished;
|
||||||
// Play Death Music if it was the player dying
|
}
|
||||||
if(mPtr == getPlayer())
|
return Result_DeathAnimFinished;
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Death.mp3");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::resurrect()
|
void CharacterController::resurrect()
|
||||||
|
|
|
@ -234,8 +234,14 @@ public:
|
||||||
void skipAnim();
|
void skipAnim();
|
||||||
bool isAnimPlaying(const std::string &groupName);
|
bool isAnimPlaying(const std::string &groupName);
|
||||||
|
|
||||||
/// @return false if the character has already been killed before
|
enum KillResult
|
||||||
bool kill();
|
{
|
||||||
|
Result_DeathAnimStarted,
|
||||||
|
Result_DeathAnimPlaying,
|
||||||
|
Result_DeathAnimJustFinished,
|
||||||
|
Result_DeathAnimFinished
|
||||||
|
};
|
||||||
|
KillResult kill();
|
||||||
|
|
||||||
void resurrect();
|
void resurrect();
|
||||||
bool isDead() const
|
bool isDead() const
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace MWMechanics
|
||||||
int CreatureStats::sActorId = 0;
|
int CreatureStats::sActorId = 0;
|
||||||
|
|
||||||
CreatureStats::CreatureStats()
|
CreatureStats::CreatureStats()
|
||||||
: mDrawState (DrawState_Nothing), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
|
: mDrawState (DrawState_Nothing), mDead (false), mDeathAnimationFinished(false), mDied (false), mMurdered(false), mFriendlyHits (0),
|
||||||
mTalkedTo (false), mAlarmed (false), mAttacked (false),
|
mTalkedTo (false), mAlarmed (false), mAttacked (false),
|
||||||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||||
|
@ -236,6 +236,16 @@ namespace MWMechanics
|
||||||
return mDead;
|
return mDead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreatureStats::isDeathAnimationFinished() const
|
||||||
|
{
|
||||||
|
return mDeathAnimationFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::setDeathAnimationFinished(bool finished)
|
||||||
|
{
|
||||||
|
mDeathAnimationFinished = finished;
|
||||||
|
}
|
||||||
|
|
||||||
void CreatureStats::notifyDied()
|
void CreatureStats::notifyDied()
|
||||||
{
|
{
|
||||||
mDied = true;
|
mDied = true;
|
||||||
|
@ -275,6 +285,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
mDynamic[0].setCurrent(mDynamic[0].getModified());
|
mDynamic[0].setCurrent(mDynamic[0].getModified());
|
||||||
mDead = false;
|
mDead = false;
|
||||||
|
mDeathAnimationFinished = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +493,7 @@ namespace MWMechanics
|
||||||
state.mGoldPool = mGoldPool;
|
state.mGoldPool = mGoldPool;
|
||||||
|
|
||||||
state.mDead = mDead;
|
state.mDead = mDead;
|
||||||
|
state.mDeathAnimationFinished = mDeathAnimationFinished;
|
||||||
state.mDied = mDied;
|
state.mDied = mDied;
|
||||||
state.mMurdered = mMurdered;
|
state.mMurdered = mMurdered;
|
||||||
// The vanilla engine does not store friendly hits in the save file. Since there's no other mechanism
|
// The vanilla engine does not store friendly hits in the save file. Since there's no other mechanism
|
||||||
|
@ -533,6 +545,7 @@ namespace MWMechanics
|
||||||
mGoldPool = state.mGoldPool;
|
mGoldPool = state.mGoldPool;
|
||||||
|
|
||||||
mDead = state.mDead;
|
mDead = state.mDead;
|
||||||
|
mDeathAnimationFinished = state.mDeathAnimationFinished;
|
||||||
mDied = state.mDied;
|
mDied = state.mDied;
|
||||||
mMurdered = state.mMurdered;
|
mMurdered = state.mMurdered;
|
||||||
mTalkedTo = state.mTalkedTo;
|
mTalkedTo = state.mTalkedTo;
|
||||||
|
|
|
@ -34,7 +34,8 @@ namespace MWMechanics
|
||||||
Stat<int> mAiSettings[4];
|
Stat<int> mAiSettings[4];
|
||||||
AiSequence mAiSequence;
|
AiSequence mAiSequence;
|
||||||
bool mDead;
|
bool mDead;
|
||||||
bool mDied;
|
bool mDeathAnimationFinished;
|
||||||
|
bool mDied; // flag for OnDeath script function
|
||||||
bool mMurdered;
|
bool mMurdered;
|
||||||
int mFriendlyHits;
|
int mFriendlyHits;
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
|
@ -161,6 +162,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool isDead() const;
|
bool isDead() const;
|
||||||
|
|
||||||
|
bool isDeathAnimationFinished() const;
|
||||||
|
void setDeathAnimationFinished(bool finished);
|
||||||
|
|
||||||
void notifyDied();
|
void notifyDied();
|
||||||
|
|
||||||
bool hasDied() const;
|
bool hasDied() const;
|
||||||
|
|
|
@ -20,6 +20,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
||||||
mDead = false;
|
mDead = false;
|
||||||
esm.getHNOT (mDead, "DEAD");
|
esm.getHNOT (mDead, "DEAD");
|
||||||
|
|
||||||
|
mDeathAnimationFinished = false;
|
||||||
|
esm.getHNOT (mDeathAnimationFinished, "DFNT");
|
||||||
|
|
||||||
mDied = false;
|
mDied = false;
|
||||||
esm.getHNOT (mDied, "DIED");
|
esm.getHNOT (mDied, "DIED");
|
||||||
|
|
||||||
|
@ -140,6 +143,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
||||||
if (mDead)
|
if (mDead)
|
||||||
esm.writeHNT ("DEAD", mDead);
|
esm.writeHNT ("DEAD", mDead);
|
||||||
|
|
||||||
|
if (mDeathAnimationFinished)
|
||||||
|
esm.writeHNT ("DFNT", mDeathAnimationFinished);
|
||||||
|
|
||||||
if (mDied)
|
if (mDied)
|
||||||
esm.writeHNT ("DIED", mDied);
|
esm.writeHNT ("DIED", mDied);
|
||||||
|
|
||||||
|
@ -233,6 +239,7 @@ void ESM::CreatureStats::blank()
|
||||||
mActorId = -1;
|
mActorId = -1;
|
||||||
mHasAiSettings = false;
|
mHasAiSettings = false;
|
||||||
mDead = false;
|
mDead = false;
|
||||||
|
mDeathAnimationFinished = false;
|
||||||
mDied = false;
|
mDied = false;
|
||||||
mMurdered = false;
|
mMurdered = false;
|
||||||
mTalkedTo = false;
|
mTalkedTo = false;
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace ESM
|
||||||
int mActorId;
|
int mActorId;
|
||||||
|
|
||||||
bool mDead;
|
bool mDead;
|
||||||
|
bool mDeathAnimationFinished;
|
||||||
bool mDied;
|
bool mDied;
|
||||||
bool mMurdered;
|
bool mMurdered;
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
|
||||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||||
int ESM::SavedGame::sCurrentFormat = 2;
|
int ESM::SavedGame::sCurrentFormat = 3;
|
||||||
|
|
||||||
void ESM::SavedGame::load (ESMReader &esm)
|
void ESM::SavedGame::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue