mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 20:26:43 +00:00 
			
		
		
		
	Merge remote-tracking branch 'torben/pathfindingoverhaul'
This commit is contained in:
		
						commit
						f586eef604
					
				
					 6 changed files with 658 additions and 631 deletions
				
			
		|  | @ -13,8 +13,9 @@ namespace | |||
| { | ||||
|     float sgn(float a) | ||||
|     { | ||||
|         if(a > 0) return 1.0; | ||||
|         else return -1.0; | ||||
|         if(a > 0) | ||||
|             return 1.0; | ||||
|         return -1.0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -24,151 +25,156 @@ namespace | |||
|     TODO: Take account for actors being in different cells. | ||||
| */ | ||||
| 
 | ||||
| MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) | ||||
| : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) | ||||
| namespace MWMechanics | ||||
| { | ||||
|     mMaxDist = 470; | ||||
| 
 | ||||
|     // The CS Help File states that if a duration is givin, the AI package will run for that long
 | ||||
|     // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
 | ||||
|     if(mX != 0 || mY != 0 || mZ != 0) | ||||
|         mDuration = 0; | ||||
| 
 | ||||
|     else | ||||
|     AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) | ||||
|     : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) | ||||
|     { | ||||
|         MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|         mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) | ||||
| : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) | ||||
| { | ||||
|     mMaxDist = 470; | ||||
| 
 | ||||
|     // The CS Help File states that if a duration is givin, the AI package will run for that long
 | ||||
|     // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
 | ||||
|     if(mX != 0 || mY != 0 || mZ != 0) | ||||
|         mDuration = 0; | ||||
| 
 | ||||
|     else | ||||
|     { | ||||
|         MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|         mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const | ||||
| { | ||||
|     return new AiEscort(*this); | ||||
| } | ||||
| 
 | ||||
| bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) | ||||
| { | ||||
|     // If AiEscort has ran for as long or longer then the duration specified
 | ||||
|     // and the duration is not infinite, the package is complete.
 | ||||
|     if(mDuration != 0) | ||||
|     { | ||||
|         MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|         unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); | ||||
|         if(currentSecond - mStartingSecond >= mDuration) | ||||
|             return true; | ||||
|     } | ||||
| 
 | ||||
|     ESM::Position pos = actor.getRefData().getPosition(); | ||||
|     bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; | ||||
|     MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); | ||||
|     const ESM::Pathgrid *pathgrid = | ||||
|         MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); | ||||
| 
 | ||||
| 
 | ||||
|     if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) | ||||
|     { | ||||
|         int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); | ||||
|         // Check if actor is near the border of an inactive cell. If so, disable AiEscort.
 | ||||
|         // FIXME: This *should* pause the AiEscort package instead of terminating it.
 | ||||
|         if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2. - 200))  | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) | ||||
|     { | ||||
|         int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); | ||||
|         // Check if actor is near the border of an inactive cell. If so, disable AiEscort.
 | ||||
|         // FIXME: This *should* pause the AiEscort package instead of terminating it.
 | ||||
|         if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2. - 200))  | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     if(!mPathFinder.isPathConstructed() || cellChange) | ||||
|     { | ||||
|         cellX = actor.getCell()->mCell->mData.mX; | ||||
|         cellY = actor.getCell()->mCell->mData.mY; | ||||
|         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 = mX; | ||||
|         dest.mY = mY; | ||||
|         dest.mZ = mZ; | ||||
| 
 | ||||
|         ESM::Pathgrid::Point start; | ||||
|         start.mX = pos.pos[0]; | ||||
|         start.mY = pos.pos[1]; | ||||
|         start.mZ = pos.pos[2]; | ||||
| 
 | ||||
|         mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); | ||||
|     } | ||||
| 
 | ||||
|     if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) | ||||
|     { | ||||
|         MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); | ||||
|     const float* const leaderPos = actor.getRefData().getPosition().pos; | ||||
|     const float* const followerPos = follower.getRefData().getPosition().pos; | ||||
|     double differenceBetween[3]; | ||||
| 
 | ||||
|     for (short i = 0; i < 3; ++i) | ||||
|         differenceBetween[i] = (leaderPos[i] - followerPos[i]); | ||||
| 
 | ||||
|     float distanceBetweenResult = | ||||
|         (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); | ||||
| 
 | ||||
|     if(distanceBetweenResult <= mMaxDist * mMaxDist) | ||||
|     { | ||||
|         float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); | ||||
|         MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); | ||||
|         MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; | ||||
|         mMaxDist = 470; | ||||
| 
 | ||||
|         // The CS Help File states that if a duration is givin, the AI package will run for that long
 | ||||
|         // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
 | ||||
|         if(mX != 0 || mY != 0 || mZ != 0) | ||||
|             mDuration = 0; | ||||
| 
 | ||||
|         else | ||||
|         { | ||||
|             MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|             mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
| 
 | ||||
|     AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) | ||||
|     : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) | ||||
|     { | ||||
|         // Stop moving if the player is to far away
 | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); | ||||
|         MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|         mMaxDist = 330; | ||||
|         mMaxDist = 470; | ||||
| 
 | ||||
|         // The CS Help File states that if a duration is givin, the AI package will run for that long
 | ||||
|         // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
 | ||||
|         if(mX != 0 || mY != 0 || mZ != 0) | ||||
|             mDuration = 0; | ||||
| 
 | ||||
|         else | ||||
|         { | ||||
|             MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|             mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| int MWMechanics::AiEscort::getTypeId() const | ||||
| { | ||||
|     return 2; | ||||
| 
 | ||||
|     AiEscort *MWMechanics::AiEscort::clone() const | ||||
|     { | ||||
|         return new AiEscort(*this); | ||||
|     } | ||||
| 
 | ||||
|     bool AiEscort::execute (const MWWorld::Ptr& actor) | ||||
|     { | ||||
|         // If AiEscort has ran for as long or longer then the duration specified
 | ||||
|         // and the duration is not infinite, the package is complete.
 | ||||
|         if(mDuration != 0) | ||||
|         { | ||||
|             MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|             unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); | ||||
|             if(currentSecond - mStartingSecond >= mDuration) | ||||
|                 return true; | ||||
|         } | ||||
| 
 | ||||
|         MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); | ||||
|         ESM::Position pos = actor.getRefData().getPosition(); | ||||
|         bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; | ||||
|         const ESM::Pathgrid *pathgrid = | ||||
|             MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); | ||||
| 
 | ||||
