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