diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 6d54358be..0b869c2a0 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,6 +28,10 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" +static U32 AIPLAYER_LOSMASK = TerrainObjectType | WaterObjectType | + ShapeBaseObjectType | StaticShapeObjectType | + PlayerObjectType | ItemObjectType; + IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); ConsoleDocClass( AIPlayer, @@ -417,28 +421,19 @@ bool AIPlayer::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 (mAimObject) { - MatrixF eyeMat; - getEyeTransform(&eyeMat); - eyeMat.getColumn(3,&location); - Point3F targetLoc = mAimObject->getBoxCenter(); - - // This ray ignores non-static shapes. Cast Ray returns true - // if it hit something. - RayInfo dummy; - if (getContainer()->castRay( location, targetLoc, - StaticShapeObjectType | StaticObjectType | - TerrainObjectType, &dummy)) { - if (mTargetInLOS) { - throwCallback( "onTargetExitLOS" ); - mTargetInLOS = false; - } + if (mAimObject) + { + mTargetInLOS = checkInLos(mAimObject.getPointer(), false); + if (mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + mTargetInLOS = true; } else - if (!mTargetInLOS) { - throwCallback( "onTargetEnterLOS" ); - mTargetInLOS = true; - } + { + throwCallback("onTargetExitLOS"); + mTargetInLOS = false; + } } // Replicate the trigger state into the move so that @@ -610,3 +605,103 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, GameBase* obj = object->getAimObject(); return obj? obj->getId(): -1; } + +bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) +{ + if (!isServerObject()) return false; + if (!(bool(target))) return false; + if (_checkEnabled) + { + ShapeBase *shapeBaseCheck = dynamic_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + + RayInfo ri; + + disableCollision(); + + S32 mountCount = target->getMountedObjectCount(); + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->disableCollision(); + } + Point3F muzzlePoint; + getMuzzlePointAI(0, &muzzlePoint); + bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), AIPLAYER_LOSMASK, &ri); + enableCollision(); + + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->enableCollision(); + } + + if (hit) + { + if (target != dynamic_cast(ri.object)) hit = false; + } + + return hit; +} + +bool AIPlayer::checkLosClear(Point3F _pos) +{ + if (!isServerObject()) return false; + + RayInfo ri; + + disableCollision(); + + Point3F muzzlePoint; + getMuzzlePointAI(0, &muzzlePoint); + gServerContainer.castRay(muzzlePoint, _pos, AIPLAYER_LOSMASK, &ri); + bool emptySpace = bool(ri.object == NULL); + enableCollision(); + return emptySpace; +} + +DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool checkEnabled), (0, false), + "@brief Check for an object in line of sight.\n") +{ + return object->checkInLos(obj, checkEnabled); +} + +bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = false) +{ + if (!isServerObject()) return false; + if (!(bool(target))) return false; + if (_checkEnabled) + { + ShapeBase *shapeBaseCheck = dynamic_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + + MatrixF cam = getTransform(); + Point3F camPos; + VectorF camDir; + + cam.getColumn(3, &camPos); + cam.getColumn(1, &camDir); + + camFov = mDegToRad(camFov) / 2; + + Point3F shapePos; + // Use the render transform instead of the box center + // otherwise it'll jitter. + MatrixF srtMat = target->getTransform(); + srtMat.getColumn(3, &shapePos); + 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 > camFov); +} + +DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (0, 45, false), + "@brief Check for an object within a specified veiw cone.\n") +{ + return object->checkInFoV(obj, fov, checkEnabled); +} \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 4e0bb5a3a..283db14db 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -80,6 +80,9 @@ public: void setAimLocation( const Point3F &location ); Point3F getAimLocation() const { return mAimLocation; } void clearAim(); + bool checkInLos(GameBase* target, bool _checkEnabled); + bool checkLosClear(Point3F _pos); + bool checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled); // Movement sets/gets void setMoveSpeed( const F32 speed );