Torque3D Documentation / _generateds / aiTurretShape.cpp

aiTurretShape.cpp

Engine/source/T3D/turret/aiTurretShape.cpp

More...

Public Functions

_scanCallback(SceneObject * object, void * data)
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, &currentPos);
 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