1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-05-10 14:11:29 +00:00

Merge branch 'outofcharacter' into 'master'

Make CharacterController less miserable, round 1

See merge request OpenMW/openmw!1999
This commit is contained in:
psi29a 2022-06-11 18:50:06 +00:00
commit f8a6001e87
4 changed files with 81 additions and 96 deletions

View file

@ -703,7 +703,7 @@ std::string chooseBestAttack(const ESM::Weapon* weapon)
attackType = "chop"; attackType = "chop";
} }
else else
MWMechanics::CharacterController::setAttackTypeRandomly(attackType); attackType = MWMechanics::CharacterController::getRandomAttackType();
return attackType; return attackType;
} }

View file

@ -385,7 +385,7 @@ void CharacterController::refreshJumpAnims(const std::string& weapShortGroup, Ju
} }
} }
bool CharacterController::onOpen() bool CharacterController::onOpen() const
{ {
if (mPtr.getType() == ESM::Container::sRecordId) if (mPtr.getType() == ESM::Container::sRecordId)
{ {
@ -406,7 +406,7 @@ bool CharacterController::onOpen()
return true; return true;
} }
void CharacterController::onClose() void CharacterController::onClose() const
{ {
if (mPtr.getType() == ESM::Container::sRecordId) if (mPtr.getType() == ESM::Container::sRecordId)
{ {
@ -445,7 +445,7 @@ std::string CharacterController::getWeaponAnimation(int weaponType) const
return weaponGroup; return weaponGroup;
} }
std::string CharacterController::fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask) std::string CharacterController::fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask) const
{ {
bool isRealWeapon = mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None; bool isRealWeapon = mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;
if (!isRealWeapon) if (!isRealWeapon)
@ -561,7 +561,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
std::string::size_type runpos = movementAnimName.find("run"); std::string::size_type runpos = movementAnimName.find("run");
if (runpos != std::string::npos) if (runpos != std::string::npos)
{ {
movementAnimName.replace(runpos, runpos+3, "walk"); movementAnimName.replace(runpos, 3, "walk");
if (!mAnimation->hasAnimation(movementAnimName)) if (!mAnimation->hasAnimation(movementAnimName))
movementAnimName.clear(); movementAnimName.clear();
} }
@ -842,28 +842,7 @@ std::string CharacterController::chooseRandomAttackAnimation() const
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr) : mPtr(ptr)
, mWeapon(MWWorld::Ptr())
, mAnimation(anim) , mAnimation(anim)
, mIdleState(CharState_None)
, mMovementState(CharState_None)
, mMovementAnimSpeed(0.f)
, mAdjustMovementAnimSpeed(false)
, mHasMovedInXY(false)
, mMovementAnimationControlled(true)
, mDeathState(CharState_None)
, mFloatToSurface(true)
, mHitState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing)
, mJumpState(JumpState_None)
, mWeaponType(ESM::Weapon::None)
, mAttackStrength(0.f)
, mSkipAnim(false)
, mSecondsOfSwimming(0)
, mSecondsOfRunning(0)
, mTurnAnimationThreshold(0)
, mCastingManualSpell(false)
, mTimeUntilWake(0.f)
, mIsMovingBackward(false)
{ {
if(!mAnimation) if(!mAnimation)
return; return;
@ -949,9 +928,9 @@ CharacterController::~CharacterController()
} }
} }
void CharacterController::handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map) void CharacterController::handleTextKey(std::string_view groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map)
{ {
const std::string &evt = key->second; std::string_view evt = key->second;
if(evt.compare(0, 7, "sound: ") == 0) if(evt.compare(0, 7, "sound: ") == 0)
{ {
@ -963,7 +942,7 @@ void CharacterController::handleTextKey(const std::string &groupname, SceneUtil:
auto& charClass = mPtr.getClass(); auto& charClass = mPtr.getClass();
if(evt.compare(0, 10, "soundgen: ") == 0) if(evt.compare(0, 10, "soundgen: ") == 0)
{ {
std::string soundgen = evt.substr(10); std::string soundgen = std::string(evt.substr(10));
// The event can optionally contain volume and pitch modifiers // The event can optionally contain volume and pitch modifiers
float volume=1.f, pitch=1.f; float volume=1.f, pitch=1.f;
@ -1043,16 +1022,18 @@ void CharacterController::handleTextKey(const std::string &groupname, SceneUtil:
{ {
std::multimap<float, std::string>::const_iterator hitKey = key; std::multimap<float, std::string>::const_iterator hitKey = key;
std::string hitKeyName = std::string(groupname) + ": hit";
std::string stopKeyName = std::string(groupname) + ": stop";
// Not all animations have a hit key defined. If there is none, the hit happens with the start key. // Not all animations have a hit key defined. If there is none, the hit happens with the start key.
bool hasHitKey = false; bool hasHitKey = false;
while (hitKey != map.end()) while (hitKey != map.end())
{ {
if (hitKey->second == groupname + ": hit") if (hitKey->second == hitKeyName)
{ {
hasHitKey = true; hasHitKey = true;
break; break;
} }
if (hitKey->second == groupname + ": stop") if (hitKey->second == stopKeyName)
break; break;
++hitKey; ++hitKey;
} }
@ -1093,7 +1074,7 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
mPtr = ptr; mPtr = ptr;
} }
void CharacterController::updateIdleStormState(bool inwater) void CharacterController::updateIdleStormState(bool inwater) const
{ {
if (!mAnimation->hasAnimation("idlestorm") || mUpperBodyState != UpperCharState_Nothing || inwater) if (!mAnimation->hasAnimation("idlestorm") || mUpperBodyState != UpperCharState_Nothing || inwater)
{ {
@ -1548,12 +1529,12 @@ bool CharacterController::updateState(CharacterState idle)
else else
{ {
// There is no "best attack" for Hand-to-Hand // There is no "best attack" for Hand-to-Hand
setAttackTypeRandomly(mAttackType); mAttackType = getRandomAttackType();
} }
} }
else else
{ {
setAttackTypeBasedOnMovement(); mAttackType = getMovementBasedAttackType();
} }
} }
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat // else if (mPtr != getPlayer()) use mAttackType set by AiCombat
@ -2382,7 +2363,7 @@ void CharacterController::update(float duration)
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead()); mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
} }
void CharacterController::persistAnimationState() void CharacterController::persistAnimationState() const
{ {
ESM::AnimationState& state = mPtr.getRefData().getAnimationState(); ESM::AnimationState& state = mPtr.getRefData().getAnimationState();
@ -2518,18 +2499,18 @@ void CharacterController::skipAnim()
mSkipAnim = true; mSkipAnim = true;
} }
bool CharacterController::isPersistentAnimPlaying() bool CharacterController::isPersistentAnimPlaying() const
{ {
if (!mAnimQueue.empty()) if (!mAnimQueue.empty())
{ {
AnimationQueueEntry& first = mAnimQueue.front(); const AnimationQueueEntry& first = mAnimQueue.front();
return first.mPersist && isAnimPlaying(first.mGroup); return first.mPersist && isAnimPlaying(first.mGroup);
} }
return false; return false;
} }
bool CharacterController::isAnimPlaying(const std::string &groupName) bool CharacterController::isAnimPlaying(const std::string &groupName) const
{ {
if(mAnimation == nullptr) if(mAnimation == nullptr)
return false; return false;
@ -2542,9 +2523,15 @@ void CharacterController::clearAnimQueue(bool clearPersistAnims)
if ((!isPersistentAnimPlaying() || clearPersistAnims) && !mAnimQueue.empty()) if ((!isPersistentAnimPlaying() || clearPersistAnims) && !mAnimQueue.empty())
mAnimation->disable(mAnimQueue.front().mGroup); mAnimation->disable(mAnimQueue.front().mGroup);
if (clearPersistAnims)
{
mAnimQueue.clear();
return;
}
for (AnimationQueue::iterator it = mAnimQueue.begin(); it != mAnimQueue.end();) for (AnimationQueue::iterator it = mAnimQueue.begin(); it != mAnimQueue.end();)
{ {
if (clearPersistAnims || !it->mPersist) if (!it->mPersist)
it = mAnimQueue.erase(it); it = mAnimQueue.erase(it);
else else
++it; ++it;
@ -2610,7 +2597,7 @@ void CharacterController::resurrect()
mWeaponType = ESM::Weapon::None; mWeaponType = ESM::Weapon::None;
} }
void CharacterController::updateContinuousVfx() void CharacterController::updateContinuousVfx() const
{ {
// Keeping track of when to stop a continuous VFX seems to be very difficult to do inside the spells code, // Keeping track of when to stop a continuous VFX seems to be very difficult to do inside the spells code,
// as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here. // as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here.
@ -2627,7 +2614,7 @@ void CharacterController::updateContinuousVfx()
} }
} }
void CharacterController::updateMagicEffects() void CharacterController::updateMagicEffects() const
{ {
if (!mPtr.getClass().isActor()) if (!mPtr.getClass().isActor())
return; return;
@ -2643,7 +2630,7 @@ void CharacterController::updateMagicEffects()
mAnimation->setVampire(vampire); mAnimation->setVampire(vampire);
} }
void CharacterController::setVisibility(float visibility) void CharacterController::setVisibility(float visibility) const
{ {
// We should take actor's invisibility in account // We should take actor's invisibility in account
if (mPtr.getClass().isActor()) if (mPtr.getClass().isActor())
@ -2669,18 +2656,17 @@ void CharacterController::setVisibility(float visibility)
mAnimation->setAlpha(visibility); mAnimation->setAlpha(visibility);
} }
void CharacterController::setAttackTypeBasedOnMovement() std::string CharacterController::getMovementBasedAttackType() const
{ {
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition; float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward
mAttackType = "thrust"; return "thrust";
else if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway
mAttackType = "slash"; return "slash";
else return "chop";
mAttackType = "chop";
} }
bool CharacterController::isRandomAttackAnimation(const std::string& group) const bool CharacterController::isRandomAttackAnimation(std::string_view group)
{ {
return (group == "attack1" || group == "swimattack1" || return (group == "attack1" || group == "swimattack1" ||
group == "attack2" || group == "swimattack2" || group == "attack2" || group == "swimattack2" ||
@ -2774,16 +2760,15 @@ void CharacterController::setAIAttackType(const std::string& attackType)
mAttackType = attackType; mAttackType = attackType;
} }
void CharacterController::setAttackTypeRandomly(std::string& attackType) std::string CharacterController::getRandomAttackType()
{ {
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
float random = Misc::Rng::rollProbability(world->getPrng()); float random = Misc::Rng::rollProbability(world->getPrng());
if (random >= 2/3.f) if (random >= 2/3.f)
attackType = "thrust"; return "thrust";
else if (random >= 1/3.f) if (random >= 1/3.f)
attackType = "slash"; return "slash";
else return "chop";
attackType = "chop";
} }
bool CharacterController::readyToPrepareAttack() const bool CharacterController::readyToPrepareAttack() const
@ -2805,12 +2790,12 @@ float CharacterController::getAttackStrength() const
return mAttackStrength; return mAttackStrength;
} }
bool CharacterController::getAttackingOrSpell() bool CharacterController::getAttackingOrSpell() const
{ {
return mPtr.getClass().getCreatureStats(mPtr).getAttackingOrSpell(); return mPtr.getClass().getCreatureStats(mPtr).getAttackingOrSpell();
} }
void CharacterController::setActive(int active) void CharacterController::setActive(int active) const
{ {
mAnimation->setActive(active); mAnimation->setActive(active);
} }
@ -2820,7 +2805,7 @@ void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target)
mHeadTrackTarget = target; mHeadTrackTarget = target;
} }
void CharacterController::playSwishSound(float attackStrength) void CharacterController::playSwishSound(float attackStrength) const
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();

View file

@ -149,53 +149,53 @@ class CharacterController : public MWRender::Animation::TextKeyListener
typedef std::deque<AnimationQueueEntry> AnimationQueue; typedef std::deque<AnimationQueueEntry> AnimationQueue;
AnimationQueue mAnimQueue; AnimationQueue mAnimQueue;
CharacterState mIdleState; CharacterState mIdleState{CharState_None};
std::string mCurrentIdle; std::string mCurrentIdle;
CharacterState mMovementState; CharacterState mMovementState{CharState_None};
std::string mCurrentMovement; std::string mCurrentMovement;
float mMovementAnimSpeed; float mMovementAnimSpeed{0.f};
bool mAdjustMovementAnimSpeed; bool mAdjustMovementAnimSpeed{false};
bool mHasMovedInXY; bool mHasMovedInXY{false};
bool mMovementAnimationControlled; bool mMovementAnimationControlled{true};
CharacterState mDeathState; CharacterState mDeathState{CharState_None};
std::string mCurrentDeath; std::string mCurrentDeath;
bool mFloatToSurface; bool mFloatToSurface{true};
CharacterState mHitState; CharacterState mHitState{CharState_None};
std::string mCurrentHit; std::string mCurrentHit;
UpperBodyCharacterState mUpperBodyState; UpperBodyCharacterState mUpperBodyState{UpperCharState_Nothing};
JumpingState mJumpState; JumpingState mJumpState{JumpState_None};
std::string mCurrentJump; std::string mCurrentJump;
int mWeaponType; int mWeaponType{ESM::Weapon::None};
std::string mCurrentWeapon; std::string mCurrentWeapon;
float mAttackStrength; float mAttackStrength{0.f};
bool mSkipAnim; bool mSkipAnim{false};
// counted for skill increase // counted for skill increase
float mSecondsOfSwimming; float mSecondsOfSwimming{0.f};
float mSecondsOfRunning; float mSecondsOfRunning{0.f};
MWWorld::ConstPtr mHeadTrackTarget; MWWorld::ConstPtr mHeadTrackTarget;
float mTurnAnimationThreshold; // how long to continue playing turning animation after actor stopped turning float mTurnAnimationThreshold{0.f}; // how long to continue playing turning animation after actor stopped turning
std::string mAttackType; // slash, chop or thrust std::string mAttackType; // slash, chop or thrust
bool mCastingManualSpell; bool mCastingManualSpell{false};
float mTimeUntilWake; float mTimeUntilWake{0.f};
bool mIsMovingBackward; bool mIsMovingBackward{false};
osg::Vec2f mSmoothedSpeed; osg::Vec2f mSmoothedSpeed;
void setAttackTypeBasedOnMovement(); std::string getMovementBasedAttackType() const;
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
void refreshHitRecoilAnims(CharacterState& idle); void refreshHitRecoilAnims(CharacterState& idle);
@ -206,18 +206,18 @@ class CharacterController : public MWRender::Animation::TextKeyListener
void clearAnimQueue(bool clearPersistAnims = false); void clearAnimQueue(bool clearPersistAnims = false);
bool updateState(CharacterState idle); bool updateState(CharacterState idle);
void updateIdleStormState(bool inwater); void updateIdleStormState(bool inwater) const;
std::string chooseRandomAttackAnimation() const; std::string chooseRandomAttackAnimation() const;
bool isRandomAttackAnimation(const std::string& group) const; static bool isRandomAttackAnimation(std::string_view group);
bool isPersistentAnimPlaying(); bool isPersistentAnimPlaying() const;
void updateAnimQueue(); void updateAnimQueue();
void updateHeadTracking(float duration); void updateHeadTracking(float duration);
void updateMagicEffects(); void updateMagicEffects() const;
void playDeath(float startpoint, CharacterState death); void playDeath(float startpoint, CharacterState death);
CharacterState chooseRandomDeathState() const; CharacterState chooseRandomDeathState() const;
@ -229,11 +229,11 @@ class CharacterController : public MWRender::Animation::TextKeyListener
bool updateCarriedLeftVisible(int weaptype) const; bool updateCarriedLeftVisible(int weaptype) const;
std::string fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask = nullptr); std::string fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask = nullptr) const;
std::string getWeaponAnimation(int weaponType) const; std::string getWeaponAnimation(int weaponType) const;
bool getAttackingOrSpell(); bool getAttackingOrSpell() const;
void setAttackingOrSpell(bool attackingOrSpell); void setAttackingOrSpell(bool attackingOrSpell);
@ -246,24 +246,24 @@ public:
const MWWorld::Ptr& getPtr() const { return mPtr; } const MWWorld::Ptr& getPtr() const { return mPtr; }
void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map) override; void handleTextKey(std::string_view groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map) override;
// Be careful when to call this, see comment in Actors // Be careful when to call this, see comment in Actors
void updateContinuousVfx(); void updateContinuousVfx() const;
void updatePtr(const MWWorld::Ptr &ptr); void updatePtr(const MWWorld::Ptr &ptr);
void update(float duration); void update(float duration);
bool onOpen(); bool onOpen() const;
void onClose(); void onClose() const;
void persistAnimationState(); void persistAnimationState() const;
void unpersistAnimationState(); void unpersistAnimationState();
bool playGroup(const std::string &groupname, int mode, int count, bool persist=false); bool playGroup(const std::string &groupname, int mode, int count, bool persist=false);
void skipAnim(); void skipAnim();
bool isAnimPlaying(const std::string &groupName); bool isAnimPlaying(const std::string &groupName) const;
enum KillResult enum KillResult
{ {
@ -291,10 +291,10 @@ public:
bool isTurning() const; bool isTurning() const;
bool isAttackingOrSpell() const; bool isAttackingOrSpell() const;
void setVisibility(float visibility); void setVisibility(float visibility) const;
void castSpell(const std::string& spellId, bool manualSpell=false); void castSpell(const std::string& spellId, bool manualSpell=false);
void setAIAttackType(const std::string& attackType); void setAIAttackType(const std::string& attackType);
static void setAttackTypeRandomly(std::string& attackType); static std::string getRandomAttackType();
bool readyToPrepareAttack() const; bool readyToPrepareAttack() const;
bool readyToStartAttack() const; bool readyToStartAttack() const;
@ -302,12 +302,12 @@ public:
float getAttackStrength() const; float getAttackStrength() const;
/// @see Animation::setActive /// @see Animation::setActive
void setActive(int active); void setActive(int active) const;
/// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr. /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
void setHeadTrackTarget(const MWWorld::ConstPtr& target); void setHeadTrackTarget(const MWWorld::ConstPtr& target);
void playSwishSound(float attackStrength); void playSwishSound(float attackStrength) const;
}; };
} }

View file

@ -149,7 +149,7 @@ public:
class TextKeyListener class TextKeyListener
{ {
public: public:
virtual void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, virtual void handleTextKey(std::string_view groupname, SceneUtil::TextKeyMap::ConstIterator key,
const SceneUtil::TextKeyMap& map) = 0; const SceneUtil::TextKeyMap& map) = 0;
virtual ~TextKeyListener() = default; virtual ~TextKeyListener() = default;