|         if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) | ||||
|         { | ||||
|             int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); | ||||
|             // Check if actor is near the border of an inactive cell. If so, disable AiEscort.
 | ||||
|             // FIXME: This *should* pause the AiEscort package instead of terminating it.
 | ||||
|             if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / | ||||
|                 2.0 - 200))  | ||||
|             { | ||||
|                 MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) | ||||
|         { | ||||
|             int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); | ||||
|             // Check if actor is near the border of an inactive cell. If so, disable AiEscort.
 | ||||
|             // FIXME: This *should* pause the AiEscort package instead of terminating it.
 | ||||
|             if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / | ||||
|                 2.0 - 200))  | ||||
|             { | ||||
|                 MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if(!mPathFinder.isPathConstructed() || cellChange) | ||||
|         { | ||||
|             cellX = actor.getCell()->mCell->mData.mX; | ||||
|             cellY = actor.getCell()->mCell->mData.mY; | ||||
|             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 = mX; | ||||
|             dest.mY = mY; | ||||
|             dest.mZ = mZ; | ||||
| 
 | ||||
|             ESM::Pathgrid::Point start; | ||||
|             start.mX = pos.pos[0]; | ||||
|             start.mY = pos.pos[1]; | ||||
|             start.mZ = pos.pos[2]; | ||||
| 
 | ||||
|             mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); | ||||
|         } | ||||
| 
 | ||||
|         if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); | ||||
|         const float* const leaderPos = actor.getRefData().getPosition().pos; | ||||
|         const float* const followerPos = follower.getRefData().getPosition().pos; | ||||
|         double differenceBetween[3]; | ||||
| 
 | ||||
|         for (short counter = 0; counter < 3; counter++) | ||||
|             differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]); | ||||
| 
 | ||||
|         float distanceBetweenResult = | ||||
|             (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * | ||||
|                 differenceBetween[2]); | ||||
| 
 | ||||
|         if(distanceBetweenResult <= mMaxDist * mMaxDist) | ||||
|         { | ||||
|             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; | ||||
|             mMaxDist = 470; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Stop moving if the player is to far away
 | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             mMaxDist = 330; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int AiEscort::getTypeId() const | ||||
|     { | ||||
|         return 2; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,99 +2,106 @@ | |||
| 
 | ||||
| #include "movement.hpp" | ||||
| 
 | ||||
| #include "../mwworld/class.hpp" | ||||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
| #include "../mwworld/class.hpp" | ||||
| #include "../mwworld/player.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|     float sgn(float a) | ||||
|     { | ||||
|         if(a > 0) return 1.; | ||||
|         else return -1.; | ||||
|         if(a > 0) | ||||
|             return 1.0; | ||||
|         return -1.0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MWMechanics::AiTravel::AiTravel(float x, float y, float z) | ||||
| : mX(x),mY(y),mZ(z),mPathFinder() | ||||
| namespace MWMechanics | ||||
| { | ||||
| } | ||||
|     AiTravel::AiTravel(float x, float y, float z) | ||||
|     : mX(x),mY(y),mZ(z),mPathFinder() | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
| MWMechanics::AiTravel *MWMechanics::AiTravel::clone() const | ||||
| { | ||||
|     return new AiTravel(*this); | ||||
| } | ||||
|     AiTravel *MWMechanics::AiTravel::clone() const | ||||
|     { | ||||
|         return new AiTravel(*this); | ||||
|     } | ||||
| 
 | ||||
| bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) | ||||
| { | ||||
|     const ESM::Pathgrid *pathgrid = | ||||
|         MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); | ||||
| 
 | ||||
|     ESM::Position pos = actor.getRefData().getPosition(); | ||||
|     bool AiTravel::execute (const MWWorld::Ptr& actor) | ||||
|     { | ||||
|         MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); | ||||
|         ESM::Position pos = actor.getRefData().getPosition(); | ||||
|         bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; | ||||
|         const ESM::Pathgrid *pathgrid = | ||||
|             MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); | ||||
| 
 | ||||
|     MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); | ||||
|     if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) | ||||
|     { | ||||
|         int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); | ||||
|         //check if actor is near the border of an inactive cell. If so, disable aitravel.
 | ||||
|         if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200))  | ||||
|         if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) | ||||
|         { | ||||
|             int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); | ||||
|             //check if actor is near the border of an inactive cell. If so, disable aitravel.
 | ||||
|             if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / | ||||
|                 2.0 - 200))  | ||||
|             { | ||||
|                 MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) | ||||
|         { | ||||
|             int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); | ||||
|             //check if actor is near the border of an inactive cell. If so, disable aitravel.
 | ||||
|             if(sideY * (pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / | ||||
|                 2.0 - 200))  | ||||
|             { | ||||
|                 MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(!mPathFinder.isPathConstructed() || cellChange) | ||||
|         { | ||||
|             cellX = actor.getCell()->mCell->mData.mX; | ||||
|             cellY = actor.getCell()->mCell->mData.mY; | ||||
|             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 = mX; | ||||
|             dest.mY = mY; | ||||
|             dest.mZ = mZ; | ||||
| 
 | ||||
|             ESM::Pathgrid::Point start; | ||||
|             start.mX = pos.pos[0]; | ||||
|             start.mY = pos.pos[1]; | ||||
|             start.mZ = pos.pos[2]; | ||||
| 
 | ||||
|             mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); | ||||
|         } | ||||
| 
 | ||||
|         if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         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; | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
|     if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) | ||||
| 
 | ||||
|     int AiTravel::getTypeId() const | ||||
|     { | ||||
|         int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); | ||||
|         //check if actor is near the border of an inactive cell. If so, disable aitravel.
 | ||||
|         if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200))  | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     if(!mPathFinder.isPathConstructed() ||cellChange) | ||||
|     { | ||||
|         cellX = actor.getCell()->mCell->mData.mX; | ||||
|         cellY = actor.getCell()->mCell->mData.mY; | ||||
|         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 = mX; | ||||
|         dest.mY = mY; | ||||
|         dest.mZ = mZ; | ||||
| 
 | ||||
|         ESM::Pathgrid::Point start; | ||||
|         start.mX = pos.pos[0]; | ||||
|         start.mY = pos.pos[1]; | ||||
|         start.mZ = pos.pos[2]; | ||||
| 
 | ||||
|         mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); | ||||
|     } | ||||
| 
 | ||||
|     if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) | ||||
|     { | ||||
|         MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); | ||||
|     MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); | ||||
|     MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| int MWMechanics::AiTravel::getTypeId() const | ||||
| { | ||||
|     return 1; | ||||
| } | ||||
|  |  | |||
|  | @ -14,310 +14,324 @@ namespace | |||
| { | ||||
|     float sgn(float a) | ||||
|     { | ||||
|         if(a > 0) return 1.0; | ||||
|         else return -1.0; | ||||
|         if(a > 0) | ||||
|             return 1.0; | ||||
|         return -1.0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): | ||||
|     mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) | ||||
| namespace MWMechanics | ||||
| { | ||||
|     for(unsigned short counter = 0; counter < mIdle.size(); counter++) | ||||
|     AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): | ||||
|         mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) | ||||
|     { | ||||
|         if(mIdle[counter] >= 127 || mIdle[counter] < 0) | ||||
|             mIdle[counter] = 0; | ||||
|     } | ||||
| 
 | ||||
|     if(mDistance < 0) | ||||
|         mDistance = 0; | ||||
|     if(mDuration < 0) | ||||
|         mDuration = 0; | ||||
|     if(mDuration == 0) | ||||
|         mTimeOfDay = 0; | ||||
| 
 | ||||
|     srand(time(NULL)); | ||||
|     mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|     mPlayedIdle = 0; | ||||
|     mPathgrid = NULL; | ||||
|     mIdleChanceMultiplier = | ||||
|         MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat(); | ||||
| 
 | ||||
|     mStoredAvailableNodes = false; | ||||
|     mChooseAction = true; | ||||
|     mIdleNow = false; | ||||
|     mMoveNow = false; | ||||
|     mWalking = false; | ||||
| } | ||||
| 
 | ||||
| MWMechanics::AiPackage * MWMechanics::AiWander::clone() const | ||||
| { | ||||
|     return new AiWander(*this); | ||||
| } | ||||
| 
 | ||||
| bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) | ||||
| { | ||||
|     if(mDuration) | ||||
|     { | ||||
|         // End package if duration is complete or mid-night hits:
 | ||||
|         MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|         if(currentTime.getHour() >= mStartTime.getHour() + mDuration) | ||||
|         for(unsigned short counter = 0; counter < mIdle.size(); counter++) | ||||
|         { | ||||
|             if(!mRepeat) | ||||
|             { | ||||
|                 stopWalking(actor, mPathFinder); | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|                 mStartTime = currentTime; | ||||
|             if(mIdle[counter] >= 127 || mIdle[counter] < 0) | ||||
|                 mIdle[counter] = 0; | ||||
|         } | ||||
|         else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) | ||||
|         { | ||||
|             if(!mRepeat) | ||||
|             { | ||||
|                 stopWalking(actor, mPathFinder); | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|                 mStartTime = currentTime; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ESM::Position pos = actor.getRefData().getPosition(); | ||||
| 
 | ||||
|     if(!mStoredAvailableNodes) | ||||
|     { | ||||
|         mStoredAvailableNodes = true; | ||||
|         mPathgrid = | ||||
|             MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); | ||||
| 
 | ||||
|         mCellX = actor.getCell()->mCell->mData.mX; | ||||
|         mCellY = actor.getCell()->mCell->mData.mY; | ||||
| 
 | ||||
|         if(!mPathgrid) | ||||
|             mDistance = 0; | ||||
|         else if(mPathgrid->mPoints.empty()) | ||||
|         if(mDistance < 0) | ||||
|             mDistance = 0; | ||||
|         if(mDuration < 0) | ||||
|             mDuration = 0; | ||||
|         if(mDuration == 0) | ||||
|             mTimeOfDay = 0; | ||||
| 
 | ||||
|         if(mDistance) | ||||
|         { | ||||
|             mXCell = 0; | ||||
|             mYCell = 0; | ||||
|             if(actor.getCell()->mCell->isExterior()) | ||||
|             { | ||||
|                 mXCell = mCellX * ESM::Land::REAL_SIZE; | ||||
|                 mYCell = mCellY * ESM::Land::REAL_SIZE; | ||||
|             } | ||||
| 
 | ||||
|             Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); | ||||
|             npcPos[0] = npcPos[0] - mXCell; | ||||
|             npcPos[1] = npcPos[1] - mYCell; | ||||
| 
 | ||||
|             for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) | ||||
|             { | ||||
|                 Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, mPathgrid->mPoints[counter].mZ); | ||||
|                 if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) | ||||
|                     mAllowedNodes.push_back(mPathgrid->mPoints[counter]); | ||||
|             } | ||||
|             if(!mAllowedNodes.empty()) | ||||
|             { | ||||
|                 Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); | ||||
|                 float closestNode = npcPos.squaredDistance(firstNodePos); | ||||
|                 unsigned int index = 0; | ||||
|                 for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) | ||||
|                 { | ||||
|                     Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, mAllowedNodes[counterThree].mZ); | ||||
|                     float tempDist = npcPos.squaredDistance(nodePos); | ||||
|                     if(tempDist < closestNode) | ||||
|                         index = counterThree; | ||||
|                 } | ||||
|                 mCurrentNode = mAllowedNodes[index]; | ||||
|                 mAllowedNodes.erase(mAllowedNodes.begin() + index); | ||||
|             } | ||||
| 
 | ||||
|             if(mAllowedNodes.empty()) | ||||
|                 mDistance = 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); | ||||
|     bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY; | ||||
| 
 | ||||
|     if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) | ||||
|     { | ||||
|         int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); | ||||
|         // Check if actor is near the border of an inactive cell. If so, disable AiWander.
 | ||||
|         // FIXME: This *should* pause the AiWander package instead of terminating it.
 | ||||
|         if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200))  | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) | ||||
|     { | ||||
|         int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); | ||||
|         // Check if actor is near the border of an inactive cell. If so, disable AiWander.
 | ||||
|         // FIXME: This *should* pause the AiWander package instead of terminating it.
 | ||||
|         if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200))  | ||||
|         { | ||||
|             MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
 | ||||
|     if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY))) | ||||
|         mDistance = 0; | ||||
| 
 | ||||
|     if(mChooseAction) | ||||
|     { | ||||
|         srand(time(NULL)); | ||||
|         mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|         mPlayedIdle = 0; | ||||
|         unsigned short idleRoll = 0; | ||||
|         mPathgrid = NULL; | ||||
|         mIdleChanceMultiplier = | ||||
|             MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat(); | ||||
| 
 | ||||
|         for(unsigned int counter = 1; counter < mIdle.size(); counter++) | ||||
|         mStoredAvailableNodes = false; | ||||
|         mChooseAction = true; | ||||
|         mIdleNow = false; | ||||
|         mMoveNow = false; | ||||
|         mWalking = false; | ||||
|     } | ||||
| 
 | ||||
|     AiPackage * MWMechanics::AiWander::clone() const | ||||
|     { | ||||
|         return new AiWander(*this); | ||||
|     } | ||||
| 
 | ||||
|     bool AiWander::execute (const MWWorld::Ptr& actor) | ||||
|     { | ||||
|         if(mDuration) | ||||
|         { | ||||
|             unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; | ||||
|             unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); | ||||
|             if(randSelect < idleChance && randSelect > idleRoll) | ||||
|             // End package if duration is complete or mid-night hits:
 | ||||
|             MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|             if(currentTime.getHour() >= mStartTime.getHour() + mDuration) | ||||
|             { | ||||
|                 mPlayedIdle = counter; | ||||
|                 idleRoll = randSelect; | ||||
|                 if(!mRepeat) | ||||
|                 { | ||||
|                     stopWalking(actor); | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                     mStartTime = currentTime; | ||||
|             } | ||||
|             else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) | ||||
|             { | ||||
|                 if(!mRepeat) | ||||
|                 { | ||||
|                     stopWalking(actor); | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                     mStartTime = currentTime; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(!mPlayedIdle && mDistance) | ||||
|         { | ||||
|             mChooseAction = false; | ||||
|             mMoveNow = true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
 | ||||
|             MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|             mStartTime = currentTime; | ||||
|             playIdle(actor, mPlayedIdle + 1); | ||||
|             mChooseAction = false; | ||||
|             mIdleNow = true; | ||||
|         } | ||||
|     } | ||||
|         ESM::Position pos = actor.getRefData().getPosition(); | ||||
| 
 | ||||
|     if(mIdleNow) | ||||
|     { | ||||
|         if(!checkIdle(actor, mPlayedIdle + 1)) | ||||
|         if(!mStoredAvailableNodes) | ||||
|         { | ||||
|             mStoredAvailableNodes = true; | ||||
|             mPathgrid = | ||||
|                 MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); | ||||
| 
 | ||||
|             mCellX = actor.getCell()->mCell->mData.mX; | ||||
|             mCellY = actor.getCell()->mCell->mData.mY; | ||||
| 
 | ||||
|             if(!mPathgrid) | ||||
|                 mDistance = 0; | ||||
|             else if(mPathgrid->mPoints.empty()) | ||||
|                 mDistance = 0; | ||||
| 
 | ||||
|             if(mDistance) | ||||
|             { | ||||
|                 mXCell = 0; | ||||
|                 mYCell = 0; | ||||
|                 if(actor.getCell()->mCell->isExterior()) | ||||
|                 { | ||||
|                     mXCell = mCellX * ESM::Land::REAL_SIZE; | ||||
|                     mYCell = mCellY * ESM::Land::REAL_SIZE; | ||||
|                 } | ||||
| 
 | ||||
|                 Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); | ||||
|                 npcPos[0] = npcPos[0] - mXCell; | ||||
|                 npcPos[1] = npcPos[1] - mYCell; | ||||
| 
 | ||||
|                 for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) | ||||
|                 { | ||||
|                     Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, | ||||
|                         mPathgrid->mPoints[counter].mZ); | ||||
|                     if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) | ||||
|                         mAllowedNodes.push_back(mPathgrid->mPoints[counter]); | ||||
|                 } | ||||
|                 if(!mAllowedNodes.empty()) | ||||
|                 { | ||||
|                     Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); | ||||
|                     float closestNode = npcPos.squaredDistance(firstNodePos); | ||||
|                     unsigned int index = 0; | ||||
|                     for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) | ||||
|                     { | ||||
|                         Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, | ||||
|                             mAllowedNodes[counterThree].mZ); | ||||
|                         float tempDist = npcPos.squaredDistance(nodePos); | ||||
|                         if(tempDist < closestNode) | ||||
|                             index = counterThree; | ||||
|                     } | ||||
|                     mCurrentNode = mAllowedNodes[index]; | ||||
|                     mAllowedNodes.erase(mAllowedNodes.begin() + index); | ||||
|                 } | ||||
| 
 | ||||
|                 if(mAllowedNodes.empty()) | ||||
|                     mDistance = 0; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); | ||||
|         bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY; | ||||
| 
 | ||||
|         if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) | ||||
|         { | ||||
|             int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); | ||||
|             // Check if actor is near the border of an inactive cell. If so, disable AiWander.
 | ||||
|             // FIXME: This *should* pause the AiWander package instead of terminating it.
 | ||||
|             if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / | ||||
|                 2.0 - 200))  | ||||
|             { | ||||
|                 stopWalking(actor); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) | ||||
|         { | ||||
|             int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); | ||||
|             // Check if actor is near the border of an inactive cell. If so, disable AiWander.
 | ||||
|             // FIXME: This *should* pause the AiWander package instead of terminating it.
 | ||||
|             if(sideY * (pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / | ||||
|                 2.0 - 200))  | ||||
|             { | ||||
|                 stopWalking(actor); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
 | ||||
|         if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY))) | ||||
|             mDistance = 0; | ||||
| 
 | ||||
|         if(mChooseAction) | ||||
|         { | ||||
|             mPlayedIdle = 0; | ||||
|             mIdleNow = false; | ||||
|             mChooseAction = true; | ||||
|         } | ||||
|     } | ||||
|             unsigned short idleRoll = 0; | ||||
| 
 | ||||
|     if(mMoveNow && mDistance) | ||||
|     { | ||||
|         if(!mPathFinder.isPathConstructed()) | ||||
|             for(unsigned int counter = 1; counter < mIdle.size(); counter++) | ||||
|             { | ||||
|                 unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; | ||||
|                 unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); | ||||
|                 if(randSelect < idleChance && randSelect > idleRoll) | ||||
|                 { | ||||
|                     mPlayedIdle = counter; | ||||
|                     idleRoll = randSelect; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if(!mPlayedIdle && mDistance) | ||||
|             { | ||||
|                 mChooseAction = false; | ||||
|                 mMoveNow = true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
 | ||||
|                 MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||
|                 mStartTime = currentTime; | ||||
|                 playIdle(actor, mPlayedIdle + 1); | ||||
|                 mChooseAction = false; | ||||
|                 mIdleNow = true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(mIdleNow) | ||||
|         { | ||||
|             unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); | ||||
|             Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); | ||||
| 
 | ||||
|             // Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
 | ||||
|             ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; | ||||
|             mAllowedNodes.erase(mAllowedNodes.begin() + randNode); | ||||
|             mAllowedNodes.push_back(mCurrentNode); | ||||
|             mCurrentNode = temp; | ||||
| 
 | ||||
|             ESM::Pathgrid::Point dest; | ||||
|             dest.mX = destNodePos[0] + mXCell; | ||||
|             dest.mY = destNodePos[1] + mYCell; | ||||
|             dest.mZ = destNodePos[2]; | ||||
| 
 | ||||
|             ESM::Pathgrid::Point start; | ||||
|             start.mX = pos.pos[0]; | ||||
|             start.mY = pos.pos[1]; | ||||
|             start.mZ = pos.pos[2]; | ||||
| 
 | ||||
|             mPathFinder.buildPath(start,dest,mPathgrid,mXCell,mYCell); | ||||
|             mWalking = true; | ||||
|             if(!checkIdle(actor, mPlayedIdle + 1)) | ||||
|             { | ||||
|                 mPlayedIdle = 0; | ||||
|                 mIdleNow = false; | ||||
|                 mChooseAction = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(mWalking) | ||||
|     { | ||||
|         float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); | ||||
|         MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); | ||||
|         MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; | ||||
| 
 | ||||
|         // Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be
 | ||||
|         // at the same path node at the same time and both will complete instead of endlessly walking into eachother:
 | ||||
|         Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ); | ||||
|         Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); | ||||
|         actorPos[0] = actorPos[0] - mXCell; | ||||
|         actorPos[1] = actorPos[1] - mYCell; | ||||
|         float distance = actorPos.squaredDistance(destNodePos); | ||||
| 
 | ||||
|         if(distance < 1200 || mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) | ||||
|         if(mMoveNow && mDistance) | ||||
|         { | ||||
|             stopWalking(actor, mPathFinder); | ||||
|             mMoveNow = false; | ||||
|             mWalking = false; | ||||
|             mChooseAction = true; | ||||
|             if(!mPathFinder.isPathConstructed()) | ||||
|             { | ||||
|                 unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); | ||||
|                 Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); | ||||
| 
 | ||||
|                 ESM::Pathgrid::Point dest; | ||||
|                 dest.mX = destNodePos[0] + mXCell; | ||||
|                 dest.mY = destNodePos[1] + mYCell; | ||||
|                 dest.mZ = destNodePos[2]; | ||||
| 
 | ||||
|                 ESM::Pathgrid::Point start; | ||||
|                 start.mX = pos.pos[0]; | ||||
|                 start.mY = pos.pos[1]; | ||||
|                 start.mZ = pos.pos[2]; | ||||
| 
 | ||||
|                 mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, false); | ||||
| 
 | ||||
|                 if(mPathFinder.isPathConstructed()) | ||||
|                 { | ||||
|                     // Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
 | ||||
|                     ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; | ||||
|                     mAllowedNodes.erase(mAllowedNodes.begin() + randNode); | ||||
|                     mAllowedNodes.push_back(mCurrentNode); | ||||
|                     mCurrentNode = temp; | ||||
| 
 | ||||
|                     mMoveNow = false; | ||||
|                     mWalking = true; | ||||
|                 } | ||||
|                 // Choose a different node and delete this one from possible nodes because it is uncreachable:
 | ||||
|                 else | ||||
|                     mAllowedNodes.erase(mAllowedNodes.begin() + randNode); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
|         if(mWalking) | ||||
|         { | ||||
|             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; | ||||
| 
 | ||||
| int MWMechanics::AiWander::getTypeId() const | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|             // Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be
 | ||||
|             // at the same path node at the same time and both will complete instead of endlessly walking into eachother:
 | ||||
|             Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ); | ||||
|             Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); | ||||
|             actorPos[0] = actorPos[0] - mXCell; | ||||
|             actorPos[1] = actorPos[1] - mYCell; | ||||
|             float distance = actorPos.squaredDistance(destNodePos); | ||||
| 
 | ||||
| void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path) | ||||
| { | ||||
|     PathFinder pathClearer; | ||||
|     path = pathClearer; | ||||
|     MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
| } | ||||
|             if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) | ||||
|             { | ||||
|                 stopWalking(actor); | ||||
|                 mMoveNow = false; | ||||
|                 mWalking = false; | ||||
|                 mChooseAction = true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| void MWMechanics::AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) | ||||
| { | ||||
|     if(idleSelect == 2) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1); | ||||
|     else if(idleSelect == 3) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); | ||||
|     else if(idleSelect == 4) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1); | ||||
|     else if(idleSelect == 5) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1); | ||||
|     else if(idleSelect == 6) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1); | ||||
|     else if(idleSelect == 7) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1); | ||||
|     else if(idleSelect == 8) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1); | ||||
|     else if(idleSelect == 9) | ||||
|         MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1); | ||||
| } | ||||
| 
 | ||||
| bool MWMechanics::AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) | ||||
| { | ||||
|     if(idleSelect == 2) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2"); | ||||
|     else if(idleSelect == 3) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3"); | ||||
|     else if(idleSelect == 4) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4"); | ||||
|     else if(idleSelect == 5) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5"); | ||||
|     else if(idleSelect == 6) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6"); | ||||
|     else if(idleSelect == 7) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7"); | ||||
|     else if(idleSelect == 8) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8"); | ||||
|     else if(idleSelect == 9) | ||||
|         return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9"); | ||||
|     else | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int AiWander::getTypeId() const | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     void AiWander::stopWalking(const MWWorld::Ptr& actor) | ||||
|     { | ||||
|         mPathFinder.clearPath(); | ||||
|         MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; | ||||
|     } | ||||
| 
 | ||||
|     void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) | ||||
|     { | ||||
|         if(idleSelect == 2) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1); | ||||
|         else if(idleSelect == 3) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); | ||||
|         else if(idleSelect == 4) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1); | ||||
|         else if(idleSelect == 5) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1); | ||||
|         else if(idleSelect == 6) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1); | ||||
|         else if(idleSelect == 7) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1); | ||||
|         else if(idleSelect == 8) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1); | ||||
|         else if(idleSelect == 9) | ||||
|             MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1); | ||||
|     } | ||||
| 
 | ||||
|     bool AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) | ||||
|     { | ||||
|         if(idleSelect == 2) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2"); | ||||
|         else if(idleSelect == 3) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3"); | ||||
|         else if(idleSelect == 4) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4"); | ||||
|         else if(idleSelect == 5) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5"); | ||||
|         else if(idleSelect == 6) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6"); | ||||
|         else if(idleSelect == 7) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7"); | ||||
|         else if(idleSelect == 8) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8"); | ||||
|         else if(idleSelect == 9) | ||||
|             return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9"); | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ namespace MWMechanics | |||
|             ///< 0: Wander
 | ||||
| 
 | ||||
|         private: | ||||
|             void stopWalking(const MWWorld::Ptr& actor, PathFinder& path); | ||||
|             void stopWalking(const MWWorld::Ptr& actor); | ||||
|             void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); | ||||
|             bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,164 +3,134 @@ | |||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
| 
 | ||||
| #include "OgreMath.h" | ||||
| 
 | ||||
| #include <boost/graph/dijkstra_shortest_paths.hpp> | ||||
| #include <boost/graph/adjacency_list.hpp> | ||||
| #include "boost/tuple/tuple.hpp" | ||||
| #include "OgreMath.h" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| 	//helpers functions
 | ||||
|     float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z) | ||||
|     struct found_path {}; | ||||
| 
 | ||||
|     typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, | ||||
|         boost::property<boost::vertex_index_t, int, ESM::Pathgrid::Point>, boost::property<boost::edge_weight_t, float> > | ||||
|         PathGridGraph; | ||||
|     typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap; | ||||
|     typedef PathGridGraph::vertex_descriptor PointID; | ||||
|     typedef PathGridGraph::edge_descriptor PointConnectionID; | ||||
| 
 | ||||
|     class goalVisited : public boost::default_dijkstra_visitor | ||||
|     { | ||||
|         return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+0.1*(point.mZ - z)*(point.mZ - z)); | ||||
|         public: | ||||
|             goalVisited(PointID goal) {mGoal = goal;}; | ||||
|             void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();}; | ||||
| 
 | ||||
|         private: | ||||
|             PointID mGoal; | ||||
|     }; | ||||
| 
 | ||||
|     float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z) | ||||
|     { | ||||
|         x -= point.mX; | ||||
|         y -= point.mY; | ||||
|         z -= point.mZ; | ||||
|         return sqrt(x * x + y * y + 0.1 * z * z); | ||||
|     } | ||||
| 
 | ||||
|     float distance(ESM::Pathgrid::Point point,float x,float y,float z) | ||||
|     float distance(ESM::Pathgrid::Point point, float x, float y, float z) | ||||
|     { | ||||
|         return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+(point.mZ - z)*(point.mZ - z)); | ||||
|         x -= point.mX; | ||||
|         y -= point.mY; | ||||
|         z -= point.mZ; | ||||
|         return sqrt(x * x + y * y + z * z); | ||||
|     } | ||||
| 
 | ||||
|     float distance(ESM::Pathgrid::Point a,ESM::Pathgrid::Point b) | ||||
|     float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b) | ||||
|     { | ||||
|         return sqrt(float(a.mX - b.mX)*(a.mX - b.mX)+(a.mY - b.mY)*(a.mY - b.mY)+(a.mZ - b.mZ)*(a.mZ - b.mZ)); | ||||
|         float x = a.mX - b.mX; | ||||
|         float y = a.mY - b.mY; | ||||
|         float z = a.mZ - b.mZ; | ||||
|         return sqrt(x * x + y * y + z * z); | ||||
|     } | ||||
| 
 | ||||
|     static float sgn(float a) | ||||
|     { | ||||
|         if(a>0) return 1.; | ||||
|         else return -1.; | ||||
|         if(a > 0) | ||||
|             return 1.0; | ||||
|         return -1.0; | ||||
|     } | ||||
| 
 | ||||
|     int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) | ||||
|     int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z) | ||||
|     { | ||||
|         if(!grid) return -1; | ||||
|         if(grid->mPoints.empty()) return -1; | ||||
|         if(!grid || grid->mPoints.empty()) | ||||
|             return -1; | ||||
| 
 | ||||
|         float m = distance(grid->mPoints[0],x,y,z); | ||||
|         int i0 = 0; | ||||
|         float distanceBetween = distance(grid->mPoints[0], x, y, z); | ||||
|         int closestIndex = 0; | ||||
| 
 | ||||
|         for(unsigned int i=1; i<grid->mPoints.size();++i) | ||||
|         for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) | ||||
|         { | ||||
|             if(distance(grid->mPoints[i],x,y,z)<m) | ||||
|             if(distance(grid->mPoints[counter], x, y, z) < distanceBetween) | ||||
|             { | ||||
|                 m = distance(grid->mPoints[i],x,y,z); | ||||
|                 i0 = i; | ||||
|                 distanceBetween = distance(grid->mPoints[counter], x, y, z); | ||||
|                 closestIndex = counter; | ||||
|             } | ||||
|         } | ||||
|         return i0; | ||||
| 
 | ||||
