1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-09 09:39:53 +00:00

Merge branch 'ai_reaction_deviation' into 'master'

Distribute AI reactions and engage combat calls over time

See merge request OpenMW/openmw!674
This commit is contained in:
psi29a 2021-03-22 21:37:09 +00:00
commit 415591b7ed
12 changed files with 101 additions and 38 deletions

View File

@ -5,6 +5,8 @@
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
#include <components/misc/timer.hpp>
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
@ -41,12 +43,18 @@ namespace MWMechanics
bool isTurningToPlayer() const; bool isTurningToPlayer() const;
void setTurningToPlayer(bool turning); void setTurningToPlayer(bool turning);
Misc::TimerStatus updateEngageCombatTimer(float duration)
{
return mEngageCombat.update(duration);
}
private: private:
std::unique_ptr<CharacterController> mCharacterController; std::unique_ptr<CharacterController> mCharacterController;
int mGreetingTimer{0}; int mGreetingTimer{0};
float mTargetAngleRadians{0.f}; float mTargetAngleRadians{0.f};
GreetingState mGreetingState{Greet_None}; GreetingState mGreetingState{Greet_None};
bool mIsTurningToPlayer{false}; bool mIsTurningToPlayer{false};
Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)};
}; };
} }

View File

@ -1905,14 +1905,11 @@ namespace MWMechanics
{ {
if(!paused) if(!paused)
{ {
static float timerUpdateAITargets = 0;
static float timerUpdateHeadTrack = 0; static float timerUpdateHeadTrack = 0;
static float timerUpdateEquippedLight = 0; static float timerUpdateEquippedLight = 0;
static float timerUpdateHello = 0; static float timerUpdateHello = 0;
const float updateEquippedLightInterval = 1.0f; const float updateEquippedLightInterval = 1.0f;
// target lists get updated once every 1.0 sec
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0; if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
if (timerUpdateHello >= 0.25f) timerUpdateHello = 0; if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0; if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
@ -1965,6 +1962,8 @@ namespace MWMechanics
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
const Misc::TimerStatus engageCombatTimerStatus = iter->second->updateEngageCombatTimer(duration);
// For dead actors we need to update looping spell particles // For dead actors we need to update looping spell particles
if (iter->first.getClass().getCreatureStats(iter->first).isDead()) if (iter->first.getClass().getCreatureStats(iter->first).isDead())
{ {
@ -1993,7 +1992,7 @@ namespace MWMechanics
} }
if (aiActive && inProcessingRange) if (aiActive && inProcessingRange)
{ {
if (timerUpdateAITargets == 0) if (engageCombatTimerStatus == Misc::TimerStatus::Elapsed)
{ {
if (!isPlayer) if (!isPlayer)
adjustCommandedActor(iter->first); adjustCommandedActor(iter->first);
@ -2080,7 +2079,6 @@ namespace MWMechanics
if (avoidCollisions) if (avoidCollisions)
predictAndAvoidCollisions(); predictAndAvoidCollisions();
timerUpdateAITargets += duration;
timerUpdateHeadTrack += duration; timerUpdateHeadTrack += duration;
timerUpdateEquippedLight += duration; timerUpdateEquippedLight += duration;
timerUpdateHello += duration; timerUpdateHello += duration;

View File

@ -146,19 +146,10 @@ namespace MWMechanics
} }
storage.mActionCooldown -= duration; storage.mActionCooldown -= duration;
float& timerReact = storage.mTimerReact; if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)
if (timerReact < AI_REACTION_TIME) return false;
{
timerReact += duration;
}
else
{
timerReact = 0;
if (attack(actor, target, storage, characterController))
return true;
}
return false; return attack(actor, target, storage, characterController);
} }
bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)

View File

@ -10,6 +10,7 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "obstacle.hpp" #include "obstacle.hpp"
#include "aitimer.hpp"
namespace ESM namespace ESM
{ {
@ -27,7 +28,7 @@ namespace MWMechanics
struct AiCombatStorage : AiTemporaryBase struct AiCombatStorage : AiTemporaryBase
{ {
float mAttackCooldown; float mAttackCooldown;
float mTimerReact; AiReactionTimer mReaction;
float mTimerCombatMove; float mTimerCombatMove;
bool mReadyToAttack; bool mReadyToAttack;
bool mAttack; bool mAttack;
@ -60,7 +61,6 @@ namespace MWMechanics
AiCombatStorage(): AiCombatStorage():
mAttackCooldown(0.0f), mAttackCooldown(0.0f),
mTimerReact(AI_REACTION_TIME),
mTimerCombatMove(0.0f), mTimerCombatMove(0.0f),
mReadyToAttack(false), mReadyToAttack(false),
mAttack(false), mAttack(false),

View File

@ -28,7 +28,6 @@
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
mTypeId(typeId), mTypeId(typeId),
mOptions(options), mOptions(options),
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
mTargetActorRefId(""), mTargetActorRefId(""),
mTargetActorId(-1), mTargetActorId(-1),
mRotateOnTheRunChecks(0), mRotateOnTheRunChecks(0),
@ -64,7 +63,7 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
void MWMechanics::AiPackage::reset() void MWMechanics::AiPackage::reset()
{ {
// reset all members // reset all members
mTimer = AI_REACTION_TIME + 1.0f; mReaction.reset();
mIsShortcutting = false; mIsShortcutting = false;
mShortcutProhibited = false; mShortcutProhibited = false;
mShortcutFailPos = osg::Vec3f(); mShortcutFailPos = osg::Vec3f();
@ -75,7 +74,7 @@ void MWMechanics::AiPackage::reset()
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance) bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance)
{ {
mTimer += duration; //Update timer const Misc::TimerStatus timerStatus = mReaction.update(duration);
const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
@ -98,7 +97,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
const bool isDestReached = (distToTarget <= destTolerance); const bool isDestReached = (distToTarget <= destTolerance);
const bool actorCanMoveByZ = canActorMoveByZAxis(actor); const bool actorCanMoveByZ = canActorMoveByZAxis(actor);
if (!isDestReached && mTimer > AI_REACTION_TIME) if (!isDestReached && timerStatus == Misc::TimerStatus::Elapsed)
{ {
if (actor.getClass().isBipedal(actor)) if (actor.getClass().isBipedal(actor))
openDoors(actor); openDoors(actor);
@ -142,8 +141,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
} }
} }
mTimer = 0;
} }
const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration

View File

@ -10,6 +10,7 @@
#include "obstacle.hpp" #include "obstacle.hpp"
#include "aistate.hpp" #include "aistate.hpp"
#include "aipackagetypeid.hpp" #include "aipackagetypeid.hpp"
#include "aitimer.hpp"
namespace MWWorld namespace MWWorld
{ {
@ -28,8 +29,6 @@ namespace ESM
namespace MWMechanics namespace MWMechanics
{ {
const float AI_REACTION_TIME = 0.25f;
class CharacterController; class CharacterController;
class PathgridGraph; class PathgridGraph;
@ -158,7 +157,7 @@ namespace MWMechanics
PathFinder mPathFinder; PathFinder mPathFinder;
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;
float mTimer; AiReactionTimer mReaction;
std::string mTargetActorRefId; std::string mTargetActorRefId;
mutable int mTargetActorId; mutable int mTargetActorId;

View File

@ -0,0 +1,26 @@
#ifndef OPENMW_MECHANICS_AITIMER_H
#define OPENMW_MECHANICS_AITIMER_H
#include <components/misc/rng.hpp>
#include <components/misc/timer.hpp>
namespace MWMechanics
{
constexpr float AI_REACTION_TIME = 0.25f;
class AiReactionTimer
{
public:
static constexpr float sDeviation = 0.1f;
Misc::TimerStatus update(float duration) { return mImpl.update(duration); }
void reset() { mImpl.reset(Misc::Rng::deviate(0, sDeviation)); }
private:
Misc::DeviatingPeriodicTimer mImpl {AI_REACTION_TIME, sDeviation,
Misc::Rng::deviate(0, sDeviation)};
};
}
#endif

View File

@ -223,15 +223,10 @@ namespace MWMechanics
doPerFrameActionsForState(actor, duration, storage); doPerFrameActionsForState(actor, duration, storage);
float& lastReaction = storage.mReaction; if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)
lastReaction += duration;
if (AI_REACTION_TIME <= lastReaction)
{
lastReaction = 0;
return reactionTimeActions(actor, storage, pos);
}
else
return false; return false;
return reactionTimeActions(actor, storage, pos);
} }
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos) bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)

View File

@ -10,6 +10,7 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include "obstacle.hpp" #include "obstacle.hpp"
#include "aistate.hpp" #include "aistate.hpp"
#include "aitimer.hpp"
namespace ESM namespace ESM
{ {
@ -25,7 +26,7 @@ namespace MWMechanics
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive. /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
struct AiWanderStorage : AiTemporaryBase struct AiWanderStorage : AiTemporaryBase
{ {
float mReaction; // update some actions infrequently AiReactionTimer mReaction;
// AiWander states // AiWander states
enum WanderState enum WanderState
@ -57,7 +58,6 @@ namespace MWMechanics
int mStuckCount; int mStuckCount;
AiWanderStorage(): AiWanderStorage():
mReaction(0),
mState(Wander_ChooseAction), mState(Wander_ChooseAction),
mIsWanderingManually(false), mIsWanderingManually(false),
mCanWanderAlongPathGrid(true), mCanWanderAlongPathGrid(true),

View File

@ -47,4 +47,9 @@ namespace Misc
{ {
return static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count()); return static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
} }
float Rng::deviate(float mean, float deviation, Seed& seed)
{
return std::uniform_real_distribution<float>(mean - deviation, mean + deviation)(seed.mGenerator);
}
} }

View File

@ -42,6 +42,8 @@ public:
/// returns default seed for RNG /// returns default seed for RNG
static unsigned int generateDefaultSeed(); static unsigned int generateDefaultSeed();
static float deviate(float mean, float deviation, Seed& seed = getSeed());
}; };
} }

42
components/misc/timer.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef OPENMW_COMPONENTS_MISC_TIMER_H
#define OPENMW_COMPONENTS_MISC_TIMER_H
#include "rng.hpp"
namespace Misc
{
enum class TimerStatus
{
Waiting,
Elapsed,
};
class DeviatingPeriodicTimer
{
public:
explicit DeviatingPeriodicTimer(float period, float deviation, float timeLeft)
: mPeriod(period), mDeviation(deviation), mTimeLeft(timeLeft)
{}
TimerStatus update(float duration)
{
if (mTimeLeft > 0)
{
mTimeLeft -= duration;
return TimerStatus::Waiting;
}
mTimeLeft = Rng::deviate(mPeriod, mDeviation);
return TimerStatus::Elapsed;
}
void reset(float timeLeft) { mTimeLeft = timeLeft; }
private:
const float mPeriod;
const float mDeviation;
float mTimeLeft;
};
}
#endif