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