mirror of
https://github.com/OpenMW/openmw.git
synced 2025-05-09 15:11:25 +00:00
Merge branch 'outofcharacter' into 'master'
Make CharacterController less miserable, round 1 See merge request OpenMW/openmw!1999
This commit is contained in:
commit
f8a6001e87
4 changed files with 81 additions and 96 deletions
|
@ -703,7 +703,7 @@ std::string chooseBestAttack(const ESM::Weapon* weapon)
|
|||
attackType = "chop";
|
||||
}
|
||||
else
|
||||
MWMechanics::CharacterController::setAttackTypeRandomly(attackType);
|
||||
attackType = MWMechanics::CharacterController::getRandomAttackType();
|
||||
|
||||
return attackType;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -406,7 +406,7 @@ bool CharacterController::onOpen()
|
|||
return true;
|
||||
}
|
||||
|
||||
void CharacterController::onClose()
|
||||
void CharacterController::onClose() const
|
||||
{
|
||||
if (mPtr.getType() == ESM::Container::sRecordId)
|
||||
{
|
||||
|
@ -445,7 +445,7 @@ std::string CharacterController::getWeaponAnimation(int weaponType) const
|
|||
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;
|
||||
if (!isRealWeapon)
|
||||
|
@ -561,7 +561,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
|
|||
std::string::size_type runpos = movementAnimName.find("run");
|
||||
if (runpos != std::string::npos)
|
||||
{
|
||||
movementAnimName.replace(runpos, runpos+3, "walk");
|
||||
movementAnimName.replace(runpos, 3, "walk");
|
||||
if (!mAnimation->hasAnimation(movementAnimName))
|
||||
movementAnimName.clear();
|
||||
}
|
||||
|
@ -842,28 +842,7 @@ std::string CharacterController::chooseRandomAttackAnimation() const
|
|||
|
||||
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
|
||||
: mPtr(ptr)
|
||||
, mWeapon(MWWorld::Ptr())
|
||||
, 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)
|
||||
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)
|
||||
{
|
||||
|
@ -963,7 +942,7 @@ void CharacterController::handleTextKey(const std::string &groupname, SceneUtil:
|
|||
auto& charClass = mPtr.getClass();
|
||||
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
|
||||
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::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.
|
||||
bool hasHitKey = false;
|
||||
while (hitKey != map.end())
|
||||
{
|
||||
if (hitKey->second == groupname + ": hit")
|
||||
if (hitKey->second == hitKeyName)
|
||||
{
|
||||
hasHitKey = true;
|
||||
break;
|
||||
}
|
||||
if (hitKey->second == groupname + ": stop")
|
||||
if (hitKey->second == stopKeyName)
|
||||
break;
|
||||
++hitKey;
|
||||
}
|
||||
|
@ -1093,7 +1074,7 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
|
|||
mPtr = ptr;
|
||||
}
|
||||
|
||||
void CharacterController::updateIdleStormState(bool inwater)
|
||||
void CharacterController::updateIdleStormState(bool inwater) const
|
||||
{
|
||||
if (!mAnimation->hasAnimation("idlestorm") || mUpperBodyState != UpperCharState_Nothing || inwater)
|
||||
{
|
||||
|
@ -1548,12 +1529,12 @@ bool CharacterController::updateState(CharacterState idle)
|
|||
else
|
||||
{
|
||||
// There is no "best attack" for Hand-to-Hand
|
||||
setAttackTypeRandomly(mAttackType);
|
||||
mAttackType = getRandomAttackType();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setAttackTypeBasedOnMovement();
|
||||
mAttackType = getMovementBasedAttackType();
|
||||
}
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
|
||||
void CharacterController::persistAnimationState()
|
||||
void CharacterController::persistAnimationState() const
|
||||
{
|
||||
ESM::AnimationState& state = mPtr.getRefData().getAnimationState();
|
||||
|
||||
|
@ -2518,18 +2499,18 @@ void CharacterController::skipAnim()
|
|||
mSkipAnim = true;
|
||||
}
|
||||
|
||||
bool CharacterController::isPersistentAnimPlaying()
|
||||
bool CharacterController::isPersistentAnimPlaying() const
|
||||
{
|
||||
if (!mAnimQueue.empty())
|
||||
{
|
||||
AnimationQueueEntry& first = mAnimQueue.front();
|
||||
const AnimationQueueEntry& first = mAnimQueue.front();
|
||||
return first.mPersist && isAnimPlaying(first.mGroup);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CharacterController::isAnimPlaying(const std::string &groupName)
|
||||
bool CharacterController::isAnimPlaying(const std::string &groupName) const
|
||||
{
|
||||
if(mAnimation == nullptr)
|
||||
return false;
|
||||
|
@ -2542,9 +2523,15 @@ void CharacterController::clearAnimQueue(bool clearPersistAnims)
|
|||
if ((!isPersistentAnimPlaying() || clearPersistAnims) && !mAnimQueue.empty())
|
||||
mAnimation->disable(mAnimQueue.front().mGroup);
|
||||
|
||||
if (clearPersistAnims)
|
||||
{
|
||||
mAnimQueue.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (AnimationQueue::iterator it = mAnimQueue.begin(); it != mAnimQueue.end();)
|
||||
{
|
||||
if (clearPersistAnims || !it->mPersist)
|
||||
if (!it->mPersist)
|
||||
it = mAnimQueue.erase(it);
|
||||
else
|
||||
++it;
|
||||
|
@ -2610,7 +2597,7 @@ void CharacterController::resurrect()
|
|||
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,
|
||||
// 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())
|
||||
return;
|
||||
|
@ -2643,7 +2630,7 @@ void CharacterController::updateMagicEffects()
|
|||
mAnimation->setVampire(vampire);
|
||||
}
|
||||
|
||||
void CharacterController::setVisibility(float visibility)
|
||||
void CharacterController::setVisibility(float visibility) const
|
||||
{
|
||||
// We should take actor's invisibility in account
|
||||
if (mPtr.getClass().isActor())
|
||||
|
@ -2669,18 +2656,17 @@ void CharacterController::setVisibility(float visibility)
|
|||
mAnimation->setAlpha(visibility);
|
||||
}
|
||||
|
||||
void CharacterController::setAttackTypeBasedOnMovement()
|
||||
std::string CharacterController::getMovementBasedAttackType() const
|
||||
{
|
||||
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
||||
if (std::abs(move[1]) > std::abs(move[0]) + 0.2f) // forward-backward
|
||||
mAttackType = "thrust";
|
||||
else if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway
|
||||
mAttackType = "slash";
|
||||
else
|
||||
mAttackType = "chop";
|
||||
return "thrust";
|
||||
if (std::abs(move[0]) > std::abs(move[1]) + 0.2f) // sideway
|
||||
return "slash";
|
||||
return "chop";
|
||||
}
|
||||
|
||||
bool CharacterController::isRandomAttackAnimation(const std::string& group) const
|
||||
bool CharacterController::isRandomAttackAnimation(std::string_view group)
|
||||
{
|
||||
return (group == "attack1" || group == "swimattack1" ||
|
||||
group == "attack2" || group == "swimattack2" ||
|
||||
|
@ -2774,16 +2760,15 @@ void CharacterController::setAIAttackType(const std::string& attackType)
|
|||
mAttackType = attackType;
|
||||
}
|
||||
|
||||
void CharacterController::setAttackTypeRandomly(std::string& attackType)
|
||||
std::string CharacterController::getRandomAttackType()
|
||||
{
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
float random = Misc::Rng::rollProbability(world->getPrng());
|
||||
if (random >= 2/3.f)
|
||||
attackType = "thrust";
|
||||
else if (random >= 1/3.f)
|
||||
attackType = "slash";
|
||||
else
|
||||
attackType = "chop";
|
||||
return "thrust";
|
||||
if (random >= 1/3.f)
|
||||
return "slash";
|
||||
return "chop";
|
||||
}
|
||||
|
||||
bool CharacterController::readyToPrepareAttack() const
|
||||
|
@ -2805,12 +2790,12 @@ float CharacterController::getAttackStrength() const
|
|||
return mAttackStrength;
|
||||
}
|
||||
|
||||
bool CharacterController::getAttackingOrSpell()
|
||||
bool CharacterController::getAttackingOrSpell() const
|
||||
{
|
||||
return mPtr.getClass().getCreatureStats(mPtr).getAttackingOrSpell();
|
||||
}
|
||||
|
||||
void CharacterController::setActive(int active)
|
||||
void CharacterController::setActive(int active) const
|
||||
{
|
||||
mAnimation->setActive(active);
|
||||
}
|
||||
|
@ -2820,7 +2805,7 @@ void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target)
|
|||
mHeadTrackTarget = target;
|
||||
}
|
||||
|
||||
void CharacterController::playSwishSound(float attackStrength)
|
||||
void CharacterController::playSwishSound(float attackStrength) const
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
|
||||
|
|
|
@ -149,53 +149,53 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
typedef std::deque<AnimationQueueEntry> AnimationQueue;
|
||||
AnimationQueue mAnimQueue;
|
||||
|
||||
CharacterState mIdleState;
|
||||
CharacterState mIdleState{CharState_None};
|
||||
std::string mCurrentIdle;
|
||||
|
||||
CharacterState mMovementState;
|
||||
CharacterState mMovementState{CharState_None};
|
||||
std::string mCurrentMovement;
|
||||
float mMovementAnimSpeed;
|
||||
bool mAdjustMovementAnimSpeed;
|
||||
bool mHasMovedInXY;
|
||||
bool mMovementAnimationControlled;
|
||||
float mMovementAnimSpeed{0.f};
|
||||
bool mAdjustMovementAnimSpeed{false};
|
||||
bool mHasMovedInXY{false};
|
||||
bool mMovementAnimationControlled{true};
|
||||
|
||||
CharacterState mDeathState;
|
||||
CharacterState mDeathState{CharState_None};
|
||||
std::string mCurrentDeath;
|
||||
bool mFloatToSurface;
|
||||
bool mFloatToSurface{true};
|
||||
|
||||
CharacterState mHitState;
|
||||
CharacterState mHitState{CharState_None};
|
||||
std::string mCurrentHit;
|
||||
|
||||
UpperBodyCharacterState mUpperBodyState;
|
||||
UpperBodyCharacterState mUpperBodyState{UpperCharState_Nothing};
|
||||
|
||||
JumpingState mJumpState;
|
||||
JumpingState mJumpState{JumpState_None};
|
||||
std::string mCurrentJump;
|
||||
|
||||
int mWeaponType;
|
||||
int mWeaponType{ESM::Weapon::None};
|
||||
std::string mCurrentWeapon;
|
||||
|
||||
float mAttackStrength;
|
||||
float mAttackStrength{0.f};
|
||||
|
||||
bool mSkipAnim;
|
||||
bool mSkipAnim{false};
|
||||
|
||||
// counted for skill increase
|
||||
float mSecondsOfSwimming;
|
||||
float mSecondsOfRunning;
|
||||
float mSecondsOfSwimming{0.f};
|
||||
float mSecondsOfRunning{0.f};
|
||||
|
||||
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
|
||||
|
||||
bool mCastingManualSpell;
|
||||
bool mCastingManualSpell{false};
|
||||
|
||||
float mTimeUntilWake;
|
||||
float mTimeUntilWake{0.f};
|
||||
|
||||
bool mIsMovingBackward;
|
||||
bool mIsMovingBackward{false};
|
||||
osg::Vec2f mSmoothedSpeed;
|
||||
|
||||
void setAttackTypeBasedOnMovement();
|
||||
std::string getMovementBasedAttackType() const;
|
||||
|
||||
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
|
||||
void refreshHitRecoilAnims(CharacterState& idle);
|
||||
|
@ -206,18 +206,18 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
void clearAnimQueue(bool clearPersistAnims = false);
|
||||
|
||||
bool updateState(CharacterState idle);
|
||||
void updateIdleStormState(bool inwater);
|
||||
void updateIdleStormState(bool inwater) 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 updateHeadTracking(float duration);
|
||||
|
||||
void updateMagicEffects();
|
||||
void updateMagicEffects() const;
|
||||
|
||||
void playDeath(float startpoint, CharacterState death);
|
||||
CharacterState chooseRandomDeathState() const;
|
||||
|
@ -229,11 +229,11 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
|
||||
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;
|
||||
|
||||
bool getAttackingOrSpell();
|
||||
bool getAttackingOrSpell() const;
|
||||
void setAttackingOrSpell(bool attackingOrSpell);
|
||||
|
||||
|
||||
|
@ -246,24 +246,24 @@ public:
|
|||
|
||||
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
|
||||
void updateContinuousVfx();
|
||||
void updateContinuousVfx() const;
|
||||
|
||||
void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
||||
void update(float duration);
|
||||
|
||||
bool onOpen();
|
||||
void onClose();
|
||||
bool onOpen() const;
|
||||
void onClose() const;
|
||||
|
||||
void persistAnimationState();
|
||||
void persistAnimationState() const;
|
||||
void unpersistAnimationState();
|
||||
|
||||
bool playGroup(const std::string &groupname, int mode, int count, bool persist=false);
|
||||
void skipAnim();
|
||||
bool isAnimPlaying(const std::string &groupName);
|
||||
bool isAnimPlaying(const std::string &groupName) const;
|
||||
|
||||
enum KillResult
|
||||
{
|
||||
|
@ -291,10 +291,10 @@ public:
|
|||
bool isTurning() const;
|
||||
bool isAttackingOrSpell() const;
|
||||
|
||||
void setVisibility(float visibility);
|
||||
void setVisibility(float visibility) const;
|
||||
void castSpell(const std::string& spellId, bool manualSpell=false);
|
||||
void setAIAttackType(const std::string& attackType);
|
||||
static void setAttackTypeRandomly(std::string& attackType);
|
||||
static std::string getRandomAttackType();
|
||||
|
||||
bool readyToPrepareAttack() const;
|
||||
bool readyToStartAttack() const;
|
||||
|
@ -302,12 +302,12 @@ public:
|
|||
float getAttackStrength() const;
|
||||
|
||||
/// @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.
|
||||
void setHeadTrackTarget(const MWWorld::ConstPtr& target);
|
||||
|
||||
void playSwishSound(float attackStrength);
|
||||
void playSwishSound(float attackStrength) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ public:
|
|||
class TextKeyListener
|
||||
{
|
||||
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;
|
||||
|
||||
virtual ~TextKeyListener() = default;
|
||||
|
|
Loading…
Reference in a new issue