1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-29 17:15:34 +00:00

Filter evade directions by supported animations

To avoid trying those which will not lead to any actor movement due to absent
animation.
This commit is contained in:
elsid 2023-07-05 23:58:04 +02:00
parent 80ae8ce116
commit b4868c6094
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
19 changed files with 199 additions and 37 deletions

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -19,6 +19,8 @@
#include "character.hpp"
#include <array>
#include <components/esm/records.hpp>
#include <components/misc/mathutil.hpp>
#include <components/misc/resourcehelpers.hpp>
@ -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;
}
}

View file

@ -315,6 +315,8 @@ namespace MWMechanics
void setHeadTrackTarget(const MWWorld::ConstPtr& target);
void playSwishSound() const;
MWWorld::MovementDirectionFlags getSupportedMovementDirections() const;
};
}

View file

@ -1,6 +1,7 @@
#include "obstacle.hpp"
#include <array>
#include <span>
#include <components/detournavigator/agentbounds.hpp>
#include <components/esm3/loaddoor.hpp>
@ -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;
}
}

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_MECHANICS_OBSTACLE_H
#define OPENMW_MECHANICS_OBSTACLE_H
#include "apps/openmw/mwworld/movementdirection.hpp"
#include <osg/Vec3f>
#include <vector>
@ -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;

View file

@ -1,5 +1,6 @@
#include "animation.hpp"
#include <algorithm>
#include <iomanip>
#include <limits>
@ -1800,6 +1801,28 @@ namespace MWRender
mInsert->removeChild(mObjectRoot);
}
MWWorld::MovementDirectionFlags Animation::getSupportedMovementDirections(
std::span<const std::string_view> 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*)

View file

@ -1,6 +1,7 @@
#ifndef GAME_RENDER_ANIMATION_H
#define GAME_RENDER_ANIMATION_H
#include "../mwworld/movementdirection.hpp"
#include "../mwworld/ptr.hpp"
#include <components/misc/strings/algorithm.hpp>
@ -9,6 +10,7 @@
#include <components/sceneutil/textkeymap.hpp>
#include <components/sceneutil/util.hpp>
#include <span>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@ -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<const std::string_view> prefixes) const;
virtual bool useShieldAnimations() const { return false; }
virtual bool getWeaponsShown() const { return false; }
virtual void showWeapons(bool showWeapon) {}

View file

@ -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

View file

@ -44,6 +44,8 @@ namespace SceneUtil
bool hasGroupStart(std::string_view groupName) const { return mGroups.count(groupName) > 0; }
const std::set<std::string, std::less<>>& getGroups() const { return mGroups; }
private:
struct IsGroupStart
{