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_Alarm, ref->mBase->mAiData.mAlarm);
|
||||
|
||||
if (data->mCreatureStats.isDead())
|
||||
data->mCreatureStats.setDeathAnimationFinished(true);
|
||||
|
||||
// spells
|
||||
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
|
||||
iter!=ref->mBase->mSpells.mList.end(); ++iter)
|
||||
|
|
|
@ -351,6 +351,8 @@ namespace MWClass
|
|||
|
||||
data->mNpcStats.setNeedRecalcDynamicStats(true);
|
||||
}
|
||||
if (data->mNpcStats.isDead())
|
||||
data->mNpcStats.setDeathAnimationFinished(true);
|
||||
|
||||
// race powers
|
||||
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/soundmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
|
@ -1208,25 +1209,14 @@ namespace MWMechanics
|
|||
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
|
||||
// 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");
|
||||
|
||||
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
|
||||
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
|
||||
{
|
||||
|
@ -1245,6 +1235,29 @@ namespace MWMechanics
|
|||
if (cls.isEssential(iter->first))
|
||||
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/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
@ -719,15 +718,20 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
mIdleState = CharState_Idle;
|
||||
else
|
||||
{
|
||||
// Set the death state, but don't play it yet
|
||||
// We will play it in the first frame, but only if no script set the skipAnim flag
|
||||
signed char deathanim = mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation();
|
||||
if (deathanim == -1)
|
||||
mDeathState = chooseRandomDeathState();
|
||||
else
|
||||
mDeathState = static_cast<CharacterState>(CharState_Death1 + deathanim);
|
||||
const MWMechanics::CreatureStats& cStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
if (cStats.isDeathAnimationFinished())
|
||||
{
|
||||
// Set the death state, but don't play it yet
|
||||
// We will play it in the first frame, but only if no script set the skipAnim flag
|
||||
signed char deathanim = cStats.getDeathAnimation();
|
||||
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
|
||||
|
@ -2000,30 +2004,28 @@ void CharacterController::forceStateUpdate()
|
|||
mAnimation->runAnimation(0.f);
|
||||
}
|
||||
|
||||
bool CharacterController::kill()
|
||||
CharacterController::KillResult CharacterController::kill()
|
||||
{
|
||||
if( isDead() )
|
||||
if (mDeathState == CharState_None)
|
||||
{
|
||||
if( mPtr == getPlayer() && !isAnimPlaying(mCurrentDeath) )
|
||||
{
|
||||
//player's death animation is over
|
||||
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
||||
}
|
||||
return false;
|
||||
playRandomDeath();
|
||||
|
||||
mAnimation->disable(mCurrentIdle);
|
||||
|
||||
mIdleState = CharState_None;
|
||||
mCurrentIdle.clear();
|
||||
return Result_DeathAnimStarted;
|
||||
}
|
||||
|
||||
playRandomDeath();
|
||||
|
||||
mAnimation->disable(mCurrentIdle);
|
||||
|
||||
mIdleState = CharState_None;
|
||||
mCurrentIdle.clear();
|
||||
|
||||
// Play Death Music if it was the player dying
|
||||
if(mPtr == getPlayer())
|
||||
MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Death.mp3");
|
||||
|
||||
return true;
|
||||
MWMechanics::CreatureStats& cStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
if (isAnimPlaying(mCurrentDeath))
|
||||
return Result_DeathAnimPlaying;
|
||||
if (!cStats.isDeathAnimationFinished())
|
||||
{
|
||||
cStats.setDeathAnimationFinished(true);
|
||||
return Result_DeathAnimJustFinished;
|
||||
}
|
||||
return Result_DeathAnimFinished;
|
||||
}
|
||||
|
||||
void CharacterController::resurrect()
|
||||
|
|
|
@ -234,8 +234,14 @@ public:
|
|||
void skipAnim();
|
||||
bool isAnimPlaying(const std::string &groupName);
|
||||
|
||||
/// @return false if the character has already been killed before
|
||||
bool kill();
|
||||
enum KillResult
|
||||
{
|
||||
Result_DeathAnimStarted,
|
||||
Result_DeathAnimPlaying,
|
||||
Result_DeathAnimJustFinished,
|
||||
Result_DeathAnimFinished
|
||||
};
|
||||
KillResult kill();
|
||||
|
||||
void resurrect();
|
||||
bool isDead() const
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace MWMechanics
|
|||
int CreatureStats::sActorId = 0;
|
||||
|
||||
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),
|
||||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||
|
@ -236,6 +236,16 @@ namespace MWMechanics
|
|||
return mDead;
|
||||
}
|
||||
|
||||
bool CreatureStats::isDeathAnimationFinished() const
|
||||
{
|
||||
return mDeathAnimationFinished;
|
||||
}
|
||||
|
||||
void CreatureStats::setDeathAnimationFinished(bool finished)
|
||||
{
|
||||
mDeathAnimationFinished = finished;
|
||||
}
|
||||
|
||||
void CreatureStats::notifyDied()
|
||||
{
|
||||
mDied = true;
|
||||
|
@ -275,6 +285,7 @@ namespace MWMechanics
|
|||
|
||||
mDynamic[0].setCurrent(mDynamic[0].getModified());
|
||||
mDead = false;
|
||||
mDeathAnimationFinished = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,6 +493,7 @@ namespace MWMechanics
|
|||
state.mGoldPool = mGoldPool;
|
||||
|
||||
state.mDead = mDead;
|
||||
state.mDeathAnimationFinished = mDeathAnimationFinished;
|
||||
state.mDied = mDied;
|
||||
state.mMurdered = mMurdered;
|
||||
// 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;
|
||||
|
||||
mDead = state.mDead;
|
||||
mDeathAnimationFinished = state.mDeathAnimationFinished;
|
||||
mDied = state.mDied;
|
||||
mMurdered = state.mMurdered;
|
||||
mTalkedTo = state.mTalkedTo;
|
||||
|
|
|
@ -34,7 +34,8 @@ namespace MWMechanics
|
|||
Stat<int> mAiSettings[4];
|
||||
AiSequence mAiSequence;
|
||||
bool mDead;
|
||||
bool mDied;
|
||||
bool mDeathAnimationFinished;
|
||||
bool mDied; // flag for OnDeath script function
|
||||
bool mMurdered;
|
||||
int mFriendlyHits;
|
||||
bool mTalkedTo;
|
||||
|
@ -161,6 +162,9 @@ namespace MWMechanics
|
|||
|
||||
bool isDead() const;
|
||||
|
||||
bool isDeathAnimationFinished() const;
|
||||
void setDeathAnimationFinished(bool finished);
|
||||
|
||||
void notifyDied();
|
||||
|
||||
bool hasDied() const;
|
||||
|
|
|
@ -20,6 +20,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
|||
mDead = false;
|
||||
esm.getHNOT (mDead, "DEAD");
|
||||
|
||||
mDeathAnimationFinished = false;
|
||||
esm.getHNOT (mDeathAnimationFinished, "DFNT");
|
||||
|
||||
mDied = false;
|
||||
esm.getHNOT (mDied, "DIED");
|
||||
|
||||
|
@ -140,6 +143,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
if (mDead)
|
||||
esm.writeHNT ("DEAD", mDead);
|
||||
|
||||
if (mDeathAnimationFinished)
|
||||
esm.writeHNT ("DFNT", mDeathAnimationFinished);
|
||||
|
||||
if (mDied)
|
||||
esm.writeHNT ("DIED", mDied);
|
||||
|
||||
|
@ -233,6 +239,7 @@ void ESM::CreatureStats::blank()
|
|||
mActorId = -1;
|
||||
mHasAiSettings = false;
|
||||
mDead = false;
|
||||
mDeathAnimationFinished = false;
|
||||
mDied = false;
|
||||
mMurdered = false;
|
||||
mTalkedTo = false;
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace ESM
|
|||
int mActorId;
|
||||
|
||||
bool mDead;
|
||||
bool mDeathAnimationFinished;
|
||||
bool mDied;
|
||||
bool mMurdered;
|
||||
bool mTalkedTo;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "defs.hpp"
|
||||
|
||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||
int ESM::SavedGame::sCurrentFormat = 2;
|
||||
int ESM::SavedGame::sCurrentFormat = 3;
|
||||
|
||||
void ESM::SavedGame::load (ESMReader &esm)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue