From 8c663a19a516a20da52c415c27f32625f92fbf20 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 15:12:27 -0500 Subject: [PATCH 01/47] generalized ai subsystem wipwork --- Engine/source/CMakeLists.txt | 2 +- Engine/source/T3D/AI/AIAimTarget.cpp | 77 +++++ Engine/source/T3D/AI/AIAimTarget.h | 39 +++ Engine/source/T3D/AI/AIController.cpp | 430 ++++++++++++++++++++++++++ Engine/source/T3D/AI/AIController.h | 155 ++++++++++ Engine/source/T3D/AI/AICover.cpp | 21 ++ Engine/source/T3D/AI/AICover.h | 37 +++ Engine/source/T3D/AI/AIGoal.cpp | 24 ++ Engine/source/T3D/AI/AIGoal.h | 34 ++ Engine/source/T3D/AI/AIInfo.cpp | 59 ++++ Engine/source/T3D/AI/AIInfo.h | 46 +++ Engine/source/T3D/AI/AINavigation.cpp | 259 ++++++++++++++++ Engine/source/T3D/AI/AINavigation.h | 91 ++++++ Engine/source/T3D/player.cpp | 35 ++- Engine/source/T3D/player.h | 13 +- Engine/source/T3D/shapeBase.h | 4 +- 16 files changed, 1320 insertions(+), 6 deletions(-) create mode 100644 Engine/source/T3D/AI/AIAimTarget.cpp create mode 100644 Engine/source/T3D/AI/AIAimTarget.h create mode 100644 Engine/source/T3D/AI/AIController.cpp create mode 100644 Engine/source/T3D/AI/AIController.h create mode 100644 Engine/source/T3D/AI/AICover.cpp create mode 100644 Engine/source/T3D/AI/AICover.h create mode 100644 Engine/source/T3D/AI/AIGoal.cpp create mode 100644 Engine/source/T3D/AI/AIGoal.h create mode 100644 Engine/source/T3D/AI/AIInfo.cpp create mode 100644 Engine/source/T3D/AI/AIInfo.h create mode 100644 Engine/source/T3D/AI/AINavigation.cpp create mode 100644 Engine/source/T3D/AI/AINavigation.h diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index 35d9d81d7..34991cce7 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -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" diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp new file mode 100644 index 000000000..907b2da3d --- /dev/null +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -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(getCtrl()->getAIInfo()->mObj.getPointer()); + if (!target) + { + target = dynamic_cast(mObj.getPointer()); + if (!target) + return false; + } + if (_checkEnabled) + { + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* shapeBaseCheck = static_cast(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; +} diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h new file mode 100644 index 000000000..650a4f9b8 --- /dev/null +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -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 objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; +}; + +#endif diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp new file mode 100644 index 000000000..ba14b2c34 --- /dev/null +++ b/Engine/source/T3D/AI/AIController.cpp @@ -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(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(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(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(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(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(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_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h new file mode 100644 index 000000000..69ebbe27f --- /dev/null +++ b/Engine/source/T3D/AI/AIController.h @@ -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 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 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 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 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_ diff --git a/Engine/source/T3D/AI/AICover.cpp b/Engine/source/T3D/AI/AICover.cpp new file mode 100644 index 000000000..509b0f0c0 --- /dev/null +++ b/Engine/source/T3D/AI/AICover.cpp @@ -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. +//----------------------------------------------------------------------------- diff --git a/Engine/source/T3D/AI/AICover.h b/Engine/source/T3D/AI/AICover.h new file mode 100644 index 000000000..d9d1cb638 --- /dev/null +++ b/Engine/source/T3D/AI/AICover.h @@ -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 mCoverPoint; + AICover(AIController* controller) : Parent(controller) {}; + AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; +}; + +#endif diff --git a/Engine/source/T3D/AI/AIGoal.cpp b/Engine/source/T3D/AI/AIGoal.cpp new file mode 100644 index 000000000..e94fdd8ee --- /dev/null +++ b/Engine/source/T3D/AI/AIGoal.cpp @@ -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" diff --git a/Engine/source/T3D/AI/AIGoal.h b/Engine/source/T3D/AI/AIGoal.h new file mode 100644 index 000000000..e169e489d --- /dev/null +++ b/Engine/source/T3D/AI/AIGoal.h @@ -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 objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; +}; +#endif diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp new file mode 100644 index 000000000..3ce55d86c --- /dev/null +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -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 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; +} diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h new file mode 100644 index 000000000..db0dc191d --- /dev/null +++ b/Engine/source/T3D/AI/AIInfo.h @@ -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 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 objIn, F32 radIn = 0.0f); + AIInfo(AIController* controller,Point3F pointIn, F32 radIn = 0.0f); +}; +#endif diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp new file mode 100644 index 000000000..18efeb3da --- /dev/null +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -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(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(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(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(); +} diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h new file mode 100644 index 000000000..130305eea --- /dev/null +++ b/Engine/source/T3D/AI/AINavigation.h @@ -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 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 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 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 diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 801df58df..cd32ea024 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -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; } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index b80b3f0e5..78367edab 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -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 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? diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 706f54b72..66a48fc29 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -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 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; From 19e73c0be29d20cc44baed6f2df6c03d24d142b4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 15:34:58 -0500 Subject: [PATCH 02/47] expose the AIPlayerControllerData subtype to console --- Engine/source/T3D/AI/AIController.cpp | 1 + Engine/source/T3D/AI/AIController.h | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index ba14b2c34..deb3450b9 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -389,6 +389,7 @@ void AIControllerData::initPersistFields() //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +IMPLEMENT_CO_DATABLOCK_V1(AIPlayerControllerData); void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) { Player* po = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 69ebbe27f..8d780c362 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -147,9 +147,13 @@ public: void resolveStuck(AIController* obj); }; -class AIPlayerControllerData : AIControllerData +class AIPlayerControllerData : public AIControllerData { + typedef AIControllerData Parent; + +public: void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIPlayerControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ From 201b7bf695586709d77a4e77b9f52be0f9eed910 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 17:00:36 -0500 Subject: [PATCH 03/47] aiController.setMoveDestination test --- Engine/source/T3D/AI/AIController.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 16 ++++++++++++++++ Engine/source/T3D/player.cpp | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 8d780c362..db13b6ee4 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -126,7 +126,7 @@ class AIControllerData : public SimDataBlock { public: - AIControllerData() {}; + AIControllerData() { mMoveTolerance = 0.25; mFollowTolerance = 1.0; mAttackRadius = 2.0; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30;}; ~AIControllerData() {}; static void initPersistFields(); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 18efeb3da..ed0fce56f 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -257,3 +257,19 @@ void AINavigation::clearPath() // Reset path data. mPathData = PathData(); } + +DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), + "@brief Tells the AI to move to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@param slowDown A boolean value. If set to true, the bot will slow down " + "when it gets within 5-meters of its move destination. If false, the bot " + "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" + + "@note Upon reaching a move destination, the bot will clear its move destination and " + "calls to getMoveDestination will return \"0 0 0\"." + + "@see getMoveDestination()\n") +{ + object->getNav()->setMoveDestination(goal, slowDown); +} diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index cd32ea024..4552e5618 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2287,6 +2287,7 @@ bool Player::getAIMove(Move* move) if (mAIController) { mAIController->getAIMove(move); //actual result + return true; } return false; From 2fe36a571b937af9d902c0131c45ff331f74be54 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 18:01:18 -0500 Subject: [PATCH 04/47] setaicontroller: use Ids get rid of duplicated mMoveDestination --- Engine/source/T3D/AI/AIController.cpp | 18 +++++++++--------- Engine/source/T3D/AI/AIController.h | 1 - Engine/source/T3D/AI/AINavigation.cpp | 1 + Engine/source/T3D/player.cpp | 4 ++-- Engine/source/T3D/player.h | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index deb3450b9..3197f3579 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -117,7 +117,7 @@ bool AIController::getAIMove(Move* movePtr) if (getAim()->mObj || getAim()->mPosSet) mMovement.mAimLocation = getAim()->getPosition(); else - mMovement.mAimLocation = mMovement.mMoveDestination; + mMovement.mAimLocation = getNav()->mMoveDestination; mControllerData->resolveYaw(this, location, movePtr); mControllerData->resolvePitch(this, location, movePtr); @@ -232,8 +232,8 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m // 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; + F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; + F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); // Check if we should mMove, or if we are 'close enough' @@ -246,22 +246,22 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m { // Build move direction in world space if (mIsZero(xDiff)) - movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) - movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->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; + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; + movePtr->x = (location.x > obj->getNav()->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; + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index db13b6ee4..bc5ad3a33 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -89,7 +89,6 @@ public: 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(); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index ed0fce56f..d9f4bb3ea 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -25,6 +25,7 @@ AINavigation::AINavigation(AIController* controller) { mControllerRef = controller; + mJump = None; } NavMesh* AINavigation::findNavMesh() const diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 4552e5618..42e8a9d5f 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2259,7 +2259,7 @@ void Player::advanceTime(F32 dt) } } -bool Player::setAIController(const char* controller) +bool Player::setAIController(S32 controller) { if (Sim::findObject(controller, mAIController)) { @@ -2271,7 +2271,7 @@ bool Player::setAIController(const char* controller) return false; } -DefineEngineMethod(Player, setAIController, bool, (const char* controller), , "") +DefineEngineMethod(Player, setAIController, bool, (S32 controller), , "") { return object->setAIController(controller); } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index 78367edab..752ea9ca1 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -763,7 +763,7 @@ public: void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; virtual bool getAIMove(Move*); - bool setAIController(const char* controller); + bool setAIController(S32 controller); AIController* getAIController() { return mAIController; }; bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? From f00b8e1ae0d174c314e47083747176300c887989 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 18:23:12 -0500 Subject: [PATCH 05/47] toEuler. n ot getforwardvector --- Engine/source/T3D/AI/AIController.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 3197f3579..49d3e2a76 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -75,7 +75,7 @@ bool AIController::getAIMove(Move* movePtr) MatrixF eye; sbo->getEyeTransform(&eye); Point3F location = eye.getPosition(); - Point3F rotation = sbo->getTransform().getForwardVector(); + Point3F rotation = sbo->getTransform().toEuler(); #ifdef TORQUE_NAVIGATION_ENABLED if (sbo->getDamageState() == ShapeBase::Enabled) @@ -190,7 +190,7 @@ void AIControllerData::resolveYaw(AIController* obj, Point3F location, Move* mov { F32 xDiff = obj->mMovement.mAimLocation.x - location.x; F32 yDiff = obj->mMovement.mAimLocation.y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); if (!mIsZero(xDiff) || !mIsZero(yDiff)) { @@ -234,7 +234,7 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m { F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) From f3ef698e890b41a3f7a7fe22c63b8d893b715961 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 17:27:26 -0500 Subject: [PATCH 06/47] getmovedestination, set/get speed, targetting script commands todo: need to see why getAIController().setAimLocation("10 10 0"); seems to get into an onreachdestination+stuck callback loop --- Engine/source/T3D/AI/AIAimTarget.cpp | 146 +++++++++++++++++++++++++- Engine/source/T3D/AI/AIAimTarget.h | 5 +- Engine/source/T3D/AI/AIController.cpp | 22 ++++ Engine/source/T3D/AI/AIController.h | 2 + Engine/source/T3D/AI/AINavigation.cpp | 14 +++ Engine/source/T3D/AI/AINavigation.h | 1 + 6 files changed, 187 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp index 907b2da3d..6e4b3d299 100644 --- a/Engine/source/T3D/AI/AIAimTarget.cpp +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -25,7 +25,31 @@ static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType; -bool AIAimTarget::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) +F32 AIAimTarget::getTargetDistance(SceneObject* target, bool _checkEnabled) +{ + if (!target) + { + target = mObj.getPointer(); + if (!target) + return F32_MAX; + } + + if (_checkEnabled) + { + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false; + } + else + return F32_MAX; + } + + return (getPosition() - target->getPosition()).len(); +} + +bool AIAimTarget::checkInLos(SceneObject* target, bool _useMuzzle, bool _checkEnabled) { ShapeBase* sbo = dynamic_cast(getCtrl()->getAIInfo()->mObj.getPointer()); if (!target) @@ -75,3 +99,123 @@ bool AIAimTarget::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabl } return hit; } + +bool AIAimTarget::checkInFoV(SceneObject* target, F32 camFov, bool _checkEnabled) +{ + ShapeBase* sbo = dynamic_cast(getCtrl()->getAIInfo()->mObj.getPointer()); + if (!target) + { + target = dynamic_cast(mObj.getPointer()); + if (!target) + return false; + } + if (_checkEnabled) + { + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false; + } + else + return false; + } + + MatrixF cam = sbo->getTransform(); + Point3F camPos; + VectorF camDir; + + cam.getColumn(3, &camPos); + cam.getColumn(1, &camDir); + + camFov = mDegToRad(camFov) / 2; + + Point3F shapePos = target->getBoxCenter(); + VectorF shapeDir = shapePos - camPos; + // Test to see if it's within our viewcone, this test doesn't + // actually match the viewport very well, should consider + // projection and box test. + shapeDir.normalize(); + F32 dot = mDot(shapeDir, camDir); + return (dot > mCos(camFov)); +} + +DefineEngineMethod(AIController, setAimLocation, void, (Point3F target), , + "@brief Tells the AIPlayer to aim at the location provided.\n\n" + + "@param target An \"x y z\" position in the game world to target.\n\n" + + "@see getAimLocation()\n") +{ + object->setAim(target); +} + +DefineEngineMethod(AIController, getAimLocation, Point3F, (), , + "@brief Returns the point the AIPlayer is aiming at.\n\n" + + "This will reflect the position set by setAimLocation(), " + "or the position of the object that the bot is now aiming at. " + "If the bot is not aiming at anything, this value will " + "change to whatever point the bot's current line-of-sight intercepts." + + "@return World space coordinates of the object AI is aiming at. Formatted as \"X Y Z\".\n\n" + + "@see setAimLocation()\n" + "@see setAimObject()\n") +{ + return object->getAim()->getPosition(); +} + +DefineEngineMethod(AIController, setAimObject, void, (const char* objName, Point3F offset), (Point3F::Zero), "( GameBase obj, [Point3F offset] )" + "Sets the bot's target object. Optionally set an offset from target location." + "@hide") +{ + // Find the target + SceneObject* targetObject; + if (Sim::findObject(objName, targetObject)) + { + + object->setAim(targetObject, 0.0f, offset); + } + else + object->setAim(0, 0.0f, offset); +} + +DefineEngineMethod(AIController, getAimObject, S32, (), , + "@brief Gets the object the AIPlayer is targeting.\n\n" + + "@return Returns -1 if no object is being aimed at, " + "or the SimObjectID of the object the AIPlayer is aiming at.\n\n" + + "@see setAimObject()\n") +{ + SceneObject* obj = dynamic_cast(object->getAim()->mObj.getPointer()); + return obj ? obj->getId() : -1; +} + + +DefineEngineMethod(AIController, getTargetDistance, F32, (SceneObject* obj, bool checkEnabled), (nullAsType(), false), + "@brief The distance to a given target.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") +{ + return object->getAim()->getTargetDistance(obj, checkEnabled); +} + +DefineEngineMethod(AIController, checkInLos, bool, (SceneObject* obj, bool useMuzzle, bool checkEnabled), (nullAsType(), false, false), + "@brief Check whether an object is in line of sight.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@useMuzzle Use muzzle position. Otherwise use eye position. (defaults to false).\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") +{ + return object->getAim()->checkInLos(obj, useMuzzle, checkEnabled); +} + +DefineEngineMethod(AIController, checkInFoV, bool, (SceneObject* obj, F32 fov, bool checkEnabled), (nullAsType(), 45.0f, false), + "@brief Check whether an object is within a specified veiw cone.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@fov view angle in degrees.(Defaults to 45)\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") +{ + return object->getAim()->checkInFoV(obj, fov, checkEnabled); +} diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index 650a4f9b8..f9b66a217 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -29,8 +29,9 @@ struct AIAimTarget : AIInfo 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); + bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); + bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); + F32 getTargetDistance(SceneObject* target, bool _checkEnabled); AIAimTarget(AIController* controller) : Parent(controller) {}; AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 49d3e2a76..c51518a53 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -183,6 +183,28 @@ void AIController::clearCover() SAFE_DELETE(mCover); } +DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , + "@brief Sets the move speed for an AI object.\n\n" + + "@param speed A speed multiplier between 0.0 and 1.0. " + "This is multiplied by the AIPlayer's base movement rates (as defined in " + "its PlayerData datablock)\n\n" + + "@see getMoveDestination()\n") +{ + object->mMovement.setMoveSpeed(speed); +} + +DefineEngineMethod(AIController, getMoveSpeed, F32, (), , + "@brief Gets the move speed of an AI object.\n\n" + + "@return A speed multiplier between 0.0 and 1.0.\n\n" + + "@see setMoveSpeed()\n") +{ + return object->mMovement.getMoveSpeed(); +} + //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIControllerData); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index bc5ad3a33..2f0839498 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -85,6 +85,8 @@ public: { MoveState mMoveState; F32 mMoveSpeed = 1.0; + void setMoveSpeed(F32 speed) { mMoveSpeed = speed; }; + F32 getMoveSpeed() { return mMoveSpeed; }; 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 diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index d9f4bb3ea..63491b459 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -274,3 +274,17 @@ DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool s { object->getNav()->setMoveDestination(goal, slowDown); } + + +DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , + "@brief Get the AIPlayer's current destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current move destination. If no move destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setMoveDestination()\n") +{ + return object->getNav()->getMoveDestination(); +} + diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 130305eea..2d6ce1ac3 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -61,6 +61,7 @@ struct AINavigation Point3F mMoveDestination; void setMoveDestination(const Point3F& location, bool slowdown); + Point3F getMoveDestination() { return mMoveDestination; }; void onReachDestination(); /// NavMesh we pathfind on. From 14a03dfc6c68419a358d470c118bd41b5e8cb4ea Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 17:40:53 -0500 Subject: [PATCH 07/47] if you've stopped moving on purpose, you're not stuck --- Engine/source/T3D/AI/AIController.cpp | 1 + Engine/source/T3D/AI/AINavigation.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c51518a53..e5d7a3ebe 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -322,6 +322,7 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m void AIControllerData::resolveStuck(AIController* obj) { + if (obj->mMovement.mMoveState == AIController::ModeStop) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during // an anim-clip effect override. diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 63491b459..896586ff7 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -155,6 +155,7 @@ void AINavigation::onReachDestination() else #endif getCtrl()->throwCallback("onReachDestination"); + getCtrl()->mMovement.mMoveState = AIController::ModeStop; } bool AINavigation::setPathDestination(const Point3F& pos) From e37ae27bc02a4f1506bcb260e71cd12322f38698 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 17:58:30 -0500 Subject: [PATCH 08/47] fix aim safeties to actually be safe, add an explicit clearAim script command --- Engine/source/T3D/AI/AIAimTarget.cpp | 5 +++++ Engine/source/T3D/AI/AIController.cpp | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp index 6e4b3d299..35c7d927e 100644 --- a/Engine/source/T3D/AI/AIAimTarget.cpp +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -181,6 +181,11 @@ DefineEngineMethod(AIController, setAimObject, void, (const char* objName, Point object->setAim(0, 0.0f, offset); } +DefineEngineMethod(AIController, clearAim, void, (), , "clears the bot's target.") +{ + object->clearAim(); +} + DefineEngineMethod(AIController, getAimObject, S32, (), , "@brief Gets the object the AIPlayer is targeting.\n\n" diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index e5d7a3ebe..30530a3e5 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -111,10 +111,10 @@ bool AIController::getAIMove(Move* movePtr) // Orient towards the aim point, aim object, or towards // our destination. - if (getAim()->mObj || getAim()->mPosSet || mMovement.mMoveState != ModeStop) + if (getAim() || mMovement.mMoveState != ModeStop) { // Update the aim position if we're aiming for an object or explicit position - if (getAim()->mObj || getAim()->mPosSet) + if (getAim()) mMovement.mAimLocation = getAim()->getPosition(); else mMovement.mAimLocation = getNav()->mMoveDestination; @@ -129,7 +129,7 @@ bool AIController::getAIMove(Move* movePtr) // 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) + if (getAim() && getAim()->mObj) { GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); if (getAim()->checkInLos(gbo)) From 4fb92f02a390ad5b93f00e61fcdb0561dc49bec6 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 01:27:08 -0500 Subject: [PATCH 09/47] completed list of roughly ported over scripthooks. todo: need to figure out why followobject is only hitting the first path node. likely amixup with goal handling --- Engine/source/T3D/AI/AIAimTarget.h | 2 +- Engine/source/T3D/AI/AIController.cpp | 87 +++++++++++- Engine/source/T3D/AI/AIController.h | 23 ++- Engine/source/T3D/AI/AICover.cpp | 87 ++++++++++++ Engine/source/T3D/AI/AICover.h | 5 +- Engine/source/T3D/AI/AIInfo.cpp | 2 +- Engine/source/T3D/AI/AIInfo.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 131 +++++++++++++++++- Engine/source/T3D/AI/AINavigation.h | 11 +- Engine/source/T3D/player.cpp | 4 +- Engine/source/T3D/player.h | 2 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 48 +++++-- Engine/source/navigation/guiNavEditorCtrl.h | 4 +- .../datablocks/defaultDatablocks.tscript | 5 + .../game/tools/navEditor/main.tscript | 3 +- .../game/tools/navEditor/navEditor.tscript | 10 +- 16 files changed, 386 insertions(+), 40 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index f9b66a217..4e26f8306 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -28,7 +28,7 @@ struct AIAimTarget : AIInfo typedef AIInfo Parent; Point3F mAimOffset; bool mTargetInLOS; // Is target object visible? - Point3F getPosition() { return ((mObj) ? mObj->getPosition() : mPosition) + mAimOffset; } + Point3F getPosition() { return ((mObj.isValid()) ? mObj->getPosition() : mPosition) + mAimOffset; } bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); F32 getTargetDistance(SceneObject* target, bool _checkEnabled); diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 30530a3e5..2ede9aea9 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -82,25 +82,25 @@ bool AIController::getAIMove(Move* movePtr) { if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); - if (!getGoal()->mObj.isNull()) + if (getGoal() && !getGoal()->mObj.isNull()) { if (getNav()->mPathData.path.isNull()) { - if (getGoal()->getDist() > mControllerData->mMoveTolerance) - getNav()->followObject(getGoal()); + if (getGoal()->getDist() > mControllerData->mFollowTolerance) + getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); } else { - if (getGoal()->getDist() > mControllerData->mMoveTolerance) + if (getGoal()->getDist() > mControllerData->mFollowTolerance) getNav()->repath(); - if (getAim()->getDist() < mControllerData->mMoveTolerance) + if (getGoal()->getDist() < mControllerData->mFollowTolerance) { getNav()->clearPath(); mMovement.mMoveState = ModeStop; throwCallback("onTargetInRange"); } - else if (getAim()->getDist() < mControllerData->mAttackRadius) + else if (getGoal()->getDist() < mControllerData->mAttackRadius) { throwCallback("onTargetInFiringRange"); } @@ -178,11 +178,29 @@ bool AIController::getAIMove(Move* movePtr) void AIController::clearCover() { // Notify cover that we are no longer on our way. - if (!getCover()->mCoverPoint.isNull()) + if (getCover() && !getCover()->mCoverPoint.isNull()) getCover()->mCoverPoint->setOccupied(false); SAFE_DELETE(mCover); } +void AIController::Movement::stopMove() +{ + mMoveState = ModeStop; +#ifdef TORQUE_NAVIGATION_ENABLED + mControllerRef->getNav()->clearPath(); + mControllerRef->clearCover(); + mControllerRef->getNav()->clearFollow(); +#endif +} +void AIController::Movement::onStuck() +{ + mControllerRef->throwCallback("onMoveStuck"); +#ifdef TORQUE_NAVIGATION_ENABLED + if (!mControllerRef->getNav()->getPath().isNull()) + mControllerRef->getNav()->repath(); +#endif +} + DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , "@brief Sets the move speed for an AI object.\n\n" @@ -205,6 +223,60 @@ DefineEngineMethod(AIController, getMoveSpeed, F32, (), , return object->mMovement.getMoveSpeed(); } +DefineEngineMethod(AIController, stop, void, (), , + "@brief Tells the AIPlayer to stop moving.\n\n") +{ + object->mMovement.stopMove(); +} + + +/** + * Set the state of a movement trigger. + * + * @param slot The trigger slot to set + * @param isSet set/unset the trigger + */ +void AIController::TriggerState::setMoveTrigger(U32 slot, const bool isSet) +{ + if (slot >= MaxTriggerKeys) + { + Con::errorf("Attempting to set an invalid trigger slot (%i)", slot); + } + else + { + mMoveTriggers[slot] = isSet; // set the trigger + mControllerRef->getAIInfo()->mObj->setMaskBits(ShapeBase::NoWarpMask); // force the client to updateMove + } +} + +/** + * Get the state of a movement trigger. + * + * @param slot The trigger slot to query + * @return True if the trigger is set, false if it is not set + */ +bool AIController::TriggerState::getMoveTrigger(U32 slot) const +{ + if (slot >= MaxTriggerKeys) + { + Con::errorf("Attempting to get an invalid trigger slot (%i)", slot); + return false; + } + else + { + return mMoveTriggers[slot]; + } +} + +/** + * Clear the trigger state for all movement triggers. + */ +void AIController::TriggerState::clearMoveTriggers() +{ + for (U32 i = 0; i < MaxTriggerKeys; i++) + setMoveTrigger(i, false); +} + //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIControllerData); @@ -341,6 +413,7 @@ void AIControllerData::resolveStuck(AIController* obj) if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0) { obj->mMovement.mMoveState = AIController::ModeStuck; + obj->mMovement.onStuck(); obj->throwCallback("onStuck"); } } diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 2f0839498..d64148a9e 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -29,7 +29,6 @@ #include "AICover.h" #include "AINavigation.h" class AIControllerData; -class AIController; //----------------------------------------------------------------------------- class AIController : public SimObject { @@ -83,6 +82,7 @@ public: AINavigation* getNav() { return mNav; }; struct Movement { + AIController* mControllerRef; MoveState mMoveState; F32 mMoveSpeed = 1.0; void setMoveSpeed(F32 speed) { mMoveSpeed = speed; }; @@ -99,6 +99,8 @@ public: struct TriggerState { + AIController* mControllerRef; + bool mMoveTriggers[MaxTriggerKeys]; // Trigger sets/gets void setMoveTrigger(U32 slot, const bool isSet = true); bool getMoveTrigger(U32 slot) const; @@ -109,12 +111,18 @@ public: static void initPersistFields(); AIController() { + for (S32 i = 0; i < MaxTriggerKeys; i++) + mTriggerState.mMoveTriggers[i] = false; + + mMovement.mControllerRef = this; + mTriggerState.mControllerRef = this; mControllerData = NULL; mAIInfo = new AIInfo(this); mGoal = new AIGoal(this); mAimTarget = new AIAimTarget(this); mCover = new AICover(this); mNav = new AINavigation(this); + mMovement.mMoveState = ModeStop; }; DECLARE_CONOBJECT(AIController); @@ -127,7 +135,16 @@ class AIControllerData : public SimDataBlock { public: - AIControllerData() { mMoveTolerance = 0.25; mFollowTolerance = 1.0; mAttackRadius = 2.0; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30;}; + AIControllerData() + { + mMoveTolerance = 0.25; + mFollowTolerance = 1.0; + mAttackRadius = 2.0; + mMoveStuckTolerance = 0.01f; + mMoveStuckTestDelay = 30; + mLinkTypes = LinkData(AllFlags); + mNavSize = AINavigation::Regular; + }; ~AIControllerData() {}; static void initPersistFields(); @@ -140,7 +157,7 @@ public: S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck /// Types of link we can use. LinkData mLinkTypes; - + AINavigation::NavSize mNavSize; void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {}; void resolveRoll(AIController* obj, Point3F location, Move* movePtr); diff --git a/Engine/source/T3D/AI/AICover.cpp b/Engine/source/T3D/AI/AICover.cpp index 509b0f0c0..97cf82a39 100644 --- a/Engine/source/T3D/AI/AICover.cpp +++ b/Engine/source/T3D/AI/AICover.cpp @@ -19,3 +19,90 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "AICover.h" +#include "AIController.h" + +struct CoverSearch +{ + Point3F loc; + Point3F from; + F32 dist; + F32 best; + CoverPoint* point; + CoverSearch() : loc(0, 0, 0), from(0, 0, 0) + { + best = -FLT_MAX; + point = NULL; + dist = FLT_MAX; + } +}; + +static void findCoverCallback(SceneObject* obj, void* key) +{ + CoverPoint* p = dynamic_cast(obj); + if (!p || p->isOccupied()) + return; + CoverSearch* s = static_cast(key); + Point3F dir = s->from - p->getPosition(); + dir.normalizeSafe(); + // Score first based on angle of cover point to enemy. + F32 score = mDot(p->getNormal(), dir); + // Score also based on distance from seeker. + score -= (p->getPosition() - s->loc).len() / s->dist; + // Finally, consider cover size. + score += (p->getSize() + 1) / CoverPoint::NumSizes; + score *= p->getQuality(); + if (score > s->best) + { + s->best = score; + s->point = p; + } +} + +bool AIController::findCover(const Point3F& from, F32 radius) +{ + if (radius <= 0) + return false; + + // Create a search state. + CoverSearch s; + s.loc = getAIInfo()->getPosition(); + s.dist = radius; + // Direction we seek cover FROM. + s.from = from; + + // Find cover points. + Box3F box(radius * 2.0f); + box.setCenter(getAIInfo()->getPosition()); + getAIInfo()->mObj->getContainer()->findObjects(box, MarkerObjectType, findCoverCallback, &s); + + // Go to cover! + if (s.point) + { + // Calling setPathDestination clears cover... + bool foundPath = getNav()->setPathDestination(s.point->getPosition()); + setCover(s.point); + s.point->setOccupied(true); + return foundPath; + } + return false; +} + + +DefineEngineMethod(AIController, findCover, S32, (Point3F from, F32 radius), , + "@brief Tells the AI to find cover nearby.\n\n" + + "@param from Location to find cover from (i.e., enemy position).\n" + "@param radius Distance to search for cover.\n" + "@return Cover point ID if cover was found, -1 otherwise.\n\n") +{ + if (object->findCover(from, radius)) + { + CoverPoint* cover = object->getCover()->mCoverPoint.getObject(); + return cover ? cover->getId() : -1; + } + else + { + return -1; + } +} diff --git a/Engine/source/T3D/AI/AICover.h b/Engine/source/T3D/AI/AICover.h index d9d1cb638..4fe33cab7 100644 --- a/Engine/source/T3D/AI/AICover.h +++ b/Engine/source/T3D/AI/AICover.h @@ -23,6 +23,9 @@ #define _AICOVER_H_ #include "AIInfo.h" +#include "navigation/coverPoint.h" + + struct AICover : AIInfo { @@ -30,7 +33,7 @@ struct AICover : AIInfo /// Pointer to a cover point. SimObjectPtr mCoverPoint; AICover(AIController* controller) : Parent(controller) {}; - AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mCoverPoint = dynamic_cast(objIn.getPointer());}; AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; }; diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index 3ce55d86c..c2c11de9a 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -53,7 +53,7 @@ AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) F32 AIInfo::getDist() { AIInfo* controlObj = getCtrl()->getAIInfo(); - F32 ret = VectorF(controlObj->mObj->getPosition() - getPosition()).len(); + F32 ret = VectorF(controlObj->getPosition() - getPosition()).len(); ret -= controlObj->mRadius + mRadius; return ret; } diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index db0dc191d..a392f4553 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -36,7 +36,7 @@ struct AIInfo Point3F mPosition, mLastPos; bool mPosSet; F32 mRadius; - Point3F getPosition() { return (mObj) ? mObj->getPosition() : mPosition; } + Point3F getPosition() { return (mObj.isValid()) ? mObj->getPosition() : mPosition; } F32 getDist(); AIInfo() = delete; AIInfo(AIController* controller); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 896586ff7..a4511921f 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -26,6 +26,7 @@ AINavigation::AINavigation(AIController* controller) { mControllerRef = controller; mJump = None; + mNavSize = Regular; } NavMesh* AINavigation::findNavMesh() const @@ -40,6 +41,19 @@ NavMesh* AINavigation::findNavMesh() const NavMesh* m = static_cast(set->at(i)); if (m->getWorldBox().isContained(gbo->getWorldBox())) { + // Check that mesh size is appropriate. + if (gbo->isMounted()) + { + if (!m->mVehicles) + continue; + } + else + { + if ((getNavSize() == Small && !m->mSmallCharacters) || + (getNavSize() == Regular && !m->mRegularCharacters) || + (getNavSize() == Large && !m->mLargeCharacters)) + continue; + } if (!mesh || m->getWorldBox().getVolume() < mesh->getWorldBox().getVolume()) mesh = m; } @@ -101,6 +115,8 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; + if (!mControllerRef->getGoal()) return; + // If we're following, get their position. mPathData.path->mTo = mControllerRef->getGoal()->getPosition(); // Update from position and replan. @@ -212,24 +228,21 @@ bool AINavigation::setPathDestination(const Point3F& pos) } } -void AINavigation::followObject(AIInfo* targ) +void AINavigation::followObject() { - if (!targ) return; - - if (targ->getDist() < mControllerRef->mControllerData->mMoveTolerance) + if ((mControllerRef->getGoal()->mLastPos - mControllerRef->getAIInfo()->getPosition()).len() < mControllerRef->mControllerData->mMoveTolerance) return; - if (setPathDestination(targ->getPosition())) + if (setPathDestination(mControllerRef->getGoal()->getPosition())) { mControllerRef->clearCover(); - mControllerRef->setGoal(targ); } } void AINavigation::followObject(SceneObject* obj, F32 radius) { mControllerRef->setGoal(obj, radius); - followObject(mControllerRef->getGoal()); + followObject(); } void AINavigation::clearFollow() @@ -289,3 +302,107 @@ DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , return object->getNav()->getMoveDestination(); } +DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , + "@brief Tells the AI to find a path to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@return True if a path was found.\n\n" + + "@see getPathDestination()\n" + "@see setMoveDestination()\n") +{ + return object->getNav()->setPathDestination(goal); +} + + +DefineEngineMethod(AIController, getPathDestination, Point3F, (), , + "@brief Get the AIPlayer's current pathfinding destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current path destination. If no path destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setPathDestination()\n") +{ + return object->getNav()->getPathDestination(); +} + +DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), , + "@brief Tell the AIPlayer to follow a path.\n\n" + + "@param obj ID of a NavPath object for the character to follow.") +{ + NavPath* path; + if (Sim::findObject(obj, path)) + object->getNav()->followNavPath(path); +} + +DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), , + "@brief Tell the AIPlayer to follow another object.\n\n" + + "@param obj ID of the object to follow.\n" + "@param radius Maximum distance we let the target escape to.") +{ + SceneObject* follow; + object->getNav()->clearPath(); + object->clearCover(); + object->getNav()->clearFollow(); + + if (Sim::findObject(obj, follow)) + object->getNav()->followObject(follow, radius); +} + + +DefineEngineMethod(AIController, repath, void, (), , + "@brief Tells the AI to re-plan its path. Does nothing if the character " + "has no path, or if it is following a mission path.\n\n") +{ + object->getNav()->repath(); +} + +DefineEngineMethod(AIController, findNavMesh, S32, (), , + "@brief Get the NavMesh object this AIPlayer is currently using.\n\n" + + "@return The ID of the NavPath object this character is using for " + "pathfinding. This is determined by the character's location, " + "navigation type and other factors. Returns -1 if no NavMesh is " + "found.") +{ + NavMesh* mesh = object->getNav()->getNavMesh(); + return mesh ? mesh->getId() : -1; +} + +DefineEngineMethod(AIController, getNavMesh, S32, (), , + "@brief Return the NavMesh this AIPlayer is using to navigate.\n\n") +{ + NavMesh* m = object->getNav()->getNavMesh(); + return m ? m->getId() : 0; +} + +DefineEngineMethod(AIController, setNavSize, void, (const char* size), , + "@brief Set the size of NavMesh this character uses. One of \"Small\", \"Regular\" or \"Large\".") +{ + if (!String::compare(size, "Small")) + object->getNav()->setNavSize(AINavigation::Small); + else if (!String::compare(size, "Regular")) + object->getNav()->setNavSize(AINavigation::Regular); + else if (!String::compare(size, "Large")) + object->getNav()->setNavSize(AINavigation::Large); + else + Con::errorf("AIPlayer::setNavSize: no such size '%s'.", size); +} + +DefineEngineMethod(AIController, getNavSize, const char*, (), , + "@brief Return the size of NavMesh this character uses for pathfinding.") +{ + switch (object->getNav()->getNavSize()) + { + case AINavigation::Small: + return "Small"; + case AINavigation::Regular: + return "Regular"; + case AINavigation::Large: + return "Large"; + } + return ""; +} diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 2d6ce1ac3..2efcbe536 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -59,6 +59,14 @@ struct AINavigation Ledge, ///< Jump when we walk off a ledge. }; + enum NavSize { + Small, + Regular, + Large + } mNavSize; + void setNavSize(NavSize size) { mNavSize = size; updateNavMesh(); } + NavSize getNavSize() const { return mNavSize; } + Point3F mMoveDestination; void setMoveDestination(const Point3F& location, bool slowdown); Point3F getMoveDestination() { return mMoveDestination; }; @@ -68,6 +76,7 @@ struct AINavigation SimObjectPtr mNavMesh; NavMesh* findNavMesh() const; void updateNavMesh(); + NavMesh* getNavMesh() const { return mNavMesh; } PathData mPathData; JumpStates mJump; @@ -81,7 +90,7 @@ struct AINavigation SimObjectPtr getPath() { return mPathData.path; }; void followNavPath(NavPath* path); - void followObject(AIInfo* targ); + void followObject(); void followObject(SceneObject* obj, F32 radius); void clearFollow(); /// Move to the specified node in the current path. diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 42e8a9d5f..38ef5efcb 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2259,14 +2259,14 @@ void Player::advanceTime(F32 dt) } } -bool Player::setAIController(S32 controller) +bool Player::setAIController(SimObjectId controller) { if (Sim::findObject(controller, mAIController)) { mAIController->setAIInfo(this); return true; } - + Con::errorf("unable to find AIController : %i", controller); mAIController = NULL; return false; } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index 752ea9ca1..df10ba291 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -763,7 +763,7 @@ public: void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; virtual bool getAIMove(Move*); - bool setAIController(S32 controller); + bool setAIController(SimObjectId controller); AIController* getAIController() { return mAIController; }; bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index bf164bc4b..b67272aff 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -225,8 +225,18 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) SimGroup* missionCleanup = dynamic_cast(cleanup); missionCleanup->addObject(obj); } - mPlayer = static_cast(obj); - Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags())); + mPlayer = obj; + Player* po = dynamic_cast(obj); + if (!po) return; //todo, more types + if (po->getAIController()) + { + if (po->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } } } @@ -383,16 +393,34 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) // Select/move character else { - if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri)) + if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri)) { - if(dynamic_cast(ri.object)) + if(ri.object) { - mPlayer = dynamic_cast(ri.object); - Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags())); + mPlayer = ri.object; + Player* po = dynamic_cast(ri.object); + if (!po) return; //todo, more types + if (po->getAIController()) + { + if (po->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } + } + } + else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) + { + Player* po = dynamic_cast(mPlayer.getPointer()); + if (!po) return; //todo, more types + if (po->getAIController()) + { + if (po->getAIController()->mControllerData) + po->getAIController()->getNav()->setPathDestination(ri.point); } } - else if(!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) - mPlayer->setPathDestination(ri.point); } } } @@ -455,8 +483,8 @@ void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) if(mMode == mTestMode) { - if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri)) - mCurPlayer = dynamic_cast(ri.object); + if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri)) + mCurPlayer = ri.object; else mCurPlayer = NULL; } diff --git a/Engine/source/navigation/guiNavEditorCtrl.h b/Engine/source/navigation/guiNavEditorCtrl.h index 6e0b34f5e..c5d2f1b5a 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.h +++ b/Engine/source/navigation/guiNavEditorCtrl.h @@ -155,8 +155,8 @@ protected: /// @name Test mode /// @{ - SimObjectPtr mPlayer; - SimObjectPtr mCurPlayer; + SimObjectPtr mPlayer; + SimObjectPtr mCurPlayer; /// @} diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index 1035f2d9a..fbd8cd5fc 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -169,3 +169,8 @@ datablock LightAnimData( SpinLightAnim ) rotKeys[2] = "az"; rotSmooth[2] = true; }; + +datablock AIPlayerControllerData( aiPlayerControl ) +{ + +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/navEditor/main.tscript b/Templates/BaseGame/game/tools/navEditor/main.tscript index 56739e3a6..bfc06d103 100644 --- a/Templates/BaseGame/game/tools/navEditor/main.tscript +++ b/Templates/BaseGame/game/tools/navEditor/main.tscript @@ -104,6 +104,7 @@ function ENavEditorSettingsPage::init(%this) { // Initialises the settings controls in the settings dialog box. %this-->SpawnClassOptions.clear(); + %this-->SpawnClassOptions.add("Player"); %this-->SpawnClassOptions.add("AIPlayer"); %this-->SpawnClassOptions.setFirstSelected(); } @@ -240,7 +241,7 @@ function NavEditorPlugin::initSettings(%this) { EditorSettings.beginGroup("NavEditor", true); - EditorSettings.setDefaultValue("SpawnClass", "AIPlayer"); + EditorSettings.setDefaultValue("SpawnClass", "Player"); EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData"); EditorSettings.endGroup(); diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 8e2234646..14d758923 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -459,6 +459,12 @@ function NavEditorGui::onLinkSelected(%this, %flags) function NavEditorGui::onPlayerSelected(%this, %flags) { + if (!isObject(%this.getPlayer().aiController)) + { + %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; + %this.getPlayer().setAIController(%this.getPlayer().aiController); + } + NavMeshIgnore(%this.getPlayer(), true); updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); } @@ -526,7 +532,7 @@ function NavEditorGui::findCover(%this) %text = NavEditorOptionsWindow-->TestProperties->CoverPosition.getText(); if(%text !$= "") %pos = eval("return " @ %text); - %this.getPlayer().findCover(%pos, NavEditorOptionsWindow-->TestProperties->CoverRadius.getText()); + %this.getPlayer().getAIController().findCover(%pos, NavEditorOptionsWindow-->TestProperties->CoverRadius.getText()); } } @@ -547,7 +553,7 @@ function NavEditorGui::followObject(%this) toolsMessageBoxOk("Error", "Cannot find object" SPC %text); } if(isObject(%obj)) - %this.getPlayer().followObject(%obj, NavEditorOptionsWindow-->TestProperties->FollowRadius.getText()); + %this.getPlayer().getAIController().followObject(%obj, NavEditorOptionsWindow-->TestProperties->FollowRadius.getText()); } } From a10169accf61352fbcefd75e76eef32b0a4c6727 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 09:44:04 -0500 Subject: [PATCH 10/47] fix node following stopping early --- Engine/source/T3D/AI/AINavigation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index a4511921f..4897e0617 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -170,8 +170,10 @@ void AINavigation::onReachDestination() } else #endif + { getCtrl()->throwCallback("onReachDestination"); - getCtrl()->mMovement.mMoveState = AIController::ModeStop; + getCtrl()->mMovement.mMoveState = AIController::ModeStop; + } } bool AINavigation::setPathDestination(const Point3F& pos) From eaa6a62b0c2626c299c820fd84203d4e0eb3ccb2 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 10:38:36 -0500 Subject: [PATCH 11/47] misc cleanups --- Engine/source/T3D/AI/AIController.cpp | 5 ++-- Engine/source/T3D/AI/AINavigation.cpp | 36 +++++++++++++-------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 2ede9aea9..fd6692f07 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -398,14 +398,15 @@ void AIControllerData::resolveStuck(AIController* obj) ShapeBase* sbo = dynamic_cast(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 (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 (locationDelta < mMoveStuckTolerance) { // 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 diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 4897e0617..aa2dd09c6 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -115,12 +115,12 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - if (!mControllerRef->getGoal()) return; + if (!getCtrl()->getGoal()) return; // If we're following, get their position. - mPathData.path->mTo = mControllerRef->getGoal()->getPosition(); + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); // Update from position and replan. - mPathData.path->mFrom = mControllerRef->getAIInfo()->getPosition(); + mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); mPathData.path->plan(); // Move to first node (skip start pos). moveToNode(1); @@ -137,9 +137,9 @@ Point3F AINavigation::getPathDestination() const 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; + getCtrl()->mMovement.mMoveState = AIController::ModeMove; + getCtrl()->mMovement.mMoveSlowdown = slowdown; + getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->mControllerData->mMoveStuckTestDelay; } void AINavigation::onReachDestination() @@ -184,7 +184,7 @@ bool AINavigation::setPathDestination(const Point3F& pos) if (!mNavMesh) { //setMoveDestination(pos); - mControllerRef->throwCallback("onPathFailed"); + getCtrl()->throwCallback("onPathFailed"); return false; } @@ -192,11 +192,11 @@ bool AINavigation::setPathDestination(const Point3F& pos) NavPath* path = new NavPath(); path->mMesh = mNavMesh; - path->mFrom = mControllerRef->getAIInfo()->getPosition(); + path->mFrom = getCtrl()->getAIInfo()->getPosition(); path->mTo = pos; path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; - path->mLinkTypes = mControllerRef->mControllerData->mLinkTypes; + path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; path->mXray = true; // Paths plan automatically upon being registered. if (!path->registerObject()) @@ -209,14 +209,14 @@ bool AINavigation::setPathDestination(const Point3F& pos) { // Clear any current path we might have. clearPath(); - mControllerRef->clearCover(); + getCtrl()->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"); + getCtrl()->throwCallback("onPathSuccess"); return true; } else @@ -224,7 +224,7 @@ bool AINavigation::setPathDestination(const Point3F& pos) // Just move normally if we can't path. //setMoveDestination(pos, true); //return; - mControllerRef->throwCallback("onPathFailed"); + getCtrl()->throwCallback("onPathFailed"); path->deleteObject(); return false; } @@ -232,31 +232,31 @@ bool AINavigation::setPathDestination(const Point3F& pos) void AINavigation::followObject() { - if ((mControllerRef->getGoal()->mLastPos - mControllerRef->getAIInfo()->getPosition()).len() < mControllerRef->mControllerData->mMoveTolerance) + if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) return; - if (setPathDestination(mControllerRef->getGoal()->getPosition())) + if (setPathDestination(getCtrl()->getGoal()->getPosition())) { - mControllerRef->clearCover(); + getCtrl()->clearCover(); } } void AINavigation::followObject(SceneObject* obj, F32 radius) { - mControllerRef->setGoal(obj, radius); + getCtrl()->setGoal(obj, radius); followObject(); } void AINavigation::clearFollow() { - mControllerRef->clearGoal(); + getCtrl()->clearGoal(); } void AINavigation::followNavPath(NavPath* path) { // Get rid of our current path. clearPath(); - mControllerRef->clearCover(); + getCtrl()->clearCover(); clearFollow(); // Follow new path. From 25b3a7c0705172304960bdd6974a8c19fbc6dc43 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 10:50:58 -0500 Subject: [PATCH 12/47] break wether we *should* be trying to move out of the resolver --- Engine/source/T3D/AI/AIController.cpp | 127 +++++++++++++------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index fd6692f07..7e9207ebd 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -122,8 +122,22 @@ bool AIController::getAIMove(Move* movePtr) mControllerData->resolveYaw(this, location, movePtr); mControllerData->resolvePitch(this, location, movePtr); mControllerData->resolveRoll(this, location, movePtr); - mControllerData->resolveSpeed(this, location, movePtr); - mControllerData->resolveStuck(this); + + if (mMovement.mMoveState != AIController::ModeStop) + { + F32 xDiff = getNav()->mMoveDestination.x - location.x; + F32 yDiff = getNav()->mMoveDestination.y - location.y; + if (mFabs(xDiff) < mControllerData->mMoveTolerance && mFabs(yDiff) < mControllerData->mMoveTolerance) + { + mMovement.mMoveState = AIController::ModeStop; + getNav()->onReachDestination(); + } + else + { + mControllerData->resolveSpeed(this, location, movePtr); + mControllerData->resolveStuck(this); + } + } } // Test for target location in sight if it's an object. The LOS is @@ -323,72 +337,59 @@ void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* mo void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - // Move towards the destination - if (obj->mMovement.mMoveState != AIController::ModeStop) - { - F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; - F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); + F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; + F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); - // 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(); - } + // Build move direction in world space + if (mIsZero(xDiff)) + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + else + if (mIsZero(yDiff)) + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; else - { - // Build move direction in world space - if (mIsZero(xDiff)) + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - else - if (mIsZero(yDiff)) - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - else - if (mFabs(xDiff) > mFabs(yDiff)) - { - F32 value = mFabs(yDiff / xDiff); - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - } - else - { - F32 value = mFabs(xDiff / yDiff); - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; - movePtr->y = (location.y > obj->getNav()->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; - } - } + // 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; } } From 83822f114808b361b2ff438a22d640ce742dd9a3 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 11:38:21 -0500 Subject: [PATCH 13/47] fix eroneous defaults --- Engine/source/T3D/AI/AIController.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index d64148a9e..054671171 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -118,10 +118,10 @@ public: mTriggerState.mControllerRef = this; mControllerData = NULL; mAIInfo = new AIInfo(this); - mGoal = new AIGoal(this); - mAimTarget = new AIAimTarget(this); - mCover = new AICover(this); mNav = new AINavigation(this); + mGoal = NULL; + mAimTarget = NULL; + mCover = NULL; mMovement.mMoveState = ModeStop; }; From 2956223a60f8b545033d3de39979533f287286ab Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 11:39:06 -0500 Subject: [PATCH 14/47] simplify setpathdestination loop --- Engine/source/T3D/AI/AIController.cpp | 4 ++-- Engine/source/T3D/AI/AINavigation.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 7e9207ebd..c878ca9c4 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -78,11 +78,11 @@ bool AIController::getAIMove(Move* movePtr) Point3F rotation = sbo->getTransform().toEuler(); #ifdef TORQUE_NAVIGATION_ENABLED - if (sbo->getDamageState() == ShapeBase::Enabled) + if (sbo->getDamageState() == ShapeBase::Enabled && getGoal()) { if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); - if (getGoal() && !getGoal()->mObj.isNull()) + if (getGoal()->mObj.isValid()) { if (getNav()->mPathData.path.isNull()) { diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index aa2dd09c6..180f575a6 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -64,7 +64,6 @@ NavMesh* AINavigation::findNavMesh() const void AINavigation::updateNavMesh() { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); - NavMesh* old = mNavMesh; if (mNavMesh.isNull()) mNavMesh = findNavMesh(); else @@ -73,9 +72,9 @@ void AINavigation::updateNavMesh() mNavMesh = findNavMesh(); } // See if we need to update our path. - if (mNavMesh != old && !mPathData.path.isNull()) + if (mNavMesh) { - setPathDestination(mPathData.path->mTo); + setPathDestination(getCtrl()->getGoal()->getPosition()); } } @@ -178,6 +177,7 @@ void AINavigation::onReachDestination() bool AINavigation::setPathDestination(const Point3F& pos) { + if (!getCtrl()->getGoal()) getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); if (!mNavMesh) updateNavMesh(); // If we can't find a mesh, just move regularly. From 4f87ad4cf7ecc5c6224895557b75b617669723ed Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 12:27:05 -0500 Subject: [PATCH 15/47] moar gaol tracking cleanups --- Engine/source/T3D/AI/AIAimTarget.h | 6 ++-- Engine/source/T3D/AI/AIController.cpp | 46 +++++++++++++++------------ Engine/source/T3D/AI/AINavigation.cpp | 2 -- Engine/source/T3D/player.cpp | 4 +-- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index 4e26f8306..1a42911fd 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -32,9 +32,9 @@ struct AIAimTarget : AIInfo bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); F32 getTargetDistance(SceneObject* target, bool _checkEnabled); - AIAimTarget(AIController* controller) : Parent(controller) {}; - AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; - AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; + AIAimTarget(AIController* controller) : Parent(controller) { mTargetInLOS = false; }; + AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mTargetInLOS = false; }; + AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mTargetInLOS = false; }; }; #endif diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c878ca9c4..3050df148 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -82,28 +82,31 @@ bool AIController::getAIMove(Move* movePtr) { if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); - if (getGoal()->mObj.isValid()) - { - if (getNav()->mPathData.path.isNull()) - { - if (getGoal()->getDist() > mControllerData->mFollowTolerance) - getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); - } - else - { - if (getGoal()->getDist() > mControllerData->mFollowTolerance) - getNav()->repath(); - if (getGoal()->getDist() < mControllerData->mFollowTolerance) - { - getNav()->clearPath(); - mMovement.mMoveState = ModeStop; - throwCallback("onTargetInRange"); - } - else if (getGoal()->getDist() < mControllerData->mAttackRadius) - { - throwCallback("onTargetInFiringRange"); - } + if (getNav()->mPathData.path.isNull()) + { + if (getGoal()->getDist() > mControllerData->mFollowTolerance) + { + if (getGoal()->mObj.isValid()) + getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); + else + getNav()->setPathDestination(getGoal()->getPosition()); + } + } + else + { + if (getGoal()->getDist() > mControllerData->mFollowTolerance) + getNav()->repath(); + + if (getGoal()->getDist() < mControllerData->mFollowTolerance) + { + getNav()->clearPath(); + mMovement.mMoveState = ModeStop; + throwCallback("onTargetInRange"); + } + else if (getGoal()->getDist() < mControllerData->mAttackRadius) + { + throwCallback("onTargetInFiringRange"); } } } @@ -396,6 +399,7 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m void AIControllerData::resolveStuck(AIController* obj) { if (obj->mMovement.mMoveState == AIController::ModeStop) return; + if (!obj->getGoal()) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during // an anim-clip effect override. diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 180f575a6..126f05d29 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -114,8 +114,6 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - if (!getCtrl()->getGoal()) return; - // If we're following, get their position. mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); // Update from position and replan. diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 38ef5efcb..00f929742 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2261,9 +2261,9 @@ void Player::advanceTime(F32 dt) bool Player::setAIController(SimObjectId controller) { - if (Sim::findObject(controller, mAIController)) + if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) { - mAIController->setAIInfo(this); + mAIController->setAIInfo(this, mAIController->mControllerData->mMoveTolerance); return true; } Con::errorf("unable to find AIController : %i", controller); From c72c3068f8bdaf323ddc88473975f287324f8ce4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 14:38:27 -0500 Subject: [PATCH 16/47] aiinfo subclass inheritance cleanups, and default constructor removals for safeties --- Engine/source/T3D/AI/AIAimTarget.h | 3 +- Engine/source/T3D/AI/AIController.cpp | 44 ++++++++++++++++++++++++++- Engine/source/T3D/AI/AIController.h | 10 +++--- Engine/source/T3D/AI/AICover.h | 3 +- Engine/source/T3D/AI/AIGoal.h | 3 +- Engine/source/T3D/AI/AINavigation.cpp | 13 +++++--- 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index 1a42911fd..4c609af27 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -23,7 +23,7 @@ #define _AIAIMTARGET_H_ #include "AIInfo.h" -struct AIAimTarget : AIInfo +struct AIAimTarget : public AIInfo { typedef AIInfo Parent; Point3F mAimOffset; @@ -32,6 +32,7 @@ struct AIAimTarget : AIInfo bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); F32 getTargetDistance(SceneObject* target, bool _checkEnabled); + AIAimTarget() = delete; AIAimTarget(AIController* controller) : Parent(controller) { mTargetInLOS = false; }; AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mTargetInLOS = false; }; AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mTargetInLOS = false; }; diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 3050df148..2e46a8d4b 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -64,6 +64,48 @@ bool AIController::setControllerDataProperty(void* obj, const char* index, const return false; } +void AIController::setGoal(AIInfo* targ) +{ + if (mGoal) { delete(mGoal); mGoal = NULL; } + + if (targ->mObj.isValid()) + { + delete(mGoal); + mGoal = new AIGoal(this, targ->mObj, targ->mRadius); + } + else if (targ->mPosSet) + { + delete(mGoal); + mGoal = new AIGoal(this, targ->mPosition, targ->mRadius); + } +} + +void AIController::setGoal(Point3F loc, F32 rad) +{ + if (mGoal) delete(mGoal); + mGoal = new AIGoal(this, loc, rad); +} + +void AIController::setGoal(SimObjectPtr objIn, F32 rad) +{ + if (mGoal) delete(mGoal); + mGoal = new AIGoal(this, objIn, rad); +} + +void AIController::setAim(Point3F loc, F32 rad, Point3F offset) +{ + if (mAimTarget) delete(mAimTarget); + mAimTarget = new AIAimTarget(this, loc, rad); + mAimTarget->mAimOffset = offset; +} + +void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offset) +{ + if (mAimTarget) delete(mAimTarget); + mAimTarget = new AIAimTarget(this, objIn, rad); + mAimTarget->mAimOffset = offset; +} + #ifdef TORQUE_NAVIGATION_ENABLED bool AIController::getAIMove(Move* movePtr) { @@ -89,7 +131,7 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->mObj.isValid()) getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); - else + else if (getGoal()->mPosSet) getNav()->setPathDestination(getGoal()->getPosition()); } } diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 054671171..140eb030b 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -55,16 +55,16 @@ public: 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 objIn, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, objIn, rad); } + void setGoal(AIInfo* targ); + void setGoal(Point3F loc, F32 rad = 0.0f); + void setGoal(SimObjectPtr objIn, F32 rad = 0.0f); 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 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; } + void setAim(Point3F loc, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)); + void setAim(SimObjectPtr objIn, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)); AIAimTarget* getAim() { return mAimTarget; } void clearAim() { SAFE_DELETE(mAimTarget); } private: diff --git a/Engine/source/T3D/AI/AICover.h b/Engine/source/T3D/AI/AICover.h index 4fe33cab7..e18863bcf 100644 --- a/Engine/source/T3D/AI/AICover.h +++ b/Engine/source/T3D/AI/AICover.h @@ -27,11 +27,12 @@ -struct AICover : AIInfo +struct AICover : public AIInfo { typedef AIInfo Parent; /// Pointer to a cover point. SimObjectPtr mCoverPoint; + AICover() = delete; AICover(AIController* controller) : Parent(controller) {}; AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mCoverPoint = dynamic_cast(objIn.getPointer());}; AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; diff --git a/Engine/source/T3D/AI/AIGoal.h b/Engine/source/T3D/AI/AIGoal.h index e169e489d..ba5096bd7 100644 --- a/Engine/source/T3D/AI/AIGoal.h +++ b/Engine/source/T3D/AI/AIGoal.h @@ -24,9 +24,10 @@ #include "AIInfo.h" -struct AIGoal : AIInfo +struct AIGoal : public AIInfo { typedef AIInfo Parent; + AIGoal() = delete; AIGoal(AIController* controller): Parent(controller) {}; AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 126f05d29..0eb5e48d4 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -64,6 +64,7 @@ NavMesh* AINavigation::findNavMesh() const void AINavigation::updateNavMesh() { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); + NavMesh* old = mNavMesh; if (mNavMesh.isNull()) mNavMesh = findNavMesh(); else @@ -72,9 +73,9 @@ void AINavigation::updateNavMesh() mNavMesh = findNavMesh(); } // See if we need to update our path. - if (mNavMesh) + if (mNavMesh != old && !mPathData.path.isNull()) { - setPathDestination(getCtrl()->getGoal()->getPosition()); + setPathDestination(mPathData.path->mTo); } } @@ -175,7 +176,11 @@ void AINavigation::onReachDestination() bool AINavigation::setPathDestination(const Point3F& pos) { - if (!getCtrl()->getGoal()) getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); + AIGoal* curgoal = getCtrl()->getGoal(); + + if (!curgoal || !curgoal->mObj.isValid()) + getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); + if (!mNavMesh) updateNavMesh(); // If we can't find a mesh, just move regularly. @@ -191,7 +196,7 @@ bool AINavigation::setPathDestination(const Point3F& pos) path->mMesh = mNavMesh; path->mFrom = getCtrl()->getAIInfo()->getPosition(); - path->mTo = pos; + path->mTo = getCtrl()->getGoal()->getPosition(); path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; From f84bf058c98b968ca4b5e7fa50e5fc31e629eed5 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 17:51:31 -0500 Subject: [PATCH 17/47] navigation: setPathdestination now takes a replace bool to preserve the goal also killed a few now extraneous clearfollow calls --- Engine/source/T3D/AI/AINavigation.cpp | 11 ++++------- Engine/source/T3D/AI/AINavigation.h | 2 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 0eb5e48d4..57d2be27a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -174,15 +174,14 @@ void AINavigation::onReachDestination() } } -bool AINavigation::setPathDestination(const Point3F& pos) +bool AINavigation::setPathDestination(const Point3F& pos, bool replace) { - AIGoal* curgoal = getCtrl()->getGoal(); - - if (!curgoal || !curgoal->mObj.isValid()) + if (replace) getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); if (!mNavMesh) updateNavMesh(); + // If we can't find a mesh, just move regularly. if (!mNavMesh) { @@ -213,7 +212,6 @@ bool AINavigation::setPathDestination(const Point3F& pos) // Clear any current path we might have. clearPath(); getCtrl()->clearCover(); - clearFollow(); // Store new path. mPathData.path = path; mPathData.owned = true; @@ -260,7 +258,6 @@ void AINavigation::followNavPath(NavPath* path) // Get rid of our current path. clearPath(); getCtrl()->clearCover(); - clearFollow(); // Follow new path. mPathData.path = path; @@ -316,7 +313,7 @@ DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , "@see getPathDestination()\n" "@see setMoveDestination()\n") { - return object->getNav()->setPathDestination(goal); + return object->getNav()->setPathDestination(goal,true); } diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 2efcbe536..ad9ae6a70 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -82,7 +82,7 @@ struct AINavigation /// Clear out the current path. void clearPath(); - bool setPathDestination(const Point3F& pos); + bool setPathDestination(const Point3F& pos, bool replace = false); Point3F getPathDestination() const; void repath(); diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index b67272aff..3237816bd 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -418,7 +418,7 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if (po->getAIController()) { if (po->getAIController()->mControllerData) - po->getAIController()->getNav()->setPathDestination(ri.point); + po->getAIController()->getNav()->setPathDestination(ri.point,true); } } } From 32f9917ed2c88f8fe4b26361c2325dad5d768818 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 17:55:38 -0500 Subject: [PATCH 18/47] aicontroller: in order to call derivatives of AIControllerData datablocks *without* requiring an accompanying AIController subclass as well, leverage the fastdelegate system for our resolver callbacks additionally, don't try and repath in mid air aigoal: initialize inange/infirinrange to false. use those to filter callbacks --- Engine/source/T3D/AI/AIController.cpp | 98 +++++++++++++++++---------- Engine/source/T3D/AI/AIController.h | 27 +++++++- Engine/source/T3D/AI/AIGoal.h | 7 +- 3 files changed, 91 insertions(+), 41 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 2e46a8d4b..fa9befa03 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -138,17 +138,33 @@ bool AIController::getAIMove(Move* movePtr) else { if (getGoal()->getDist() > mControllerData->mFollowTolerance) - getNav()->repath(); - - if (getGoal()->getDist() < mControllerData->mFollowTolerance) + { + RayInfo info; + if (getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) + { + getNav()->repath(); + } + getGoal()->mInRange = false; + } + if (getGoal()->getDist() < mControllerData->mFollowTolerance && !getGoal()->mInRange) { getNav()->clearPath(); mMovement.mMoveState = ModeStop; + getGoal()->mInRange = true; throwCallback("onTargetInRange"); } - else if (getGoal()->getDist() < mControllerData->mAttackRadius) + else { - throwCallback("onTargetInFiringRange"); + if (getGoal()->getDist() < mControllerData->mAttackRadius ) + { + if (!getGoal()->mInFiringRange) + { + getGoal()->mInFiringRange = true; + throwCallback("onTargetInFiringRange"); + } + } + else + getGoal()->mInFiringRange = false; } } } @@ -164,9 +180,9 @@ bool AIController::getAIMove(Move* movePtr) else mMovement.mAimLocation = getNav()->mMoveDestination; - mControllerData->resolveYaw(this, location, movePtr); - mControllerData->resolvePitch(this, location, movePtr); - mControllerData->resolveRoll(this, location, movePtr); + mControllerData->resolveYawPtr(this, location, movePtr); + mControllerData->resolvePitchPtr(this, location, movePtr); + mControllerData->resolveRollPtr(this, location, movePtr); if (mMovement.mMoveState != AIController::ModeStop) { @@ -179,12 +195,14 @@ bool AIController::getAIMove(Move* movePtr) } else { - mControllerData->resolveSpeed(this, location, movePtr); - mControllerData->resolveStuck(this); + mControllerData->resolveSpeedPtr(this, location, movePtr); + mControllerData->resolveStuckPtr(this); } } } + mControllerData->resolveTriggerStatePtr(this, movePtr); + // 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. @@ -206,31 +224,6 @@ bool AIController::getAIMove(Move* movePtr) } } - /* - // 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; } @@ -438,6 +431,15 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m } } +void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr) +{ + //check for scripted overides + for (U32 slot = 0; slot < MaxTriggerKeys; slot++) + { + movePtr->trigger[slot] = obj->mTriggerState.mMoveTriggers[slot]; + } +} + void AIControllerData::resolveStuck(AIController* obj) { if (obj->mMovement.mMoveState == AIController::ModeStop) return; @@ -539,10 +541,10 @@ void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, M Player* po = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!po) return;//not a player - if (obj->getAim()->mObj || obj->getAim()->mPosSet || obj->mMovement.mMoveState != AIController::ModeStop) + if (obj->getAim() || obj->mMovement.mMoveState != AIController::ModeStop) { // Next do pitch. - if (!obj->getAim()->mObj && !obj->getAim()->mPosSet) + if (!obj->getAim()) { // Level out if were just looking at our next way point. Point3F headRotation = po->getHeadRotation(); @@ -572,4 +574,26 @@ void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, M movePtr->pitch = -headRotation.x; } } + +void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePtr) +{ + Parent::resolveTriggerState(obj, movePtr); +#ifdef TORQUE_NAVIGATION_ENABLED + if (obj->getNav()->mJump == AINavigation::Now) + { + movePtr->trigger[2] = true; + obj->getNav()->mJump = AINavigation::None; + } + else if (obj->getNav()->mJump == AINavigation::Ledge) + { + // If we're not touching the ground, jump! + RayInfo info; + if (!obj->getAIInfo()->mObj->getContainer()->castRay(obj->getAIInfo()->getPosition(), obj->getAIInfo()->getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) + { + movePtr->trigger[2] = true; + obj->getNav()->mJump = AINavigation::None; + } + } +#endif // TORQUE_NAVIGATION_ENABLED +} #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 140eb030b..78c7cb86b 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -124,7 +124,7 @@ public: mCover = NULL; mMovement.mMoveState = ModeStop; }; - + DECLARE_CONOBJECT(AIController); }; @@ -144,6 +144,13 @@ public: mMoveStuckTestDelay = 30; mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; + + resolveYawPtr.bind(this, &AIControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); + resolveRollPtr.bind(this, &AIControllerData::resolveRoll); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); + resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); }; ~AIControllerData() {}; @@ -158,10 +165,22 @@ public: /// Types of link we can use. LinkData mLinkTypes; AINavigation::NavSize mNavSize; + Delegate resolveYawPtr; void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + + Delegate resolvePitchPtr; void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {}; + + Delegate resolveRollPtr; void resolveRoll(AIController* obj, Point3F location, Move* movePtr); + + Delegate resolveSpeedPtr; void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + + Delegate resolveTriggerStatePtr; + void resolveTriggerState(AIController* obj, Move* movePtr); + + Delegate resolveStuckPtr; void resolveStuck(AIController* obj); }; @@ -170,7 +189,13 @@ class AIPlayerControllerData : public AIControllerData typedef AIControllerData Parent; public: + AIPlayerControllerData() + { + resolvePitchPtr.bind(this, &AIPlayerControllerData::resolvePitch); + resolveTriggerStatePtr.bind(this, &AIPlayerControllerData::resolveTriggerState); + } void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIPlayerControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIGoal.h b/Engine/source/T3D/AI/AIGoal.h index ba5096bd7..e8a22b873 100644 --- a/Engine/source/T3D/AI/AIGoal.h +++ b/Engine/source/T3D/AI/AIGoal.h @@ -27,9 +27,10 @@ struct AIGoal : public AIInfo { typedef AIInfo Parent; + bool mInRange, mInFiringRange; AIGoal() = delete; - AIGoal(AIController* controller): Parent(controller) {}; - AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; - AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; + AIGoal(AIController* controller) : Parent(controller) { mInRange = mInFiringRange = false; }; + AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mInRange = mInFiringRange = false; }; + AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mInRange = mInFiringRange = false; }; }; #endif From d0b0070ec7271f42b41b537fccf2b15866e83afd Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 18:15:41 -0500 Subject: [PATCH 19/47] set navmesh test tool bots to damage enabled and navmeshignore --- Templates/BaseGame/game/tools/navEditor/navEditor.tscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 14d758923..75a84977b 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -463,8 +463,10 @@ function NavEditorGui::onPlayerSelected(%this, %flags) { %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; %this.getPlayer().setAIController(%this.getPlayer().aiController); + NavMeshIgnore(%this.getPlayer(), true); + %this.getPlayer().setDamageState("Enabled"); } - NavMeshIgnore(%this.getPlayer(), true); + updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); } From 1fad2c73729c685118d4cb3ccd4f1812e35061b4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 20:01:36 -0500 Subject: [PATCH 20/47] AIWheeledVehicleControllerData wipwork --- Engine/source/T3D/AI/AIController.cpp | 140 +++++++++++++++++- Engine/source/T3D/AI/AIController.h | 26 ++++ Engine/source/T3D/vehicles/vehicle.h | 1 + .../datablocks/defaultDatablocks.tscript | 5 + 4 files changed, 171 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index fa9befa03..f69345e10 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -22,6 +22,8 @@ #include "AIController.h" #include "T3D/player.h" +#include "T3D/rigidShape.h" +#include "T3D/vehicles/wheeledVehicle.h" IMPLEMENT_CONOBJECT(AIController); @@ -139,8 +141,12 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->getDist() > mControllerData->mFollowTolerance) { + SceneObject* obj = getAIInfo()->mObj->getObjectMount(); + if (!obj) + obj = getAIInfo()->mObj; + RayInfo info; - if (getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) + if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) { getNav()->repath(); } @@ -596,4 +602,136 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt } #endif // TORQUE_NAVIGATION_ENABLED } + +IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); + +// Build a Triangle .. calculate angle of rotation required to meet target.. +// man there has to be a better way! >:) +F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) +{ + WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return 0;//not a WheeledVehicle + + // What is our target + Point3F desired; + desired = obj->getNav()->mMoveDestination; + + MatrixF mat = wvo->getTransform(); + Point3F center, front; + Point3F wFront; + Box3F box = wvo->getObjBox(); + + box.getCenter(¢er); + front = center; + front.y = box.maxExtents.y; // should be true for all these objects + + obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); + front = center + front; + + Point3F objFront = front; + Point3F offset = front - center; + EulerF rot; + rot = mat.toEuler(); + MatrixF transform(rot); + transform.mulV(offset, &wFront); + front = wFront + center; + + Point3F ftoc; + ftoc.x = mFabs(front.x - center.x); + ftoc.y = mFabs(front.y - center.y); + ftoc.z = mFabs(front.z - center.z); + F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); + + Point3F ltoc; + ltoc.x = mFabs(desired.x - center.x); + ltoc.y = mFabs(desired.y - center.y); + ltoc.z = mFabs(desired.z - center.z); + F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); + + Point3F ftol; + ftol.x = mFabs(front.x - desired.x); + ftol.y = mFabs(front.y - desired.y); + ftol.z = mFabs(front.z - desired.z); + F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); + + F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); + Point3F location = obj->getAIInfo()->getPosition(); + + F32 xDiff = desired.x - location.x; + F32 yDiff = desired.y - location.y; + + F32 finalYaw = mRadToDeg(myAngle); + + F32 maxSteeringAngle = 0; + + VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + maxSteeringAngle = vd->maxSteeringAngle; + + // if(finalYaw > 150) + // steerState = TurnAround; + if (finalYaw < 5) + mSteerState = Straight; + else + {// Quickly Hack out left or right turn info + Point3F rotData = objFront - desired; + MatrixF leftM(-rot); + Point3F leftP; + leftM.mulV(rotData, &leftP); + leftP = leftP + desired; + + if (leftP.x < desired.x) + mSteerState = Right; + else + mSteerState = Left; + } + + Point2F steering = wvo->getSteering(); + + F32 steer = 0; + switch (mSteerState) + { + case SteerNull: + break; + case Left: + steer = myAngle < maxSteeringAngle ? -myAngle - steering.x : -maxSteeringAngle - steering.x; + break; + case Right: + steer = myAngle < maxSteeringAngle ? myAngle - steering.x : maxSteeringAngle - steering.x; + break; + case Straight: + steer = -steering.x; + break; + case TurnAround: + steer = maxSteeringAngle - steering.x; + break; + }; + + // Con::printf("AI Steering : %f", steer); + return steer; +} + + +void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) +{ + WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a WheeledVehicle + + // Orient towards our destination. + if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { + movePtr->yaw = getSteeringAngle(obj); + } +}; +void AIWheeledVehicleControllerData::resolveTriggerState(AIController* obj, Move* movePtr) {}; #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 78c7cb86b..d724d9ee5 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -45,6 +45,7 @@ public: ModeMove, // AI is currently moving. ModeStuck, // AI is stuck, but wants to move. ModeSlowing, // AI is slowing down as it reaches it's destination. + ModeReverse // AI is reversing }; private: @@ -198,5 +199,30 @@ public: void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIPlayerControllerData); }; + +class AIWheeledVehicleControllerData : public AIControllerData +{ + typedef AIControllerData Parent; + + enum DrivingState { + SteerNull, + Left, + Right, + Straight, + TurnAround + } mSteerState; + +public: + AIWheeledVehicleControllerData() + { + mSteerState = SteerNull; + resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); + resolveTriggerStatePtr.bind(this, &AIWheeledVehicleControllerData::resolveTriggerState); + } + F32 getSteeringAngle(AIController* obj); + void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolveTriggerState(AIController* obj, Move* movePtr); + DECLARE_CONOBJECT(AIWheeledVehicleControllerData); +}; #endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 402a3109a..52dbb8e68 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -146,6 +146,7 @@ public: bool onAdd() override; void onRemove() override; + Point2F getSteering() { return mSteering; }; /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index fbd8cd5fc..ffdc4806c 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -173,4 +173,9 @@ datablock LightAnimData( SpinLightAnim ) datablock AIPlayerControllerData( aiPlayerControl ) { +}; + +datablock AIWheeledVehicleControllerData( aiCarControl ) +{ + }; \ No newline at end of file From 2d5e8c156043f601a7696ec448d1247e402c0866 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 23:31:30 -0500 Subject: [PATCH 21/47] make steerstate for AIWheeledVehicleControllerData's self contained variable raylength for the rpath filter. same 0.001 for players to stop recalculating a path when jumping, but bump anything they're mounted to to a 2 unit check bit of work towards parallel parking. or at least not ending up arcing back and forth infinitely in an arc --- Engine/source/T3D/AI/AIController.cpp | 88 ++++++++++++++++++++------- Engine/source/T3D/AI/AIController.h | 8 +-- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index f69345e10..8ce21583b 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -141,12 +141,16 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->getDist() > mControllerData->mFollowTolerance) { + F32 raylength = 2.0; //for vehicles SceneObject* obj = getAIInfo()->mObj->getObjectMount(); if (!obj) + { obj = getAIInfo()->mObj; + raylength = 0.001f; //for jumping + } RayInfo info; - if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) + if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, raylength), StaticShapeObjectType, &info)) { getNav()->repath(); } @@ -607,7 +611,7 @@ IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) -F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) +F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) { WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!wvo) @@ -618,6 +622,8 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) } if (!wvo) return 0;//not a WheeledVehicle + DrivingState steerState = SteerNull; + // What is our target Point3F desired; desired = obj->getNav()->mMoveDestination; @@ -661,10 +667,6 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); - Point3F location = obj->getAIInfo()->getPosition(); - - F32 xDiff = desired.x - location.x; - F32 yDiff = desired.y - location.y; F32 finalYaw = mRadToDeg(myAngle); @@ -673,10 +675,12 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); maxSteeringAngle = vd->maxSteeringAngle; - // if(finalYaw > 150) - // steerState = TurnAround; - if (finalYaw < 5) - mSteerState = Straight; + Point2F steering = wvo->getSteering(); + + if (finalYaw < 5 && steering.x != 0.0f) + steerState = Straight; + else if (finalYaw < 5) + steerState = SteerNull; else {// Quickly Hack out left or right turn info Point3F rotData = objFront - desired; @@ -686,32 +690,74 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) leftP = leftP + desired; if (leftP.x < desired.x) - mSteerState = Right; + steerState = Right; else - mSteerState = Left; + steerState = Left; } - Point2F steering = wvo->getSteering(); + + F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; + F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; + Point3F rotation = wvo->getTransform().toEuler(); + Point2F mov; + // Build move direction in world space + if (mIsZero(xDiff)) + mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + else + { + if (mIsZero(yDiff)) + mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + else + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; + mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; + mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + } + } + // Rotate the move into object space (this really only needs + // a 2D matrix) + Point3F throttle; + MatrixF moveMatrix; + moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + steering.x))); + moveMatrix.mulV(Point3F(mov.x, mov.y, 0.0f), &throttle); + + F32 turnAdjust = myAngle - steering.x; + + if (throttle.y < 0.0f) + { + F32 reverseReduction = 0.25f; + if (steerState == Left) + steerState = Right; + else if (steerState == Right) + steerState = Left; + turnAdjust *= reverseReduction; + myAngle *= reverseReduction; + } F32 steer = 0; - switch (mSteerState) + switch (steerState) { - case SteerNull: - break; case Left: - steer = myAngle < maxSteeringAngle ? -myAngle - steering.x : -maxSteeringAngle - steering.x; + steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; break; case Right: - steer = myAngle < maxSteeringAngle ? myAngle - steering.x : maxSteeringAngle - steering.x; + steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; break; case Straight: steer = -steering.x; break; - case TurnAround: - steer = maxSteeringAngle - steering.x; + default: break; }; + // Con::printf("AI Steering : %f", steer); return steer; } @@ -730,7 +776,7 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat // Orient towards our destination. if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { - movePtr->yaw = getSteeringAngle(obj); + movePtr->yaw = getSteeringAngle(obj, location); } }; void AIWheeledVehicleControllerData::resolveTriggerState(AIController* obj, Move* movePtr) {}; diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index d724d9ee5..ccaaf5a35 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -208,18 +208,16 @@ class AIWheeledVehicleControllerData : public AIControllerData SteerNull, Left, Right, - Straight, - TurnAround - } mSteerState; + Straight + }; public: AIWheeledVehicleControllerData() { - mSteerState = SteerNull; resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); resolveTriggerStatePtr.bind(this, &AIWheeledVehicleControllerData::resolveTriggerState); } - F32 getSteeringAngle(AIController* obj); + F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); From 78a26b010876502bf1b9cd5743dc627340122f36 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 11:00:13 -0500 Subject: [PATCH 22/47] expose a getThrottle for vehicles. save some calcs in AIWheeledVehicleControllerData deletion cleanups --- Engine/source/T3D/AI/AIController.cpp | 37 ++------------------------- Engine/source/T3D/AI/AIController.h | 9 ++++++- Engine/source/T3D/AI/AINavigation.cpp | 8 ++++++ Engine/source/T3D/AI/AINavigation.h | 2 +- Engine/source/T3D/player.cpp | 3 ++- Engine/source/T3D/vehicles/vehicle.h | 1 + 6 files changed, 22 insertions(+), 38 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 8ce21583b..974f5e10a 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -695,42 +695,10 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F steerState = Left; } - - F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; - F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; - Point3F rotation = wvo->getTransform().toEuler(); - Point2F mov; - // Build move direction in world space - if (mIsZero(xDiff)) - mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - else - { - if (mIsZero(yDiff)) - mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - else - if (mFabs(xDiff) > mFabs(yDiff)) - { - F32 value = mFabs(yDiff / xDiff); - mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; - mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - } - else - { - F32 value = mFabs(xDiff / yDiff); - mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; - mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - } - } - // Rotate the move into object space (this really only needs - // a 2D matrix) - Point3F throttle; - MatrixF moveMatrix; - moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + steering.x))); - moveMatrix.mulV(Point3F(mov.x, mov.y, 0.0f), &throttle); - F32 turnAdjust = myAngle - steering.x; - if (throttle.y < 0.0f) + F32 throttle = wvo->getThrottle(); + if (throttle < 0.0f) { F32 reverseReduction = 0.25f; if (steerState == Left) @@ -757,7 +725,6 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F break; }; - // Con::printf("AI Steering : %f", steer); return steer; } diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index ccaaf5a35..43c9be04c 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -125,7 +125,14 @@ public: mCover = NULL; mMovement.mMoveState = ModeStop; }; - + ~AIController() + { + SAFE_DELETE(mAIInfo); + SAFE_DELETE(mNav); + clearGoal(); + clearAim(); + clearCover(); + } DECLARE_CONOBJECT(AIController); }; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 57d2be27a..abe6d512a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -29,6 +29,14 @@ AINavigation::AINavigation(AIController* controller) mNavSize = Regular; } +AINavigation::~AINavigation() +{ +#ifdef TORQUE_NAVIGATION_ENABLED + clearPath(); + clearFollow(); +#endif +} + NavMesh* AINavigation::findNavMesh() const { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index ad9ae6a70..978ae01c6 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -35,7 +35,7 @@ struct AINavigation AINavigation() = delete; AINavigation(AIController* controller); - + ~AINavigation(); /// Stores information about a path. struct PathData { /// Pointer to path object. diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 00f929742..e48f204e6 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -1643,7 +1643,7 @@ Player::Player() mLastAbsoluteYaw = 0.0f; mLastAbsolutePitch = 0.0f; mLastAbsoluteRoll = 0.0f; - + mAIController = NULL; afx_init(); } @@ -1654,6 +1654,7 @@ Player::~Player() delete mShapeFPInstance[i]; mShapeFPInstance[i] = 0; } + if (mAIController) mAIController->deleteObject(); } diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 52dbb8e68..8c43a6e41 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -147,6 +147,7 @@ public: void onRemove() override; Point2F getSteering() { return mSteering; }; + F32 getThrottle() { return mThrottle;}; /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; From 2d0bcbcf8d7fc734987f8359eed87bfb1cd9b9b2 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 11:36:27 -0500 Subject: [PATCH 23/47] behavioural change: feeding an AInfo an object with a 0 radius causes the class to fill in radius from that objects bounds box also, vehicle direct hooks --- Engine/source/T3D/AI/AIInfo.cpp | 2 ++ Engine/source/T3D/player.cpp | 2 +- Engine/source/T3D/vehicles/vehicle.cpp | 25 ++++++++++++++++++++++++- Engine/source/T3D/vehicles/vehicle.h | 6 +++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index c2c11de9a..2f0022658 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -39,6 +39,8 @@ AIInfo::AIInfo(AIController* controller, SimObjectPtr objIn, F32 ra mPosition = mLastPos = objIn->getPosition(); mRadius = radIn; mPosSet = false; + if (radIn == 0.0f) + mRadius = mMax(objIn->getObjBox().len_x(), objIn->getObjBox().len_y()) * 0.5; }; AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index e48f204e6..938fc3e6d 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2264,7 +2264,7 @@ bool Player::setAIController(SimObjectId controller) { if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) { - mAIController->setAIInfo(this, mAIController->mControllerData->mMoveTolerance); + mAIController->setAIInfo(this); return true; } Con::errorf("unable to find AIController : %i", controller); diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index cfbd9a876..cd420c9d3 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -405,6 +405,7 @@ Vehicle::Vehicle() mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; mPhysicsRep = NULL; + mAIController = NULL; } U32 Vehicle::getCollisionMask() @@ -471,7 +472,7 @@ bool Vehicle::onAdd() void Vehicle::onRemove() { SAFE_DELETE(mPhysicsRep); - + if (mAIController) mAIController->deleteObject(); U32 i=0; for( i=0; imControllerData) + { + mAIController->setAIInfo(this); + return true; + } + Con::errorf("unable to find AIController : %i", controller); + mAIController = NULL; + return false; +} + +DefineEngineMethod(Vehicle, setAIController, bool, (S32 controller), , "") +{ + return object->setAIController(controller); +} + +DefineEngineMethod(Vehicle, getAIController, AIController*, (), , "") +{ + return object->getAIController(); +} diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 8c43a6e41..4dd518552 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -27,6 +27,8 @@ #include "T3D/rigidShape.h" #endif +#include "T3D/AI/AIController.h" + class ParticleEmitter; class ParticleEmitterData; class ClippedPolyList; @@ -98,7 +100,7 @@ class Vehicle : public RigidShape Point2F mSteering; F32 mThrottle; bool mJetting; - + AIController* mAIController; GFXStateBlockRef mSolidSB; SimObjectPtr mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS]; @@ -148,6 +150,8 @@ public: Point2F getSteering() { return mSteering; }; F32 getThrottle() { return mThrottle;}; + bool setAIController(SimObjectId controller); + AIController* getAIController() { return mAIController; }; /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; From 712404c9b44feb8e94b17e228caaf3b8ea90ccfe Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 12:28:49 -0500 Subject: [PATCH 24/47] hook up Vehicle's getAIMove(Move*); list aiControllerData's in the datablock. though the command is still required to set the controler and look up the relevant db for game specific logic --- Engine/source/T3D/AI/AIController.cpp | 2 +- Engine/source/T3D/player.cpp | 6 ++++-- Engine/source/T3D/vehicles/vehicle.cpp | 25 +++++++++++++++++++++++++ Engine/source/T3D/vehicles/vehicle.h | 4 ++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 974f5e10a..89d1decd3 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -31,6 +31,7 @@ IMPLEMENT_CONOBJECT(AIController); //----------------------------------------------------------------------------- void AIController::throwCallback(const char* name) { + Con::warnf("throwCallback: %s", name); Con::executef(mControllerData, name, getIdString()); //controller data callbacks GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); @@ -676,7 +677,6 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F maxSteeringAngle = vd->maxSteeringAngle; Point2F steering = wvo->getSteering(); - if (finalYaw < 5 && steering.x != 0.0f) steerState = Straight; else if (finalYaw < 5) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 938fc3e6d..18a0560b7 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -461,7 +461,7 @@ PlayerData::PlayerData() physicsPlayerType = StringTable->EmptyString(); mControlMap = StringTable->EmptyString(); - + mAIControllData = NULL; dMemset( actionList, 0, sizeof(actionList) ); } @@ -740,8 +740,10 @@ void PlayerData::initPersistFields() endGroup( "Camera" ); addGroup( "Movement" ); - addField("controlMap", TypeString, Offset(mControlMap, PlayerData), + addField("controlMap", TypeString, Offset(mControlMap, PlayerData), "@brief movemap used by these types of objects.\n\n"); + addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, PlayerData), + "@brief ai controller 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" diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index cd420c9d3..ac01b74e7 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -147,6 +147,7 @@ VehicleData::VehicleData() collDamageThresholdVel = 20; collDamageMultiplier = 0.05f; enablePhysicsRep = true; + mAIControllData = NULL; } @@ -320,6 +321,13 @@ void VehicleData::initPersistFields() "velocity).\n\nCurrently unused." ); endGroup("Collision"); + addGroup("Movement"); + addField("controlMap", TypeString, Offset(mControlMap, VehicleData), + "@brief movemap used by these types of objects.\n\n"); + addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, VehicleData), + "@brief ai controller used by these types of objects.\n\n"); + endGroup("Collision"); + addGroup("Steering"); addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat, "@brief Additional force applied to the vehicle when it is jetting.\n\n" @@ -501,6 +509,12 @@ void Vehicle::processTick(const Move* move) if ( isMounted() ) return; + // If we're not being controlled by a client, let the + // AI sub-module get a chance at producing a move. + Move aiMove; + if (!move && isServerObject() && getAIMove(&aiMove)) + move = &aiMove; + // Warp to catch up to server if (mDelta.warpCount < mDelta.warpTicks) { @@ -1233,6 +1247,17 @@ bool Vehicle::setAIController(SimObjectId controller) return false; } +bool Vehicle::getAIMove(Move* move) +{ + if (mAIController) + { + mAIController->getAIMove(move); //actual result + return true; + } + + return false; +} + DefineEngineMethod(Vehicle, setAIController, bool, (S32 controller), , "") { return object->setAIController(controller); diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 4dd518552..ee3913a29 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -72,6 +72,8 @@ struct VehicleData : public RigidShapeData F32 numDmgEmitterAreas; bool enablePhysicsRep; + StringTableEntry mControlMap; + AIControllerData* mAIControllData; // VehicleData(); @@ -152,6 +154,8 @@ public: F32 getThrottle() { return mThrottle;}; bool setAIController(SimObjectId controller); AIController* getAIController() { return mAIController; }; + virtual bool getAIMove(Move*); + /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; From d36cf317075358cd718e1b5cc4da76c0d8666db4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 18:27:39 -0500 Subject: [PATCH 25/47] more safeties. simplified reverse steering calc --- Engine/source/T3D/AI/AIController.cpp | 16 ++++------------ Engine/source/T3D/AI/AIController.h | 5 ++--- Engine/source/T3D/player.cpp | 2 +- Engine/source/T3D/vehicles/vehicle.cpp | 11 +++++++---- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 89d1decd3..ad0243b18 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -609,7 +609,6 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt } IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); - // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) @@ -695,19 +694,13 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F steerState = Left; } - F32 turnAdjust = myAngle - steering.x; - F32 throttle = wvo->getThrottle(); - if (throttle < 0.0f) + if (throttle < 0.0f && steerState != Straight) { - F32 reverseReduction = 0.25f; - if (steerState == Left) - steerState = Right; - else if (steerState == Right) - steerState = Left; - turnAdjust *= reverseReduction; - myAngle *= reverseReduction; + F32 reverseReduction = 0.25; + steering.x = steering.x * reverseReduction * throttle; } + F32 turnAdjust = myAngle - steering.x; F32 steer = 0; switch (steerState) @@ -746,5 +739,4 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat movePtr->yaw = getSteeringAngle(obj, location); } }; -void AIWheeledVehicleControllerData::resolveTriggerState(AIController* obj, Move* movePtr) {}; #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 43c9be04c..5e8b8bf0e 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -161,7 +161,8 @@ public: resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); }; ~AIControllerData() {}; - + void packData(BitStream* stream) override {}; + void unpackData(BitStream* stream) override {}; static void initPersistFields(); DECLARE_CONOBJECT(AIControllerData); @@ -222,11 +223,9 @@ public: AIWheeledVehicleControllerData() { resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); - resolveTriggerStatePtr.bind(this, &AIWheeledVehicleControllerData::resolveTriggerState); } F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); - void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 18a0560b7..ee715517b 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -1744,7 +1744,6 @@ bool Player::onAdd() world ); mPhysicsRep->setTransform( getTransform() ); } - mAIController = NULL; return true; } @@ -2287,6 +2286,7 @@ DefineEngineMethod(Player, getAIController, AIController*, (), , "") bool Player::getAIMove(Move* move) { + if (!isServerObject()) return false; if (mAIController) { mAIController->getAIMove(move); //actual result diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index ac01b74e7..e6a86baad 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -147,6 +147,7 @@ VehicleData::VehicleData() collDamageThresholdVel = 20; collDamageMultiplier = 0.05f; enablePhysicsRep = true; + mControlMap = StringTable->EmptyString(); mAIControllData = NULL; } @@ -505,16 +506,17 @@ void Vehicle::processTick(const Move* move) { PROFILE_SCOPE( Vehicle_ProcessTick ); - ShapeBase::processTick(move); - if ( isMounted() ) - return; - // If we're not being controlled by a client, let the // AI sub-module get a chance at producing a move. Move aiMove; if (!move && isServerObject() && getAIMove(&aiMove)) move = &aiMove; + ShapeBase::processTick(move); + if ( isMounted() ) + return; + + // Warp to catch up to server if (mDelta.warpCount < mDelta.warpTicks) { @@ -1249,6 +1251,7 @@ bool Vehicle::setAIController(SimObjectId controller) bool Vehicle::getAIMove(Move* move) { + if (!isServerObject()) return false; if (mAIController) { mAIController->getAIMove(move); //actual result From 3210325f3fff93d0c989912587d3285297c5cf33 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 04:25:36 -0500 Subject: [PATCH 26/47] elevated mAicontroller to shapebase aiwheeleedveiclecontrollerdata resolvespeed now only touches throttle objects assigned aicontrollers now reflect that by thier objecttype basic flocking --- Engine/source/T3D/AI/AIController.cpp | 191 +++++++++++++----- Engine/source/T3D/AI/AIController.h | 30 ++- Engine/source/T3D/AI/AIInfo.h | 4 +- Engine/source/T3D/AI/AINavigation.cpp | 124 ++++++++++++ Engine/source/T3D/AI/AINavigation.h | 1 + Engine/source/T3D/gameFunctions.cpp | 1 + Engine/source/T3D/objectTypes.h | 2 +- Engine/source/T3D/player.cpp | 41 ---- Engine/source/T3D/player.h | 7 - Engine/source/T3D/shapeBase.cpp | 52 ++++- Engine/source/T3D/shapeBase.h | 8 + Engine/source/T3D/vehicles/vehicle.cpp | 41 +--- Engine/source/T3D/vehicles/vehicle.h | 5 - Engine/source/console/simObject.cpp | 2 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 28 ++- 15 files changed, 352 insertions(+), 185 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index ad0243b18..8740165d0 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -122,6 +122,27 @@ bool AIController::getAIMove(Move* movePtr) Point3F location = eye.getPosition(); Point3F rotation = sbo->getTransform().toEuler(); + // 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() && getAim()->mObj) + { + GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); + if (getAim()->checkInLos(gbo)) + { + if (!getAim()->mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + getAim()->mTargetInLOS = true; + } + } + else if (getAim()->mTargetInLOS) + { + throwCallback("onTargetExitLOS"); + getAim()->mTargetInLOS = false; + } + } + #ifdef TORQUE_NAVIGATION_ENABLED if (sbo->getDamageState() == ShapeBase::Enabled && getGoal()) { @@ -181,6 +202,7 @@ bool AIController::getAIMove(Move* movePtr) } #endif // TORQUE_NAVIGATION_ENABLED + getNav()->flock(); // Orient towards the aim point, aim object, or towards // our destination. if (getAim() || mMovement.mMoveState != ModeStop) @@ -189,7 +211,7 @@ bool AIController::getAIMove(Move* movePtr) if (getAim()) mMovement.mAimLocation = getAim()->getPosition(); else - mMovement.mAimLocation = getNav()->mMoveDestination; + mMovement.mAimLocation = getNav()->getMoveDestination(); mControllerData->resolveYawPtr(this, location, movePtr); mControllerData->resolvePitchPtr(this, location, movePtr); @@ -197,11 +219,10 @@ bool AIController::getAIMove(Move* movePtr) if (mMovement.mMoveState != AIController::ModeStop) { - F32 xDiff = getNav()->mMoveDestination.x - location.x; - F32 yDiff = getNav()->mMoveDestination.y - location.y; + F32 xDiff = getNav()->getMoveDestination().x - location.x; + F32 yDiff = getNav()->getMoveDestination().y - location.y; if (mFabs(xDiff) < mControllerData->mMoveTolerance && mFabs(yDiff) < mControllerData->mMoveTolerance) { - mMovement.mMoveState = AIController::ModeStop; getNav()->onReachDestination(); } else @@ -214,27 +235,7 @@ bool AIController::getAIMove(Move* movePtr) mControllerData->resolveTriggerStatePtr(this, movePtr); - // 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() && getAim()->mObj) - { - GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); - if (getAim()->checkInLos(gbo)) - { - if (!getAim()->mTargetInLOS) - { - throwCallback("onTargetEnterLOS"); - getAim()->mTargetInLOS = true; - } - } - else if (getAim()->mTargetInLOS) - { - throwCallback("onTargetExitLOS"); - getAim()->mTargetInLOS = false; - } - } - + getAIInfo()->mLastPos = getAIInfo()->getPosition(); return true; } @@ -250,17 +251,18 @@ void AIController::Movement::stopMove() { mMoveState = ModeStop; #ifdef TORQUE_NAVIGATION_ENABLED - mControllerRef->getNav()->clearPath(); - mControllerRef->clearCover(); - mControllerRef->getNav()->clearFollow(); + getCtrl()->getNav()->clearPath(); + getCtrl()->clearCover(); + getCtrl()->getNav()->clearFollow(); #endif } void AIController::Movement::onStuck() { - mControllerRef->throwCallback("onMoveStuck"); + mMoveState = AIController::ModeStuck; + getCtrl()->throwCallback("onMoveStuck"); #ifdef TORQUE_NAVIGATION_ENABLED - if (!mControllerRef->getNav()->getPath().isNull()) - mControllerRef->getNav()->repath(); + if (!getCtrl()->getNav()->getPath().isNull()) + getCtrl()->getNav()->repath(); #endif } @@ -386,28 +388,28 @@ void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* mo void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; - F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; + F32 xDiff = obj->getNav()->getMoveDestination().x - location.x; + F32 yDiff = obj->getNav()->getMoveDestination().y - location.y; Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); // Build move direction in world space if (mIsZero(xDiff)) - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value; + movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; + movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs @@ -430,15 +432,11 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m 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; } } @@ -453,36 +451,57 @@ void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr) void AIControllerData::resolveStuck(AIController* obj) { - if (obj->mMovement.mMoveState == AIController::ModeStop) return; + if (obj->mMovement.mMoveState == AIController::ModeStuck) return; if (!obj->getGoal()) return; ShapeBase* sbo = dynamic_cast(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) { - // We should check to see if we are stuck... - F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len(); - if (locationDelta < mMoveStuckTolerance) + if (obj->mMovement.mMoveStuckTestCountdown > 0) + --obj->mMovement.mMoveStuckTestCountdown; + else { // 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->mMovement.onStuck(); - obj->throwCallback("onStuck"); } + obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay; } } - obj->getAIInfo()->mLastPos = obj->getAIInfo()->getPosition(); } } +AIControllerData::AIControllerData() +{ + mMoveTolerance = 0.25; + mFollowTolerance = 1.0; + mAttackRadius = 2.0; + mMoveStuckTolerance = 0.01f; + mMoveStuckTestDelay = 30; + mLinkTypes = LinkData(AllFlags); + mNavSize = AINavigation::Regular; + + mFlocking.mChance = 100; + mFlocking.mMin = 1.0f; + mFlocking.mMax = 3.0f; + mFlocking.mSideStep = 0.125f; + + resolveYawPtr.bind(this, &AIControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); + resolveRollPtr.bind(this, &AIControllerData::resolveRoll); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); + resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); +} + void AIControllerData::initPersistFields() { docsURL; @@ -518,6 +537,14 @@ void AIControllerData::initPersistFields() addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance considered in firing range for callback purposes."); + addFieldV("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent, + "@brief chance of flocking."); + addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat, + "@brief min flocking separation distance."); + addFieldV("FlockMax", TypeRangedF32, Offset(mFlocking.mMax, AIControllerData), &CommonValidators::PositiveFloat, + "@brief max flocking clustering distance."); + addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before we stop moving out of the way."); endGroup("AI"); #ifdef TORQUE_NAVIGATION_ENABLED @@ -626,7 +653,7 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F // What is our target Point3F desired; - desired = obj->getNav()->mMoveDestination; + desired = obj->getNav()->getMoveDestination(); MatrixF mat = wvo->getTransform(); Point3F center, front; @@ -739,4 +766,60 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat movePtr->yaw = getSteeringAngle(obj, location); } }; + +void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) +{ + F32 xDiff = obj->getNav()->getMoveDestination().x - location.x; + F32 yDiff = obj->getNav()->getMoveDestination().y - location.y; + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); + + Point2F movTarg; + + // Build move direction in world space + if (mIsZero(xDiff)) + movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; + else + { + if (mIsZero(yDiff)) + movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; + else + { + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value; + movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; + movTarg.y = (location.y > obj->getNav()->getMoveDestination().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(movTarg.x, movTarg.y, 0.0f), &newMove); + movTarg.y = newMove.y; + + // Set Throttle. We'll slow down once we get close + // to try and stop on the spot... + if (obj->mMovement.mMoveSlowdown) + { + F32 throttle = obj->mMovement.mMoveSpeed; + F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); + F32 maxDist = mMoveTolerance * 2; + if (dist < maxDist) + throttle *= dist / maxDist; + movePtr->y *= throttle; + } + else + { + movePtr->y *= obj->mMovement.mMoveSpeed; + } +} #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 5e8b8bf0e..9dba64129 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -84,6 +84,7 @@ public: struct Movement { AIController* mControllerRef; + AIController* getCtrl() { return mControllerRef; }; MoveState mMoveState; F32 mMoveSpeed = 1.0; void setMoveSpeed(F32 speed) { mMoveSpeed = speed; }; @@ -101,6 +102,7 @@ public: struct TriggerState { AIController* mControllerRef; + AIController* getCtrl() { return mControllerRef; }; bool mMoveTriggers[MaxTriggerKeys]; // Trigger sets/gets void setMoveTrigger(U32 slot, const bool isSet = true); @@ -143,23 +145,7 @@ class AIControllerData : public SimDataBlock { public: - AIControllerData() - { - mMoveTolerance = 0.25; - mFollowTolerance = 1.0; - mAttackRadius = 2.0; - mMoveStuckTolerance = 0.01f; - mMoveStuckTestDelay = 30; - mLinkTypes = LinkData(AllFlags); - mNavSize = AINavigation::Regular; - - resolveYawPtr.bind(this, &AIControllerData::resolveYaw); - resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); - resolveRollPtr.bind(this, &AIControllerData::resolveRoll); - resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); - resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); - resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); - }; + AIControllerData(); ~AIControllerData() {}; void packData(BitStream* stream) override {}; void unpackData(BitStream* stream) override {}; @@ -171,6 +157,14 @@ public: 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 + + struct Flocking { + U32 mChance; // chance of flocking + F32 mMin; // min flocking separation distance + F32 mMax; // max flocking clustering distance + F32 mSideStep; // Distance from destination before we stop moving out of the way + } mFlocking; + /// Types of link we can use. LinkData mLinkTypes; AINavigation::NavSize mNavSize; @@ -223,9 +217,11 @@ public: AIWheeledVehicleControllerData() { resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); } F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index a392f4553..e64bd38d3 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -22,8 +22,8 @@ #ifndef _AIINFO_H_ #define _AIINFO_H_ -#ifndef _SHAPEBASE_H_ -#include "T3D/shapeBase.h" +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" #endif class AIController; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index abe6d512a..1171af58a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -21,6 +21,9 @@ //----------------------------------------------------------------------------- #include "AINavigation.h" #include "AIController.h" +#include "T3D/shapeBase.h" + +static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType | AIObjectType; AINavigation::AINavigation(AIController* controller) { @@ -283,6 +286,127 @@ void AINavigation::clearPath() mPathData = PathData(); } +void AINavigation::flock() +{ + AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking; + SimObjectPtr obj = getCtrl()->getAIInfo()->mObj; + + if (mRandI(0,100) > flockingData.mChance) + return; + + obj->disableCollision(); + Point3F pos = obj->getBoxCenter(); + Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2); + + F32 maxFlocksq = flockingData.mMax * flockingData.mMax; + + if (getCtrl()->getGoal()) + { + Point3F dest = mMoveDestination; + + if (getCtrl()->mMovement.mMoveState == AIController::ModeStuck) + { + Point3F shuffle = Point3F(mRandF() - 0.5, mRandF() - 0.5, 0); + shuffle.normalize(); + dest += shuffle * flockingData.mMin; + } + + dest.z = pos.z; + if ((pos - dest).len() > flockingData.mSideStep) + { + //find closest object + SimpleQueryList sql; + Box3F queryBox = Box3F(pos - searchArea, pos + searchArea); + obj->getContainer()->findObjects(queryBox, AIObjectType, SimpleQueryList::insertionCallback, &sql); + sql.mList.remove(obj); + + Point3F avoidanceOffset = Point3F::Zero; + U32 found = 0; + + //avoid objects in the way + RayInfo info; + if (obj->getContainer()->castRay(pos, dest + Point3F(0, 0, obj->getObjBox().len_z() / 2), sAILoSMask, &info)) + { + Point3F blockerOffset = (info.point - dest); + blockerOffset.z = 0; + avoidanceOffset += blockerOffset; + } + + //avoid bots that are too close + for (U32 i = 0; i < sql.mList.size(); i++) + { + ShapeBase* other = dynamic_cast(sql.mList[i]); + Point3F objectCenter = other->getBoxCenter(); + + F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; + sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + + Point3F offset = (pos - objectCenter); + F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares + if ((flockingData.mMin > 0) && (offsetLensq < (sumRad * sumRad))) + { + other->disableCollision(); + if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info)) + { + found++; + offset.normalizeSafe(); + offset *= sumRad; + avoidanceOffset += offset; //accumulate total group, move away from that + } + other->enableCollision(); + } + } + //if we don't have to worry about bumping into one another (nothing found lower than minFLock), see about grouping up + if (found == 0) + { + for (U32 i = 0; i < sql.mList.size(); i++) + { + ShapeBase* other = static_cast(sql.mList[i]); + Point3F objectCenter = other->getBoxCenter(); + + F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; + sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + + Point3F offset = (pos - objectCenter); + if ((flockingData.mMin > 0) && ((sumRad * sumRad) < (maxFlocksq))) + { + other->disableCollision(); + if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info)) + { + found++; + offset.normalizeSafe(); + offset *= sumRad; + avoidanceOffset -= offset; // subtract total group, move toward it + } + other->enableCollision(); + } + } + } + + avoidanceOffset.z = 0; + avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75; + avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; + if (avoidanceOffset.lenSquared() < (maxFlocksq)) + { + avoidanceOffset.normalizeSafe(); + dest += avoidanceOffset; + } + + //if we're not jumping... + if ((mPathData.path) && !(mPathData.path->getFlags(mPathData.index) & JumpFlag)) + { + //make sure we don't run off a cliff + Point3F zlen(0, 0, getCtrl()->getAIInfo()->mRadius); + if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) + { + mMoveDestination = dest; + } + } + } + } + obj->enableCollision(); +} + DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), "@brief Tells the AI to move to the location provided\n\n" diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 978ae01c6..d4e66c3c1 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -96,6 +96,7 @@ struct AINavigation /// Move to the specified node in the current path. void moveToNode(S32 node); + void flock(); }; #endif diff --git a/Engine/source/T3D/gameFunctions.cpp b/Engine/source/T3D/gameFunctions.cpp index c40e2f081..24487c5ec 100644 --- a/Engine/source/T3D/gameFunctions.cpp +++ b/Engine/source/T3D/gameFunctions.cpp @@ -669,6 +669,7 @@ static void RegisterGameFunctions() Con::setIntVariable("$TypeMasks::PathShapeObjectType", PathShapeObjectType); // PATHSHAPE END Con::setIntVariable("$TypeMasks::TurretObjectType", TurretObjectType); + Con::setIntVariable("$TypeMasks::AIObjectType", AIObjectType); Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut, "InOut ease for curve movement.\n" diff --git a/Engine/source/T3D/objectTypes.h b/Engine/source/T3D/objectTypes.h index c6b05df74..47fb2edee 100644 --- a/Engine/source/T3D/objectTypes.h +++ b/Engine/source/T3D/objectTypes.h @@ -174,7 +174,7 @@ enum SceneObjectTypes /// @see TurretShape TurretObjectType = BIT(29), N_A_31 = BIT(30), - N_A_32 = BIT(31), + AIObjectType = BIT(31), /// @} }; diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index ee715517b..00fd037b0 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -461,7 +461,6 @@ PlayerData::PlayerData() physicsPlayerType = StringTable->EmptyString(); mControlMap = StringTable->EmptyString(); - mAIControllData = NULL; dMemset( actionList, 0, sizeof(actionList) ); } @@ -742,8 +741,6 @@ void PlayerData::initPersistFields() addGroup( "Movement" ); addField("controlMap", TypeString, Offset(mControlMap, PlayerData), "@brief movemap used by these types of objects.\n\n"); - addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, PlayerData), - "@brief ai controller 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" @@ -1645,7 +1642,6 @@ Player::Player() mLastAbsoluteYaw = 0.0f; mLastAbsolutePitch = 0.0f; mLastAbsoluteRoll = 0.0f; - mAIController = NULL; afx_init(); } @@ -1656,7 +1652,6 @@ Player::~Player() delete mShapeFPInstance[i]; mShapeFPInstance[i] = 0; } - if (mAIController) mAIController->deleteObject(); } @@ -2260,42 +2255,6 @@ void Player::advanceTime(F32 dt) } } } - -bool Player::setAIController(SimObjectId controller) -{ - if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) - { - mAIController->setAIInfo(this); - return true; - } - Con::errorf("unable to find AIController : %i", controller); - mAIController = NULL; - return false; -} - -DefineEngineMethod(Player, setAIController, bool, (S32 controller), , "") -{ - return object->setAIController(controller); -} - -DefineEngineMethod(Player, getAIController, AIController*, (), , "") -{ - return object->getAIController(); -} - - -bool Player::getAIMove(Move* move) -{ - if (!isServerObject()) return false; - if (mAIController) - { - mAIController->getAIMove(move); //actual result - return true; - } - - return false; -} - void Player::setState(ActionState state, U32 recoverTicks) { if (state != mState) { diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index df10ba291..2c6c88ab5 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -55,7 +55,6 @@ class OpenVRTrackedObject; #include "navigation/navMesh.h" #include "navigation/coverPoint.h" #endif // TORQUE_NAVIGATION_ENABLED -#include "AI/AIController.h" //---------------------------------------------------------------------------- @@ -354,7 +353,6 @@ 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; @@ -496,7 +494,6 @@ protected: SimObjectPtr mControlObject; ///< Controlling object - AIController* mAIController; /// @name Animation threads & data /// @{ @@ -762,10 +759,6 @@ public: Point3F getMomentum() const override; void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; - virtual bool getAIMove(Move*); - bool setAIController(SimObjectId controller); - AIController* getAIController() { return mAIController; }; - bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? // diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 968e8fb9b..38e1b57a9 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -69,6 +69,7 @@ #include "core/stream/fileStream.h" #include "T3D/accumulationVolume.h" #include "console/persistenceManager.h" +#include "AI/AIController.h" IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData); @@ -195,7 +196,8 @@ ShapeBaseData::ShapeBaseData() useEyePoint( false ), isInvincible( false ), renderWhenDestroyed( true ), - inheritEnergyFromMount( false ) + inheritEnergyFromMount( false ), + mAIControllData(NULL) { INIT_ASSET(Shape); INIT_ASSET(DebrisShape); @@ -544,6 +546,10 @@ void ShapeBaseData::initPersistFields() addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData)); INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional."); endGroup( "Shapes" ); + addGroup("Movement"); + addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, ShapeBaseData), + "@brief ai controller used by these types of objects.\n\n"); + endGroup("Movement"); addGroup("Particle Effects"); addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData), @@ -981,7 +987,8 @@ ShapeBase::ShapeBase() mCameraFov( 90.0f ), mIsControlled( false ), mLastRenderFrame( 0 ), - mLastRenderDistance( 0.0f ) + mLastRenderDistance( 0.0f ), + mAIController(NULL) { mTypeMask |= ShapeBaseObjectType | LightObjectType; @@ -1032,6 +1039,7 @@ ShapeBase::~ShapeBase() cur->next = sFreeTimeoutList; sFreeTimeoutList = cur; } + if (mAIController) mAIController->deleteObject(); } void ShapeBase::initPersistFields() @@ -5449,3 +5457,43 @@ DefineEngineMethod(ShapeBase, getNodePoint, Point3F, (const char* nodeName), , return pos; } + +bool ShapeBase::setAIController(SimObjectId controller) +{ + if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) + { + mAIController->setAIInfo(this); + mTypeMask |= AIObjectType; + return true; + } + Con::errorf("unable to find AIController : %i", controller); + mAIController = NULL; + mTypeMask |= ~AIObjectType; + return false; +} + +bool ShapeBase::getAIMove(Move* move) +{ + if (!isServerObject()) return false; + if (!(mTypeMask & VehicleObjectType || mTypeMask & PlayerObjectType)) return false; //only support players and vehicles for now + if (mAIController) + { + mAIController->getAIMove(move); //actual result + mTypeMask |= AIObjectType; + return true; + } + mAIController = NULL; + mTypeMask &= ~AIObjectType; + return false; +} + + +DefineEngineMethod(ShapeBase, setAIController, bool, (S32 controller), , "") +{ + return object->setAIController(controller); +} + +DefineEngineMethod(ShapeBase, getAIController, AIController*, (), , "") +{ + return object->getAIController(); +} diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 66a48fc29..c272110ed 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -88,6 +88,8 @@ class ShapeBase; class SFXSource; class SFXTrack; class SFXProfile; +struct AIController; +struct AIControllerData; typedef void* Light; @@ -555,6 +557,7 @@ public: U32 cubeDescId; ReflectorDesc *reflectorDesc; + AIControllerData* mAIControllData; /// @name Destruction /// /// Everyone likes to blow things up! @@ -1754,6 +1757,11 @@ public: /// Returns true if this object is controlling by something bool isControlled() { return(mIsControlled); } + AIController* mAIController; + bool setAIController(SimObjectId controller); + AIController* getAIController() { return mAIController; }; + virtual bool getAIMove(Move* move); + /// Returns true if this object is being used as a camera in first person bool isFirstPerson() const; diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index e6a86baad..98819cbd2 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -148,7 +148,6 @@ VehicleData::VehicleData() collDamageMultiplier = 0.05f; enablePhysicsRep = true; mControlMap = StringTable->EmptyString(); - mAIControllData = NULL; } @@ -325,9 +324,7 @@ void VehicleData::initPersistFields() addGroup("Movement"); addField("controlMap", TypeString, Offset(mControlMap, VehicleData), "@brief movemap used by these types of objects.\n\n"); - addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, VehicleData), - "@brief ai controller used by these types of objects.\n\n"); - endGroup("Collision"); + endGroup("Movement"); addGroup("Steering"); addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat, @@ -414,7 +411,6 @@ Vehicle::Vehicle() mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; mPhysicsRep = NULL; - mAIController = NULL; } U32 Vehicle::getCollisionMask() @@ -481,7 +477,6 @@ bool Vehicle::onAdd() void Vehicle::onRemove() { SAFE_DELETE(mPhysicsRep); - if (mAIController) mAIController->deleteObject(); U32 i=0; for( i=0; imControllerData) - { - mAIController->setAIInfo(this); - return true; - } - Con::errorf("unable to find AIController : %i", controller); - mAIController = NULL; - return false; -} - -bool Vehicle::getAIMove(Move* move) -{ - if (!isServerObject()) return false; - if (mAIController) - { - mAIController->getAIMove(move); //actual result - return true; - } - - return false; -} - -DefineEngineMethod(Vehicle, setAIController, bool, (S32 controller), , "") -{ - return object->setAIController(controller); -} - -DefineEngineMethod(Vehicle, getAIController, AIController*, (), , "") -{ - return object->getAIController(); -} diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index ee3913a29..ba7bf8e68 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -73,7 +73,6 @@ struct VehicleData : public RigidShapeData bool enablePhysicsRep; StringTableEntry mControlMap; - AIControllerData* mAIControllData; // VehicleData(); @@ -102,7 +101,6 @@ class Vehicle : public RigidShape Point2F mSteering; F32 mThrottle; bool mJetting; - AIController* mAIController; GFXStateBlockRef mSolidSB; SimObjectPtr mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS]; @@ -152,9 +150,6 @@ public: Point2F getSteering() { return mSteering; }; F32 getThrottle() { return mThrottle;}; - bool setAIController(SimObjectId controller); - AIController* getAIController() { return mAIController; }; - virtual bool getAIMove(Move*); /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index fdbc13d72..3a5dca309 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -82,7 +82,7 @@ ImplementBitfieldType(GameTypeMasksType, { SceneObjectTypes::PathShapeObjectType, "$TypeMasks::PathShapeObjectType", "Path-following Objects.\n" }, { SceneObjectTypes::TurretObjectType, "$TypeMasks::TurretObjectType", "Turret Objects.\n" }, { SceneObjectTypes::N_A_31, "$TypeMasks::N_A_31", "unused 31st bit.\n" }, -{ SceneObjectTypes::N_A_32, "$TypeMasks::N_A_32", "unused 32nd bit.\n" }, +{ SceneObjectTypes::AIObjectType, "$TypeMasks::AIObjectType", "AIObjectType.\n" }, EndImplementBitfieldType; diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 3237816bd..609eb7751 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -37,6 +37,7 @@ #include "gui/buttons/guiButtonCtrl.h" #include "gui/worldEditor/undoActions.h" #include "T3D/gameBase/gameConnection.h" +#include "T3D/AI/AIController.h" IMPLEMENT_CONOBJECT(GuiNavEditorCtrl); @@ -226,12 +227,11 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) missionCleanup->addObject(obj); } mPlayer = obj; - Player* po = dynamic_cast(obj); - if (!po) return; //todo, more types - if (po->getAIController()) + ShapeBase* sbo = dynamic_cast(obj); + if (sbo->getAIController()) { - if (po->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); } else { @@ -398,12 +398,11 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if(ri.object) { mPlayer = ri.object; - Player* po = dynamic_cast(ri.object); - if (!po) return; //todo, more types - if (po->getAIController()) + ShapeBase* sbo = dynamic_cast(ri.object); + if (sbo->getAIController()) { - if (po->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); } else { @@ -413,12 +412,11 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) } else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) { - Player* po = dynamic_cast(mPlayer.getPointer()); - if (!po) return; //todo, more types - if (po->getAIController()) + ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); + if (sbo->getAIController()) { - if (po->getAIController()->mControllerData) - po->getAIController()->getNav()->setPathDestination(ri.point,true); + if (sbo->getAIController()->mControllerData) + sbo->getAIController()->getNav()->setPathDestination(ri.point,true); } } } From a609917ceedf67b46dcb2f825e79c5a5ae87d6ed Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 06:37:28 -0500 Subject: [PATCH 27/47] put the flocking protocol ion the repath command itself with a high weight so it prioritizes avoidance vs straight following --- Engine/source/T3D/AI/AIController.cpp | 23 +++++++++++++---------- Engine/source/T3D/AI/AINavigation.cpp | 17 ++++++++++++++--- Engine/source/T3D/AI/AINavigation.h | 1 + 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 8740165d0..5abbc916c 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -110,6 +110,7 @@ void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offs } #ifdef TORQUE_NAVIGATION_ENABLED + bool AIController::getAIMove(Move* movePtr) { *movePtr = NullMove; @@ -178,12 +179,17 @@ bool AIController::getAIMove(Move* movePtr) } getGoal()->mInRange = false; } - if (getGoal()->getDist() < mControllerData->mFollowTolerance && !getGoal()->mInRange) + if (getGoal()->getDist() < mControllerData->mFollowTolerance ) { getNav()->clearPath(); mMovement.mMoveState = ModeStop; - getGoal()->mInRange = true; - throwCallback("onTargetInRange"); + + if (!getGoal()->mInRange) + { + getGoal()->mInRange = true; + throwCallback("onTargetInRange"); + } + else getGoal()->mInRange = false; } else { @@ -195,14 +201,11 @@ bool AIController::getAIMove(Move* movePtr) throwCallback("onTargetInFiringRange"); } } - else - getGoal()->mInFiringRange = false; + else getGoal()->mInFiringRange = false; } } } #endif // TORQUE_NAVIGATION_ENABLED - - getNav()->flock(); // Orient towards the aim point, aim object, or towards // our destination. if (getAim() || mMovement.mMoveState != ModeStop) @@ -472,8 +475,8 @@ void AIControllerData::resolveStuck(AIController* obj) if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0) { obj->mMovement.onStuck(); + obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay; } - obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay; } } } @@ -489,10 +492,10 @@ AIControllerData::AIControllerData() mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; - mFlocking.mChance = 100; + mFlocking.mChance = 90; mFlocking.mMin = 1.0f; mFlocking.mMax = 3.0f; - mFlocking.mSideStep = 0.125f; + mFlocking.mSideStep = 0.01f; resolveYawPtr.bind(this, &AIControllerData::resolveYaw); resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 1171af58a..fdc68de5a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -126,11 +126,21 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - // If we're following, get their position. - mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance) + { + flock(); + mPathData.path->mTo = mMoveDestination; + } + else + { + // If we're following, get their position. + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + } + // Update from position and replan. mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); mPathData.path->plan(); + // Move to first node (skip start pos). moveToNode(1); } @@ -393,8 +403,9 @@ void AINavigation::flock() } //if we're not jumping... - if ((mPathData.path) && !(mPathData.path->getFlags(mPathData.index) & JumpFlag)) + if (mJump == None) { + dest.z -= obj->getObjBox().len_z()*0.5; //make sure we don't run off a cliff Point3F zlen(0, 0, getCtrl()->getAIInfo()->mRadius); if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index d4e66c3c1..acc0aadf2 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -70,6 +70,7 @@ struct AINavigation Point3F mMoveDestination; void setMoveDestination(const Point3F& location, bool slowdown); Point3F getMoveDestination() { return mMoveDestination; }; + void onReachDestination(); /// NavMesh we pathfind on. From b864908efd9c73e1102f464bdf9566e11a451b16 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 07:10:18 -0500 Subject: [PATCH 28/47] clean up a few stray bits --- Engine/source/T3D/AI/AINavigation.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index fdc68de5a..0cb42d998 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -301,9 +301,6 @@ void AINavigation::flock() AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking; SimObjectPtr obj = getCtrl()->getAIInfo()->mObj; - if (mRandI(0,100) > flockingData.mChance) - return; - obj->disableCollision(); Point3F pos = obj->getBoxCenter(); Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2); @@ -349,7 +346,8 @@ void AINavigation::flock() Point3F objectCenter = other->getBoxCenter(); F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; - sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + sumRad += separation; Point3F offset = (pos - objectCenter); F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares @@ -360,7 +358,7 @@ void AINavigation::flock() { found++; offset.normalizeSafe(); - offset *= sumRad; + offset *= sumRad + separation; avoidanceOffset += offset; //accumulate total group, move away from that } other->enableCollision(); @@ -375,7 +373,8 @@ void AINavigation::flock() Point3F objectCenter = other->getBoxCenter(); F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; - sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + sumRad += separation; Point3F offset = (pos - objectCenter); if ((flockingData.mMin > 0) && ((sumRad * sumRad) < (maxFlocksq))) @@ -385,7 +384,7 @@ void AINavigation::flock() { found++; offset.normalizeSafe(); - offset *= sumRad; + offset *= sumRad + separation; avoidanceOffset -= offset; // subtract total group, move toward it } other->enableCollision(); @@ -398,7 +397,6 @@ void AINavigation::flock() avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; if (avoidanceOffset.lenSquared() < (maxFlocksq)) { - avoidanceOffset.normalizeSafe(); dest += avoidanceOffset; } From 185acd23e068a097576300ed9c2a8cedd6fa068d Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 14:35:18 -0500 Subject: [PATCH 29/47] stop controllobjects from fighting --- Engine/source/T3D/shapeBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 38e1b57a9..4cd5d333b 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -5475,6 +5475,7 @@ bool ShapeBase::setAIController(SimObjectId controller) bool ShapeBase::getAIMove(Move* move) { if (!isServerObject()) return false; + if (isControlled()) return false; //something else is steering us, so use that one's controller if (!(mTypeMask & VehicleObjectType || mTypeMask & PlayerObjectType)) return false; //only support players and vehicles for now if (mAIController) { From 32d95d3b8b5041d6593d06709ee647345a5200e4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 20 Apr 2025 15:29:26 -0500 Subject: [PATCH 30/47] put AIPlayer support back, and it as the default class to spawn --- Engine/source/navigation/guiNavEditorCtrl.cpp | 52 ++++++++++++++----- .../datablocks/defaultDatablocks.tscript | 6 +-- .../game/tools/navEditor/NavEditorGui.gui | 2 +- .../game/tools/navEditor/main.tscript | 4 +- .../game/tools/navEditor/navEditor.tscript | 15 ++++-- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 609eb7751..90a6e7f78 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -227,15 +227,23 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) missionCleanup->addObject(obj); } mPlayer = obj; - ShapeBase* sbo = dynamic_cast(obj); - if (sbo->getAIController()) + AIPlayer* asAIPlayer = dynamic_cast(obj); + if (asAIPlayer) //try direct { - if (sbo->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); } else { - Con::executef(this, "onPlayerSelected"); + ShapeBase* sbo = dynamic_cast(obj); + if (sbo->getAIController()) + { + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } } } } @@ -398,25 +406,41 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if(ri.object) { mPlayer = ri.object; - ShapeBase* sbo = dynamic_cast(ri.object); - if (sbo->getAIController()) + AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); + if (asAIPlayer) //try direct { - if (sbo->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); } else { - Con::executef(this, "onPlayerSelected"); + ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); + if (sbo->getAIController()) + { + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } } } } else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) { - ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); - if (sbo->getAIController()) + AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); + if (asAIPlayer) //try direct { - if (sbo->getAIController()->mControllerData) - sbo->getAIController()->getNav()->setPathDestination(ri.point,true); + asAIPlayer->setPathDestination(ri.point); + } + else + { + ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); + if (sbo->getAIController()) + { + if (sbo->getAIController()->mControllerData) + sbo->getAIController()->getNav()->setPathDestination(ri.point, true); + } } } } diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index ffdc4806c..6d9ee6067 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -172,10 +172,10 @@ datablock LightAnimData( SpinLightAnim ) datablock AIPlayerControllerData( aiPlayerControl ) { - + moveTolerance = 0.25; followTolerance = 1.0; mAttackRadius = 2; }; datablock AIWheeledVehicleControllerData( aiCarControl ) { - -}; \ No newline at end of file + moveTolerance = 1.0; followTolerance = 2.0; mAttackRadius = 5.0; +}; diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 92505f4e7..18646fe1a 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -485,7 +485,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { VertSizing = "bottom"; Extent = "90 18"; text = "Stop"; - command = "NavEditorGui.getPlayer().stop();"; + command = "NavEditorGui.stop();"; }; }; }; diff --git a/Templates/BaseGame/game/tools/navEditor/main.tscript b/Templates/BaseGame/game/tools/navEditor/main.tscript index bfc06d103..3c2ef7aab 100644 --- a/Templates/BaseGame/game/tools/navEditor/main.tscript +++ b/Templates/BaseGame/game/tools/navEditor/main.tscript @@ -87,7 +87,7 @@ function NavEditorPlugin::onWorldEditorStartup(%this) // Add ourselves to the Editor Settings window. exec("./NavEditorSettingsTab.gui"); - //ESettingsWindow.addTabPage(ENavEditorSettingsPage); + ESettingsWindow.addTabPage(ENavEditorSettingsPage); ENavEditorSettingsPage.init(); // Add items to World Editor Creator @@ -241,7 +241,7 @@ function NavEditorPlugin::initSettings(%this) { EditorSettings.beginGroup("NavEditor", true); - EditorSettings.setDefaultValue("SpawnClass", "Player"); + EditorSettings.setDefaultValue("SpawnClass", "AIPlayer"); EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData"); EditorSettings.endGroup(); diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 75a84977b..159599ba6 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -459,13 +459,13 @@ function NavEditorGui::onLinkSelected(%this, %flags) function NavEditorGui::onPlayerSelected(%this, %flags) { - if (!isObject(%this.getPlayer().aiController)) + if (!isObject(%this.getPlayer().aiController) && (!(%this.getPlayer().isMemberOfClass("AIPlayer")))) { %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; %this.getPlayer().setAIController(%this.getPlayer().aiController); - NavMeshIgnore(%this.getPlayer(), true); - %this.getPlayer().setDamageState("Enabled"); } + NavMeshIgnore(%this.getPlayer(), true); + %this.getPlayer().setDamageState("Enabled"); updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); } @@ -559,6 +559,15 @@ function NavEditorGui::followObject(%this) } } +function NavEditorGui::stop(%this) +{ + if (isObject(%this.getPlayer().aiController)) + %this.getPlayer().aiController.stop(); + else + { + NavEditorGui.getPlayer().stop(); + } +} function NavInspector::inspect(%this, %obj) { %name = ""; From fdb64b15a84da07755f374f0df9cd0b5217a1834 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 20 Apr 2025 21:41:04 -0500 Subject: [PATCH 31/47] stop spamming the console with callack status --- Engine/source/T3D/AI/AIController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 5abbc916c..daa06a12d 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -31,7 +31,7 @@ IMPLEMENT_CONOBJECT(AIController); //----------------------------------------------------------------------------- void AIController::throwCallback(const char* name) { - Con::warnf("throwCallback: %s", name); + //Con::warnf("throwCallback: %s", name); Con::executef(mControllerData, name, getIdString()); //controller data callbacks GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); From 6200a6f1fb3e2f5640321f41c22cd48e4b3a91a9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 21 Apr 2025 14:58:11 -0500 Subject: [PATCH 32/47] add a calibrateable mHeightTolerance for players this defaults to 0.001, for wheeledvehicles, 2.0, and for flyingvehicles, 200 fix naveditor cript not looking up the object.datablock.aicontrollerdata fix AIWheeledVehicleControllerData not binding the relevant ::resolvespeed also the relevant ::resolvespeed now lowers the throttle post-turning add AIFlyingVehicleControllerData --- Engine/source/T3D/AI/AIController.cpp | 223 ++++++++++++++---- Engine/source/T3D/AI/AIController.h | 29 ++- .../datablocks/defaultDatablocks.tscript | 6 + .../game/tools/navEditor/navEditor.tscript | 2 +- 4 files changed, 205 insertions(+), 55 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index daa06a12d..c06983a72 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -24,6 +24,7 @@ #include "T3D/player.h" #include "T3D/rigidShape.h" #include "T3D/vehicles/wheeledVehicle.h" +#include "T3D/vehicles/flyingVehicle.h" IMPLEMENT_CONOBJECT(AIController); @@ -164,16 +165,13 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->getDist() > mControllerData->mFollowTolerance) { - F32 raylength = 2.0; //for vehicles SceneObject* obj = getAIInfo()->mObj->getObjectMount(); if (!obj) { obj = getAIInfo()->mObj; - raylength = 0.001f; //for jumping } - RayInfo info; - if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, raylength), StaticShapeObjectType, &info)) + if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, mControllerData->mHeightTolerance), StaticShapeObjectType, &info)) { getNav()->repath(); } @@ -399,9 +397,11 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m if (mIsZero(xDiff)) movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; else + { if (mIsZero(yDiff)) movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; else + { if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); @@ -414,7 +414,8 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; } - + } + } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; @@ -484,13 +485,14 @@ void AIControllerData::resolveStuck(AIController* obj) AIControllerData::AIControllerData() { - mMoveTolerance = 0.25; - mFollowTolerance = 1.0; - mAttackRadius = 2.0; + mMoveTolerance = 0.25f; + mFollowTolerance = 1.0f; + mAttackRadius = 2.0f; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30; mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; + mHeightTolerance = 0.001f; mFlocking.mChance = 90; mFlocking.mMin = 1.0f; @@ -638,6 +640,8 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt #endif // TORQUE_NAVIGATION_ENABLED } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) @@ -772,57 +776,172 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - F32 xDiff = obj->getNav()->getMoveDestination().x - location.x; - F32 yDiff = obj->getNav()->getMoveDestination().y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); - - Point2F movTarg; - - // Build move direction in world space - if (mIsZero(xDiff)) - movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; - else + WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) { - if (mIsZero(yDiff)) - movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a WheeledVehicle + + Parent::resolveSpeed(obj, location, movePtr); + + VehicleData* db = static_cast(wvo->getDataBlock()); + movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; + movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(AIFlyingVehicleControllerData); +// Build a Triangle .. calculate angle of rotation required to meet target.. +// man there has to be a better way! >:) +F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return 0;//not a FlyingVehicle + + DrivingState steerState = SteerNull; + + // What is our target + Point3F desired; + desired = obj->getNav()->getMoveDestination(); + + MatrixF mat = wvo->getTransform(); + Point3F center, front; + Point3F wFront; + Box3F box = wvo->getObjBox(); + + box.getCenter(¢er); + front = center; + front.y = box.maxExtents.y; // should be true for all these objects + + obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); + front = center + front; + + Point3F objFront = front; + Point3F offset = front - center; + EulerF rot; + rot = mat.toEuler(); + MatrixF transform(rot); + transform.mulV(offset, &wFront); + front = wFront + center; + + Point3F ftoc; + ftoc.x = mFabs(front.x - center.x); + ftoc.y = mFabs(front.y - center.y); + ftoc.z = mFabs(front.z - center.z); + F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); + + Point3F ltoc; + ltoc.x = mFabs(desired.x - center.x); + ltoc.y = mFabs(desired.y - center.y); + ltoc.z = mFabs(desired.z - center.z); + F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); + + Point3F ftol; + ftol.x = mFabs(front.x - desired.x); + ftol.y = mFabs(front.y - desired.y); + ftol.z = mFabs(front.z - desired.z); + F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); + + F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); + + F32 finalYaw = mRadToDeg(myAngle); + + F32 maxSteeringAngle = 0; + + VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + maxSteeringAngle = vd->maxSteeringAngle; + + Point2F steering = wvo->getSteering(); + if (finalYaw < 5 && steering.x != 0.0f) + steerState = Straight; + else if (finalYaw < 5) + steerState = SteerNull; + else + {// Quickly Hack out left or right turn info + Point3F rotData = objFront - desired; + MatrixF leftM(-rot); + Point3F leftP; + leftM.mulV(rotData, &leftP); + leftP = leftP + desired; + + if (leftP.x < desired.x) + steerState = Right; else - { - if (mFabs(xDiff) > mFabs(yDiff)) - { - F32 value = mFabs(yDiff / xDiff); - movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value; - movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; - } - else - { - F32 value = mFabs(xDiff / yDiff); - movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; - movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; - } - } + steerState = Left; } - // 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(movTarg.x, movTarg.y, 0.0f), &newMove); - movTarg.y = newMove.y; - // Set Throttle. We'll slow down once we get close - // to try and stop on the spot... - if (obj->mMovement.mMoveSlowdown) + F32 throttle = wvo->getThrottle(); + if (throttle < 0.0f && steerState != Straight) { - F32 throttle = obj->mMovement.mMoveSpeed; - F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); - F32 maxDist = mMoveTolerance * 2; - if (dist < maxDist) - throttle *= dist / maxDist; - movePtr->y *= throttle; + F32 reverseReduction = 0.25; + steering.x = steering.x * reverseReduction * throttle; } - else + F32 turnAdjust = myAngle - steering.x; + + F32 steer = 0; + switch (steerState) { - movePtr->y *= obj->mMovement.mMoveSpeed; + case Left: + steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; + break; + case Right: + steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; + break; + case Straight: + steer = -steering.x; + break; + default: + break; + }; + + // Con::printf("AI Steering : %f", steer); + return steer; +} + + +void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } + if (!wvo) return;//not a FlyingVehicle + + // Orient towards our destination. + if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { + movePtr->yaw = getSteeringAngle(obj, location); + } +}; + +void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a FlyingVehicle + + Parent::resolveSpeed(obj, location, movePtr); + + VehicleData* db = static_cast(wvo->getDataBlock()); + movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; + movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 9dba64129..06b5e7ff3 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -157,7 +157,7 @@ public: 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 - + F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath struct Flocking { U32 mChance; // chance of flocking F32 mMin; // min flocking separation distance @@ -217,12 +217,37 @@ public: AIWheeledVehicleControllerData() { resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); - resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveSpeedPtr.bind(this, &AIWheeledVehicleControllerData::resolveSpeed); + mHeightTolerance = 2.0f; } F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); }; + +class AIFlyingVehicleControllerData : public AIControllerData +{ + typedef AIControllerData Parent; + + enum DrivingState { + SteerNull, + Left, + Right, + Straight + }; + +public: + AIFlyingVehicleControllerData() + { + resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); + mHeightTolerance = 200.0f; + } + F32 getSteeringAngle(AIController* obj, Point3F location); + void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIFlyingVehicleControllerData); +}; #endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index 6d9ee6067..bc7cc1101 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -179,3 +179,9 @@ datablock AIWheeledVehicleControllerData( aiCarControl ) { moveTolerance = 1.0; followTolerance = 2.0; mAttackRadius = 5.0; }; + +datablock AIFlyingVehicleControllerData( aiPlaneControl ) +{ + moveTolerance = 2.0; followTolerance = 5.0; mAttackRadius = 10.0; +}; + diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 159599ba6..7f7b01804 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -461,7 +461,7 @@ function NavEditorGui::onPlayerSelected(%this, %flags) { if (!isObject(%this.getPlayer().aiController) && (!(%this.getPlayer().isMemberOfClass("AIPlayer")))) { - %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; + %this.getPlayer().aiController = new AIController(){ ControllerData = %this.getPlayer().getDatablock().aiControllerData; }; %this.getPlayer().setAIController(%this.getPlayer().aiController); } NavMeshIgnore(%this.getPlayer(), true); From 584093f48dade42425c3cd76123c86308826d55c Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 21 Apr 2025 19:13:31 -0500 Subject: [PATCH 33/47] aiInfo getPosition now optionally takes a doCastray bool (off by default) AIFlyingVehicleControllerData add flightfloor and cieling resolvepitch from (portions of) the old aiflyingvehicle resource no reversing for flyingvehicles, so bottom out resolvespeed at 0 --- Engine/source/T3D/AI/AIController.cpp | 74 ++++++++++++++++++++++----- Engine/source/T3D/AI/AIController.h | 9 +++- Engine/source/T3D/AI/AIInfo.cpp | 22 +++++++- Engine/source/T3D/AI/AIInfo.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 10 ++-- 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c06983a72..b0653c513 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -158,7 +158,7 @@ bool AIController::getAIMove(Move* movePtr) if (getGoal()->mObj.isValid()) getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); else if (getGoal()->mPosSet) - getNav()->setPathDestination(getGoal()->getPosition()); + getNav()->setPathDestination(getGoal()->getPosition(true)); } } else @@ -271,7 +271,7 @@ DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , "@brief Sets the move speed for an AI object.\n\n" "@param speed A speed multiplier between 0.0 and 1.0. " - "This is multiplied by the AIPlayer's base movement rates (as defined in " + "This is multiplied by the AIController controlled object's base movement rates (as defined in " "its PlayerData datablock)\n\n" "@see getMoveDestination()\n") @@ -290,7 +290,7 @@ DefineEngineMethod(AIController, getMoveSpeed, F32, (), , } DefineEngineMethod(AIController, stop, void, (), , - "@brief Tells the AIPlayer to stop moving.\n\n") + "@brief Tells the AIController controlled object to stop moving.\n\n") { object->mMovement.stopMove(); } @@ -514,28 +514,35 @@ void AIControllerData::initPersistFields() 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 " + "When the AIController controlled object 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, " + "it helps the AIController controlled object 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 " + "When the AIController controlled object 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, " + "it helps the AIController controlled object 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 " + "When the AIController controlled object controlled object is moving to a given destination, if it ever moves less than " + "this tolerance during a single tick, the AIController controlled object is considered stuck. At this point " "the onMoveStuck() callback is called on the datablock.\n"); + addFieldV("HeightTolerance", TypeRangedF32, Offset(mHeightTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIController controlled object 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 AIController controlled object 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("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 " + "@brief The number of ticks to wait before testing if the AIController controlled object is stuck.\n\n" + "When the AIController controlled object is asked to move, this property is the number of ticks to wait " + "before the AIController controlled object starts to check if it is stuck. This delay allows the AIController controlled object " "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"); @@ -796,6 +803,22 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIFlyingVehicleControllerData); + +void AIFlyingVehicleControllerData::initPersistFields() +{ + docsURL; + addGroup("AI"); + + addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + "@brief Max height we can target."); + + addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + "@brief Max height we can target."); + + endGroup("AI"); + + Parent::initPersistFields(); +} // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) @@ -927,6 +950,31 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati } }; +void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a FlyingVehicle + + Point3F up = wvo->getTransform().getUpVector(); + up.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotPitch = mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = -dotPitch; +} + void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); @@ -942,6 +990,6 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca VehicleData* db = static_cast(wvo->getDataBlock()); movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; - movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; + movePtr->y = mMax(movePtr->y*1.1 - wvo->getSteering().y / db->maxSteeringAngle, 0.0f); } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 06b5e7ff3..eeaa13a94 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -236,17 +236,24 @@ class AIFlyingVehicleControllerData : public AIControllerData Right, Straight }; - + F32 mFlightFloor; + F32 mFlightCeiling; public: AIFlyingVehicleControllerData() { resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch); resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); mHeightTolerance = 200.0f; + mFlightCeiling = 200.0f; + mFlightFloor = 1.0; } + static void initPersistFields(); F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIFlyingVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index 2f0022658..f4a02e47f 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -52,10 +52,30 @@ AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) mPosSet = true; }; +Point3F AIInfo::getPosition(bool doCastray) +{ + Point3F pos = (mObj.isValid()) ? mObj->getPosition() : mPosition; + if (doCastray) + { + RayInfo info; + if (gServerContainer.castRay(pos, pos - Point3F(0, 0, getCtrl()->mControllerData->mHeightTolerance), StaticShapeObjectType, &info)) + { + pos = info.point; + } + } + + return pos; +} + F32 AIInfo::getDist() { AIInfo* controlObj = getCtrl()->getAIInfo(); - F32 ret = VectorF(controlObj->getPosition() - getPosition()).len(); + Point3F targPos = getPosition(); + + if (mFabs(targPos.z - controlObj->getPosition().z) < getCtrl()->mControllerData->mHeightTolerance) + targPos.z = controlObj->getPosition().z; + + F32 ret = VectorF(controlObj->getPosition() - targPos).len(); ret -= controlObj->mRadius + mRadius; return ret; } diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index e64bd38d3..8aede0cae 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -36,7 +36,7 @@ struct AIInfo Point3F mPosition, mLastPos; bool mPosSet; F32 mRadius; - Point3F getPosition() { return (mObj.isValid()) ? mObj->getPosition() : mPosition; } + Point3F getPosition(bool doCastray = false); F32 getDist(); AIInfo() = delete; AIInfo(AIController* controller); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 0cb42d998..ff3e25b22 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -134,11 +134,11 @@ void AINavigation::repath() else { // If we're following, get their position. - mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(true); } // Update from position and replan. - mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); + mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(true); mPathData.path->plan(); // Move to first node (skip start pos). @@ -215,8 +215,8 @@ bool AINavigation::setPathDestination(const Point3F& pos, bool replace) NavPath* path = new NavPath(); path->mMesh = mNavMesh; - path->mFrom = getCtrl()->getAIInfo()->getPosition(); - path->mTo = getCtrl()->getGoal()->getPosition(); + path->mFrom = getCtrl()->getAIInfo()->getPosition(true); + path->mTo = getCtrl()->getGoal()->getPosition(true); path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; @@ -257,7 +257,7 @@ void AINavigation::followObject() if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) return; - if (setPathDestination(getCtrl()->getGoal()->getPosition())) + if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) { getCtrl()->clearCover(); } From 71b8046bb1ff28a69dd7d4f9ea92f99aa6e6ce68 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 22 Apr 2025 00:34:32 -0500 Subject: [PATCH 34/47] shift mode stop and stuck below modeslowing so we can just do basic math filtering fix a few bits of eroneous sidestepping attempts by flying and wheeled vehicles. more work on flyingvehicle resolvepitch and resolvespeed --- Engine/source/T3D/AI/AIController.cpp | 73 +++++++++++++++------------ Engine/source/T3D/AI/AIController.h | 2 +- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index b0653c513..38c0660b8 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -455,7 +455,7 @@ void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr) void AIControllerData::resolveStuck(AIController* obj) { - if (obj->mMovement.mMoveState == AIController::ModeStuck) return; + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; if (!obj->getGoal()) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during @@ -766,6 +766,7 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!wvo) { @@ -783,6 +784,7 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!wvo) { @@ -795,7 +797,7 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc Parent::resolveSpeed(obj, location, movePtr); VehicleData* db = static_cast(wvo->getDataBlock()); - movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; + movePtr->x = 0; movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; } @@ -823,14 +825,14 @@ void AIFlyingVehicleControllerData::initPersistFields() // man there has to be a better way! >:) F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return 0;//not a FlyingVehicle + if (!fvo) return 0;//not a FlyingVehicle DrivingState steerState = SteerNull; @@ -838,10 +840,10 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l Point3F desired; desired = obj->getNav()->getMoveDestination(); - MatrixF mat = wvo->getTransform(); + MatrixF mat = fvo->getTransform(); Point3F center, front; Point3F wFront; - Box3F box = wvo->getObjBox(); + Box3F box = fvo->getObjBox(); box.getCenter(¢er); front = center; @@ -882,10 +884,10 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l F32 maxSteeringAngle = 0; - VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + VehicleData* vd = (VehicleData*)(fvo->getDataBlock()); maxSteeringAngle = vd->maxSteeringAngle; - Point2F steering = wvo->getSteering(); + Point2F steering = fvo->getSteering(); if (finalYaw < 5 && steering.x != 0.0f) steerState = Straight; else if (finalYaw < 5) @@ -904,7 +906,7 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l steerState = Left; } - F32 throttle = wvo->getThrottle(); + F32 throttle = fvo->getThrottle(); if (throttle < 0.0f && steerState != Straight) { F32 reverseReduction = 0.25; @@ -935,61 +937,66 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return;//not a FlyingVehicle + if (!fvo) return;//not a FlyingVehicle // Orient towards our destination. - if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { - movePtr->yaw = getSteeringAngle(obj, location); - } + movePtr->yaw = getSteeringAngle(obj, location); }; void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return;//not a FlyingVehicle + if (!fvo) return;//not a FlyingVehicle - Point3F up = wvo->getTransform().getUpVector(); + F32 lastPitch = fvo->getSteering().y* (1.0f - fvo->getThrottle()); + + Point3F up = fvo->getTransform().getUpVector(); up.normalize(); Point3F aimLoc = obj->mMovement.mAimLocation; aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); - // Get the Target to AI vector and normalize it. - Point3F toTarg = aimLoc - location; + // Get the AI to Target vector and normalize it. + Point3F toTarg = location-aimLoc; toTarg.normalize(); F32 dotPitch = mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = -dotPitch; + movePtr->pitch = dotPitch - lastPitch; + else + movePtr->pitch = -lastPitch; } void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return;//not a FlyingVehicle + if (!fvo) return;//not a FlyingVehicle Parent::resolveSpeed(obj, location, movePtr); - VehicleData* db = static_cast(wvo->getDataBlock()); - movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; - movePtr->y = mMax(movePtr->y*1.1 - wvo->getSteering().y / db->maxSteeringAngle, 0.0f); + movePtr->x = 0; + movePtr->y = mMax(movePtr->y, 0.0f); } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index eeaa13a94..b1ee5d485 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -42,9 +42,9 @@ protected: 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. + ModeMove, // AI is currently moving. ModeReverse // AI is reversing }; From d8ea8803c38ab6d89b14e9f0f8d41e58402183e9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 22 Apr 2025 22:06:32 -0500 Subject: [PATCH 35/47] simplify calcs by leveraging pre-existing matrix methods and dot product properties --- Engine/source/T3D/AI/AIController.cpp | 286 +++++--------------------- Engine/source/T3D/AI/AIController.h | 15 -- 2 files changed, 46 insertions(+), 255 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 38c0660b8..598dc6d5f8 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -650,119 +650,6 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); -// Build a Triangle .. calculate angle of rotation required to meet target.. -// man there has to be a better way! >:) -F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) -{ - WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) - { - //cover the case of a connection controling an object in turn controlling another - if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); - } - if (!wvo) return 0;//not a WheeledVehicle - - DrivingState steerState = SteerNull; - - // What is our target - Point3F desired; - desired = obj->getNav()->getMoveDestination(); - - MatrixF mat = wvo->getTransform(); - Point3F center, front; - Point3F wFront; - Box3F box = wvo->getObjBox(); - - box.getCenter(¢er); - front = center; - front.y = box.maxExtents.y; // should be true for all these objects - - obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); - front = center + front; - - Point3F objFront = front; - Point3F offset = front - center; - EulerF rot; - rot = mat.toEuler(); - MatrixF transform(rot); - transform.mulV(offset, &wFront); - front = wFront + center; - - Point3F ftoc; - ftoc.x = mFabs(front.x - center.x); - ftoc.y = mFabs(front.y - center.y); - ftoc.z = mFabs(front.z - center.z); - F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); - - Point3F ltoc; - ltoc.x = mFabs(desired.x - center.x); - ltoc.y = mFabs(desired.y - center.y); - ltoc.z = mFabs(desired.z - center.z); - F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); - - Point3F ftol; - ftol.x = mFabs(front.x - desired.x); - ftol.y = mFabs(front.y - desired.y); - ftol.z = mFabs(front.z - desired.z); - F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); - - F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); - - F32 finalYaw = mRadToDeg(myAngle); - - F32 maxSteeringAngle = 0; - - VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); - maxSteeringAngle = vd->maxSteeringAngle; - - Point2F steering = wvo->getSteering(); - if (finalYaw < 5 && steering.x != 0.0f) - steerState = Straight; - else if (finalYaw < 5) - steerState = SteerNull; - else - {// Quickly Hack out left or right turn info - Point3F rotData = objFront - desired; - MatrixF leftM(-rot); - Point3F leftP; - leftM.mulV(rotData, &leftP); - leftP = leftP + desired; - - if (leftP.x < desired.x) - steerState = Right; - else - steerState = Left; - } - - F32 throttle = wvo->getThrottle(); - if (throttle < 0.0f && steerState != Straight) - { - F32 reverseReduction = 0.25; - steering.x = steering.x * reverseReduction * throttle; - } - F32 turnAdjust = myAngle - steering.x; - - F32 steer = 0; - switch (steerState) - { - case Left: - steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; - break; - case Right: - steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; - break; - case Straight: - steer = -steering.x; - break; - default: - break; - }; - - // Con::printf("AI Steering : %f", steer); - return steer; -} - void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { @@ -776,10 +663,29 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat } if (!wvo) return;//not a WheeledVehicle - // Orient towards our destination. - if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { - movePtr->yaw = getSteeringAngle(obj, location); - } + F32 lastYaw = wvo->getSteering().x; + + Point3F right = wvo->getTransform().getRightVector(); + right.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotYaw = mDot(right, toTarg); + movePtr->yaw = -lastYaw; + + VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + F32 maxSteeringAngle = vd->maxSteeringAngle; + if (mFabs(dotYaw) > maxSteeringAngle * 1.5 && wvo->getThrottle() < 0.0f) + dotYaw *= -1.0f; + + if (dotYaw > maxSteeringAngle) dotYaw = maxSteeringAngle; + if (dotYaw < -maxSteeringAngle) dotYaw = -maxSteeringAngle; + + if (mFabs(dotYaw) > 0.05f) + movePtr->yaw = dotYaw - lastYaw; }; void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) @@ -798,7 +704,7 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc VehicleData* db = static_cast(wvo->getDataBlock()); movePtr->x = 0; - movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; + movePtr->y *= mMax((db->maxSteeringAngle-mFabs(movePtr->yaw) / db->maxSteeringAngle),0.75f); } //----------------------------------------------------------------------------- @@ -821,119 +727,6 @@ void AIFlyingVehicleControllerData::initPersistFields() Parent::initPersistFields(); } -// Build a Triangle .. calculate angle of rotation required to meet target.. -// man there has to be a better way! >:) -F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) -{ - FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!fvo) - { - //cover the case of a connection controling an object in turn controlling another - if (obj->getAIInfo()->mObj->getObjectMount()) - fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); - } - if (!fvo) return 0;//not a FlyingVehicle - - DrivingState steerState = SteerNull; - - // What is our target - Point3F desired; - desired = obj->getNav()->getMoveDestination(); - - MatrixF mat = fvo->getTransform(); - Point3F center, front; - Point3F wFront; - Box3F box = fvo->getObjBox(); - - box.getCenter(¢er); - front = center; - front.y = box.maxExtents.y; // should be true for all these objects - - obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); - front = center + front; - - Point3F objFront = front; - Point3F offset = front - center; - EulerF rot; - rot = mat.toEuler(); - MatrixF transform(rot); - transform.mulV(offset, &wFront); - front = wFront + center; - - Point3F ftoc; - ftoc.x = mFabs(front.x - center.x); - ftoc.y = mFabs(front.y - center.y); - ftoc.z = mFabs(front.z - center.z); - F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); - - Point3F ltoc; - ltoc.x = mFabs(desired.x - center.x); - ltoc.y = mFabs(desired.y - center.y); - ltoc.z = mFabs(desired.z - center.z); - F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); - - Point3F ftol; - ftol.x = mFabs(front.x - desired.x); - ftol.y = mFabs(front.y - desired.y); - ftol.z = mFabs(front.z - desired.z); - F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); - - F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); - - F32 finalYaw = mRadToDeg(myAngle); - - F32 maxSteeringAngle = 0; - - VehicleData* vd = (VehicleData*)(fvo->getDataBlock()); - maxSteeringAngle = vd->maxSteeringAngle; - - Point2F steering = fvo->getSteering(); - if (finalYaw < 5 && steering.x != 0.0f) - steerState = Straight; - else if (finalYaw < 5) - steerState = SteerNull; - else - {// Quickly Hack out left or right turn info - Point3F rotData = objFront - desired; - MatrixF leftM(-rot); - Point3F leftP; - leftM.mulV(rotData, &leftP); - leftP = leftP + desired; - - if (leftP.x < desired.x) - steerState = Right; - else - steerState = Left; - } - - F32 throttle = fvo->getThrottle(); - if (throttle < 0.0f && steerState != Straight) - { - F32 reverseReduction = 0.25; - steering.x = steering.x * reverseReduction * throttle; - } - F32 turnAdjust = myAngle - steering.x; - - F32 steer = 0; - switch (steerState) - { - case Left: - steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; - break; - case Right: - steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; - break; - case Straight: - steer = -steering.x; - break; - default: - break; - }; - - // Con::printf("AI Steering : %f", steer); - return steer; -} - void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { @@ -947,8 +740,19 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati } if (!fvo) return;//not a FlyingVehicle - // Orient towards our destination. - movePtr->yaw = getSteeringAngle(obj, location); + Point3F right = fvo->getTransform().getRightVector(); + right.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotYaw = mDot(right, toTarg); + movePtr->yaw = 0; + + if (mFabs(dotYaw) > 0.05f) + movePtr->yaw = dotYaw; }; void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) @@ -963,8 +767,6 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca } if (!fvo) return;//not a FlyingVehicle - F32 lastPitch = fvo->getSteering().y* (1.0f - fvo->getThrottle()); - Point3F up = fvo->getTransform().getUpVector(); up.normalize(); Point3F aimLoc = obj->mMovement.mAimLocation; @@ -974,12 +776,16 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F toTarg = location-aimLoc; toTarg.normalize(); - F32 dotPitch = mDot(up, toTarg); + movePtr->pitch = 0.0f; + Point3F forward = fvo->getTransform().getForwardVector(); + if (mDot(forward, toTarg)>0.0f) + { + F32 dotPitch = mDot(up, toTarg); - if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch - lastPitch; - else - movePtr->pitch = -lastPitch; + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = dotPitch; + } + } void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index b1ee5d485..b2fe8ffb7 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -206,13 +206,6 @@ class AIWheeledVehicleControllerData : public AIControllerData { typedef AIControllerData Parent; - enum DrivingState { - SteerNull, - Left, - Right, - Straight - }; - public: AIWheeledVehicleControllerData() { @@ -220,7 +213,6 @@ public: resolveSpeedPtr.bind(this, &AIWheeledVehicleControllerData::resolveSpeed); mHeightTolerance = 2.0f; } - F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); @@ -230,12 +222,6 @@ class AIFlyingVehicleControllerData : public AIControllerData { typedef AIControllerData Parent; - enum DrivingState { - SteerNull, - Left, - Right, - Straight - }; F32 mFlightFloor; F32 mFlightCeiling; public: @@ -249,7 +235,6 @@ public: mFlightFloor = 1.0; } static void initPersistFields(); - F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); void resolvePitch(AIController* obj, Point3F location, Move* movePtr); From c1d32a9fd69ef070ad5cd65404c35a9eeade3345 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 23 Apr 2025 00:05:57 -0500 Subject: [PATCH 36/47] more cleanups and standardizations --- Engine/source/T3D/AI/AIController.cpp | 30 ++++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 598dc6d5f8..9408dea9c 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -669,16 +669,17 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat right.normalize(); Point3F aimLoc = obj->mMovement.mAimLocation; - // Get the Target to AI vector and normalize it. - Point3F toTarg = aimLoc - location; + // Get the AI to Target vector and normalize it. + Point3F toTarg = location - aimLoc; toTarg.normalize(); - F32 dotYaw = mDot(right, toTarg); + F32 dotYaw = -mDot(right, toTarg); movePtr->yaw = -lastYaw; VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); F32 maxSteeringAngle = vd->maxSteeringAngle; - if (mFabs(dotYaw) > maxSteeringAngle * 1.5 && wvo->getThrottle() < 0.0f) + + if (mFabs(dotYaw) > maxSteeringAngle*1.5f) dotYaw *= -1.0f; if (dotYaw > maxSteeringAngle) dotYaw = maxSteeringAngle; @@ -745,10 +746,10 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati Point3F aimLoc = obj->mMovement.mAimLocation; // Get the Target to AI vector and normalize it. - Point3F toTarg = aimLoc - location; + Point3F toTarg = location - aimLoc; toTarg.normalize(); - F32 dotYaw = mDot(right, toTarg); + F32 dotYaw = -mDot(right, toTarg); movePtr->yaw = 0; if (mFabs(dotYaw) > 0.05f) @@ -772,19 +773,14 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F aimLoc = obj->mMovement.mAimLocation; aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); - // Get the AI to Target vector and normalize it. - Point3F toTarg = location-aimLoc; + // Get the Target to AI vector and normalize it. + Point3F toTarg = location - aimLoc; toTarg.normalize(); - + F32 lastPitch = fvo->getSteering().y; movePtr->pitch = 0.0f; - Point3F forward = fvo->getTransform().getForwardVector(); - if (mDot(forward, toTarg)>0.0f) - { - F32 dotPitch = mDot(up, toTarg); - - if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch; - } + F32 dotPitch = -mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = dotPitch - lastPitch; } From 6efb3843f6d63002d6db37a77a71e8936a12482d Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 23 Apr 2025 16:25:35 -0500 Subject: [PATCH 37/47] scale flyingvehicle ai pitch by the amount yaw forces it to roll --- Engine/source/T3D/AI/AIController.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 9408dea9c..2cf2c50e5 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -779,6 +779,13 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca F32 lastPitch = fvo->getSteering().y; movePtr->pitch = 0.0f; F32 dotPitch = -mDot(up, toTarg); + + FlyingVehicleData* db = static_cast(fvo->getDataBlock()); + + F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw * db->steeringRollForce); + dotPitch *= 1.0-(mClampF(rollAmt, 0.0,1.0)); // reduce pitch by how much we're rolling + dotPitch *= M_PI_F; + if (mFabs(dotPitch) > 0.05f) movePtr->pitch = dotPitch - lastPitch; From 675bdfe6b31c83fb8110bb4def04c33fb9e6765f Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 25 Apr 2025 18:50:16 -0500 Subject: [PATCH 38/47] fix pack/unpack data for AIControllerData's (though we still send nothing, we do need to mark it clientside as false) more pitchwork for flying vehicle drivers when flocking is irrelevant just path to next node --- Engine/source/T3D/AI/AIController.cpp | 13 ++++++------- Engine/source/T3D/AI/AIController.h | 4 ++-- Engine/source/T3D/AI/AINavigation.cpp | 18 +++++++++++------- Engine/source/T3D/AI/AINavigation.h | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 2cf2c50e5..7e383a5e8 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -777,17 +777,18 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F toTarg = location - aimLoc; toTarg.normalize(); F32 lastPitch = fvo->getSteering().y; + movePtr->pitch = 0.0f; - F32 dotPitch = -mDot(up, toTarg); + F32 dotPitch = mDot(up, toTarg); FlyingVehicleData* db = static_cast(fvo->getDataBlock()); - F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw * db->steeringRollForce); + F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw / (db->steeringRollForce+1.0)); dotPitch *= 1.0-(mClampF(rollAmt, 0.0,1.0)); // reduce pitch by how much we're rolling - dotPitch *= M_PI_F; + dotPitch *= M_2PI_F; if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch - lastPitch; + movePtr->pitch = dotPitch; } @@ -803,9 +804,7 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca } if (!fvo) return;//not a FlyingVehicle - Parent::resolveSpeed(obj, location, movePtr); - movePtr->x = 0; - movePtr->y = mMax(movePtr->y, 0.0f); + movePtr->y = obj->mMovement.mMoveSpeed; } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index b2fe8ffb7..92aa481ea 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -147,8 +147,8 @@ public: AIControllerData(); ~AIControllerData() {}; - void packData(BitStream* stream) override {}; - void unpackData(BitStream* stream) override {}; + void packData(BitStream* stream) override { Parent::packData(stream); }; + void unpackData(BitStream* stream) override { Parent::unpackData(stream); }; static void initPersistFields(); DECLARE_CONOBJECT(AIControllerData); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index ff3e25b22..9534313ea 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -126,9 +126,8 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance) + if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance && flock()) { - flock(); mPathData.path->mTo = mMoveDestination; } else @@ -296,7 +295,7 @@ void AINavigation::clearPath() mPathData = PathData(); } -void AINavigation::flock() +bool AINavigation::flock() { AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking; SimObjectPtr obj = getCtrl()->getAIInfo()->mObj; @@ -306,7 +305,7 @@ void AINavigation::flock() Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2); F32 maxFlocksq = flockingData.mMax * flockingData.mMax; - + bool flocking = false; if (getCtrl()->getGoal()) { Point3F dest = mMoveDestination; @@ -403,17 +402,22 @@ void AINavigation::flock() //if we're not jumping... if (mJump == None) { - dest.z -= obj->getObjBox().len_z()*0.5; + dest.z = obj->getPosition().z; //make sure we don't run off a cliff - Point3F zlen(0, 0, getCtrl()->getAIInfo()->mRadius); + Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance); if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) { - mMoveDestination = dest; + if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance) + { + mMoveDestination = dest; + flocking = true; + } } } } } obj->enableCollision(); + return flocking; } DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index acc0aadf2..39b1276a4 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -97,7 +97,7 @@ struct AINavigation /// Move to the specified node in the current path. void moveToNode(S32 node); - void flock(); + bool flock(); }; #endif From b2021caa6d49d7e18fa208ee11f9452205ec3ee9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 25 Apr 2025 20:36:22 -0500 Subject: [PATCH 39/47] skip sidestepping off a cliff raycast if we're not sidestepping take current velocity into account for vehicles since some degree of momentum will be maintained --- Engine/source/T3D/AI/AIController.cpp | 6 ++-- Engine/source/T3D/AI/AINavigation.cpp | 40 ++++++++++++++------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 7e383a5e8..237333f09 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -670,7 +670,7 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat Point3F aimLoc = obj->mMovement.mAimLocation; // Get the AI to Target vector and normalize it. - Point3F toTarg = location - aimLoc; + Point3F toTarg = (location + wvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); F32 dotYaw = -mDot(right, toTarg); @@ -746,7 +746,7 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati Point3F aimLoc = obj->mMovement.mAimLocation; // Get the Target to AI vector and normalize it. - Point3F toTarg = location - aimLoc; + Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); F32 dotYaw = -mDot(right, toTarg); @@ -774,7 +774,7 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); // Get the Target to AI vector and normalize it. - Point3F toTarg = location - aimLoc; + Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); F32 lastPitch = fvo->getSteering().y; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 9534313ea..f5d3fd2cd 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -306,6 +306,7 @@ bool AINavigation::flock() F32 maxFlocksq = flockingData.mMax * flockingData.mMax; bool flocking = false; + U32 found = 0; if (getCtrl()->getGoal()) { Point3F dest = mMoveDestination; @@ -327,7 +328,6 @@ bool AINavigation::flock() sql.mList.remove(obj); Point3F avoidanceOffset = Point3F::Zero; - U32 found = 0; //avoid objects in the way RayInfo info; @@ -390,27 +390,29 @@ bool AINavigation::flock() } } } - - avoidanceOffset.z = 0; - avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75; - avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; - if (avoidanceOffset.lenSquared() < (maxFlocksq)) + if (found > 0) { - dest += avoidanceOffset; - } - - //if we're not jumping... - if (mJump == None) - { - dest.z = obj->getPosition().z; - //make sure we don't run off a cliff - Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance); - if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) + avoidanceOffset.z = 0; + avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75; + avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; + if (avoidanceOffset.lenSquared() < (maxFlocksq)) { - if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance) + dest += avoidanceOffset; + } + + //if we're not jumping... + if (mJump == None) + { + dest.z = obj->getPosition().z; + //make sure we don't run off a cliff + Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance); + if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) { - mMoveDestination = dest; - flocking = true; + if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance) + { + mMoveDestination = dest; + flocking = true; + } } } } From 8fa132707d8904b94d0ce7e330fd0043ba07c799 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 26 Apr 2025 10:37:42 -0500 Subject: [PATCH 40/47] fix stop button --- Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 18646fe1a..92505f4e7 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -485,7 +485,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { VertSizing = "bottom"; Extent = "90 18"; text = "Stop"; - command = "NavEditorGui.stop();"; + command = "NavEditorGui.getPlayer().stop();"; }; }; }; From b643aa41a2e7585f82738758c049b78a8572c208 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 26 Apr 2025 21:04:06 -0500 Subject: [PATCH 41/47] take flight floor and ceiling into account for yaw too ditch roll compensation as it was causing more problems than it was solving --- Engine/source/T3D/AI/AIController.cpp | 27 +++++++------------ .../datablocks/defaultDatablocks.tscript | 1 + 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 237333f09..e4fa0882c 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -718,10 +718,10 @@ void AIFlyingVehicleControllerData::initPersistFields() docsURL; addGroup("AI"); - addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, - "@brief Max height we can target."); + addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::F32Range, + "@brief Min height we can target."); - addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::F32Range, "@brief Max height we can target."); endGroup("AI"); @@ -743,15 +743,15 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati Point3F right = fvo->getTransform().getRightVector(); right.normalize(); - Point3F aimLoc = obj->mMovement.mAimLocation; // Get the Target to AI vector and normalize it. + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); - F32 dotYaw = -mDot(right, toTarg); movePtr->yaw = 0; - + F32 dotYaw = -mDot(right, toTarg); if (mFabs(dotYaw) > 0.05f) movePtr->yaw = dotYaw; }; @@ -770,25 +770,18 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F up = fvo->getTransform().getUpVector(); up.normalize(); - Point3F aimLoc = obj->mMovement.mAimLocation; - aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); + // Get the Target to AI vector and normalize it. + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); - F32 lastPitch = fvo->getSteering().y; movePtr->pitch = 0.0f; F32 dotPitch = mDot(up, toTarg); - - FlyingVehicleData* db = static_cast(fvo->getDataBlock()); - - F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw / (db->steeringRollForce+1.0)); - dotPitch *= 1.0-(mClampF(rollAmt, 0.0,1.0)); // reduce pitch by how much we're rolling - dotPitch *= M_2PI_F; - if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch; + movePtr->pitch = dotPitch * M_2PI_F; } diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index bc7cc1101..512acd273 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -183,5 +183,6 @@ datablock AIWheeledVehicleControllerData( aiCarControl ) datablock AIFlyingVehicleControllerData( aiPlaneControl ) { moveTolerance = 2.0; followTolerance = 5.0; mAttackRadius = 10.0; + FlightFloor = 15; FlightCeiling = 150; }; From 7ed3f11c8ee8c57be0d9b7df44b1f96297bb5ef8 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 00:26:45 -0500 Subject: [PATCH 42/47] might as well go ahead and allow substitution statement support --- Engine/source/T3D/AI/AIController.cpp | 75 +++++++++++++++++++++++++++ Engine/source/T3D/AI/AIController.h | 6 ++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index e4fa0882c..18b5c2d4e 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -507,6 +507,30 @@ AIControllerData::AIControllerData() resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); } +AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clone) : SimDataBlock(other, temp_clone) +{ + mMoveTolerance = other.mMoveTolerance; + mFollowTolerance = other.mFollowTolerance; + mAttackRadius = other.mAttackRadius; + mMoveStuckTolerance = other.mMoveStuckTolerance; + mMoveStuckTestDelay = other.mMoveStuckTestDelay; + mLinkTypes = other.mLinkTypes; + mNavSize = other.mNavSize; + mHeightTolerance = other.mHeightTolerance; + + mFlocking.mChance = other.mFlocking.mChance; + mFlocking.mMin = other.mFlocking.mMin; + mFlocking.mMax = other.mFlocking.mMax; + mFlocking.mSideStep = other.mFlocking.mSideStep; + + resolveYawPtr.bind(this, &AIControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); + resolveRollPtr.bind(this, &AIControllerData::resolveRoll); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); + resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); +} + void AIControllerData::initPersistFields() { docsURL; @@ -583,6 +607,48 @@ void AIControllerData::initPersistFields() Parent::initPersistFields(); } +void AIControllerData::packData(BitStream* stream) +{ + Parent::packData(stream); + stream->write(mMoveTolerance); + stream->write(mFollowTolerance); + stream->write(mAttackRadius); + stream->write(mMoveStuckTolerance); + stream->write(mMoveStuckTestDelay); + //enums + stream->write(mLinkTypes.getFlags()); + stream->write((U32)mNavSize); + // end enums + stream->write(mHeightTolerance); + stream->write(mFlocking.mChance); + stream->write(mFlocking.mMin); + stream->write(mFlocking.mMax); + stream->write(mFlocking.mSideStep); +}; + +void AIControllerData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&mMoveTolerance); + stream->read(&mFollowTolerance); + stream->read(&mAttackRadius); + stream->read(&mMoveStuckTolerance); + stream->read(&mMoveStuckTestDelay); + //enums + U16 linkFlags; + stream->read(&linkFlags); + mLinkTypes = LinkData(linkFlags); + U32 navSize; + stream->read(&navSize); + mNavSize = (AINavigation::NavSize)(navSize); + // end enums + stream->read(&mHeightTolerance); + stream->read(&(mFlocking.mChance)); + stream->read(&(mFlocking.mMin)); + stream->read(&(mFlocking.mMax)); + stream->read(&(mFlocking.mSideStep)); +}; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIPlayerControllerData); @@ -729,6 +795,15 @@ void AIFlyingVehicleControllerData::initPersistFields() Parent::initPersistFields(); } +AIFlyingVehicleControllerData::AIFlyingVehicleControllerData(const AIFlyingVehicleControllerData& other, bool temp_clone) : AIControllerData(other, temp_clone) +{ + mFlightCeiling = other.mFlightCeiling; + mFlightFloor = other.mFlightFloor; + resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch); + resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); +} + void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 92aa481ea..14bddeccd 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -146,9 +146,10 @@ class AIControllerData : public SimDataBlock { public: AIControllerData(); + AIControllerData(const AIControllerData&, bool = false); ~AIControllerData() {}; - void packData(BitStream* stream) override { Parent::packData(stream); }; - void unpackData(BitStream* stream) override { Parent::unpackData(stream); }; + void packData(BitStream* stream) override; + void unpackData(BitStream* stream) override; static void initPersistFields(); DECLARE_CONOBJECT(AIControllerData); @@ -234,6 +235,7 @@ public: mFlightCeiling = 200.0f; mFlightFloor = 1.0; } + AIFlyingVehicleControllerData(const AIFlyingVehicleControllerData&, bool = false); static void initPersistFields(); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); From c6f19e5cae906dd42cdc69ad565760e8d8233516 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 12:49:48 -0500 Subject: [PATCH 43/47] fix nav editor stop comand --- Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 92505f4e7..18646fe1a 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -485,7 +485,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { VertSizing = "bottom"; Extent = "90 18"; text = "Stop"; - command = "NavEditorGui.getPlayer().stop();"; + command = "NavEditorGui.stop();"; }; }; }; From f2781501857b7613f9166e665ff9b6e7086e7148 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 14:31:27 -0500 Subject: [PATCH 44/47] TORQUE_NAVIGATION_ENABLED filtering --- Engine/source/T3D/AI/AIController.cpp | 92 ++++--- Engine/source/T3D/AI/AIController.h | 8 +- Engine/source/T3D/AI/AINavigation.cpp | 332 +++++++++++++------------- Engine/source/T3D/AI/AINavigation.h | 19 +- 4 files changed, 249 insertions(+), 202 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 18b5c2d4e..acd7063f6 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -110,8 +110,6 @@ void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offs mAimTarget->mAimOffset = offset; } -#ifdef TORQUE_NAVIGATION_ENABLED - bool AIController::getAIMove(Move* movePtr) { *movePtr = NullMove; @@ -145,9 +143,9 @@ bool AIController::getAIMove(Move* movePtr) } } -#ifdef TORQUE_NAVIGATION_ENABLED if (sbo->getDamageState() == ShapeBase::Enabled && getGoal()) { +#ifdef TORQUE_NAVIGATION_ENABLED if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); @@ -202,8 +200,39 @@ bool AIController::getAIMove(Move* movePtr) else getGoal()->mInFiringRange = false; } } - } +#else + if (getGoal()->getDist() > mControllerData->mMoveTolerance) + { + if (getGoal()->mPosSet) + getNav()->setPathDestination(getGoal()->getPosition(true)); + + getGoal()->mInRange = false; + } + if (getGoal()->getDist() < mControllerData->mMoveTolerance) + { + mMovement.mMoveState = ModeStop; + + if (!getGoal()->mInRange) + { + getGoal()->mInRange = true; + throwCallback("onTargetInRange"); + } + else getGoal()->mInRange = false; + } + else + { + if (getGoal()->getDist() < mControllerData->mAttackRadius) + { + if (!getGoal()->mInFiringRange) + { + getGoal()->mInFiringRange = true; + throwCallback("onTargetInFiringRange"); + } + } + else getGoal()->mInFiringRange = false; + } #endif // TORQUE_NAVIGATION_ENABLED + } // Orient towards the aim point, aim object, or towards // our destination. if (getAim() || mMovement.mMoveState != ModeStop) @@ -486,19 +515,20 @@ void AIControllerData::resolveStuck(AIController* obj) AIControllerData::AIControllerData() { mMoveTolerance = 0.25f; - mFollowTolerance = 1.0f; mAttackRadius = 2.0f; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30; - mLinkTypes = LinkData(AllFlags); - mNavSize = AINavigation::Regular; mHeightTolerance = 0.001f; +#ifdef TORQUE_NAVIGATION_ENABLED + mFollowTolerance = 1.0f; + mLinkTypes = LinkData(AllFlags); + mNavSize = AINavigation::Regular; mFlocking.mChance = 90; mFlocking.mMin = 1.0f; mFlocking.mMax = 3.0f; mFlocking.mSideStep = 0.01f; - +#endif resolveYawPtr.bind(this, &AIControllerData::resolveYaw); resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); resolveRollPtr.bind(this, &AIControllerData::resolveRoll); @@ -510,18 +540,20 @@ AIControllerData::AIControllerData() AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clone) : SimDataBlock(other, temp_clone) { mMoveTolerance = other.mMoveTolerance; - mFollowTolerance = other.mFollowTolerance; mAttackRadius = other.mAttackRadius; mMoveStuckTolerance = other.mMoveStuckTolerance; mMoveStuckTestDelay = other.mMoveStuckTestDelay; - mLinkTypes = other.mLinkTypes; - mNavSize = other.mNavSize; mHeightTolerance = other.mHeightTolerance; +#ifdef TORQUE_NAVIGATION_ENABLED + mFollowTolerance = other.mFollowTolerance; + mLinkTypes = other.mLinkTypes; + mNavSize = other.mNavSize; mFlocking.mChance = other.mFlocking.mChance; mFlocking.mMin = other.mFlocking.mMin; mFlocking.mMax = other.mFlocking.mMax; mFlocking.mSideStep = other.mFlocking.mSideStep; +#endif resolveYawPtr.bind(this, &AIControllerData::resolveYaw); resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); @@ -543,12 +575,8 @@ void AIControllerData::initPersistFields() "it helps the AIController controlled object 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 AIController controlled object 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 AIController controlled object 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("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance considered in firing range for callback purposes."); addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance tolerance on stuck check.\n\n" @@ -569,10 +597,16 @@ void AIControllerData::initPersistFields() "before the AIController controlled object starts to check if it is stuck. This delay allows the AIController controlled object " "to accelerate to full speed without its initial slow start being considered as stuck.\n" "@note Set to zero to have the stuck test start immediately.\n"); + endGroup("AI"); - addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, - "@brief Distance considered in firing range for callback purposes."); - +#ifdef TORQUE_NAVIGATION_ENABLED + addGroup("Pathfinding"); + addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIController controlled object 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 AIController controlled object 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("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent, "@brief chance of flocking."); addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat, @@ -581,10 +615,6 @@ void AIControllerData::initPersistFields() "@brief max flocking clustering distance."); addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance from destination before we stop moving out of the way."); - endGroup("AI"); - -#ifdef TORQUE_NAVIGATION_ENABLED - addGroup("Pathfinding"); addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData), "Allow the character to walk on dry land."); @@ -611,19 +641,21 @@ void AIControllerData::packData(BitStream* stream) { Parent::packData(stream); stream->write(mMoveTolerance); - stream->write(mFollowTolerance); stream->write(mAttackRadius); stream->write(mMoveStuckTolerance); stream->write(mMoveStuckTestDelay); + stream->write(mHeightTolerance); +#ifdef TORQUE_NAVIGATION_ENABLED + stream->write(mFollowTolerance); //enums stream->write(mLinkTypes.getFlags()); stream->write((U32)mNavSize); // end enums - stream->write(mHeightTolerance); stream->write(mFlocking.mChance); stream->write(mFlocking.mMin); stream->write(mFlocking.mMax); stream->write(mFlocking.mSideStep); +#endif // TORQUE_NAVIGATION_ENABLED }; void AIControllerData::unpackData(BitStream* stream) @@ -631,10 +663,13 @@ void AIControllerData::unpackData(BitStream* stream) Parent::unpackData(stream); stream->read(&mMoveTolerance); - stream->read(&mFollowTolerance); stream->read(&mAttackRadius); stream->read(&mMoveStuckTolerance); stream->read(&mMoveStuckTestDelay); + stream->read(&mHeightTolerance); + +#ifdef TORQUE_NAVIGATION_ENABLED + stream->read(&mFollowTolerance); //enums U16 linkFlags; stream->read(&linkFlags); @@ -643,11 +678,11 @@ void AIControllerData::unpackData(BitStream* stream) stream->read(&navSize); mNavSize = (AINavigation::NavSize)(navSize); // end enums - stream->read(&mHeightTolerance); stream->read(&(mFlocking.mChance)); stream->read(&(mFlocking.mMin)); stream->read(&(mFlocking.mMax)); stream->read(&(mFlocking.mSideStep)); +#endif // TORQUE_NAVIGATION_ENABLED }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -875,4 +910,3 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca movePtr->x = 0; movePtr->y = obj->mMovement.mMoveSpeed; } -#endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 14bddeccd..3180c4213 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -21,7 +21,6 @@ //----------------------------------------------------------------------------- #ifndef _AICONTROLLER_H_ #define _AICONTROLLER_H_ -#ifdef TORQUE_NAVIGATION_ENABLED #include "navigation/coverPoint.h" #include "AIInfo.h" #include "AIGoal.h" @@ -154,11 +153,12 @@ public: 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 + F32 mMoveStuckTolerance; // Distance tolerance on stuck check F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath +#ifdef TORQUE_NAVIGATION_ENABLED + F32 mFollowTolerance; // Distance from destination object before we stop struct Flocking { U32 mChance; // chance of flocking F32 mMin; // min flocking separation distance @@ -169,6 +169,7 @@ public: /// Types of link we can use. LinkData mLinkTypes; AINavigation::NavSize mNavSize; +#endif Delegate resolveYawPtr; void resolveYaw(AIController* obj, Point3F location, Move* movePtr); @@ -243,5 +244,4 @@ public: DECLARE_CONOBJECT(AIFlyingVehicleControllerData); }; -#endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index f5d3fd2cd..ceb9d668e 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -28,8 +28,10 @@ static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObject AINavigation::AINavigation(AIController* controller) { mControllerRef = controller; +#ifdef TORQUE_NAVIGATION_ENABLED mJump = None; mNavSize = Regular; +#endif } AINavigation::~AINavigation() @@ -40,6 +42,175 @@ AINavigation::~AINavigation() #endif } +void AINavigation::setMoveDestination(const Point3F& location, bool slowdown) +{ + mMoveDestination = location; + getCtrl()->mMovement.mMoveState = AIController::ModeMove; + getCtrl()->mMovement.mMoveSlowdown = slowdown; + getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->mControllerData->mMoveStuckTestDelay; +} + +bool AINavigation::setPathDestination(const Point3F& pos, bool replace) +{ +#ifdef TORQUE_NAVIGATION_ENABLED + if (replace) + getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); + + if (!mNavMesh) + updateNavMesh(); + + // If we can't find a mesh, just move regularly. + if (!mNavMesh) + { + //setMoveDestination(pos); + getCtrl()->throwCallback("onPathFailed"); + return false; + } + + // Create a new path. + NavPath* path = new NavPath(); + + path->mMesh = mNavMesh; + path->mFrom = getCtrl()->getAIInfo()->getPosition(true); + path->mTo = getCtrl()->getGoal()->getPosition(true); + path->mFromSet = path->mToSet = true; + path->mAlwaysRender = true; + path->mLinkTypes = getCtrl()->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(); + getCtrl()->clearCover(); + // Store new path. + mPathData.path = path; + mPathData.owned = true; + // Skip node 0, which we are currently standing on. + moveToNode(1); + getCtrl()->throwCallback("onPathSuccess"); + return true; + } + else + { + // Just move normally if we can't path. + //setMoveDestination(pos, true); + //return; + getCtrl()->throwCallback("onPathFailed"); + path->deleteObject(); + return false; + } +#else + setMoveDestination(pos, false); + return true; +#endif +} + +Point3F AINavigation::getPathDestination() const +{ +#ifdef TORQUE_NAVIGATION_ENABLED + if (!mPathData.path.isNull()) + return mPathData.path->mTo; + return Point3F(0, 0, 0); +#else + return getMoveDestination(); +#endif +} +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"); + getCtrl()->mMovement.mMoveState = AIController::ModeStop; + } +} + +DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), + "@brief Tells the AI to move to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@param slowDown A boolean value. If set to true, the bot will slow down " + "when it gets within 5-meters of its move destination. If false, the bot " + "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" + + "@note Upon reaching a move destination, the bot will clear its move destination and " + "calls to getMoveDestination will return \"0 0 0\"." + + "@see getMoveDestination()\n") +{ + object->getNav()->setMoveDestination(goal, slowDown); +} + + +DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , + "@brief Get the AIPlayer's current destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current move destination. If no move destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setMoveDestination()\n") +{ + return object->getNav()->getMoveDestination(); +} + +DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , + "@brief Tells the AI to find a path to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@return True if a path was found.\n\n" + + "@see getPathDestination()\n" + "@see setMoveDestination()\n") +{ + return object->getNav()->setPathDestination(goal, true); +} + + +DefineEngineMethod(AIController, getPathDestination, Point3F, (), , + "@brief Get the AIPlayer's current pathfinding destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current path destination. If no path destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setPathDestination()\n") +{ + return object->getNav()->getPathDestination(); +} + +#ifdef TORQUE_NAVIGATION_ENABLED NavMesh* AINavigation::findNavMesh() const { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); @@ -144,113 +315,6 @@ void AINavigation::repath() 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; - getCtrl()->mMovement.mMoveState = AIController::ModeMove; - getCtrl()->mMovement.mMoveSlowdown = slowdown; - getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->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"); - getCtrl()->mMovement.mMoveState = AIController::ModeStop; - } -} - -bool AINavigation::setPathDestination(const Point3F& pos, bool replace) -{ - if (replace) - getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); - - if (!mNavMesh) - updateNavMesh(); - - // If we can't find a mesh, just move regularly. - if (!mNavMesh) - { - //setMoveDestination(pos); - getCtrl()->throwCallback("onPathFailed"); - return false; - } - - // Create a new path. - NavPath* path = new NavPath(); - - path->mMesh = mNavMesh; - path->mFrom = getCtrl()->getAIInfo()->getPosition(true); - path->mTo = getCtrl()->getGoal()->getPosition(true); - path->mFromSet = path->mToSet = true; - path->mAlwaysRender = true; - path->mLinkTypes = getCtrl()->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(); - getCtrl()->clearCover(); - // Store new path. - mPathData.path = path; - mPathData.owned = true; - // Skip node 0, which we are currently standing on. - moveToNode(1); - getCtrl()->throwCallback("onPathSuccess"); - return true; - } - else - { - // Just move normally if we can't path. - //setMoveDestination(pos, true); - //return; - getCtrl()->throwCallback("onPathFailed"); - path->deleteObject(); - return false; - } -} - void AINavigation::followObject() { if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) @@ -422,59 +486,6 @@ bool AINavigation::flock() return flocking; } -DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), - "@brief Tells the AI to move to the location provided\n\n" - - "@param goal Coordinates in world space representing location to move to.\n" - "@param slowDown A boolean value. If set to true, the bot will slow down " - "when it gets within 5-meters of its move destination. If false, the bot " - "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" - - "@note Upon reaching a move destination, the bot will clear its move destination and " - "calls to getMoveDestination will return \"0 0 0\"." - - "@see getMoveDestination()\n") -{ - object->getNav()->setMoveDestination(goal, slowDown); -} - - -DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , - "@brief Get the AIPlayer's current destination.\n\n" - - "@return Returns a point containing the \"x y z\" position " - "of the AIPlayer's current move destination. If no move destination " - "has yet been set, this returns \"0 0 0\"." - - "@see setMoveDestination()\n") -{ - return object->getNav()->getMoveDestination(); -} - -DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , - "@brief Tells the AI to find a path to the location provided\n\n" - - "@param goal Coordinates in world space representing location to move to.\n" - "@return True if a path was found.\n\n" - - "@see getPathDestination()\n" - "@see setMoveDestination()\n") -{ - return object->getNav()->setPathDestination(goal,true); -} - - -DefineEngineMethod(AIController, getPathDestination, Point3F, (), , - "@brief Get the AIPlayer's current pathfinding destination.\n\n" - - "@return Returns a point containing the \"x y z\" position " - "of the AIPlayer's current path destination. If no path destination " - "has yet been set, this returns \"0 0 0\"." - - "@see setPathDestination()\n") -{ - return object->getNav()->getPathDestination(); -} DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), , "@brief Tell the AIPlayer to follow a path.\n\n" @@ -555,3 +566,4 @@ DefineEngineMethod(AIController, getNavSize, const char*, (), , } return ""; } +#endif diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 39b1276a4..e93871691 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -36,6 +36,15 @@ struct AINavigation AINavigation() = delete; AINavigation(AIController* controller); ~AINavigation(); + Point3F mMoveDestination; + void setMoveDestination(const Point3F& location, bool slowdown); + Point3F getMoveDestination() const { return mMoveDestination; }; + bool setPathDestination(const Point3F& pos, bool replace = false); + Point3F getPathDestination() const; + + void onReachDestination(); + +#ifdef TORQUE_NAVIGATION_ENABLED /// Stores information about a path. struct PathData { /// Pointer to path object. @@ -67,12 +76,6 @@ struct AINavigation void setNavSize(NavSize size) { mNavSize = size; updateNavMesh(); } NavSize getNavSize() const { return mNavSize; } - Point3F mMoveDestination; - void setMoveDestination(const Point3F& location, bool slowdown); - Point3F getMoveDestination() { return mMoveDestination; }; - - void onReachDestination(); - /// NavMesh we pathfind on. SimObjectPtr mNavMesh; NavMesh* findNavMesh() const; @@ -83,8 +86,6 @@ struct AINavigation /// Clear out the current path. void clearPath(); - bool setPathDestination(const Point3F& pos, bool replace = false); - Point3F getPathDestination() const; void repath(); /// Get the current path we're following. @@ -96,8 +97,8 @@ struct AINavigation void clearFollow(); /// Move to the specified node in the current path. void moveToNode(S32 node); - bool flock(); +#endif }; #endif From 9e2666ed09bb544298fa512379da13dbac112e46 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 14:35:26 -0500 Subject: [PATCH 45/47] nav editor compliance --- Engine/source/navigation/guiNavEditorCtrl.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 90a6e7f78..eae204447 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -227,6 +227,7 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) missionCleanup->addObject(obj); } mPlayer = obj; +#ifdef TORQUE_NAVIGATION_ENABLED AIPlayer* asAIPlayer = dynamic_cast(obj); if (asAIPlayer) //try direct { @@ -242,9 +243,12 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) } else { +#endif Con::executef(this, "onPlayerSelected"); +#ifdef TORQUE_NAVIGATION_ENABLED } } +#endif } } @@ -406,6 +410,7 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if(ri.object) { mPlayer = ri.object; +#ifdef TORQUE_NAVIGATION_ENABLED AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); if (asAIPlayer) //try direct { @@ -421,17 +426,24 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) } else { +#endif Con::executef(this, "onPlayerSelected"); } +#ifdef TORQUE_NAVIGATION_ENABLED } } +#endif } else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) { AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); if (asAIPlayer) //try direct { +#ifdef TORQUE_NAVIGATION_ENABLED asAIPlayer->setPathDestination(ri.point); +#else + asAIPlayer->setMoveDestination(ri.point,false); +#endif } else { From a05ff4f351da7711b99491b4e0d7ec9eb28fae0d Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 14:38:51 -0500 Subject: [PATCH 46/47] kill dupe controlmap tracking var define --- Engine/source/T3D/vehicles/vehicle.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 9ea2c9289..ba7bf8e68 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -71,7 +71,6 @@ struct VehicleData : public RigidShapeData F32 damageLevelTolerance[ VC_NUM_DAMAGE_LEVELS ]; F32 numDmgEmitterAreas; - StringTableEntry mControlMap; bool enablePhysicsRep; StringTableEntry mControlMap; From e489e0cd18221f7d9d8aeac805b4ecb56ba83132 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 16:01:24 -0500 Subject: [PATCH 47/47] go ahead and allow follow without needing a navmesh --- Engine/source/T3D/AI/AIController.cpp | 30 ++++++----- Engine/source/T3D/AI/AIController.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 77 ++++++++++++++------------- Engine/source/T3D/AI/AINavigation.h | 7 +-- 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index acd7063f6..e54b173bb 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -201,14 +201,16 @@ bool AIController::getAIMove(Move* movePtr) } } #else - if (getGoal()->getDist() > mControllerData->mMoveTolerance) + if (getGoal()->getDist() > mControllerData->mFollowTolerance) { - if (getGoal()->mPosSet) + if (getGoal()->mObj.isValid()) + getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); + else if (getGoal()->mPosSet) getNav()->setPathDestination(getGoal()->getPosition(true)); getGoal()->mInRange = false; } - if (getGoal()->getDist() < mControllerData->mMoveTolerance) + if (getGoal()->getDist() < mControllerData->mFollowTolerance) { mMovement.mMoveState = ModeStop; @@ -519,9 +521,9 @@ AIControllerData::AIControllerData() mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30; mHeightTolerance = 0.001f; + mFollowTolerance = 1.0f; #ifdef TORQUE_NAVIGATION_ENABLED - mFollowTolerance = 1.0f; mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; mFlocking.mChance = 90; @@ -544,9 +546,9 @@ AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clon mMoveStuckTolerance = other.mMoveStuckTolerance; mMoveStuckTestDelay = other.mMoveStuckTestDelay; mHeightTolerance = other.mHeightTolerance; + mFollowTolerance = other.mFollowTolerance; #ifdef TORQUE_NAVIGATION_ENABLED - mFollowTolerance = other.mFollowTolerance; mLinkTypes = other.mLinkTypes; mNavSize = other.mNavSize; mFlocking.mChance = other.mFlocking.mChance; @@ -575,6 +577,13 @@ void AIControllerData::initPersistFields() "it helps the AIController controlled object 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 AIController controlled object 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 AIController controlled object 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("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance considered in firing range for callback purposes."); @@ -601,12 +610,6 @@ void AIControllerData::initPersistFields() #ifdef TORQUE_NAVIGATION_ENABLED addGroup("Pathfinding"); - addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, - "@brief Distance from destination before stopping.\n\n" - "When the AIController controlled object 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 AIController controlled object 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("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent, "@brief chance of flocking."); addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat, @@ -645,8 +648,9 @@ void AIControllerData::packData(BitStream* stream) stream->write(mMoveStuckTolerance); stream->write(mMoveStuckTestDelay); stream->write(mHeightTolerance); -#ifdef TORQUE_NAVIGATION_ENABLED stream->write(mFollowTolerance); + +#ifdef TORQUE_NAVIGATION_ENABLED //enums stream->write(mLinkTypes.getFlags()); stream->write((U32)mNavSize); @@ -667,9 +671,9 @@ void AIControllerData::unpackData(BitStream* stream) stream->read(&mMoveStuckTolerance); stream->read(&mMoveStuckTestDelay); stream->read(&mHeightTolerance); + stream->read(&mFollowTolerance); #ifdef TORQUE_NAVIGATION_ENABLED - stream->read(&mFollowTolerance); //enums U16 linkFlags; stream->read(&linkFlags); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 3180c4213..18d95e210 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -153,12 +153,12 @@ public: 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 S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck F32 mMoveStuckTolerance; // Distance tolerance on stuck check F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath #ifdef TORQUE_NAVIGATION_ENABLED - F32 mFollowTolerance; // Distance from destination object before we stop struct Flocking { U32 mChance; // chance of flocking F32 mMin; // min flocking separation distance diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index ceb9d668e..258939f06 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -156,6 +156,30 @@ void AINavigation::onReachDestination() } } +void AINavigation::followObject() +{ + if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) + return; + + if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) + { +#ifdef TORQUE_NAVIGATION_ENABLED + getCtrl()->clearCover(); +#endif + } +} + +void AINavigation::followObject(SceneObject* obj, F32 radius) +{ + getCtrl()->setGoal(obj, radius); + followObject(); +} + +void AINavigation::clearFollow() +{ + getCtrl()->clearGoal(); +} + DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), "@brief Tells the AI to move to the location provided\n\n" @@ -210,6 +234,23 @@ DefineEngineMethod(AIController, getPathDestination, Point3F, (), , return object->getNav()->getPathDestination(); } +DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), , + "@brief Tell the AIPlayer to follow another object.\n\n" + + "@param obj ID of the object to follow.\n" + "@param radius Maximum distance we let the target escape to.") +{ + SceneObject* follow; +#ifdef TORQUE_NAVIGATION_ENABLED + object->getNav()->clearPath(); + object->clearCover(); +#endif + object->getNav()->clearFollow(); + + if (Sim::findObject(obj, follow)) + object->getNav()->followObject(follow, radius); +} + #ifdef TORQUE_NAVIGATION_ENABLED NavMesh* AINavigation::findNavMesh() const { @@ -315,27 +356,6 @@ void AINavigation::repath() moveToNode(1); } -void AINavigation::followObject() -{ - if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) - return; - - if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) - { - getCtrl()->clearCover(); - } -} - -void AINavigation::followObject(SceneObject* obj, F32 radius) -{ - getCtrl()->setGoal(obj, radius); - followObject(); -} - -void AINavigation::clearFollow() -{ - getCtrl()->clearGoal(); -} void AINavigation::followNavPath(NavPath* path) { @@ -497,21 +517,6 @@ DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), , object->getNav()->followNavPath(path); } -DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), , - "@brief Tell the AIPlayer to follow another object.\n\n" - - "@param obj ID of the object to follow.\n" - "@param radius Maximum distance we let the target escape to.") -{ - SceneObject* follow; - object->getNav()->clearPath(); - object->clearCover(); - object->getNav()->clearFollow(); - - if (Sim::findObject(obj, follow)) - object->getNav()->followObject(follow, radius); -} - DefineEngineMethod(AIController, repath, void, (), , "@brief Tells the AI to re-plan its path. Does nothing if the character " diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index e93871691..4d4f1b966 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -44,6 +44,10 @@ struct AINavigation void onReachDestination(); + void followObject(); + void followObject(SceneObject* obj, F32 radius); + void clearFollow(); + #ifdef TORQUE_NAVIGATION_ENABLED /// Stores information about a path. struct PathData { @@ -92,9 +96,6 @@ struct AINavigation SimObjectPtr getPath() { return mPathData.path; }; void followNavPath(NavPath* path); - void followObject(); - void followObject(SceneObject* obj, F32 radius); - void clearFollow(); /// Move to the specified node in the current path. void moveToNode(S32 node); bool flock();