|         return closestIndex; | ||||
|     } | ||||
| 
 | ||||
|     typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS, | ||||
|         boost::property<boost::vertex_index_t,int,ESM::Pathgrid::Point>,boost::property<boost::edge_weight_t,float> > PathGridGraph; | ||||
|     typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap; | ||||
|     typedef PathGridGraph::vertex_descriptor PointID; | ||||
|     typedef PathGridGraph::edge_descriptor   PointConnectionID; | ||||
| 
 | ||||
|     struct found_path {}; | ||||
| 
 | ||||
|     /*class goalVisited : public boost::default_astar_visitor
 | ||||
|     { | ||||
|     public: | ||||
|         goalVisited(PointID goal) : mGoal(goal) {} | ||||
| 
 | ||||
|         void examine_vertex(PointID u, const PathGridGraph g) | ||||
|         { | ||||
|             if(u == mGoal) | ||||
|                 throw found_path(); | ||||
|         } | ||||
|     private: | ||||
|         PointID mGoal; | ||||
|     }; | ||||
| 
 | ||||
|     class DistanceHeuristic : public boost::atasr_heuristic <PathGridGraph, float> | ||||
|     { | ||||
|     public: | ||||
|         DistanceHeuristic(const PathGridGraph & l, PointID goal) | ||||
|             : mGraph(l), mGoal(goal) {} | ||||
| 
 | ||||
|         float operator()(PointID u) | ||||
|         { | ||||
|             const ESM::Pathgrid::Point & U = mGraph[u]; | ||||
|             const ESM::Pathgrid::Point & V = mGraph[mGoal]; | ||||
|             float dx = U.mX - V.mX; | ||||
|             float dy = U.mY - V.mY; | ||||
|             float dz = U.mZ - V.mZ; | ||||
|             return sqrt(dx * dx + dy * dy + dz * dz); | ||||
|         } | ||||
|     private: | ||||
|         const PathGridGraph & mGraph; | ||||
|         PointID mGoal; | ||||
|     };*/ | ||||
| 
 | ||||
| 	class goalVisited : public boost::default_dijkstra_visitor | ||||
| 	{ | ||||
| 	public: | ||||
| 		goalVisited(PointID goal) : mGoal(goal) {} | ||||
| 
 | ||||
| 		void examine_vertex(PointID u, const PathGridGraph g) | ||||
| 		{ | ||||
| 			if(u == mGoal) | ||||
| 				throw found_path(); | ||||
| 		} | ||||
| 	private: | ||||
| 		PointID mGoal; | ||||
| 	}; | ||||
| 
 | ||||
| 
 | ||||
|     PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) | ||||
|     PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0) | ||||
|     { | ||||
|         PathGridGraph graph; | ||||
| 
 | ||||
|         for(unsigned int i = 0;i<pathgrid->mPoints.size();++i) | ||||
|         for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) | ||||
|         { | ||||
|             PointID pID = boost::add_vertex(graph); | ||||
|             graph[pID].mX = pathgrid->mPoints[i].mX + xCell; | ||||
|             graph[pID].mY = pathgrid->mPoints[i].mY + yCell; | ||||
|             graph[pID].mZ = pathgrid->mPoints[i].mZ; | ||||
|             graph[pID].mX = pathgrid->mPoints[counter].mX + xCell; | ||||
|             graph[pID].mY = pathgrid->mPoints[counter].mY + yCell; | ||||
|             graph[pID].mZ = pathgrid->mPoints[counter].mZ; | ||||
|         } | ||||
| 
 | ||||
|         for(unsigned int i = 0;i<pathgrid->mEdges.size();++i) | ||||
|         for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++) | ||||
|         { | ||||
|             PointID u = pathgrid->mEdges[i].mV0; | ||||
|             PointID v = pathgrid->mEdges[i].mV1; | ||||
|             PointID u = pathgrid->mEdges[counterTwo].mV0; | ||||
|             PointID v = pathgrid->mEdges[counterTwo].mV1; | ||||
| 
 | ||||
|             PointConnectionID edge; | ||||
|             bool done; | ||||
|             boost::tie(edge,done) = boost::add_edge(u,v,graph); | ||||
|             boost::tie(edge, done) = boost::add_edge(u, v, graph); | ||||
|             WeightMap weightmap = boost::get(boost::edge_weight, graph); | ||||
|             weightmap[edge] = distance(graph[u],graph[v]); | ||||
| 
 | ||||
|             weightmap[edge] = distance(graph[u], graph[v]); | ||||
|         } | ||||
| 
 | ||||
|         return graph; | ||||
|     } | ||||
| 
 | ||||
|     std::list<ESM::Pathgrid::Point> findPath(PointID start,PointID end,PathGridGraph graph){ | ||||
|     std::list<ESM::Pathgrid::Point> findPath(PointID start, PointID end, PathGridGraph graph) | ||||
|     { | ||||
|         std::vector<PointID> p(boost::num_vertices(graph)); | ||||
|         std::vector<float> d(boost::num_vertices(graph)); | ||||
|         std::list<ESM::Pathgrid::Point> shortest_path; | ||||
| 
 | ||||
|         try { | ||||
|             boost::dijkstra_shortest_paths | ||||
|                 ( | ||||
|                 graph, | ||||
|                 start, | ||||
|                 boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph))
 | ||||
| 				); | ||||
|         try | ||||
|         { | ||||
|             boost::dijkstra_shortest_paths(graph, start, | ||||
|                 boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))); | ||||
|         } | ||||
| 
 | ||||
