1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-01 09:15:36 +00:00

Implement awareness check function. Use this for combat AI and GetDetected instruction.

This commit is contained in:
scrawl 2014-01-07 00:51:09 +01:00
parent 4a3d148a48
commit 29c823b9d4
6 changed files with 121 additions and 51 deletions

View file

@ -88,6 +88,9 @@ namespace MWBase
virtual int countDeaths (const std::string& id) const = 0;
///< Return the number of deaths for actors with the given ID.
/// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check!
virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
enum PersuasionType
{
PT_Admire,

View file

@ -158,50 +158,49 @@ namespace MWMechanics
calculateDynamicStats (ptr);
calculateCreatureStatModifiers (ptr, duration);
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
// AI
if(MWBase::Environment::get().getMechanicsManager()->isAIActive())
{
// AI
if(MWBase::Environment::get().getMechanicsManager()->isAIActive())
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
//engage combat or not?
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
if(ptr != player && !creatureStats.isHostile())
{
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
//engage combat or not?
if(ptr != MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && !creatureStats.isHostile())
ESM::Position playerpos = player.getRefData().getPosition();
ESM::Position actorpos = ptr.getRefData().getPosition();
float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0])
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
float disp = 100; //creatures don't have disposition, so set it to 100 by default
if(ptr.getTypeName() == typeid(ESM::NPC).name())
{
ESM::Position playerpos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition();
ESM::Position actorpos = ptr.getRefData().getPosition();
float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0])
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
float disp = 100; //creatures don't have disposition, so set it to 100 by default
if(ptr.getTypeName() == typeid(ESM::NPC).name())
{
disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr);
}
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
if( ( (fight == 100 )
|| (fight >= 95 && d <= 3000)
|| (fight >= 90 && d <= 2000)
|| (fight >= 80 && d <= 1000)
|| (fight >= 80 && disp <= 40)
|| (fight >= 70 && disp <= 35 && d <= 1000)
|| (fight >= 60 && disp <= 30 && d <= 1000)
|| (fight >= 50 && disp == 0)
|| (fight >= 40 && disp <= 10 && d <= 500) )
&& LOS
)
{
creatureStats.getAiSequence().stack(AiCombat("player"));
creatureStats.setHostile(true);
}
disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr);
}
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player)
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr);
if( ( (fight == 100 )
|| (fight >= 95 && d <= 3000)
|| (fight >= 90 && d <= 2000)
|| (fight >= 80 && d <= 1000)
|| (fight >= 80 && disp <= 40)
|| (fight >= 70 && disp <= 35 && d <= 1000)
|| (fight >= 60 && disp <= 30 && d <= 1000)
|| (fight >= 50 && disp == 0)
|| (fight >= 40 && disp <= 10 && d <= 500) )
&& LOS
)
{
creatureStats.getAiSequence().stack(AiCombat("player"));
creatureStats.setHostile(true);
}
creatureStats.getAiSequence().execute (ptr,duration);
}
// fatigue restoration
calculateRestoration(ptr, duration);
creatureStats.getAiSequence().execute (ptr,duration);
}
// fatigue restoration
calculateRestoration(ptr, duration);
}
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)

View file

@ -12,6 +12,8 @@
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
#include <OgreSceneNode.h>
#include "spellcasting.hpp"
namespace MWMechanics
@ -725,4 +727,72 @@ namespace MWMechanics
{
return mAI;
}
bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)
{
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
float invisibility = stats.getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude;
if (invisibility > 0)
return false;
float sneakTerm = 0;
if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak))
{
static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->getFloat();
float sneak = 0;
if (ptr.getClass().isNpc())
sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified();
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0;
if (ptr.getClass().isNpc())
{
MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
MWWorld::ContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
if (it != inv.end())
bootWeight = it->getClass().getWeight(*it);
}
sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMult;
}
static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat();
static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat();
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
Ogre::Vector3 pos2 (observer.getRefData().getPosition().pos);
float distTerm = fSneakDistBase + fSneakDistMult * pos1.distance(pos2);
float chameleon = stats.getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude;
float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;
CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude;
int obsSneak = 0;
if (observer.getClass().isNpc())
obsSneak = observer.getClass().getNpcStats(observer).getSkill(ESM::Skill::Sneak).getModified();
float obsTerm = obsSneak + 0.2 * obsAgility + 0.1 * obsLuck - obsBlind;
// is ptr behind the observer?
static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat();
static float fSneakViewMult = store.find("fSneakViewMult")->getFloat();
float y = 0;
Ogre::Vector3 vec = pos1 - pos2;
Ogre::Radian angle = observer.getRefData().getBaseNode()->getOrientation().yAxis().angleBetween(vec);
if (angle < Ogre::Degree(90))
y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult;
else
y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult;
float target = x - y;
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
return (roll >= target);
}
}

View file

@ -98,6 +98,9 @@ namespace MWMechanics
void toLower(std::string npcFaction);
///< Perform a persuasion action on NPC
/// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check!
virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer);
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);

View file

@ -358,19 +358,21 @@ namespace MWScript
};
template<class R>
class OpGetDetected : public Interpreter::Opcode1
class OpGetDetected : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr observer = R()(runtime);
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Integer value = false; // TODO replace with implementation
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
std::cout << "AiGetDetected: " << actorID << ", " << value << std::endl;
Interpreter::Type_Integer value =
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
runtime.push (value);
}
@ -432,8 +434,8 @@ namespace MWScript
new OpGetAiPackageDone<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage<ExplicitRef>);
interpreter.installSegment3 (Compiler::Ai::opcodeGetDetected, new OpGetDetected<ImplicitRef>);
interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetDetected, new OpGetDetected<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI<ImplicitRef>);

View file

@ -1840,13 +1840,6 @@ namespace MWWorld
bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc)
{
// This is a placeholder! Needs to go into an NPC awareness check function (see
// https://wiki.openmw.org/index.php?title=Research:NPC_AI_Behaviour#NPC_Awareness_Check )
if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude)
return false;
if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude > 100)
return false;
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents();
float* pos1 = npc.getRefData().getPosition().pos;
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents();