1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-29 00:06:43 +00:00

Do not update mIdleState directly

This commit is contained in:
Andrei Kortunov 2018-08-20 22:04:02 +04:00
parent 929d78d6a3
commit 0136f0552b
2 changed files with 78 additions and 48 deletions

View file

@ -243,7 +243,7 @@ std::string CharacterController::chooseRandomGroup (const std::string& prefix, i
return prefix + toString(roll); return prefix + toString(roll);
} }
void CharacterController::refreshHitRecoilAnims() void CharacterController::refreshHitRecoilAnims(CharacterState& idle)
{ {
bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery();
bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown();
@ -348,14 +348,16 @@ void CharacterController::refreshHitRecoilAnims()
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0);
} }
if (mHitState != CharState_None) if (mHitState != CharState_None)
mIdleState = CharState_None; idle = CharState_None;
} }
void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force) void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force)
{ {
if(force || jump != mJumpState) if(force || jump != mJumpState)
{ {
mIdleState = CharState_None; if (jump != JumpState_None)
idle = CharState_None;
bool startAtLoop = (jump == mJumpState); bool startAtLoop = (jump == mJumpState);
mJumpState = jump; mJumpState = jump;
@ -372,6 +374,11 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
jumpmask = MWRender::Animation::BlendMask_LowerBody; jumpmask = MWRender::Animation::BlendMask_LowerBody;
jumpAnimName = "jump"; jumpAnimName = "jump";
// Since we apply movement only for lower body, do not reset idle animations.
// For upper body there will be idle animation.
if (idle == CharState_None)
idle = CharState_Idle;
// For crossbow animations use 1h ones as fallback // For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow) if (mWeaponType == WeapType_Crossbow)
jumpAnimName += "1h"; jumpAnimName += "1h";
@ -406,20 +413,21 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
} }
} }
void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force) void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force)
{ {
if(force || movement != mMovementState) std::string movementAnimName;
MWRender::Animation::BlendMask movemask;
const StateInfo *movestate;
if(force || movement != mMovementState || idle != mIdleState)
{ {
mMovementState = movement; movemask = MWRender::Animation::BlendMask_All;
std::string movementAnimName; movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement));
MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All;
const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState));
if(movestate != sMovementListEnd) if(movestate != sMovementListEnd)
{ {
movementAnimName = movestate->groupname; movementAnimName = movestate->groupname;
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
{ {
if (mWeaponType == WeapType_Spell && (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)) // Spellcasting stance turning is a special case if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case
movementAnimName = weap->shortgroup + movementAnimName; movementAnimName = weap->shortgroup + movementAnimName;
else else
movementAnimName += weap->shortgroup; movementAnimName += weap->shortgroup;
@ -429,12 +437,24 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
movemask = MWRender::Animation::BlendMask_LowerBody; movemask = MWRender::Animation::BlendMask_LowerBody;
movementAnimName = movestate->groupname; movementAnimName = movestate->groupname;
// Since we apply movement only for lower body, do not reset idle animations.
// For upper body there will be idle animation.
if (idle == CharState_None)
idle = CharState_Idle;
// For crossbow animations use 1h ones as fallback // For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow) if (mWeaponType == WeapType_Crossbow)
movementAnimName += "1h"; movementAnimName += "1h";
} }
} }
}
}
if(force || movement != mMovementState)
{
mMovementState = movement;
if(movestate != sMovementListEnd)
{
if(!mAnimation->hasAnimation(movementAnimName)) if(!mAnimation->hasAnimation(movementAnimName))
{ {
std::string::size_type swimpos = movementAnimName.find("swim"); std::string::size_type swimpos = movementAnimName.find("swim");
@ -532,7 +552,15 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force) void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force)
{ {
if(force || idle != mIdleState || mIdleState == CharState_None || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
// the idle animation should be displayed
if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped)
|| (mMovementState != CharState_None && !isTurning())
|| mHitState != CharState_None)
&& !mPtr.getClass().isBipedal(mPtr))
idle = CharState_None;
if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty()))
{ {
mIdleState = idle; mIdleState = idle;
size_t numLoops = ~0ul; size_t numLoops = ~0ul;
@ -591,24 +619,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
return; return;
if (mPtr.getClass().isActor()) if (mPtr.getClass().isActor())
refreshHitRecoilAnims(); refreshHitRecoilAnims(idle);
const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType));
if (!mPtr.getClass().hasInventoryStore(mPtr)) if (!mPtr.getClass().hasInventoryStore(mPtr))
weap = sWeaponTypeListEnd; weap = sWeaponTypeListEnd;
refreshJumpAnims(weap, jump, force); refreshJumpAnims(weap, jump, idle, force);
refreshMovementAnims(weap, movement, force); refreshMovementAnims(weap, movement, idle, force);
// idle handled last as it can depend on the other states // idle handled last as it can depend on the other states
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
// the idle animation should be displayed
if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped)
|| (mMovementState != CharState_None && !isTurning())
|| mHitState != CharState_None)
&& !mPtr.getClass().isBipedal(mPtr))
idle = CharState_None;
refreshIdleAnims(weap, idle, force); refreshIdleAnims(weap, idle, force);
} }
@ -1200,7 +1220,7 @@ bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const
} }
} }
bool CharacterController::updateWeaponState() bool CharacterController::updateWeaponState(CharacterState& idle)
{ {
const MWWorld::Class &cls = mPtr.getClass(); const MWWorld::Class &cls = mPtr.getClass();
CreatureStats &stats = cls.getCreatureStats(mPtr); CreatureStats &stats = cls.getCreatureStats(mPtr);
@ -1570,11 +1590,11 @@ bool CharacterController::updateWeaponState()
// We should reset player's idle animation in the first-person mode. // We should reset player's idle animation in the first-person mode.
if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson())
mIdleState = CharState_None; idle = CharState_None;
// In other cases we should not break swim and sneak animations // In other cases we should not break swim and sneak animations
if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
mIdleState = CharState_None; idle = CharState_None;
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
@ -2085,12 +2105,18 @@ void CharacterController::update(float duration)
: (sneak ? CharState_SneakBack : (sneak ? CharState_SneakBack
: (isrunning ? CharState_RunBack : CharState_WalkBack))); : (isrunning ? CharState_RunBack : CharState_WalkBack)));
} }
else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) else if(rot.z() != 0.0f)
{ {
if(rot.z() > rotationThreshold) // It seems only bipedal actors use turning animations.
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; // Also do not use turning animations in the first-person view and when sneaking.
else if(rot.z() < -rotationThreshold) bool isFirstPlayer = mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson();
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr))
{
if(rot.z() > rotationThreshold)
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
else if(rot.z() < -rotationThreshold)
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
}
} }
} }
@ -2108,16 +2134,21 @@ void CharacterController::update(float duration)
} }
else else
{ {
mTurnAnimationThreshold -= duration; if (mPtr.getClass().isBipedal(mPtr))
if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||
movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)
{ {
mTurnAnimationThreshold = 0.05f; if (mTurnAnimationThreshold > 0)
} mTurnAnimationThreshold -= duration;
else if (movestate == CharState_None && isTurning()
&& mTurnAnimationThreshold > 0) if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||
{ movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)
movestate = mMovementState; {
mTurnAnimationThreshold = 0.05f;
}
else if (movestate == CharState_None && isTurning()
&& mTurnAnimationThreshold > 0)
{
movestate = mMovementState;
}
} }
} }
@ -2126,13 +2157,12 @@ void CharacterController::update(float duration)
if(mAnimQueue.empty() || inwater || sneak) if(mAnimQueue.empty() || inwater || sneak)
{ {
// Note: turning animations should not interrupt idle ones. // Note: turning animations should not interrupt idle ones
// Also movement should not stop idle animation for spellcasting stance.
if (inwater) if (inwater)
idlestate = CharState_IdleSwim; idlestate = CharState_IdleSwim;
else if (sneak && !inJump) else if (sneak && !inJump)
idlestate = CharState_IdleSneak; idlestate = CharState_IdleSneak;
else if (movestate != CharState_None && !isTurning() && mWeaponType != WeapType_Spell) else if (movestate != CharState_None && !isTurning())
idlestate = CharState_None; idlestate = CharState_None;
else else
idlestate = CharState_Idle; idlestate = CharState_Idle;
@ -2144,7 +2174,7 @@ void CharacterController::update(float duration)
{ {
// bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used. // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used.
if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr)) if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr))
forcestateupdate = updateWeaponState() || forcestateupdate; forcestateupdate = updateWeaponState(idlestate) || forcestateupdate;
else else
forcestateupdate = updateCreatureState() || forcestateupdate; forcestateupdate = updateCreatureState() || forcestateupdate;

View file

@ -213,14 +213,14 @@ class CharacterController : public MWRender::Animation::TextKeyListener
void setAttackTypeBasedOnMovement(); void setAttackTypeBasedOnMovement();
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
void refreshHitRecoilAnims(); void refreshHitRecoilAnims(CharacterState& idle);
void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force=false); void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force=false);
void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force=false); void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false);
void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false); void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false);
void clearAnimQueue(bool clearPersistAnims = false); void clearAnimQueue(bool clearPersistAnims = false);
bool updateWeaponState(); bool updateWeaponState(CharacterState& idle);
bool updateCreatureState(); bool updateCreatureState();
void updateIdleStormState(bool inwater); void updateIdleStormState(bool inwater);