diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 41cc320ad..ce7700ddd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket levelledlist combat + disease pickpocket levelledlist combat steering ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 2ad667d3f..44ed59bb4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -574,8 +574,6 @@ namespace MWInput double x = arg.xrel * mCameraSensitivity * (1.0f/256.f); double y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; - float scale = MWBase::Environment::get().getFrameDuration(); - if(scale <= 0.0f) scale = 1.0f; float rot[3]; rot[0] = -y; @@ -585,8 +583,8 @@ namespace MWInput // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { - mPlayer->yaw(x/scale); - mPlayer->pitch(-y/scale); + mPlayer->yaw(x); + mPlayer->pitch(-y); } if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ab39e8f0f..cf08dabf8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,23 +1,22 @@ #include "aicombat.hpp" -#include "aifollow.hpp" -#include "movement.hpp" +#include +#include + #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "character.hpp" -#include "../mwworld/inventorystore.hpp" -#include "creaturestats.hpp" #include "npcstats.hpp" - -#include -#include +#include "steering.hpp" +#include "movement.hpp" +#include "character.hpp" // fixme: for getActiveWeapon namespace { @@ -43,7 +42,9 @@ namespace MWMechanics mReadyToAttack(false), mStrike(false), mCombatMove(false), - mMovement() + mRotate(false), + mMovement(), + mTargetAngle(0) { } @@ -68,10 +69,16 @@ namespace MWMechanics mCombatMove = false; } } + actor.getClass().getMovementSettings(actor) = mMovement; + + if (mRotate) + { + if (zTurn(actor, Ogre::Degree(mTargetAngle))) + mRotate = false; + } - //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); mTimerAttack -= duration; actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); @@ -156,12 +163,7 @@ namespace MWMechanics weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - ESM::Position pos = actor.getRefData().getPosition(); - - float zAngle; - float rangeMelee; float rangeCloseUp; @@ -189,12 +191,8 @@ namespace MWMechanics //Melee and Close-up combat vDir.z = 0; float dirLen = vDir.length(); - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + mRotate = true; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); if (mFollowTarget && distBetween > rangeMelee) @@ -237,12 +235,6 @@ namespace MWMechanics else { //target is at far distance: build path to target OR follow target (if previously actor had reached it once) - - /* - //apply when AIFOLLOW package implementation will be existent - if(mFollowTarget) - actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ - mFollowTarget = false; buildNewPath(actor); @@ -252,13 +244,10 @@ namespace MWMechanics //try shortcut if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); else - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - //mMovement.mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -294,6 +283,8 @@ namespace MWMechanics } } + actor.getClass().getMovementSettings(actor) = mMovement; + return false; } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 27f7f5d95..767a36292 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -29,16 +29,21 @@ namespace MWMechanics private: PathFinder mPathFinder; - //controls duration of the actual strike + // controls duration of the actual strike float mTimerAttack; float mTimerReact; - //controls duration of the sideway & forward moves - //when mCombatMove is true + // controls duration of the sideway & forward moves + // when mCombatMove is true float mTimerCombatMove; + // the z rotation angle (degrees) we want to reach + // used every frame when mRotate is true + float mTargetAngle; + bool mReadyToAttack, mStrike; bool mFollowTarget; bool mCombatMove; + bool mRotate; MWMechanics::Movement mMovement; MWWorld::Ptr mTarget; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 901b8c31d..bac258425 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -8,6 +8,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "steering.hpp" + namespace { float sgn(float a) @@ -33,7 +35,7 @@ namespace MWMechanics { mMaxDist = 470; - // The CS Help File states that if a duration is givin, the AI package will run for that long + // The CS Help File states that if a duration is given, 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; @@ -52,7 +54,7 @@ namespace MWMechanics { mMaxDist = 470; - // The CS Help File states that if a duration is givin, the AI package will run for that long + // The CS Help File states that if a duration is given, 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; @@ -89,25 +91,23 @@ namespace MWMechanics 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. + // Check if actor is near the border of an inactive cell. If so, pause walking. 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; + return false; } } 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. + // Check if actor is near the border of an inactive cell. If so, pause walking. 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; + return false; } } @@ -151,8 +151,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(zAngle)); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 10bff8356..cf5291fd3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,15 +6,17 @@ #include "movement.hpp" #include - + +#include "steering.hpp" + MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) -{ -} -MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) -{ -} +{ +} +MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) +{ +} bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { @@ -45,14 +47,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) } } - ESM::Pathgrid::Point dest; - dest.mX = target.getRefData().getPosition().pos[0]; - dest.mY = target.getRefData().getPosition().pos[1]; + 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]; + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; start.mZ = pos.pos[2]; if(mPathFinder.getPath().empty()) @@ -88,18 +90,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) if(!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).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); - //std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n"; - //MWWorld::Class::get(actor).get - } - - if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) - < 100*100) - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + } + + if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) + < 100*100) + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; else - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; return false; } @@ -109,12 +107,12 @@ std::string MWMechanics::AiFollow::getFollowedActor() return mActorId; } -MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - - int MWMechanics::AiFollow::getTypeId() const -{ - return TypeIdFollow; +MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +{ + return new AiFollow(*this); +} + + int MWMechanics::AiFollow::getTypeId() const +{ + return TypeIdFollow; } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 9a1b98d8f..8a0b2ebd0 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,11 +1,12 @@ #include "aitravel.hpp" -#include "movement.hpp" - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" +#include "steering.hpp" +#include "movement.hpp" + namespace { float sgn(float a) @@ -86,9 +87,7 @@ namespace MWMechanics return true; } - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); movement.mPosition[1] = 1; return false; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c6ea2ee91..5be604ab1 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -11,6 +11,8 @@ #include "creaturestats.hpp" #include +#include "steering.hpp" + namespace { float sgn(float a) @@ -282,11 +284,6 @@ namespace MWMechanics if(mWalking) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) { stopWalking(actor); @@ -294,6 +291,12 @@ namespace MWMechanics mWalking = false; mChooseAction = true; } + else + { + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + } } return false; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a54f2365d..32a05832c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1111,9 +1111,9 @@ void CharacterController::update(float duration) if (!mSkipAnim) { + rot *= Ogre::Math::RadiansToDegrees(1.0f); if(mHitState != CharState_KnockDown) { - rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } else //avoid z-rotating for knockdown diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index a3ac22012..8771ef0ca 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace MWWorld { @@ -26,8 +25,10 @@ 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; float getDistToNext(float x, float y, float z); diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp new file mode 100644 index 000000000..d911fd81b --- /dev/null +++ b/apps/openmw/mwmechanics/steering.cpp @@ -0,0 +1,43 @@ +#include "steering.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" + +#include "../mwbase/environment.hpp" + +#include "movement.hpp" + +namespace MWMechanics +{ + +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle) +{ + Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]); + Ogre::Radian diff (targetAngle - currentAngle); + if (diff >= Ogre::Degree(180)) + { + // Turning the other way would be a better idea + diff = diff-Ogre::Degree(360); + } + else if (diff <= Ogre::Degree(-180)) + { + diff = Ogre::Degree(360)-diff; + } + Ogre::Radian absDiff = Ogre::Math::Abs(diff); + + // The turning animation actually moves you slightly, so the angle will be wrong again. + // Use epsilon to prevent jerkiness. + const Ogre::Degree epsilon (0.5); + if (absDiff < epsilon) + return true; + + // Max. speed of 10 radian per sec + Ogre::Radian limit = Ogre::Radian(10) * MWBase::Environment::get().getFrameDuration(); + if (absDiff > limit) + diff = Ogre::Math::Sign(diff) * limit; + + actor.getClass().getMovementSettings(actor).mRotation[2] = diff.valueRadians(); + return false; +} + +} diff --git a/apps/openmw/mwmechanics/steering.hpp b/apps/openmw/mwmechanics/steering.hpp new file mode 100644 index 000000000..504dc3ac3 --- /dev/null +++ b/apps/openmw/mwmechanics/steering.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_MECHANICS_STEERING_H + +#include + +namespace MWWorld +{ +class Ptr; +} + +namespace MWMechanics +{ + +/// configure rotation settings for an actor to reach this target angle (eventually) +/// @return have we reached the target angle? +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle); + +} + +#endif