mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-24 14:49:27 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
602
Engine/source/T3D/aiPlayer.cpp
Normal file
602
Engine/source/T3D/aiPlayer.cpp
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/aiPlayer.h"
|
||||
|
||||
#include "console/consoleInternal.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(AIPlayer);
|
||||
|
||||
ConsoleDocClass( AIPlayer,
|
||||
"@brief A Player object not controlled by conventional input, but by an AI engine.\n\n"
|
||||
|
||||
"The AIPlayer provides a Player object that may be controlled from script. You control "
|
||||
"where the player moves and how fast. You may also set where the AIPlayer is aiming at "
|
||||
"-- either a location or another game object.\n\n"
|
||||
|
||||
"The AIPlayer class does not have a datablock of its own. It makes use of the PlayerData "
|
||||
"datablock to define how it looks, etc. As the AIPlayer is an extension of the Player class "
|
||||
"it can mount objects and fire weapons, or mount vehicles and drive them.\n\n"
|
||||
|
||||
"While the PlayerData datablock is used, there are a number of additional callbacks that are "
|
||||
"implemented by AIPlayer on the datablock. These are listed here:\n\n"
|
||||
|
||||
"void onReachDestination(AIPlayer obj) \n"
|
||||
"Called when the player has reached its set destination using the setMoveDestination() method. "
|
||||
"The actual point at which this callback is called is when the AIPlayer is within the mMoveTolerance "
|
||||
"of the defined destination.\n\n"
|
||||
|
||||
"void onMoveStuck(AIPlayer obj) \n"
|
||||
"While in motion, if an AIPlayer has moved less than moveStuckTolerance within a single tick, this "
|
||||
"callback is called. From here you could choose an alternate destination to get the AIPlayer moving "
|
||||
"again.\n\n"
|
||||
|
||||
"void onTargetEnterLOS(AIPlayer obj) \n"
|
||||
"When an object is being aimed at (following a call to setAimObject()) and the targeted object enters "
|
||||
"the AIPlayer's line of sight, this callback is called. The LOS test is a ray from the AIPlayer's eye "
|
||||
"position to the center of the target's bounding box. The LOS ray test only checks against interiors, "
|
||||
"statis shapes, and terrain.\n\n"
|
||||
|
||||
"void onTargetExitLOS(AIPlayer obj) \n"
|
||||
"When an object is being aimed at (following a call to setAimObject()) and the targeted object leaves "
|
||||
"the AIPlayer's line of sight, this callback is called. The LOS test is a ray from the AIPlayer's eye "
|
||||
"position to the center of the target's bounding box. The LOS ray test only checks against interiors, "
|
||||
"statis shapes, and terrain.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Create the demo player object\n"
|
||||
"%player = new AiPlayer()\n"
|
||||
"{\n"
|
||||
" dataBlock = DemoPlayer;\n"
|
||||
" path = \"\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see Player for a list of all inherited functions, variables, and base description\n"
|
||||
|
||||
"@ingroup AI\n"
|
||||
"@ingroup gameObjects\n");
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
AIPlayer::AIPlayer()
|
||||
{
|
||||
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
|
||||
mMoveSpeed = 1.0f;
|
||||
mMoveTolerance = 0.25f;
|
||||
mMoveStuckTolerance = 0.01f;
|
||||
mMoveStuckTestDelay = 30;
|
||||
mMoveStuckTestCountdown = 0;
|
||||
mMoveSlowdown = true;
|
||||
mMoveState = ModeStop;
|
||||
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = false;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
|
||||
mIsAiControlled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
AIPlayer::~AIPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void AIPlayer::initPersistFields()
|
||||
{
|
||||
addGroup( "AI" );
|
||||
|
||||
addField( "mMoveTolerance", TypeF32, Offset( mMoveTolerance, AIPlayer ),
|
||||
"@brief Distance from destination before stopping.\n\n"
|
||||
"When the AIPlayer is moving to a given destination it will move to within "
|
||||
"this distance of the destination and then stop. By providing this tolerance "
|
||||
"it helps the AIPlayer from never reaching its destination due to minor obstacles, "
|
||||
"rounding errors on its position calculation, etc. By default it is set to 0.25.\n");
|
||||
|
||||
addField( "moveStuckTolerance", TypeF32, Offset( mMoveStuckTolerance, AIPlayer ),
|
||||
"@brief Distance tolerance on stuck check.\n\n"
|
||||
"When the AIPlayer is moving to a given destination, if it ever moves less than "
|
||||
"this tolerance during a single tick, the AIPlayer is considered stuck. At this point "
|
||||
"the onMoveStuck() callback is called on the datablock.\n");
|
||||
|
||||
addField( "moveStuckTestDelay", TypeS32, Offset( mMoveStuckTestDelay, AIPlayer ),
|
||||
"@brief The number of ticks to wait before testing if the AIPlayer is stuck.\n\n"
|
||||
"When the AIPlayer is asked to move, this property is the number of ticks to wait "
|
||||
"before the AIPlayer starts to check if it is stuck. This delay allows the AIPlayer "
|
||||
"to accelerate to full speed without its initial slow start being considered as stuck.\n"
|
||||
"@note Set to zero to have the stuck test start immediately.\n");
|
||||
|
||||
endGroup( "AI" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool AIPlayer::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// Use the eye as the current position (see getAIMove)
|
||||
MatrixF eye;
|
||||
getEyeTransform(&eye);
|
||||
mLastLocation = eye.getPosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the speed at which this AI moves
|
||||
*
|
||||
* @param speed Speed to move, default player was 10
|
||||
*/
|
||||
void AIPlayer::setMoveSpeed( F32 speed )
|
||||
{
|
||||
mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops movement for this AI
|
||||
*/
|
||||
void AIPlayer::stopMove()
|
||||
{
|
||||
mMoveState = ModeStop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how far away from the move location is considered
|
||||
* "on target"
|
||||
*
|
||||
* @param tolerance Movement tolerance for error
|
||||
*/
|
||||
void AIPlayer::setMoveTolerance( const F32 tolerance )
|
||||
{
|
||||
mMoveTolerance = getMax( 0.1f, tolerance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to run to
|
||||
*
|
||||
* @param location Point to run to
|
||||
*/
|
||||
void AIPlayer::setMoveDestination( const Point3F &location, bool slowdown )
|
||||
{
|
||||
mMoveDestination = location;
|
||||
mMoveState = ModeMove;
|
||||
mMoveSlowdown = slowdown;
|
||||
mMoveStuckTestCountdown = mMoveStuckTestDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object the bot is targeting
|
||||
*
|
||||
* @param targetObject The object to target
|
||||
*/
|
||||
void AIPlayer::setAimObject( GameBase *targetObject )
|
||||
{
|
||||
mAimObject = targetObject;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object the bot is targeting and an offset to add to target location
|
||||
*
|
||||
* @param targetObject The object to target
|
||||
* @param offset The offest from the target location to aim at
|
||||
*/
|
||||
void AIPlayer::setAimObject( GameBase *targetObject, Point3F offset )
|
||||
{
|
||||
mAimObject = targetObject;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to aim at
|
||||
*
|
||||
* @param location Point to aim at
|
||||
*/
|
||||
void AIPlayer::setAimLocation( const Point3F &location )
|
||||
{
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = true;
|
||||
mAimLocation = location;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the aim location and sets it to the bot's
|
||||
* current destination so he looks where he's going
|
||||
*/
|
||||
void AIPlayer::clearAim()
|
||||
{
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates the moves for the AI player
|
||||
*
|
||||
* @param movePtr Pointer to move the move list into
|
||||
*/
|
||||
bool AIPlayer::getAIMove(Move *movePtr)
|
||||
{
|
||||
*movePtr = NullMove;
|
||||
|
||||
// Use the eye as the current position.
|
||||
MatrixF eye;
|
||||
getEyeTransform(&eye);
|
||||
Point3F location = eye.getPosition();
|
||||
Point3F rotation = getRotation();
|
||||
|
||||
// Orient towards the aim point, aim object, or towards
|
||||
// our destination.
|
||||
if (mAimObject || mAimLocationSet || mMoveState != ModeStop)
|
||||
{
|
||||
// Update the aim position if we're aiming for an object
|
||||
if (mAimObject)
|
||||
mAimLocation = mAimObject->getPosition() + mAimOffset;
|
||||
else
|
||||
if (!mAimLocationSet)
|
||||
mAimLocation = mMoveDestination;
|
||||
|
||||
F32 xDiff = mAimLocation.x - location.x;
|
||||
F32 yDiff = mAimLocation.y - location.y;
|
||||
|
||||
if (!mIsZero(xDiff) || !mIsZero(yDiff))
|
||||
{
|
||||
// First do Yaw
|
||||
// use the cur yaw between -Pi and Pi
|
||||
F32 curYaw = rotation.z;
|
||||
while (curYaw > M_2PI_F)
|
||||
curYaw -= M_2PI_F;
|
||||
while (curYaw < -M_2PI_F)
|
||||
curYaw += M_2PI_F;
|
||||
|
||||
// find the yaw offset
|
||||
F32 newYaw = mAtan2( xDiff, yDiff );
|
||||
F32 yawDiff = newYaw - curYaw;
|
||||
|
||||
// make it between 0 and 2PI
|
||||
if( yawDiff < 0.0f )
|
||||
yawDiff += M_2PI_F;
|
||||
else if( yawDiff >= M_2PI_F )
|
||||
yawDiff -= M_2PI_F;
|
||||
|
||||
// now make sure we take the short way around the circle
|
||||
if( yawDiff > M_PI_F )
|
||||
yawDiff -= M_2PI_F;
|
||||
else if( yawDiff < -M_PI_F )
|
||||
yawDiff += M_2PI_F;
|
||||
|
||||
movePtr->yaw = yawDiff;
|
||||
|
||||
// Next do pitch.
|
||||
if (!mAimObject && !mAimLocationSet)
|
||||
{
|
||||
// Level out if were just looking at our next way point.
|
||||
Point3F headRotation = getHeadRotation();
|
||||
movePtr->pitch = -headRotation.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should be adjusted to run from the
|
||||
// eye point to the object's center position. Though this
|
||||
// works well enough for now.
|
||||
F32 vertDist = mAimLocation.z - location.z;
|
||||
F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f );
|
||||
if (mFabs(newPitch) > 0.01f)
|
||||
{
|
||||
Point3F headRotation = getHeadRotation();
|
||||
movePtr->pitch = newPitch - headRotation.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Level out if we're not doing anything else
|
||||
Point3F headRotation = getHeadRotation();
|
||||
movePtr->pitch = -headRotation.x;
|
||||
}
|
||||
|
||||
// Move towards the destination
|
||||
if (mMoveState != ModeStop)
|
||||
{
|
||||
F32 xDiff = mMoveDestination.x - location.x;
|
||||
F32 yDiff = mMoveDestination.y - location.y;
|
||||
|
||||
// Check if we should mMove, or if we are 'close enough'
|
||||
if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance)
|
||||
{
|
||||
mMoveState = ModeStop;
|
||||
throwCallback("onReachDestination");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build move direction in world space
|
||||
if (mIsZero(xDiff))
|
||||
movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
|
||||
else
|
||||
if (mIsZero(yDiff))
|
||||
movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
|
||||
else
|
||||
if (mFabs(xDiff) > mFabs(yDiff))
|
||||
{
|
||||
F32 value = mFabs(yDiff / xDiff);
|
||||
movePtr->y = (location.y > mMoveDestination.y) ? -value : value;
|
||||
movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 value = mFabs(xDiff / yDiff);
|
||||
movePtr->x = (location.x > mMoveDestination.x) ? -value : value;
|
||||
movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
// Rotate the move into object space (this really only needs
|
||||
// a 2D matrix)
|
||||
Point3F newMove;
|
||||
MatrixF moveMatrix;
|
||||
moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw)));
|
||||
moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove );
|
||||
movePtr->x = newMove.x;
|
||||
movePtr->y = newMove.y;
|
||||
|
||||
// Set movement speed. We'll slow down once we get close
|
||||
// to try and stop on the spot...
|
||||
if (mMoveSlowdown)
|
||||
{
|
||||
F32 speed = mMoveSpeed;
|
||||
F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
|
||||
F32 maxDist = 5.0f;
|
||||
if (dist < maxDist)
|
||||
speed *= dist / maxDist;
|
||||
movePtr->x *= speed;
|
||||
movePtr->y *= speed;
|
||||
|
||||
mMoveState = ModeSlowing;
|
||||
}
|
||||
else
|
||||
{
|
||||
movePtr->x *= mMoveSpeed;
|
||||
movePtr->y *= mMoveSpeed;
|
||||
|
||||
mMoveState = ModeMove;
|
||||
}
|
||||
|
||||
if (mMoveStuckTestCountdown > 0)
|
||||
--mMoveStuckTestCountdown;
|
||||
else
|
||||
{
|
||||
// We should check to see if we are stuck...
|
||||
F32 locationDelta = (location - mLastLocation).len();
|
||||
if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled)
|
||||
{
|
||||
// If we are slowing down, then it's likely that our location delta will be less than
|
||||
// our move stuck tolerance. Because we can be both slowing and stuck
|
||||
// we should TRY to check if we've moved. This could use better detection.
|
||||
if ( mMoveState != ModeSlowing || locationDelta == 0 )
|
||||
{
|
||||
mMoveState = ModeStuck;
|
||||
throwCallback("onMoveStuck");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test for target location in sight if it's an object. The LOS is
|
||||
// run from the eye position to the center of the object's bounding,
|
||||
// which is not very accurate.
|
||||
if (mAimObject) {
|
||||
MatrixF eyeMat;
|
||||
getEyeTransform(&eyeMat);
|
||||
eyeMat.getColumn(3,&location);
|
||||
Point3F targetLoc = mAimObject->getBoxCenter();
|
||||
|
||||
// This ray ignores non-static shapes. Cast Ray returns true
|
||||
// if it hit something.
|
||||
RayInfo dummy;
|
||||
if (getContainer()->castRay( location, targetLoc,
|
||||
InteriorObjectType | StaticShapeObjectType | StaticObjectType |
|
||||
TerrainObjectType, &dummy)) {
|
||||
if (mTargetInLOS) {
|
||||
throwCallback( "onTargetExitLOS" );
|
||||
mTargetInLOS = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!mTargetInLOS) {
|
||||
throwCallback( "onTargetEnterLOS" );
|
||||
mTargetInLOS = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Replicate the trigger state into the move so that
|
||||
// triggers can be controlled from scripts.
|
||||
for( int i = 0; i < MaxTriggerKeys; i++ )
|
||||
movePtr->trigger[i] = getImageTriggerState(i);
|
||||
|
||||
mLastLocation = location;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to throw callbacks. Callbacks always occure
|
||||
* on the datablock class.
|
||||
*
|
||||
* @param name Name of script function to call
|
||||
*/
|
||||
void AIPlayer::throwCallback( const char *name )
|
||||
{
|
||||
Con::executef(getDataBlock(), name, getIdString());
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Console Functions
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( AIPlayer, stop, void, ( ),,
|
||||
"@brief Tells the AIPlayer to stop moving.\n\n")
|
||||
{
|
||||
object->stopMove();
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, clearAim, void, ( ),,
|
||||
"@brief Use this to stop aiming at an object or a point.\n\n"
|
||||
|
||||
"@see setAimLocation()\n"
|
||||
"@see setAimObject()\n")
|
||||
{
|
||||
object->clearAim();
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, setMoveSpeed, void, ( F32 speed ),,
|
||||
"@brief Sets the move speed for an AI object.\n\n"
|
||||
|
||||
"@param speed A speed multiplier between 0.0 and 1.0. "
|
||||
"This is multiplied by the AIPlayer's base movement rates (as defined in "
|
||||
"its PlayerData datablock)\n\n"
|
||||
|
||||
"@see getMoveDestination()\n")
|
||||
{
|
||||
object->setMoveSpeed(speed);
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, setMoveDestination, void, ( Point3F goal, bool slowDown ), ( true ),
|
||||
"@brief Tells the AI to move to the location provided\n\n"
|
||||
|
||||
"@param goal Coordinates in world space representing location to move to.\n"
|
||||
"@param slowDown A boolean value. If set to true, the bot will slow down "
|
||||
"when it gets within 5-meters of its move destination. If false, the bot "
|
||||
"will stop abruptly when it reaches the move destination. By default, this is true.\n\n"
|
||||
|
||||
"@note Upon reaching a move destination, the bot will clear its move destination and "
|
||||
"calls to getMoveDestination will return \"0 0 0\"."
|
||||
|
||||
"@see getMoveDestination()\n")
|
||||
{
|
||||
object->setMoveDestination( goal, slowDown);
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, getMoveDestination, Point3F, (),,
|
||||
"@brief Get the AIPlayer's current destination.\n\n"
|
||||
|
||||
"@return Returns a point containing the \"x y z\" position "
|
||||
"of the AIPlayer's current move destination. If no move destination "
|
||||
"has yet been set, this returns \"0 0 0\"."
|
||||
|
||||
"@see setMoveDestination()\n")
|
||||
{
|
||||
return object->getMoveDestination();
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, setAimLocation, void, ( Point3F target ),,
|
||||
"@brief Tells the AIPlayer to aim at the location provided.\n\n"
|
||||
|
||||
"@param target An \"x y z\" position in the game world to target.\n\n"
|
||||
|
||||
"@see getAimLocation()\n")
|
||||
{
|
||||
object->setAimLocation(target);
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, getAimLocation, Point3F, (),,
|
||||
"@brief Returns the point the AIPlayer is aiming at.\n\n"
|
||||
|
||||
"This will reflect the position set by setAimLocation(), "
|
||||
"or the position of the object that the bot is now aiming at. "
|
||||
"If the bot is not aiming at anything, this value will "
|
||||
"change to whatever point the bot's current line-of-sight intercepts."
|
||||
|
||||
"@return World space coordinates of the object AI is aiming at. Formatted as \"X Y Z\".\n\n"
|
||||
|
||||
"@see setAimLocation()\n"
|
||||
"@see setAimObject()\n")
|
||||
{
|
||||
return object->getAimLocation();
|
||||
}
|
||||
|
||||
ConsoleDocFragment _setAimObject(
|
||||
"@brief Sets the AIPlayer's target object. May optionally set an offset from target location\n\n"
|
||||
|
||||
"@param targetObject The object to target\n"
|
||||
"@param offset Optional three-element offset vector which will be added to the position of the aim object.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Without an offset\n"
|
||||
"%ai.setAimObject(%target);\n\n"
|
||||
"// With an offset\n"
|
||||
"// Cause our AI object to aim at the target\n"
|
||||
"// offset (0, 0, 1) so you don't aim at the target's feet\n"
|
||||
"%ai.setAimObject(%target, \"0 0 1\");\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see getAimLocation()\n"
|
||||
"@see getAimObject()\n"
|
||||
"@see clearAim()\n",
|
||||
|
||||
"AIPlayer",
|
||||
"void setAimObject(GameBase targetObject, Point3F offset);"
|
||||
);
|
||||
ConsoleMethod( AIPlayer, setAimObject, void, 3, 4, "( GameBase obj, [Point3F offset] )"
|
||||
"Sets the bot's target object. Optionally set an offset from target location."
|
||||
"@hide")
|
||||
{
|
||||
Point3F off( 0.0f, 0.0f, 0.0f );
|
||||
|
||||
// Find the target
|
||||
GameBase *targetObject;
|
||||
if( Sim::findObject( argv[2], targetObject ) )
|
||||
{
|
||||
if (argc == 4)
|
||||
dSscanf( argv[3], "%g %g %g", &off.x, &off.y, &off.z );
|
||||
|
||||
object->setAimObject( targetObject, off );
|
||||
}
|
||||
else
|
||||
object->setAimObject( 0, off );
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, getAimObject, S32, (),,
|
||||
"@brief Gets the object the AIPlayer is targeting.\n\n"
|
||||
|
||||
"@return Returns -1 if no object is being aimed at, "
|
||||
"or the SimObjectID of the object the AIPlayer is aiming at.\n\n"
|
||||
|
||||
"@see setAimObject()\n")
|
||||
{
|
||||
GameBase* obj = object->getAimObject();
|
||||
return obj? obj->getId(): -1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue