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:
commit
415591b7ed
@ -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)};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
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);
|
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)
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
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