mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-31 14:36:39 +00:00
Merged pull request #1881
This commit is contained in:
commit
ea053d9f47
2 changed files with 91 additions and 48 deletions
|
@ -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,34 +413,48 @@ 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)
|
|
||||||
{
|
|
||||||
mMovementState = movement;
|
|
||||||
std::string movementAnimName;
|
std::string movementAnimName;
|
||||||
MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All;
|
MWRender::Animation::BlendMask movemask;
|
||||||
const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState));
|
const StateInfo *movestate;
|
||||||
|
if(force || movement != mMovementState || idle != mIdleState)
|
||||||
|
{
|
||||||
|
movemask = MWRender::Animation::BlendMask_All;
|
||||||
|
movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement));
|
||||||
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;
|
||||||
|
|
||||||
if(!mAnimation->hasAnimation(movementAnimName))
|
if(!mAnimation->hasAnimation(movementAnimName))
|
||||||
{
|
{
|
||||||
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");
|
||||||
|
@ -451,6 +472,10 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// For crossbow animations use 1h ones as fallback
|
||||||
|
if (mWeaponType == WeapType_Crossbow)
|
||||||
|
movementAnimName += "1h";
|
||||||
|
|
||||||
movementAnimName.erase(swimpos, 4);
|
movementAnimName.erase(swimpos, 4);
|
||||||
if (weap != sWeaponTypeListEnd)
|
if (weap != sWeaponTypeListEnd)
|
||||||
{
|
{
|
||||||
|
@ -527,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;
|
||||||
|
@ -586,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,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);
|
||||||
|
@ -1394,6 +1419,14 @@ bool CharacterController::updateWeaponState()
|
||||||
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
|
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
|
||||||
mAttackStrength = 0;
|
mAttackStrength = 0;
|
||||||
|
|
||||||
|
// Randomize attacks for non-bipedal creatures with Weapon flag
|
||||||
|
if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() &&
|
||||||
|
!mPtr.getClass().isBipedal(mPtr) &&
|
||||||
|
(!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon)))
|
||||||
|
{
|
||||||
|
mCurrentWeapon = chooseRandomAttackAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
if(mWeaponType == WeapType_Spell)
|
if(mWeaponType == WeapType_Spell)
|
||||||
{
|
{
|
||||||
// Unset casting flag, otherwise pressing the mouse button down would
|
// Unset casting flag, otherwise pressing the mouse button down would
|
||||||
|
@ -1557,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())
|
||||||
|
@ -2072,7 +2105,12 @@ 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)
|
||||||
|
{
|
||||||
|
// It seems only bipedal actors use turning animations.
|
||||||
|
// Also do not use turning animations in the first-person view and when sneaking.
|
||||||
|
bool isFirstPlayer = mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson();
|
||||||
|
if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr))
|
||||||
{
|
{
|
||||||
if(rot.z() > rotationThreshold)
|
if(rot.z() > rotationThreshold)
|
||||||
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
|
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
|
||||||
|
@ -2080,6 +2118,7 @@ void CharacterController::update(float duration)
|
||||||
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
|
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering
|
// Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering
|
||||||
if (mPtr == getPlayer())
|
if (mPtr == getPlayer())
|
||||||
|
@ -2095,7 +2134,11 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (mPtr.getClass().isBipedal(mPtr))
|
||||||
|
{
|
||||||
|
if (mTurnAnimationThreshold > 0)
|
||||||
mTurnAnimationThreshold -= duration;
|
mTurnAnimationThreshold -= duration;
|
||||||
|
|
||||||
if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||
|
if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||
|
||||||
movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)
|
movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)
|
||||||
{
|
{
|
||||||
|
@ -2107,19 +2150,19 @@ void CharacterController::update(float duration)
|
||||||
movestate = mMovementState;
|
movestate = mMovementState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(movestate != CharState_None && !isTurning())
|
if(movestate != CharState_None && !isTurning())
|
||||||
clearAnimQueue();
|
clearAnimQueue();
|
||||||
|
|
||||||
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;
|
||||||
|
@ -2131,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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue