mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-26 14:56:39 +00:00 
			
		
		
		
	Implement awareness check function. Use this for combat AI and GetDetected instruction.
This commit is contained in:
		
							parent
							
								
									4a3d148a48
								
							
						
					
					
						commit
						29c823b9d4
					
				
					 6 changed files with 121 additions and 51 deletions
				
			
		|  | @ -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, | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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>); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue