mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-03 17:54:06 +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:
commit
415591b7ed
@ -5,6 +5,8 @@
|
||||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
||||
#include <components/misc/timer.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Animation;
|
||||
@ -41,12 +43,18 @@ namespace MWMechanics
|
||||
bool isTurningToPlayer() const;
|
||||
void setTurningToPlayer(bool turning);
|
||||
|
||||
Misc::TimerStatus updateEngageCombatTimer(float duration)
|
||||
{
|
||||
return mEngageCombat.update(duration);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<CharacterController> mCharacterController;
|
||||
int mGreetingTimer{0};
|
||||
float mTargetAngleRadians{0.f};
|
||||
GreetingState mGreetingState{Greet_None};
|
||||
bool mIsTurningToPlayer{false};
|
||||
Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1905,14 +1905,11 @@ namespace MWMechanics
|
||||
{
|
||||
if(!paused)
|
||||
{
|
||||
static float timerUpdateAITargets = 0;
|
||||
static float timerUpdateHeadTrack = 0;
|
||||
static float timerUpdateEquippedLight = 0;
|
||||
static float timerUpdateHello = 0;
|
||||
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 (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
|
||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||
@ -1965,6 +1962,8 @@ namespace MWMechanics
|
||||
|
||||
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
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
|
||||
{
|
||||
@ -1993,7 +1992,7 @@ namespace MWMechanics
|
||||
}
|
||||
if (aiActive && inProcessingRange)
|
||||
{
|
||||
if (timerUpdateAITargets == 0)
|
||||
if (engageCombatTimerStatus == Misc::TimerStatus::Elapsed)
|
||||
{
|
||||
if (!isPlayer)
|
||||
adjustCommandedActor(iter->first);
|
||||
@ -2080,7 +2079,6 @@ namespace MWMechanics
|
||||
if (avoidCollisions)
|
||||
predictAndAvoidCollisions();
|
||||
|
||||
timerUpdateAITargets += duration;
|
||||
timerUpdateHeadTrack += duration;
|
||||
timerUpdateEquippedLight += duration;
|
||||
timerUpdateHello += duration;
|
||||
|
@ -146,19 +146,10 @@ namespace MWMechanics
|
||||
}
|
||||
storage.mActionCooldown -= duration;
|
||||
|
||||
float& timerReact = storage.mTimerReact;
|
||||
if (timerReact < AI_REACTION_TIME)
|
||||
{
|
||||
timerReact += duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
timerReact = 0;
|
||||
if (attack(actor, target, storage, characterController))
|
||||
return true;
|
||||
}
|
||||
if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
return attack(actor, target, storage, characterController);
|
||||
}
|
||||
|
||||
bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "pathfinding.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "obstacle.hpp"
|
||||
#include "aitimer.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
@ -27,7 +28,7 @@ namespace MWMechanics
|
||||
struct AiCombatStorage : AiTemporaryBase
|
||||
{
|
||||
float mAttackCooldown;
|
||||
float mTimerReact;
|
||||
AiReactionTimer mReaction;
|
||||
float mTimerCombatMove;
|
||||
bool mReadyToAttack;
|
||||
bool mAttack;
|
||||
@ -60,7 +61,6 @@ namespace MWMechanics
|
||||
|
||||
AiCombatStorage():
|
||||
mAttackCooldown(0.0f),
|
||||
mTimerReact(AI_REACTION_TIME),
|
||||
mTimerCombatMove(0.0f),
|
||||
mReadyToAttack(false),
|
||||
mAttack(false),
|
||||
|
@ -28,7 +28,6 @@
|
||||
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
|
||||
mTypeId(typeId),
|
||||
mOptions(options),
|
||||
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
|
||||
mTargetActorRefId(""),
|
||||
mTargetActorId(-1),
|
||||
mRotateOnTheRunChecks(0),
|
||||
@ -64,7 +63,7 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
|
||||
void MWMechanics::AiPackage::reset()
|
||||
{
|
||||
// reset all members
|
||||
mTimer = AI_REACTION_TIME + 1.0f;
|
||||
mReaction.reset();
|
||||
mIsShortcutting = false;
|
||||
mShortcutProhibited = false;
|
||||
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)
|
||||
{
|
||||
mTimer += duration; //Update timer
|
||||
const Misc::TimerStatus timerStatus = mReaction.update(duration);
|
||||
|
||||
const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor
|
||||
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 actorCanMoveByZ = canActorMoveByZAxis(actor);
|
||||
|
||||
if (!isDestReached && mTimer > AI_REACTION_TIME)
|
||||
if (!isDestReached && timerStatus == Misc::TimerStatus::Elapsed)
|
||||
{
|
||||
if (actor.getClass().isBipedal(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
|
||||
}
|
||||
}
|
||||
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "obstacle.hpp"
|
||||
#include "aistate.hpp"
|
||||
#include "aipackagetypeid.hpp"
|
||||
#include "aitimer.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
@ -28,8 +29,6 @@ namespace ESM
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
const float AI_REACTION_TIME = 0.25f;
|
||||
|
||||
class CharacterController;
|
||||
class PathgridGraph;
|
||||
|
||||
@ -158,7 +157,7 @@ namespace MWMechanics
|
||||
PathFinder mPathFinder;
|
||||
ObstacleCheck mObstacleCheck;
|
||||
|
||||
float mTimer;
|
||||
AiReactionTimer mReaction;
|
||||
|
||||
std::string mTargetActorRefId;
|
||||
mutable int mTargetActorId;
|
||||
|
26
apps/openmw/mwmechanics/aitimer.hpp
Normal file
26
apps/openmw/mwmechanics/aitimer.hpp
Normal 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
|
@ -223,15 +223,10 @@ namespace MWMechanics
|
||||
|
||||
doPerFrameActionsForState(actor, duration, storage);
|
||||
|
||||
float& lastReaction = storage.mReaction;
|
||||
lastReaction += duration;
|
||||
if (AI_REACTION_TIME <= lastReaction)
|
||||
{
|
||||
lastReaction = 0;
|
||||
return reactionTimeActions(actor, storage, pos);
|
||||
}
|
||||
else
|
||||
if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)
|
||||
return false;
|
||||
|
||||
return reactionTimeActions(actor, storage, pos);
|
||||
}
|
||||
|
||||
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "pathfinding.hpp"
|
||||
#include "obstacle.hpp"
|
||||
#include "aistate.hpp"
|
||||
#include "aitimer.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
@ -25,7 +26,7 @@ namespace MWMechanics
|
||||
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
||||
struct AiWanderStorage : AiTemporaryBase
|
||||
{
|
||||
float mReaction; // update some actions infrequently
|
||||
AiReactionTimer mReaction;
|
||||
|
||||
// AiWander states
|
||||
enum WanderState
|
||||
@ -57,7 +58,6 @@ namespace MWMechanics
|
||||
int mStuckCount;
|
||||
|
||||
AiWanderStorage():
|
||||
mReaction(0),
|
||||
mState(Wander_ChooseAction),
|
||||
mIsWanderingManually(false),
|
||||
mCanWanderAlongPathGrid(true),
|
||||
|
@ -47,4 +47,9 @@ namespace Misc
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ public:
|
||||
|
||||
/// returns default seed for RNG
|
||||
static unsigned int generateDefaultSeed();
|
||||
|
||||
static float deviate(float mean, float deviation, Seed& seed = getSeed());
|
||||
};
|
||||
|
||||
}
|
||||
|
42
components/misc/timer.hpp
Normal file
42
components/misc/timer.hpp
Normal 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
|
Loading…
x
Reference in New Issue
Block a user