Merge branch 'attackontitan' into 'master'

Make the character controller less miserable, round 7: attack animations, round 2

See merge request OpenMW/openmw!2253
post_malone
psi29a 2 years ago
commit d9d7277b0e

@ -1129,9 +1129,10 @@ bool CharacterController::updateWeaponState()
const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf();
std::string upSoundId;
std::string downSoundId;
bool weaponChanged = false;
bool ammunition = true;
float weapSpeed = 1.f;
if (cls.hasInventoryStore(mPtr))
{
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
@ -1139,23 +1140,55 @@ bool CharacterController::updateWeaponState()
if(stats.getDrawState() == DrawState::Spell)
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end() && mWeaponType != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::Spell && weaptype != ESM::Weapon::None)
upSoundId = weapon->getClass().getUpSoundId(*weapon);
if(weapon != inv.end() && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None)
downSoundId = weapon->getClass().getDownSoundId(*weapon);
MWWorld::Ptr newWeapon;
if (weapon != inv.end())
{
newWeapon = *weapon;
if (isRealWeapon(mWeaponType))
downSoundId = newWeapon.getClass().getDownSoundId(newWeapon);
}
// weapon->HtH switch: weapon is empty already, so we need to take sound from previous weapon
if(weapon == inv.end() && !mWeapon.isEmpty() && weaptype == ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell)
else if (!mWeapon.isEmpty() && weaptype == ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell)
downSoundId = mWeapon.getClass().getDownSoundId(mWeapon);
MWWorld::Ptr newWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr();
if (mWeapon != newWeapon)
{
mWeapon = newWeapon;
weaponChanged = true;
}
if (stats.getDrawState() == DrawState::Weapon && !mWeapon.isEmpty() && mWeapon.getType() == ESM::Weapon::sRecordId)
{
weapSpeed = mWeapon.get<ESM::Weapon>()->mBase->mData.mSpeed;
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
int ammotype = getWeaponType(mWeapon.get<ESM::Weapon>()->mBase->mData.mType)->mAmmoType;
if (ammotype != ESM::Weapon::None)
ammunition = ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ammotype;
// Cancel attack if we no longer have ammunition
if (!ammunition)
{
if (mUpperBodyState == UpperBodyState::AttackPreWindUp || mUpperBodyState == UpperBodyState::AttackWindUp)
{
mAnimation->disable(mCurrentWeapon);
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");
}
}
// For biped actors, blend weapon animations with lower body animations with higher priority
@ -1268,10 +1301,13 @@ bool CharacterController::updateWeaponState()
{
mAnimation->showWeapons(true);
}
}
if (!upSoundId.empty())
{
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
if (!mWeapon.isEmpty() && mWeaponType != ESM::Weapon::HandToHand && isRealWeapon(weaptype))
{
std::string upSoundId = mWeapon.getClass().getUpSoundId(mWeapon);
if (!upSoundId.empty())
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
}
}
}
@ -1287,7 +1323,6 @@ bool CharacterController::updateWeaponState()
mWeaponType = weaptype;
mCurrentWeapon = weapgroup;
}
// Make sure that we disabled unequipping animation
@ -1314,46 +1349,6 @@ bool CharacterController::updateWeaponState()
sndMgr->stopSound3D(mPtr, "WolfRun");
}
bool ammunition = true;
float weapSpeed = 1.f;
if (cls.hasInventoryStore(mPtr))
{
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
if (stats.getDrawState() == DrawState::Weapon && !mWeapon.isEmpty() && mWeapon.getType() == ESM::Weapon::sRecordId)
{
weapSpeed = mWeapon.get<ESM::Weapon>()->mBase->mData.mSpeed;
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
int ammotype = getWeaponType(mWeapon.get<ESM::Weapon>()->mBase->mData.mType)->mAmmoType;
if (ammotype != ESM::Weapon::None)
ammunition = ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ammotype;
}
// Cancel attack if we no longer have ammunition
if (!ammunition)
{
if (mUpperBodyState == UpperBodyState::AttackPreWindUp || mUpperBodyState == UpperBodyState::AttackWindUp)
{
mAnimation->disable(mCurrentWeapon);
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");
}
}
// Combat for actors with persistent animations obviously will be buggy
if (isPersistentAnimPlaying())
return forcestateupdate;
@ -1397,17 +1392,14 @@ bool CharacterController::updateWeaponState()
spellCastResult = world->startSpellCast(mPtr);
mCanCast = spellCastResult == MWWorld::SpellCastState::Success;
if (spellid.empty())
if (spellid.empty() && cls.hasInventoryStore(mPtr))
{
if (cls.hasInventoryStore(mPtr))
MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
if (inv.getSelectedEnchantItem() != inv.end())
{
MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
if (inv.getSelectedEnchantItem() != inv.end())
{
const MWWorld::Ptr& enchantItem = *inv.getSelectedEnchantItem();
spellid = enchantItem.getClass().getEnchantment(enchantItem);
isMagicItem = true;
}
const MWWorld::Ptr& enchantItem = *inv.getSelectedEnchantItem();
spellid = enchantItem.getClass().getEnchantment(enchantItem);
isMagicItem = true;
}
}
@ -1554,8 +1546,8 @@ bool CharacterController::updateWeaponState()
}
}
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat
startKey = mAttackType+" start";
stopKey = mAttackType+" min attack";
startKey = mAttackType + ' ' + startKey;
stopKey = mAttackType + " min attack";
}
mAnimation->play(mCurrentWeapon, priorityWeapon,
@ -1584,18 +1576,6 @@ bool CharacterController::updateWeaponState()
if (!animPlaying)
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if (isKnockedDown())
{
if (mUpperBodyState > UpperBodyState::WeaponEquipped)
{
mUpperBodyState = UpperBodyState::WeaponEquipped;
if (mWeaponType > ESM::Weapon::None)
mAnimation->showWeapons(true);
}
if (!mCurrentWeapon.empty())
mAnimation->disable(mCurrentWeapon);
}
if (mUpperBodyState == UpperBodyState::AttackWindUp)
{
mAttackStrength = complete;
@ -1627,35 +1607,6 @@ bool CharacterController::updateWeaponState()
}
}
mAnimation->setPitchFactor(0.f);
if (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)
{
switch (mUpperBodyState)
{
case UpperBodyState::AttackPreWindUp:
mAnimation->setPitchFactor(complete);
break;
case UpperBodyState::AttackWindUp:
case UpperBodyState::AttackRelease:
case UpperBodyState::AttackHit:
mAnimation->setPitchFactor(1.f);
break;
case UpperBodyState::AttackEnd:
if (animPlaying)
{
// technically we do not need a pitch for crossbow reload animation,
// but we should avoid abrupt repositioning
if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f));
else
mAnimation->setPitchFactor(1.f-complete);
}
break;
default:
break;
}
}
if(!animPlaying)
{
if (mUpperBodyState == UpperBodyState::Equipping ||
@ -1709,35 +1660,23 @@ bool CharacterController::updateWeaponState()
playSwishSound(0.0f);
}
if(mAttackType == "shoot")
{
start = mAttackType+" min hit";
stop = mAttackType+" release";
}
else
{
start = mAttackType+" min hit";
stop = mAttackType+" hit";
}
std::string hit = mAttackType == "shoot" ? "release" : "hit";
start = mAttackType + " min hit";
stop = mAttackType + ' ' + hit;
mUpperBodyState = UpperBodyState::AttackHit;
break;
}
case UpperBodyState::AttackHit:
if(mAttackType == "shoot")
{
start = mAttackType+" follow start";
stop = mAttackType+" follow stop";
}
else
start = "follow start";
stop = "follow stop";
if (mAttackType != "shoot")
{
float str = mAttackStrength;
start = mAttackType+((str < 0.5f) ? " small follow start"
: (str < 1.0f) ? " medium follow start"
: " large follow start");
stop = mAttackType+((str < 0.5f) ? " small follow stop"
: (str < 1.0f) ? " medium follow stop"
: " large follow stop");
std::string strength = mAttackStrength < 0.5f ? "small" : mAttackStrength < 1.f ? "medium" : "large";
start = strength + ' ' + start;
stop = strength + ' ' + stop;
}
start = mAttackType + ' ' + start;
stop = mAttackType + ' ' + stop;
mUpperBodyState = UpperBodyState::AttackEnd;
break;
default:
@ -1757,6 +1696,27 @@ bool CharacterController::updateWeaponState()
mUpperBodyState = UpperBodyState::WeaponEquipped;
}
mAnimation->getInfo(mCurrentWeapon, &complete);
mAnimation->setPitchFactor(0.f);
if (mUpperBodyState > UpperBodyState::WeaponEquipped && (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown))
{
mAnimation->setPitchFactor(1.f);
// A smooth transition can be provided if a pre-wind-up section is defined. Random attack animations never have one.
if (mUpperBodyState == UpperBodyState::AttackPreWindUp && !isRandomAttackAnimation(mCurrentWeapon))
mAnimation->setPitchFactor(complete);
else if (mUpperBodyState == UpperBodyState::AttackEnd)
{
// technically we do not need a pitch for crossbow reload animation,
// but we should avoid abrupt repositioning
if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f));
else
mAnimation->setPitchFactor(1.f-complete);
}
}
mAnimation->setAccurateAiming(mUpperBodyState > UpperBodyState::WeaponEquipped);
return forcestateupdate;

Loading…
Cancel
Save