mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-19 14:41:34 +00:00
Merge branch 'lockandload' into 'master'
Make the character controller less miserable, round 6: attack animations, round 1 See merge request OpenMW/openmw!2252
This commit is contained in:
commit
0a16d54865
2 changed files with 186 additions and 218 deletions
|
@ -307,7 +307,7 @@ void CharacterController::resetCurrentHitState()
|
||||||
void CharacterController::resetCurrentWeaponState()
|
void CharacterController::resetCurrentWeaponState()
|
||||||
{
|
{
|
||||||
clearStateAnimation(mCurrentWeapon);
|
clearStateAnimation(mCurrentWeapon);
|
||||||
mUpperBodyState = UpperCharState_Nothing;
|
mUpperBodyState = UpperBodyState::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::resetCurrentDeathState()
|
void CharacterController::resetCurrentDeathState()
|
||||||
|
@ -401,15 +401,15 @@ void CharacterController::refreshHitRecoilAnims()
|
||||||
{
|
{
|
||||||
if (!mCurrentWeapon.empty())
|
if (!mCurrentWeapon.empty())
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
if (mUpperBodyState > UpperCharState_WeapEquiped)
|
if (mUpperBodyState > UpperBodyState::WeaponEquipped)
|
||||||
{
|
{
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
if (mWeaponType > ESM::Weapon::None)
|
if (mWeaponType > ESM::Weapon::None)
|
||||||
mAnimation->showWeapons(true);
|
mAnimation->showWeapons(true);
|
||||||
}
|
}
|
||||||
else if (mUpperBodyState < UpperCharState_WeapEquiped)
|
else if (mUpperBodyState < UpperBodyState::WeaponEquipped)
|
||||||
{
|
{
|
||||||
mUpperBodyState = UpperCharState_Nothing;
|
mUpperBodyState = UpperBodyState::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,7 +680,7 @@ void CharacterController::refreshIdleAnims(CharacterState idle, bool force)
|
||||||
{
|
{
|
||||||
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
|
// 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
|
// the idle animation should be displayed
|
||||||
if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped)
|
if (((mUpperBodyState != UpperBodyState::None && mUpperBodyState != UpperBodyState::WeaponEquipped)
|
||||||
|| mMovementState != CharState_None || mHitState != CharState_None) && !mPtr.getClass().isBipedal(mPtr))
|
|| mMovementState != CharState_None || mHitState != CharState_None) && !mPtr.getClass().isBipedal(mPtr))
|
||||||
{
|
{
|
||||||
resetCurrentIdleState();
|
resetCurrentIdleState();
|
||||||
|
@ -855,7 +855,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
||||||
getActiveWeapon(mPtr, &mWeaponType);
|
getActiveWeapon(mPtr, &mWeaponType);
|
||||||
if (mWeaponType != ESM::Weapon::None)
|
if (mWeaponType != ESM::Weapon::None)
|
||||||
{
|
{
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
mCurrentWeapon = getWeaponAnimation(mWeaponType);
|
mCurrentWeapon = getWeaponAnimation(mWeaponType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,7 +1071,7 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
|
||||||
|
|
||||||
void CharacterController::updateIdleStormState(bool inwater) const
|
void CharacterController::updateIdleStormState(bool inwater) const
|
||||||
{
|
{
|
||||||
if (!mAnimation->hasAnimation("idlestorm") || mUpperBodyState != UpperCharState_Nothing || inwater)
|
if (!mAnimation->hasAnimation("idlestorm") || mUpperBodyState != UpperBodyState::None || inwater)
|
||||||
{
|
{
|
||||||
mAnimation->disable("idlestorm");
|
mAnimation->disable("idlestorm");
|
||||||
return;
|
return;
|
||||||
|
@ -1113,7 +1113,7 @@ bool CharacterController::updateCarriedLeftVisible(const int weaptype) const
|
||||||
return mAnimation->updateCarriedLeftVisible(weaptype);
|
return mAnimation->updateCarriedLeftVisible(weaptype);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::updateWeaponState(CharacterState idle)
|
bool CharacterController::updateWeaponState()
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
auto& prng = world->getPrng();
|
auto& prng = world->getPrng();
|
||||||
|
@ -1166,20 +1166,18 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
bool forcestateupdate = false;
|
bool forcestateupdate = false;
|
||||||
|
|
||||||
// We should not play equipping animation and sound during weapon->weapon transition
|
// We should not play equipping animation and sound during weapon->weapon transition
|
||||||
const bool isStillWeapon = weaptype != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::Spell && weaptype != ESM::Weapon::None &&
|
const bool isStillWeapon = isRealWeapon(mWeaponType) && isRealWeapon(weaptype);
|
||||||
mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;
|
|
||||||
|
|
||||||
// If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires),
|
// If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires),
|
||||||
// we should force actor to the "weapon equipped" state, interrupt attack and update animations.
|
// we should force actor to the "weapon equipped" state, interrupt attack and update animations.
|
||||||
if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperCharState_WeapEquiped)
|
if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperBodyState::WeaponEquipped)
|
||||||
{
|
{
|
||||||
forcestateupdate = true;
|
forcestateupdate = true;
|
||||||
if (!mCurrentWeapon.empty())
|
if (!mCurrentWeapon.empty())
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
setAttackingOrSpell(false);
|
setAttackingOrSpell(false);
|
||||||
mAnimation->showWeapons(true);
|
mAnimation->showWeapons(true);
|
||||||
stats.setAttackingOrSpell(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
|
if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
|
||||||
|
@ -1187,7 +1185,7 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
std::string weapgroup;
|
std::string weapgroup;
|
||||||
if ((!isWerewolf || mWeaponType != ESM::Weapon::Spell)
|
if ((!isWerewolf || mWeaponType != ESM::Weapon::Spell)
|
||||||
&& weaptype != mWeaponType
|
&& weaptype != mWeaponType
|
||||||
&& mUpperBodyState != UpperCharState_UnEquipingWeap
|
&& mUpperBodyState != UpperBodyState::Unequipping
|
||||||
&& !isStillWeapon)
|
&& !isStillWeapon)
|
||||||
{
|
{
|
||||||
// We can not play un-equip animation if weapon changed since last update
|
// We can not play un-equip animation if weapon changed since last update
|
||||||
|
@ -1209,7 +1207,7 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
|
|
||||||
mAnimation->play(weapgroup, priorityWeapon, unequipMask, false,
|
mAnimation->play(weapgroup, priorityWeapon, unequipMask, false,
|
||||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
mUpperBodyState = UpperBodyState::Unequipping;
|
||||||
|
|
||||||
mAnimation->detachArrow();
|
mAnimation->detachArrow();
|
||||||
|
|
||||||
|
@ -1247,7 +1245,8 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
|
|
||||||
if (!isStillWeapon)
|
if (!isStillWeapon)
|
||||||
{
|
{
|
||||||
clearStateAnimation(mCurrentWeapon);
|
if (animPlaying)
|
||||||
|
mAnimation->disable(mCurrentWeapon);
|
||||||
if (weaptype != ESM::Weapon::None)
|
if (weaptype != ESM::Weapon::None)
|
||||||
{
|
{
|
||||||
mAnimation->showWeapons(false);
|
mAnimation->showWeapons(false);
|
||||||
|
@ -1262,15 +1261,18 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
|
|
||||||
mAnimation->play(weapgroup, priorityWeapon, equipMask, true,
|
mAnimation->play(weapgroup, priorityWeapon, equipMask, true,
|
||||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
mUpperBodyState = UpperBodyState::Equipping;
|
||||||
|
|
||||||
// If we do not have the "equip attach" key, show weapon manually.
|
// If we do not have the "equip attach" key, show weapon manually.
|
||||||
if (weaptype != ESM::Weapon::Spell)
|
if (weaptype != ESM::Weapon::Spell && mAnimation->getTextKeyTime(weapgroup+": equip attach") < 0)
|
||||||
{
|
{
|
||||||
if (mAnimation->getTextKeyTime(weapgroup+": equip attach") < 0)
|
|
||||||
mAnimation->showWeapons(true);
|
mAnimation->showWeapons(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!upSoundId.empty())
|
||||||
|
{
|
||||||
|
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isWerewolf)
|
if(isWerewolf)
|
||||||
|
@ -1286,18 +1288,13 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
mWeaponType = weaptype;
|
mWeaponType = weaptype;
|
||||||
mCurrentWeapon = weapgroup;
|
mCurrentWeapon = weapgroup;
|
||||||
|
|
||||||
if(!upSoundId.empty() && !isStillWeapon)
|
|
||||||
{
|
|
||||||
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that we disabled unequipping animation
|
// Make sure that we disabled unequipping animation
|
||||||
if (mUpperBodyState == UpperCharState_UnEquipingWeap)
|
if (mUpperBodyState == UpperBodyState::Unequipping)
|
||||||
{
|
{
|
||||||
resetCurrentWeaponState();
|
resetCurrentWeaponState();
|
||||||
mWeaponType = ESM::Weapon::None;
|
mWeaponType = ESM::Weapon::None;
|
||||||
mCurrentWeapon = getWeaponAnimation(mWeaponType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1317,29 +1314,43 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
sndMgr->stopSound3D(mPtr, "WolfRun");
|
sndMgr->stopSound3D(mPtr, "WolfRun");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel attack if we no longer have ammunition
|
|
||||||
bool ammunition = true;
|
bool ammunition = true;
|
||||||
bool isWeapon = false;
|
|
||||||
float weapSpeed = 1.f;
|
float weapSpeed = 1.f;
|
||||||
if (cls.hasInventoryStore(mPtr))
|
if (cls.hasInventoryStore(mPtr))
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
|
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
|
||||||
MWWorld::ConstContainerStoreIterator weapon = getActiveWeapon(mPtr, &weaptype);
|
if (stats.getDrawState() == DrawState::Weapon && !mWeapon.isEmpty() && mWeapon.getType() == ESM::Weapon::sRecordId)
|
||||||
isWeapon = (weapon != inv.end() && weapon->getType() == ESM::Weapon::sRecordId);
|
|
||||||
if (isWeapon)
|
|
||||||
{
|
{
|
||||||
weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
|
weapSpeed = mWeapon.get<ESM::Weapon>()->mBase->mData.mSpeed;
|
||||||
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||||
int ammotype = getWeaponType(weapon->get<ESM::Weapon>()->mBase->mData.mType)->mAmmoType;
|
int ammotype = getWeaponType(mWeapon.get<ESM::Weapon>()->mBase->mData.mType)->mAmmoType;
|
||||||
if (ammotype != ESM::Weapon::None && (ammo == inv.end() || ammo->get<ESM::Weapon>()->mBase->mData.mType != ammotype))
|
if (ammotype != ESM::Weapon::None)
|
||||||
ammunition = false;
|
ammunition = ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ammotype;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped)
|
// Cancel attack if we no longer have ammunition
|
||||||
|
if (!ammunition)
|
||||||
|
{
|
||||||
|
if (mUpperBodyState == UpperBodyState::AttackPreWindUp || mUpperBodyState == UpperBodyState::AttackWindUp)
|
||||||
{
|
{
|
||||||
if (!mCurrentWeapon.empty())
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
|
}
|
||||||
|
setAttackingOrSpell(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||||
|
if (torch != inv.end() && torch->getType() == ESM::Light::sRecordId && updateCarriedLeftVisible(mWeaponType))
|
||||||
|
{
|
||||||
|
if (mAnimation->isPlaying("shield"))
|
||||||
|
mAnimation->disable("shield");
|
||||||
|
|
||||||
|
mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm,
|
||||||
|
false, 1.0f, "start", "stop", 0.0f, std::numeric_limits<size_t>::max(), true);
|
||||||
|
}
|
||||||
|
else if (mAnimation->isPlaying("torch"))
|
||||||
|
{
|
||||||
|
mAnimation->disable("torch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1352,15 +1363,13 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
ESM::WeaponType::Class weapclass = getWeaponType(mWeaponType)->mWeaponClass;
|
ESM::WeaponType::Class weapclass = getWeaponType(mWeaponType)->mWeaponClass;
|
||||||
if(getAttackingOrSpell())
|
if(getAttackingOrSpell())
|
||||||
{
|
{
|
||||||
bool resetIdle = ammunition;
|
bool resetIdle = true;
|
||||||
if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block))
|
if (mUpperBodyState == UpperBodyState::WeaponEquipped && (mHitState == CharState_None || mHitState == CharState_Block))
|
||||||
{
|
{
|
||||||
mAttackStrength = 0;
|
mAttackStrength = 0;
|
||||||
|
|
||||||
// Randomize attacks for non-bipedal creatures
|
// Randomize attacks for non-bipedal creatures
|
||||||
if (cls.getType() == ESM::Creature::sRecordId &&
|
if (!cls.isBipedal(mPtr) && (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon)))
|
||||||
!cls.isBipedal(mPtr) &&
|
|
||||||
(!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon)))
|
|
||||||
{
|
{
|
||||||
mCurrentWeapon = chooseRandomAttackAnimation();
|
mCurrentWeapon = chooseRandomAttackAnimation();
|
||||||
}
|
}
|
||||||
|
@ -1411,7 +1420,7 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
resetIdle = false;
|
resetIdle = false;
|
||||||
// Spellcasting animation needs to "play" for at least one frame to reset the aiming factor
|
// Spellcasting animation needs to "play" for at least one frame to reset the aiming factor
|
||||||
animPlaying = true;
|
animPlaying = true;
|
||||||
mUpperBodyState = UpperCharState_CastingSpell;
|
mUpperBodyState = UpperBodyState::Casting;
|
||||||
}
|
}
|
||||||
// Play the spellcasting animation/VFX if the spellcasting was successful or failed due to insufficient magicka.
|
// Play the spellcasting animation/VFX if the spellcasting was successful or failed due to insufficient magicka.
|
||||||
// Used up powers are exempt from this from some reason.
|
// Used up powers are exempt from this from some reason.
|
||||||
|
@ -1485,65 +1494,53 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
MWRender::Animation::BlendMask_All, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
1, startKey, stopKey,
|
1, startKey, stopKey,
|
||||||
0.0f, 0);
|
0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_CastingSpell;
|
mUpperBodyState = UpperBodyState::Casting;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resetIdle = false;
|
resetIdle = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(mWeaponType == ESM::Weapon::PickProbe)
|
else
|
||||||
{
|
{
|
||||||
|
std::string startKey = "start";
|
||||||
|
std::string stopKey = "stop";
|
||||||
|
bool autodisable = false;
|
||||||
|
|
||||||
|
if (mWeaponType == ESM::Weapon::PickProbe)
|
||||||
|
{
|
||||||
|
autodisable = true;
|
||||||
|
mUpperBodyState = UpperBodyState::AttackEnd;
|
||||||
|
|
||||||
world->breakInvisibility(mPtr);
|
world->breakInvisibility(mPtr);
|
||||||
MWWorld::ContainerStoreIterator weapon = cls.getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
||||||
MWWorld::Ptr item = *weapon;
|
|
||||||
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
|
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
|
||||||
MWWorld::Ptr target = world->getFacedObject();
|
MWWorld::Ptr target = world->getFacedObject();
|
||||||
std::string resultMessage, resultSound;
|
std::string resultMessage, resultSound;
|
||||||
|
|
||||||
if(!target.isEmpty())
|
if(!target.isEmpty())
|
||||||
{
|
{
|
||||||
if(item.getType() == ESM::Lockpick::sRecordId)
|
if (mWeapon.getType() == ESM::Lockpick::sRecordId)
|
||||||
Security(mPtr).pickLock(target, item, resultMessage, resultSound);
|
Security(mPtr).pickLock(target, mWeapon, resultMessage, resultSound);
|
||||||
else if(item.getType() == ESM::Probe::sRecordId)
|
else if (mWeapon.getType() == ESM::Probe::sRecordId)
|
||||||
Security(mPtr).probeTrap(target, item, resultMessage, resultSound);
|
Security(mPtr).probeTrap(target, mWeapon, resultMessage, resultSound);
|
||||||
}
|
}
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
|
||||||
MWRender::Animation::BlendMask_All, true,
|
|
||||||
1.0f, "start", "stop", 0.0, 0);
|
|
||||||
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
|
|
||||||
|
|
||||||
if (!resultMessage.empty())
|
if (!resultMessage.empty())
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(resultMessage);
|
MWBase::Environment::get().getWindowManager()->messageBox(resultMessage);
|
||||||
if (!resultSound.empty())
|
if (!resultSound.empty())
|
||||||
sndMgr->playSound3D(target, resultSound, 1.0f, 1.0f);
|
sndMgr->playSound3D(target, resultSound, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else if (ammunition)
|
else if (!isRandomAttackAnimation(mCurrentWeapon))
|
||||||
{
|
{
|
||||||
std::string startKey;
|
|
||||||
std::string stopKey;
|
|
||||||
|
|
||||||
if (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)
|
if (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)
|
||||||
{
|
|
||||||
mAttackType = "shoot";
|
mAttackType = "shoot";
|
||||||
startKey = mAttackType+" start";
|
else if (mPtr == getPlayer())
|
||||||
stopKey = mAttackType+" min attack";
|
|
||||||
}
|
|
||||||
else if (isRandomAttackAnimation(mCurrentWeapon))
|
|
||||||
{
|
|
||||||
startKey = "start";
|
|
||||||
stopKey = "stop";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(mPtr == getPlayer())
|
|
||||||
{
|
{
|
||||||
if (Settings::Manager::getBool("best attack", "Game"))
|
if (Settings::Manager::getBool("best attack", "Game"))
|
||||||
{
|
{
|
||||||
if (isWeapon)
|
if (!mWeapon.isEmpty() && mWeapon.getType() == ESM::Weapon::sRecordId)
|
||||||
{
|
{
|
||||||
MWWorld::ConstContainerStoreIterator weapon = cls.getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
mAttackType = getBestAttack(mWeapon.get<ESM::Weapon>()->mBase);
|
||||||
mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1562,12 +1559,12 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_All, autodisable,
|
||||||
weapSpeed, startKey, stopKey,
|
weapSpeed, startKey, stopKey,
|
||||||
0.0f, 0);
|
0.0f, 0);
|
||||||
if(mAnimation->getCurrentTime(mCurrentWeapon) != -1.f)
|
if (mWeaponType != ESM::Weapon::PickProbe && mAnimation->getCurrentTime(mCurrentWeapon) != -1.f)
|
||||||
{
|
{
|
||||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
mUpperBodyState = UpperBodyState::AttackPreWindUp;
|
||||||
if (isRandomAttackAnimation(mCurrentWeapon))
|
if (isRandomAttackAnimation(mCurrentWeapon))
|
||||||
{
|
{
|
||||||
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability(prng));
|
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability(prng));
|
||||||
|
@ -1578,25 +1575,34 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should not break swim and sneak animations
|
// We should not break swim and sneak animations
|
||||||
if (resetIdle &&
|
if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
|
||||||
idle != CharState_IdleSneak && idle != CharState_IdleSwim &&
|
|
||||||
mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
|
|
||||||
{
|
{
|
||||||
resetCurrentIdleState();
|
resetCurrentIdleState();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!animPlaying)
|
if (!animPlaying)
|
||||||
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
||||||
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
|
|
||||||
mAttackStrength = complete;
|
if (isKnockedDown())
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
if (mUpperBodyState > UpperBodyState::WeaponEquipped)
|
||||||
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
|
{
|
||||||
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
|
if (mWeaponType > ESM::Weapon::None)
|
||||||
|
mAnimation->showWeapons(true);
|
||||||
|
}
|
||||||
|
if (!mCurrentWeapon.empty())
|
||||||
|
mAnimation->disable(mCurrentWeapon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mUpperBodyState == UpperBodyState::AttackWindUp)
|
||||||
|
{
|
||||||
|
mAttackStrength = complete;
|
||||||
|
|
||||||
|
if (!getAttackingOrSpell())
|
||||||
{
|
{
|
||||||
world->breakInvisibility(mPtr);
|
world->breakInvisibility(mPtr);
|
||||||
float attackStrength = complete;
|
|
||||||
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
|
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
|
||||||
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
|
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
|
||||||
if (minAttackTime == maxAttackTime)
|
if (minAttackTime == maxAttackTime)
|
||||||
|
@ -1604,25 +1610,12 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
// most creatures don't actually have an attack wind-up animation, so use a uniform random value
|
// most creatures don't actually have an attack wind-up animation, so use a uniform random value
|
||||||
// (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)
|
// (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)
|
||||||
// Note: vanilla MW uses a random value for *all* non-player actors, but we probably don't need to go that far.
|
// Note: vanilla MW uses a random value for *all* non-player actors, but we probably don't need to go that far.
|
||||||
attackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability(prng));
|
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability(prng));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)
|
playSwishSound(mAttackStrength);
|
||||||
{
|
|
||||||
if(isWerewolf)
|
|
||||||
{
|
|
||||||
const MWWorld::ESMStore &store = world->getStore();
|
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfSwing", prng);
|
|
||||||
if(sound)
|
|
||||||
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
playSwishSound(attackStrength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mAttackStrength = attackStrength;
|
|
||||||
|
|
||||||
|
if (animPlaying)
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
|
@ -1630,18 +1623,7 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
1.0f-complete, 0);
|
1.0f-complete, 0);
|
||||||
|
|
||||||
complete = 0.f;
|
complete = 0.f;
|
||||||
mUpperBodyState = UpperCharState_MaxAttackToMinHit;
|
mUpperBodyState = UpperBodyState::AttackRelease;
|
||||||
}
|
|
||||||
else if (isKnockedDown())
|
|
||||||
{
|
|
||||||
if (mUpperBodyState > UpperCharState_WeapEquiped)
|
|
||||||
{
|
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
|
||||||
if (mWeaponType > ESM::Weapon::None)
|
|
||||||
mAnimation->showWeapons(true);
|
|
||||||
}
|
|
||||||
if (!mCurrentWeapon.empty())
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1650,15 +1632,15 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
{
|
{
|
||||||
switch (mUpperBodyState)
|
switch (mUpperBodyState)
|
||||||
{
|
{
|
||||||
case UpperCharState_StartToMinAttack:
|
case UpperBodyState::AttackPreWindUp:
|
||||||
mAnimation->setPitchFactor(complete);
|
mAnimation->setPitchFactor(complete);
|
||||||
break;
|
break;
|
||||||
case UpperCharState_MinAttackToMaxAttack:
|
case UpperBodyState::AttackWindUp:
|
||||||
case UpperCharState_MaxAttackToMinHit:
|
case UpperBodyState::AttackRelease:
|
||||||
case UpperCharState_MinHitToHit:
|
case UpperBodyState::AttackHit:
|
||||||
mAnimation->setPitchFactor(1.f);
|
mAnimation->setPitchFactor(1.f);
|
||||||
break;
|
break;
|
||||||
case UpperCharState_FollowStartToFollowStop:
|
case UpperBodyState::AttackEnd:
|
||||||
if (animPlaying)
|
if (animPlaying)
|
||||||
{
|
{
|
||||||
// technically we do not need a pitch for crossbow reload animation,
|
// technically we do not need a pitch for crossbow reload animation,
|
||||||
|
@ -1676,39 +1658,39 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
|
|
||||||
if(!animPlaying)
|
if(!animPlaying)
|
||||||
{
|
{
|
||||||
if(mUpperBodyState == UpperCharState_EquipingWeap ||
|
if (mUpperBodyState == UpperBodyState::Equipping ||
|
||||||
mUpperBodyState == UpperCharState_FollowStartToFollowStop ||
|
mUpperBodyState == UpperBodyState::AttackEnd ||
|
||||||
mUpperBodyState == UpperCharState_CastingSpell)
|
mUpperBodyState == UpperBodyState::Casting)
|
||||||
{
|
{
|
||||||
if (ammunition && mWeaponType == ESM::Weapon::MarksmanCrossbow)
|
if (ammunition && mWeaponType == ESM::Weapon::MarksmanCrossbow)
|
||||||
mAnimation->attachArrow();
|
mAnimation->attachArrow();
|
||||||
|
|
||||||
// Cancel stagger animation at the end of an attack to avoid abrupt transitions
|
// Cancel stagger animation at the end of an attack to avoid abrupt transitions
|
||||||
// in favor of a different abrupt transition, like Morrowind
|
// in favor of a different abrupt transition, like Morrowind
|
||||||
if (mUpperBodyState != UpperCharState_EquipingWeap && isRecovery())
|
if (mUpperBodyState != UpperBodyState::Equipping && isRecovery())
|
||||||
mAnimation->disable(mCurrentHit);
|
mAnimation->disable(mCurrentHit);
|
||||||
|
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
}
|
}
|
||||||
else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
|
else if (mUpperBodyState == UpperBodyState::Unequipping)
|
||||||
mUpperBodyState = UpperCharState_Nothing;
|
mUpperBodyState = UpperBodyState::None;
|
||||||
}
|
}
|
||||||
else if(complete >= 1.0f && !isRandomAttackAnimation(mCurrentWeapon))
|
else if(complete >= 1.0f && !isRandomAttackAnimation(mCurrentWeapon))
|
||||||
{
|
{
|
||||||
std::string start, stop;
|
std::string start, stop;
|
||||||
switch(mUpperBodyState)
|
switch(mUpperBodyState)
|
||||||
{
|
{
|
||||||
case UpperCharState_MinAttackToMaxAttack:
|
case UpperBodyState::AttackWindUp:
|
||||||
//hack to avoid body pos desync when jumping/sneaking in 'max attack' state
|
//hack to avoid body pos desync when jumping/sneaking in 'max attack' state
|
||||||
if(!mAnimation->isPlaying(mCurrentWeapon))
|
if(!mAnimation->isPlaying(mCurrentWeapon))
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
|
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
|
||||||
break;
|
break;
|
||||||
case UpperCharState_StartToMinAttack:
|
case UpperBodyState::AttackPreWindUp:
|
||||||
case UpperCharState_MaxAttackToMinHit:
|
case UpperBodyState::AttackRelease:
|
||||||
{
|
{
|
||||||
if (mUpperBodyState == UpperCharState_StartToMinAttack)
|
if (mUpperBodyState == UpperBodyState::AttackPreWindUp)
|
||||||
{
|
{
|
||||||
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
|
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
|
||||||
// Happens if the player did not hold the attack button.
|
// Happens if the player did not hold the attack button.
|
||||||
|
@ -1719,12 +1701,11 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
{
|
{
|
||||||
start = mAttackType+" min attack";
|
start = mAttackType+" min attack";
|
||||||
stop = mAttackType+" max attack";
|
stop = mAttackType+" max attack";
|
||||||
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
|
mUpperBodyState = UpperBodyState::AttackWindUp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
world->breakInvisibility(mPtr);
|
world->breakInvisibility(mPtr);
|
||||||
if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)
|
|
||||||
playSwishSound(0.0f);
|
playSwishSound(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1738,10 +1719,10 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
start = mAttackType+" min hit";
|
start = mAttackType+" min hit";
|
||||||
stop = mAttackType+" hit";
|
stop = mAttackType+" hit";
|
||||||
}
|
}
|
||||||
mUpperBodyState = UpperCharState_MinHitToHit;
|
mUpperBodyState = UpperBodyState::AttackHit;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UpperCharState_MinHitToHit:
|
case UpperBodyState::AttackHit:
|
||||||
if(mAttackType == "shoot")
|
if(mAttackType == "shoot")
|
||||||
{
|
{
|
||||||
start = mAttackType+" follow start";
|
start = mAttackType+" follow start";
|
||||||
|
@ -1757,57 +1738,26 @@ bool CharacterController::updateWeaponState(CharacterState idle)
|
||||||
: (str < 1.0f) ? " medium follow stop"
|
: (str < 1.0f) ? " medium follow stop"
|
||||||
: " large follow stop");
|
: " large follow stop");
|
||||||
}
|
}
|
||||||
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
|
mUpperBodyState = UpperBodyState::AttackEnd;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: apply crossbow reload animation only for upper body
|
|
||||||
// since blending with movement animations can give weird result.
|
|
||||||
if(!start.empty())
|
if(!start.empty())
|
||||||
{
|
{
|
||||||
int mask = MWRender::Animation::BlendMask_All;
|
bool autodisable = mUpperBodyState == UpperBodyState::AttackEnd;
|
||||||
if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
|
|
||||||
mask = MWRender::Animation::BlendMask_UpperBody;
|
|
||||||
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, autodisable, weapSpeed, start, stop, 0.0f, 0);
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
|
||||||
mask, true,
|
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
|
||||||
else
|
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
|
||||||
mask, false,
|
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(complete >= 1.0f && isRandomAttackAnimation(mCurrentWeapon))
|
else if(complete >= 1.0f && isRandomAttackAnimation(mCurrentWeapon))
|
||||||
{
|
{
|
||||||
clearStateAnimation(mCurrentWeapon);
|
clearStateAnimation(mCurrentWeapon);
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls.hasInventoryStore(mPtr))
|
mAnimation->setAccurateAiming(mUpperBodyState > UpperBodyState::WeaponEquipped);
|
||||||
{
|
|
||||||
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
|
|
||||||
MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
||||||
if(torch != inv.end() && torch->getType() == ESM::Light::sRecordId
|
|
||||||
&& updateCarriedLeftVisible(mWeaponType))
|
|
||||||
{
|
|
||||||
if (mAnimation->isPlaying("shield"))
|
|
||||||
mAnimation->disable("shield");
|
|
||||||
|
|
||||||
mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm,
|
|
||||||
false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true);
|
|
||||||
}
|
|
||||||
else if (mAnimation->isPlaying("torch"))
|
|
||||||
{
|
|
||||||
mAnimation->disable("torch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mAnimation->setAccurateAiming(mUpperBodyState > UpperCharState_WeapEquiped);
|
|
||||||
|
|
||||||
return forcestateupdate;
|
return forcestateupdate;
|
||||||
}
|
}
|
||||||
|
@ -2270,7 +2220,7 @@ void CharacterController::update(float duration)
|
||||||
|
|
||||||
if (!mSkipAnim)
|
if (!mSkipAnim)
|
||||||
{
|
{
|
||||||
refreshCurrentAnims(idlestate, movestate, jumpstate, updateWeaponState(idlestate));
|
refreshCurrentAnims(idlestate, movestate, jumpstate, updateWeaponState());
|
||||||
updateIdleStormState(inwater);
|
updateIdleStormState(inwater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2567,8 +2517,8 @@ void CharacterController::forceStateUpdate()
|
||||||
mCanCast = false;
|
mCanCast = false;
|
||||||
mCastingManualSpell = false;
|
mCastingManualSpell = false;
|
||||||
setAttackingOrSpell(false);
|
setAttackingOrSpell(false);
|
||||||
if (mUpperBodyState != UpperCharState_Nothing)
|
if (mUpperBodyState != UpperBodyState::None)
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperBodyState::WeaponEquipped;
|
||||||
|
|
||||||
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||||
|
|
||||||
|
@ -2687,13 +2637,13 @@ bool CharacterController::isRandomAttackAnimation(std::string_view group)
|
||||||
|
|
||||||
bool CharacterController::isAttackPreparing() const
|
bool CharacterController::isAttackPreparing() const
|
||||||
{
|
{
|
||||||
return mUpperBodyState == UpperCharState_StartToMinAttack ||
|
return mUpperBodyState == UpperBodyState::AttackPreWindUp ||
|
||||||
mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
|
mUpperBodyState == UpperBodyState::AttackWindUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::isCastingSpell() const
|
bool CharacterController::isCastingSpell() const
|
||||||
{
|
{
|
||||||
return mCastingManualSpell || mUpperBodyState == UpperCharState_CastingSpell;
|
return mCastingManualSpell || mUpperBodyState == UpperBodyState::Casting;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::isReadyToBlock() const
|
bool CharacterController::isReadyToBlock() const
|
||||||
|
@ -2729,8 +2679,7 @@ bool CharacterController::isRecovery() const
|
||||||
|
|
||||||
bool CharacterController::isAttackingOrSpell() const
|
bool CharacterController::isAttackingOrSpell() const
|
||||||
{
|
{
|
||||||
return mUpperBodyState != UpperCharState_Nothing &&
|
return mUpperBodyState != UpperBodyState::None && mUpperBodyState != UpperBodyState::WeaponEquipped;
|
||||||
mUpperBodyState != UpperCharState_WeapEquiped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::isSneaking() const
|
bool CharacterController::isSneaking() const
|
||||||
|
@ -2786,7 +2735,7 @@ std::string_view CharacterController::getRandomAttackType()
|
||||||
bool CharacterController::readyToPrepareAttack() const
|
bool CharacterController::readyToPrepareAttack() const
|
||||||
{
|
{
|
||||||
return (mHitState == CharState_None || mHitState == CharState_Block)
|
return (mHitState == CharState_None || mHitState == CharState_Block)
|
||||||
&& mUpperBodyState <= UpperCharState_WeapEquiped;
|
&& mUpperBodyState <= UpperBodyState::WeaponEquipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::readyToStartAttack() const
|
bool CharacterController::readyToStartAttack() const
|
||||||
|
@ -2794,7 +2743,7 @@ bool CharacterController::readyToStartAttack() const
|
||||||
if (mHitState != CharState_None && mHitState != CharState_Block)
|
if (mHitState != CharState_None && mHitState != CharState_Block)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return mUpperBodyState == UpperCharState_WeapEquiped;
|
return mUpperBodyState == UpperBodyState::WeaponEquipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
float CharacterController::getAttackStrength() const
|
float CharacterController::getAttackStrength() const
|
||||||
|
@ -2819,15 +2768,33 @@ void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target)
|
||||||
|
|
||||||
void CharacterController::playSwishSound(float attackStrength) const
|
void CharacterController::playSwishSound(float attackStrength) const
|
||||||
{
|
{
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
ESM::WeaponType::Class weapclass = getWeaponType(mWeaponType)->mWeaponClass;
|
||||||
|
if (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)
|
||||||
|
return;
|
||||||
|
|
||||||
std::string sound = "Weapon Swish";
|
std::string soundId;
|
||||||
if(attackStrength < 0.5f)
|
float pitch = 1.f;
|
||||||
sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
|
|
||||||
else if(attackStrength < 1.0f)
|
const MWWorld::Class &cls = mPtr.getClass();
|
||||||
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
|
if (cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf())
|
||||||
|
{
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
const MWWorld::ESMStore &store = world->getStore();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfSwing", world->getPrng());
|
||||||
|
if (sound)
|
||||||
|
soundId = sound->mId;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack
|
{
|
||||||
|
soundId = "Weapon Swish";
|
||||||
|
if (attackStrength < 0.5f)
|
||||||
|
pitch = 0.8f; // Weak attack
|
||||||
|
else if (attackStrength >= 1.f)
|
||||||
|
pitch = 1.2f; // Strong attack
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!soundId.empty())
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, soundId, 1.0f, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::updateHeadTracking(float duration)
|
void CharacterController::updateHeadTracking(float duration)
|
||||||
|
|
|
@ -103,17 +103,18 @@ enum CharacterState {
|
||||||
CharState_Block
|
CharState_Block
|
||||||
};
|
};
|
||||||
|
|
||||||
enum UpperBodyCharacterState {
|
enum class UpperBodyState
|
||||||
UpperCharState_Nothing,
|
{
|
||||||
UpperCharState_EquipingWeap,
|
None,
|
||||||
UpperCharState_UnEquipingWeap,
|
Equipping,
|
||||||
UpperCharState_WeapEquiped,
|
Unequipping,
|
||||||
UpperCharState_StartToMinAttack,
|
WeaponEquipped,
|
||||||
UpperCharState_MinAttackToMaxAttack,
|
AttackPreWindUp,
|
||||||
UpperCharState_MaxAttackToMinHit,
|
AttackWindUp,
|
||||||
UpperCharState_MinHitToHit,
|
AttackRelease,
|
||||||
UpperCharState_FollowStartToFollowStop,
|
AttackHit,
|
||||||
UpperCharState_CastingSpell
|
AttackEnd,
|
||||||
|
Casting
|
||||||
};
|
};
|
||||||
|
|
||||||
enum JumpingState {
|
enum JumpingState {
|
||||||
|
@ -156,7 +157,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
||||||
CharacterState mHitState{CharState_None};
|
CharacterState mHitState{CharState_None};
|
||||||
std::string mCurrentHit;
|
std::string mCurrentHit;
|
||||||
|
|
||||||
UpperBodyCharacterState mUpperBodyState{UpperCharState_Nothing};
|
UpperBodyState mUpperBodyState{UpperBodyState::None};
|
||||||
|
|
||||||
JumpingState mJumpState{JumpState_None};
|
JumpingState mJumpState{JumpState_None};
|
||||||
std::string mCurrentJump;
|
std::string mCurrentJump;
|
||||||
|
@ -203,7 +204,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
||||||
|
|
||||||
void clearAnimQueue(bool clearPersistAnims = false);
|
void clearAnimQueue(bool clearPersistAnims = false);
|
||||||
|
|
||||||
bool updateWeaponState(CharacterState idle);
|
bool updateWeaponState();
|
||||||
void updateIdleStormState(bool inwater) const;
|
void updateIdleStormState(bool inwater) const;
|
||||||
|
|
||||||
std::string chooseRandomAttackAnimation() const;
|
std::string chooseRandomAttackAnimation() const;
|
||||||
|
|
Loading…
Reference in a new issue