mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 15:15:31 +00:00
Improvements to smooth NPC steering
This commit is contained in:
parent
2b15b8b484
commit
39d86a9468
12 changed files with 151 additions and 95 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
#include "aicombat.hpp"
|
||||
#include "aifollow.hpp"
|
||||
|
||||
#include "movement.hpp"
|
||||
#include <OgreMath.h>
|
||||
#include <OgreVector3.h>
|
||||
|
||||
|
||||
#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 <OgreMath.h>
|
||||
#include <OgreVector3.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -6,15 +6,17 @@
|
|||
#include "movement.hpp"
|
||||
|
||||
#include <OgreMath.h>
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "creaturestats.hpp"
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <components/esm/loadpgrd.hpp>
|
||||
#include <list>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
|
||||
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);
|
||||
|
|
43
apps/openmw/mwmechanics/steering.cpp
Normal file
43
apps/openmw/mwmechanics/steering.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
19
apps/openmw/mwmechanics/steering.hpp
Normal file
19
apps/openmw/mwmechanics/steering.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef OPENMW_MECHANICS_STEERING_H
|
||||
|
||||
#include <OgreMath.h>
|
||||
|
||||
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
|
Loading…
Reference in a new issue