|         } catch(found_path fg) { | ||||
|             for(PointID v = end;; v = p[v]) { | ||||
|         catch(found_path fg) | ||||
|         { | ||||
|             for(PointID v = end; ; v = p[v]) | ||||
|             { | ||||
|                 shortest_path.push_front(graph[v]); | ||||
|                 if(p[v] == v) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return shortest_path; | ||||
|     } | ||||
| 
 | ||||
|     //end of helpers functions
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace MWMechanics | ||||
|  | @ -170,55 +140,81 @@ namespace MWMechanics | |||
|         mIsPathConstructed = false; | ||||
|     } | ||||
| 
 | ||||
|     void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, | ||||
|         const ESM::Pathgrid* pathGrid,float xCell,float yCell) | ||||
|     void PathFinder::clearPath() | ||||
|     { | ||||
|         //first check if there is an obstacle
 | ||||
|         if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, | ||||
|             endPoint.mX,endPoint.mY,endPoint.mZ) ) | ||||
|         { | ||||
|             int start = getClosestPoint(pathGrid,startPoint.mX - xCell,startPoint.mY - yCell,startPoint.mZ); | ||||
|             int end = getClosestPoint(pathGrid,endPoint.mX - xCell,endPoint.mY - yCell,endPoint.mZ); | ||||
|         if(!mPath.empty()) | ||||
|             mPath.clear(); | ||||
|         mIsPathConstructed = false; | ||||
|     } | ||||
| 
 | ||||
|             if(start != -1 && end != -1) | ||||
|     void PathFinder::buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint, | ||||
|         const ESM::Pathgrid* pathGrid, float xCell, float yCell, bool allowShortcuts) | ||||
|     { | ||||
|         if(allowShortcuts) | ||||
|         { | ||||
|             if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, endPoint.mX, endPoint.mY, | ||||
|                 endPoint.mZ)) | ||||
|                 allowShortcuts = false; | ||||
|         } | ||||
| 
 | ||||
|         if(!allowShortcuts) | ||||
|         { | ||||
|             int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); | ||||
|             int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); | ||||
| 
 | ||||
|             if(startNode != -1 && endNode != -1) | ||||
|             { | ||||
|                 PathGridGraph graph = buildGraph(pathGrid,xCell,yCell); | ||||
|                 mPath = findPath(start,end,graph); | ||||
|                 PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); | ||||
|                 mPath = findPath(startNode, endNode, graph); | ||||
| 
 | ||||
|                 if(!mPath.empty()) | ||||
|                 { | ||||
|                     mPath.push_back(endPoint); | ||||
|                     mIsPathConstructed = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         mPath.push_back(endPoint); | ||||
|         mIsPathConstructed = true; | ||||
|     } | ||||
| 
 | ||||
|     float PathFinder::getZAngleToNext(float x,float y,float z) | ||||
|     { | ||||
|         if(mPath.empty()) | ||||
|         else | ||||
|         { | ||||
|             return 0;/// shouldn't happen!
 | ||||
|             mPath.push_back(endPoint); | ||||
|             mIsPathConstructed = true; | ||||
|         } | ||||
|         ESM::Pathgrid::Point nextPoint = *mPath.begin(); | ||||
|         float dX = nextPoint.mX - x; | ||||
|         float dY = nextPoint.mY - y; | ||||
|         float h = sqrt(dX*dX+dY*dY); | ||||
|         return Ogre::Radian(acos(dY/h)*sgn(asin(dX/h))).valueDegrees(); | ||||
| 
 | ||||
|         if(mPath.empty()) | ||||
|             mIsPathConstructed = false; | ||||
|     } | ||||
| 
 | ||||
|     bool PathFinder::checkIfNextPointReached(float x,float y,float z) | ||||
|     float PathFinder::getZAngleToNext(float x, float y) | ||||
|     { | ||||
|         // This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call
 | ||||
|         // if otherwise).
 | ||||
|         if(mPath.empty()) | ||||
|             return 0; | ||||
| 
 | ||||
|         ESM::Pathgrid::Point nextPoint = *mPath.begin(); | ||||
|         float directionX = nextPoint.mX - x; | ||||
|         float directionY = nextPoint.mY - y; | ||||
|         float directionResult = sqrt(directionX * directionX + directionY * directionY); | ||||
| 
 | ||||
|         return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees(); | ||||
|     } | ||||
| 
 | ||||
|     bool PathFinder::checkPathCompleted(float x, float y, float z) | ||||
|     { | ||||
|         if(mPath.empty()) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         ESM::Pathgrid::Point nextPoint = *mPath.begin(); | ||||
|         if(distanceZCorrected(nextPoint,x,y,z) < 20) | ||||
|         if(distanceZCorrected(nextPoint, x, y, z) < 40) | ||||
|         { | ||||
|             mPath.pop_front(); | ||||
|             if(mPath.empty()) | ||||
|             { | ||||
|                 mIsPathConstructed = false; | ||||
|                 return true; | ||||
|             } | ||||
|             nextPoint = *mPath.begin(); | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|  | @ -226,8 +222,10 @@ namespace MWMechanics | |||
|     { | ||||
|         return mPath; | ||||
|     } | ||||
| 
 | ||||
|     bool PathFinder::isPathConstructed() | ||||
|     { | ||||
|         return mIsPathConstructed; | ||||
|     } | ||||
| } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,22 +8,24 @@ namespace MWMechanics | |||
| { | ||||
|     class PathFinder | ||||
|     { | ||||
|     public: | ||||
|         PathFinder(); | ||||
|         public: | ||||
|             PathFinder(); | ||||
| 
 | ||||
|         void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, | ||||
|             const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); | ||||
|             void clearPath(); | ||||
|             void buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint, | ||||
|                 const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0, bool allowShortcuts = 1); | ||||
| 
 | ||||
|         bool checkIfNextPointReached(float x,float y,float z);//returns true if the last point of the path has been reached.
 | ||||
|         float getZAngleToNext(float x,float y,float z); | ||||
|             bool checkPathCompleted(float x, float y, float z); | ||||
|             ///< \Returns true if the last point of the path has been reached.
 | ||||
|             float getZAngleToNext(float x, float y); | ||||
| 
 | ||||
|         std::list<ESM::Pathgrid::Point> getPath(); | ||||
|         bool isPathConstructed(); | ||||
|             std::list<ESM::Pathgrid::Point> getPath(); | ||||
|             bool isPathConstructed(); | ||||
| 
 | ||||
|     private: | ||||
|         std::list<ESM::Pathgrid::Point> mPath; | ||||
|         bool mIsPathConstructed; | ||||
|         private: | ||||
|             std::list<ESM::Pathgrid::Point> mPath; | ||||
|             bool mIsPathConstructed; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue