From b4868c6094ebe69afd256443fa350d50490b6e0f Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 5 Jul 2023 23:58:04 +0200 Subject: [PATCH] Filter evade directions by supported animations To avoid trying those which will not lead to any actor movement due to absent animation. --- apps/openmw/mwmechanics/aicast.cpp | 5 +- apps/openmw/mwmechanics/aicombat.cpp | 11 ++-- apps/openmw/mwmechanics/aicombat.hpp | 4 +- apps/openmw/mwmechanics/aiescort.cpp | 3 +- apps/openmw/mwmechanics/aifollow.cpp | 5 +- apps/openmw/mwmechanics/aipackage.cpp | 5 +- apps/openmw/mwmechanics/aipackage.hpp | 3 +- apps/openmw/mwmechanics/aipursue.cpp | 4 +- apps/openmw/mwmechanics/aitravel.cpp | 3 +- apps/openmw/mwmechanics/aiwander.cpp | 16 +++-- apps/openmw/mwmechanics/aiwander.hpp | 6 +- apps/openmw/mwmechanics/character.cpp | 75 +++++++++++++++++++++++ apps/openmw/mwmechanics/character.hpp | 2 + apps/openmw/mwmechanics/obstacle.cpp | 42 +++++++++---- apps/openmw/mwmechanics/obstacle.hpp | 5 +- apps/openmw/mwrender/animation.cpp | 23 +++++++ apps/openmw/mwrender/animation.hpp | 5 ++ apps/openmw/mwworld/movementdirection.hpp | 17 +++++ components/sceneutil/textkeymap.hpp | 2 + 19 files changed, 199 insertions(+), 37 deletions(-) create mode 100644 apps/openmw/mwworld/movementdirection.hpp diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index 583a85c538..249ca97326 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -9,6 +9,7 @@ #include "../mwworld/class.hpp" #include "aicombataction.hpp" +#include "character.hpp" #include "steering.hpp" namespace MWMechanics @@ -48,7 +49,9 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac if (target.isEmpty()) return true; - if (!mManual && !pathTo(actor, target.getRefData().getPosition().asVec3(), duration, mDistance)) + if (!mManual + && !pathTo(actor, target.getRefData().getPosition().asVec3(), duration, + characterController.getSupportedMovementDirections(), mDistance)) { return false; } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index b146b45020..6daa8ea011 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -134,7 +134,8 @@ namespace MWMechanics const osg::Vec3f destination = storage.mUseCustomDestination ? storage.mCustomDestination : target.getRefData().getPosition().asVec3(); - const bool is_target_reached = pathTo(actor, destination, duration, targetReachedTolerance); + const bool is_target_reached = pathTo(actor, destination, duration, + characterController.getSupportedMovementDirections(), targetReachedTolerance); if (is_target_reached) storage.mReadyToAttack = true; } @@ -149,7 +150,7 @@ namespace MWMechanics } else { - updateFleeing(actor, target, duration, storage); + updateFleeing(actor, target, duration, characterController.getSupportedMovementDirections(), storage); } storage.mActionCooldown -= duration; @@ -342,8 +343,8 @@ namespace MWMechanics storage.mUpdateLOSTimer -= duration; } - void MWMechanics::AiCombat::updateFleeing( - const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) + void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, AiCombatStorage& storage) { static const float BLIND_RUN_DURATION = 1.0f; @@ -437,7 +438,7 @@ namespace MWMechanics float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); if ((dist > fFleeDistance && !storage.mLOS) - || pathTo(actor, PathFinder::makeOsgVec3(storage.mFleeDest), duration)) + || pathTo(actor, PathFinder::makeOsgVec3(storage.mFleeDest), duration, supportedMovementDirections)) { state = AiCombatStorage::FleeState_Idle; } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 0ae40e48e2..92d380dbd8 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -111,8 +111,8 @@ namespace MWMechanics void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage); - void updateFleeing( - const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage); + void updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, AiCombatStorage& storage); /// Transfer desired movement (from AiCombatStorage) to Actor void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 41cb5a59cb..e020e4a717 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -11,6 +11,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "character.hpp" #include "creaturestats.hpp" #include "movement.hpp" @@ -94,7 +95,7 @@ namespace MWMechanics if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist) { const osg::Vec3f dest(mX, mY, mZ); - if (pathTo(actor, dest, duration, maxHalfExtent)) // Returns true on path complete + if (pathTo(actor, dest, duration, characterController.getSupportedMovementDirections(), maxHalfExtent)) { mRemainingDuration = mDuration; return true; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 68162119d2..3f0e4e287c 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -11,6 +11,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "character.hpp" #include "creaturestats.hpp" #include "steering.hpp" @@ -202,7 +203,9 @@ namespace MWMechanics return false; } - storage.mMoving = !pathTo(actor, targetPos, duration, baseFollowDistance); // Go to the destination + // Go to the destination + storage.mMoving = !pathTo( + actor, targetPos, duration, characterController.getSupportedMovementDirections(), baseFollowDistance); if (storage.mMoving) { diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index f1c5d62a36..a733f6bef9 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -110,7 +110,8 @@ void MWMechanics::AiPackage::reset() } bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, - float destTolerance, float endTolerance, PathType pathType) + MWWorld::MovementDirectionFlags supportedMovementDirections, float destTolerance, float endTolerance, + PathType pathType) { const Misc::TimerStatus timerStatus = mReaction.update(duration); @@ -230,7 +231,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0); const auto destination = getNextPathPoint(dest); - mObstacleCheck.update(actor, destination, duration); + mObstacleCheck.update(actor, destination, duration, supportedMovementDirections); if (smoothMovement) { diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index c7be6f5dc7..fa018609e4 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -125,7 +125,8 @@ namespace MWMechanics protected: /// Handles path building and shortcutting with obstacles avoiding /** \return If the actor has arrived at his destination **/ - bool pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance = 0.0f, + bool pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, float destTolerance = 0.0f, float endTolerance = 0.0f, PathType pathType = PathType::Full); /// Check if there aren't any obstacles along the path to make shortcut possible diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 22b28d697d..2ae4ce5def 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -10,6 +10,7 @@ #include "../mwworld/class.hpp" #include "actorutil.hpp" +#include "character.hpp" #include "creaturestats.hpp" namespace MWMechanics @@ -55,7 +56,8 @@ namespace MWMechanics const float pathTolerance = 100.f; // check the true distance in case the target is far away in Z-direction - bool reached = pathTo(actor, dest, duration, pathTolerance, (actorPos - dest).length(), PathType::Partial) + bool reached = pathTo(actor, dest, duration, characterController.getSupportedMovementDirections(), + pathTolerance, (actorPos - dest).length(), PathType::Partial) && std::abs(dest.z() - actorPos.z()) < pathTolerance; if (reached) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 01187c98e7..f0781565bf 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -10,6 +10,7 @@ #include "../mwworld/class.hpp" +#include "character.hpp" #include "creaturestats.hpp" #include "movement.hpp" @@ -89,7 +90,7 @@ namespace MWMechanics if (!isWithinMaxRange(targetPos, actorPos)) return mHidden; - if (pathTo(actor, targetPos, duration)) + if (pathTo(actor, targetPos, duration, characterController.getSupportedMovementDirections())) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; return true; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 69f78616e7..f9e0ec1358 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -21,6 +21,7 @@ #include "../mwphysics/raycasting.hpp" #include "actorutil.hpp" +#include "character.hpp" #include "creaturestats.hpp" #include "movement.hpp" #include "pathgrid.hpp" @@ -190,7 +191,7 @@ namespace MWMechanics * will kick in. */ bool AiWander::execute( - const MWWorld::Ptr& actor, CharacterController& /*characterController*/, AiState& state, float duration) + const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); if (cStats.isDead() || cStats.getHealth().getCurrent() <= 0) @@ -244,7 +245,7 @@ namespace MWMechanics } } - doPerFrameActionsForState(actor, duration, storage); + doPerFrameActionsForState(actor, duration, characterController.getSupportedMovementDirections(), storage); if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting) return false; @@ -454,7 +455,8 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_IdleNow); } - void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) + void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage) { switch (storage.mState) { @@ -463,7 +465,7 @@ namespace MWMechanics break; case AiWanderStorage::Wander_Walking: - onWalkingStatePerFrameActions(actor, duration, storage); + onWalkingStatePerFrameActions(actor, duration, supportedMovementDirections, storage); break; case AiWanderStorage::Wander_ChooseAction: @@ -520,11 +522,13 @@ namespace MWMechanics return false; } - void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) + void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage) { // Is there no destination or are we there yet? if ((!mPathFinder.isPathConstructed()) - || pathTo(actor, osg::Vec3f(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE)) + || pathTo(actor, osg::Vec3f(mPathFinder.getPath().back()), duration, supportedMovementDirections, + DESTINATION_TOLERANCE)) { stopWalking(actor); storage.setState(AiWanderStorage::Wander_ChooseAction); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8b74ed72da..6d5bd7f8cd 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -123,9 +123,11 @@ namespace MWMechanics int getRandomIdle() const; void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage); - void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); + void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage); void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); - void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); + void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); inline bool isPackageCompleted() const; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 24d7285ad9..992c048c5a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -19,6 +19,8 @@ #include "character.hpp" +#include + #include #include #include @@ -2926,4 +2928,77 @@ namespace MWMechanics mAnimation->setHeadYaw(zAngleRadians); } + MWWorld::MovementDirectionFlags CharacterController::getSupportedMovementDirections() const + { + using namespace std::string_view_literals; + // There are fallbacks in the CharacterController::refreshMovementAnims for certain animations. Arrays below + // represent them. + constexpr std::array all = { ""sv }; + constexpr std::array walk = { "walk"sv }; + constexpr std::array swimWalk = { "swimwalk"sv, "walk"sv }; + constexpr std::array sneak = { "sneak"sv }; + constexpr std::array run = { "run"sv, "walk"sv }; + constexpr std::array swimRun = { "swimrun"sv, "run"sv, "walk"sv }; + constexpr std::array swim = { "swim"sv }; + switch (mMovementState) + { + case CharState_None: + case CharState_SpecialIdle: + case CharState_Idle: + case CharState_IdleSwim: + case CharState_IdleSneak: + return mAnimation->getSupportedMovementDirections(all); + case CharState_WalkForward: + case CharState_WalkBack: + case CharState_WalkLeft: + case CharState_WalkRight: + return mAnimation->getSupportedMovementDirections(walk); + case CharState_SwimWalkForward: + case CharState_SwimWalkBack: + case CharState_SwimWalkLeft: + case CharState_SwimWalkRight: + return mAnimation->getSupportedMovementDirections(swimWalk); + case CharState_RunForward: + case CharState_RunBack: + case CharState_RunLeft: + case CharState_RunRight: + return mAnimation->getSupportedMovementDirections(run); + case CharState_SwimRunForward: + case CharState_SwimRunBack: + case CharState_SwimRunLeft: + case CharState_SwimRunRight: + return mAnimation->getSupportedMovementDirections(swimRun); + case CharState_SneakForward: + case CharState_SneakBack: + case CharState_SneakLeft: + case CharState_SneakRight: + return mAnimation->getSupportedMovementDirections(sneak); + case CharState_TurnLeft: + case CharState_TurnRight: + return mAnimation->getSupportedMovementDirections(all); + case CharState_SwimTurnLeft: + case CharState_SwimTurnRight: + return mAnimation->getSupportedMovementDirections(swim); + case CharState_Death1: + case CharState_Death2: + case CharState_Death3: + case CharState_Death4: + case CharState_Death5: + case CharState_SwimDeath: + case CharState_SwimDeathKnockDown: + case CharState_SwimDeathKnockOut: + case CharState_DeathKnockDown: + case CharState_DeathKnockOut: + case CharState_Hit: + case CharState_SwimHit: + case CharState_KnockDown: + case CharState_KnockOut: + case CharState_SwimKnockDown: + case CharState_SwimKnockOut: + case CharState_Block: + return mAnimation->getSupportedMovementDirections(all); + } + return 0; + } + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8f79d845bc..2fc1b1de8e 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -315,6 +315,8 @@ namespace MWMechanics void setHeadTrackTarget(const MWWorld::ConstPtr& target); void playSwishSound() const; + + MWWorld::MovementDirectionFlags getSupportedMovementDirections() const; }; } diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 8f7829e634..4afaf7c2d5 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -1,6 +1,7 @@ #include "obstacle.hpp" #include +#include #include #include @@ -22,14 +23,21 @@ namespace MWMechanics constexpr float durationSameSpot = 1.5f; constexpr float durationToEvade = 1; - constexpr float evadeDirections[][2] = { - { 1.0f, 1.0f }, // move to side and forward - { 1.0f, 0.0f }, // move to side - { 1.0f, -1.0f }, // move to side and backwards - { 0.0f, -1.0f }, // move backwards - { -1.0f, -1.0f }, // move to other side and backwards - { -1.0f, 0.0f }, // move to other side - { -1.0f, 1.0f }, // move to other side and forward + struct EvadeDirection + { + float mMovementX; + float mMovementY; + MWWorld::MovementDirectionFlag mRequiredAnimation; + }; + + constexpr EvadeDirection evadeDirections[] = { + { 1.0f, 1.0f, MWWorld::MovementDirectionFlag_Forward }, // move to right and forward + { 1.0f, 0.0f, MWWorld::MovementDirectionFlag_Right }, // move to right + { 1.0f, -1.0f, MWWorld::MovementDirectionFlag_Back }, // move to right and backwards + { 0.0f, -1.0f, MWWorld::MovementDirectionFlag_Back }, // move backwards + { -1.0f, -1.0f, MWWorld::MovementDirectionFlag_Back }, // move to left and backwards + { -1.0f, 0.0f, MWWorld::MovementDirectionFlag_Left }, // move to left + { -1.0f, 1.0f, MWWorld::MovementDirectionFlag_Forward }, // move to left and forward }; } @@ -132,7 +140,8 @@ namespace MWMechanics * u = how long to move sideways * */ - void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration) + void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirection) { const auto position = actor.getRefData().getPosition().asVec3(); @@ -185,8 +194,15 @@ namespace MWMechanics mWalkState = WalkState::Evade; mStateDuration = 0; - if (++mEvadeDirectionIndex == std::size(evadeDirections)) - mEvadeDirectionIndex = 0; + std::size_t newEvadeDirectionIndex = mEvadeDirectionIndex; + do + { + ++newEvadeDirectionIndex; + if (newEvadeDirectionIndex == std::size(evadeDirections)) + newEvadeDirectionIndex = 0; + if ((evadeDirections[newEvadeDirectionIndex].mRequiredAnimation & supportedMovementDirection) != 0) + break; + } while (mEvadeDirectionIndex != newEvadeDirectionIndex); return; } @@ -202,7 +218,7 @@ namespace MWMechanics void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement) const { - actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0]; - actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1]; + actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex].mMovementX; + actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex].mMovementY; } } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 4ca36423ea..532bc91331 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_MECHANICS_OBSTACLE_H #define OPENMW_MECHANICS_OBSTACLE_H +#include "apps/openmw/mwworld/movementdirection.hpp" + #include #include @@ -36,7 +38,8 @@ namespace MWMechanics bool isEvading() const; // Updates internal state, call each frame for moving actor - void update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration); + void update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration, + MWWorld::MovementDirectionFlags supportedMovementDirection); // change direction to try to fix "stuck" actor void takeEvasiveAction(Movement& actorMovement) const; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d0669c3357..e10b260388 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1,5 +1,6 @@ #include "animation.hpp" +#include #include #include @@ -1800,6 +1801,28 @@ namespace MWRender mInsert->removeChild(mObjectRoot); } + MWWorld::MovementDirectionFlags Animation::getSupportedMovementDirections( + std::span prefixes) const + { + MWWorld::MovementDirectionFlags result = 0; + for (const std::string_view animation : mSupportedAnimations) + { + if (std::find_if( + prefixes.begin(), prefixes.end(), [&](std::string_view v) { return animation.starts_with(v); }) + == prefixes.end()) + continue; + if (animation.ends_with("forward")) + result |= MWWorld::MovementDirectionFlag_Forward; + else if (animation.ends_with("back")) + result |= MWWorld::MovementDirectionFlag_Back; + else if (animation.ends_with("left")) + result |= MWWorld::MovementDirectionFlag_Left; + else if (animation.ends_with("right")) + result |= MWWorld::MovementDirectionFlag_Right; + } + return result; + } + // ------------------------------------------------------ float Animation::AnimationTime::getValue(osg::NodeVisitor*) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 30acbe5dea..045f13a714 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,6 +1,7 @@ #ifndef GAME_RENDER_ANIMATION_H #define GAME_RENDER_ANIMATION_H +#include "../mwworld/movementdirection.hpp" #include "../mwworld/ptr.hpp" #include @@ -9,6 +10,7 @@ #include #include +#include #include #include #include @@ -472,6 +474,9 @@ namespace MWRender /// @note The matching is case-insensitive. const osg::Node* getNode(std::string_view name) const; + MWWorld::MovementDirectionFlags getSupportedMovementDirections( + std::span prefixes) const; + virtual bool useShieldAnimations() const { return false; } virtual bool getWeaponsShown() const { return false; } virtual void showWeapons(bool showWeapon) {} diff --git a/apps/openmw/mwworld/movementdirection.hpp b/apps/openmw/mwworld/movementdirection.hpp new file mode 100644 index 0000000000..6784db67dd --- /dev/null +++ b/apps/openmw/mwworld/movementdirection.hpp @@ -0,0 +1,17 @@ +#ifndef OPENMW_APPS_OPENMW_MWWORLD_MOVEMENTDIRECTION_H +#define OPENMW_APPS_OPENMW_MWWORLD_MOVEMENTDIRECTION_H + +namespace MWWorld +{ + using MovementDirectionFlags = unsigned char; + + enum MovementDirectionFlag : MovementDirectionFlags + { + MovementDirectionFlag_Forward = 1 << 0, + MovementDirectionFlag_Back = 1 << 1, + MovementDirectionFlag_Left = 1 << 2, + MovementDirectionFlag_Right = 1 << 3, + }; +} + +#endif diff --git a/components/sceneutil/textkeymap.hpp b/components/sceneutil/textkeymap.hpp index b507a5e550..a359c67d1b 100644 --- a/components/sceneutil/textkeymap.hpp +++ b/components/sceneutil/textkeymap.hpp @@ -44,6 +44,8 @@ namespace SceneUtil bool hasGroupStart(std::string_view groupName) const { return mGroups.count(groupName) > 0; } + const std::set>& getGroups() const { return mGroups; } + private: struct IsGroupStart {