mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
generalized ai subsystem wipwork
This commit is contained in:
parent
75e23e85ea
commit
8c663a19a5
|
|
@ -58,7 +58,7 @@ torqueAddSourceDirectories("platform" "platform/threads" "platform/async"
|
|||
|
||||
torqueAddSourceDirectories("platform/nativeDialogs")
|
||||
# Handle T3D
|
||||
torqueAddSourceDirectories( "T3D" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx"
|
||||
torqueAddSourceDirectories( "T3D" "T3D/AI" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx"
|
||||
"T3D/gameBase" "T3D/gameBase/std"
|
||||
"T3D/lighting"
|
||||
"T3D/physics"
|
||||
|
|
|
|||
77
Engine/source/T3D/AI/AIAimTarget.cpp
Normal file
77
Engine/source/T3D/AI/AIAimTarget.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "AIAimTarget.h"
|
||||
#include "AIController.h"
|
||||
|
||||
static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
|
||||
|
||||
bool AIAimTarget::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled)
|
||||
{
|
||||
ShapeBase* sbo = dynamic_cast<ShapeBase*>(getCtrl()->getAIInfo()->mObj.getPointer());
|
||||
if (!target)
|
||||
{
|
||||
target = dynamic_cast<ShapeBase*>(mObj.getPointer());
|
||||
if (!target)
|
||||
return false;
|
||||
}
|
||||
if (_checkEnabled)
|
||||
{
|
||||
if (target->getTypeMask() & ShapeBaseObjectType)
|
||||
{
|
||||
ShapeBase* shapeBaseCheck = static_cast<ShapeBase*>(target);
|
||||
if (shapeBaseCheck)
|
||||
if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
RayInfo ri;
|
||||
|
||||
sbo->disableCollision();
|
||||
|
||||
S32 mountCount = target->getMountedObjectCount();
|
||||
for (S32 i = 0; i < mountCount; i++)
|
||||
{
|
||||
target->getMountedObject(i)->disableCollision();
|
||||
}
|
||||
|
||||
Point3F checkPoint;
|
||||
if (_useMuzzle)
|
||||
sbo->getMuzzlePoint(0, &checkPoint);
|
||||
else
|
||||
{
|
||||
MatrixF eyeMat;
|
||||
sbo->getEyeTransform(&eyeMat);
|
||||
eyeMat.getColumn(3, &checkPoint);
|
||||
}
|
||||
|
||||
bool hit = !gServerContainer.castRay(checkPoint, target->getBoxCenter(), sAILoSMask, &ri);
|
||||
sbo->enableCollision();
|
||||
|
||||
for (S32 i = 0; i < mountCount; i++)
|
||||
{
|
||||
target->getMountedObject(i)->enableCollision();
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
39
Engine/source/T3D/AI/AIAimTarget.h
Normal file
39
Engine/source/T3D/AI/AIAimTarget.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _AIAIMTARGET_H_
|
||||
#define _AIAIMTARGET_H_
|
||||
|
||||
#include "AIInfo.h"
|
||||
struct AIAimTarget : AIInfo
|
||||
{
|
||||
typedef AIInfo Parent;
|
||||
Point3F mAimOffset;
|
||||
bool mTargetInLOS; // Is target object visible?
|
||||
Point3F getPosition() { return ((mObj) ? mObj->getPosition() : mPosition) + mAimOffset; }
|
||||
bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false);
|
||||
bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false);
|
||||
AIAimTarget(AIController* controller) : Parent(controller) {};
|
||||
AIAimTarget(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn) : Parent(controller, objIn, radIn) {};
|
||||
AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {};
|
||||
};
|
||||
|
||||
#endif
|
||||
430
Engine/source/T3D/AI/AIController.cpp
Normal file
430
Engine/source/T3D/AI/AIController.cpp
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "AIController.h"
|
||||
#include "T3D/player.h"
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT(AIController);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void AIController::throwCallback(const char* name)
|
||||
{
|
||||
Con::executef(mControllerData, name, getIdString()); //controller data callbacks
|
||||
|
||||
GameBase* gbo = dynamic_cast<GameBase*>(getAIInfo()->mObj.getPointer());
|
||||
if (!gbo) return;
|
||||
Con::executef(gbo->getDataBlock(), name, getAIInfo()->mObj->getIdString()); //legacy support for object db callbacks
|
||||
}
|
||||
|
||||
void AIController::initPersistFields()
|
||||
{
|
||||
addProtectedField("ControllerData", TYPEID< AIControllerData >(), Offset(mControllerData, AIController),
|
||||
&setControllerDataProperty, &defaultProtectedGetFn,
|
||||
"Script datablock used for game objects.");
|
||||
addFieldV("MoveSpeed", TypeRangedF32, Offset(mMovement.mMoveSpeed, AIController), &CommonValidators::PositiveFloat,
|
||||
"@brief default move sepeed.");
|
||||
}
|
||||
|
||||
bool AIController::setControllerDataProperty(void* obj, const char* index, const char* db)
|
||||
{
|
||||
if (db == NULL || !db[0])
|
||||
{
|
||||
Con::errorf("AIController::setControllerDataProperty - Can't unset ControllerData on AIController objects");
|
||||
return false;
|
||||
}
|
||||
|
||||
AIController* object = static_cast<AIController*>(obj);
|
||||
AIControllerData* data;
|
||||
if (Sim::findObject(db, data))
|
||||
{
|
||||
object->mControllerData = data;
|
||||
return true;
|
||||
}
|
||||
Con::errorf("AIController::setControllerDataProperty - Could not find ControllerData \"%s\"", db);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
bool AIController::getAIMove(Move* movePtr)
|
||||
{
|
||||
*movePtr = NullMove;
|
||||
ShapeBase* sbo = dynamic_cast<ShapeBase*>(getAIInfo()->mObj.getPointer());
|
||||
if (!sbo) return false;
|
||||
|
||||
// Use the eye as the current position.
|
||||
MatrixF eye;
|
||||
sbo->getEyeTransform(&eye);
|
||||
Point3F location = eye.getPosition();
|
||||
Point3F rotation = sbo->getTransform().getForwardVector();
|
||||
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
if (sbo->getDamageState() == ShapeBase::Enabled)
|
||||
{
|
||||
if (mMovement.mMoveState != ModeStop)
|
||||
getNav()->updateNavMesh();
|
||||
if (!getGoal()->mObj.isNull())
|
||||
{
|
||||
if (getNav()->mPathData.path.isNull())
|
||||
{
|
||||
if (getGoal()->getDist() > mControllerData->mMoveTolerance)
|
||||
getNav()->followObject(getGoal());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getGoal()->getDist() > mControllerData->mMoveTolerance)
|
||||
getNav()->repath();
|
||||
|
||||
if (getAim()->getDist() < mControllerData->mMoveTolerance)
|
||||
{
|
||||
getNav()->clearPath();
|
||||
mMovement.mMoveState = ModeStop;
|
||||
throwCallback("onTargetInRange");
|
||||
}
|
||||
else if (getAim()->getDist() < mControllerData->mAttackRadius)
|
||||
{
|
||||
throwCallback("onTargetInFiringRange");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // TORQUE_NAVIGATION_ENABLED
|
||||
|
||||
// Orient towards the aim point, aim object, or towards
|
||||
// our destination.
|
||||
if (getAim()->mObj || getAim()->mPosSet || mMovement.mMoveState != ModeStop)
|
||||
{
|
||||
// Update the aim position if we're aiming for an object or explicit position
|
||||
if (getAim()->mObj || getAim()->mPosSet)
|
||||
mMovement.mAimLocation = getAim()->getPosition();
|
||||
else
|
||||
mMovement.mAimLocation = mMovement.mMoveDestination;
|
||||
|
||||
mControllerData->resolveYaw(this, location, movePtr);
|
||||
mControllerData->resolvePitch(this, location, movePtr);
|
||||
mControllerData->resolveRoll(this, location, movePtr);
|
||||
mControllerData->resolveSpeed(this, location, movePtr);
|
||||
mControllerData->resolveStuck(this);
|
||||
}
|
||||
|
||||
// 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 (getAim()->mObj)
|
||||
{
|
||||
GameBase* gbo = dynamic_cast<GameBase*>(getAIInfo()->mObj.getPointer());
|
||||
if (getAim()->checkInLos(gbo))
|
||||
{
|
||||
if (!getAim()->mTargetInLOS)
|
||||
{
|
||||
throwCallback("onTargetEnterLOS");
|
||||
getAim()->mTargetInLOS = true;
|
||||
}
|
||||
}
|
||||
else if (getAim()->mTargetInLOS)
|
||||
{
|
||||
throwCallback("onTargetExitLOS");
|
||||
getAim()->mTargetInLOS = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Replicate the trigger state into the move so that
|
||||
// triggers can be controlled from scripts.
|
||||
for (U32 i = 0; i < MaxTriggerKeys; i++)
|
||||
movePtr->trigger[i] = getImageTriggerState(i);
|
||||
*/
|
||||
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
if (getNav()->mJump == AINavigation::Now)
|
||||
{
|
||||
movePtr->trigger[2] = true;
|
||||
getNav()->mJump = AINavigation::None;
|
||||
}
|
||||
else if (getNav()->mJump == AINavigation::Ledge)
|
||||
{
|
||||
// If we're not touching the ground, jump!
|
||||
RayInfo info;
|
||||
if (!getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info))
|
||||
{
|
||||
movePtr->trigger[2] = true;
|
||||
getNav()->mJump = AINavigation::None;
|
||||
}
|
||||
}
|
||||
#endif // TORQUE_NAVIGATION_ENABLED
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIController::clearCover()
|
||||
{
|
||||
// Notify cover that we are no longer on our way.
|
||||
if (!getCover()->mCoverPoint.isNull())
|
||||
getCover()->mCoverPoint->setOccupied(false);
|
||||
SAFE_DELETE(mCover);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AIControllerData);
|
||||
void AIControllerData::resolveYaw(AIController* obj, Point3F location, Move* move)
|
||||
{
|
||||
F32 xDiff = obj->mMovement.mAimLocation.x - location.x;
|
||||
F32 yDiff = obj->mMovement.mAimLocation.y - location.y;
|
||||
Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector();
|
||||
|
||||
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;
|
||||
|
||||
move->yaw = yawDiff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* movePtr)
|
||||
{
|
||||
}
|
||||
|
||||
void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr)
|
||||
{
|
||||
// Move towards the destination
|
||||
if (obj->mMovement.mMoveState != AIController::ModeStop)
|
||||
{
|
||||
F32 xDiff = obj->mMovement.mMoveDestination.x - location.x;
|
||||
F32 yDiff = obj->mMovement.mMoveDestination.y - location.y;
|
||||
Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector();
|
||||
|
||||
// Check if we should mMove, or if we are 'close enough'
|
||||
if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance)
|
||||
{
|
||||
obj->mMovement.mMoveState = AIController::ModeStop;
|
||||
obj->getNav()->onReachDestination();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build move direction in world space
|
||||
if (mIsZero(xDiff))
|
||||
movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -1.0f : 1.0f;
|
||||
else
|
||||
if (mIsZero(yDiff))
|
||||
movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f;
|
||||
else
|
||||
if (mFabs(xDiff) > mFabs(yDiff))
|
||||
{
|
||||
F32 value = mFabs(yDiff / xDiff);
|
||||
movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -value : value;
|
||||
movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 value = mFabs(xDiff / yDiff);
|
||||
movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -value : value;
|
||||
movePtr->y = (location.y > obj->mMovement.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 (obj->mMovement.mMoveSlowdown)
|
||||
{
|
||||
F32 speed = obj->mMovement.mMoveSpeed;
|
||||
F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
F32 maxDist = mMoveTolerance * 2;
|
||||
if (dist < maxDist)
|
||||
speed *= dist / maxDist;
|
||||
movePtr->x *= speed;
|
||||
movePtr->y *= speed;
|
||||
|
||||
obj->mMovement.mMoveState = AIController::ModeSlowing;
|
||||
}
|
||||
else
|
||||
{
|
||||
movePtr->x *= obj->mMovement.mMoveSpeed;
|
||||
movePtr->y *= obj->mMovement.mMoveSpeed;
|
||||
|
||||
obj->mMovement.mMoveState = AIController::ModeMove;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AIControllerData::resolveStuck(AIController* obj)
|
||||
{
|
||||
ShapeBase* sbo = dynamic_cast<ShapeBase*>(obj->getAIInfo()->mObj.getPointer());
|
||||
// Don't check for ai stuckness if animation during
|
||||
// an anim-clip effect override.
|
||||
if (sbo->getDamageState() == ShapeBase::Enabled && !(sbo->anim_clip_flags & ShapeBase::ANIM_OVERRIDDEN) && !sbo->isAnimationLocked()) {
|
||||
if (obj->mMovement.mMoveStuckTestCountdown > 0)
|
||||
--obj->mMovement.mMoveStuckTestCountdown;
|
||||
else
|
||||
{
|
||||
// We should check to see if we are stuck...
|
||||
F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len();
|
||||
if (locationDelta < mMoveStuckTolerance && (sbo->getDamageState() == ShapeBase::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 (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0)
|
||||
{
|
||||
obj->mMovement.mMoveState = AIController::ModeStuck;
|
||||
obj->throwCallback("onStuck");
|
||||
}
|
||||
}
|
||||
}
|
||||
obj->getAIInfo()->mLastPos = obj->getAIInfo()->getPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void AIControllerData::initPersistFields()
|
||||
{
|
||||
docsURL;
|
||||
addGroup("AI");
|
||||
|
||||
addFieldV("moveTolerance", TypeRangedF32, Offset(mMoveTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||
"@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");
|
||||
|
||||
addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||
"@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");
|
||||
|
||||
addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||
"@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");
|
||||
|
||||
addFieldV("moveStuckTestDelay", TypeRangedS32, Offset(mMoveStuckTestDelay, AIControllerData), &CommonValidators::PositiveInt,
|
||||
"@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");
|
||||
|
||||
addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat,
|
||||
"@brief Distance considered in firing range for callback purposes.");
|
||||
|
||||
endGroup("AI");
|
||||
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
addGroup("Pathfinding");
|
||||
|
||||
addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData),
|
||||
"Allow the character to walk on dry land.");
|
||||
addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIControllerData),
|
||||
"Allow the character to use jump links.");
|
||||
addField("allowDrop", TypeBool, Offset(mLinkTypes.drop, AIControllerData),
|
||||
"Allow the character to use drop links.");
|
||||
addField("allowSwim", TypeBool, Offset(mLinkTypes.swim, AIControllerData),
|
||||
"Allow the character to move in water.");
|
||||
addField("allowLedge", TypeBool, Offset(mLinkTypes.ledge, AIControllerData),
|
||||
"Allow the character to jump ledges.");
|
||||
addField("allowClimb", TypeBool, Offset(mLinkTypes.climb, AIControllerData),
|
||||
"Allow the character to use climb links.");
|
||||
addField("allowTeleport", TypeBool, Offset(mLinkTypes.teleport, AIControllerData),
|
||||
"Allow the character to use teleporters.");
|
||||
|
||||
endGroup("Pathfinding");
|
||||
#endif // TORQUE_NAVIGATION_ENABLED
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr)
|
||||
{
|
||||
Player* po = dynamic_cast<Player*>(obj->getAIInfo()->mObj.getPointer());
|
||||
if (!po) return;//not a player
|
||||
|
||||
if (obj->getAim()->mObj || obj->getAim()->mPosSet || obj->mMovement.mMoveState != AIController::ModeStop)
|
||||
{
|
||||
// Next do pitch.
|
||||
if (!obj->getAim()->mObj && !obj->getAim()->mPosSet)
|
||||
{
|
||||
// Level out if were just looking at our next way point.
|
||||
Point3F headRotation = po->getHeadRotation();
|
||||
movePtr->pitch = -headRotation.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 xDiff = obj->mMovement.mAimLocation.x - location.x;
|
||||
F32 yDiff = obj->mMovement.mAimLocation.y - location.y;
|
||||
// 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 = obj->mMovement.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 = po->getHeadRotation();
|
||||
movePtr->pitch = newPitch - headRotation.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Level out if we're not doing anything else
|
||||
Point3F headRotation = po->getHeadRotation();
|
||||
movePtr->pitch = -headRotation.x;
|
||||
}
|
||||
}
|
||||
#endif //_AICONTROLLER_H_
|
||||
155
Engine/source/T3D/AI/AIController.h
Normal file
155
Engine/source/T3D/AI/AIController.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _AICONTROLLER_H_
|
||||
#define _AICONTROLLER_H_
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
#include "navigation/coverPoint.h"
|
||||
#include "AIInfo.h"
|
||||
#include "AIGoal.h"
|
||||
#include "AIAimTarget.h"
|
||||
#include "AICover.h"
|
||||
#include "AINavigation.h"
|
||||
class AIControllerData;
|
||||
class AIController;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class AIController : public SimObject {
|
||||
|
||||
typedef SimObject Parent;
|
||||
|
||||
public:
|
||||
AIControllerData* mControllerData;
|
||||
protected:
|
||||
static bool setControllerDataProperty(void* object, const char* index, const char* data);
|
||||
public:
|
||||
enum MoveState {
|
||||
ModeStop, // AI has stopped moving.
|
||||
ModeMove, // AI is currently moving.
|
||||
ModeStuck, // AI is stuck, but wants to move.
|
||||
ModeSlowing, // AI is slowing down as it reaches it's destination.
|
||||
};
|
||||
|
||||
private:
|
||||
AIInfo*mAIInfo;
|
||||
public:
|
||||
void setAIInfo(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f) { delete(mAIInfo); mAIInfo = new AIInfo(this, objIn, rad); }
|
||||
AIInfo* getAIInfo() { return mAIInfo; }
|
||||
private:
|
||||
AIGoal* mGoal;
|
||||
public:
|
||||
void setGoal(AIInfo* targ) { mGoal = (targ) ? new AIGoal(this, targ->getPosition(), targ->mRadius) : NULL; }
|
||||
void setGoal(Point3F loc, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, loc, rad); }
|
||||
void setGoal(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, objIn, rad); }
|
||||
AIGoal* getGoal() { return mGoal; }
|
||||
void clearGoal() { SAFE_DELETE(mGoal); }
|
||||
private:
|
||||
AIAimTarget* mAimTarget;
|
||||
public:
|
||||
void setAim(Point3F loc, F32 rad = 0.0f, Point3F offset = Point3F(0.0f,0.0f,0.0f)) { delete(mAimTarget); mAimTarget = new AIAimTarget(this, loc, rad); mAimTarget->mAimOffset = offset; }
|
||||
void setAim(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)) { delete(mAimTarget); mAimTarget = new AIAimTarget(this, objIn, rad); mAimTarget->mAimOffset = offset; }
|
||||
AIAimTarget* getAim() { return mAimTarget; }
|
||||
void clearAim() { SAFE_DELETE(mAimTarget); }
|
||||
private:
|
||||
AICover* mCover;
|
||||
public:
|
||||
void setCover(Point3F loc, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, loc, rad); }
|
||||
void setCover(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, objIn, rad); }
|
||||
AICover* getCover() { return mCover; }
|
||||
bool findCover(const Point3F& from, F32 radius);
|
||||
void clearCover();
|
||||
|
||||
// Utility Methods
|
||||
void throwCallback(const char* name);
|
||||
AINavigation* mNav;
|
||||
AINavigation* getNav() { return mNav; };
|
||||
struct Movement
|
||||
{
|
||||
MoveState mMoveState;
|
||||
F32 mMoveSpeed = 1.0;
|
||||
bool mMoveSlowdown; // Slowdown as we near the destination
|
||||
Point3F mLastLocation; // For stuck check
|
||||
S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck
|
||||
Point3F mAimLocation;
|
||||
Point3F mMoveDestination;
|
||||
// move triggers
|
||||
bool mMoveTriggers[MaxTriggerKeys];
|
||||
void stopMove();
|
||||
void onStuck();
|
||||
} mMovement;
|
||||
|
||||
struct TriggerState
|
||||
{
|
||||
// Trigger sets/gets
|
||||
void setMoveTrigger(U32 slot, const bool isSet = true);
|
||||
bool getMoveTrigger(U32 slot) const;
|
||||
void clearMoveTriggers();
|
||||
} mTriggerState;
|
||||
bool getAIMove(Move* move);
|
||||
|
||||
static void initPersistFields();
|
||||
AIController()
|
||||
{
|
||||
mControllerData = NULL;
|
||||
mAIInfo = new AIInfo(this);
|
||||
mGoal = new AIGoal(this);
|
||||
mAimTarget = new AIAimTarget(this);
|
||||
mCover = new AICover(this);
|
||||
mNav = new AINavigation(this);
|
||||
};
|
||||
|
||||
DECLARE_CONOBJECT(AIController);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class AIControllerData : public SimDataBlock {
|
||||
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
AIControllerData() {};
|
||||
~AIControllerData() {};
|
||||
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT(AIControllerData);
|
||||
|
||||
F32 mMoveTolerance; // Distance from destination point before we stop
|
||||
F32 mFollowTolerance; // Distance from destination object before we stop
|
||||
F32 mAttackRadius; // Distance to trigger weaponry calcs
|
||||
F32 mMoveStuckTolerance; // Distance tolerance on stuck check
|
||||
S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck
|
||||
/// Types of link we can use.
|
||||
LinkData mLinkTypes;
|
||||
|
||||
void resolveYaw(AIController* obj, Point3F location, Move* movePtr);
|
||||
void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {};
|
||||
void resolveRoll(AIController* obj, Point3F location, Move* movePtr);
|
||||
void resolveSpeed(AIController* obj, Point3F location, Move* movePtr);
|
||||
void resolveStuck(AIController* obj);
|
||||
};
|
||||
|
||||
class AIPlayerControllerData : AIControllerData
|
||||
{
|
||||
void resolvePitch(AIController* obj, Point3F location, Move* movePtr);
|
||||
};
|
||||
#endif // TORQUE_NAVIGATION_ENABLED
|
||||
#endif //_AICONTROLLER_H_
|
||||
21
Engine/source/T3D/AI/AICover.cpp
Normal file
21
Engine/source/T3D/AI/AICover.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
37
Engine/source/T3D/AI/AICover.h
Normal file
37
Engine/source/T3D/AI/AICover.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _AICOVER_H_
|
||||
#define _AICOVER_H_
|
||||
|
||||
#include "AIInfo.h"
|
||||
|
||||
struct AICover : AIInfo
|
||||
{
|
||||
typedef AIInfo Parent;
|
||||
/// Pointer to a cover point.
|
||||
SimObjectPtr<CoverPoint> mCoverPoint;
|
||||
AICover(AIController* controller) : Parent(controller) {};
|
||||
AICover(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn) : Parent(controller, objIn, radIn) {};
|
||||
AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {};
|
||||
};
|
||||
|
||||
#endif
|
||||
24
Engine/source/T3D/AI/AIGoal.cpp
Normal file
24
Engine/source/T3D/AI/AIGoal.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "AIGoal.h"
|
||||
#include "AIController.h"
|
||||
34
Engine/source/T3D/AI/AIGoal.h
Normal file
34
Engine/source/T3D/AI/AIGoal.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _AIGOAL_H_
|
||||
#define _AIGOAL_H_
|
||||
|
||||
#include "AIInfo.h"
|
||||
|
||||
struct AIGoal : AIInfo
|
||||
{
|
||||
typedef AIInfo Parent;
|
||||
AIGoal(AIController* controller): Parent(controller) {};
|
||||
AIGoal(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn) : Parent(controller, objIn, radIn) {};
|
||||
AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {};
|
||||
};
|
||||
#endif
|
||||
59
Engine/source/T3D/AI/AIInfo.cpp
Normal file
59
Engine/source/T3D/AI/AIInfo.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "AIInfo.h"
|
||||
#include "AIController.h"
|
||||
|
||||
AIInfo::AIInfo(AIController* controller)
|
||||
{
|
||||
mControllerRef = controller;
|
||||
mObj = NULL;
|
||||
mPosition = mLastPos = Point3F(0.0f, 0.0f, 0.0f);
|
||||
mRadius = 0.0f;
|
||||
mPosSet = false;
|
||||
};
|
||||
|
||||
AIInfo::AIInfo(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn)
|
||||
{
|
||||
mControllerRef = controller;
|
||||
mObj = objIn;
|
||||
mPosition = mLastPos = objIn->getPosition();
|
||||
mRadius = radIn;
|
||||
mPosSet = false;
|
||||
};
|
||||
|
||||
AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn)
|
||||
{
|
||||
mControllerRef = controller;
|
||||
mObj = NULL;
|
||||
mPosition = mLastPos = pointIn;
|
||||
mRadius = radIn;
|
||||
mPosSet = true;
|
||||
};
|
||||
|
||||
F32 AIInfo::getDist()
|
||||
{
|
||||
AIInfo* controlObj = getCtrl()->getAIInfo();
|
||||
F32 ret = VectorF(controlObj->mObj->getPosition() - getPosition()).len();
|
||||
ret -= controlObj->mRadius + mRadius;
|
||||
return ret;
|
||||
}
|
||||
46
Engine/source/T3D/AI/AIInfo.h
Normal file
46
Engine/source/T3D/AI/AIInfo.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _AIINFO_H_
|
||||
#define _AIINFO_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "T3D/shapeBase.h"
|
||||
#endif
|
||||
|
||||
class AIController;
|
||||
struct AIInfo
|
||||
{
|
||||
AIController* mControllerRef;
|
||||
AIController* getCtrl() { return mControllerRef; };
|
||||
void setCtrl(AIController* controller) { mControllerRef = controller; };
|
||||
SimObjectPtr<SceneObject> mObj;
|
||||
Point3F mPosition, mLastPos;
|
||||
bool mPosSet;
|
||||
F32 mRadius;
|
||||
Point3F getPosition() { return (mObj) ? mObj->getPosition() : mPosition; }
|
||||
F32 getDist();
|
||||
AIInfo() = delete;
|
||||
AIInfo(AIController* controller);
|
||||
AIInfo(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn = 0.0f);
|
||||
AIInfo(AIController* controller,Point3F pointIn, F32 radIn = 0.0f);
|
||||
};
|
||||
#endif
|
||||
259
Engine/source/T3D/AI/AINavigation.cpp
Normal file
259
Engine/source/T3D/AI/AINavigation.cpp
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "AINavigation.h"
|
||||
#include "AIController.h"
|
||||
|
||||
AINavigation::AINavigation(AIController* controller)
|
||||
{
|
||||
mControllerRef = controller;
|
||||
}
|
||||
|
||||
NavMesh* AINavigation::findNavMesh() const
|
||||
{
|
||||
GameBase* gbo = dynamic_cast<GameBase*>(mControllerRef->getAIInfo()->mObj.getPointer());
|
||||
// Search for NavMeshes that contain us entirely with the smallest possible
|
||||
// volume.
|
||||
NavMesh* mesh = NULL;
|
||||
SimSet* set = NavMesh::getServerSet();
|
||||
for (U32 i = 0; i < set->size(); i++)
|
||||
{
|
||||
NavMesh* m = static_cast<NavMesh*>(set->at(i));
|
||||
if (m->getWorldBox().isContained(gbo->getWorldBox()))
|
||||
{
|
||||
if (!mesh || m->getWorldBox().getVolume() < mesh->getWorldBox().getVolume())
|
||||
mesh = m;
|
||||
}
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void AINavigation::updateNavMesh()
|
||||
{
|
||||
GameBase* gbo = dynamic_cast<GameBase*>(mControllerRef->getAIInfo()->mObj.getPointer());
|
||||
NavMesh* old = mNavMesh;
|
||||
if (mNavMesh.isNull())
|
||||
mNavMesh = findNavMesh();
|
||||
else
|
||||
{
|
||||
if (!mNavMesh->getWorldBox().isContained(gbo->getWorldBox()))
|
||||
mNavMesh = findNavMesh();
|
||||
}
|
||||
// See if we need to update our path.
|
||||
if (mNavMesh != old && !mPathData.path.isNull())
|
||||
{
|
||||
setPathDestination(mPathData.path->mTo);
|
||||
}
|
||||
}
|
||||
|
||||
void AINavigation::moveToNode(S32 node)
|
||||
{
|
||||
if (mPathData.path.isNull())
|
||||
return;
|
||||
|
||||
// -1 is shorthand for 'last path node'.
|
||||
if (node == -1)
|
||||
node = mPathData.path->size() - 1;
|
||||
|
||||
// Consider slowing down on the last path node.
|
||||
setMoveDestination(mPathData.path->getNode(node), false);
|
||||
|
||||
// Check flags for this segment.
|
||||
if (mPathData.index)
|
||||
{
|
||||
U16 flags = mPathData.path->getFlags(node - 1);
|
||||
// Jump if we must.
|
||||
if (flags & LedgeFlag)
|
||||
mJump = Ledge;
|
||||
else if (flags & JumpFlag)
|
||||
mJump = Now;
|
||||
else
|
||||
// Catch pathing errors.
|
||||
mJump = None;
|
||||
}
|
||||
|
||||
// Store current index.
|
||||
mPathData.index = node;
|
||||
}
|
||||
|
||||
void AINavigation::repath()
|
||||
{
|
||||
// Ineffectual if we don't have a path, or are using someone else's.
|
||||
if (mPathData.path.isNull() || !mPathData.owned)
|
||||
return;
|
||||
|
||||
// If we're following, get their position.
|
||||
mPathData.path->mTo = mControllerRef->getGoal()->getPosition();
|
||||
// Update from position and replan.
|
||||
mPathData.path->mFrom = mControllerRef->getAIInfo()->getPosition();
|
||||
mPathData.path->plan();
|
||||
// Move to first node (skip start pos).
|
||||
moveToNode(1);
|
||||
}
|
||||
|
||||
Point3F AINavigation::getPathDestination() const
|
||||
{
|
||||
if (!mPathData.path.isNull())
|
||||
return mPathData.path->mTo;
|
||||
return Point3F(0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
void AINavigation::setMoveDestination(const Point3F& location, bool slowdown)
|
||||
{
|
||||
mMoveDestination = location;
|
||||
mControllerRef->mMovement.mMoveState = AIController::ModeMove;
|
||||
mControllerRef->mMovement.mMoveSlowdown = slowdown;
|
||||
mControllerRef->mMovement.mMoveStuckTestCountdown = mControllerRef->mControllerData->mMoveStuckTestDelay;
|
||||
}
|
||||
|
||||
void AINavigation::onReachDestination()
|
||||
{
|
||||
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
if (!getPath().isNull())
|
||||
{
|
||||
if (mPathData.index == getPath()->size() - 1)
|
||||
{
|
||||
// Handle looping paths.
|
||||
if (getPath()->mIsLooping)
|
||||
moveToNode(0);
|
||||
// Otherwise end path.
|
||||
else
|
||||
{
|
||||
clearPath();
|
||||
getCtrl()->throwCallback("onReachDestination");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
moveToNode(mPathData.index + 1);
|
||||
// Throw callback every time if we're on a looping path.
|
||||
//if(mPathData.path->mIsLooping)
|
||||
//throwCallback("onReachDestination");
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
getCtrl()->throwCallback("onReachDestination");
|
||||
}
|
||||
|
||||
bool AINavigation::setPathDestination(const Point3F& pos)
|
||||
{
|
||||
if (!mNavMesh)
|
||||
updateNavMesh();
|
||||
// If we can't find a mesh, just move regularly.
|
||||
if (!mNavMesh)
|
||||
{
|
||||
//setMoveDestination(pos);
|
||||
mControllerRef->throwCallback("onPathFailed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new path.
|
||||
NavPath* path = new NavPath();
|
||||
|
||||
path->mMesh = mNavMesh;
|
||||
path->mFrom = mControllerRef->getAIInfo()->getPosition();
|
||||
path->mTo = pos;
|
||||
path->mFromSet = path->mToSet = true;
|
||||
path->mAlwaysRender = true;
|
||||
path->mLinkTypes = mControllerRef->mControllerData->mLinkTypes;
|
||||
path->mXray = true;
|
||||
// Paths plan automatically upon being registered.
|
||||
if (!path->registerObject())
|
||||
{
|
||||
delete path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path->success())
|
||||
{
|
||||
// Clear any current path we might have.
|
||||
clearPath();
|
||||
mControllerRef->clearCover();
|
||||
clearFollow();
|
||||
// Store new path.
|
||||
mPathData.path = path;
|
||||
mPathData.owned = true;
|
||||
// Skip node 0, which we are currently standing on.
|
||||
moveToNode(1);
|
||||
mControllerRef->throwCallback("onPathSuccess");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just move normally if we can't path.
|
||||
//setMoveDestination(pos, true);
|
||||
//return;
|
||||
mControllerRef->throwCallback("onPathFailed");
|
||||
path->deleteObject();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AINavigation::followObject(AIInfo* targ)
|
||||
{
|
||||
if (!targ) return;
|
||||
|
||||
if (targ->getDist() < mControllerRef->mControllerData->mMoveTolerance)
|
||||
return;
|
||||
|
||||
if (setPathDestination(targ->getPosition()))
|
||||
{
|
||||
mControllerRef->clearCover();
|
||||
mControllerRef->setGoal(targ);
|
||||
}
|
||||
}
|
||||
|
||||
void AINavigation::followObject(SceneObject* obj, F32 radius)
|
||||
{
|
||||
mControllerRef->setGoal(obj, radius);
|
||||
followObject(mControllerRef->getGoal());
|
||||
}
|
||||
|
||||
void AINavigation::clearFollow()
|
||||
{
|
||||
mControllerRef->clearGoal();
|
||||
}
|
||||
|
||||
void AINavigation::followNavPath(NavPath* path)
|
||||
{
|
||||
// Get rid of our current path.
|
||||
clearPath();
|
||||
mControllerRef->clearCover();
|
||||
clearFollow();
|
||||
|
||||
// Follow new path.
|
||||
mPathData.path = path;
|
||||
mPathData.owned = false;
|
||||
// Start from 0 since we might not already be there.
|
||||
moveToNode(0);
|
||||
}
|
||||
|
||||
void AINavigation::clearPath()
|
||||
{
|
||||
// Only delete if we own the path.
|
||||
if (!mPathData.path.isNull() && mPathData.owned)
|
||||
mPathData.path->deleteObject();
|
||||
// Reset path data.
|
||||
mPathData = PathData();
|
||||
}
|
||||
91
Engine/source/T3D/AI/AINavigation.h
Normal file
91
Engine/source/T3D/AI/AINavigation.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _AINAVIGATION_H_
|
||||
#define _AINAVIGATION_H_
|
||||
|
||||
#include "AIInfo.h"
|
||||
|
||||
#include "navigation/navPath.h"
|
||||
#include "navigation/navMesh.h"
|
||||
|
||||
class AIController;
|
||||
struct AINavigation
|
||||
{
|
||||
AIController* mControllerRef;
|
||||
AIController* getCtrl() { return mControllerRef; };
|
||||
|
||||
AINavigation() = delete;
|
||||
AINavigation(AIController* controller);
|
||||
|
||||
/// Stores information about a path.
|
||||
struct PathData {
|
||||
/// Pointer to path object.
|
||||
SimObjectPtr<NavPath> path;
|
||||
/// Do we own our path? If so, we will delete it when finished.
|
||||
bool owned;
|
||||
/// Path node we're at.
|
||||
U32 index;
|
||||
/// Default constructor.
|
||||
PathData() : path(NULL)
|
||||
{
|
||||
owned = false;
|
||||
index = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// Should we jump?
|
||||
enum JumpStates {
|
||||
None, ///< No, don't jump.
|
||||
Now, ///< Jump immediately.
|
||||
Ledge, ///< Jump when we walk off a ledge.
|
||||
};
|
||||
|
||||
Point3F mMoveDestination;
|
||||
void setMoveDestination(const Point3F& location, bool slowdown);
|
||||
void onReachDestination();
|
||||
|
||||
/// NavMesh we pathfind on.
|
||||
SimObjectPtr<NavMesh> mNavMesh;
|
||||
NavMesh* findNavMesh() const;
|
||||
void updateNavMesh();
|
||||
PathData mPathData;
|
||||
JumpStates mJump;
|
||||
|
||||
/// Clear out the current path.
|
||||
void clearPath();
|
||||
bool setPathDestination(const Point3F& pos);
|
||||
Point3F getPathDestination() const;
|
||||
void repath();
|
||||
|
||||
/// Get the current path we're following.
|
||||
SimObjectPtr<NavPath> getPath() { return mPathData.path; };
|
||||
void followNavPath(NavPath* path);
|
||||
|
||||
void followObject(AIInfo* targ);
|
||||
void followObject(SceneObject* obj, F32 radius);
|
||||
void clearFollow();
|
||||
/// Move to the specified node in the current path.
|
||||
void moveToNode(S32 node);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -460,6 +460,7 @@ PlayerData::PlayerData()
|
|||
jumpTowardsNormal = true;
|
||||
|
||||
physicsPlayerType = StringTable->EmptyString();
|
||||
mControlMap = StringTable->EmptyString();
|
||||
|
||||
dMemset( actionList, 0, sizeof(actionList) );
|
||||
}
|
||||
|
|
@ -739,7 +740,9 @@ void PlayerData::initPersistFields()
|
|||
endGroup( "Camera" );
|
||||
|
||||
addGroup( "Movement" );
|
||||
|
||||
addField("controlMap", TypeString, Offset(mControlMap, PlayerData),
|
||||
"@brief movemap used by these types of objects.\n\n");
|
||||
|
||||
addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat,
|
||||
"@brief Maximum height the player can step up.\n\n"
|
||||
"The player will automatically step onto changes in ground height less "
|
||||
|
|
@ -1738,7 +1741,7 @@ bool Player::onAdd()
|
|||
world );
|
||||
mPhysicsRep->setTransform( getTransform() );
|
||||
}
|
||||
|
||||
mAIController = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2256,8 +2259,36 @@ void Player::advanceTime(F32 dt)
|
|||
}
|
||||
}
|
||||
|
||||
bool Player::setAIController(const char* controller)
|
||||
{
|
||||
if (Sim::findObject(controller, mAIController))
|
||||
{
|
||||
mAIController->setAIInfo(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
mAIController = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
DefineEngineMethod(Player, setAIController, bool, (const char* controller), , "")
|
||||
{
|
||||
return object->setAIController(controller);
|
||||
}
|
||||
|
||||
DefineEngineMethod(Player, getAIController, AIController*, (), , "")
|
||||
{
|
||||
return object->getAIController();
|
||||
}
|
||||
|
||||
|
||||
bool Player::getAIMove(Move* move)
|
||||
{
|
||||
if (mAIController)
|
||||
{
|
||||
mAIController->getAIMove(move); //actual result
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@ class Player;
|
|||
class OpenVRTrackedObject;
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||
#include "navigation/navPath.h"
|
||||
#include "navigation/navMesh.h"
|
||||
#include "navigation/coverPoint.h"
|
||||
#endif // TORQUE_NAVIGATION_ENABLED
|
||||
#include "AI/AIController.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct PlayerData: public ShapeBaseData {
|
||||
|
|
@ -346,7 +353,8 @@ struct PlayerData: public ShapeBaseData {
|
|||
|
||||
// Jump off surfaces at their normal rather than straight up
|
||||
bool jumpTowardsNormal;
|
||||
|
||||
StringTableEntry mControlMap;
|
||||
AIControllerData* mAIControllData;
|
||||
// For use if/when mPhysicsPlayer is created
|
||||
StringTableEntry physicsPlayerType;
|
||||
|
||||
|
|
@ -488,6 +496,7 @@ protected:
|
|||
|
||||
SimObjectPtr<ShapeBase> mControlObject; ///< Controlling object
|
||||
|
||||
AIController* mAIController;
|
||||
/// @name Animation threads & data
|
||||
/// @{
|
||||
|
||||
|
|
@ -754,6 +763,8 @@ public:
|
|||
void setMomentum(const Point3F &momentum) override;
|
||||
bool displaceObject(const Point3F& displaceVector) override;
|
||||
virtual bool getAIMove(Move*);
|
||||
bool setAIController(const char* controller);
|
||||
AIController* getAIController() { return mAIController; };
|
||||
|
||||
bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here?
|
||||
|
||||
|
|
|
|||
|
|
@ -1895,7 +1895,6 @@ public:
|
|||
void registerCollisionCallback(CollisionEventCallback*);
|
||||
void unregisterCollisionCallback(CollisionEventCallback*);
|
||||
|
||||
protected:
|
||||
enum {
|
||||
ANIM_OVERRIDDEN = BIT(0),
|
||||
BLOCK_USER_CONTROL = BIT(1),
|
||||
|
|
@ -1903,6 +1902,8 @@ protected:
|
|||
BAD_ANIM_ID = 999999999,
|
||||
BLENDED_CLIP = 0x80000000,
|
||||
};
|
||||
U8 anim_clip_flags;
|
||||
protected:
|
||||
struct BlendThread
|
||||
{
|
||||
TSThread* thread;
|
||||
|
|
@ -1910,7 +1911,6 @@ protected:
|
|||
};
|
||||
Vector<BlendThread> blend_clips;
|
||||
static U32 unique_anim_tag_counter;
|
||||
U8 anim_clip_flags;
|
||||
S32 last_anim_id;
|
||||
U32 last_anim_tag;
|
||||
U32 last_anim_lock_tag;
|
||||
|
|
|
|||
Loading…
Reference in a new issue