aiTurretShape.cpp
Engine/source/T3D/turret/aiTurretShape.cpp
Public Variables
Public Functions
_scanCallback(SceneObject * object, void * data)
_sortCallback(const void * a, const void * b)
ConsoleDocClass(AITurretShape , "@ingroup gameObjects" )
ConsoleDocClass(AITurretShapeData , "@brief Defines properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an <a href="/coding/class/classaiturretshape/">AITurretShape</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AITurretShape\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TurretShapeData\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
DefineEngineMethod(AITurretShape , activateTurret , void , () , "@brief Activate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> turret from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> deactive <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" )
DefineEngineMethod(AITurretShape , addToIgnoreList , void , (ShapeBase *obj) , "@brief Adds object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the turret's ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" "@param obj The <a href="/coding/class/classshapebase/">ShapeBase</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ignore.\n</a>" )
DefineEngineMethod(AITurretShape , clearIgnoreList , void , () , "@brief Removes all objects from the turret's ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" )
DefineEngineMethod(AITurretShape , deactivateTurret , void , () , "@brief Deactivate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> turret from an active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" )
DefineEngineMethod(AITurretShape , getIgnoreListObject , SimObject * , (S32 index) , "@brief Returns the object in the ignore list at <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">index.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" "@param index The index of the object in the ignore list being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">retrieved.\n</a>" )
DefineEngineMethod(AITurretShape , getTarget , SimObject * , () , "@brief Get the turret's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" "@returns The object that is the target's current target, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n</a>" )
DefineEngineMethod(AITurretShape , getWeaponLeadVelocity , F32 , () , "@brief Get the turret's defined projectile velocity that helps with target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">leading.\n\n</a>" "@returns The defined weapon projectile speed, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> leading is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">disabled.\n</a>" )
DefineEngineMethod(AITurretShape , hasTarget , bool , () , "@brief Indicates <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the turret has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" "@returns True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the turret has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n</a>" )
DefineEngineMethod(AITurretShape , ignoreListCount , S32 , () , "@brief Returns the number of objects in the turrets ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" )
DefineEngineMethod(AITurretShape , recenterTurret , void , () , "@brief Recenter the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">weapon.\n\n</a>" )
DefineEngineMethod(AITurretShape , removeFromIgnoreList , void , (ShapeBase *obj) , "@brief Removes object from the turret's ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" "@param obj The <a href="/coding/class/classshapebase/">ShapeBase</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> once again allow <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" )
DefineEngineMethod(AITurretShape , resetTarget , void , () , "@brief Resets the turret's target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tracking.\n\n</a>" "Only resets the internal target tracking. Does not modify the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">facing.\n</a>" )
DefineEngineMethod(AITurretShape , setAllGunsFiring , void , (bool fire) , "@brief Set the firing state of the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">guns.\n\n</a>" "@param fire Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> activate all guns. False <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> deactivate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">them.\n</a>" )
DefineEngineMethod(AITurretShape , setGunSlotFiring , void , (S32 slot, bool fire) , "@brief Set the firing state of the given gun <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot.\n\n</a>" "@param slot The gun <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> modify. Valid range is 0-3 that corresponds <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the weapon mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param fire Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> activate the gun. False <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> deactivate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" )
DefineEngineMethod(AITurretShape , setTurretState , void , (const char *newState, bool force) , (false) , "@brief Set the turret's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" "Normally the turret's state comes from updating the state machine but this method " "allows you <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> override this and jump <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the requested state <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">immediately.\n</a>" "@param newState The name of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n</a>" "@param force Is true then force the full processing of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> state even <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is the " "same as the current state. If false then only the time out <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> is reset and the state's " "script method is called, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">any.\n</a>" )
DefineEngineMethod(AITurretShape , setWeaponLeadVelocity , void , (F32 velocity) , "@brief Set the turret's projectile velocity <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> help lead the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" "This <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> normally comes from <a href="/coding/class/classaiturretshapedata/#classaiturretshapedata_1a55db385001d1ffa356a548da81827f67">AITurretShapeData::weaponLeadVelocity</a> but this method " "allows you <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> override the datablock value. This can be useful <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the turret changes " " ammunition, uses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different weapon than the default, is damaged, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">etc.\n</a>" " @note Setting this <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 0 will disable target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">leading.\n</a>" )
DefineEngineMethod(AITurretShape , startScanForTargets , void , () , "@brief Begin scanning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" )
DefineEngineMethod(AITurretShape , startTrackingTarget , void , () , "@brief Have the turret track the current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" )
DefineEngineMethod(AITurretShape , stopScanForTargets , void , () , "@brief Stop scanning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targets.\n\n</a>" "@note Only impacts the scanning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> targets. Does not effect <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> turret's current " "target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">lock.\n</a>" )
DefineEngineMethod(AITurretShape , stopTrackingTarget , void , () , "@brief Stop the turret from tracking the current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" )
Detailed Description
Public Variables
Point3F comparePoint
AITurretShapeData::StateData gDefaultStateData
U32 sAimTypeMask
U32 sScanTypeMask
Public Functions
_scanCallback(SceneObject * object, void * data)
_sortCallback(const void * a, const void * b)
ConsoleDocClass(AITurretShape , "@ingroup gameObjects" )
ConsoleDocClass(AITurretShapeData , "@brief Defines properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an <a href="/coding/class/classaiturretshape/">AITurretShape</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AITurretShape\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TurretShapeData\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
DefineEngineMethod(AITurretShape , activateTurret , void , () , "@brief Activate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> turret from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> deactive <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" )
DefineEngineMethod(AITurretShape , addToIgnoreList , void , (ShapeBase *obj) , "@brief Adds object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the turret's ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" "@param obj The <a href="/coding/class/classshapebase/">ShapeBase</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ignore.\n</a>" )
DefineEngineMethod(AITurretShape , clearIgnoreList , void , () , "@brief Removes all objects from the turret's ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" )
DefineEngineMethod(AITurretShape , deactivateTurret , void , () , "@brief Deactivate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> turret from an active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" )
DefineEngineMethod(AITurretShape , getIgnoreListObject , SimObject * , (S32 index) , "@brief Returns the object in the ignore list at <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">index.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" "@param index The index of the object in the ignore list being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">retrieved.\n</a>" )
DefineEngineMethod(AITurretShape , getTarget , SimObject * , () , "@brief Get the turret's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" "@returns The object that is the target's current target, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n</a>" )
DefineEngineMethod(AITurretShape , getWeaponLeadVelocity , F32 , () , "@brief Get the turret's defined projectile velocity that helps with target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">leading.\n\n</a>" "@returns The defined weapon projectile speed, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> leading is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">disabled.\n</a>" )
DefineEngineMethod(AITurretShape , hasTarget , bool , () , "@brief Indicates <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the turret has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" "@returns True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the turret has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n</a>" )
DefineEngineMethod(AITurretShape , ignoreListCount , S32 , () , "@brief Returns the number of objects in the turrets ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" )
DefineEngineMethod(AITurretShape , recenterTurret , void , () , "@brief Recenter the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">weapon.\n\n</a>" )
DefineEngineMethod(AITurretShape , removeFromIgnoreList , void , (ShapeBase *obj) , "@brief Removes object from the turret's ignore <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "All objects in this list will be ignored by the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" "@param obj The <a href="/coding/class/classshapebase/">ShapeBase</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> once again allow <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targeting.\n</a>" )
DefineEngineMethod(AITurretShape , resetTarget , void , () , "@brief Resets the turret's target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tracking.\n\n</a>" "Only resets the internal target tracking. Does not modify the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">facing.\n</a>" )
DefineEngineMethod(AITurretShape , setAllGunsFiring , void , (bool fire) , "@brief Set the firing state of the turret's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">guns.\n\n</a>" "@param fire Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> activate all guns. False <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> deactivate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">them.\n</a>" )
DefineEngineMethod(AITurretShape , setGunSlotFiring , void , (S32 slot, bool fire) , "@brief Set the firing state of the given gun <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot.\n\n</a>" "@param slot The gun <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> modify. Valid range is 0-3 that corresponds <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the weapon mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param fire Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> activate the gun. False <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> deactivate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" )
DefineEngineMethod(AITurretShape , setTurretState , void , (const char *newState, bool force) , (false) , "@brief Set the turret's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" "Normally the turret's state comes from updating the state machine but this method " "allows you <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> override this and jump <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the requested state <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">immediately.\n</a>" "@param newState The name of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n</a>" "@param force Is true then force the full processing of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> state even <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is the " "same as the current state. If false then only the time out <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> is reset and the state's " "script method is called, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">any.\n</a>" )
DefineEngineMethod(AITurretShape , setWeaponLeadVelocity , void , (F32 velocity) , "@brief Set the turret's projectile velocity <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> help lead the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" "This <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> normally comes from <a href="/coding/class/classaiturretshapedata/#classaiturretshapedata_1a55db385001d1ffa356a548da81827f67">AITurretShapeData::weaponLeadVelocity</a> but this method " "allows you <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> override the datablock value. This can be useful <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the turret changes " " ammunition, uses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different weapon than the default, is damaged, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">etc.\n</a>" " @note Setting this <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 0 will disable target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">leading.\n</a>" )
DefineEngineMethod(AITurretShape , startScanForTargets , void , () , "@brief Begin scanning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" )
DefineEngineMethod(AITurretShape , startTrackingTarget , void , () , "@brief Have the turret track the current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" )
DefineEngineMethod(AITurretShape , stopScanForTargets , void , () , "@brief Stop scanning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">targets.\n\n</a>" "@note Only impacts the scanning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> targets. Does not effect <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> turret's current " "target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">lock.\n</a>" )
DefineEngineMethod(AITurretShape , stopTrackingTarget , void , () , "@brief Stop the turret from tracking the current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">target.\n\n</a>" )
IMPLEMENT_CO_DATABLOCK_V1(AITurretShapeData )
IMPLEMENT_CO_NETOBJECT_V1(AITurretShape )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "T3D/turret/aiTurretShape.h" 25 26#include "console/console.h" 27#include "console/consoleTypes.h" 28#include "console/engineAPI.h" 29#include "core/stream/bitStream.h" 30#include "math/mMath.h" 31#include "math/mathIO.h" 32#include "math/mathUtils.h" 33#include "gfx/gfxDrawUtil.h" 34#include "ts/tsShapeInstance.h" 35#include "math/mRandom.h" 36 37static U32 sScanTypeMask = PlayerObjectType | 38 VehicleObjectType; 39 40static U32 sAimTypeMask = TerrainObjectType | 41 WaterObjectType | 42 PlayerObjectType | 43 StaticShapeObjectType | 44 VehicleObjectType | 45 ItemObjectType; 46 47//---------------------------------------------------------------------------- 48 49AITurretShapeData::StateData::StateData() 50{ 51 name = 0; 52 transition.rest[0] = transition.rest[1] = -1; 53 transition.target[0] = transition.target[1] = -1; 54 transition.activated[0] = transition.activated[1] = -1; 55 transition.timeout = -1; 56 waitForTimeout = true; 57 timeoutValue = 0; 58 fire = false; 59 scan = false; 60 script = 0; 61 62 scaleAnimation = false; 63 direction = false; 64 65 sequence = -1; 66} 67 68static AITurretShapeData::StateData gDefaultStateData; 69 70//---------------------------------------------------------------------------- 71 72IMPLEMENT_CO_DATABLOCK_V1(AITurretShapeData); 73 74ConsoleDocClass( AITurretShapeData, 75 "@brief Defines properties for an AITurretShape object.\n\n" 76 "@see AITurretShape\n" 77 "@see TurretShapeData\n" 78 "@ingroup gameObjects\n" 79); 80 81AITurretShapeData::AITurretShapeData() 82{ 83 maxScanHeading = 90; 84 maxScanPitch = 90; 85 maxScanDistance = 20; 86 87 // Do a full scan every three ticks 88 scanTickFrequency = 3; 89 90 // Randomly add 0 to 1 ticks to the scan frequency as 91 // chosen every scan period. 92 scanTickFrequencyVariance = 1; 93 94 trackLostTargetTime = 0; 95 96 scanNode = -1; 97 aimNode = -1; 98 99 maxWeaponRange = 100; 100 101 weaponLeadVelocity = 0; 102 103 for (S32 i = 0; i < MaxStates; i++) { 104 stateName[i] = 0; 105 stateTransitionAtRest[i] = 0; 106 stateTransitionNotAtRest[i] = 0; 107 stateTransitionTarget[i] = 0; 108 stateTransitionNoTarget[i] = 0; 109 stateTransitionActivated[i] = 0; 110 stateTransitionDeactivated[i] = 0; 111 stateTransitionTimeout[i] = 0; 112 stateWaitForTimeout[i] = true; 113 stateTimeoutValue[i] = 0; 114 stateFire[i] = false; 115 stateScan[i] = false; 116 stateScaleAnimation[i] = true; 117 stateDirection[i] = true; 118 stateSequence[i] = 0; 119 stateScript[i] = 0; 120 } 121 isAnimated = false; 122 statesLoaded = false; 123 fireState = -1; 124} 125 126void AITurretShapeData::initPersistFields() 127{ 128 addField("maxScanHeading", TypeF32, Offset(maxScanHeading, AITurretShapeData), 129 "@brief Maximum number of degrees to scan left and right.\n\n" 130 "@note Maximum scan heading is 90 degrees.\n"); 131 addField("maxScanPitch", TypeF32, Offset(maxScanPitch, AITurretShapeData), 132 "@brief Maximum number of degrees to scan up and down.\n\n" 133 "@note Maximum scan pitch is 90 degrees.\n"); 134 addField("maxScanDistance", TypeF32, Offset(maxScanDistance, AITurretShapeData), 135 "@brief Maximum distance to scan.\n\n" 136 "When combined with maxScanHeading and maxScanPitch this forms a 3D scanning wedge used to initially " 137 "locate a target.\n"); 138 139 addField("scanTickFrequency", TypeS32, Offset(scanTickFrequency, AITurretShapeData), 140 "@brief How often should we perform a full scan when looking for a target.\n\n" 141 "Expressed as the number of ticks between full scans, but no less than 1.\n"); 142 addField("scanTickFrequencyVariance", TypeS32, Offset(scanTickFrequencyVariance, AITurretShapeData), 143 "@brief Random amount that should be added to the scan tick frequency each scan period.\n\n" 144 "Expressed as the number of ticks to randomly add, but no less than zero.\n"); 145 146 addField("trackLostTargetTime", TypeF32, Offset(trackLostTargetTime, AITurretShapeData), 147 "@brief How long after the turret has lost the target should it still track it.\n\n" 148 "Expressed in seconds.\n"); 149 150 addField("maxWeaponRange", TypeF32, Offset(maxWeaponRange, AITurretShapeData), 151 "@brief Maximum distance that the weapon will fire upon a target.\n\n"); 152 153 addField("weaponLeadVelocity", TypeF32, Offset(weaponLeadVelocity, AITurretShapeData), 154 "@brief Velocity used to lead target.\n\n" 155 "If value <= 0, don't lead target.\n"); 156 157 // State arrays 158 addArray( "States", MaxStates ); 159 160 addField( "stateName", TypeCaseString, Offset(stateName, AITurretShapeData), MaxStates, 161 "Name of this state." ); 162 addField( "stateTransitionOnAtRest", TypeString, Offset(stateTransitionAtRest, AITurretShapeData), MaxStates, 163 "Name of the state to transition to when the turret is at rest (static)."); 164 addField( "stateTransitionOnNotAtRest", TypeString, Offset(stateTransitionNotAtRest, AITurretShapeData), MaxStates, 165 "Name of the state to transition to when the turret is not at rest (not static)."); 166 addField( "stateTransitionOnTarget", TypeString, Offset(stateTransitionTarget, AITurretShapeData), MaxStates, 167 "Name of the state to transition to when the turret gains a target." ); 168 addField( "stateTransitionOnNoTarget", TypeString, Offset(stateTransitionNoTarget, AITurretShapeData), MaxStates, 169 "Name of the state to transition to when the turret loses a target." ); 170 addField( "stateTransitionOnActivated", TypeString, Offset(stateTransitionActivated, AITurretShapeData), MaxStates, 171 "Name of the state to transition to when the turret goes from deactivated to activated."); 172 addField( "stateTransitionOnDeactivated", TypeString, Offset(stateTransitionDeactivated, AITurretShapeData), MaxStates, 173 "Name of the state to transition to when the turret goes from activated to deactivated"); 174 addField( "stateTransitionOnTimeout", TypeString, Offset(stateTransitionTimeout, AITurretShapeData), MaxStates, 175 "Name of the state to transition to when we have been in this state " 176 "for stateTimeoutValue seconds." ); 177 addField( "stateTimeoutValue", TypeF32, Offset(stateTimeoutValue, AITurretShapeData), MaxStates, 178 "Time in seconds to wait before transitioning to stateTransitionOnTimeout." ); 179 addField( "stateWaitForTimeout", TypeBool, Offset(stateWaitForTimeout, AITurretShapeData), MaxStates, 180 "If false, this state ignores stateTimeoutValue and transitions " 181 "immediately if other transition conditions are met." ); 182 addField( "stateFire", TypeBool, Offset(stateFire, AITurretShapeData), MaxStates, 183 "The first state with this set to true is the state entered by the " 184 "client when it receives the 'fire' event." ); 185 addField( "stateScan", TypeBool, Offset(stateScan, AITurretShapeData), MaxStates, 186 "Indicates the turret should perform a continuous scan looking for targets." ); 187 addField( "stateDirection", TypeBool, Offset(stateDirection, AITurretShapeData), MaxStates, 188 "@brief Direction of the animation to play in this state.\n\n" 189 "True is forward, false is backward." ); 190 addField( "stateSequence", TypeString, Offset(stateSequence, AITurretShapeData), MaxStates, 191 "Name of the sequence to play on entry to this state." ); 192 addField( "stateScaleAnimation", TypeBool, Offset(stateScaleAnimation, AITurretShapeData), MaxStates, 193 "If true, the timeScale of the stateSequence animation will be adjusted " 194 "such that the sequence plays for stateTimeoutValue seconds. " ); 195 addField( "stateScript", TypeCaseString, Offset(stateScript, AITurretShapeData), MaxStates, 196 "@brief Method to execute on entering this state.\n\n" 197 "Scoped to AITurretShapeData."); 198 199 endArray( "States" ); 200 201 Parent::initPersistFields(); 202} 203 204bool AITurretShapeData::onAdd() 205{ 206 if (!Parent::onAdd()) 207 return false; 208 209 // Copy state data from the scripting arrays into the 210 // state structure array. If we have state data already, 211 // we are on the client and need to leave it alone. 212 for (U32 i = 0; i < MaxStates; i++) { 213 StateData& s = state[i]; 214 if (statesLoaded == false) { 215 s.name = stateName[i]; 216 s.transition.rest[0] = lookupState(stateTransitionNotAtRest[i]); 217 s.transition.rest[1] = lookupState(stateTransitionAtRest[i]); 218 s.transition.target[0] = lookupState(stateTransitionNoTarget[i]); 219 s.transition.target[1] = lookupState(stateTransitionTarget[i]); 220 s.transition.activated[0] = lookupState(stateTransitionDeactivated[i]); 221 s.transition.activated[1] = lookupState(stateTransitionActivated[i]); 222 s.transition.timeout = lookupState(stateTransitionTimeout[i]); 223 s.waitForTimeout = stateWaitForTimeout[i]; 224 s.timeoutValue = stateTimeoutValue[i]; 225 s.fire = stateFire[i]; 226 s.scan = stateScan[i]; 227 s.scaleAnimation = stateScaleAnimation[i]; 228 s.direction = stateDirection[i]; 229 s.script = stateScript[i]; 230 231 // Resolved at load time 232 s.sequence = -1; 233 } 234 235 // The first state marked as "fire" is the state entered on the 236 // client when it recieves a fire event. 237 if (s.fire && fireState == -1) 238 fireState = i; 239 } 240 241 // Always preload images, this is needed to avoid problems with 242 // resolving sequences before transmission to a client. 243 return true; 244} 245 246bool AITurretShapeData::preload(bool server, String &errorStr) 247{ 248 if (!Parent::preload(server, errorStr)) 249 return false; 250 251 // We have mShape at this point. Resolve nodes. 252 scanNode = mShape->findNode("scanPoint"); 253 aimNode = mShape->findNode("aimPoint"); 254 255 if (scanNode == -1) scanNode = pitchNode; 256 if (scanNode == -1) scanNode = headingNode; 257 if (aimNode == -1) aimNode = pitchNode; 258 if (aimNode == -1) aimNode = headingNode; 259 260 // Resolve state sequence names & emitter nodes 261 isAnimated = false; 262 for (U32 j = 0; j < MaxStates; j++) { 263 StateData& s = state[j]; 264 if (stateSequence[j] && stateSequence[j][0]) 265 s.sequence = mShape->findSequence(stateSequence[j]); 266 if (s.sequence != -1) 267 { 268 // This state has an animation sequence 269 isAnimated = true; 270 } 271 } 272 273 return true; 274} 275 276void AITurretShapeData::packData(BitStream* stream) 277{ 278 Parent::packData(stream); 279 280 stream->write(maxScanHeading); 281 stream->write(maxScanPitch); 282 stream->write(maxScanDistance); 283 stream->write(maxWeaponRange); 284 stream->write(weaponLeadVelocity); 285 286 for (U32 i = 0; i < MaxStates; i++) 287 if (stream->writeFlag(state[i].name && state[i].name[0])) { 288 StateData& s = state[i]; 289 // States info not needed on the client: 290 // s.scriptNames 291 // Transitions are inc. one to account for -1 values 292 stream->writeString(state[i].name); 293 294 stream->writeInt(s.transition.rest[0]+1,NumStateBits); 295 stream->writeInt(s.transition.rest[1]+1,NumStateBits); 296 stream->writeInt(s.transition.target[0]+1,NumStateBits); 297 stream->writeInt(s.transition.target[1]+1,NumStateBits); 298 stream->writeInt(s.transition.activated[0]+1,NumStateBits); 299 stream->writeInt(s.transition.activated[1]+1,NumStateBits); 300 stream->writeInt(s.transition.timeout+1,NumStateBits); 301 302 if(stream->writeFlag(s.timeoutValue != gDefaultStateData.timeoutValue)) 303 stream->write(s.timeoutValue); 304 305 stream->writeFlag(s.waitForTimeout); 306 stream->writeFlag(s.fire); 307 stream->writeFlag(s.scan); 308 stream->writeFlag(s.scaleAnimation); 309 stream->writeFlag(s.direction); 310 311 if(stream->writeFlag(s.sequence != gDefaultStateData.sequence)) 312 stream->writeSignedInt(s.sequence, 16); 313 } 314} 315 316void AITurretShapeData::unpackData(BitStream* stream) 317{ 318 Parent::unpackData(stream); 319 320 stream->read(&maxScanHeading); 321 stream->read(&maxScanPitch); 322 stream->read(&maxScanDistance); 323 stream->read(&maxWeaponRange); 324 stream->read(&weaponLeadVelocity); 325 326 for (U32 i = 0; i < MaxStates; i++) { 327 if (stream->readFlag()) { 328 StateData& s = state[i]; 329 // States info not needed on the client: 330 // s.scriptNames 331 // Transitions are dec. one to restore -1 values 332 s.name = stream->readSTString(); 333 334 s.transition.rest[0] = stream->readInt(NumStateBits) - 1; 335 s.transition.rest[1] = stream->readInt(NumStateBits) - 1; 336 s.transition.target[0] = stream->readInt(NumStateBits) - 1; 337 s.transition.target[1] = stream->readInt(NumStateBits) - 1; 338 s.transition.activated[0] = stream->readInt(NumStateBits) - 1; 339 s.transition.activated[1] = stream->readInt(NumStateBits) - 1; 340 s.transition.timeout = stream->readInt(NumStateBits) - 1; 341 if(stream->readFlag()) 342 stream->read(&s.timeoutValue); 343 else 344 s.timeoutValue = gDefaultStateData.timeoutValue; 345 346 s.waitForTimeout = stream->readFlag(); 347 s.fire = stream->readFlag(); 348 s.scan = stream->readFlag(); 349 s.scaleAnimation = stream->readFlag(); 350 s.direction = stream->readFlag(); 351 352 if(stream->readFlag()) 353 s.sequence = stream->readSignedInt(16); 354 } 355 } 356 357 statesLoaded = true; 358} 359 360S32 AITurretShapeData::lookupState(const char* name) 361{ 362 if (!name || !name[0]) 363 return -1; 364 for (U32 i = 0; i < MaxStates; i++) 365 if (stateName[i] && !dStricmp(name,stateName[i])) 366 return i; 367 Con::errorf(ConsoleLogEntry::General,"AITurretShapeData:: Could not resolve state \"%s\" for image \"%s\"",name,getName()); 368 return 0; 369} 370 371 372//---------------------------------------------------------------------------- 373 374// Used to build potential target list 375static void _scanCallback( SceneObject* object, void* data ) 376{ 377 AITurretShape* turret = (AITurretShape*)data; 378 379 ShapeBase* shape = dynamic_cast<ShapeBase*>(object); 380 if (shape && shape->getDamageState() == ShapeBase::Enabled) 381 { 382 Point3F targetPos = shape->getBoxCenter(); 383 384 // Put target position into the scan node's space 385 turret->mScanWorkspaceScanWorldMat.mulP(targetPos); 386 387 // Is the target within scanning distance 388 if (targetPos.lenSquared() > turret->getMaxScanDistanceSquared()) 389 return; 390 391 // Make sure the target is in front and within the maximum 392 // heading range 393 Point2F targetXY(targetPos.x, targetPos.y); 394 targetXY.normalizeSafe(); 395 F32 headingDot = mDot(Point2F(0, 1), targetXY); 396 F32 heading = mAcos(headingDot); 397 if (headingDot < 0 || heading > turret->getMaxScanHeading()) 398 return; 399 400 // Make sure the target is in front and within the maximum 401 // pitch range 402 Point2F targetZY(targetPos.z, targetPos.y); 403 targetZY.normalizeSafe(); 404 F32 pitchDot = mDot(Point2F(0, 1), targetZY); 405 F32 pitch = mAcos(pitchDot); 406 if (pitchDot < 0 || pitch > turret->getMaxScanPitch()) 407 return; 408 409 turret->addPotentialTarget(shape); 410 } 411} 412 413// Used to sort potential target list based on distance 414static Point3F comparePoint; 415static S32 QSORT_CALLBACK _sortCallback(const void* a,const void* b) 416{ 417 const ShapeBase* s1 = (*reinterpret_cast<const ShapeBase* const*>(a)); 418 const ShapeBase* s2 = (*reinterpret_cast<const ShapeBase* const*>(b)); 419 420 F32 s1Len = (s1->getPosition() - comparePoint).lenSquared(); 421 F32 s2Len = (s2->getPosition() - comparePoint).lenSquared(); 422 423 return s1Len - s2Len; 424} 425 426IMPLEMENT_CO_NETOBJECT_V1(AITurretShape); 427 428ConsoleDocClass( AITurretShape, 429 "@ingroup gameObjects" 430); 431 432AITurretShape::AITurretShape() 433{ 434 mTypeMask |= VehicleObjectType | DynamicShapeObjectType; 435 mDataBlock = 0; 436 437 mScanHeading = 0; 438 mScanPitch = 0; 439 mScanDistance = 0; 440 mScanDistanceSquared = 0; 441 mScanBox = Box3F::Zero; 442 443 mScanTickFrequency = 1; 444 mScanTickFrequencyVariance = 0; 445 mTicksToNextScan = mScanTickFrequency; 446 447 mWeaponRangeSquared = 0; 448 mWeaponLeadVelocitySquared = 0; 449 450 mScanForTargets = false; 451 mTrackTarget = false; 452 453 mState = NULL; 454 mStateDelayTime = 0; 455 mStateActive = true; 456 mStateAnimThread = NULL; 457 458 // For the TurretShape class 459 mSubclassTurretShapeHandlesScene = true; 460} 461 462AITurretShape::~AITurretShape() 463{ 464 _cleanupPotentialTargets(); 465} 466 467//---------------------------------------------------------------------------- 468 469void AITurretShape::initPersistFields() 470{ 471 Parent::initPersistFields(); 472} 473 474bool AITurretShape::onAdd() 475{ 476 if( !Parent::onAdd() ) 477 return false; 478 479 // Add this object to the scene 480 addToScene(); 481 482 _setScanBox(); 483 484 if (isServerObject()) 485 _initState(); 486 487 if (isServerObject()) 488 scriptOnAdd(); 489 490 return true; 491} 492 493void AITurretShape::onRemove() 494{ 495 Parent::onRemove(); 496 497 scriptOnRemove(); 498 499 mIgnoreObjects.clear(); 500 501 // Remove this object from the scene 502 removeFromScene(); 503} 504 505bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) 506{ 507 mDataBlock = dynamic_cast<AITurretShapeData*>(dptr); 508 if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) 509 return false; 510 511 mScanHeading = mDegToRad(mDataBlock->maxScanHeading); 512 if (mIsZero(mScanHeading)) 513 mScanHeading = M_PI_F; 514 mScanPitch = mDegToRad(mDataBlock->maxScanPitch); 515 if (mIsZero(mScanPitch)) 516 mScanPitch = M_PI_F; 517 518 mScanDistance = mDataBlock->maxScanDistance; 519 mScanDistanceSquared = mScanDistance * mScanDistance; 520 521 mWeaponRangeSquared = mDataBlock->maxWeaponRange * mDataBlock->maxWeaponRange; 522 mWeaponLeadVelocitySquared = (mDataBlock->weaponLeadVelocity > 0) ? (mDataBlock->weaponLeadVelocity * mDataBlock->weaponLeadVelocity) : 0; 523 524 // The scan box is built such that the scanning origin is at (0,0,0) and the scanning distance 525 // is out along the y axis. When this is transformed to the turret's location is provides a 526 // scanning volume in front of the turret. 527 F32 scanX = mScanDistance*mSin(mScanHeading); 528 F32 scanY = mScanDistance; 529 F32 scanZ = mScanDistance*mSin(mScanPitch); 530 mScanBox.set(-scanX, 0, -scanZ, scanX, scanY, scanZ); 531 532 mScanTickFrequency = mDataBlock->scanTickFrequency; 533 if (mScanTickFrequency < 1) 534 mScanTickFrequency = 1; 535 536 mScanTickFrequencyVariance = mDataBlock->scanTickFrequencyVariance; 537 if (mScanTickFrequencyVariance < 0) 538 mScanTickFrequencyVariance = 0; 539 540 mTicksToNextScan = mScanTickFrequency; 541 542 // For states 543 mStateAnimThread = 0; 544 if (mDataBlock->isAnimated) 545 { 546 mStateAnimThread = mShapeInstance->addThread(); 547 mShapeInstance->setTimeScale(mStateAnimThread,0); 548 } 549 550 scriptOnNewDataBlock(); 551 return true; 552} 553 554//---------------------------------------------------------------------------- 555 556void AITurretShape::addToIgnoreList(ShapeBase* obj) 557{ 558 mIgnoreObjects.addObject(obj); 559} 560 561void AITurretShape::removeFromIgnoreList(ShapeBase* obj) 562{ 563 mIgnoreObjects.removeObject(obj); 564} 565 566void AITurretShape::clearIgnoreList() 567{ 568 mIgnoreObjects.clear(); 569} 570 571S32 AITurretShape::ignoreListCount() 572{ 573 return mIgnoreObjects.size(); 574} 575 576SimObject* AITurretShape::getIgnoreListObject(S32 index) 577{ 578 return mIgnoreObjects.at(index); 579} 580 581//---------------------------------------------------------------------------- 582 583void AITurretShape::_initState() 584{ 585 // Set the turret to its starting state. 586 setTurretState(0, true); 587} 588 589void AITurretShape::setTurretStateName(const char* newState, bool force) 590{ 591 S32 stateVal = mDataBlock->lookupState(newState); 592 if (stateVal >= 0) 593 { 594 setTurretState(stateVal, force); 595 } 596} 597 598void AITurretShape::setTurretState(U32 newState, bool force) 599{ 600 setMaskBits(TurretStateMask); 601 602 // If going back into the same state, just reset the timer 603 // and invoke the script callback 604 if (!force && mState == &mDataBlock->state[newState]) 605 { 606 mStateDelayTime = mState->timeoutValue; 607 if (mState->script && !isGhost()) 608 _scriptCallback(mState->script); 609 610 return; 611 } 612 613 mState = &mDataBlock->state[newState]; 614 615 // Reset cyclic sequences back to the first frame to turn it off 616 // (the first key frame should be it's off state). 617 if (mStateAnimThread && mStateAnimThread->getSequence()->isCyclic()) 618 { 619 mShapeInstance->setPos(mStateAnimThread,0); 620 mShapeInstance->setTimeScale(mStateAnimThread,0); 621 } 622 623 AITurretShapeData::StateData& stateData = *mState; 624 625 // Check for immediate transitions 626 S32 ns; 627 if ((ns = stateData.transition.rest[mAtRest]) != -1) 628 { 629 setTurretState(ns); 630 return; 631 } 632 if ((ns = stateData.transition.target[mTarget.isValid()]) != -1) 633 { 634 setTurretState(ns); 635 return; 636 } 637 if ((ns = stateData.transition.activated[mStateActive]) != -1) 638 { 639 setTurretState(ns); 640 return; 641 } 642 643 // 644 // Initialize the new state... 645 // 646 mStateDelayTime = stateData.timeoutValue; 647 648 // Play animation 649 if (mStateAnimThread && stateData.sequence != -1) 650 { 651 mShapeInstance->setSequence(mStateAnimThread,stateData.sequence, stateData.direction ? 0.0f : 1.0f); 652 F32 timeScale = (stateData.scaleAnimation && stateData.timeoutValue) ? 653 mShapeInstance->getDuration(mStateAnimThread) / stateData.timeoutValue : 1.0f; 654 mShapeInstance->setTimeScale(mStateAnimThread, stateData.direction ? timeScale : -timeScale); 655 } 656 657 // Script callback on server 658 if (stateData.script && stateData.script[0] && !isGhost()) 659 _scriptCallback(stateData.script); 660 661 // If there is a zero timeout, and a timeout transition, then 662 // go ahead and transition imediately. 663 if (!mStateDelayTime) 664 { 665 if ((ns = stateData.transition.timeout) != -1) 666 { 667 setTurretState(ns); 668 return; 669 } 670 } 671} 672 673void AITurretShape::_updateTurretState(F32 dt) 674{ 675 AITurretShapeData::StateData& stateData = *mState; 676 677 mStateDelayTime -= dt; 678 679 // Check for transitions. On some states we must wait for the 680 // full timeout value before moving on. 681 if (mStateDelayTime <= 0 || !stateData.waitForTimeout) 682 { 683 S32 ns; 684 685 if ((ns = stateData.transition.rest[mAtRest]) != -1) 686 setTurretState(ns); 687 else if ((ns = stateData.transition.target[mTarget.isValid()]) != -1) 688 setTurretState(ns); 689 else if ((ns = stateData.transition.activated[mStateActive]) != -1) 690 setTurretState(ns); 691 else if (mStateDelayTime <= 0 && (ns = stateData.transition.timeout) != -1) 692 setTurretState(ns); 693 } 694} 695 696void AITurretShape::_scriptCallback(const char* function) 697{ 698 Con::executef( mDataBlock, function, getIdString() ); 699} 700 701//---------------------------------------------------------------------------- 702 703void AITurretShape::_cleanupPotentialTargets() 704{ 705 mPotentialTargets.clear(); 706} 707 708void AITurretShape::_performScan() 709{ 710 // Only on server 711 if (isClientObject()) 712 return; 713 714 // Are we ready for a scan? 715 --mTicksToNextScan; 716 if (mTicksToNextScan > 0) 717 return; 718 719 _cleanupPotentialTargets(); 720 721 _setScanBox(); 722 723 // Set up for the scan 724 getScanTransform(mScanWorkspaceScanMat); 725 mScanWorkspaceScanWorldMat = mScanWorkspaceScanMat; 726 mScanWorkspaceScanWorldMat.affineInverse(); 727 728 disableCollision(); 729 for ( SimSetIterator iter(&mIgnoreObjects); *iter; ++iter ) 730 { 731 ShapeBase* obj = static_cast<ShapeBase*>( *iter ); 732 obj->disableCollision(); 733 } 734 735 gServerContainer.findObjects( mTransformedScanBox, sScanTypeMask, _scanCallback, (void*)this ); 736 737 for ( SimSetIterator iter(&mIgnoreObjects); *iter; ++iter ) 738 { 739 ShapeBase* obj = static_cast<ShapeBase*>( *iter ); 740 obj->enableCollision(); 741 } 742 enableCollision(); 743 744 if (mPotentialTargets.size() == 0) 745 { 746 // No targets in range. Clear out our current target, if necessary. 747 _lostTarget(); 748 } 749 else 750 { 751 // Sort the targets 752 comparePoint = getPosition(); 753 dQsort(mPotentialTargets.address(),mPotentialTargets.size(),sizeof(SimObjectList::value_type),_sortCallback); 754 755 // Go through the targets in order to find one that is not blocked from view 756 Point3F start; 757 mScanWorkspaceScanMat.getColumn(3, &start); 758 S32 index = 0; 759 bool los = false; 760 761 disableCollision(); 762 for (index=0; index < mPotentialTargets.size(); ++index) 763 { 764 ShapeBase* shape = (ShapeBase*)mPotentialTargets[index]; 765 766 Point3F sightPoint; 767 los = _testTargetLineOfSight(start, shape, sightPoint); 768 769 // Check if we have a clear line of sight 770 if (los) 771 break; 772 } 773 enableCollision(); 774 775 // If we found a valid, visible target (no hits between here and there), latch on to it 776 if (los) 777 { 778 _gainedTarget((ShapeBase*)mPotentialTargets[index]); 779 } 780 } 781 782 // Prepare for next scan period 783 mTicksToNextScan = mScanTickFrequency; 784 if (mScanTickFrequencyVariance > 0) 785 { 786 mTicksToNextScan += gRandGen.randI(0, mScanTickFrequencyVariance); 787 } 788} 789 790void AITurretShape::addPotentialTarget(ShapeBase* shape) 791{ 792 mPotentialTargets.push_back(shape); 793} 794 795void AITurretShape::_lostTarget() 796{ 797 mTarget.target = NULL; 798} 799 800void AITurretShape::_gainedTarget(ShapeBase* target) 801{ 802 mTarget.target = target; 803 if (target) 804 { 805 mTarget.hadValidTarget = true; 806 } 807} 808 809void AITurretShape::_trackTarget(F32 dt) 810{ 811 // Only on server 812 if (isClientObject()) 813 return; 814 815 // We can only track a target if we have one 816 if (!mTarget.isValid()) 817 return; 818 819 Point3F targetPos = mTarget.target->getBoxCenter(); 820 821 // Can we see the target? 822 MatrixF aimMat; 823 getAimTransform(aimMat); 824 Point3F start; 825 aimMat.getColumn(3, &start); 826 RayInfo ri; 827 828 Point3F sightPoint; 829 830 disableCollision(); 831 bool los = _testTargetLineOfSight(start, mTarget.target, sightPoint); 832 enableCollision(); 833 834 if (!los) 835 { 836 // Target is blocked. Should we try to track from its last 837 // known position and velocity? 838 SimTime curTime = Sim::getCurrentTime(); 839 if ( (curTime - mTarget.lastSightTime) > (mDataBlock->trackLostTargetTime * 1000.0f) ) 840 { 841 // Time's up. Stop tracking. 842 _cleanupTargetAndTurret(); 843 return; 844 } 845 846 // Use last known information to attempt to 847 // continue to track target for a while. 848 targetPos = mTarget.lastPos + mTarget.lastVel * F32(curTime - mTarget.lastSightTime) / 1000.0f; 849 } 850 else 851 { 852 // Target is visible 853 854 // We only track targets that are alive 855 if (mTarget.target->getDamageState() != Enabled) 856 { 857 // We can't track any more 858 _cleanupTargetAndTurret(); 859 return; 860 } 861 862 targetPos = sightPoint; 863 864 // Store latest target info 865 mTarget.lastPos = targetPos; 866 mTarget.lastVel = mTarget.target->getVelocity(); 867 mTarget.lastSightTime = Sim::getCurrentTime(); 868 } 869 870 // Calculate angles to face the target, specifically the part that we can see 871 VectorF toTarget; 872 MatrixF mat; 873 S32 node = mDataBlock->aimNode; 874 if (node != -1) 875 { 876 // Get the current position of our node 877 MatrixF* nodeTrans = &mShapeInstance->mNodeTransforms[node]; 878 Point3F currentPos; 879 nodeTrans->getColumn(3, ¤tPos); 880 881 // Turn this into a matrix we can use to put the target 882 // position into our space. 883 MatrixF nodeMat(true); 884 nodeMat.setColumn(3, currentPos); 885 mat.mul(mObjToWorld, nodeMat); 886 mat.affineInverse(); 887 } 888 else 889 { 890 mat = mWorldToObj; 891 } 892 mat.mulP(targetPos, &toTarget); 893 894 // lead the target 895 F32 timeToTargetSquared = (mWeaponLeadVelocitySquared > 0) ? toTarget.lenSquared() / mWeaponLeadVelocitySquared : 0; 896 if (timeToTargetSquared > 1.0) 897 { 898 targetPos = targetPos + (mTarget.lastVel * mSqrt(timeToTargetSquared)); 899 mat.mulP(targetPos, &toTarget); 900 } 901 902 F32 yaw, pitch; 903 MathUtils::getAnglesFromVector(toTarget, yaw, pitch); 904 if (yaw > M_PI_F) 905 yaw = yaw - M_2PI_F; 906 //if (pitch > M_PI_F) 907 // pitch = -(pitch - M_2PI_F); 908 909 Point3F rot(-pitch, 0.0f, yaw); 910 911 // If we have a rotation rate make sure we follow it 912 if (mHeadingRate > 0) 913 { 914 F32 rate = mHeadingRate * dt; 915 F32 rateCheck = mFabs(rot.z - mRot.z); 916 if (rateCheck > rate) 917 { 918 // This will clamp the new value to the rate regardless if it 919 // is increasing or decreasing. 920 rot.z = mClampF(rot.z, mRot.z-rate, mRot.z+rate); 921 } 922 } 923 if (mPitchRate > 0) 924 { 925 F32 rate = mPitchRate * dt; 926 F32 rateCheck = mFabs(rot.x - mRot.x); 927 if (rateCheck > rate) 928 { 929 // This will clamp the new value to the rate regardless if it 930 // is increasing or decreasing. 931 rot.x = mClampF(rot.x, mRot.x-rate, mRot.x+rate); 932 } 933 } 934 935 // Test if the rotation to the target is outside of our limits 936 if (_outsideLimits(rot)) 937 { 938 // We can't track any more 939 _cleanupTargetAndTurret(); 940 return; 941 } 942 943 // Test if the target is out of weapons range 944 if (toTarget.lenSquared() > mWeaponRangeSquared) 945 { 946 // We can't track any more 947 _cleanupTargetAndTurret(); 948 return; 949 } 950 951 mRot = rot; 952 953 _setRotation( mRot ); 954 setMaskBits(TurretUpdateMask); 955} 956 957void AITurretShape::_cleanupTargetAndTurret() 958{ 959 _lostTarget(); 960 resetTarget(); 961} 962 963bool AITurretShape::_testTargetLineOfSight(Point3F& aimPoint, ShapeBase* target, Point3F& sightPoint) 964{ 965 Point3F targetCenter = target->getBoxCenter(); 966 RayInfo ri; 967 bool hit = false; 968 969 target->disableCollision(); 970 971 // First check for a clear line of sight to the target's center 972 Point3F testPoint = targetCenter; 973 hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); 974 if (hit) 975 { 976 // No clear line of sight to center, so try to the target's right. Players holding 977 // a gun in their right hand will tend to stick their right shoulder out first if 978 // they're peering around some cover to shoot, like a wall. 979 Box3F targetBounds = target->getObjBox(); 980 F32 radius = targetBounds.len_x() > targetBounds.len_y() ? targetBounds.len_x() : targetBounds.len_y(); 981 radius *= 0.5; 982 983 VectorF toTurret = aimPoint - targetCenter; 984 toTurret.normalizeSafe(); 985 VectorF toTurretRight = mCross(toTurret, Point3F::UnitZ); 986 987 testPoint = targetCenter + toTurretRight * radius; 988 989 hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); 990 991 if (hit) 992 { 993 // No clear line of sight to right, so try the target's left 994 VectorF toTurretLeft = toTurretRight * -1.0f; 995 testPoint = targetCenter + toTurretLeft * radius; 996 hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); 997 } 998 999 if (hit) 1000 { 1001 // No clear line of sight to left, so try the target's top 1002 testPoint = targetCenter; 1003 testPoint.z += targetBounds.len_z() * 0.5f; 1004 hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); 1005 } 1006 1007 if (hit) 1008 { 1009 // No clear line of sight to top, so try the target's bottom 1010 testPoint = targetCenter; 1011 testPoint.z -= targetBounds.len_z() * 0.5f; 1012 hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); 1013 } 1014 } 1015 1016 target->enableCollision(); 1017 1018 if (!hit) 1019 { 1020 // Line of sight point is that last one we tested 1021 sightPoint = testPoint; 1022 } 1023 1024 return !hit; 1025} 1026 1027//---------------------------------------------------------------------------- 1028 1029void AITurretShape::setAllGunsFiring(bool fire) 1030{ 1031 setImageTriggerState(0,fire); 1032 setImageTriggerState(1,fire); 1033 setImageTriggerState(2,fire); 1034 setImageTriggerState(3,fire); 1035} 1036 1037void AITurretShape::setGunSlotFiring(S32 slot, bool fire) 1038{ 1039 if (slot < 0 || slot > 3) 1040 return; 1041 1042 setImageTriggerState(slot, fire); 1043} 1044 1045//---------------------------------------------------------------------------- 1046 1047void AITurretShape::setTransform(const MatrixF& mat) 1048{ 1049 Parent::setTransform(mat); 1050 1051 // Set the scanning box 1052 _setScanBox(); 1053} 1054 1055void AITurretShape::recenterTurret() 1056{ 1057 mRot.set(0,0,0); 1058 _setRotation( mRot ); 1059 setMaskBits(TurretUpdateMask); 1060} 1061 1062void AITurretShape::_setScanBox() 1063{ 1064 mTransformedScanBox = mScanBox; 1065 MatrixF mat; 1066 getScanTransform(mat); 1067 mat.mul(mTransformedScanBox); 1068} 1069 1070void AITurretShape::getScanTransform(MatrixF& mat) 1071{ 1072 if (mDataBlock && mDataBlock->scanNode != -1) 1073 { 1074 if (getNodeTransform(mDataBlock->scanNode, mat)) 1075 { 1076 return; 1077 } 1078 } 1079 1080 mat = mObjToWorld; 1081} 1082 1083void AITurretShape::getAimTransform(MatrixF& mat) 1084{ 1085 if (mDataBlock && mDataBlock->aimNode != -1) 1086 { 1087 if (getNodeTransform(mDataBlock->aimNode, mat)) 1088 { 1089 return; 1090 } 1091 } 1092 1093 mat = mObjToWorld; 1094} 1095 1096//---------------------------------------------------------------------------- 1097 1098void AITurretShape::processTick(const Move* move) 1099{ 1100 Parent::processTick(move); 1101 1102 if (isServerObject() && mDamageState == Enabled) 1103 { 1104 _updateTurretState(TickSec); 1105 1106 if (mScanForTargets) 1107 { 1108 // Perform a scan for targets 1109 _performScan(); 1110 1111 // If we found one, turn off the scan 1112 if (mTarget.isValid()) 1113 { 1114 mScanForTargets = false; 1115 } 1116 } 1117 1118 if (mTrackTarget) 1119 { 1120 _trackTarget(TickSec); 1121 1122 // If the target is lost, no longer track it 1123 if (!mTarget.isValid()) 1124 { 1125 mTrackTarget = false; 1126 } 1127 } 1128 } 1129} 1130 1131void AITurretShape::advanceTime(F32 dt) 1132{ 1133 // If there were any ShapeBase script threads that 1134 // have played, then we need to update all code 1135 // controlled nodes. This is done before the Parent 1136 // call as script threads may play and be destroyed 1137 // before our code is called. 1138 bool updateNodes = false; 1139 for (U32 i = 0; i < MaxScriptThreads; i++) 1140 { 1141 Thread& st = mScriptThread[i]; 1142 if (st.thread) 1143 { 1144 updateNodes = true; 1145 break; 1146 } 1147 } 1148 1149 Parent::advanceTime(dt); 1150 1151 // Update any state thread 1152 AITurretShapeData::StateData& stateData = *mState; 1153 if (mStateAnimThread && stateData.sequence != -1) 1154 { 1155 mShapeInstance->advanceTime(dt,mStateAnimThread); 1156 1157 updateNodes = true; 1158 } 1159 1160 if (updateNodes) 1161 { 1162 // Update all code controlled nodes 1163 _updateNodes(mRot); 1164 } 1165} 1166 1167//---------------------------------------------------------------------------- 1168 1169U32 AITurretShape::packUpdate(NetConnection *connection, U32 mask, BitStream *bstream) 1170{ 1171 U32 retMask = Parent::packUpdate(connection,mask,bstream); 1172 1173 // Indicate that the transform has changed to update the scan box 1174 bstream->writeFlag(mask & (PositionMask | ExtendedInfoMask)); 1175 1176 // Handle any state changes that need to be passed along 1177 if (bstream->writeFlag(mask & TurretStateMask)) 1178 { 1179 bstream->write(mDataBlock->lookupState(mState->name)); 1180 } 1181 1182 return retMask; 1183} 1184 1185void AITurretShape::unpackUpdate(NetConnection *connection, BitStream *bstream) 1186{ 1187 Parent::unpackUpdate(connection,bstream); 1188 1189 // Transform has changed 1190 if (bstream->readFlag()) 1191 { 1192 _setScanBox(); 1193 } 1194 1195 //TurretStateMask 1196 if (bstream->readFlag()) 1197 { 1198 S32 newstate; 1199 bstream->read( &newstate ); 1200 setTurretState(newstate); 1201 } 1202} 1203 1204//---------------------------------------------------------------------------- 1205 1206void AITurretShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ) 1207{ 1208 Parent::prepBatchRender( state, mountedImageIndex ); 1209 1210 if ( !gShowBoundingBox ) 1211 return; 1212 1213 ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>(); 1214 ri->renderDelegate.bind( this, &AITurretShape::_renderScanner ); 1215 ri->type = RenderPassManager::RIT_Editor; 1216 state->getRenderPass()->addInst( ri ); 1217} 1218 1219void AITurretShape::_renderScanner( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) 1220{ 1221 GFXStateBlockDesc desc; 1222 desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); 1223 desc.setZReadWrite(false,true); 1224 desc.fillMode = GFXFillWireframe; 1225 1226 // Render the scan box 1227 GFX->getDrawUtil()->drawCube(desc, mTransformedScanBox.getExtents(), mTransformedScanBox.getCenter(), ColorI(255, 0, 0)); 1228 1229 // Render a line from the scan node to the max scan distance 1230 MatrixF nodeMat; 1231 if (getNodeTransform(mDataBlock->scanNode, nodeMat)) 1232 { 1233 Point3F start; 1234 nodeMat.getColumn(3, &start); 1235 1236 Point3F end(0.0f, mScanDistance, 0.0f); 1237 nodeMat.mulP(end); 1238 1239 GFX->getDrawUtil()->drawLine(start, end, ColorI(255, 255, 0)); 1240 } 1241} 1242 1243//---------------------------------------------------------------------------- 1244 1245DefineEngineMethod( AITurretShape, addToIgnoreList, void, (ShapeBase* obj),, 1246 "@brief Adds object to the turret's ignore list.\n\n" 1247 "All objects in this list will be ignored by the turret's targeting.\n" 1248 "@param obj The ShapeBase object to ignore.\n") 1249{ 1250 object->addToIgnoreList(obj); 1251} 1252 1253DefineEngineMethod( AITurretShape, removeFromIgnoreList, void, (ShapeBase* obj),, 1254 "@brief Removes object from the turret's ignore list.\n\n" 1255 "All objects in this list will be ignored by the turret's targeting.\n" 1256 "@param obj The ShapeBase object to once again allow for targeting.\n") 1257{ 1258 object->removeFromIgnoreList(obj); 1259} 1260 1261DefineEngineMethod( AITurretShape, clearIgnoreList, void, (),, 1262 "@brief Removes all objects from the turret's ignore list.\n\n" 1263 "All objects in this list will be ignored by the turret's targeting.\n") 1264{ 1265 object->clearIgnoreList(); 1266} 1267 1268DefineEngineMethod( AITurretShape, ignoreListCount, S32, (),, 1269 "@brief Returns the number of objects in the turrets ignore list.\n\n" 1270 "All objects in this list will be ignored by the turret's targeting.\n") 1271{ 1272 return object->ignoreListCount(); 1273} 1274 1275DefineEngineMethod( AITurretShape, getIgnoreListObject, SimObject*, (S32 index),, 1276 "@brief Returns the object in the ignore list at index.\n\n" 1277 "All objects in this list will be ignored by the turret's targeting.\n" 1278 "@param index The index of the object in the ignore list being retrieved.\n") 1279{ 1280 return object->getIgnoreListObject(index); 1281} 1282 1283DefineEngineMethod( AITurretShape, setTurretState, void, (const char* newState, bool force), (false), 1284 "@brief Set the turret's current state.\n\n" 1285 "Normally the turret's state comes from updating the state machine but this method " 1286 "allows you to override this and jump to the requested state immediately.\n" 1287 "@param newState The name of the new state.\n" 1288 "@param force Is true then force the full processing of the new state even if it is the " 1289 "same as the current state. If false then only the time out value is reset and the state's " 1290 "script method is called, if any.\n") 1291{ 1292 object->setTurretStateName(newState, force); 1293} 1294 1295DefineEngineMethod( AITurretShape, activateTurret, void, ( ),, 1296 "@brief Activate a turret from a deactive state.\n\n") 1297{ 1298 object->activateTurret(); 1299} 1300 1301DefineEngineMethod( AITurretShape, deactivateTurret, void, ( ),, 1302 "@brief Deactivate a turret from an active state.\n\n") 1303{ 1304 object->deactivateTurret(); 1305} 1306 1307DefineEngineMethod( AITurretShape, startScanForTargets, void, ( ),, 1308 "@brief Begin scanning for a target.\n\n") 1309{ 1310 object->startScanForTargets(); 1311} 1312 1313DefineEngineMethod( AITurretShape, stopScanForTargets, void, ( ),, 1314 "@brief Stop scanning for targets.\n\n" 1315 "@note Only impacts the scanning for new targets. Does not effect a turret's current " 1316 "target lock.\n") 1317{ 1318 object->stopScanForTargets(); 1319} 1320 1321DefineEngineMethod( AITurretShape, startTrackingTarget, void, ( ),, 1322 "@brief Have the turret track the current target.\n\n") 1323{ 1324 object->startTrackingTarget(); 1325} 1326 1327DefineEngineMethod( AITurretShape, stopTrackingTarget, void, ( ),, 1328 "@brief Stop the turret from tracking the current target.\n\n") 1329{ 1330 object->stopTrackingTarget(); 1331} 1332 1333DefineEngineMethod( AITurretShape, hasTarget, bool, (),, 1334 "@brief Indicates if the turret has a target.\n\n" 1335 "@returns True if the turret has a target.\n") 1336{ 1337 return object->hasTarget(); 1338} 1339 1340DefineEngineMethod( AITurretShape, getTarget, SimObject*, (),, 1341 "@brief Get the turret's current target.\n\n" 1342 "@returns The object that is the target's current target, or 0 if no target.\n") 1343{ 1344 return object->getTarget(); 1345} 1346 1347DefineEngineMethod( AITurretShape, resetTarget, void, ( ),, 1348 "@brief Resets the turret's target tracking.\n\n" 1349 "Only resets the internal target tracking. Does not modify the turret's facing.\n") 1350{ 1351 object->resetTarget(); 1352} 1353 1354DefineEngineMethod( AITurretShape, setWeaponLeadVelocity, void, (F32 velocity),, 1355 "@brief Set the turret's projectile velocity to help lead the target.\n\n" 1356 "This value normally comes from AITurretShapeData::weaponLeadVelocity but this method " 1357 "allows you to override the datablock value. This can be useful if the turret changes " 1358 "ammunition, uses a different weapon than the default, is damaged, etc.\n" 1359 "@note Setting this to 0 will disable target leading.\n") 1360{ 1361 object->setWeaponLeadVelocity(velocity); 1362} 1363 1364DefineEngineMethod( AITurretShape, getWeaponLeadVelocity, F32, (),, 1365 "@brief Get the turret's defined projectile velocity that helps with target leading.\n\n" 1366 "@returns The defined weapon projectile speed, or 0 if leading is disabled.\n") 1367{ 1368 return object->getWeaponLeadVelocity(); 1369} 1370 1371DefineEngineMethod( AITurretShape, setAllGunsFiring, void, (bool fire),, 1372 "@brief Set the firing state of the turret's guns.\n\n" 1373 "@param fire Set to true to activate all guns. False to deactivate them.\n") 1374{ 1375 object->setAllGunsFiring(fire); 1376} 1377 1378DefineEngineMethod( AITurretShape, setGunSlotFiring, void, (S32 slot, bool fire),, 1379 "@brief Set the firing state of the given gun slot.\n\n" 1380 "@param slot The gun to modify. Valid range is 0-3 that corresponds to the weapon mount point.\n" 1381 "@param fire Set to true to activate the gun. False to deactivate it.\n") 1382{ 1383 object->setGunSlotFiring(slot, fire); 1384} 1385 1386DefineEngineMethod( AITurretShape, recenterTurret, void, ( ),, 1387 "@brief Recenter the turret's weapon.\n\n") 1388{ 1389 object->recenterTurret(); 1390} 1391