generalized ai subsystem wipwork

This commit is contained in:
AzaezelX 2025-04-15 15:12:27 -05:00
parent 75e23e85ea
commit 8c663a19a5
16 changed files with 1320 additions and 6 deletions

View file

@ -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"

View 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;
}

View 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

View 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_

View 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_

View 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.
//-----------------------------------------------------------------------------

View 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

View 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"

View 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

View 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;
}

View 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

View 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();
}

View 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

View file

@ -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;
}

View file

@ -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?

View file

@ -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;