diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4469de85ec..9895bdf65d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -72,7 +72,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow - aiescort aiactivate repair enchanting pathfinding security spellsuccess spellcasting + aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 24dc569d8c..b6f8f8a5c7 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -119,6 +119,8 @@ namespace MWBase /// paused we may want to do it manually (after equipping permanent enchantment) virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; + virtual void toggleAI() = 0; + virtual bool isAIActive() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e1adfbec46..29381eb42e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -362,6 +362,9 @@ namespace MWBase virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; ///< get all items in active cells owned by this Npc + virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0; + ///< get Line of Sight (morrowind stupid implementation) + virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6d31a810aa..ecb62ecd85 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -22,6 +22,11 @@ #include "creaturestats.hpp" #include "movement.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include "aicombat.hpp" + namespace MWMechanics { void Actors::updateActor (const MWWorld::Ptr& ptr, float duration) @@ -34,8 +39,47 @@ namespace MWMechanics if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) { // AI - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - creatureStats.getAiSequence().execute (ptr); + if(MWBase::Environment::get().getMechanicsManager())//check MechanismsManager is already created + { + if(MWBase::Environment::get().getMechanicsManager()->isAIActive())//MWBase::Environment::get().getMechanicsManager()->isAIActive()) + { + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + if(ptr != MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(); + //engage combat or not? + if(ptr != MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && !creatureStats.isHostile()) + { + 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(1); + 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); + } + } + + creatureStats.getAiSequence().execute (ptr,duration); + } + } // fatigue restoration calculateRestoration(ptr, duration); diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index b94c8c2599..ee0dcf96e5 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -9,7 +9,7 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const { return new AiActivate(*this); } -bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor) +bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) { std::cout << "AiActivate completed.\n"; return true; diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 7f3d4016dc..f922e238c2 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -12,7 +12,7 @@ namespace MWMechanics public: AiActivate(const std::string &objectId); virtual AiActivate *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); + virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? virtual int getTypeId() const; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp new file mode 100644 index 0000000000..39a97df6c1 --- /dev/null +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -0,0 +1,148 @@ +#include "aicombat.hpp" + +#include "movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/timestamp.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include "creaturestats.hpp" +#include "npcstats.hpp" + +#include "OgreMath.h" + +namespace +{ + static float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } +} + +namespace MWMechanics +{ + + AiCombat::AiCombat(const std::string &targetId) + :mTargetId(targetId),mTimer(0),mTimer2(0) + { + } + + bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) + { + if(!MWWorld::Class::get(actor).getCreatureStats(actor).isHostile()) return true; + + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mTargetId, false); + + if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; + + if(actor.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::Class::get(actor). + MWWorld::Class::get(actor).setStance(actor, MWWorld::Class::Run,true); + MWMechanics::DrawState_ state = MWWorld::Class::get(actor).getNpcStats(actor).getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + MWWorld::Class::get(actor).getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + } + ESM::Position pos = actor.getRefData().getPosition(); + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + float xCell = 0; + float yCell = 0; + + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = target.getRefData().getPosition().pos[0]; + dest.mY = target.getRefData().getPosition().pos[1]; + dest.mZ = target.getRefData().getPosition().pos[2]; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mTimer2 = mTimer2 + duration; + + if(!mPathFinder.isPathConstructed()) + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + else + { + mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || + (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) + { + mTimer2 = 0; + mPathFinder = mPathFinder2; + } + } + + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + + float range = 100; + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + if((dest.mX - start.mX)*(dest.mX - start.mX)+(dest.mY - start.mY)*(dest.mY - start.mY)+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) + < range*range) + { + float directionX = dest.mX - start.mX; + float directionY = dest.mY - start.mY; + float directionResult = sqrt(directionX * directionX + directionY * directionY); + + zAngle = Ogre::Radian( acos(directionY / directionResult) * sgn(asin(directionX / directionResult)) ).valueDegrees(); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + + mPathFinder.clearPath(); + + if(mTimer == 0) + { + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + //mTimer = mTimer + duration; + } + if( mTimer > 1) + { + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + mTimer = 0; + } + else + { + mTimer = mTimer + duration; + } + + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(!MWWorld::Class::get(actor).getCreatureStats(actor).getAttackingOrSpell()); + } + return false; + } + + int AiCombat::getTypeId() const + { + return 5; + } + + unsigned int AiCombat::getPriority() const + { + return 1; + } + + AiCombat *MWMechanics::AiCombat::clone() const + { + return new AiCombat(*this); + } +} + diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp new file mode 100644 index 0000000000..fa71e261fc --- /dev/null +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -0,0 +1,36 @@ +#ifndef GAME_MWMECHANICS_AICOMBAT_H +#define GAME_MWMECHANICS_AICOMBAT_H + +#include "aipackage.hpp" + +#include "pathfinding.hpp" + +#include "movement.hpp" + +namespace MWMechanics +{ + class AiCombat : public AiPackage + { + public: + AiCombat(const std::string &targetId); + + virtual AiCombat *clone() const; + + virtual bool execute (const MWWorld::Ptr& actor,float duration); + ///< \return Package completed? + + virtual int getTypeId() const; + + virtual unsigned int getPriority() const; + + private: + std::string mTargetId; + + PathFinder mPathFinder; + PathFinder mPathFinder2; + float mTimer; + float mTimer2; + }; +} + +#endif \ No newline at end of file diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 556e0b1267..3615c8546e 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -71,7 +71,7 @@ namespace MWMechanics return new AiEscort(*this); } - bool AiEscort::execute (const MWWorld::Ptr& actor) + bool AiEscort::execute (const MWWorld::Ptr& actor,float duration) { // If AiEscort has ran for as long or longer then the duration specified // and the duration is not infinite, the package is complete. diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 3ae604035a..f3f6d2bd9b 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -18,7 +18,7 @@ namespace MWMechanics virtual AiEscort *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); + virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? virtual int getTypeId() const; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index dab9e02839..73bf9259af 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -15,7 +15,7 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const return new AiFollow(*this); } - bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor) + bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { std::cout << "AiFollow completed.\n"; return true; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 0b37b0a2d9..39df024e4b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -13,7 +13,7 @@ namespace MWMechanics AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); virtual AiFollow *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); + virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? virtual int getTypeId() const; diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index d286fbba89..5832198dad 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -17,11 +17,14 @@ namespace MWMechanics virtual AiPackage *clone() const = 0; - virtual bool execute (const MWWorld::Ptr& actor) = 0; + virtual bool execute (const MWWorld::Ptr& actor,float duration) = 0; ///< \return Package completed? virtual int getTypeId() const = 0; ///< 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate + + virtual unsigned int getPriority() const {return 0;} + ///< higher number is higher priority (0 beeing the lowest) }; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2f06b849a3..d5fb76eded 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -8,6 +8,14 @@ #include "aitravel.hpp" #include "aifollow.hpp" #include "aiactivate.hpp" +#include "aicombat.hpp" + +#include "../mwworld/class.hpp" +#include "creaturestats.hpp" +#include "npcstats.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" void MWMechanics::AiSequence::copy (const AiSequence& sequence) { @@ -52,17 +60,20 @@ bool MWMechanics::AiSequence::isPackageDone() const return mDone; } -void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor) +void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { - if (!mPackages.empty()) + if(actor != MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) { - if (mPackages.front()->execute (actor)) + if (!mPackages.empty()) { - mPackages.erase (mPackages.begin()); - mDone = true; + if (mPackages.front()->execute (actor,duration)) + { + mPackages.erase (mPackages.begin()); + mDone = true; + } + else + mDone = false; } - else - mDone = false; } } @@ -76,7 +87,14 @@ void MWMechanics::AiSequence::clear() void MWMechanics::AiSequence::stack (const AiPackage& package) { - mPackages.push_front (package.clone()); + for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); it++) + { + if(mPackages.front()->getPriority() <= package.getPriority()) + mPackages.insert(it,package.clone()); + } + + if(mPackages.empty()) + mPackages.push_front (package.clone()); } void MWMechanics::AiSequence::queue (const AiPackage& package) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 9f70daeb83..0976ef0995 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -18,6 +18,7 @@ namespace MWMechanics class AiSequence { std::list mPackages; + bool mDone; void copy (const AiSequence& sequence); @@ -33,17 +34,17 @@ namespace MWMechanics virtual ~AiSequence(); int getTypeId() const; - ///< -1: None, 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate + ///< -1: None, 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate, 5 Combat bool isPackageDone() const; ///< Has a package been completed during the last update? - void execute (const MWWorld::Ptr& actor); + void execute (const MWWorld::Ptr& actor,float duration); ///< Execute package. void clear(); ///< Remove all packages. - + void stack (const AiPackage& package); ///< Add \a package to the front of the sequence (suspends current package) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index d47a49c702..08d7586388 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -31,7 +31,7 @@ namespace MWMechanics return new AiTravel(*this); } - bool AiTravel::execute (const MWWorld::Ptr& actor) + bool AiTravel::execute (const MWWorld::Ptr& actor,float duration) { MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::Position pos = actor.getRefData().getPosition(); diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 6eb9af8cec..b479dfd431 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -13,7 +13,7 @@ namespace MWMechanics AiTravel(float x, float y, float z); virtual AiTravel *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); + virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? virtual int getTypeId() const; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 96a41883b9..ecc41489a5 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -63,7 +63,7 @@ namespace MWMechanics return new AiWander(*this); } - bool AiWander::execute (const MWWorld::Ptr& actor) + bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index c82ccc2155..48bc62c625 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -16,7 +16,7 @@ namespace MWMechanics AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); virtual AiPackage *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); + virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? virtual int getTypeId() const; ///< 0: Wander diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index e1a7ac1231..239ca0676d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -165,7 +165,7 @@ namespace MWMechanics MechanicsManager::MechanicsManager() : mUpdatePlayer (true), mClassSelected (false), - mRaceSelected (false) + mRaceSelected (false), mAI(true) { //buildPlayer no longer here, needs to be done explicitely after all subsystems are up and running } @@ -684,4 +684,13 @@ namespace MWMechanics mActors.updateMagicEffects(ptr); } + void MechanicsManager::toggleAI() + { + mAI = !mAI; + } + + bool MechanicsManager::isAIActive() + { + return mAI; + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 42656d5abc..59c96aad3a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -29,6 +29,7 @@ namespace MWMechanics bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; + bool mAI;///< is AI active? Objects mObjects; Actors mActors; @@ -89,7 +90,7 @@ namespace MWMechanics virtual int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. - + virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); void toLower(std::string npcFaction); @@ -105,6 +106,8 @@ namespace MWMechanics /// paused we may want to do it manually (after equipping permanent enchantment) virtual void updateMagicEffects (const MWWorld::Ptr& ptr); + virtual void toggleAI(); + virtual bool isAIActive(); }; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 8ef0edab83..ff266f9ae7 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -189,7 +189,7 @@ namespace MWMechanics // This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call // if otherwise). if(mPath.empty()) - return 0; + return 0.; const ESM::Pathgrid::Point &nextPoint = *mPath.begin(); float directionX = nextPoint.mX - x; @@ -199,6 +199,21 @@ namespace MWMechanics return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees(); } + bool PathFinder::checkWaypoint(float x, float y, float z) + { + if(mPath.empty()) + return true; + + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + if(distanceZCorrected(nextPoint, x, y, z) < 64) + { + mPath.pop_front(); + if(mPath.empty()) mIsPathConstructed = false; + return true; + } + return false; + } + bool PathFinder::checkPathCompleted(float x, float y, float z) { if(mPath.empty()) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 35e0fa9087..916df850b5 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -18,6 +18,8 @@ namespace MWMechanics bool checkPathCompleted(float x, float y, float z); ///< \Returns true if the last point of the path has been reached. + bool checkWaypoint(float x, float y, float z); + ///< \Returns true if a way point was reached float getZAngleToNext(float x, float y) const; bool isPathConstructed() const @@ -25,6 +27,16 @@ namespace MWMechanics return mIsPathConstructed; } + int getPathSize() const + { + return mPath.size(); + } + + std::list getPath() const + { + return mPath; + } + private: std::list mPath; bool mIsPathConstructed; diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index fac44c08f3..1cdbaa008d 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -17,11 +17,16 @@ #include "../mwmechanics/aitravel.hpp" #include "../mwmechanics/aiwander.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "interpretercontext.hpp" #include "ref.hpp" #include +#include "../mwbase/mechanicsmanager.hpp" + namespace MWScript { namespace Ai @@ -364,6 +369,39 @@ namespace MWScript } }; + template + class OpGetLineOfSight : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + + MWWorld::Ptr source = R()(runtime); + + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true); + bool value = false; + if(dest != MWWorld::Ptr() ) + { + value = MWBase::Environment::get().getWorld()->getLOS(source,dest); + } + runtime.push (value); + } + }; + + template + class OpToggleAI : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getMechanicsManager()->toggleAI(); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -389,6 +427,11 @@ namespace MWScript interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); interpreter.installSegment3 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); + interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight); + interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight); + interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); + interpreter.installSegment5 (Compiler::Ai::opcodeToggleAIExplicit, new OpToggleAI); + interpreter.installSegment5 (Compiler::Ai::opcodeSetHello, new OpSetAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeSetHelloExplicit, new OpSetAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeSetFight, new OpSetAiSetting(1)); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 7bbb336999..9e024f8723 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -354,5 +354,9 @@ op 0x200021e: ShowVarsExplicit op 0x200021f: ToggleGodMode op 0x2000220: DisableLevitation op 0x2000221: EnableLevitation +op 0x2000222: GetLineOfSight +op 0x2000223: GetLineOfSightExplicit +op 0x2000224: ToggleAI +op 0x2000225: ToggleAIExplicit -opcodes 0x2000222-0x3ffffff unused +opcodes 0x2000226-0x3ffffff unused diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7dec848ad3..a75f355529 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1832,6 +1832,21 @@ namespace MWWorld } } + bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) + { + Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); + float* pos1 = npc.getRefData().getPosition().pos; + Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); + float* pos2 = targetNpc.getRefData().getPosition().pos; + + btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z); + btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z); + + std::pair result = mPhysEngine->rayTest(from, to,false); + if(result.first == "") return true; + return false; + } + void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable) { OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle()); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 19890b8310..7ccea25029 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -441,6 +441,9 @@ namespace MWWorld virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out); ///< get all items in active cells owned by this Npc + virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc); + ///< get Line of Sight (morrowind stupid implementation) + virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 65f6e112a3..01792def95 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -59,10 +59,12 @@ namespace Compiler extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit); extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit); extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); + extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit); + extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index aca24e0d30..a885a373de 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -49,6 +49,10 @@ namespace Compiler const int opcodeGetFleeExplicit = 0x20001c4; const int opcodeGetAlarm = 0x20001c5; const int opcodeGetAlarmExplicit = 0x20001c6; + const int opcodeGetLineOfSight = 0x2000222; + const int opcodeGetLineOfSightExplicit = 0x2000223; + const int opcodeToggleAI = 0x2000224; + const int opcodeToggleAIExplicit = 0x2000225; } namespace Animation