player.cpp

Engine/source/T3D/player.cpp

More...

Public Defines

define
LH_HACK() 1

Public Enumerations

enum
PlayerConstants {  JumpSkipContactsMax = 8
}

Public Functions

accel(F32 & from, F32 to, F32 rate)
ConsoleDocClass(Player , "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
ConsoleDocClass(PlayerData , "@brief Defines properties <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/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Player\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
DefineEngineMethod(Player , allowAllPoses , void , () , "@brief Allow all poses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> chance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">occur.\n\n</a>" "This method resets any poses that have manually been blocked from occuring. " "This includes the regular pose states such as sprinting, crouch , being prone " "and swimming. It also includes being able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> jump and jet jump. While this " "is allowing these poses <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> occur it doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> mean that they all can due <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> other " "conditions. We 're just not manually blocking them from being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">allowed.\n</a>" " @see allowJumping()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowJetJumping()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowSprinting()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowCrouching()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowProne()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowSwimming()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , allowCrouching , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">crouch.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow crouching unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow crouching " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow crouching, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , allowJetJumping , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> jet <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">jump.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jet jumping unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow jet jumping " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jet jumping, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , allowJumping , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">jump.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jumping unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow jumping " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jumping, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , allowProne , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> go <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">prone.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow being prone unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow going prone " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow being prone, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , allowSprinting , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">sprint.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow sprinting unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow sprinting " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow sprinting, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , allowSwimming , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swim.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow swimming unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow swimming " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow swimming, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Player , checkDismountPoint , bool , (Point3F oldPos, Point3F pos) , "@brief Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is safe <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dismount at this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n\n</a>" "Internally this method casts <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> ray from oldPos <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> pos <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> determine <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it hits the " " terrain, an interior object, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> water object, another player, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> static shape, " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>  vehicle, or physical zone. If this ray " "is in the clear, then the player 's bounding box is also checked <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> collision at " "the pos position. If this displaced bounding box is also in the clear, then " "checkDismountPoint() returns <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" " @param oldPos The player 's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" " @param pos The dismount position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">check\n</a>" " @return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the dismount position is clear, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not\n</a>" " @note The player must be already mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this method <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">assert.\n</a>" )
DefineEngineMethod(Player , clearControlObject , void , () )
DefineEngineMethod(Player , copyHeadRotation , void , (Player *other_player) , "" )
DefineEngineMethod(Player , getControlObject , S32 , () , "@brief Get the current object we are <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controlling.\n\n</a>" "@return ID of the <a href="/coding/class/classshapebase/">ShapeBase</a> object we control, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not controlling an " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @see setControlObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see clearControlObject()" )
DefineEngineMethod(Player , getDamageLocation , const char * , (Point3F pos) , "@brief Get the named damage location and modifier <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> given world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n\n</a>" "the <a href="/coding/class/classplayer/">Player</a> object can simulate different hit locations based on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> pre-defined set " "of <a href="/coding/class/structplayerdata/">PlayerData</a> defined percentages. These hit percentages divide up the <a href="/coding/class/classplayer/">Player</a>'s " "bounding box into different regions. The diagram below demonstrates how the various " "<a href="/coding/class/structplayerdata/">PlayerData</a> properties split up the bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume:\n\n</a>" "<img src=\"images/player_damageloc.png\">\n\n" "While you may pass in any world position and getDamageLocation() will provide <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> best-fit " " location, you should be aware that this can produce some interesting results. For example, " "any position that is above <a href="/coding/class/structplayerdata/#structplayerdata_1aecaba01bf37a4ccc9ca439392a015b13">PlayerData::boxHeadPercentage</a> will be considered <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 'head' hit, even " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the world position is high in the sky. Therefore it may be wise <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep the passed in point " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> somewhere on the surface of, or within, the <a href="/coding/class/classplayer/">Player</a> 's bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume.\n\n</a>" " @note This method will not return an accurate location when the player is " "prone or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swimming.\n\n</a>" " @param pos A world position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> retrieve <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> body region on this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player.\n</a>" " @return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> string containing two  words, where the " "first is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> location and the second is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">modifier.\n\n</a>" "Posible locations:< ul >" "< li >head</li >" "< li >torso</li >" "< li >legs</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "Head modifiers:< ul >" "< li >left_back</li >" "< li >middle_back</li >" "< li >right_back</li >" "< li >left_middle</li >" "< li >middle_middle</li >" "< li >right_middle</li >" "< li >left_front</li >" "< li >middle_front</li >" "< li >right_front</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "Legs/Torso modifiers:< ul >" "< li >front_left</li >" "< li >front_right</li >" "< li >back_left</li >" "< li >back_right</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadFrontPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadBackPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadLeftPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadRightPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxTorsoPercentage\n</a>" )
DefineEngineMethod(Player , getNumDeathAnimations , S32 , () , "@brief Get the number of death animations available <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player.\n\n</a>" "Death animations are assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be named death1-N using consecutive indices." )
DefineEngineMethod(Player , getPose , const char * , () , "@brief Get the name of the player's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pose.\n\n</a>" "The pose is one of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">following:\n\n</a><ul>" "<li>Stand - Standard movement pose.</li>" "<li>Sprint - Sprinting pose.</li>" "<li>Crouch - Crouch pose.</li>" "<li>Prone - Prone pose.</li>" "<li>Swim - Swimming pose.</li></ul>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return The current pose; one of: \"Stand\" , \"Sprint\" , \"Crouch\" , \"Prone\" , \"Swim\"\n" )
DefineEngineMethod(Player , getState , const char * , () , "@brief Get the name of the player's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" "The state is one of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">following:\n\n</a><ul>" "<li>Dead - The <a href="/coding/class/classplayer/">Player</a> is dead.</li>" "<li>Mounted - The <a href="/coding/class/classplayer/">Player</a> is mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an object such as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vehicle.</li>" "<li><a href="/coding/class/structmove/">Move</a> - The <a href="/coding/class/classplayer/">Player</a> is free <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move. The usual state.</li>" "<li>Recover - The <a href="/coding/class/classplayer/">Player</a> is recovering from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> fall. See PlayerData::recoverDelay.</li></ul>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return The current state; one of: \"Dead\" , \"Mounted\" , \"Move\" , \"Recover\"\n" )
DefineEngineMethod(Player , isAnimationLocked , bool , () , "" )
DefineEngineMethod(Player , setActionThread , bool , (const char *name, bool hold, bool fsp) , (false, true) , "@brief Set the <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> action sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player.\n\n</a>" "@param name Name of the action sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set\n</a>" "@param hold Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> callback on the datablock when the sequence ends (PlayerData::animationDone()). " "When set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true no callback is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">made.\n</a>" "@param fsp True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> first person and none of the spine nodes in the shape should animate. False will allow the shape's " "spine nodes <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">animate.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> succesful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n</a>" " @note The spine nodes <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/class/classplayer/">Player</a> 's shape are named as <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">follows:\n\n</a>< ul >" "< li >Bip01 Pelvis</li >" "< li >Bip01 Spine</li >" "< li >Bip01 Spine1</li >" "< li >Bip01 Spine2</li >" "< li >Bip01 Neck</li >" "< li >Bip01 Head</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "You cannot use setActionThread() <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have the <a href="/coding/class/classplayer/">Player</a> play one of the motion " "determined action animation sequences. These sequences are chosen based on how " "the <a href="/coding/class/classplayer/">Player</a> moves and the <a href="/coding/class/classplayer/">Player</a> 's current pose. The names of these sequences <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">are:\n\n</a>< ul >" "< li >root</li >" "< li >run</li >" "< li >side</li >" "< li >side_right</li >" "< li >crouch_root</li >" "< li >crouch_forward</li >" "< li >crouch_backward</li >" "< li >crouch_side</li >" "< li >crouch_right</li >" "< li >prone_root</li >" "< li >prone_forward</li >" "< li >prone_backward</li >" "< li >swim_root</li >" "< li >swim_forward</li >" "< li >swim_backward</li >" "< li >swim_left</li >" "< li >swim_right</li >" "< li >fall</li >" "< li >jump</li >" "< li >standjump</li >" "< li >land</li >" "< li >jet</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "If the player moves in any direction then the animation sequence set using this " "method will be cancelled and the chosen mation-based sequence will take over. This makes " "great <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> times when the <a href="/coding/class/classplayer/">Player</a> cannot move, such as when mounted, or when it doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> matter " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the action sequence changes, such as waving and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">saluting.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Place the player in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sitting position after being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mounted\n</a>" "%player.setActionThread(\"sitting\", true, true );\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )
DefineEngineMethod(Player , setArmThread , bool , (const char *name) , "@brief Set the sequence that controls the player's arms (dynamically adjusted " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> match look direction).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@param name Name of the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play on the player's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">arms.\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed.\n</a>" " @note By default the 'look' sequence is used, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">available.\n</a>" )
DefineEngineMethod(Player , setControlObject , bool , (ShapeBase *obj) , "@brief Set the object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be controlled by this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player\n\n</a>" "It is possible <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have the moves sent <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/class/classplayer/">Player</a> object from the " "<a href="/coding/class/classgameconnection/">GameConnection</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be passed along <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another object. This happens, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> example " "when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> player is mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vehicle. The move commands pass through the <a href="/coding/class/classplayer/">Player</a> " "and on <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the vehicle(<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> the player remains stationary within the vehicle). " "With setControlObject() you can have the <a href="/coding/class/classplayer/">Player</a> pass along its moves <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any object. " "One possible use is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> player <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> remote controlled vehicle. In this case " "the player does not mount the vehicle directly, but still wants <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> with this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player\n</a>" " @return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the object is valid, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not\n</a>" " @see getControlObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see clearControlObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see <a href="/coding/class/classgameconnection/#classgameconnection_1aad985dd7714086beede38fd1a0ffa348">GameConnection::setControlObject</a>()" )
DefineEngineMethod(Player , setLookAnimationOverride , void , (bool flag) , "" )
DefineEngineMethod(Player , setMovementSpeedBias , void , (F32 bias) , "setMovementSpeedBias(<a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> bias)" )
IMPLEMENT_CALLBACK(PlayerData , animationDone , void , (Player *obj) , (obj) , "@brief Called on the server when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scripted animation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">completes.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@see <a href="/coding/class/classplayer/#classplayer_1ac39c29e6eed89d0a1e854a33689401b3">Player::setActionThread</a>() <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> setting <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scripted animation and its 'hold' parameter <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "determine <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> this callback is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">used.\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , doDismount , void , (Player *obj) , (obj) , "@brief Called when attempting <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dismount the player from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n\n</a>" "It is up <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the doDismount() method <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> actually perform the dismount. Often " "there are some conditions that prevent this, such as the vehicle moving too <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fast.\n</a>" " @param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onEnterLiquid , void , (Player *obj, F32 coverage, const char *type) , (obj, coverage, type) , "@brief Called when the player enters <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param coverage Percentage of the player's bounding box covered by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid\n</a>" "@param type The type of liquid the player has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onEnterMissionArea , void , (Player *obj) , (obj) , "@brief Called when the player enters the mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">area.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MissionArea\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onLeaveLiquid , void , (Player *obj, const char *type) , (obj, type) , "@brief Called when the player leaves <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param type The type of liquid the player has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onLeaveMissionArea , void , (Player *obj) , (obj) , "@brief Called when the player leaves the mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">area.\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MissionArea\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onPoseChange , void , (Player *obj, const char *oldPose, const char *newPose) , (obj, oldPose, newPose) , "@brief Called when the player changes <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">poses.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param oldPose The pose the player is switching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "@param newPose The pose the player is switching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onStartSprintMotion , void , (Player *obj) , (obj) , "@brief Called when the player starts moving <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Sprint <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pose.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onStartSwim , void , (Player *obj) , (obj) , "@brief Called when the player starts <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swimming.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onStopSprintMotion , void , (Player *obj) , (obj) , "@brief Called when the player stops moving <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Sprint <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pose.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(PlayerData , onStopSwim , void , (Player *obj) , (obj) , "@brief Called when the player stops <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swimming.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
ImplementEnumType(PlayerPose , "@brief The pose of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Player.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )

Detailed Description

Public Defines

LH_HACK() 1

Public Enumerations

PlayerConstants

Enumerator

JumpSkipContactsMax = 8

Public Variables

 EndImplementEnumType 
AngAxisF gPlayerMoveRot 
MatrixF IMat (1)
F32 sAnimationTransitionTime 
U32 sBalance 
U32 sClientCollisionContactMask 
U32 sCollisionMoveMask 
const S32 sContactTickTime 
S32 sCrouchTrigger 
S32 sImageTrigger0 
S32 sImageTrigger1 
S32 sJumpJetTrigger 
S32 sJumpTrigger 
F32 sLandReverseScale 
F32 sMaxImpulseVelocity 
S32 sMaxPredictionTicks 
S32 sMaxWarpTicks 
F32 sMinFaceDistance 
F32 sMinWarpTicks 
const F32 sMountPendingTickWait 
U32 sMoveRetryCount 
const F32 sNewAnimationTickTime 
F32 sNormalElasticity 
const U32 sPlayerConformMask 
S32 sProneTrigger 
S32 sRenderMyItems 
S32 sRenderMyPlayer 
bool sRenderPlayerCollision 
U32 sServerCollisionContactMask 
F32 sSlowStandThreshSquared 
S32 sSprintTrigger 
F32 sTractionDistance 
bool sUseAnimationTransitions 
S32 sVehicleDismountTrigger 
F32 sVerticalStepDot 

Public Functions

accel(F32 & from, F32 to, F32 rate)

ConsoleDocClass(Player , "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )

ConsoleDocClass(PlayerData , "@brief Defines properties <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/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Player\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )

createInterpPos(const Point3F & s, const Point3F & e, const F32 t, const F32 d)

DefineEngineMethod(Player , allowAllPoses , void , () , "@brief Allow all poses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> chance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">occur.\n\n</a>" "This method resets any poses that have manually been blocked from occuring. " "This includes the regular pose states such as sprinting, crouch , being prone " "and swimming. It also includes being able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> jump and jet jump. While this " "is allowing these poses <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> occur it doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> mean that they all can due <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> other " "conditions. We 're just not manually blocking them from being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">allowed.\n</a>" " @see allowJumping()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowJetJumping()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowSprinting()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowCrouching()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowProne()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see allowSwimming()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , allowCrouching , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">crouch.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow crouching unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow crouching " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow crouching, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , allowJetJumping , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> jet <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">jump.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jet jumping unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow jet jumping " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jet jumping, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , allowJumping , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">jump.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jumping unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow jumping " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow jumping, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , allowProne , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> go <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">prone.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow being prone unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow going prone " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow being prone, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , allowSprinting , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">sprint.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow sprinting unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow sprinting " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow sprinting, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , allowSwimming , void , (bool state) , "@brief Set <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/class/classplayer/">Player</a> is allowed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swim.\n\n</a>" "The default is <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow swimming unless there are other environmental concerns " "that prevent it. This method is mainly used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> explicitly disallow swimming " "at any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">time.\n</a>" "@param state Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> allow swimming, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @see allowAllPoses()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Player , checkDismountPoint , bool , (Point3F oldPos, Point3F pos) , "@brief Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is safe <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dismount at this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n\n</a>" "Internally this method casts <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> ray from oldPos <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> pos <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> determine <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it hits the " " terrain, an interior object, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> water object, another player, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> static shape, " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>  vehicle, or physical zone. If this ray " "is in the clear, then the player 's bounding box is also checked <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> collision at " "the pos position. If this displaced bounding box is also in the clear, then " "checkDismountPoint() returns <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" " @param oldPos The player 's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" " @param pos The dismount position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">check\n</a>" " @return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the dismount position is clear, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not\n</a>" " @note The player must be already mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this method <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">assert.\n</a>" )

DefineEngineMethod(Player , clearControlObject , void , () )

DefineEngineMethod(Player , copyHeadRotation , void , (Player *other_player) , "" )

DefineEngineMethod(Player , getControlObject , S32 , () , "@brief Get the current object we are <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controlling.\n\n</a>" "@return ID of the <a href="/coding/class/classshapebase/">ShapeBase</a> object we control, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not controlling an " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @see setControlObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see clearControlObject()" )

DefineEngineMethod(Player , getDamageLocation , const char * , (Point3F pos) , "@brief Get the named damage location and modifier <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> given world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n\n</a>" "the <a href="/coding/class/classplayer/">Player</a> object can simulate different hit locations based on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> pre-defined set " "of <a href="/coding/class/structplayerdata/">PlayerData</a> defined percentages. These hit percentages divide up the <a href="/coding/class/classplayer/">Player</a>'s " "bounding box into different regions. The diagram below demonstrates how the various " "<a href="/coding/class/structplayerdata/">PlayerData</a> properties split up the bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume:\n\n</a>" "<img src=\"images/player_damageloc.png\">\n\n" "While you may pass in any world position and getDamageLocation() will provide <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> best-fit " " location, you should be aware that this can produce some interesting results. For example, " "any position that is above <a href="/coding/class/structplayerdata/#structplayerdata_1aecaba01bf37a4ccc9ca439392a015b13">PlayerData::boxHeadPercentage</a> will be considered <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 'head' hit, even " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the world position is high in the sky. Therefore it may be wise <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep the passed in point " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> somewhere on the surface of, or within, the <a href="/coding/class/classplayer/">Player</a> 's bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume.\n\n</a>" " @note This method will not return an accurate location when the player is " "prone or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swimming.\n\n</a>" " @param pos A world position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> retrieve <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> body region on this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player.\n</a>" " @return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> string containing two  words, where the " "first is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> location and the second is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">modifier.\n\n</a>" "Posible locations:< ul >" "< li >head</li >" "< li >torso</li >" "< li >legs</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "Head modifiers:< ul >" "< li >left_back</li >" "< li >middle_back</li >" "< li >right_back</li >" "< li >left_middle</li >" "< li >middle_middle</li >" "< li >right_middle</li >" "< li >left_front</li >" "< li >middle_front</li >" "< li >right_front</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "Legs/Torso modifiers:< ul >" "< li >front_left</li >" "< li >front_right</li >" "< li >back_left</li >" "< li >back_right</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadFrontPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadBackPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadLeftPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxHeadRightPercentage\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PlayerData::boxTorsoPercentage\n</a>" )

DefineEngineMethod(Player , getNumDeathAnimations , S32 , () , "@brief Get the number of death animations available <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player.\n\n</a>" "Death animations are assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be named death1-N using consecutive indices." )

DefineEngineMethod(Player , getPose , const char * , () , "@brief Get the name of the player's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pose.\n\n</a>" "The pose is one of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">following:\n\n</a><ul>" "<li>Stand - Standard movement pose.</li>" "<li>Sprint - Sprinting pose.</li>" "<li>Crouch - Crouch pose.</li>" "<li>Prone - Prone pose.</li>" "<li>Swim - Swimming pose.</li></ul>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return The current pose; one of: \"Stand\" , \"Sprint\" , \"Crouch\" , \"Prone\" , \"Swim\"\n" )

DefineEngineMethod(Player , getState , const char * , () , "@brief Get the name of the player's current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" "The state is one of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">following:\n\n</a><ul>" "<li>Dead - The <a href="/coding/class/classplayer/">Player</a> is dead.</li>" "<li>Mounted - The <a href="/coding/class/classplayer/">Player</a> is mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an object such as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vehicle.</li>" "<li><a href="/coding/class/structmove/">Move</a> - The <a href="/coding/class/classplayer/">Player</a> is free <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move. The usual state.</li>" "<li>Recover - The <a href="/coding/class/classplayer/">Player</a> is recovering from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> fall. See PlayerData::recoverDelay.</li></ul>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return The current state; one of: \"Dead\" , \"Mounted\" , \"Move\" , \"Recover\"\n" )

DefineEngineMethod(Player , isAnimationLocked , bool , () , "" )

DefineEngineMethod(Player , setActionThread , bool , (const char *name, bool hold, bool fsp) , (false, true) , "@brief Set the <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> action sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player.\n\n</a>" "@param name Name of the action sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set\n</a>" "@param hold Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> callback on the datablock when the sequence ends (PlayerData::animationDone()). " "When set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true no callback is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">made.\n</a>" "@param fsp True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> first person and none of the spine nodes in the shape should animate. False will allow the shape's " "spine nodes <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">animate.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> succesful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n</a>" " @note The spine nodes <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/class/classplayer/">Player</a> 's shape are named as <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">follows:\n\n</a>< ul >" "< li >Bip01 Pelvis</li >" "< li >Bip01 Spine</li >" "< li >Bip01 Spine1</li >" "< li >Bip01 Spine2</li >" "< li >Bip01 Neck</li >" "< li >Bip01 Head</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "You cannot use setActionThread() <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have the <a href="/coding/class/classplayer/">Player</a> play one of the motion " "determined action animation sequences. These sequences are chosen based on how " "the <a href="/coding/class/classplayer/">Player</a> moves and the <a href="/coding/class/classplayer/">Player</a> 's current pose. The names of these sequences <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">are:\n\n</a>< ul >" "< li >root</li >" "< li >run</li >" "< li >side</li >" "< li >side_right</li >" "< li >crouch_root</li >" "< li >crouch_forward</li >" "< li >crouch_backward</li >" "< li >crouch_side</li >" "< li >crouch_right</li >" "< li >prone_root</li >" "< li >prone_forward</li >" "< li >prone_backward</li >" "< li >swim_root</li >" "< li >swim_forward</li >" "< li >swim_backward</li >" "< li >swim_left</li >" "< li >swim_right</li >" "< li >fall</li >" "< li >jump</li >" "< li >standjump</li >" "< li >land</li >" "< li >jet</li ></ul >\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "If the player moves in any direction then the animation sequence set using this " "method will be cancelled and the chosen mation-based sequence will take over. This makes " "great <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> times when the <a href="/coding/class/classplayer/">Player</a> cannot move, such as when mounted, or when it doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> matter " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the action sequence changes, such as waving and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">saluting.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Place the player in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sitting position after being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mounted\n</a>" "%player.setActionThread(\"sitting\", true, true );\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

DefineEngineMethod(Player , setArmThread , bool , (const char *name) , "@brief Set the sequence that controls the player's arms (dynamically adjusted " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> match look direction).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@param name Name of the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play on the player's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">arms.\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed.\n</a>" " @note By default the 'look' sequence is used, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">available.\n</a>" )

DefineEngineMethod(Player , setControlObject , bool , (ShapeBase *obj) , "@brief Set the object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be controlled by this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player\n\n</a>" "It is possible <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have the moves sent <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/class/classplayer/">Player</a> object from the " "<a href="/coding/class/classgameconnection/">GameConnection</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be passed along <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another object. This happens, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> example " "when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> player is mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vehicle. The move commands pass through the <a href="/coding/class/classplayer/">Player</a> " "and on <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the vehicle(<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> the player remains stationary within the vehicle). " "With setControlObject() you can have the <a href="/coding/class/classplayer/">Player</a> pass along its moves <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any object. " "One possible use is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> player <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> remote controlled vehicle. In this case " "the player does not mount the vehicle directly, but still wants <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n</a>" " @param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> with this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player\n</a>" " @return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the object is valid, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not\n</a>" " @see getControlObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see clearControlObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @see <a href="/coding/class/classgameconnection/#classgameconnection_1aad985dd7714086beede38fd1a0ffa348">GameConnection::setControlObject</a>()" )

DefineEngineMethod(Player , setLookAnimationOverride , void , (bool flag) , "" )

DefineEngineMethod(Player , setMovementSpeedBias , void , (F32 bias) , "setMovementSpeedBias(<a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> bias)" )

IMPLEMENT_CALLBACK(PlayerData , animationDone , void , (Player *obj) , (obj) , "@brief Called on the server when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scripted animation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">completes.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@see <a href="/coding/class/classplayer/#classplayer_1ac39c29e6eed89d0a1e854a33689401b3">Player::setActionThread</a>() <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> setting <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scripted animation and its 'hold' parameter <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "determine <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> this callback is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">used.\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , doDismount , void , (Player *obj) , (obj) , "@brief Called when attempting <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dismount the player from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n\n</a>" "It is up <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the doDismount() method <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> actually perform the dismount. Often " "there are some conditions that prevent this, such as the vehicle moving too <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fast.\n</a>" " @param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onEnterLiquid , void , (Player *obj, F32 coverage, const char *type) , (obj, coverage, type) , "@brief Called when the player enters <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param coverage Percentage of the player's bounding box covered by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid\n</a>" "@param type The type of liquid the player has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onEnterMissionArea , void , (Player *obj) , (obj) , "@brief Called when the player enters the mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">area.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MissionArea\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onLeaveLiquid , void , (Player *obj, const char *type) , (obj, type) , "@brief Called when the player leaves <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param type The type of liquid the player has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onLeaveMissionArea , void , (Player *obj) , (obj) , "@brief Called when the player leaves the mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">area.\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MissionArea\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onPoseChange , void , (Player *obj, const char *oldPose, const char *newPose) , (obj, oldPose, newPose) , "@brief Called when the player changes <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">poses.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param oldPose The pose the player is switching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "@param newPose The pose the player is switching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onStartSprintMotion , void , (Player *obj) , (obj) , "@brief Called when the player starts moving <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Sprint <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pose.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onStartSwim , void , (Player *obj) , (obj) , "@brief Called when the player starts <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swimming.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onStopSprintMotion , void , (Player *obj) , (obj) , "@brief Called when the player stops moving <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Sprint <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pose.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(PlayerData , onStopSwim , void , (Player *obj) , (obj) , "@brief Called when the player stops <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">swimming.\n\n</a>" "@param obj The <a href="/coding/class/classplayer/">Player</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(PlayerData )

IMPLEMENT_CO_NETOBJECT_V1(Player )

ImplementEnumType(PlayerPose , "@brief The pose of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Player.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )

   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//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28#include "platform/platform.h"
  29#include "T3D/player.h"
  30
  31#include "platform/profiler.h"
  32#include "math/mMath.h"
  33#include "math/mathIO.h"
  34#include "core/resourceManager.h"
  35#include "core/stringTable.h"
  36#include "core/volume.h"
  37#include "core/stream/bitStream.h"
  38#include "console/consoleTypes.h"
  39#include "console/engineAPI.h"
  40#include "collision/extrudedPolyList.h"
  41#include "collision/clippedPolyList.h"
  42#include "collision/earlyOutPolyList.h"
  43#include "ts/tsShapeInstance.h"
  44#include "sfx/sfxSystem.h"
  45#include "sfx/sfxTrack.h"
  46#include "sfx/sfxSource.h"
  47#include "sfx/sfxTypes.h"
  48#include "scene/sceneManager.h"
  49#include "scene/sceneRenderState.h"
  50#include "T3D/gameBase/gameConnection.h"
  51#include "T3D/trigger.h"
  52#include "T3D/physicalZone.h"
  53#include "T3D/item.h"
  54#include "T3D/missionArea.h"
  55#include "T3D/fx/particleEmitter.h"
  56#include "T3D/fx/cameraFXMgr.h"
  57#include "T3D/fx/splash.h"
  58#include "T3D/tsStatic.h"
  59#include "T3D/physics/physicsPlugin.h"
  60#include "T3D/physics/physicsPlayer.h"
  61#include "T3D/decal/decalManager.h"
  62#include "T3D/decal/decalData.h"
  63#include "materials/baseMatInstance.h"
  64#include "math/mathUtils.h"
  65#include "gfx/sim/debugDraw.h"
  66
  67#ifdef TORQUE_EXTENDED_MOVE
  68   #include "T3D/gameBase/extended/extendedMove.h"
  69#endif
  70
  71#ifdef TORQUE_OPENVR
  72#include "platform/input/openVR/openVRProvider.h"
  73#include "platform/input/openVR/openVRTrackedObject.h"
  74#endif
  75
  76// Amount of time if takes to transition to a new action sequence.
  77static F32 sAnimationTransitionTime = 0.25f;
  78static bool sUseAnimationTransitions = true;
  79static F32 sLandReverseScale = 0.25f;
  80static F32 sSlowStandThreshSquared = 1.69f;
  81static S32 sRenderMyPlayer = true;
  82static S32 sRenderMyItems = true;
  83static bool sRenderPlayerCollision = false;
  84
  85// Chooses new action animations every n ticks.
  86static const F32 sNewAnimationTickTime = 4.0f;
  87static const F32 sMountPendingTickWait = 13.0f * F32(TickMs);
  88
  89// Number of ticks before we pick non-contact animations
  90static const S32 sContactTickTime = 10;
  91
  92// Movement constants
  93static F32 sVerticalStepDot = 0.173f;   // 80
  94static F32 sMinFaceDistance = 0.01f;
  95static F32 sTractionDistance = 0.04f;
  96static F32 sNormalElasticity = 0.01f;
  97static U32 sMoveRetryCount = 5;
  98static F32 sMaxImpulseVelocity = 200.0f;
  99
 100// Move triggers
 101static S32 sJumpTrigger = 2;
 102static S32 sCrouchTrigger = 3;
 103static S32 sProneTrigger = 4;
 104static S32 sSprintTrigger = 5;
 105static S32 sImageTrigger0 = 0;
 106static S32 sImageTrigger1 = 1;
 107static S32 sJumpJetTrigger = 1;
 108static S32 sVehicleDismountTrigger = 2;
 109
 110// Client prediction
 111static F32 sMinWarpTicks = 0.5f;       // Fraction of tick at which instant warp occurs
 112static S32 sMaxWarpTicks = 3;          // Max warp duration in ticks
 113static S32 sMaxPredictionTicks = 30;   // Number of ticks to predict
 114
 115S32 Player::smExtendedMoveHeadPosRotIndex = 0;  // The ExtendedMove position/rotation index used for head movements
 116
 117
 118//
 119static U32 sCollisionMoveMask =  TerrainObjectType       |
 120                                 WaterObjectType         | 
 121                                 PlayerObjectType        |
 122                                 StaticShapeObjectType   | 
 123                                 VehicleObjectType       |
 124                         PhysicalZoneObjectType  |
 125// PATHSHAPE
 126                         PathShapeObjectType;
 127// PATHSHAPE END
 128
 129static U32 sServerCollisionContactMask = sCollisionMoveMask |
 130                                         ItemObjectType     |
 131                                         TriggerObjectType  |
 132                                         CorpseObjectType;
 133
 134static U32 sClientCollisionContactMask = sCollisionMoveMask |
 135                                         TriggerObjectType;
 136
 137enum PlayerConstants {
 138   JumpSkipContactsMax = 8
 139};
 140
 141//----------------------------------------------------------------------------
 142// Player shape animation sequences:
 143
 144// Action Animations:
 145PlayerData::ActionAnimationDef PlayerData::ActionAnimationList[NumTableActionAnims] =
 146{
 147   // *** WARNING ***
 148   // This array is indexed using the enum values defined in player.h
 149
 150   // Root is the default animation
 151   { "root" },       // RootAnim,
 152
 153   // These are selected in the move state based on velocity
 154   { "run",          { 0.0f, 1.0f, 0.0f } },       // RunForwardAnim,
 155   { "back",         { 0.0f,-1.0f, 0.0f } },       // BackBackwardAnim
 156   { "side",         {-1.0f, 0.0f, 0.0f } },       // SideLeftAnim,
 157   { "side_right",   { 1.0f, 0.0f, 0.0f } },       // SideRightAnim,
 158
 159   { "sprint_root" },
 160   { "sprint_forward",  { 0.0f, 1.0f, 0.0f } },
 161   { "sprint_backward", { 0.0f,-1.0f, 0.0f } },
 162   { "sprint_side",     {-1.0f, 0.0f, 0.0f } },
 163   { "sprint_right",    { 1.0f, 0.0f, 0.0f } },
 164
 165   { "crouch_root" },
 166   { "crouch_forward",  { 0.0f, 1.0f, 0.0f } },
 167   { "crouch_backward", { 0.0f,-1.0f, 0.0f } },
 168   { "crouch_side",     {-1.0f, 0.0f, 0.0f } },
 169   { "crouch_right",    { 1.0f, 0.0f, 0.0f } },
 170
 171   { "prone_root" },
 172   { "prone_forward",   { 0.0f, 1.0f, 0.0f } },
 173   { "prone_backward",  { 0.0f,-1.0f, 0.0f } },
 174
 175   { "swim_root" },
 176   { "swim_forward",    { 0.0f, 1.0f, 0.0f } },
 177   { "swim_backward",   { 0.0f,-1.0f, 0.0f }  },
 178   { "swim_left",       {-1.0f, 0.0f, 0.0f }  },
 179   { "swim_right",      { 1.0f, 0.0f, 0.0f }  },
 180
 181   // These are set explicitly based on player actions
 182   { "fall" },       // FallAnim
 183   { "jump" },       // JumpAnim
 184   { "standjump" },  // StandJumpAnim
 185   { "land" },       // LandAnim
 186   { "jet" },        // JetAnim
 187};
 188
 189
 190//----------------------------------------------------------------------------
 191//----------------------------------------------------------------------------
 192
 193//----------------------------------------------------------------------------
 194
 195IMPLEMENT_CO_DATABLOCK_V1(PlayerData);
 196
 197ConsoleDocClass( PlayerData,
 198   "@brief Defines properties for a Player object.\n\n"
 199   "@see Player\n"
 200   "@ingroup gameObjects\n"
 201);
 202
 203IMPLEMENT_CALLBACK( PlayerData, onPoseChange, void, ( Player* obj, const char* oldPose, const char* newPose ), ( obj, oldPose, newPose ),
 204   "@brief Called when the player changes poses.\n\n"
 205   "@param obj The Player object\n"
 206   "@param oldPose The pose the player is switching from.\n"
 207   "@param newPose The pose the player is switching to.\n");
 208
 209IMPLEMENT_CALLBACK( PlayerData, onStartSwim, void, ( Player* obj ), ( obj ),
 210   "@brief Called when the player starts swimming.\n\n"
 211   "@param obj The Player object\n" );
 212
 213IMPLEMENT_CALLBACK( PlayerData, onStopSwim, void, ( Player* obj ), ( obj ),
 214   "@brief Called when the player stops swimming.\n\n"
 215   "@param obj The Player object\n" );
 216
 217IMPLEMENT_CALLBACK( PlayerData, onStartSprintMotion, void, ( Player* obj ), ( obj ),
 218   "@brief Called when the player starts moving while in a Sprint pose.\n\n"
 219   "@param obj The Player object\n" );
 220
 221IMPLEMENT_CALLBACK( PlayerData, onStopSprintMotion, void, ( Player* obj ), ( obj ),
 222   "@brief Called when the player stops moving while in a Sprint pose.\n\n"
 223   "@param obj The Player object\n" );
 224
 225IMPLEMENT_CALLBACK( PlayerData, doDismount, void, ( Player* obj ), ( obj ),
 226   "@brief Called when attempting to dismount the player from a vehicle.\n\n"
 227   "It is up to the doDismount() method to actually perform the dismount.  Often "
 228   "there are some conditions that prevent this, such as the vehicle moving too fast.\n"
 229   "@param obj The Player object\n" );
 230
 231IMPLEMENT_CALLBACK( PlayerData, onEnterLiquid, void, ( Player* obj, F32 coverage, const char* type ), ( obj, coverage, type ),
 232   "@brief Called when the player enters a liquid.\n\n"
 233   "@param obj The Player object\n"
 234   "@param coverage Percentage of the player's bounding box covered by the liquid\n"
 235   "@param type The type of liquid the player has entered\n" );
 236
 237IMPLEMENT_CALLBACK( PlayerData, onLeaveLiquid, void, ( Player* obj, const char* type ), ( obj, type ),
 238   "@brief Called when the player leaves a liquid.\n\n"
 239   "@param obj The Player object\n"
 240   "@param type The type of liquid the player has left\n" );
 241
 242IMPLEMENT_CALLBACK( PlayerData, animationDone, void, ( Player* obj ), ( obj ),
 243   "@brief Called on the server when a scripted animation completes.\n\n"
 244   "@param obj The Player object\n"
 245   "@see Player::setActionThread() for setting a scripted animation and its 'hold' parameter to "
 246   "determine if this callback is used.\n" );
 247
 248IMPLEMENT_CALLBACK( PlayerData, onEnterMissionArea, void, ( Player* obj ), ( obj ),
 249   "@brief Called when the player enters the mission area.\n\n"
 250   "@param obj The Player object\n"
 251   "@see MissionArea\n" );
 252
 253IMPLEMENT_CALLBACK( PlayerData, onLeaveMissionArea, void, ( Player* obj ), ( obj ),
 254   "@brief Called when the player leaves the mission area.\n"
 255   "@param obj The Player object\n"
 256   "@see MissionArea\n" );
 257
 258PlayerData::PlayerData()
 259{
 260   shadowEnable = true;
 261   shadowSize = 256;
 262   shadowProjectionDistance = 14.0f;
 263
 264   renderFirstPerson = true;
 265   firstPersonShadows = false;
 266
 267   // Used for third person image rendering
 268   imageAnimPrefix = StringTable->EmptyString();
 269
 270   allowImageStateAnimation = false;
 271
 272   // Used for first person image rendering
 273   imageAnimPrefixFP = StringTable->EmptyString();
 274   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
 275   {
 276      shapeNameFP[i] = StringTable->EmptyString();
 277      mCRCFP[i] = 0;
 278      mValidShapeFP[i] = false;
 279   }
 280
 281   pickupRadius = 0.0f;
 282   minLookAngle = -1.4f;
 283   maxLookAngle = 1.4f;
 284   maxFreelookAngle = 3.0f;
 285   maxTimeScale = 1.5f;
 286
 287   mass = 9.0f;         // from ShapeBase
 288   maxEnergy = 60.0f;   // from ShapeBase
 289   drag = 0.3f;         // from ShapeBase
 290   density = 1.1f;      // from ShapeBase
 291
 292   maxStepHeight = 1.0f;
 293   runSurfaceAngle = 80.0f;
 294   runSurfaceCos = mCos(mDegToRad(runSurfaceAngle));
 295
 296   fallingSpeedThreshold = -10.0f;
 297
 298   recoverDelay = 30;
 299   recoverRunForceScale = 1.0f;
 300   landSequenceTime = 0.0f;
 301   transitionToLand = false;
 302
 303   // Running/Walking
 304   runForce = 40.0f * 9.0f;
 305   runEnergyDrain = 0.0f;
 306   minRunEnergy = 0.0f;
 307   maxForwardSpeed = 10.0f;
 308   maxBackwardSpeed = 10.0f;
 309   maxSideSpeed = 10.0f;
 310
 311   // Jumping
 312   jumpForce = 75.0f;
 313   jumpEnergyDrain = 0.0f;
 314   minJumpEnergy = 0.0f;
 315   jumpSurfaceAngle = 78.0f;
 316   jumpSurfaceCos = mCos(mDegToRad(jumpSurfaceAngle));
 317
 318   for (U32 i = 0; i < NumRecoilSequences; i++)
 319      recoilSequence[i] = -1;
 320
 321   jumpDelay = 30;
 322   minJumpSpeed = 500.0f;
 323   maxJumpSpeed = 2.0f * minJumpSpeed;
 324
 325   // Sprinting
 326   sprintForce = 50.0f * 9.0f;
 327   sprintEnergyDrain = 0.0f;
 328   minSprintEnergy = 0.0f;
 329   maxSprintForwardSpeed = 15.0f;
 330   maxSprintBackwardSpeed = 10.0f;
 331   maxSprintSideSpeed = 10.0f;
 332   sprintStrafeScale = 1.0f;
 333   sprintYawScale = 1.0f;
 334   sprintPitchScale = 1.0f;
 335   sprintCanJump = true;
 336
 337   // Swimming
 338   swimForce = 55.0f * 9.0f;  
 339   maxUnderwaterForwardSpeed = 6.0f;
 340   maxUnderwaterBackwardSpeed = 6.0f;
 341   maxUnderwaterSideSpeed = 6.0f;
 342
 343   // Crouching
 344   crouchForce = 45.0f * 9.0f;
 345   maxCrouchForwardSpeed = 4.0f;
 346   maxCrouchBackwardSpeed = 4.0f;
 347   maxCrouchSideSpeed = 4.0f;    
 348
 349   // Prone
 350   proneForce = 45.0f * 9.0f;            
 351   maxProneForwardSpeed = 2.0f;  
 352   maxProneBackwardSpeed = 2.0f; 
 353   maxProneSideSpeed = 0.0f;     
 354
 355   // Jetting
 356   jetJumpForce = 0;
 357   jetJumpEnergyDrain = 0;
 358   jetMinJumpEnergy = 0;
 359   jetJumpSurfaceAngle = 78;
 360   jetMinJumpSpeed = 20;
 361   jetMaxJumpSpeed = 100;
 362
 363   horizMaxSpeed = 80.0f;
 364   horizResistSpeed = 38.0f;
 365   horizResistFactor = 1.0f;
 366
 367   upMaxSpeed = 80.0f;
 368   upResistSpeed = 38.0f;
 369   upResistFactor = 1.0f;
 370
 371   minImpactSpeed = 25.0f;
 372   minLateralImpactSpeed = 25.0f;
 373
 374   decalData      = NULL;
 375   decalID        = 0;
 376   decalOffset      = 0.0f;
 377
 378   actionCount = 0;
 379   lookAction = 0;
 380   dMemset(spineNode, 0, sizeof(spineNode));
 381
 382   pickupDelta = 0.0f;
 383
 384   // size of bounding box
 385   boxSize.set(1.0f, 1.0f, 2.3f);
 386   crouchBoxSize.set(1.0f, 1.0f, 2.0f);
 387   proneBoxSize.set(1.0f, 2.3f, 1.0f);
 388   swimBoxSize.set(1.0f, 2.3f, 1.0f);
 389
 390   // location of head, torso, legs
 391   boxHeadPercentage = 0.85f;
 392   boxTorsoPercentage = 0.55f;
 393
 394   // damage locations
 395   boxHeadLeftPercentage  = 0;
 396   boxHeadRightPercentage = 1;
 397   boxHeadBackPercentage  = 0;
 398   boxHeadFrontPercentage = 1;
 399
 400   for (S32 i = 0; i < MaxSounds; i++)
 401      sound[i] = NULL;
 402
 403   footPuffEmitter = NULL;
 404   footPuffID = 0;
 405   footPuffNumParts = 15;
 406   footPuffRadius = .25f;
 407
 408   dustEmitter = NULL;
 409   dustID = 0;
 410
 411   splash = NULL;
 412   splashId = 0;
 413   splashVelocity = 1.0f;
 414   splashAngle = 45.0f;
 415   splashFreqMod = 300.0f;
 416   splashVelEpsilon = 0.25f;
 417   bubbleEmitTime = 0.4f;
 418
 419   medSplashSoundVel = 2.0f;
 420   hardSplashSoundVel = 3.0f;
 421   exitSplashSoundVel = 2.0f;
 422   footSplashHeight = 0.1f;
 423
 424   dMemset( splashEmitterList, 0, sizeof( splashEmitterList ) );
 425   dMemset( splashEmitterIDList, 0, sizeof( splashEmitterIDList ) );
 426
 427   groundImpactMinSpeed = 10.0f;
 428   groundImpactShakeFreq.set( 10.0f, 10.0f, 10.0f );
 429   groundImpactShakeAmp.set( 20.0f, 20.0f, 20.0f );
 430   groundImpactShakeDuration = 1.0f;
 431   groundImpactShakeFalloff = 10.0f;
 432
 433   // Air control
 434   airControl = 0.0f;
 435
 436   jumpTowardsNormal = true;
 437
 438   physicsPlayerType = StringTable->EmptyString();
 439
 440   dMemset( actionList, 0, sizeof(actionList) );
 441}
 442
 443bool PlayerData::preload(bool server, String &errorStr)
 444{
 445   if(!Parent::preload(server, errorStr))
 446      return false;
 447
 448   // Resolve objects transmitted from server
 449   if( !server )
 450   {
 451      for( U32 i = 0; i < MaxSounds; ++ i )
 452      {
 453         String sfxErrorStr;
 454         if( !sfxResolve( &sound[ i ], sfxErrorStr ) )
 455            Con::errorf( "PlayerData::preload: %s", sfxErrorStr.c_str() );
 456      }
 457   }
 458
 459   //
 460   runSurfaceCos = mCos(mDegToRad(runSurfaceAngle));
 461   jumpSurfaceCos = mCos(mDegToRad(jumpSurfaceAngle));
 462   if (minJumpEnergy < jumpEnergyDrain)
 463      minJumpEnergy = jumpEnergyDrain;   
 464
 465   // Jetting
 466   if (jetMinJumpEnergy < jetJumpEnergyDrain)
 467      jetMinJumpEnergy = jetJumpEnergyDrain;
 468
 469   // Validate some of the data
 470   if (fallingSpeedThreshold > 0.0f)
 471      Con::printf("PlayerData:: Falling speed threshold should be downwards (negative)");
 472
 473   if (recoverDelay > (1 << RecoverDelayBits) - 1) {
 474      recoverDelay = (1 << RecoverDelayBits) - 1;
 475      Con::printf("PlayerData:: Recover delay exceeds range (0-%d)",recoverDelay);
 476   }
 477   if (jumpDelay > (1 << JumpDelayBits) - 1) {
 478      jumpDelay = (1 << JumpDelayBits) - 1;
 479      Con::printf("PlayerData:: Jump delay exceeds range (0-%d)",jumpDelay);
 480   }
 481
 482   // If we don't have a shape don't crash out trying to
 483   // setup animations and sequences.
 484   if ( mShape )
 485   {
 486      // Go ahead a pre-load the player shape
 487      TSShapeInstance* si = new TSShapeInstance(mShape, false);
 488      TSThread* thread = si->addThread();
 489
 490      // Extract ground transform velocity from animations
 491      // Get the named ones first so they can be indexed directly.
 492      ActionAnimation *dp = &actionList[0];
 493      for (S32 i = 0; i < NumTableActionAnims; i++,dp++)
 494      {
 495         ActionAnimationDef *sp = &ActionAnimationList[i];
 496         dp->name          = sp->name;
 497         dp->dir.set(sp->dir.x,sp->dir.y,sp->dir.z);
 498         dp->sequence      = mShape->findSequence(sp->name);
 499
 500         // If this is a sprint action and is missing a sequence, attempt to use
 501         // the standard run ones.
 502         if(dp->sequence == -1 && i >= SprintRootAnim && i <= SprintRightAnim)
 503         {
 504            S32 offset = i-SprintRootAnim;
 505            ActionAnimationDef *standDef = &ActionAnimationList[RootAnim+offset];
 506            dp->sequence = mShape->findSequence(standDef->name);
 507         }
 508
 509         dp->velocityScale = true;
 510         dp->death         = false;
 511         if (dp->sequence != -1)
 512            getGroundInfo(si,thread,dp);
 513      }
 514      for (S32 b = 0; b < mShape->sequences.size(); b++)
 515      {
 516         if (!isTableSequence(b))
 517         {
 518            dp->sequence      = b;
 519            dp->name          = mShape->getName(mShape->sequences[b].nameIndex);
 520            dp->velocityScale = false;
 521            getGroundInfo(si,thread,dp++);
 522         }
 523      }
 524      actionCount = dp - actionList;
 525      AssertFatal(actionCount <= NumActionAnims, "Too many action animations!");
 526      delete si;
 527
 528      // Resolve lookAction index
 529      dp = &actionList[0];
 530      String lookName("look");
 531      for (S32 c = 0; c < actionCount; c++,dp++)
 532         if( dStricmp( dp->name, lookName ) == 0 )
 533            lookAction = c;
 534
 535      // Resolve spine
 536      spineNode[0] = mShape->findNode("Bip01 Pelvis");
 537      spineNode[1] = mShape->findNode("Bip01 Spine");
 538      spineNode[2] = mShape->findNode("Bip01 Spine1");
 539      spineNode[3] = mShape->findNode("Bip01 Spine2");
 540      spineNode[4] = mShape->findNode("Bip01 Neck");
 541      spineNode[5] = mShape->findNode("Bip01 Head");
 542
 543      // Recoil animations
 544      recoilSequence[0] = mShape->findSequence("light_recoil");
 545      recoilSequence[1] = mShape->findSequence("medium_recoil");
 546      recoilSequence[2] = mShape->findSequence("heavy_recoil");
 547   }
 548
 549   // Convert pickupRadius to a delta of boundingBox
 550   //
 551   // NOTE: it is not really correct to precalculate a pickupRadius based 
 552   // on boxSize since the actual player's bounds can vary by "pose".
 553   //
 554   F32 dr = (boxSize.x > boxSize.y)? boxSize.x: boxSize.y;
 555   if (pickupRadius < dr)
 556      pickupRadius = dr;
 557   else
 558      if (pickupRadius > 2.0f * dr)
 559         pickupRadius = 2.0f * dr;
 560   pickupDelta = (S32)(pickupRadius - dr);
 561
 562   // Validate jump speed
 563   if (maxJumpSpeed <= minJumpSpeed)
 564      maxJumpSpeed = minJumpSpeed + 0.1f;
 565
 566   // Load up all the emitters
 567   if (!footPuffEmitter && footPuffID != 0)
 568      if (!Sim::findObject(footPuffID, footPuffEmitter))
 569         Con::errorf(ConsoleLogEntry::General, "PlayerData::preload - Invalid packet, bad datablockId(footPuffEmitter): 0x%x", footPuffID);
 570
 571   if (!decalData && decalID != 0 )
 572      if (!Sim::findObject(decalID, decalData))
 573         Con::errorf(ConsoleLogEntry::General, "PlayerData::preload Invalid packet, bad datablockId(decalData): 0x%x", decalID);
 574
 575   if (!dustEmitter && dustID != 0 )
 576      if (!Sim::findObject(dustID, dustEmitter))
 577         Con::errorf(ConsoleLogEntry::General, "PlayerData::preload - Invalid packet, bad datablockId(dustEmitter): 0x%x", dustID);
 578
 579   for (S32 i=0; i<NUM_SPLASH_EMITTERS; i++)
 580      if( !splashEmitterList[i] && splashEmitterIDList[i] != 0 )
 581         if( Sim::findObject( splashEmitterIDList[i], splashEmitterList[i] ) == false)
 582            Con::errorf(ConsoleLogEntry::General, "PlayerData::onAdd - Invalid packet, bad datablockId(particle emitter): 0x%x", splashEmitterIDList[i]);
 583
 584   // First person mounted image shapes.
 585   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
 586   {
 587      bool shapeError = false;
 588
 589      if (shapeNameFP[i] && shapeNameFP[i][0])
 590      {
 591         mShapeFP[i] = ResourceManager::get().load(shapeNameFP[i]);
 592         if (bool(mShapeFP[i]) == false)
 593         {
 594            errorStr = String::ToString("PlayerData: Couldn't load mounted image %d shape \"%s\"",i,shapeNameFP[i]);
 595            return false;
 596         }
 597
 598         if(!server && !mShapeFP[i]->preloadMaterialList(mShapeFP[i].getPath()) && NetConnection::filesWereDownloaded())
 599            shapeError = true;
 600
 601         if(computeCRC)
 602         {
 603            Con::printf("Validation required for mounted image %d shape: %s", i, shapeNameFP[i]);
 604
 605            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShapeFP[i].getPath());
 606
 607            if (!fileRef)
 608            {
 609               errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.",i,mShapeFP[i].getPath().getFullPath().c_str());
 610               return false;
 611            }
 612
 613            if(server)
 614               mCRCFP[i] = fileRef->getChecksum();
 615            else if(mCRCFP[i] != fileRef->getChecksum())
 616            {
 617               errorStr = String::ToString("PlayerData: Mounted image %d shape \"%s\" does not match version on server.",i,shapeNameFP[i]);
 618               return false;
 619            }
 620         }
 621
 622         mValidShapeFP[i] = true;
 623      }
 624   }
 625
 626   return true;
 627}
 628
 629void PlayerData::getGroundInfo(TSShapeInstance* si, TSThread* thread,ActionAnimation *dp)
 630{
 631   dp->death = !dStrnicmp(dp->name, "death", 5);
 632   if (dp->death)
 633   {
 634      // Death animations use roll frame-to-frame changes in ground transform into position
 635      dp->speed = 0.0f;
 636      dp->dir.set(0.0f, 0.0f, 0.0f);
 637
 638      // Death animations MUST define ground transforms, so add dummy ones if required
 639      if (si->getShape()->sequences[dp->sequence].numGroundFrames == 0)
 640         si->getShape()->setSequenceGroundSpeed(dp->name, Point3F(0, 0, 0), Point3F(0, 0, 0));
 641   }
 642   else
 643   {
 644      VectorF save = dp->dir;
 645      si->setSequence(thread,dp->sequence,0);
 646      si->animate();
 647      si->advanceTime(1);
 648      si->animateGround();
 649      si->getGroundTransform().getColumn(3,&dp->dir);
 650      if ((dp->speed = dp->dir.len()) < 0.01f)
 651      {
 652         // No ground displacement... In this case we'll use the
 653         // default table entry, if there is one.
 654         if (save.len() > 0.01f)
 655         {
 656            dp->dir = save;
 657            dp->speed = 1.0f;
 658            dp->velocityScale = false;
 659         }
 660         else
 661            dp->speed = 0.0f;
 662      }
 663      else
 664         dp->dir *= 1.0f / dp->speed;
 665   }
 666}
 667
 668bool PlayerData::isTableSequence(S32 seq)
 669{
 670   // The sequences from the table must already have
 671   // been loaded for this to work.
 672   for (S32 i = 0; i < NumTableActionAnims; i++)
 673      if (actionList[i].sequence == seq)
 674         return true;
 675   return false;
 676}
 677
 678bool PlayerData::isJumpAction(U32 action)
 679{
 680   return (action == JumpAnim || action == StandJumpAnim);
 681}
 682
 683void PlayerData::initPersistFields()
 684{
 685   addField( "pickupRadius", TypeF32, Offset(pickupRadius, PlayerData),
 686      "@brief Radius around the player to collide with Items in the scene (on server).\n\n"
 687      "Internally the pickupRadius is added to the larger side of the initial bounding box "
 688      "to determine the actual distance, to a maximum of 2 times the bounding box size.  The "
 689      "initial bounding box is that used for the root pose, and therefore doesn't take into "
 690      "account the change in pose.\n");
 691   addField( "maxTimeScale", TypeF32, Offset(maxTimeScale, PlayerData),
 692      "@brief Maximum time scale for action animations.\n\n"
 693      "If an action animation has a defined ground frame, it is automatically scaled to match the "
 694      "player's ground velocity.  This field limits the maximum time scale used even if "
 695      "the player's velocity exceeds it." );
 696
 697   addGroup( "Camera" );
 698
 699      addField( "renderFirstPerson", TypeBool, Offset(renderFirstPerson, PlayerData),
 700         "@brief Flag controlling whether to render the player shape in first person view.\n\n" );
 701
 702      addField( "firstPersonShadows", TypeBool, Offset(firstPersonShadows, PlayerData),
 703         "@brief Forces shadows to be rendered in first person when renderFirstPerson is disabled.  Defaults to false.\n\n" );
 704
 705      addField( "minLookAngle", TypeF32, Offset(minLookAngle, PlayerData),
 706         "@brief Lowest angle (in radians) the player can look.\n\n"
 707         "@note An angle of zero is straight ahead, with positive up and negative down." );
 708      addField( "maxLookAngle", TypeF32, Offset(maxLookAngle, PlayerData),
 709         "@brief Highest angle (in radians) the player can look.\n\n"
 710         "@note An angle of zero is straight ahead, with positive up and negative down." );
 711      addField( "maxFreelookAngle", TypeF32, Offset(maxFreelookAngle, PlayerData),
 712         "@brief Defines the maximum left and right angles (in radians) the player can "
 713         "look in freelook mode.\n\n" );
 714
 715   endGroup( "Camera" );
 716
 717   addGroup( "Movement" );
 718
 719      addField( "maxStepHeight", TypeF32, Offset(maxStepHeight, PlayerData),
 720         "@brief Maximum height the player can step up.\n\n"
 721         "The player will automatically step onto changes in ground height less "
 722         "than maxStepHeight.  The player will collide with ground height changes "
 723         "greater than this." );
 724
 725      addField( "runForce", TypeF32, Offset(runForce, PlayerData),
 726         "@brief Force used to accelerate the player when running.\n\n" );
 727
 728      addField( "runEnergyDrain", TypeF32, Offset(runEnergyDrain, PlayerData),
 729         "@brief Energy value drained each tick that the player is moving.\n\n"
 730         "The player will not be able to move when his energy falls below "
 731         "minRunEnergy.\n"
 732         "@note Setting this to zero will disable any energy drain.\n"
 733         "@see minRunEnergy\n");
 734      addField( "minRunEnergy", TypeF32, Offset(minRunEnergy, PlayerData),
 735         "@brief Minimum energy level required to run or swim.\n\n"
 736         "@see runEnergyDrain\n");
 737
 738      addField( "maxForwardSpeed", TypeF32, Offset(maxForwardSpeed, PlayerData),
 739         "@brief Maximum forward speed when running." );
 740      addField( "maxBackwardSpeed", TypeF32, Offset(maxBackwardSpeed, PlayerData),
 741         "@brief Maximum backward speed when running." );
 742      addField( "maxSideSpeed", TypeF32, Offset(maxSideSpeed, PlayerData),
 743         "@brief Maximum sideways speed when running." );
 744
 745      addField( "runSurfaceAngle", TypeF32, Offset(runSurfaceAngle, PlayerData),
 746         "@brief Maximum angle from vertical (in degrees) the player can run up.\n\n" );
 747
 748      addField( "minImpactSpeed", TypeF32, Offset(minImpactSpeed, PlayerData),
 749         "@brief Minimum impact speed to apply falling damage.\n\n"
 750         "This field also sets the minimum speed for the onImpact callback "
 751         "to be invoked.\n"
 752         "@see ShapeBaseData::onImpact()\n");
 753      addField( "minLateralImpactSpeed", TypeF32, Offset(minLateralImpactSpeed, PlayerData),
 754         "@brief Minimum impact speed to apply non-falling damage.\n\n"
 755         "This field also sets the minimum speed for the onLateralImpact callback "
 756         "to be invoked.\n"
 757         "@see ShapeBaseData::onLateralImpact()\n");
 758
 759      addField( "horizMaxSpeed", TypeF32, Offset(horizMaxSpeed, PlayerData),
 760         "@brief Maximum horizontal speed.\n\n"
 761         "@note This limit is only enforced if the player's horizontal speed "
 762         "exceeds horizResistSpeed.\n"
 763         "@see horizResistSpeed\n"
 764         "@see horizResistFactor\n" );
 765      addField( "horizResistSpeed", TypeF32, Offset(horizResistSpeed, PlayerData),
 766         "@brief Horizontal speed at which resistence will take place.\n\n"
 767         "@see horizMaxSpeed\n"
 768         "@see horizResistFactor\n" );
 769      addField( "horizResistFactor", TypeF32, Offset(horizResistFactor, PlayerData),
 770         "@brief Factor of resistence once horizResistSpeed has been reached.\n\n"
 771         "@see horizMaxSpeed\n"
 772         "@see horizResistSpeed\n" );
 773
 774      addField( "upMaxSpeed", TypeF32, Offset(upMaxSpeed, PlayerData),
 775         "@brief Maximum upwards speed.\n\n"
 776         "@note This limit is only enforced if the player's upward speed exceeds "
 777         "upResistSpeed.\n"
 778         "@see upResistSpeed\n"
 779         "@see upResistFactor\n" );
 780      addField( "upResistSpeed", TypeF32, Offset(upResistSpeed, PlayerData),
 781         "@brief Upwards speed at which resistence will take place.\n\n"
 782         "@see upMaxSpeed\n"
 783         "@see upResistFactor\n" );
 784      addField( "upResistFactor", TypeF32, Offset(upResistFactor, PlayerData),
 785         "@brief Factor of resistence once upResistSpeed has been reached.\n\n"
 786         "@see upMaxSpeed\n"
 787         "@see upResistSpeed\n" );
 788
 789   endGroup( "Movement" );
 790   
 791   addGroup( "Movement: Jumping" );
 792
 793      addField( "jumpForce", TypeF32, Offset(jumpForce, PlayerData),
 794         "@brief Force used to accelerate the player when a jump is initiated.\n\n" );
 795
 796      addField( "jumpEnergyDrain", TypeF32, Offset(jumpEnergyDrain, PlayerData),
 797         "@brief Energy level drained each time the player jumps.\n\n"
 798         "@note Setting this to zero will disable any energy drain\n"
 799         "@see minJumpEnergy\n");
 800      addField( "minJumpEnergy", TypeF32, Offset(minJumpEnergy, PlayerData),
 801         "@brief Minimum energy level required to jump.\n\n"
 802         "@see jumpEnergyDrain\n");
 803
 804      addField( "minJumpSpeed", TypeF32, Offset(minJumpSpeed, PlayerData),
 805         "@brief Minimum speed needed to jump.\n\n"
 806         "If the player's own z velocity is greater than this, then it is used to scale "
 807         "the jump speed, up to maxJumpSpeed.\n"
 808         "@see maxJumpSpeed\n");
 809      addField( "maxJumpSpeed", TypeF32, Offset(maxJumpSpeed, PlayerData),
 810         "@brief Maximum vertical speed before the player can no longer jump.\n\n" );
 811      addField( "jumpSurfaceAngle", TypeF32, Offset(jumpSurfaceAngle, PlayerData),
 812         "@brief Angle from vertical (in degrees) where the player can jump.\n\n" );
 813      addField( "jumpDelay", TypeS32, Offset(jumpDelay, PlayerData),
 814         "@brief Delay time in number of ticks ticks between jumps.\n\n" );
 815      addField( "airControl", TypeF32, Offset(airControl, PlayerData),
 816         "@brief Amount of movement control the player has when in the air.\n\n"
 817         "This is applied as a multiplier to the player's x and y motion.\n");
 818      addField( "jumpTowardsNormal", TypeBool, Offset(jumpTowardsNormal, PlayerData),
 819         "@brief Controls the direction of the jump impulse.\n"
 820         "When false, jumps are always in the vertical (+Z) direction. When true "
 821         "jumps are in the direction of the ground normal so long as the player is not "
 822         "directly facing the surface.  If the player is directly facing the surface, then "
 823         "they will jump straight up.\n" );
 824   
 825   endGroup( "Movement: Jumping" );
 826   
 827   addGroup( "Movement: Sprinting" );
 828
 829      addField( "sprintForce", TypeF32, Offset(sprintForce, PlayerData),
 830         "@brief Force used to accelerate the player when sprinting.\n\n" );
 831
 832      addField( "sprintEnergyDrain", TypeF32, Offset(sprintEnergyDrain, PlayerData),
 833         "@brief Energy value drained each tick that the player is sprinting.\n\n"
 834         "The player will not be able to move when his energy falls below "
 835         "sprintEnergyDrain.\n"
 836         "@note Setting this to zero will disable any energy drain.\n"
 837         "@see minSprintEnergy\n");
 838      addField( "minSprintEnergy", TypeF32, Offset(minSprintEnergy, PlayerData),
 839         "@brief Minimum energy level required to sprint.\n\n"
 840         "@see sprintEnergyDrain\n");
 841
 842      addField( "maxSprintForwardSpeed", TypeF32, Offset(maxSprintForwardSpeed, PlayerData),
 843         "@brief Maximum forward speed when sprinting." );
 844      addField( "maxSprintBackwardSpeed", TypeF32, Offset(maxSprintBackwardSpeed, PlayerData),
 845         "@brief Maximum backward speed when sprinting." );
 846      addField( "maxSprintSideSpeed", TypeF32, Offset(maxSprintSideSpeed, PlayerData),
 847         "@brief Maximum sideways speed when sprinting." );
 848
 849      addField( "sprintStrafeScale", TypeF32, Offset(sprintStrafeScale, PlayerData),
 850         "@brief Amount to scale strafing motion vector while sprinting." );
 851      addField( "sprintYawScale", TypeF32, Offset(sprintYawScale, PlayerData),
 852         "@brief Amount to scale yaw motion while sprinting." );
 853      addField( "sprintPitchScale", TypeF32, Offset(sprintPitchScale, PlayerData),
 854         "@brief Amount to scale pitch motion while sprinting." );
 855
 856      addField( "sprintCanJump", TypeBool, Offset(sprintCanJump, PlayerData),
 857         "@brief Can the player jump while sprinting." );
 858
 859   endGroup( "Movement: Sprinting" );
 860
 861   addGroup( "Movement: Swimming" );
 862
 863      addField( "swimForce", TypeF32, Offset(swimForce, PlayerData),
 864         "@brief Force used to accelerate the player when swimming.\n\n" );
 865      addField( "maxUnderwaterForwardSpeed", TypeF32, Offset(maxUnderwaterForwardSpeed, PlayerData),
 866         "@brief Maximum forward speed when underwater.\n\n" );
 867      addField( "maxUnderwaterBackwardSpeed", TypeF32, Offset(maxUnderwaterBackwardSpeed, PlayerData),
 868         "@brief Maximum backward speed when underwater.\n\n" );
 869      addField( "maxUnderwaterSideSpeed", TypeF32, Offset(maxUnderwaterSideSpeed, PlayerData),
 870         "@brief Maximum sideways speed when underwater.\n\n" );
 871
 872   endGroup( "Movement: Swimming" );
 873
 874   addGroup( "Movement: Crouching" );
 875
 876      addField( "crouchForce", TypeF32, Offset(crouchForce, PlayerData),
 877         "@brief Force used to accelerate the player when crouching.\n\n" );
 878      addField( "maxCrouchForwardSpeed", TypeF32, Offset(maxCrouchForwardSpeed, PlayerData),
 879         "@brief Maximum forward speed when crouching.\n\n" );
 880      addField( "maxCrouchBackwardSpeed", TypeF32, Offset(maxCrouchBackwardSpeed, PlayerData),
 881         "@brief Maximum backward speed when crouching.\n\n" );
 882      addField( "maxCrouchSideSpeed", TypeF32, Offset(maxCrouchSideSpeed, PlayerData),
 883         "@brief Maximum sideways speed when crouching.\n\n" );
 884
 885   endGroup( "Movement: Crouching" );
 886
 887   addGroup( "Movement: Prone" );
 888
 889      addField( "proneForce", TypeF32, Offset(proneForce, PlayerData),
 890         "@brief Force used to accelerate the player when prone (laying down).\n\n" );
 891      addField( "maxProneForwardSpeed", TypeF32, Offset(maxProneForwardSpeed, PlayerData),
 892         "@brief Maximum forward speed when prone (laying down).\n\n" );
 893      addField( "maxProneBackwardSpeed", TypeF32, Offset(maxProneBackwardSpeed, PlayerData),
 894         "@brief Maximum backward speed when prone (laying down).\n\n" );
 895      addField( "maxProneSideSpeed", TypeF32, Offset(maxProneSideSpeed, PlayerData),
 896         "@brief Maximum sideways speed when prone (laying down).\n\n" );
 897
 898   endGroup( "Movement: Prone" );
 899
 900   addGroup( "Movement: Jetting" );
 901
 902      addField( "jetJumpForce", TypeF32, Offset(jetJumpForce, PlayerData),
 903         "@brief Force used to accelerate the player when a jet jump is initiated.\n\n" );
 904
 905      addField( "jetJumpEnergyDrain", TypeF32, Offset(jetJumpEnergyDrain, PlayerData),
 906         "@brief Energy level drained each time the player jet jumps.\n\n"
 907         "@note Setting this to zero will disable any energy drain\n"
 908         "@see jetMinJumpEnergy\n");
 909      addField( "jetMinJumpEnergy", TypeF32, Offset(jetMinJumpEnergy, PlayerData),
 910         "@brief Minimum energy level required to jet jump.\n\n"
 911         "@see jetJumpEnergyDrain\n");
 912
 913      addField( "jetMinJumpSpeed", TypeF32, Offset(jetMinJumpSpeed, PlayerData),
 914         "@brief Minimum speed needed to jet jump.\n\n"
 915         "If the player's own z velocity is greater than this, then it is used to scale "
 916         "the jet jump speed, up to jetMaxJumpSpeed.\n"
 917         "@see jetMaxJumpSpeed\n");
 918      addField( "jetMaxJumpSpeed", TypeF32, Offset(jetMaxJumpSpeed, PlayerData),
 919         "@brief Maximum vertical speed before the player can no longer jet jump.\n\n" );
 920      addField( "jetJumpSurfaceAngle", TypeF32, Offset(jetJumpSurfaceAngle, PlayerData),
 921         "@brief Angle from vertical (in degrees) where the player can jet jump.\n\n" );
 922
 923   endGroup( "Movement: Jetting" );
 924
 925   addGroup( "Falling" );
 926
 927      addField( "fallingSpeedThreshold", TypeF32, Offset(fallingSpeedThreshold, PlayerData),
 928         "@brief Downward speed at which we consider the player falling.\n\n" );
 929
 930      addField( "recoverDelay", TypeS32, Offset(recoverDelay, PlayerData),
 931         "@brief Number of ticks for the player to recover from falling.\n\n" );
 932
 933      addField( "recoverRunForceScale", TypeF32, Offset(recoverRunForceScale, PlayerData),
 934         "@brief Scale factor applied to runForce while in the recover state.\n\n"
 935         "This can be used to temporarily slow the player's movement after a fall, or "
 936         "prevent the player from moving at all if set to zero.\n" );
 937
 938      addField( "landSequenceTime", TypeF32, Offset(landSequenceTime, PlayerData),
 939         "@brief Time of land sequence play back when using new recover system.\n\n"
 940         "If greater than 0 then the legacy fall recovery system will be bypassed "
 941         "in favour of just playing the player's land sequence.  The time to "
 942         "recover from a fall then becomes this parameter's time and the land "
 943         "sequence's playback will be scaled to match.\n"
 944         "@see transitionToLand\n" );
 945
 946      addField( "transitionToLand", TypeBool, Offset(transitionToLand, PlayerData),
 947         "@brief When going from a fall to a land, should we transition between the two.\n\n"
 948         "@note Only takes affect when landSequenceTime is greater than 0.\n"
 949         "@see landSequenceTime\n" );
 950
 951   endGroup( "Falling" );
 952
 953   addGroup( "Collision" );
 954
 955      addField( "boundingBox", TypePoint3F, Offset(boxSize, PlayerData),
 956         "@brief Size of the bounding box used by the player for collision.\n\n"
 957         "Dimensions are given as \"width depth height\"." );
 958      addField( "crouchBoundingBox", TypePoint3F, Offset(crouchBoxSize, PlayerData),
 959         "@brief Collision bounding box used when the player is crouching.\n\n"
 960         "@see boundingBox" );
 961      addField( "proneBoundingBox", TypePoint3F, Offset(proneBoxSize, PlayerData),
 962         "@brief Collision bounding box used when the player is prone (laying down).\n\n"
 963         "@see boundingBox" );
 964      addField( "swimBoundingBox", TypePoint3F, Offset(swimBoxSize, PlayerData),
 965         "@brief Collision bounding box used when the player is swimming.\n\n"
 966         "@see boundingBox" );
 967
 968      addField( "boxHeadPercentage", TypeF32, Offset(boxHeadPercentage, PlayerData),
 969         "@brief Percentage of the player's bounding box height that represents the head.\n\n"
 970         "Used when computing the damage location.\n"
 971         "@see Player::getDamageLocation" );
 972      addField( "boxTorsoPercentage", TypeF32, Offset(boxTorsoPercentage, PlayerData),
 973         "@brief Percentage of the player's bounding box height that represents the torso.\n\n"
 974         "Used when computing the damage location.\n"
 975         "@see Player::getDamageLocation" );
 976      addField( "boxHeadLeftPercentage", TypeF32, Offset(boxHeadLeftPercentage, PlayerData),
 977         "@brief Percentage of the player's bounding box width that represents the left side of the head.\n\n"
 978         "Used when computing the damage location.\n"
 979         "@see Player::getDamageLocation" );
 980      addField( "boxHeadRightPercentage", TypeF32, Offset(boxHeadRightPercentage, PlayerData),
 981         "@brief Percentage of the player's bounding box width that represents the right side of the head.\n\n"
 982         "Used when computing the damage location.\n"
 983         "@see Player::getDamageLocation" );
 984      addField( "boxHeadBackPercentage", TypeF32, Offset(boxHeadBackPercentage, PlayerData),
 985         "@brief Percentage of the player's bounding box depth that represents the back side of the head.\n\n"
 986         "Used when computing the damage location.\n"
 987         "@see Player::getDamageLocation" );
 988      addField( "boxHeadFrontPercentage", TypeF32, Offset(boxHeadFrontPercentage, PlayerData),
 989         "@brief Percentage of the player's bounding box depth that represents the front side of the head.\n\n"
 990         "Used when computing the damage location.\n"
 991         "@see Player::getDamageLocation" );
 992
 993   endGroup( "Collision" );
 994   
 995   addGroup( "Interaction: Footsteps" );
 996
 997      addField( "footPuffEmitter", TYPEID< ParticleEmitterData >(), Offset(footPuffEmitter, PlayerData),
 998         "@brief Particle emitter used to generate footpuffs (particles created as the player "
 999         "walks along the ground).\n\n"
1000         "@note The generation of foot puffs requires the appropriate triggeres to be defined in the "
1001         "player's animation sequences.  Without these, no foot puffs will be generated.\n");
1002      addField( "footPuffNumParts", TypeS32, Offset(footPuffNumParts, PlayerData),
1003         "@brief Number of footpuff particles to generate each step.\n\n"
1004         "Each foot puff is randomly placed within the defined foot puff radius.  This "
1005         "includes having footPuffNumParts set to one.\n"
1006         "@see footPuffRadius\n");
1007      addField( "footPuffRadius", TypeF32, Offset(footPuffRadius, PlayerData),
1008         "@brief Particle creation radius for footpuff particles.\n\n"
1009         "This is applied to each foot puff particle, even if footPuffNumParts is set to one.  So "
1010         "set this value to zero if you want a single foot puff placed at exactly the same location "
1011         "under the player each time.\n");
1012      addField( "dustEmitter", TYPEID< ParticleEmitterData >(), Offset(dustEmitter, PlayerData),
1013         "@brief Emitter used to generate dust particles.\n\n"
1014         "@note Currently unused." );
1015
1016      addField( "decalData", TYPEID< DecalData >(), Offset(decalData, PlayerData),
1017         "@brief Decal to place on the ground for player footsteps.\n\n" );
1018      addField( "decalOffset",TypeF32, Offset(decalOffset, PlayerData),
1019         "@brief Distance from the center of the model to the right foot.\n\n"
1020         "While this defines the distance to the right foot, it is also used to place "
1021         "the left foot decal as well.  Just on the opposite side of the player." );
1022
1023   endGroup( "Interaction: Footsteps" );
1024
1025   addGroup( "Interaction: Sounds" );
1026
1027      addField( "FootSoftSound", TypeSFXTrackName, Offset(sound[FootSoft], PlayerData),
1028         "@brief Sound to play when walking on a surface with Material footstepSoundId 0.\n\n" );
1029      addField( "FootHardSound", TypeSFXTrackName, Offset(sound[FootHard], PlayerData),
1030         "@brief Sound to play when walking on a surface with Material footstepSoundId 1.\n\n" );
1031      addField( "FootMetalSound", TypeSFXTrackName, Offset(sound[FootMetal], PlayerData),
1032         "@brief Sound to play when walking on a surface with Material footstepSoundId 2.\n\n" );
1033      addField( "FootSnowSound", TypeSFXTrackName, Offset(sound[FootSnow], PlayerData),
1034         "@brief Sound to play when walking on a surface with Material footstepSoundId 3.\n\n" );
1035
1036      addField( "FootShallowSound", TypeSFXTrackName, Offset(sound[FootShallowSplash], PlayerData),
1037         "@brief Sound to play when walking in water and coverage is less than "
1038         "footSplashHeight.\n\n"
1039         "@see footSplashHeight\n" );
1040      addField( "FootWadingSound", TypeSFXTrackName, Offset(sound[FootWading], PlayerData),
1041         "@brief Sound to play when walking in water and coverage is less than 1, "
1042         "but > footSplashHeight.\n\n"
1043         "@see footSplashHeight\n" );
1044      addField( "FootUnderwaterSound", TypeSFXTrackName, Offset(sound[FootUnderWater], PlayerData),
1045         "@brief Sound to play when walking in water and coverage equals 1.0 "
1046         "(fully underwater).\n\n" );
1047      addField( "FootBubblesSound", TypeSFXTrackName, Offset(sound[FootBubbles], PlayerData),
1048         "@brief Sound to play when walking in water and coverage equals 1.0 "
1049         "(fully underwater).\n\n" );
1050      addField( "movingBubblesSound", TypeSFXTrackName, Offset(sound[MoveBubbles], PlayerData),
1051         "@brief Sound to play when in water and coverage equals 1.0 (fully underwater).\n\n"
1052         "Note that unlike FootUnderwaterSound, this sound plays even if the "
1053         "player is not moving around in the water.\n" );
1054      addField( "waterBreathSound", TypeSFXTrackName, Offset(sound[WaterBreath], PlayerData),
1055         "@brief Sound to play when in water and coverage equals 1.0 (fully underwater).\n\n"
1056         "Note that unlike FootUnderwaterSound, this sound plays even if the "
1057         "player is not moving around in the water.\n" );
1058
1059      addField( "impactSoftSound", TypeSFXTrackName, Offset(sound[ImpactSoft], PlayerData),
1060         "@brief Sound to play after falling on a surface with Material footstepSoundId 0.\n\n" );
1061      addField( "impactHardSound", TypeSFXTrackName, Offset(sound[ImpactHard], PlayerData),
1062         "@brief Sound to play after falling on a surface with Material footstepSoundId 1.\n\n" );
1063      addField( "impactMetalSound", TypeSFXTrackName, Offset(sound[ImpactMetal], PlayerData),
1064         "@brief Sound to play after falling on a surface with Material footstepSoundId 2.\n\n" );
1065      addField( "impactSnowSound", TypeSFXTrackName, Offset(sound[ImpactSnow], PlayerData),
1066         "@brief Sound to play after falling on a surface with Material footstepSoundId 3.\n\n" );
1067
1068      addField( "impactWaterEasy", TypeSFXTrackName, Offset(sound[ImpactWaterEasy], PlayerData),
1069         "@brief Sound to play when entering the water with velocity < "
1070         "mediumSplashSoundVelocity.\n\n"
1071         "@see mediumSplashSoundVelocity\n");
1072      addField( "impactWaterMedium", TypeSFXTrackName, Offset(sound[ImpactWaterMedium], PlayerData),
1073         "@brief Sound to play when entering the water with velocity >= "
1074         "mediumSplashSoundVelocity and < hardSplashSoundVelocity.\n\n"
1075         "@see mediumSplashSoundVelocity\n"
1076         "@see hardSplashSoundVelocity\n");
1077      addField( "impactWaterHard", TypeSFXTrackName, Offset(sound[ImpactWaterHard], PlayerData),
1078         "@brief Sound to play when entering the water with velocity >= "
1079         "hardSplashSoundVelocity.\n\n"
1080         "@see hardSplashSoundVelocity\n");
1081      addField( "exitingWater", TypeSFXTrackName, Offset(sound[ExitWater], PlayerData),
1082         "@brief Sound to play when exiting the water with velocity >= exitSplashSoundVelocity.\n\n"
1083         "@see exitSplashSoundVelocity\n");
1084
1085   endGroup( "Interaction: Sounds" );
1086
1087   addGroup( "Interaction: Splashes" );
1088
1089      addField( "splash", TYPEID< SplashData >(), Offset(splash, PlayerData),
1090         "@brief SplashData datablock used to create splashes when the player moves "
1091         "through water.\n\n" );
1092      addField( "splashVelocity", TypeF32, Offset(splashVelocity, PlayerData),
1093         "@brief Minimum velocity when moving through water to generate splashes.\n\n" );
1094      addField( "splashAngle", TypeF32, Offset(splashAngle, PlayerData),
1095         "@brief Maximum angle (in degrees) from pure vertical movement in water to "
1096         "generate splashes.\n\n" );
1097
1098      addField( "splashFreqMod", TypeF32, Offset(splashFreqMod, PlayerData),
1099         "@brief Multipled by speed to determine the number of splash particles to generate.\n\n" );
1100      addField( "splashVelEpsilon", TypeF32, Offset(splashVelEpsilon, PlayerData),
1101         "@brief Minimum speed to generate splash particles.\n\n" );
1102      addField( "bubbleEmitTime", TypeF32, Offset(bubbleEmitTime, PlayerData),
1103         "@brief Time in seconds to generate bubble particles after entering the water.\n\n" );
1104      addField( "splashEmitter", TYPEID< ParticleEmitterData >(), Offset(splashEmitterList, PlayerData), NUM_SPLASH_EMITTERS,
1105         "@brief Particle emitters used to generate splash particles.\n\n" );
1106
1107      addField( "footstepSplashHeight", TypeF32, Offset(footSplashHeight, PlayerData),
1108         "@brief Water coverage level to choose between FootShallowSound and FootWadingSound.\n\n"
1109         "@see FootShallowSound\n"
1110         "@see FootWadingSound\n");
1111
1112      addField( "mediumSplashSoundVelocity", TypeF32, Offset(medSplashSoundVel, PlayerData),
1113         "@brief Minimum velocity when entering the water for choosing between the impactWaterEasy and "
1114         "impactWaterMedium sounds to play.\n\n"
1115         "@see impactWaterEasy\n"
1116         "@see impactWaterMedium\n" );
1117      addField( "hardSplashSoundVelocity", TypeF32, Offset(hardSplashSoundVel, PlayerData),
1118         "@brief Minimum velocity when entering the water for choosing between the impactWaterMedium and "
1119         "impactWaterHard sound to play.\n\n"
1120         "@see impactWaterMedium\n"
1121         "@see impactWaterHard\n" );
1122      addField( "exitSplashSoundVelocity", TypeF32, Offset(exitSplashSoundVel, PlayerData),
1123         "@brief Minimum velocity when leaving the water for the exitingWater sound to "
1124         "play.\n\n"
1125         "@see exitingWater");
1126
1127   endGroup( "Interaction: Splashes" );
1128
1129   addGroup( "Interaction: Ground Impact" );
1130
1131      addField( "groundImpactMinSpeed", TypeF32, Offset(groundImpactMinSpeed, PlayerData),
1132         "@brief Minimum falling impact speed to apply damage and initiate the camera "
1133         "shaking effect.\n\n" );
1134      addField( "groundImpactShakeFreq", TypePoint3F, Offset(groundImpactShakeFreq, PlayerData),
1135         "@brief Frequency of the camera shake effect after falling.\n\n"
1136         "This is how fast to shake the camera.\n");
1137      addField( "groundImpactShakeAmp", TypePoint3F, Offset(groundImpactShakeAmp, PlayerData),
1138         "@brief Amplitude of the camera shake effect after falling.\n\n"
1139         "This is how much to shake the camera.\n");
1140      addField( "groundImpactShakeDuration", TypeF32, Offset(groundImpactShakeDuration, PlayerData),
1141         "@brief Duration (in seconds) of the camera shake effect after falling.\n\n"
1142         "This is how long to shake the camera.\n");
1143      addField( "groundImpactShakeFalloff", TypeF32, Offset(groundImpactShakeFalloff, PlayerData),
1144         "@brief Falloff factor of the camera shake effect after falling.\n\n"
1145         "This is how to fade the camera shake over the duration.\n");
1146
1147   endGroup( "Interaction: Ground Impact" );
1148
1149   addGroup( "Physics" );
1150
1151      // PhysicsPlayer
1152      addField( "physicsPlayerType", TypeString, Offset(physicsPlayerType, PlayerData),
1153         "@brief Specifies the type of physics used by the player.\n\n"
1154         "This depends on the physics module used.  An example is 'Capsule'.\n"
1155         "@note Not current used.\n");
1156
1157   endGroup( "Physics" );
1158
1159   addGroup( "First Person Arms" );
1160
1161      addField( "imageAnimPrefixFP", TypeCaseString, Offset(imageAnimPrefixFP, PlayerData),
1162         "@brief Optional prefix to all mounted image animation sequences in first person.\n\n"
1163         "This defines a prefix that will be added when looking up mounted image "
1164         "animation sequences while in first person.  It allows for the customization "
1165         "of a first person image based on the type of player.\n");
1166
1167      // Mounted images arrays
1168      addArray( "Mounted Images", ShapeBase::MaxMountedImages );
1169
1170         addField( "shapeNameFP", TypeShapeFilename, Offset(shapeNameFP, PlayerData), ShapeBase::MaxMountedImages,
1171            "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n"
1172            "These optional parameters correspond to each mounted image slot to indicate a shape that is rendered "
1173            "in addition to the mounted image shape.  Typically these are a player's arms (or arm) that is "
1174            "animated along with the mounted image's state animation sequences.\n");
1175
1176      endArray( "Mounted Images" );
1177
1178   endGroup( "First Person Arms" );
1179
1180   addGroup( "Third Person" );
1181
1182      addField( "imageAnimPrefix", TypeCaseString, Offset(imageAnimPrefix, PlayerData),
1183         "@brief Optional prefix to all mounted image animation sequences in third person.\n\n"
1184         "This defines a prefix that will be added when looking up mounted image "
1185         "animation sequences while in third person.  It allows for the customization "
1186         "of a third person image based on the type of player.\n");
1187
1188      addField( "allowImageStateAnimation", TypeBool, Offset(allowImageStateAnimation, PlayerData),
1189         "@brief Allow mounted images to request a sequence be played on the Player.\n\n"
1190         "When true a new thread is added to the player to allow for "
1191         "mounted images to request a sequence be played on the player "
1192         "through the image's state machine.  It is only optional so "
1193         "that we don't create a TSThread on the player if we don't "
1194         "need to.\n");
1195
1196   endGroup( "Third Person" );
1197
1198   Parent::initPersistFields();
1199}
1200
1201void PlayerData::packData(BitStream* stream)
1202{
1203   Parent::packData(stream);
1204
1205   stream->writeFlag(renderFirstPerson);
1206   stream->writeFlag(firstPersonShadows);
1207   
1208   stream->write(minLookAngle);
1209   stream->write(maxLookAngle);
1210   stream->write(maxFreelookAngle);
1211   stream->write(maxTimeScale);
1212
1213   stream->write(mass);
1214   stream->write(maxEnergy);
1215   stream->write(drag);
1216   stream->write(density);
1217
1218   stream->write(maxStepHeight);
1219
1220   stream->write(runForce);
1221   stream->write(runEnergyDrain);
1222   stream->write(minRunEnergy);
1223   stream->write(maxForwardSpeed);
1224   stream->write(maxBackwardSpeed);
1225   stream->write(maxSideSpeed);
1226   stream->write(runSurfaceAngle);
1227
1228   stream->write(fallingSpeedThreshold);
1229
1230   stream->write(recoverDelay);
1231   stream->write(recoverRunForceScale);
1232   stream->write(landSequenceTime);
1233   stream->write(transitionToLand);
1234
1235   // Jumping
1236   stream->write(jumpForce);
1237   stream->write(jumpEnergyDrain);
1238   stream->write(minJumpEnergy);
1239   stream->write(minJumpSpeed);
1240   stream->write(maxJumpSpeed);
1241   stream->write(jumpSurfaceAngle);
1242   stream->writeInt(jumpDelay,JumpDelayBits);
1243
1244   // Sprinting
1245   stream->write(sprintForce);
1246   stream->write(sprintEnergyDrain);
1247   stream->write(minSprintEnergy);
1248   stream->write(maxSprintForwardSpeed);
1249   stream->write(maxSprintBackwardSpeed);
1250   stream->write(maxSprintSideSpeed);
1251   stream->write(sprintStrafeScale);
1252   stream->write(sprintYawScale);
1253   stream->write(sprintPitchScale);
1254   stream->writeFlag(sprintCanJump);
1255
1256   // Swimming
1257   stream->write(swimForce);   
1258   stream->write(maxUnderwaterForwardSpeed);
1259   stream->write(maxUnderwaterBackwardSpeed);
1260   stream->write(maxUnderwaterSideSpeed);
1261
1262   // Crouching
1263   stream->write(crouchForce);   
1264   stream->write(maxCrouchForwardSpeed);
1265   stream->write(maxCrouchBackwardSpeed);
1266   stream->write(maxCrouchSideSpeed);
1267
1268   // Prone
1269   stream->write(proneForce);   
1270   stream->write(maxProneForwardSpeed);
1271   stream->write(maxProneBackwardSpeed);
1272   stream->write(maxProneSideSpeed);
1273
1274   // Jetting
1275   stream->write(jetJumpForce);
1276   stream->write(jetJumpEnergyDrain);
1277   stream->write(jetMinJumpEnergy);
1278   stream->write(jetMinJumpSpeed);
1279   stream->write(jetMaxJumpSpeed);
1280   stream->write(jetJumpSurfaceAngle);
1281
1282   stream->write(horizMaxSpeed);
1283   stream->write(horizResistSpeed);
1284   stream->write(horizResistFactor);
1285
1286   stream->write(upMaxSpeed);
1287   stream->write(upResistSpeed);
1288   stream->write(upResistFactor);
1289
1290   stream->write(splashVelocity);
1291   stream->write(splashAngle);
1292   stream->write(splashFreqMod);
1293   stream->write(splashVelEpsilon);
1294   stream->write(bubbleEmitTime);
1295
1296   stream->write(medSplashSoundVel);
1297   stream->write(hardSplashSoundVel);
1298   stream->write(exitSplashSoundVel);
1299   stream->write(footSplashHeight);
1300   // Don't need damage scale on the client
1301   stream->write(minImpactSpeed);
1302   stream->write(minLateralImpactSpeed);
1303
1304   for( U32 i = 0; i < MaxSounds; i++)
1305      sfxWrite( stream, sound[ i ] );
1306
1307   mathWrite(*stream, boxSize);
1308   mathWrite(*stream, crouchBoxSize);
1309   mathWrite(*stream, proneBoxSize);
1310   mathWrite(*stream, swimBoxSize);
1311
1312   if( stream->writeFlag( footPuffEmitter ) )
1313   {
1314      stream->writeRangedU32( footPuffEmitter->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
1315   }
1316
1317   stream->write( footPuffNumParts );
1318   stream->write( footPuffRadius );
1319
1320   if( stream->writeFlag( decalData ) )
1321   {
1322      stream->writeRangedU32( decalData->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
1323   }
1324   stream->write(decalOffset);
1325
1326   if( stream->writeFlag( dustEmitter ) )
1327   {
1328      stream->writeRangedU32( dustEmitter->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
1329   }
1330
1331
1332   if (stream->writeFlag( splash ))
1333   {
1334      stream->writeRangedU32(splash->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
1335   }
1336
1337   for( U32 i=0; i<NUM_SPLASH_EMITTERS; i++ )
1338   {
1339      if( stream->writeFlag( splashEmitterList[i] != NULL ) )
1340      {
1341         stream->writeRangedU32( splashEmitterList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
1342      }
1343   }
1344
1345
1346   stream->write(groundImpactMinSpeed);
1347   stream->write(groundImpactShakeFreq.x);
1348   stream->write(groundImpactShakeFreq.y);
1349   stream->write(groundImpactShakeFreq.z);
1350   stream->write(groundImpactShakeAmp.x);
1351   stream->write(groundImpactShakeAmp.y);
1352   stream->write(groundImpactShakeAmp.z);
1353   stream->write(groundImpactShakeDuration);
1354   stream->write(groundImpactShakeFalloff);
1355
1356   // Air control
1357   stream->write(airControl);
1358
1359   // Jump off at normal
1360   stream->writeFlag(jumpTowardsNormal);
1361
1362   stream->writeString(physicsPlayerType);
1363
1364   // Third person mounted image shapes
1365   stream->writeString(imageAnimPrefix);
1366
1367   stream->writeFlag(allowImageStateAnimation);
1368
1369   // First person mounted image shapes
1370   stream->writeString(imageAnimPrefixFP);
1371   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
1372   {
1373      stream->writeString(shapeNameFP[i]);
1374
1375      // computeCRC is handled in ShapeBaseData
1376      if (computeCRC)
1377      {
1378         stream->write(mCRCFP[i]);
1379      }
1380   }
1381}
1382
1383void PlayerData::unpackData(BitStream* stream)
1384{
1385   Parent::unpackData(stream);
1386
1387   renderFirstPerson = stream->readFlag();
1388   firstPersonShadows = stream->readFlag();
1389
1390   stream->read(&minLookAngle);
1391   stream->read(&maxLookAngle);
1392   stream->read(&maxFreelookAngle);
1393   stream->read(&maxTimeScale);
1394
1395   stream->read(&mass);
1396   stream->read(&maxEnergy);
1397   stream->read(&drag);
1398   stream->read(&density);
1399
1400   stream->read(&maxStepHeight);
1401
1402   stream->read(&runForce);
1403   stream->read(&runEnergyDrain);
1404   stream->read(&minRunEnergy);
1405   stream->read(&maxForwardSpeed);
1406   stream->read(&maxBackwardSpeed);
1407   stream->read(&maxSideSpeed);
1408   stream->read(&runSurfaceAngle);
1409
1410   stream->read(&fallingSpeedThreshold);
1411   stream->read(&recoverDelay);
1412   stream->read(&recoverRunForceScale);
1413   stream->read(&landSequenceTime);
1414   stream->read(&transitionToLand);
1415
1416   // Jumping
1417   stream->read(&jumpForce);
1418   stream->read(&jumpEnergyDrain);
1419   stream->read(&minJumpEnergy);
1420   stream->read(&minJumpSpeed);
1421   stream->read(&maxJumpSpeed);
1422   stream->read(&jumpSurfaceAngle);
1423   jumpDelay = stream->readInt(JumpDelayBits);
1424
1425   // Sprinting
1426   stream->read(&sprintForce);
1427   stream->read(&sprintEnergyDrain);
1428   stream->read(&minSprintEnergy);
1429   stream->read(&maxSprintForwardSpeed);
1430   stream->read(&maxSprintBackwardSpeed);
1431   stream->read(&maxSprintSideSpeed);
1432   stream->read(&sprintStrafeScale);
1433   stream->read(&sprintYawScale);
1434   stream->read(&sprintPitchScale);
1435   sprintCanJump = stream->readFlag();
1436
1437   // Swimming
1438   stream->read(&swimForce);
1439   stream->read(&maxUnderwaterForwardSpeed);
1440   stream->read(&maxUnderwaterBackwardSpeed);
1441   stream->read(&maxUnderwaterSideSpeed);   
1442
1443   // Crouching
1444   stream->read(&crouchForce);
1445   stream->read(&maxCrouchForwardSpeed);
1446   stream->read(&maxCrouchBackwardSpeed);
1447   stream->read(&maxCrouchSideSpeed);
1448
1449   // Prone
1450   stream->read(&proneForce);
1451   stream->read(&maxProneForwardSpeed);
1452   stream->read(&maxProneBackwardSpeed);
1453   stream->read(&maxProneSideSpeed);
1454
1455   // Jetting
1456   stream->read(&jetJumpForce);
1457   stream->read(&jetJumpEnergyDrain);
1458   stream->read(&jetMinJumpEnergy);
1459   stream->read(&jetMinJumpSpeed);
1460   stream->read(&jetMaxJumpSpeed);
1461   stream->read(&jetJumpSurfaceAngle);
1462
1463   stream->read(&horizMaxSpeed);
1464   stream->read(&horizResistSpeed);
1465   stream->read(&horizResistFactor);
1466
1467   stream->read(&upMaxSpeed);
1468   stream->read(&upResistSpeed);
1469   stream->read(&upResistFactor);
1470
1471   stream->read(&splashVelocity);
1472   stream->read(&splashAngle);
1473   stream->read(&splashFreqMod);
1474   stream->read(&splashVelEpsilon);
1475   stream->read(&bubbleEmitTime);
1476
1477   stream->read(&medSplashSoundVel);
1478   stream->read(&hardSplashSoundVel);
1479   stream->read(&exitSplashSoundVel);
1480   stream->read(&footSplashHeight);
1481
1482   stream->read(&minImpactSpeed);
1483   stream->read(&minLateralImpactSpeed);
1484
1485   for( U32 i = 0; i < MaxSounds; i++)
1486      sfxRead( stream, &sound[ i ] );
1487
1488   mathRead(*stream, &boxSize);
1489   mathRead(*stream, &crouchBoxSize);
1490   mathRead(*stream, &proneBoxSize);
1491   mathRead(*stream, &swimBoxSize);
1492
1493   if( stream->readFlag() )
1494   {
1495      footPuffID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
1496   }
1497
1498   stream->read(&footPuffNumParts);
1499   stream->read(&footPuffRadius);
1500
1501   if( stream->readFlag() )
1502   {
1503      decalID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
1504   }
1505   stream->read(&decalOffset);
1506
1507   if( stream->readFlag() )
1508   {
1509      dustID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
1510   }
1511
1512
1513   if (stream->readFlag())
1514   {
1515      splashId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
1516   }
1517
1518   for( U32 i=0; i<NUM_SPLASH_EMITTERS; i++ )
1519   {
1520      if( stream->readFlag() )
1521      {
1522         splashEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
1523      }
1524   }
1525
1526   stream->read(&groundImpactMinSpeed);
1527   stream->read(&groundImpactShakeFreq.x);
1528   stream->read(&groundImpactShakeFreq.y);
1529   stream->read(&groundImpactShakeFreq.z);
1530   stream->read(&groundImpactShakeAmp.x);
1531   stream->read(&groundImpactShakeAmp.y);
1532   stream->read(&groundImpactShakeAmp.z);
1533   stream->read(&groundImpactShakeDuration);
1534   stream->read(&groundImpactShakeFalloff);
1535
1536   // Air control
1537   stream->read(&airControl);
1538
1539   // Jump off at normal
1540   jumpTowardsNormal = stream->readFlag();
1541
1542   physicsPlayerType = stream->readSTString();
1543
1544   // Third person mounted image shapes
1545   imageAnimPrefix = stream->readSTString();
1546
1547   allowImageStateAnimation = stream->readFlag();
1548
1549   // First person mounted image shapes
1550   imageAnimPrefixFP = stream->readSTString();
1551   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
1552   {
1553      shapeNameFP[i] = stream->readSTString();
1554
1555      // computeCRC is handled in ShapeBaseData
1556      if (computeCRC)
1557      {
1558         stream->read(&(mCRCFP[i]));
1559      }
1560   }
1561}
1562
1563
1564//----------------------------------------------------------------------------
1565//----------------------------------------------------------------------------
1566
1567ImplementEnumType( PlayerPose,
1568   "@brief The pose of the Player.\n\n"
1569   "@ingroup gameObjects\n\n")
1570   { Player::StandPose,    "Stand",    "Standard movement pose.\n" },
1571   { Player::SprintPose,   "Sprint",   "Sprinting pose.\n" },
1572   { Player::CrouchPose,   "Crouch",   "Crouch pose.\n" },
1573   { Player::PronePose,    "Prone",    "Prone pose.\n" },
1574   { Player::SwimPose,     "Swim",     "Swimming pose.\n" },
1575EndImplementEnumType;
1576
1577//----------------------------------------------------------------------------
1578
1579IMPLEMENT_CO_NETOBJECT_V1(Player);
1580
1581ConsoleDocClass( Player,
1582   "@ingroup gameObjects\n"
1583);
1584
1585//----------------------------------------------------------------------------
1586
1587Player::Player()
1588{
1589   mTypeMask |= PlayerObjectType | DynamicShapeObjectType;
1590
1591   mDelta.pos = mAnchorPoint = Point3F(0,0,100);
1592   mDelta.rot = mDelta.head = Point3F(0,0,0);
1593   mDelta.rotOffset.set(0.0f,0.0f,0.0f);
1594   mDelta.warpOffset.set(0.0f,0.0f,0.0f);
1595   mDelta.posVec.set(0.0f,0.0f,0.0f);
1596   mDelta.rotVec.set(0.0f,0.0f,0.0f);
1597   mDelta.headVec.set(0.0f,0.0f,0.0f);
1598   mDelta.warpTicks = 0;
1599   mDelta.dt = 1.0f;
1600   mDelta.move = NullMove;
1601   mPredictionCount = sMaxPredictionTicks;
1602   mObjToWorld.setColumn(3, mDelta.pos);
1603   mRot = mDelta.rot;
1604   mHead = mDelta.head;
1605   mVelocity.set(0.0f, 0.0f, 0.0f);
1606   mDataBlock = 0;
1607   mHeadHThread = mHeadVThread = mRecoilThread = mImageStateThread = 0;
1608   mArmAnimation.action = PlayerData::NullAnimation;
1609   mArmAnimation.thread = 0;
1610   mActionAnimation.action = PlayerData::NullAnimation;
1611   mActionAnimation.thread = 0;
1612   mActionAnimation.delayTicks = 0;
1613   mActionAnimation.forward = true;
1614   mActionAnimation.firstPerson = false;
1615   //mActionAnimation.time = 1.0f; //ActionAnimation::Scale;
1616   mActionAnimation.waitForEnd = false;
1617   mActionAnimation.holdAtEnd = false;
1618   mActionAnimation.animateOnServer = false;
1619   mActionAnimation.atEnd = false;
1620   mState = MoveState;
1621   mJetting = false;
1622   mFalling = false;
1623   mSwimming = false;
1624   mInWater = false;
1625   mPose = StandPose;
1626   mContactTimer = 0;
1627   mJumpDelay = 0;
1628   mJumpSurfaceLastContact = 0;
1629   mJumpSurfaceNormal.set(0.0f, 0.0f, 1.0f);
1630   mControlObject = 0;
1631   dMemset( mSplashEmitter, 0, sizeof( mSplashEmitter ) );
1632
1633   mUseHeadZCalc = true;
1634
1635   allowAllPoses();
1636
1637   mImpactSound = 0;
1638   mRecoverTicks = 0;
1639   mReversePending = 0;
1640
1641   mLastPos.set( 0.0f, 0.0f, 0.0f );
1642
1643   mMoveBubbleSound = 0;
1644   mWaterBreathSound = 0;   
1645
1646   mConvex.init(this);
1647   mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
1648   mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
1649
1650   mWeaponBackFraction = 0.0f;
1651
1652   mInMissionArea = true;
1653
1654   mBubbleEmitterTime = 10.0;
1655   mLastWaterPos.set( 0.0, 0.0, 0.0 );
1656
1657   mMountPending = 0;
1658
1659   mPhysicsRep = NULL;
1660
1661   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
1662   {
1663      mShapeFPInstance[i] = 0;
1664      mShapeFPAmbientThread[i] = 0;
1665      mShapeFPVisThread[i] = 0;
1666      mShapeFPAnimThread[i] = 0;
1667      mShapeFPFlashThread[i] = 0;
1668      mShapeFPSpinThread[i] = 0;
1669   }
1670
1671   mLastAbsoluteYaw = 0.0f;
1672   mLastAbsolutePitch = 0.0f;
1673   mLastAbsoluteRoll = 0.0f;
1674   
1675   afx_init();
1676}
1677
1678Player::~Player()
1679{
1680   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
1681   {
1682      delete mShapeFPInstance[i];
1683      mShapeFPInstance[i] = 0;
1684   }
1685}
1686
1687
1688//----------------------------------------------------------------------------
1689
1690bool Player::onAdd()
1691{
1692   ActionAnimation serverAnim = mActionAnimation;
1693   if(!Parent::onAdd() || !mDataBlock)
1694      return false;
1695
1696   mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
1697   mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
1698
1699   addToScene();
1700
1701   // Make sure any state and animation passed from the server
1702   // in the initial update is set correctly.
1703   ActionState state = mState;
1704   mState = NullState;
1705   setState(state);
1706   setPose(StandPose);
1707
1708   if (serverAnim.action != PlayerData::NullAnimation)
1709   {
1710      setActionThread(serverAnim.action, true, serverAnim.holdAtEnd, true, false, true);
1711      if (serverAnim.atEnd)
1712      {
1713         mShapeInstance->clearTransition(mActionAnimation.thread);
1714         mShapeInstance->setPos(mActionAnimation.thread,
1715                                mActionAnimation.forward ? 1.0f : 0.0f);
1716         if (inDeathAnim())
1717            mDeath.lastPos = 1.0f;
1718      }
1719
1720      // We have to leave them sitting for a while since mounts don't come through right
1721      // away (and sometimes not for a while).  Still going to let this time out because
1722      // I'm not sure if we're guaranteed another anim will come through and cancel.
1723      if (!isServerObject() && inSittingAnim())
1724         mMountPending = (S32) sMountPendingTickWait;
1725      else
1726         mMountPending = 0;
1727   }
1728   if (mArmAnimation.action != PlayerData::NullAnimation)
1729      setArmThread(mArmAnimation.action);
1730
1731   //
1732   if (isServerObject())
1733   {
1734      scriptOnAdd();
1735   }
1736   else
1737   {
1738      U32 i;
1739      for( i=0; i<PlayerData::NUM_SPLASH_EMITTERS; i++ )
1740      {
1741         if ( mDataBlock->splashEmitterList[i] ) 
1742         {
1743            mSplashEmitter[i] = new ParticleEmitter;
1744            mSplashEmitter[i]->onNewDataBlock( mDataBlock->splashEmitterList[i], false );
1745            if( !mSplashEmitter[i]->registerObject() )
1746            {
1747               Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() );
1748               mSplashEmitter[i].getPointer()->destroySelf();
1749               mSplashEmitter[i] = NULL;
1750            }
1751         }
1752      }
1753      mLastWaterPos = getPosition();
1754
1755      // clear out all camera effects
1756      gCamFXMgr.clear();
1757   }
1758
1759   if ( PHYSICSMGR )
1760   {
1761      PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
1762
1763      mPhysicsRep = PHYSICSMGR->createPlayer();
1764      mPhysicsRep->init(   mDataBlock->physicsPlayerType,
1765                           mDataBlock->boxSize,
1766                           mDataBlock->runSurfaceCos,
1767                           mDataBlock->maxStepHeight,
1768                           this, 
1769                           world );
1770      mPhysicsRep->setTransform( getTransform() );
1771   }
1772
1773   return true;
1774}
1775
1776void Player::onRemove()
1777{
1778   setControlObject(0);
1779   scriptOnRemove();
1780   removeFromScene();
1781   
1782   if ( isGhost() )
1783   {
1784      SFX_DELETE( mMoveBubbleSound );
1785      SFX_DELETE( mWaterBreathSound );
1786   }
1787
1788   U32 i;
1789   for( i=0; i<PlayerData::NUM_SPLASH_EMITTERS; i++ )
1790   {
1791      if( mSplashEmitter[i] )
1792      {
1793         mSplashEmitter[i]->deleteWhenEmpty();
1794         mSplashEmitter[i] = NULL;
1795      }
1796   }
1797
1798   mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
1799   mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
1800
1801   SAFE_DELETE( mPhysicsRep );
1802
1803   Parent::onRemove();
1804}
1805
1806void Player::onScaleChanged()
1807{
1808   const Point3F& scale = getScale();
1809   mScaledBox = mObjBox;
1810   mScaledBox.minExtents.convolve( scale );
1811   mScaledBox.maxExtents.convolve( scale );
1812}
1813
1814
1815//----------------------------------------------------------------------------
1816
1817bool Player::onNewDataBlock( GameBaseData *dptr, bool reload )
1818{
1819   PlayerData* prevData = mDataBlock;
1820   mDataBlock = dynamic_cast<PlayerData*>(dptr);
1821   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
1822      return false;
1823
1824   // Player requires a shape instance.
1825   if ( mShapeInstance == NULL )
1826      return false;
1827
1828   // Initialize arm thread, preserve arm sequence from last datablock.
1829   // Arm animation can be from last datablock, or sent from the server.
1830   U32 prevAction = mArmAnimation.action;
1831   mArmAnimation.action = PlayerData::NullAnimation;
1832   if (mDataBlock->lookAction) {
1833      mArmAnimation.thread = mShapeInstance->addThread();
1834      mShapeInstance->setTimeScale(mArmAnimation.thread,0);
1835      if (prevData) {
1836         if (prevAction != prevData->lookAction && prevAction != PlayerData::NullAnimation)
1837            setArmThread(prevData->actionList[prevAction].name);
1838         prevAction = PlayerData::NullAnimation;
1839      }
1840      if (mArmAnimation.action == PlayerData::NullAnimation) {
1841         mArmAnimation.action = (prevAction != PlayerData::NullAnimation)?
1842            prevAction: mDataBlock->lookAction;
1843         mShapeInstance->setSequence(mArmAnimation.thread,
1844           mDataBlock->actionList[mArmAnimation.action].sequence,0);
1845      }
1846   }
1847   else
1848      mArmAnimation.thread = 0;
1849
1850   // Initialize head look thread
1851   TSShape const* shape = mShapeInstance->getShape();
1852   S32 headSeq = shape->findSequence("head");
1853   if (headSeq != -1) {
1854      mHeadVThread = mShapeInstance->addThread();
1855      mShapeInstance->setSequence(mHeadVThread,headSeq,0);
1856      mShapeInstance->setTimeScale(mHeadVThread,0);
1857   }
1858   else
1859      mHeadVThread = 0;
1860
1861   headSeq = shape->findSequence("headside");
1862   if (headSeq != -1) {
1863      mHeadHThread = mShapeInstance->addThread();
1864      mShapeInstance->setSequence(mHeadHThread,headSeq,0);
1865      mShapeInstance->setTimeScale(mHeadHThread,0);
1866   }
1867   else
1868      mHeadHThread = 0;
1869
1870   // Create Recoil thread if any recoil sequences are specified.
1871   // Note that the server player does not play this animation.
1872   mRecoilThread = 0;
1873   if (isGhost())
1874      for (U32 s = 0; s < PlayerData::NumRecoilSequences; s++)
1875         if (mDataBlock->recoilSequence[s] != -1) {
1876            mRecoilThread = mShapeInstance->addThread();
1877            mShapeInstance->setSequence(mRecoilThread, mDataBlock->recoilSequence[s], 0);
1878            mShapeInstance->setTimeScale(mRecoilThread, 0);
1879            break;
1880         }
1881
1882   // Reset the image state driven animation thread.  This will be properly built
1883   // in onImageStateAnimation() when needed.
1884   mImageStateThread = 0;
1885
1886   // Initialize the primary thread, the actual sequence is
1887   // set later depending on player actions.
1888   mActionAnimation.action = PlayerData::NullAnimation;
1889   mActionAnimation.thread = mShapeInstance->addThread();
1890   updateAnimationTree(!isGhost());
1891
1892   // First person mounted image shapes.  Only on client.
1893   if ( isGhost() )
1894   {
1895      for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
1896      {
1897         if (bool(mDataBlock->mShapeFP[i]))
1898         {
1899            mShapeFPInstance[i] = new TSShapeInstance(mDataBlock->mShapeFP[i], isClientObject());
1900
1901            mShapeFPInstance[i]->cloneMaterialList();
1902
1903            // Ambient animation
1904            if (mShapeFPAmbientThread[i])
1905            {
1906               S32 seq = mShapeFPInstance[i]->getShape()->findSequence("ambient");
1907               if (seq != -1)
1908               {
1909                  mShapeFPAmbientThread[i] = mShapeFPInstance[i]->addThread();
1910                  mShapeFPInstance[i]->setTimeScale(mShapeFPAmbientThread[i], 1);
1911                  mShapeFPInstance[i]->setSequence(mShapeFPAmbientThread[i], seq, 0);
1912               }
1913            }
1914
1915            // Standard state animation
1916            mShapeFPAnimThread[i] = mShapeFPInstance[i]->addThread();
1917            if (mShapeFPAnimThread[i])
1918            {
1919               mShapeFPInstance[i]->setTimeScale(mShapeFPAnimThread[i],0);
1920            }
1921         }
1922      }
1923   }
1924
1925   if ( isGhost() )
1926   {
1927      // Create the sounds ahead of time.  This reduces runtime
1928      // costs and makes the system easier to understand.
1929
1930      SFX_DELETE( mMoveBubbleSound );
1931      SFX_DELETE( mWaterBreathSound );
1932
1933      if ( mDataBlock->sound[PlayerData::MoveBubbles] )
1934         mMoveBubbleSound = SFX->createSource( mDataBlock->sound[PlayerData::MoveBubbles] );
1935
1936      if ( mDataBlock->sound[PlayerData::WaterBreath] )
1937         mWaterBreathSound = SFX->createSource( mDataBlock->sound[PlayerData::WaterBreath] );
1938   }
1939
1940   mObjBox.maxExtents.x = mDataBlock->boxSize.x * 0.5f;
1941   mObjBox.maxExtents.y = mDataBlock->boxSize.y * 0.5f;
1942   mObjBox.maxExtents.z = mDataBlock->boxSize.z;
1943   mObjBox.minExtents.x = -mObjBox.maxExtents.x;
1944   mObjBox.minExtents.y = -mObjBox.maxExtents.y;
1945   mObjBox.minExtents.z = 0.0f;
1946
1947   // Setup the box for our convex object...
1948   mObjBox.getCenter(&mConvex.mCenter);
1949   mConvex.mSize.x = mObjBox.len_x() / 2.0f;
1950   mConvex.mSize.y = mObjBox.len_y() / 2.0f;
1951   mConvex.mSize.z = mObjBox.len_z() / 2.0f;
1952
1953   // Initialize our scaled attributes as well
1954   onScaleChanged();
1955   resetWorldBox();
1956
1957   scriptOnNewDataBlock();
1958   return true;
1959}
1960
1961//----------------------------------------------------------------------------
1962
1963void Player::reSkin()
1964{
1965   if ( isGhost() && mShapeInstance && mSkinNameHandle.isValidString() )
1966   {
1967     mShapeInstance->resetMaterialList();
1968      Vector<String> skins;
1969      String(mSkinNameHandle.getString()).split( ";", skins );
1970
1971      for ( S32 i = 0; i < skins.size(); i++ )
1972      {
1973         String oldSkin( mAppliedSkinName.c_str() );
1974         String newSkin( skins[i] );
1975
1976         // Check if the skin handle contains an explicit "old" base string. This
1977         // allows all models to support skinning, even if they don't follow the 
1978         // "base_xxx" material naming convention.
1979         S32 split = newSkin.find( '=' );    // "old=new" format skin?
1980         if ( split != String::NPos )
1981         {
1982            oldSkin = newSkin.substr( 0, split );
1983            newSkin = newSkin.erase( 0, split+1 );
1984         }
1985
1986         // Apply skin to both 3rd person and 1st person shape instances
1987         mShapeInstance->reSkin( newSkin, oldSkin );
1988         for ( S32 j = 0; j < ShapeBase::MaxMountedImages; j++ )
1989         {
1990            if (mShapeFPInstance[j])
1991               mShapeFPInstance[j]->reSkin( newSkin, oldSkin );
1992         }
1993
1994         mAppliedSkinName = newSkin;
1995      }
1996   }
1997}
1998
1999//----------------------------------------------------------------------------
2000
2001void Player::setControllingClient(GameConnection* client)
2002{
2003   Parent::setControllingClient(client);
2004   if (mControlObject)
2005      mControlObject->setControllingClient(client);
2006}
2007
2008void Player::setControlObject(ShapeBase* obj)
2009{
2010   if (mControlObject == obj)
2011      return;
2012
2013   if (mControlObject) {
2014      mControlObject->setControllingObject(0);
2015      mControlObject->setControllingClient(0);
2016   }
2017   if (obj == this || obj == 0)
2018      mControlObject = 0;
2019   else {
2020      if (ShapeBase* coo = obj->getControllingObject())
2021         coo->setControlObject(0);
2022      if (GameConnection* con = obj->getControllingClient())
2023         con->setControlObject(0);
2024
2025      mControlObject = obj;
2026      mControlObject->setControllingObject(this);
2027      mControlObject->setControllingClient(getControllingClient());
2028   }
2029}
2030
2031void Player::onCameraScopeQuery(NetConnection *connection, CameraScopeQuery *query)
2032{
2033   // First, we are certainly in scope, and whatever we're riding is too...
2034   if(mControlObject.isNull() || mControlObject == mMount.object)
2035      Parent::onCameraScopeQuery(connection, query);
2036   else
2037   {
2038      connection->objectInScope(this);
2039      if (isMounted())
2040         connection->objectInScope(mMount.object);
2041      mControlObject->onCameraScopeQuery(connection, query);
2042   }
2043}
2044
2045ShapeBase* Player::getControlObject()
2046{
2047   return mControlObject;
2048}
2049
2050void Player::processTick(const Move* move)
2051{
2052   PROFILE_SCOPE(Player_ProcessTick);
2053
2054   bool prevMoveMotion = mMoveMotion;
2055   Pose prevPose = getPose();
2056
2057   // If we're not being controlled by a client, let the
2058   // AI sub-module get a chance at producing a move.
2059   Move aiMove;
2060   if (!move && isServerObject() && getAIMove(&aiMove))
2061      move = &aiMove;
2062
2063   // Manage the control object and filter moves for the player
2064   Move pMove,cMove;
2065   if (mControlObject) {
2066      if (!move)
2067         mControlObject->processTick(0);
2068      else {
2069         pMove = NullMove;
2070         cMove = *move;
2071         //if (isMounted()) {
2072            // Filter Jump trigger if mounted
2073            //pMove.trigger[2] = move->trigger[2];
2074            //cMove.trigger[2] = false;
2075         //}
2076         if (move->freeLook) {
2077            // Filter yaw/picth/roll when freelooking.
2078            pMove.yaw = move->yaw;
2079            pMove.pitch = move->pitch;
2080            pMove.roll = move->roll;
2081            pMove.freeLook = true;
2082            cMove.freeLook = false;
2083            cMove.yaw = cMove.pitch = cMove.roll = 0.0f;
2084         }
2085         mControlObject->processTick((mDamageState == Enabled)? &cMove: &NullMove);
2086         move = &pMove;
2087      }
2088   }
2089
2090   Parent::processTick(move);
2091   // Check for state changes in the standard move triggers and
2092   // set bits for any triggers that switched on this tick in
2093   // the fx_s_triggers mask. Flag any changes to be packed to
2094   // clients.
2095   if (isServerObject())
2096   {
2097      fx_s_triggers = 0;
2098      if (move)
2099      {
2100         U8 on_bits = 0;
2101         for (S32 i = 0; i < MaxTriggerKeys; i++)
2102            if (move->trigger[i])
2103               on_bits |= BIT(i);
2104
2105         if (on_bits != move_trigger_states)
2106         {
2107            U8 switched_on_bits = (on_bits & ~move_trigger_states);
2108            if (switched_on_bits)
2109            {
2110               fx_s_triggers |= (U32)switched_on_bits;
2111               setMaskBits(TriggerMask);
2112            }
2113            move_trigger_states = on_bits;
2114         }
2115      }
2116   }
2117   // Warp to catch up to server
2118   if (mDelta.warpTicks > 0) {
2119      mDelta.warpTicks--;
2120
2121      // Set new pos
2122      getTransform().getColumn(3, &mDelta.pos);
2123     mDelta.pos += mDelta.warpOffset;
2124     mDelta.rot += mDelta.rotOffset;
2125
2126      // Wrap yaw to +/-PI
2127      if (mDelta.rot.z < - M_PI_F)
2128        mDelta.rot.z += M_2PI_F;
2129      else if (mDelta.rot.z > M_PI_F)
2130        mDelta.rot.z -= M_2PI_F;
2131
2132      if (!ignore_updates)
2133      {
2134         setPosition(mDelta.pos, mDelta.rot);
2135      }
2136      updateDeathOffsets();
2137      updateLookAnimation();
2138
2139      // Backstepping
2140     mDelta.posVec = -mDelta.warpOffset;
2141     mDelta.rotVec = -mDelta.rotOffset;
2142   }
2143   else {
2144      // If there is no move, the player is either an
2145      // unattached player on the server, or a player's
2146      // client ghost.
2147      if (!move) {
2148         if (isGhost()) {
2149            // If we haven't run out of prediction time,
2150            // predict using the last known move.
2151            if (mPredictionCount-- <= 0)
2152               return;
2153
2154            move = &mDelta.move;
2155         }
2156         else
2157            move = &NullMove;
2158      }
2159      if (!isGhost())
2160         updateAnimation(TickSec);
2161
2162      PROFILE_START(Player_PhysicsSection);
2163      if ( isServerObject() || didRenderLastRender() || getControllingClient() )
2164      {
2165         if ( !mPhysicsRep )
2166         {
2167            if ( isMounted() )
2168            {
2169               // If we're mounted then do not perform any collision checks
2170               // and clear our previous working list.
2171               mConvex.clearWorkingList();
2172            }
2173            else
2174            {
2175               updateWorkingCollisionSet();
2176            }
2177         }
2178
2179         updateState();
2180         updateMove(move);
2181         updateLookAnimation();
2182         updateDeathOffsets();
2183         updatePos();
2184      }
2185      PROFILE_END();
2186
2187      if (!isGhost())
2188      {
2189         // Animations are advanced based on frame rate on the
2190         // client and must be ticked on the server.
2191         updateActionThread();
2192         updateAnimationTree(true);
2193
2194         // Check for sprinting motion changes
2195         Pose currentPose = getPose();
2196         // Player has just switched into Sprint pose and is moving
2197         if (currentPose == SprintPose && prevPose != SprintPose && mMoveMotion)
2198         {
2199            mDataBlock->onStartSprintMotion_callback( this );
2200         }
2201         // Player has just switched out of Sprint pose and is moving, or was just moving
2202         else if (currentPose != SprintPose && prevPose == SprintPose && (mMoveMotion || prevMoveMotion))
2203         {
2204            mDataBlock->onStopSprintMotion_callback( this );
2205         }
2206         // Player is in Sprint pose and has modified their motion
2207         else if (currentPose == SprintPose && prevMoveMotion != mMoveMotion)
2208         {
2209            if (mMoveMotion)
2210            {
2211               mDataBlock->onStartSprintMotion_callback( this );
2212            }
2213            else
2214            {
2215               mDataBlock->onStopSprintMotion_callback( this );
2216            }
2217         }
2218      }
2219   }
2220// PATHSHAPE
2221   if (!isGhost()) updateAttachment(); 
2222// PATHSHAPE END
2223}
2224
2225void Player::interpolateTick(F32 dt)
2226{
2227   if (mControlObject)
2228      mControlObject->interpolateTick(dt);
2229
2230   // Client side interpolation
2231   Parent::interpolateTick(dt);
2232
2233   Point3F pos = mDelta.pos + mDelta.posVec * dt;
2234   Point3F rot = mDelta.rot + mDelta.rotVec * dt;
2235
2236   if (!ignore_updates)
2237      setRenderPosition(pos,rot,dt);
2238
2239/*
2240   // apply camera effects - is this the best place? - bramage
2241   GameConnection* connection = GameConnection::getConnectionToServer();
2242   if( connection->isFirstPerson() )
2243   {
2244      ShapeBase *obj = dynamic_cast<ShapeBase*>(connection->getControlObject());
2245      if( obj == this )
2246      {
2247         MatrixF curTrans = getRenderTransform();
2248         curTrans.mul( gCamFXMgr.getTrans() );
2249         Parent::setRenderTransform( curTrans );
2250      }
2251   }
2252*/
2253
2254   updateLookAnimation(dt);
2255   mDelta.dt = dt;
2256// PATHSHAPE
2257   updateRenderChangesByParent();
2258// PATHSHAPE END
2259}
2260
2261void Player::advanceTime(F32 dt)
2262{
2263   // Client side animations
2264   Parent::advanceTime(dt);
2265   // Increment timer for triggering idle events.
2266   if (idle_timer >= 0.0f)
2267      idle_timer += dt;
2268   updateActionThread();
2269   updateAnimation(dt);
2270   updateSplash();
2271   updateFroth(dt);
2272   updateWaterSounds(dt);
2273
2274   mLastPos = getPosition();
2275
2276   if (mImpactSound)
2277      playImpactSound();
2278
2279   // update camera effects.  Definitely need to find better place for this - bramage
2280   if( isControlObject() )
2281   {
2282      if( mDamageState == Disabled || mDamageState == Destroyed )
2283      {
2284         // clear out all camera effects being applied to player if dead
2285         gCamFXMgr.clear();
2286      }
2287   }
2288}
2289
2290bool Player::getAIMove(Move* move)
2291{
2292   return false;
2293}
2294
2295void Player::setState(ActionState state, U32 recoverTicks)
2296{
2297   if (state != mState) {
2298      // Skip initialization if there is no manager, the state
2299      // will get reset when the object is added to a manager.
2300      if (isProperlyAdded()) {
2301         switch (state) {
2302            case RecoverState: {
2303               if (mDataBlock->landSequenceTime > 0.0f)
2304               {
2305                  // Use the land sequence as the basis for the recovery
2306                  setActionThread(PlayerData::LandAnim, true, false, true, true);
2307                  F32 timeScale = mShapeInstance->getDuration(mActionAnimation.thread) / mDataBlock->landSequenceTime;
2308                  mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale);
2309                  mRecoverDelay =  mDataBlock->landSequenceTime;
2310               }
2311               else
2312               {
2313                  // Legacy recover system
2314                  mRecoverTicks = recoverTicks;
2315                  mReversePending = U32(F32(mRecoverTicks) / sLandReverseScale);
2316                  setActionThread(PlayerData::LandAnim, true, false, true, true);
2317               }
2318               break;
2319            }
2320            
2321            default:
2322               break;
2323         }
2324      }
2325
2326      mState = state;
2327   }
2328}
2329
2330void Player::updateState()
2331{
2332   switch (mState)
2333   {
2334      case RecoverState:
2335         if (mDataBlock->landSequenceTime > 0.0f)
2336         {
2337            // Count down the land time
2338            mRecoverDelay -= TickSec;
2339            if (mRecoverDelay <= 0.0f)
2340            {
2341               setState(MoveState);
2342            }
2343         }
2344         else
2345         {
2346            // Legacy recover system
2347            if (mRecoverTicks-- <= 0)
2348            {
2349               if (mReversePending && mActionAnimation.action != PlayerData::NullAnimation)
2350               {
2351                  // this serves and counter, and direction state
2352                  mRecoverTicks = mReversePending;
2353                  mActionAnimation.forward = false;
2354
2355                  S32 seq = mDataBlock->actionList[mActionAnimation.action].sequence;
2356                  S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
2357                  if (imageBasedSeq != -1)
2358                     seq = imageBasedSeq;
2359
2360                  F32 pos = mShapeInstance->getPos(mActionAnimation.thread);
2361
2362                  mShapeInstance->setTimeScale(mActionAnimation.thread, -sLandReverseScale);
2363                  mShapeInstance->transitionToSequence(mActionAnimation.thread,
2364                                                       seq, pos, sAnimationTransitionTime, true);
2365                  mReversePending = 0;
2366               }
2367               else
2368               {
2369                  setState(MoveState);
2370               }
2371            }        // Stand back up slowly only if not moving much-
2372            else if (!mReversePending && mVelocity.lenSquared() > sSlowStandThreshSquared)
2373            {
2374               mActionAnimation.waitForEnd = false;
2375               setState(MoveState);
2376            }
2377         }
2378         break;
2379      
2380      default:
2381         break;
2382   }
2383}
2384
2385const char* Player::getStateName()
2386{
2387   if (mDamageState != Enabled)
2388      return "Dead";
2389   if (isMounted())
2390      return "Mounted";
2391   if (mState == RecoverState)
2392      return "Recover";
2393   return "Move";
2394}
2395
2396void Player::getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad)
2397{
2398   // TODO: This will be WRONG when player is prone or swimming!
2399
2400   Point3F newPoint;
2401   mWorldToObj.mulP(in_rPos, &newPoint);
2402
2403   Point3F boxSize = mObjBox.getExtents();
2404   F32 zHeight = boxSize.z;
2405   F32 zTorso  = mDataBlock->boxTorsoPercentage;
2406   F32 zHead   = mDataBlock->boxHeadPercentage;
2407
2408   zTorso *= zHeight;
2409   zHead  *= zHeight;
2410
2411   if (newPoint.z <= zTorso)
2412      out_rpVert = "legs";
2413   else if (newPoint.z <= zHead)
2414      out_rpVert = "torso";
2415   else
2416      out_rpVert = "head";
2417
2418   if(String::compare(out_rpVert, "head") != 0)
2419   {
2420      if (newPoint.y >= 0.0f)
2421      {
2422         if (newPoint.x <= 0.0f)
2423            out_rpQuad = "front_left";
2424         else
2425            out_rpQuad = "front_right";
2426      }
2427      else
2428      {
2429         if (newPoint.x <= 0.0f)
2430            out_rpQuad = "back_left";
2431         else
2432            out_rpQuad = "back_right";
2433      }
2434   }
2435   else
2436   {
2437      F32 backToFront = boxSize.x;
2438      F32 leftToRight = boxSize.y;
2439
2440      F32 backPoint  = backToFront * mDataBlock->boxHeadBackPercentage;
2441      F32 frontPoint = backToFront * mDataBlock->boxHeadFrontPercentage;
2442      F32 leftPoint  = leftToRight * mDataBlock->boxHeadLeftPercentage;
2443      F32 rightPoint = leftToRight * mDataBlock->boxHeadRightPercentage;
2444
2445      S32 index = 0;
2446      if (newPoint.y < backPoint)
2447         index += 0;
2448      else if (newPoint.y >= frontPoint)
2449         index += 3;
2450      else
2451         index += 6;
2452
2453      if (newPoint.x < leftPoint)
2454         index += 0;
2455      else if (newPoint.x >= rightPoint)
2456         index += 1;
2457      else
2458         index += 2;
2459
2460      switch (index)
2461      {
2462         case 0: out_rpQuad = "left_back";      break;
2463         case 1: out_rpQuad = "middle_back";    break;
2464         case 2: out_rpQuad = "right_back";     break;
2465         case 3: out_rpQuad = "left_middle";    break;
2466         case 4: out_rpQuad = "middle_middle";  break;
2467         case 5: out_rpQuad = "right_middle";   break;
2468         case 6: out_rpQuad = "left_front";     break;
2469         case 7: out_rpQuad = "middle_front";   break;
2470         case 8: out_rpQuad = "right_front";    break;
2471
2472         default:
2473            AssertFatal(0, "Bad non-tant index");
2474      };
2475   }
2476}
2477
2478const char* Player::getPoseName() const
2479{
2480   return EngineMarshallData< PlayerPose >(getPose());
2481}
2482
2483void Player::setPose( Pose pose )
2484{
2485   // Already the set pose, return.
2486   if ( pose == mPose ) 
2487      return;
2488
2489   Pose oldPose = mPose;
2490
2491   mPose = pose;
2492
2493   // Not added yet, just assign the pose and return.
2494   if ( !isProperlyAdded() )   
2495      return;
2496        
2497   Point3F boxSize(1,1,1);
2498
2499   // Resize the player boxes
2500   switch (pose) 
2501   {
2502      case StandPose:
2503      case SprintPose:
2504         boxSize = mDataBlock->boxSize;
2505         break;
2506      case CrouchPose:
2507         boxSize = mDataBlock->crouchBoxSize;         
2508         break;
2509      case PronePose:
2510         boxSize = mDataBlock->proneBoxSize;         
2511         break;
2512      case SwimPose:
2513         boxSize = mDataBlock->swimBoxSize;
2514         break;
2515   }
2516
2517   // Object and World Boxes...
2518   mObjBox.maxExtents.x = boxSize.x * 0.5f;
2519   mObjBox.maxExtents.y = boxSize.y * 0.5f;
2520   mObjBox.maxExtents.z = boxSize.z;
2521   mObjBox.minExtents.x = -mObjBox.maxExtents.x;
2522   mObjBox.minExtents.y = -mObjBox.maxExtents.y;
2523   mObjBox.minExtents.z = 0.0f;
2524
2525   resetWorldBox();
2526
2527   // Setup the box for our convex object...
2528   mObjBox.getCenter(&mConvex.mCenter);
2529   mConvex.mSize.x = mObjBox.len_x() / 2.0f;
2530   mConvex.mSize.y = mObjBox.len_y() / 2.0f;
2531   mConvex.mSize.z = mObjBox.len_z() / 2.0f;
2532
2533   // Initialize our scaled attributes as well...
2534   onScaleChanged();
2535
2536   // Resize the PhysicsPlayer rep. should we have one
2537   if ( mPhysicsRep )
2538      mPhysicsRep->setSpacials( getPosition(), boxSize );
2539
2540   if ( isServerObject() )
2541      mDataBlock->onPoseChange_callback( this, EngineMarshallData< PlayerPose >(oldPose), EngineMarshallData< PlayerPose >(mPose));
2542}
2543
2544void Player::allowAllPoses()
2545{
2546   mAllowJumping = true;
2547   mAllowJetJumping = true;
2548   mAllowSprinting = true;
2549   mAllowCrouching = true;
2550   mAllowProne = true;
2551   mAllowSwimming = true;
2552}
2553
2554AngAxisF gPlayerMoveRot;
2555
2556void Player::updateMove(const Move* move)
2557{
2558   struct Move my_move;
2559   if (override_movement && movement_op < 3)
2560   {
2561      my_move = *move;
2562      switch (movement_op)
2563      {
2564      case 0: // add
2565         my_move.x += movement_data.x;
2566         my_move.y += movement_data.y;
2567         my_move.z += movement_data.z;
2568         break;
2569      case 1: // mult
2570         my_move.x *= movement_data.x;
2571         my_move.y *= movement_data.y;
2572         my_move.z *= movement_data.z;
2573         break;
2574      case 2: // replace
2575         my_move.x = movement_data.x;
2576         my_move.y = movement_data.y;
2577         my_move.z = movement_data.z;
2578         break;
2579      }
2580      move = &my_move;
2581   }
2582   mDelta.move = *move;
2583
2584#ifdef TORQUE_OPENVR
2585   if (mControllers[0])
2586   {
2587      mControllers[0]->processTick(move);
2588   }
2589
2590   if (mControllers[1])
2591   {
2592      mControllers[1]->processTick(move);
2593   }
2594
2595#endif
2596
2597   // Is waterCoverage high enough to be 'swimming'?
2598   {
2599      bool swimming = mWaterCoverage > 0.65f && canSwim();      
2600
2601      if ( swimming != mSwimming )
2602      {
2603         if ( !isGhost() )
2604         {
2605            if ( swimming )
2606               mDataBlock->onStartSwim_callback( this );
2607            else
2608               mDataBlock->onStopSwim_callback( this );
2609         }
2610
2611         mSwimming = swimming;
2612      }
2613   }
2614
2615   // Trigger images
2616   if (mDamageState == Enabled) 
2617   {
2618      setImageTriggerState( 0, move->trigger[sImageTrigger0] );
2619
2620      // If you have a secondary mounted image then
2621      // send the second trigger to it.  Else give it
2622      // to the first image as an alt fire.
2623      if ( getMountedImage( 1 ) )
2624         setImageTriggerState( 1, move->trigger[sImageTrigger1] );
2625      else
2626         setImageAltTriggerState( 0, move->trigger[sImageTrigger1] );
2627   }
2628
2629   // Update current orientation
2630   if (mDamageState == Enabled) {
2631      F32 prevZRot = mRot.z;
2632     mDelta.headVec = mHead;
2633
2634      bool doStandardMove = true;
2635      bool absoluteDelta = false;
2636      GameConnection* con = getControllingClient();
2637
2638#ifdef TORQUE_EXTENDED_MOVE
2639      // Work with an absolute rotation from the ExtendedMove class?
2640      if(con && con->getControlSchemeAbsoluteRotation())
2641      {
2642         doStandardMove = false;
2643         const ExtendedMove* emove = dynamic_cast<const ExtendedMove*>(move);
2644         U32 emoveIndex = smExtendedMoveHeadPosRotIndex;
2645         if(emoveIndex >= ExtendedMove::MaxPositionsRotations)
2646            emoveIndex = 0;
2647
2648         if(emove->EulerBasedRotation[emoveIndex])
2649         {
2650            // Head pitch
2651            mHead.x += (emove->rotX[emoveIndex] - mLastAbsolutePitch);
2652
2653            // Do we also include the relative yaw value?
2654            if(con->getControlSchemeAddPitchToAbsRot())
2655            {
2656               F32 x = move->pitch;
2657               if (x > M_PI_F)
2658                  x -= M_2PI_F;
2659
2660               mHead.x += x;
2661            }
2662
2663            // Constrain the range of mHead.x
2664            while (mHead.x < -M_PI_F) 
2665               mHead.x += M_2PI_F;
2666            while (mHead.x > M_PI_F) 
2667               mHead.x -= M_2PI_F;
2668
2669            // Rotate (heading) head or body?
2670            if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
2671            {
2672               // Rotate head
2673               mHead.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
2674
2675               // Do we also include the relative yaw value?
2676               if(con->getControlSchemeAddYawToAbsRot())
2677               {
2678                  F32 z = move->yaw;
2679                  if (z > M_PI_F)
2680                     z -= M_2PI_F;
2681
2682                  mHead.z += z;
2683               }
2684
2685               // Constrain the range of mHead.z
2686               while (mHead.z < 0.0f)
2687                  mHead.z += M_2PI_F;
2688               while (mHead.z > M_2PI_F)
2689                  mHead.z -= M_2PI_F;
2690            }
2691            else
2692            {
2693               // Rotate body
2694               mRot.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
2695
2696               // Do we also include the relative yaw value?
2697               if(con->getControlSchemeAddYawToAbsRot())
2698               {
2699                  F32 z = move->yaw;
2700                  if (z > M_PI_F)
2701                     z -= M_2PI_F;
2702
2703                  mRot.z += z;
2704               }
2705
2706               // Constrain the range of mRot.z
2707               while (mRot.z < 0.0f)
2708                  mRot.z += M_2PI_F;
2709               while (mRot.z > M_2PI_F)
2710                  mRot.z -= M_2PI_F;
2711            }
2712            mLastAbsoluteYaw = emove->rotZ[emoveIndex];
2713            mLastAbsolutePitch = emove->rotX[emoveIndex];
2714            mLastAbsoluteRoll = emove->rotY[emoveIndex];
2715
2716            // Head bank
2717            mHead.y = emove->rotY[emoveIndex];
2718
2719            // Constrain the range of mHead.y
2720            while (mHead.y > M_PI_F) 
2721               mHead.y -= M_2PI_F;
2722         }
2723         else
2724         {
2725            // Orient the player so we are looking towards the required position, ignoring any banking
2726            AngAxisF moveRot(Point3F(emove->rotX[emoveIndex], emove->rotY[emoveIndex], emove->rotZ[emoveIndex]), emove->rotW[emoveIndex]);
2727            MatrixF trans(1);
2728            moveRot.setMatrix(&trans);
2729            trans.inverse();
2730
2731            Point3F vecForward(0, 10, 0);
2732            Point3F viewAngle;
2733            Point3F orient;
2734            EulerF rot;
2735            trans.mulV(vecForward);
2736            viewAngle = vecForward;
2737            vecForward.z = 0; // flatten
2738            vecForward.normalizeSafe();
2739
2740            F32 yawAng;
2741            F32 pitchAng;
2742            MathUtils::getAnglesFromVector(vecForward, yawAng, pitchAng);
2743
2744            mRot = EulerF(0);
2745            mRot.z = yawAng;
2746            mHead = EulerF(0);
2747
2748            while (mRot.z < 0.0f)
2749               mRot.z += M_2PI_F;
2750            while (mRot.z > M_2PI_F)
2751               mRot.z -= M_2PI_F;
2752
2753            absoluteDelta = true;
2754         }
2755      }
2756#endif
2757
2758      if(doStandardMove)
2759      {
2760         F32 p = move->pitch * (mPose == SprintPose ? mDataBlock->sprintPitchScale : 1.0f);
2761         if (p > M_PI_F) 
2762            p -= M_2PI_F;
2763         mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
2764                           mDataBlock->maxLookAngle);
2765
2766         F32 y = move->yaw * (mPose == SprintPose ? mDataBlock->sprintYawScale : 1.0f);
2767         if (y > M_PI_F)
2768            y -= M_2PI_F;
2769
2770         if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
2771         {
2772            mHead.z = mClampF(mHead.z + y,
2773                              -mDataBlock->maxFreelookAngle,
2774                              mDataBlock->maxFreelookAngle);
2775         }
2776         else
2777         {
2778            mRot.z += y;
2779            // Rotate the head back to the front, center horizontal
2780            // as well if we're controlling another object.
2781            mHead.z *= 0.5f;
2782            if (mControlObject)
2783               mHead.x *= 0.5f;
2784         }
2785
2786         // constrain the range of mRot.z
2787         while (mRot.z < 0.0f)
2788            mRot.z += M_2PI_F;
2789         while (mRot.z > M_2PI_F)
2790            mRot.z -= M_2PI_F;
2791      }
2792
2793     mDelta.rot = mRot;
2794     mDelta.rotVec.x = mDelta.rotVec.y = 0.0f;
2795     mDelta.rotVec.z = prevZRot - mRot.z;
2796      if (mDelta.rotVec.z > M_PI_F)
2797        mDelta.rotVec.z -= M_2PI_F;
2798      else if (mDelta.rotVec.z < -M_PI_F)
2799        mDelta.rotVec.z += M_2PI_F;
2800
2801     mDelta.head = mHead;
2802     mDelta.headVec -= mHead;
2803
2804      if (absoluteDelta)
2805      {
2806         mDelta.headVec = Point3F(0, 0, 0);
2807       mDelta.rotVec = Point3F(0, 0, 0);
2808      }
2809
2810      for(U32 i=0; i<3; ++i)
2811      {
2812         if (mDelta.headVec[i] > M_PI_F)
2813            mDelta.headVec[i] -= M_2PI_F;
2814         else if (mDelta.headVec[i] < -M_PI_F)
2815            mDelta.headVec[i] += M_2PI_F;
2816      }
2817   }
2818   MatrixF zRot;
2819   zRot.set(EulerF(0.0f, 0.0f, mRot.z));
2820
2821   // Desired move direction & speed
2822   VectorF moveVec;
2823   F32 moveSpeed;
2824   // If BLOCK_USER_CONTROL is set in anim_clip_flags, the user won't be able to
2825   // resume control over the player character. This generally happens for
2826   // short periods of time synchronized with script driven animation at places
2827   // where it makes sense that user motion is prohibited, such as when the 
2828   // player is lifted off the ground or knocked down.
2829   if ((mState == MoveState || (mState == RecoverState && mDataBlock->recoverRunForceScale > 0.0f)) && mDamageState == Enabled && !isAnimationLocked())
2830   {
2831      zRot.getColumn(0,&moveVec);
2832      moveVec *= (move->x * (mPose == SprintPose ? mDataBlock->sprintStrafeScale : 1.0f));
2833      VectorF tv;
2834      zRot.getColumn(1,&tv);
2835      moveVec += tv * move->y;
2836
2837      // Clamp water movement
2838      if (move->y > 0.0f)
2839      {
2840         if ( mSwimming )
2841            moveSpeed = getMax(mDataBlock->maxUnderwaterForwardSpeed * move->y,
2842                               mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x));
2843         else if ( mPose == PronePose )
2844            moveSpeed = getMax(mDataBlock->maxProneForwardSpeed * move->y,
2845                               mDataBlock->maxProneSideSpeed * mFabs(move->x));
2846         else if ( mPose == CrouchPose )
2847            moveSpeed = getMax(mDataBlock->maxCrouchForwardSpeed * move->y,
2848                               mDataBlock->maxCrouchSideSpeed * mFabs(move->x));
2849         else if ( mPose == SprintPose )
2850            moveSpeed = getMax(mDataBlock->maxSprintForwardSpeed * move->y,
2851                               mDataBlock->maxSprintSideSpeed * mFabs(move->x));
2852
2853         else // StandPose
2854            moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y,
2855                               mDataBlock->maxSideSpeed * mFabs(move->x));
2856      }
2857      else
2858      {
2859         if ( mSwimming )
2860            moveSpeed = getMax(mDataBlock->maxUnderwaterBackwardSpeed * mFabs(move->y),
2861                               mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x));
2862         else if ( mPose == PronePose )
2863            moveSpeed = getMax(mDataBlock->maxProneBackwardSpeed * mFabs(move->y),
2864                               mDataBlock->maxProneSideSpeed * mFabs(move->x));
2865         else if ( mPose == CrouchPose )
2866            moveSpeed = getMax(mDataBlock->maxCrouchBackwardSpeed * mFabs(move->y),
2867                               mDataBlock->maxCrouchSideSpeed * mFabs(move->x));         
2868         else if ( mPose == SprintPose )
2869            moveSpeed = getMax(mDataBlock->maxSprintBackwardSpeed * mFabs(move->y),
2870                               mDataBlock->maxSprintSideSpeed * mFabs(move->x));         
2871         else // StandPose
2872            moveSpeed = getMax(mDataBlock->maxBackwardSpeed * mFabs(move->y),
2873                               mDataBlock->maxSideSpeed * mFabs(move->x));
2874      }
2875
2876      // Cancel any script driven animations if we are going to move.
2877      if (moveVec.x + moveVec.y + moveVec.z != 0.0f &&
2878          (mActionAnimation.action >= PlayerData::NumTableActionAnims
2879               || mActionAnimation.action == PlayerData::LandAnim))
2880         mActionAnimation.action = PlayerData::NullAnimation;
2881   }
2882   else
2883   {
2884      moveVec.set(0.0f, 0.0f, 0.0f);
2885      moveSpeed = 0.0f;
2886   }
2887
2888   // apply speed bias here.
2889   speed_bias = speed_bias + (speed_bias_goal - speed_bias)*0.1f;
2890   moveSpeed *= speed_bias;
2891   // Acceleration due to gravity
2892   VectorF acc(0.0f, 0.0f, mNetGravity/(1.0 - mBuoyancy) * TickSec);
2893   if (getParent() !=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>)
2894      acc = VectorF::Zero;
2895
2896   // Determine ground contact normal. Only look for contacts if
2897   // we can move and aren't mounted.
2898   VectorF contactNormal(0,0,0);
2899   bool jumpSurface = false, runSurface = false;
2900   if ( !isMounted() )
2901      findContact( &runSurface, &jumpSurface, &contactNormal );
2902   if ( jumpSurface )
2903      mJumpSurfaceNormal = contactNormal;
2904   
2905   // If we don't have a runSurface but we do have a contactNormal,
2906   // then we are standing on something that is too steep.
2907   // Deflect the force of gravity by the normal so we slide.
2908   // We could also try aligning it to the runSurface instead,
2909   // but this seems to work well.
2910   if ( !runSurface && !contactNormal.isZero() )  
2911      acc = ( acc - 2 * contactNormal * mDot( acc, contactNormal ) );   
2912
2913   // Acceleration on run surface
2914   if (runSurface && !mSwimming) {
2915      mContactTimer = 0;
2916
2917      // Remove acc into contact surface (should only be gravity)
2918      // Clear out floating point acc errors, this will allow
2919      // the player to "rest" on the ground.
2920      // However, no need to do that if we're using a physics library.
2921      // It will take care of itself.
2922      if (!mPhysicsRep)
2923      {
2924         F32 vd = -mDot(acc,contactNormal);
2925         if (vd > 0.0f) {
2926            VectorF dv = contactNormal * (vd + 0.002f);
2927            acc += dv;
2928            if (acc.len() < 0.0001f)
2929               acc.set(0.0f, 0.0f, 0.0f);
2930         }
2931      }
2932
2933      // Force a 0 move if there is no energy, and only drain
2934      // move energy if we're moving.
2935      VectorF pv;
2936      if (mPose == SprintPose && mEnergy >= mDataBlock->minSprintEnergy) {
2937         if (moveSpeed)
2938            mEnergy -= mDataBlock->sprintEnergyDrain;
2939         pv = moveVec;
2940      }
2941      else if (mEnergy >= mDataBlock->minRunEnergy) {
2942         if (moveSpeed)
2943            mEnergy -= mDataBlock->runEnergyDrain;
2944         pv = moveVec;
2945      }
2946      else
2947         pv.set(0.0f, 0.0f, 0.0f);
2948
2949      // Adjust the player's requested dir. to be parallel
2950      // to the contact surface.
2951      F32 pvl = pv.len();
2952      if(mJetting)
2953      {
2954         pvl = moveVec.len();
2955         if (pvl)
2956         {
2957            VectorF nn;
2958            mCross(pv,VectorF(0.0f, 0.0f, 0.0f),&nn);
2959            nn *= 1 / pvl;
2960            VectorF cv(0.0f, 0.0f, 0.0f);
2961            cv -= nn * mDot(nn,cv);
2962            pv -= cv * mDot(pv,cv);
2963            pvl = pv.len();
2964         }
2965      }
2966      else if (!mPhysicsRep)
2967      {
2968         // We only do this if we're not using a physics library.  The
2969         // library will take care of itself.
2970         if (pvl)
2971         {
2972            VectorF nn;
2973            mCross(pv,VectorF(0.0f, 0.0f, 1.0f),&nn);
2974            nn *= 1.0f / pvl;
2975            VectorF cv = contactNormal;
2976            cv -= nn * mDot(nn,cv);
2977            pv -= cv * mDot(pv,cv);
2978            pvl = pv.len();
2979         }
2980      }
2981
2982      // Convert to acceleration
2983      if ( pvl )
2984         pv *= moveSpeed / pvl;
2985      VectorF runAcc = pv - (mVelocity + acc);
2986      F32 runSpeed = runAcc.len();
2987
2988      // Clamp acceleration, player also accelerates faster when
2989      // in his hard landing recover state.
2990      F32 maxAcc;
2991      if (mPose == SprintPose)
2992      {
2993         maxAcc = (mDataBlock->sprintForce / getMass()) * TickSec;
2994      }
2995      else
2996      {
2997         maxAcc = (mDataBlock->runForce / getMass()) * TickSec;
2998      }
2999      if (mState == RecoverState)
3000         maxAcc *= mDataBlock->recoverRunForceScale;
3001      if (runSpeed > maxAcc)
3002         runAcc *= maxAcc / runSpeed;
3003      acc += runAcc;
3004
3005      // If we are running on the ground, then we're not jumping
3006      if (mDataBlock->isJumpAction(mActionAnimation.action))
3007         mActionAnimation.action = PlayerData::NullAnimation;
3008   }
3009   else if (!mSwimming && mDataBlock->airControl > 0.0f)
3010   {
3011      VectorF pv;
3012      pv = moveVec;
3013      F32 pvl = pv.len();
3014
3015      if (pvl)
3016         pv *= moveSpeed / pvl;
3017
3018      VectorF runAcc = pv - (mVelocity + acc);
3019      runAcc.z = 0;
3020      runAcc.x = runAcc.x * mDataBlock->airControl;
3021      runAcc.y = runAcc.y * mDataBlock->airControl;
3022      F32 runSpeed = runAcc.len();
3023      // We don't test for sprinting when performing air control
3024      F32 maxAcc = (mDataBlock->runForce / getMass()) * TickSec * 0.3f;
3025
3026      if (runSpeed > maxAcc)
3027         runAcc *= maxAcc / runSpeed;
3028
3029      acc += runAcc;
3030
3031      // There are no special air control animations 
3032      // so... increment this unless you really want to 
3033      // play the run anims in the air.
3034      mContactTimer++;
3035   }
3036   else if (mSwimming)
3037   {
3038      // Remove acc into contact surface (should only be gravity)
3039      // Clear out floating point acc errors, this will allow
3040      // the player to "rest" on the ground.
3041      F32 vd = -mDot(acc,contactNormal);
3042      if (vd > 0.0f) {
3043         VectorF dv = contactNormal * (vd + 0.002f);
3044         acc += dv;
3045         if (acc.len() < 0.0001f)
3046            acc.set(0.0f, 0.0f, 0.0f);
3047      }
3048
3049      // get the head pitch and add it to the moveVec
3050      // This more accurate swim vector calc comes from Matt Fairfax
3051      MatrixF xRot;
3052      xRot.set(EulerF(mHead.x, 0, 0));
3053      zRot.set(EulerF(0, 0, mRot.z));
3054      MatrixF rot;
3055      rot.mul(zRot, xRot);
3056      rot.getColumn(0,&moveVec);
3057
3058      moveVec *= move->x;
3059      VectorF tv;
3060      rot.getColumn(1,&tv);
3061      moveVec += tv * move->y;
3062      rot.getColumn(2,&tv);
3063      moveVec += tv * move->z;
3064
3065      // Force a 0 move if there is no energy, and only drain
3066      // move energy if we're moving.
3067      VectorF swimVec;
3068      if (mEnergy >= mDataBlock->minRunEnergy) {
3069         if (moveSpeed)         
3070            mEnergy -= mDataBlock->runEnergyDrain;
3071         swimVec = moveVec;
3072      }
3073      else
3074         swimVec.set(0.0f, 0.0f, 0.0f);
3075
3076      // If we are swimming but close enough to the shore/ground
3077      // we can still have a surface-normal. In this case align the
3078      // velocity to the normal to make getting out of water easier.
3079      
3080      moveVec.normalize();
3081      F32 isSwimUp = mDot( moveVec, contactNormal );
3082
3083      if ( !contactNormal.isZero() && isSwimUp < 0.1f )
3084      {
3085         F32 pvl = swimVec.len();
3086
3087         if ( true && pvl )
3088         {
3089            VectorF nn;
3090            mCross(swimVec,VectorF(0.0f, 0.0f, 1.0f),&nn);
3091            nn *= 1.0f / pvl;
3092            VectorF cv = contactNormal;
3093            cv -= nn * mDot(nn,cv);
3094            swimVec -= cv * mDot(swimVec,cv);
3095         }
3096      }
3097
3098      F32 swimVecLen = swimVec.len();
3099
3100      // Convert to acceleration.
3101      if ( swimVecLen )
3102         swimVec *= moveSpeed / swimVecLen;
3103      VectorF swimAcc = swimVec - (mVelocity + acc);
3104      F32 swimSpeed = swimAcc.len();
3105
3106      // Clamp acceleration.
3107      F32 maxAcc = (mDataBlock->swimForce / getMass()) * TickSec;
3108      if ( swimSpeed > maxAcc )
3109         swimAcc *= maxAcc / swimSpeed;      
3110
3111      acc += swimAcc;
3112
3113      mContactTimer++;
3114   }
3115   else
3116      mContactTimer++;   
3117
3118   // Acceleration from Jumping
3119   // While BLOCK_USER_CONTROL is set in anim_clip_flags, the user won't be able to
3120   // make the player character jump.
3121   if (move->trigger[sJumpTrigger] && canJump() && !isAnimationLocked())
3122   {
3123      // Scale the jump impulse base on maxJumpSpeed
3124      F32 zSpeedScale = mVelocity.z;
3125      if (zSpeedScale <= mDataBlock->maxJumpSpeed)
3126      {
3127         zSpeedScale = (zSpeedScale <= mDataBlock->minJumpSpeed)? 1:
3128            1 - (zSpeedScale - mDataBlock->minJumpSpeed) /
3129            (mDataBlock->maxJumpSpeed - mDataBlock->minJumpSpeed);
3130
3131         // Desired jump direction
3132         VectorF pv = moveVec;
3133         F32 len = pv.len();
3134         if (len > 0)
3135            pv *= 1 / len;
3136
3137         // We want to scale the jump size by the player size, somewhat
3138         // in reduced ratio so a smaller player can jump higher in
3139         // proportion to his size, than a larger player.
3140         F32 scaleZ = (getScale().z * 0.25) + 0.75;
3141
3142         // Calculate our jump impulse
3143         F32 impulse = mDataBlock->jumpForce / getMass();
3144
3145         if (mDataBlock->jumpTowardsNormal)
3146         {
3147            // If we are facing into the surface jump up, otherwise
3148            // jump away from surface.
3149            F32 dot = mDot(pv,mJumpSurfaceNormal);
3150            if (dot <= 0)
3151               acc.z += mJumpSurfaceNormal.z * scaleZ * impulse * zSpeedScale;
3152            else
3153            {
3154               acc.x += pv.x * impulse * dot;
3155               acc.y += pv.y * impulse * dot;
3156               acc.z += mJumpSurfaceNormal.z * scaleZ * impulse * zSpeedScale;
3157            }
3158         }
3159         else
3160            acc.z += scaleZ * impulse * zSpeedScale;
3161
3162         mJumpDelay = mDataBlock->jumpDelay;
3163         mEnergy -= mDataBlock->jumpEnergyDrain;
3164
3165         // If we don't have a StandJumpAnim, just play the JumpAnim...
3166         S32 seq = (mVelocity.len() < 0.5) ? PlayerData::StandJumpAnim: PlayerData::JumpAnim; 
3167         if ( mDataBlock->actionList[seq].sequence == -1 )
3168            seq = PlayerData::JumpAnim;         
3169         setActionThread( seq, true, false, true );
3170
3171         mJumpSurfaceLastContact = JumpSkipContactsMax;
3172         // Flag the jump event trigger.
3173         fx_s_triggers |= PLAYER_JUMP_S_TRIGGER;
3174         setMaskBits(TriggerMask);
3175      }
3176   }
3177   else
3178   {
3179      if (jumpSurface) 
3180      {
3181         if (mJumpDelay > 0)
3182            mJumpDelay--;
3183         mJumpSurfaceLastContact = 0;
3184      }
3185      else
3186         mJumpSurfaceLastContact++;
3187   }
3188
3189   if (move->trigger[sJumpJetTrigger] && !isMounted() && canJetJump())
3190   {
3191      mJetting = true;
3192
3193      // Scale the jump impulse base on maxJumpSpeed
3194      F32 zSpeedScale = mVelocity.z;
3195
3196      if (zSpeedScale <= mDataBlock->jetMaxJumpSpeed)
3197      {
3198         zSpeedScale = (zSpeedScale <= mDataBlock->jetMinJumpSpeed)? 1:
3199         1 - (zSpeedScale - mDataBlock->jetMinJumpSpeed) / (mDataBlock->jetMaxJumpSpeed - mDataBlock->jetMinJumpSpeed);
3200
3201         // Desired jump direction
3202         VectorF pv = moveVec;
3203         F32 len = pv.len();
3204
3205         if (len > 0.0f)
3206            pv *= 1 / len;
3207
3208         // If we are facing into the surface jump up, otherwise
3209         // jump away from surface.
3210         F32 dot = mDot(pv,mJumpSurfaceNormal);
3211         F32 impulse = mDataBlock->jetJumpForce / getMass();
3212
3213         if (dot <= 0)
3214            acc.z += mJumpSurfaceNormal.z * impulse * zSpeedScale;
3215         else
3216         {
3217            acc.x += pv.x * impulse * dot;
3218            acc.y += pv.y * impulse * dot;
3219            acc.z += mJumpSurfaceNormal.z * impulse * zSpeedScale;
3220         }
3221
3222         mEnergy -= mDataBlock->jetJumpEnergyDrain;
3223      }
3224   }
3225   else
3226   {
3227      mJetting = false;
3228   }
3229
3230   // Add in force from physical zones...
3231   acc += (mAppliedForce / getMass()) * TickSec;
3232
3233   // Adjust velocity with all the move & gravity acceleration
3234   // TG: I forgot why doesn't the TickSec multiply happen here...
3235   mVelocity += acc;
3236
3237   // apply horizontal air resistance
3238
3239   F32 hvel = mSqrt(mVelocity.x * mVelocity.x + mVelocity.y * mVelocity.y);
3240
3241   if(hvel > mDataBlock->horizResistSpeed)
3242   {
3243      F32 speedCap = hvel;
3244      if(speedCap > mDataBlock->horizMaxSpeed)
3245         speedCap = mDataBlock->horizMaxSpeed;
3246      speedCap -= mDataBlock->horizResistFactor * TickSec * (speedCap - mDataBlock->horizResistSpeed);
3247      F32 scale = speedCap / hvel;
3248      mVelocity.x *= scale;
3249      mVelocity.y *= scale;
3250   }
3251   if(mVelocity.z > mDataBlock->upResistSpeed)
3252   {
3253      if(mVelocity.z > mDataBlock->upMaxSpeed)
3254         mVelocity.z = mDataBlock->upMaxSpeed;
3255      mVelocity.z -= mDataBlock->upResistFactor * TickSec * (mVelocity.z - mDataBlock->upResistSpeed);
3256   }
3257
3258   // Apply drag
3259   if ( mSwimming )
3260      mVelocity -= mVelocity * mDrag * TickSec * ( mVelocity.len() / mDataBlock->maxUnderwaterForwardSpeed );
3261   else
3262      mVelocity -= mVelocity * mDrag * TickSec;
3263
3264   // Clamp very small velocity to zero
3265   if ( mVelocity.isZero() )
3266      mVelocity = Point3F::Zero;
3267
3268   // If we are not touching anything and have sufficient -z vel,
3269   // we are falling.
3270   if (runSurface)
3271      mFalling = false;
3272   else
3273   {
3274      VectorF vel;
3275      mWorldToObj.mulV(mVelocity,&vel);
3276      mFalling = vel.z < mDataBlock->fallingSpeedThreshold;
3277   }
3278   
3279   // Vehicle Dismount   
3280   if ( !isGhost() && move->trigger[sVehicleDismountTrigger] && canJump())
3281      mDataBlock->doDismount_callback( this );
3282
3283   // Enter/Leave Liquid
3284   if ( !mInWater && mWaterCoverage > 0.0f ) 
3285   {      
3286      mInWater = true;
3287
3288      if ( !isGhost() )
3289         mDataBlock->onEnterLiquid_callback( this, mWaterCoverage, mLiquidType.c_str() );
3290   }
3291   else if ( mInWater && mWaterCoverage <= 0.0f ) 
3292   {      
3293      mInWater = false;
3294
3295      if ( !isGhost() )
3296         mDataBlock->onLeaveLiquid_callback( this, mLiquidType.c_str() );
3297      else
3298      {
3299         // exit-water splash sound happens for client only
3300         if ( getSpeed() >= mDataBlock->exitSplashSoundVel && !isMounted() )         
3301            SFX->playOnce( mDataBlock->sound[PlayerData::ExitWater], &getTransform() );                     
3302      }
3303   }
3304
3305   // Update the PlayerPose
3306   Pose desiredPose = mPose;
3307
3308   if ( !mIsAiControlled )
3309   {
3310      if ( mSwimming )
3311         desiredPose = SwimPose; 
3312      else if ( runSurface && move->trigger[sCrouchTrigger] && canCrouch() )     
3313         desiredPose = CrouchPose;
3314      else if ( runSurface && move->trigger[sProneTrigger] && canProne() )
3315         desiredPose = PronePose;
3316      else if ( move->trigger[sSprintTrigger] && canSprint() )
3317         desiredPose = SprintPose;
3318      else if ( canStand() )
3319         desiredPose = StandPose;
3320
3321      setPose( desiredPose );
3322   }
3323}
3324
3325
3326//----------------------------------------------------------------------------
3327
3328bool Player::checkDismountPosition(const MatrixF& oldMat, const MatrixF& mat)
3329{
3330   AssertFatal(getContainer() != NULL, "Error, must have a container!");
3331   AssertFatal(getObjectMount() != NULL, "Error, must be mounted!");
3332
3333   Point3F pos;
3334   Point3F oldPos;
3335   mat.getColumn(3, &pos);
3336   oldMat.getColumn(3, &oldPos);
3337   RayInfo info;
3338   disableCollision();
3339   getObjectMount()->disableCollision();
3340   if (getContainer()->castRay(oldPos, pos, sCollisionMoveMask, &info))
3341   {
3342      enableCollision();
3343      getObjectMount()->enableCollision();
3344      return false;
3345   }
3346
3347   Box3F wBox = mObjBox;
3348   wBox.minExtents += pos;
3349   wBox.maxExtents += pos;
3350
3351   EarlyOutPolyList polyList;
3352   polyList.mNormal.set(0.0f, 0.0f, 0.0f);
3353   polyList.mPlaneList.clear();
3354   polyList.mPlaneList.setSize(6);
3355   polyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
3356   polyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
3357   polyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
3358   polyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
3359   polyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
3360   polyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
3361
3362   if (getContainer()->buildPolyList(PLC_Collision, wBox, sCollisionMoveMask, &polyList))
3363   {
3364      enableCollision();
3365      getObjectMount()->enableCollision();
3366      return false;
3367   }
3368
3369   enableCollision();
3370   getObjectMount()->enableCollision();
3371   return true;
3372}
3373
3374
3375//----------------------------------------------------------------------------
3376
3377bool Player::canJump()
3378{
3379   return mAllowJumping && mState == MoveState && mDamageState == Enabled && !isMounted() && !mJumpDelay && mEnergy >= mDataBlock->minJumpEnergy && mJumpSurfaceLastContact < JumpSkipContactsMax && !mSwimming && (mPose != SprintPose || mDataBlock->sprintCanJump);
3380}
3381
3382bool Player::canJetJump()
3383{
3384   return mAllowJetJumping && mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->jetMinJumpEnergy && mDataBlock->jetJumpForce != 0.0f;
3385}
3386
3387bool Player::canSwim()
3388{  
3389   // Not used!
3390   //return mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->minSwimEnergy && mWaterCoverage >= 0.8f;
3391   return mAllowSwimming;
3392}
3393
3394bool Player::canCrouch()
3395{
3396   if (!mAllowCrouching)
3397      return false;
3398
3399   if ( mState != MoveState || 
3400        mDamageState != Enabled || 
3401        isMounted() || 
3402        mSwimming ||
3403        mFalling )
3404      return false;
3405
3406   // Can't crouch if no crouch animation!
3407   if ( mDataBlock->actionList[PlayerData::CrouchRootAnim].sequence == -1 )
3408      return false;       
3409
3410   // We are already in this pose, so don't test it again...
3411   if ( mPose == CrouchPose )
3412      return true;
3413
3414   // Do standard Torque physics test here!
3415   if ( !mPhysicsRep )
3416   {
3417      F32 radius;
3418
3419      if ( mPose == PronePose )
3420         radius = mDataBlock->proneBoxSize.z;
3421      else
3422         return true;
3423
3424      // use our X and Y dimentions on our boxsize as the radii for our search, and the difference between a standing position
3425      // and the position we currently are in.
3426      Point3F extent( mDataBlock->crouchBoxSize.x / 2, mDataBlock->crouchBoxSize.y / 2, mDataBlock->crouchBoxSize.z - radius );
3427
3428      Point3F position = getPosition();
3429      position.z += radius;
3430
3431      // Use these radii to create a box that represents the difference between a standing position and the position
3432      // we want to move into.
3433      Box3F B(position - extent, position + extent, true);
3434
3435      EarlyOutPolyList polyList;
3436      polyList.mPlaneList.clear();
3437      polyList.mNormal.set( 0,0,0 );
3438      polyList.mPlaneList.setSize( 6 );
3439      polyList.mPlaneList[0].set( B.minExtents, VectorF( -1,0,0 ) );
3440      polyList.mPlaneList[1].set( B.maxExtents, VectorF( 0,1,0 ) );
3441      polyList.mPlaneList[2].set( B.maxExtents, VectorF( 1,0,0 ) );
3442      polyList.mPlaneList[3].set( B.minExtents, VectorF( 0,-1,0 ) );
3443      polyList.mPlaneList[4].set( B.minExtents, VectorF( 0,0,-1 ) );
3444      polyList.mPlaneList[5].set( B.maxExtents, VectorF( 0,0,1 ) );
3445
3446      // If an object exists in this space, we must stay prone. Otherwise we are free to crouch.
3447      return !getContainer()->buildPolyList( PLC_Collision, B, StaticShapeObjectType, &polyList );
3448   }
3449
3450   return mPhysicsRep->testSpacials( getPosition(), mDataBlock->crouchBoxSize );
3451}
3452
3453bool Player::canStand()
3454{   
3455   if ( mState != MoveState || 
3456        mDamageState != Enabled || 
3457        isMounted() || 
3458        mSwimming )
3459      return false;
3460
3461   // We are already in this pose, so don't test it again...
3462   if ( mPose == StandPose )
3463      return true;
3464
3465   // Do standard Torque physics test here!
3466   if ( !mPhysicsRep )
3467   {
3468      F32 radius;
3469
3470      if (mPose == CrouchPose)
3471         radius = mDataBlock->crouchBoxSize.z;
3472      else if (mPose == PronePose)
3473         radius = mDataBlock->proneBoxSize.z;
3474      else
3475         return true;
3476
3477      // use our X and Y dimentions on our boxsize as the radii for our search, and the difference between a standing position
3478      // and the position we currently are in.
3479      Point3F extent( mDataBlock->boxSize.x / 2, mDataBlock->boxSize.y / 2, mDataBlock->boxSize.z - radius );
3480
3481      Point3F position = getPosition();
3482      position.z += radius;
3483
3484      // Use these radii to create a box that represents the difference between a standing position and the position
3485      // we want to move into.
3486      Box3F B(position - extent, position + extent, true);
3487
3488      EarlyOutPolyList polyList;
3489      polyList.mPlaneList.clear();
3490      polyList.mNormal.set(0,0,0);
3491      polyList.mPlaneList.setSize(6);
3492      polyList.mPlaneList[0].set(B.minExtents, VectorF(-1,0,0));
3493      polyList.mPlaneList[1].set(B.maxExtents, VectorF(0,1,0));
3494      polyList.mPlaneList[2].set(B.maxExtents, VectorF(1,0,0));
3495      polyList.mPlaneList[3].set(B.minExtents, VectorF(0,-1,0));
3496      polyList.mPlaneList[4].set(B.minExtents, VectorF(0,0,-1));
3497      polyList.mPlaneList[5].set(B.maxExtents, VectorF(0,0,1));
3498
3499      // If an object exists in this space, we must stay crouched/prone. Otherwise we are free to stand.
3500      return !getContainer()->buildPolyList(PLC_Collision, B, StaticShapeObjectType, &polyList);
3501   }
3502
3503   return mPhysicsRep->testSpacials( getPosition(), mDataBlock->boxSize );
3504}
3505
3506bool Player::canProne()
3507{
3508   if (!mAllowProne)
3509      return false;
3510
3511   if ( mState != MoveState || 
3512        mDamageState != Enabled || 
3513        isMounted() || 
3514        mSwimming ||
3515        mFalling )
3516      return false;
3517
3518   // Can't go prone if no prone animation!
3519   if ( mDataBlock->actionList[PlayerData::ProneRootAnim].sequence == -1 )
3520      return false;
3521
3522   // Do standard Torque physics test here!
3523   if ( !mPhysicsRep )
3524      return true;
3525
3526   // We are already in this pose, so don't test it again...
3527   if ( mPose == PronePose )
3528      return true;
3529
3530   return mPhysicsRep->testSpacials( getPosition(), mDataBlock->proneBoxSize );
3531}
3532
3533bool Player::canSprint()
3534{
3535   return mAllowSprinting && mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->minSprintEnergy && !mSwimming;
3536}
3537
3538//----------------------------------------------------------------------------
3539
3540void Player::updateDamageLevel()
3541{
3542   if (!isGhost())
3543      setDamageState((mDamage >= mDataBlock->maxDamage)? Disabled: Enabled);
3544   if (mDamageThread)
3545      mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel);
3546}
3547
3548void Player::updateDamageState()
3549{
3550   // Become a corpse when we're disabled (dead).
3551   if (mDamageState == Enabled) {
3552      mTypeMask &= ~<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a5816f1c71a46a07c1bb0c090da20941b">CorpseObjectType</a>;
3553      mTypeMask |= PlayerObjectType;
3554   }
3555   else {
3556      mTypeMask &= ~<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084aabc4c763464f732b143d331ec4c15dce">PlayerObjectType</a>;
3557      mTypeMask |= CorpseObjectType;
3558   }
3559
3560   Parent::updateDamageState();
3561}
3562
3563
3564//----------------------------------------------------------------------------
3565
3566void Player::updateLookAnimation(F32 dt)
3567{
3568   // If the preference setting overrideLookAnimation is true, the player's
3569   // arm and head no longer animate according to the view direction. They
3570   // are instead given fixed positions.
3571   if (overrideLookAnimation)
3572   {
3573      if (mArmAnimation.thread) 
3574         mShapeInstance->setPos(mArmAnimation.thread, armLookOverridePos);
3575      if (mHeadVThread) 
3576         mShapeInstance->setPos(mHeadVThread, headVLookOverridePos);
3577      if (mHeadHThread) 
3578         mShapeInstance->setPos(mHeadHThread, headHLookOverridePos);
3579      return;
3580   }
3581   // Calculate our interpolated head position.
3582   Point3F renderHead = mDelta.head + mDelta.headVec * dt;
3583
3584   // Adjust look pos.  This assumes that the animations match
3585   // the min and max look angles provided in the datablock.
3586   if (mArmAnimation.thread) 
3587   {
3588      if(mControlObject)
3589      {
3590         mShapeInstance->setPos(mArmAnimation.thread,0.5f);
3591      }
3592      else
3593      {
3594         F32 d = mDataBlock->maxLookAngle - mDataBlock->minLookAngle;
3595         F32 tp = (renderHead.x - mDataBlock->minLookAngle) / d;
3596         mShapeInstance->setPos(mArmAnimation.thread,mClampF(tp,0,1));
3597      }
3598   }
3599   
3600   if (mHeadVThread) 
3601   {
3602      F32 d = mDataBlock->maxLookAngle - mDataBlock->minLookAngle;
3603      F32 tp = (renderHead.x - mDataBlock->minLookAngle) / d;
3604      mShapeInstance->setPos(mHeadVThread,mClampF(tp,0,1));
3605   }
3606   
3607   if (mHeadHThread) 
3608   {
3609      F32 d = 2 * mDataBlock->maxFreelookAngle;
3610      F32 tp = (renderHead.z + mDataBlock->maxFreelookAngle) / d;
3611      mShapeInstance->setPos(mHeadHThread,mClampF(tp,0,1));
3612   }
3613}
3614
3615
3616//----------------------------------------------------------------------------
3617// Methods to get delta (as amount to affect velocity by)
3618
3619bool Player::inDeathAnim()
3620{
3621   if ((anim_clip_flags & ANIM_OVERRIDDEN) != 0 && (anim_clip_flags & IS_DEATH_ANIM) == 0)
3622      return false;
3623   if (mActionAnimation.thread && mActionAnimation.action >= 0)
3624      if (mActionAnimation.action < mDataBlock->actionCount)
3625         return mDataBlock->actionList[mActionAnimation.action].death;
3626
3627   return false;
3628}
3629
3630// Get change from mLastDeathPos - return current pos.  Assumes we're in death anim.
3631F32 Player::deathDelta(Point3F & delta)
3632{
3633   // Get ground delta from the last time we offset this.
3634   MatrixF  mat;
3635   F32 pos = mShapeInstance->getPos(mActionAnimation.thread);
3636   mShapeInstance->deltaGround1(mActionAnimation.thread, mDeath.lastPos, pos, mat);
3637   mat.getColumn(3, & delta);
3638   return pos;
3639}
3640
3641// Called before updatePos() to prepare it's needed change to velocity, which
3642// must roll over.  Should be updated on tick, this is where we remember last
3643// position of animation that was used to roll into velocity.
3644void Player::updateDeathOffsets()
3645{
3646   if (inDeathAnim())
3647      // Get ground delta from the last time we offset this.
3648      mDeath.lastPos = deathDelta(mDeath.posAdd);
3649   else
3650      mDeath.clear();
3651}
3652
3653
3654//----------------------------------------------------------------------------
3655
3656// PATHSHAPE
3657static const U32 sPlayerConformMask =  StaticShapeObjectType | StaticObjectType |
3658                                       TerrainObjectType | PathShapeObjectType;
3659// PATHSHAPE END
3660
3661static void accel(F32& from, F32 to, F32 rate)
3662{
3663   if (from < to)
3664      from = getMin(from += rate, to);
3665   else
3666      from = getMax(from -= rate, to);
3667}
3668
3669// if (dt == -1)
3670//    normal tick, so we advance.
3671// else
3672//    interpolate with dt as % of tick, don't advance
3673//
3674MatrixF * Player::Death::fallToGround(F32 dt, const Point3F& loc, F32 curZ, F32 boxRad)
3675{
3676   static const F32 sConformCheckDown = 4.0f;
3677   RayInfo     coll;
3678   bool        conformToStairs = false;
3679   Point3F     pos(loc.x, loc.y, loc.z + 0.1f);
3680   Point3F     below(pos.x, pos.y, loc.z - sConformCheckDown);
3681   MatrixF  *  retVal = NULL;
3682
3683   PROFILE_SCOPE(ConformToGround);
3684
3685   if (gClientContainer.castRay(pos, below, sPlayerConformMask, &coll))
3686   {
3687      F32      adjust, height = (loc.z - coll.point.z), sink = curSink;
3688      VectorF  desNormal = coll.normal;
3689      VectorF  normal = curNormal;
3690
3691      // dt >= 0 means we're interpolating and don't accel the numbers
3692      if (dt >= 0.0f)
3693         adjust = dt * TickSec;
3694      else
3695         adjust = TickSec;
3696
3697      // Try to get them to conform to stairs by doing several LOS calls.  We do this if
3698      // normal is within about 5 deg. of vertical.
3699      if (desNormal.z > 0.995f)
3700      {
3701         Point3F  corners[3], downpts[3];
3702         S32      c;
3703
3704         for (c = 0; c < 3; c++) {    // Build 3 corners to cast down from-
3705            corners[c].set(loc.x - boxRad, loc.y - boxRad, loc.z + 1.0f);
3706            if (c)      // add (0,boxWidth) and (boxWidth,0)
3707               corners[c][c - 1] += (boxRad * 2.0f);
3708            downpts[c].set(corners[c].x, corners[c].y, loc.z - sConformCheckDown);
3709         }
3710
3711         // Do the three casts-
3712         for (c = 0; c < 3; c++)
3713            if (gClientContainer.castRay(corners[c], downpts[c], sPlayerConformMask, &coll))
3714               downpts[c] = coll.point;
3715            else
3716               break;
3717
3718         // Do the math if everything hit below-
3719         if (c == 3) {
3720            mCross(downpts[1] - downpts[0], downpts[2] - downpts[1], &desNormal);
3721            AssertFatal(desNormal.z > 0, "Abnormality in Player::Death::fallToGround()");
3722            downpts[2] = downpts[2] - downpts[1];
3723            downpts[1] = downpts[1] - downpts[0];
3724            desNormal.normalize();
3725            conformToStairs = true;
3726         }
3727      }
3728
3729      // Move normal in direction we want-
3730      F32   * cur = normal, * des = desNormal;
3731      for (S32 i = 0; i < 3; i++)
3732         accel(*cur++, *des++, adjust * 0.25f);
3733
3734      if (mFabs(height) < 2.2f && !normal.isZero() && desNormal.z > 0.01f)
3735      {
3736         VectorF  upY(0.0f, 1.0f, 0.0f), ahead;
3737         VectorF  sideVec;
3738         MatrixF  mat(true);
3739
3740         normal.normalize();
3741         mat.set(EulerF (0.0f, 0.0f, curZ));
3742         mat.mulV(upY, & ahead);
3743         mCross(ahead, normal, &sideVec);
3744         sideVec.normalize();
3745         mCross(normal, sideVec, &ahead);
3746
3747         static MatrixF resMat(true);
3748         resMat.setColumn(0, sideVec);
3749         resMat.setColumn(1, ahead);
3750         resMat.setColumn(2, normal);
3751
3752         // Adjust Z down to account for box offset on slope.  Figure out how
3753         // much we want to sink, and gradually accel to this amount.  Don't do if
3754         // we're conforming to stairs though
3755         F32   xy = mSqrt(desNormal.x * desNormal.x + desNormal.y * desNormal.y);
3756         F32   desiredSink = (boxRad * xy / desNormal.z);
3757         if (conformToStairs)
3758            desiredSink *= 0.5f;
3759
3760         accel(sink, desiredSink, adjust * 0.15f);
3761
3762         Point3F  position(pos);
3763         position.z -= sink;
3764         resMat.setColumn(3, position);
3765
3766         if (dt < 0.0f)
3767         {  // we're moving, so update normal and sink amount
3768            curNormal = normal;
3769            curSink = sink;
3770         }
3771
3772         retVal = &resMat;
3773      }
3774   }
3775   return retVal;
3776}
3777
3778
3779//-------------------------------------------------------------------------------------
3780
3781// This is called ::onAdd() to see if we're in a sitting animation.  These then
3782// can use a longer tick delay for the mount to get across.
3783bool Player::inSittingAnim()
3784{
3785   U32   action = mActionAnimation.action;
3786   if (mActionAnimation.thread && action < mDataBlock->actionCount) {
3787      const char * name = mDataBlock->actionList[action].name;
3788      if (!dStricmp(name, "Sitting") || !dStricmp(name, "Scoutroot"))
3789         return true;
3790   }
3791   return false;
3792}
3793
3794
3795//----------------------------------------------------------------------------
3796
3797const String& Player::getArmThread() const
3798{
3799   if (mArmAnimation.thread && mArmAnimation.thread->hasSequence())
3800   {
3801      return mArmAnimation.thread->getSequenceName();
3802   }
3803
3804   return String::EmptyString;
3805}
3806
3807bool Player::setArmThread(const char* sequence)
3808{
3809   // The arm sequence must be in the action list.
3810   for (U32 i = 1; i < mDataBlock->actionCount; i++)
3811      if (!dStricmp(mDataBlock->actionList[i].name,sequence))
3812         return setArmThread(i);
3813   return false;
3814}
3815
3816bool Player::setArmThread(U32 action)
3817{
3818   PlayerData::ActionAnimation &anim = mDataBlock->actionList[action];
3819   if (anim.sequence != -1 &&
3820      anim.sequence != mShapeInstance->getSequence(mArmAnimation.thread))
3821   {
3822      mShapeInstance->setSequence(mArmAnimation.thread,anim.sequence,0);
3823      mArmAnimation.action = action;
3824      setMaskBits(ActionMask);
3825      return true;
3826   }
3827   return false;
3828}
3829
3830
3831//----------------------------------------------------------------------------
3832
3833bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp)
3834{
3835   if (anim_clip_flags & ANIM_OVERRIDDEN)
3836      return false;
3837   for (U32 i = 1; i < mDataBlock->actionCount; i++)
3838   {
3839      PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
3840      if (!dStricmp(anim.name,sequence))
3841      {
3842         setActionThread(i,true,hold,wait,fsp);
3843         setMaskBits(ActionMask);
3844         return true;
3845      }
3846   }
3847   return false;
3848}
3849
3850void Player::setActionThread(U32 action,bool forward,bool hold,bool wait,bool fsp, bool forceSet)
3851{
3852   if (!mDataBlock || !mDataBlock->actionCount || (mActionAnimation.action == action && mActionAnimation.forward == forward && !forceSet))
3853      return;
3854
3855   if (action >= PlayerData::NumActionAnims)
3856   {
3857      Con::errorf("Player::setActionThread(%d): Player action out of range", action);
3858      return;
3859   }
3860
3861   if (isClientObject())
3862   {
3863      mark_idle = (action == PlayerData::RootAnim);
3864      idle_timer = (mark_idle) ? 0.0f : -1.0f;
3865   }
3866   PlayerData::ActionAnimation &anim = mDataBlock->actionList[action];
3867   if (anim.sequence != -1)
3868   {
3869      U32 lastAction = mActionAnimation.action;
3870
3871      mActionAnimation.action          = action;
3872      mActionAnimation.forward         = forward;
3873      mActionAnimation.firstPerson     = fsp;
3874      mActionAnimation.holdAtEnd       = hold;
3875      mActionAnimation.waitForEnd      = hold? true: wait;
3876      mActionAnimation.animateOnServer = fsp;
3877      mActionAnimation.atEnd           = false;
3878      mActionAnimation.delayTicks      = (S32)sNewAnimationTickTime;
3879      mActionAnimation.atEnd           = false;
3880
3881      if (sUseAnimationTransitions && (action != PlayerData::LandAnim || !(mDataBlock->landSequenceTime > 0.0f && !mDataBlock->transitionToLand)) && (isGhost()/* || mActionAnimation.animateOnServer*/))
3882      {
3883         // The transition code needs the timeScale to be set in the
3884         // right direction to know which way to go.
3885         F32   transTime = sAnimationTransitionTime;
3886         if (mDataBlock && mDataBlock->isJumpAction(action))
3887            transTime = 0.15f;
3888
3889         F32 timeScale = mActionAnimation.forward ? 1.0f : -1.0f;
3890         if (mDataBlock && mDataBlock->isJumpAction(action))
3891            timeScale *= 1.5f;
3892
3893         mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale);
3894
3895         S32 seq = anim.sequence;
3896         S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
3897         if (imageBasedSeq != -1)
3898            seq = imageBasedSeq;
3899
3900         // If we're transitioning into the same sequence (an action may use the
3901         // same sequence as a previous action) then we want to start at the same
3902         // position.
3903         F32 pos = mActionAnimation.forward ? 0.0f : 1.0f;
3904         PlayerData::ActionAnimation &lastAnim = mDataBlock->actionList[lastAction];
3905         if (lastAnim.sequence == anim.sequence)
3906         {
3907            pos = mShapeInstance->getPos(mActionAnimation.thread);
3908         }
3909
3910         mShapeInstance->transitionToSequence(mActionAnimation.thread,seq,
3911            pos, transTime, true);
3912      }
3913      else
3914      {
3915         S32 seq = anim.sequence;
3916         S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
3917         if (imageBasedSeq != -1)
3918            seq = imageBasedSeq;
3919
3920         mShapeInstance->setSequence(mActionAnimation.thread,seq,
3921            mActionAnimation.forward ? 0.0f : 1.0f);
3922      }
3923   }
3924}
3925
3926void Player::updateActionThread()
3927{
3928   PROFILE_START(UpdateActionThread);
3929
3930   // Select an action animation sequence, this assumes that
3931   // this function is called once per tick.
3932   if(mActionAnimation.action != PlayerData::NullAnimation)
3933   {
3934      if (mActionAnimation.forward)
3935         mActionAnimation.atEnd = mShapeInstance->getPos(mActionAnimation.thread) == 1;
3936      else
3937         mActionAnimation.atEnd = mShapeInstance->getPos(mActionAnimation.thread) == 0;
3938   }
3939    
3940   // Only need to deal with triggers on the client
3941   if( isGhost() )
3942   {
3943      bool triggeredLeft = false;
3944      bool triggeredRight = false;
3945      
3946      F32 offset = 0.0f;
3947      if( mShapeInstance->getTriggerState( 1 ) )
3948      {
3949         triggeredLeft = true;
3950         offset = -mDataBlock->decalOffset * getScale().x;
3951      }
3952      else if(mShapeInstance->getTriggerState( 2 ) )
3953      {
3954         triggeredRight = true;
3955         offset = mDataBlock->decalOffset * getScale().x;
3956      }
3957
3958      process_client_triggers(triggeredLeft, triggeredRight);
3959      if ((triggeredLeft || triggeredRight) && !noFootfallFX)
3960      {
3961         Point3F rot, pos;
3962         RayInfo rInfo;
3963         MatrixF mat = getRenderTransform();
3964         mat.getColumn( 1, &rot );
3965         mat.mulP( Point3F( offset, 0.0f, 0.0f), &pos );
3966
3967         if( gClientContainer.castRay( Point3F( pos.x, pos.y, pos.z + 0.01f ),
3968               Point3F( pos.x, pos.y, pos.z - 2.0f ),
3969               STATIC_COLLISION_TYPEMASK | VehicleObjectType, &rInfo ) )
3970         {
3971            Material* material = ( rInfo.material ? dynamic_cast< Material* >( rInfo.material->getMaterial() ) : 0 );
3972
3973            // Put footprints on surface, if appropriate for material.
3974
3975            if( material && material->mShowFootprints
3976                && mDataBlock->decalData && !footfallDecalOverride )
3977            {
3978               Point3F normal;
3979               Point3F tangent;
3980               mObjToWorld.getColumn( 0, &tangent );
3981               mObjToWorld.getColumn( 2, &normal );
3982               gDecalManager->addDecal( rInfo.point, normal, tangent, mDataBlock->decalData, getScale().y );
3983            }
3984            
3985            // Emit footpuffs.
3986
3987            if (!footfallDustOverride && rInfo.t <= 0.5f && mWaterCoverage == 0.0f
3988                                         && material && material->mShowDust )
3989            {
3990               // New emitter every time for visibility reasons
3991               ParticleEmitter * emitter = new ParticleEmitter;
3992               emitter->onNewDataBlock( mDataBlock->footPuffEmitter, false );
3993
3994               LinearColorF colorList[ ParticleData::PDC_NUM_KEYS];
3995
3996               for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x )
3997                  colorList[ x ].set( material->mEffectColor[ x ].red,
3998                                      material->mEffectColor[ x ].green,
3999                                      material->mEffectColor[ x ].blue,
4000                                      material->mEffectColor[ x ].alpha );
4001               for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x )
4002                  colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 );
4003
4004               emitter->setColors( colorList );
4005               if( !emitter->registerObject() )
4006               {
4007                  Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
4008                  delete emitter;
4009                  emitter = NULL;
4010               }
4011               else
4012               {
4013                  emitter->emitParticles( pos, Point3F( 0.0, 0.0, 1.0 ), mDataBlock->footPuffRadius,
4014                     Point3F( 0, 0, 0 ), mDataBlock->footPuffNumParts );
4015                  emitter->deleteWhenEmpty();
4016               }
4017            }
4018
4019            // Play footstep sound.
4020            
4021            if (footfallSoundOverride <= 0)
4022            playFootstepSound( triggeredLeft, material, rInfo.object );
4023         }
4024      }
4025   }
4026
4027   // Mount pending variable puts a hold on the delayTicks below so players don't
4028   // inadvertently stand up because their mount has not come over yet.
4029   if (mMountPending)
4030      mMountPending = (isMounted() ? 0 : (mMountPending - 1));
4031
4032   if ((mActionAnimation.action == PlayerData::NullAnimation) ||
4033       ((!mActionAnimation.waitForEnd || mActionAnimation.atEnd) &&
4034       (!mActionAnimation.holdAtEnd && (mActionAnimation.delayTicks -= !mMountPending) <= 0)))
4035   {
4036      //The scripting language will get a call back when a script animation has finished...
4037      //  example: When the chat menu animations are done playing...
4038      if ( isServerObject() && mActionAnimation.action >= PlayerData::NumTableActionAnims )
4039         mDataBlock->animationDone_callback( this );
4040      pickActionAnimation();
4041   }
4042
4043   // prevent scaling of AFX picked actions
4044   if ( (mActionAnimation.action != PlayerData::LandAnim) &&
4045        (mActionAnimation.action != PlayerData::NullAnimation) &&
4046        !(anim_clip_flags & ANIM_OVERRIDDEN))
4047   {
4048      // Update action animation time scale to match ground velocity
4049      PlayerData::ActionAnimation &anim =
4050         mDataBlock->actionList[mActionAnimation.action];
4051      F32 scale = 1;
4052      if (anim.velocityScale && anim.speed) {
4053         VectorF vel;
4054         mWorldToObj.mulV(mVelocity,&vel);
4055         scale = mFabs(mDot(vel, anim.dir) / anim.speed);
4056
4057         if (scale > mDataBlock->maxTimeScale)
4058            scale = mDataBlock->maxTimeScale;
4059      }
4060
4061      mShapeInstance->setTimeScale(mActionAnimation.thread,
4062                                   mActionAnimation.forward? scale: -scale);
4063   }
4064   PROFILE_END();
4065}
4066
4067void Player::pickBestMoveAction(U32 startAnim, U32 endAnim, U32 * action, bool * forward) const
4068{
4069   *action = startAnim;
4070   *forward = false;
4071
4072   VectorF vel;
4073   mWorldToObj.mulV(mVelocity,&vel);
4074
4075   if (vel.lenSquared() > 0.01f)
4076   {
4077      // Bias the velocity towards picking the forward/backward anims over
4078      // the sideways ones to prevent oscillation between anims.
4079      vel *= VectorF(0.5f, 1.0f, 0.5f);
4080
4081      // Pick animation that is the best fit for our current (local) velocity.
4082      // Assumes that the root (stationary) animation is at startAnim.
4083      F32 curMax = -0.1f;
4084      for (U32 i = startAnim+1; i <= endAnim; i++)
4085      {
4086         const PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
4087         if (anim.sequence != -1 && anim.speed) 
4088         {
4089            F32 d = mDot(vel, anim.dir);
4090            if (d > curMax)
4091            {
4092               curMax = d;
4093               *action = i;
4094               *forward = true;
4095            }
4096            else
4097            {
4098               // Check if reversing this animation would fit (bias against this
4099               // so that when moving right, the real right anim is still chosen,
4100               // but if not present, the reversed left anim will be used instead)
4101               d *= -0.75f;
4102               if (d > curMax)
4103               {
4104                  curMax = d;
4105                  *action = i;
4106                  *forward = false;
4107               }
4108            }
4109         }
4110      }
4111   }
4112}
4113
4114void Player::pickActionAnimation()
4115{
4116   // Only select animations in our normal move state.
4117   if (mState != MoveState || mDamageState != Enabled)
4118      return;
4119
4120   if (isMounted() || mMountPending)
4121   {
4122      // Go into root position unless something was set explicitly
4123      // from a script.
4124      if (mActionAnimation.action != PlayerData::RootAnim &&
4125          mActionAnimation.action < PlayerData::NumTableActionAnims)
4126         setActionThread(PlayerData::RootAnim,true,false,false);
4127      return;
4128   }
4129
4130   bool forward = true;
4131   U32 action = PlayerData::RootAnim;
4132   bool fsp = false;
4133   
4134   // Jetting overrides the fall animation condition
4135   if (mJetting)
4136   {
4137      // Play the jetting animation
4138      action = PlayerData::JetAnim;
4139   }
4140   else if (mFalling)
4141   {
4142      // Not in contact with any surface and falling
4143      action = PlayerData::FallAnim;
4144   }
4145   else if ( mSwimming )
4146   {
4147      pickBestMoveAction(PlayerData::SwimRootAnim, PlayerData::SwimRightAnim, &action, &forward);
4148   }
4149   else if ( mPose == StandPose )
4150   {
4151      if (mContactTimer >= sContactTickTime)
4152      {
4153         // Nothing under our feet
4154         action = PlayerData::RootAnim;
4155      }
4156      else
4157      {
4158         // Our feet are on something
4159         pickBestMoveAction(PlayerData::RootAnim, PlayerData::SideRightAnim, &action, &forward);
4160      }
4161   }
4162   else if ( mPose == CrouchPose )
4163   {
4164      pickBestMoveAction(PlayerData::CrouchRootAnim, PlayerData::CrouchRightAnim, &action, &forward);
4165   }
4166   else if ( mPose == PronePose )
4167   {
4168      pickBestMoveAction(PlayerData::ProneRootAnim, PlayerData::ProneBackwardAnim, &action, &forward);
4169   }
4170   else if ( mPose == SprintPose )
4171   {
4172      pickBestMoveAction(PlayerData::SprintRootAnim, PlayerData::SprintRightAnim, &action, &forward);
4173   }
4174   setActionThread(action,forward,false,false,fsp);
4175}
4176
4177void Player::onImage(U32 imageSlot, bool unmount)
4178{
4179   // Update 3rd person sequences based on images used.  Start be getting a
4180   // list of all possible image prefix sequences.
4181   String prefixPaths[ShapeBase::MaxMountedImages];
4182   buildImagePrefixPaths(prefixPaths);
4183
4184   // Clear out any previous image state animation
4185   if (mImageStateThread)
4186   {
4187      mShapeInstance->destroyThread(mImageStateThread);
4188      mImageStateThread = 0;
4189   }
4190
4191   // Attempt to update the action thread
4192   U32 action = mActionAnimation.action;
4193   if (action != PlayerData::NullAnimation)
4194   {
4195      String actionSeq = mDataBlock->actionList[action].name;
4196      if (actionSeq.isNotEmpty())
4197      {
4198         S32 seqIndex = mDataBlock->actionList[action].sequence;
4199         S32 prefixIndex = findPrefixSequence(prefixPaths, actionSeq);
4200         if (prefixIndex != -1)
4201         {
4202            seqIndex = prefixIndex;
4203         }
4204
4205         // Only change the sequence if it isn't already playing.
4206         if (seqIndex != mShapeInstance->getSequence(mActionAnimation.thread))
4207         {
4208            F32 pos = mShapeInstance->getPos(mActionAnimation.thread);
4209            mShapeInstance->setSequence(mActionAnimation.thread, seqIndex, pos);
4210         }
4211      }
4212   }
4213
4214   // Attempt to update the arm thread
4215   U32 armAction = getArmAction();
4216   if (armAction != PlayerData::NullAnimation)
4217   {
4218      String armSeq = mDataBlock->actionList[armAction].name;
4219      if (armSeq.isNotEmpty())
4220      {
4221         S32 seqIndex = mDataBlock->actionList[armAction].sequence;
4222         S32 prefixIndex = findPrefixSequence(prefixPaths, armSeq);
4223         if (prefixIndex != -1)
4224         {
4225            seqIndex = prefixIndex;
4226         }
4227
4228         // Only change the sequence if it isn't already playing.
4229         if (seqIndex != mShapeInstance->getSequence(mArmAnimation.thread))
4230         {
4231            F32 pos = mShapeInstance->getPos(mArmAnimation.thread);
4232            mShapeInstance->setSequence(mArmAnimation.thread, seqIndex, pos);
4233         }
4234      }
4235   }
4236
4237   // Attempt to update the head threads
4238   if (mHeadVThread)
4239   {
4240      TSShape const* shape = mShapeInstance->getShape();
4241      S32 seqIndex = shape->findSequence("head");
4242      S32 prefixIndex = findPrefixSequence(prefixPaths, "head");
4243      if (prefixIndex != -1)
4244      {
4245         seqIndex = prefixIndex;
4246      }
4247
4248      // Only change the sequence if it isn't already playing.
4249      if (seqIndex != mShapeInstance->getSequence(mHeadVThread))
4250      {
4251         F32 pos = mShapeInstance->getPos(mHeadVThread);
4252         mShapeInstance->setSequence(mHeadVThread, seqIndex, pos);
4253      }
4254   }
4255
4256   if (mHeadHThread)
4257   {
4258      TSShape const* shape = mShapeInstance->getShape();
4259      S32 seqIndex = shape->findSequence("headside");
4260      S32 prefixIndex = findPrefixSequence(prefixPaths, "headside");
4261      if (prefixIndex != -1)
4262      {
4263         seqIndex = prefixIndex;
4264      }
4265
4266      // Only change the sequence if it isn't already playing.
4267      if (seqIndex != mShapeInstance->getSequence(mHeadHThread))
4268      {
4269         F32 pos = mShapeInstance->getPos(mHeadHThread);
4270         mShapeInstance->setSequence(mHeadHThread, seqIndex, pos);
4271      }
4272   }
4273}
4274
4275void Player::buildImagePrefixPaths(String* prefixPaths)
4276{
4277   // We begin obtaining the anim prefix for each image.
4278   String prefix[ShapeBase::MaxMountedImages];
4279   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
4280   {
4281      MountedImage& image = mMountedImageList[i];
4282      if (image.dataBlock && image.dataBlock->imageAnimPrefix && image.dataBlock->imageAnimPrefix[0])
4283      {
4284         prefix[i] = String(image.dataBlock->imageAnimPrefix);
4285      }
4286   }
4287
4288   // Build out the full prefix names we will be searching for.
4289   S32 counter = ShapeBase::MaxMountedImages-1;
4290   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
4291   {
4292      // Only build out the prefix path for images that have a defined prefix.
4293      if (prefix[i].isNotEmpty())
4294      {
4295         bool start = true;
4296         for (U32 j=0; j<=<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>; ++j)
4297         {
4298            if (prefix[j].isNotEmpty())
4299            {
4300               if (!start)
4301               {
4302                  prefixPaths[counter] += "_";
4303               }
4304               else
4305               {
4306                  start = false;
4307               }
4308               prefixPaths[counter] += prefix[j];
4309            }
4310         }
4311      }
4312
4313      -- counter;
4314   }
4315}
4316S32 Player::findPrefixSequence(String* prefixPaths, const String& baseSeq)
4317{
4318   // Go through the prefix list.  If we find a match then return the sequence
4319   // index.
4320   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
4321   {
4322      if (prefixPaths[i].isNotEmpty())
4323      {
4324         String seq = prefixPaths[i] + "_" + baseSeq;
4325         S32 seqIndex = mShapeInstance->getShape()->findSequence(seq);
4326         if (seqIndex != -1)
4327         {
4328            return seqIndex;
4329         }
4330      }
4331   }
4332
4333   return -1;
4334}
4335
4336S32 Player::convertActionToImagePrefix(U32 action)
4337{
4338   String prefixPaths[ShapeBase::MaxMountedImages];
4339   buildImagePrefixPaths(prefixPaths);
4340
4341   if (action != PlayerData::NullAnimation)
4342   {
4343      String actionSeq;
4344      S32 seq = -1;
4345
4346      // We'll first attempt to find the action sequence by name
4347      // as defined within the action list.
4348      actionSeq = mDataBlock->actionList[action].name;
4349      if (actionSeq.isNotEmpty())
4350      {
4351         seq = findPrefixSequence(prefixPaths, actionSeq);
4352      }
4353
4354      if (seq == -1)
4355      {
4356         // Couldn't find a valid sequence.  If this is a sprint action
4357         // then we also need to search through the standard movement
4358         // sequences.
4359         if (action >= PlayerData::SprintRootAnim && action <= PlayerData::SprintRightAnim)
4360         {
4361            U32 standardAction = action - PlayerData::SprintRootAnim;
4362            actionSeq = mDataBlock->actionList[standardAction].name;
4363            if (actionSeq.isNotEmpty())
4364            {
4365               seq = findPrefixSequence(prefixPaths, actionSeq);
4366            }
4367         }
4368      }
4369
4370      return seq;
4371   }
4372
4373   return -1;
4374}
4375
4376void Player::onImageRecoil( U32, ShapeBaseImageData::StateData::RecoilState state )
4377{
4378   if ( mRecoilThread )
4379   {
4380      if ( state != ShapeBaseImageData::StateData::NoRecoil )
4381      {
4382         S32 stateIndex = state - ShapeBaseImageData::StateData::LightRecoil;
4383         if ( mDataBlock->recoilSequence[stateIndex] != -1 )
4384         {
4385            mShapeInstance->setSequence( mRecoilThread, mDataBlock->recoilSequence[stateIndex], 0 );
4386            mShapeInstance->setTimeScale( mRecoilThread, 1 );
4387         }
4388      }
4389   }
4390}
4391
4392void Player::onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue)
4393{
4394   if (mDataBlock->allowImageStateAnimation && isGhost())
4395   {
4396      MountedImage& image = mMountedImageList[imageSlot];
4397
4398      // Just as with onImageAnimThreadChange we're going to apply various prefixes to determine the final sequence to use.
4399      // Here is the order:
4400      // imageBasePrefix_scriptPrefix_baseAnimName
4401      // imageBasePrefix_baseAnimName
4402      // scriptPrefix_baseAnimName
4403      // baseAnimName
4404
4405      // Collect the prefixes
4406      const char* imageBasePrefix = "";
4407      bool hasImageBasePrefix = image.dataBlock && image.dataBlock->imageAnimPrefix && image.dataBlock->imageAnimPrefix[0];
4408      if (hasImageBasePrefix)
4409         imageBasePrefix = image.dataBlock->imageAnimPrefix;
4410      const char* scriptPrefix = getImageScriptAnimPrefix(imageSlot).getString();
4411      bool hasScriptPrefix = scriptPrefix && scriptPrefix[0];
4412
4413      S32 seqIndex = mShapeInstance->getShape()->findSequence(seqName);
4414
4415      // Find the final sequence based on the prefix combinations
4416      if (hasImageBasePrefix || hasScriptPrefix)
4417      {
4418         bool found = false;
4419         String baseSeqName(seqName);
4420
4421         if (!found && hasImageBasePrefix && hasScriptPrefix)
4422         {
4423            String comboSeqName = String(imageBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseSeqName;
4424            S32 index = mShapeInstance->getShape()->findSequence(comboSeqName);
4425            if (index != -1)
4426            {
4427               seqIndex = index;
4428               found = true;
4429            }
4430         }
4431
4432         if (!found && hasImageBasePrefix)
4433         {
4434            String imgSeqName = String(imageBasePrefix) + String("_") + baseSeqName;
4435            S32 index = mShapeInstance->getShape()->findSequence(imgSeqName);
4436            if (index != -1)
4437            {
4438               seqIndex = index;
4439               found = true;
4440            }
4441         }
4442
4443         if (!found && hasScriptPrefix)
4444         {
4445            String scriptSeqName = String(scriptPrefix) + String("_") + baseSeqName;
4446            S32 index = mShapeInstance->getShape()->findSequence(scriptSeqName);
4447            if (index != -1)
4448            {
4449               seqIndex = index;
4450               found = true;
4451            }
4452         }
4453      }
4454
4455      if (seqIndex != -1)
4456      {
4457         if (!mImageStateThread)
4458         {
4459            mImageStateThread = mShapeInstance->addThread();
4460         }
4461
4462         mShapeInstance->setSequence( mImageStateThread, seqIndex, 0 );
4463
4464         F32 timeScale = (scaleToState && stateTimeOutValue) ?
4465            mShapeInstance->getDuration(mImageStateThread) / stateTimeOutValue : 1.0f;
4466
4467         mShapeInstance->setTimeScale( mImageStateThread, direction ? timeScale : -timeScale );
4468      }
4469   }
4470}
4471
4472const char* Player::getImageAnimPrefix(U32 imageSlot, S32 imageShapeIndex)
4473{
4474   if (!mDataBlock)
4475      return "";
4476
4477   switch (imageShapeIndex)
4478   {
4479      case ShapeBaseImageData::StandardImageShape:
4480      {
4481         return mDataBlock->imageAnimPrefix;
4482      }
4483
4484      case ShapeBaseImageData::FirstPersonImageShape:
4485      {
4486         return mDataBlock->imageAnimPrefixFP;
4487      }
4488
4489      default:
4490      {
4491         return "";
4492      }
4493   }
4494}
4495
4496void Player::onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset)
4497{
4498   if (!mShapeFPInstance[imageSlot] || !mShapeFPAnimThread[imageSlot])
4499      return;
4500
4501   MountedImage& image = mMountedImageList[imageSlot];
4502   ShapeBaseImageData::StateData& stateData = *image.state;
4503
4504   if (reset)
4505   {
4506      // Reset cyclic sequences back to the first frame to turn it off
4507      // (the first key frame should be it's off state).
4508      if (mShapeFPAnimThread[imageSlot]->getSequence()->isCyclic() && (stateData.sequenceNeverTransition || !(stateData.sequenceTransitionIn || (lastState && lastState->sequenceTransitionOut))) )
4509      {
4510         mShapeFPInstance[imageSlot]->setPos(mShapeFPAnimThread[imageSlot],0);
4511         mShapeFPInstance[imageSlot]->setTimeScale(mShapeFPAnimThread[imageSlot],0);
4512      }
4513
4514      return;
4515   }
4516
4517   // Just as with ShapeBase::udpateAnimThread we're going to apply various prefixes to determine the final sequence to use.
4518   // Here is the order:
4519   // imageBasePrefix_scriptPrefix_baseAnimName
4520   // imageBasePrefix_baseAnimName
4521   // scriptPrefix_baseAnimName
4522   // baseAnimName
4523
4524   // Collect the prefixes
4525   const char* imageBasePrefix = "";
4526   bool hasImageBasePrefix = image.dataBlock && image.dataBlock->imageAnimPrefixFP && image.dataBlock->imageAnimPrefixFP[0];
4527   if (hasImageBasePrefix)
4528      imageBasePrefix = image.dataBlock->imageAnimPrefixFP;
4529   const char* scriptPrefix = getImageScriptAnimPrefix(imageSlot).getString();
4530   bool hasScriptPrefix = scriptPrefix && scriptPrefix[0];
4531
4532   S32 seqIndex = mShapeFPInstance[imageSlot]->getShape()->findSequence(anim);
4533
4534   // Find the final sequence based on the prefix combinations
4535   if (hasImageBasePrefix || hasScriptPrefix)
4536   {
4537      bool found = false;
4538      String baseSeqName(anim);
4539
4540      if (!found && hasImageBasePrefix && hasScriptPrefix)
4541      {
4542         String seqName = String(imageBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseSeqName;
4543         S32 index = mShapeFPInstance[imageSlot]->getShape()->findSequence(seqName);
4544         if (index != -1)
4545         {
4546            seqIndex = index;
4547            found = true;
4548         }
4549      }
4550
4551      if (!found && hasImageBasePrefix)
4552      {
4553         String seqName = String(imageBasePrefix) + String("_") + baseSeqName;
4554         S32 index = mShapeFPInstance[imageSlot]->getShape()->findSequence(seqName);
4555         if (index != -1)
4556         {
4557            seqIndex = index;
4558            found = true;
4559         }
4560      }
4561
4562      if (!found && hasScriptPrefix)
4563      {
4564         String seqName = String(scriptPrefix) + String("_") + baseSeqName;
4565         S32 index = mShapeFPInstance[imageSlot]->getShape()->findSequence(seqName);
4566         if (index != -1)
4567         {
4568            seqIndex = index;
4569            found = true;
4570         }
4571      }
4572   }
4573
4574   if (seqIndex != -1)
4575   {
4576      if (!lastState)
4577      {
4578         // No lastState indicates that we are just switching animation sequences, not states.  Transition into this new sequence, but only
4579         // if it is different than what we're currently playing.
4580         S32 prevSeq = -1;
4581         if (mShapeFPAnimThread[imageSlot]->hasSequence())
4582         {
4583            prevSeq = mShapeFPInstance[imageSlot]->getSequence(mShapeFPAnimThread[imageSlot]);
4584         }
4585         if (seqIndex != prevSeq)
4586         {
4587            mShapeFPInstance[imageSlot]->transitionToSequence(mShapeFPAnimThread[imageSlot], seqIndex, pos, image.dataBlock->scriptAnimTransitionTime, true);
4588         }
4589      }
4590      else if (!stateData.sequenceNeverTransition && stateData.sequenceTransitionTime && (stateData.sequenceTransitionIn || (lastState && lastState->sequenceTransitionOut)) )
4591      {
4592         mShapeFPInstance[imageSlot]->transitionToSequence(mShapeFPAnimThread[imageSlot], seqIndex, pos, stateData.sequenceTransitionTime, true);
4593      }
4594      else
4595      {
4596         mShapeFPInstance[imageSlot]->setSequence(mShapeFPAnimThread[imageSlot], seqIndex, pos);
4597      }
4598      mShapeFPInstance[imageSlot]->setTimeScale(mShapeFPAnimThread[imageSlot], timeScale);
4599   }
4600}
4601
4602void Player::onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt)
4603{
4604   if (!mShapeFPInstance[imageSlot])
4605      return;
4606
4607   if (mShapeFPAmbientThread[imageSlot] && mShapeFPAmbientThread[imageSlot]->hasSequence())
4608   {
4609      mShapeFPInstance[imageSlot]->advanceTime(dt,mShapeFPAmbientThread[imageSlot]);
4610   }
4611
4612   if (mShapeFPAnimThread[imageSlot] && mShapeFPAnimThread[imageSlot]->hasSequence())
4613   {
4614      mShapeFPInstance[imageSlot]->advanceTime(dt,mShapeFPAnimThread[imageSlot]);
4615   }
4616}
4617
4618void Player::onUnmount( SceneObject *obj, S32 node )
4619{
4620   // Reset back to root position during dismount.
4621   setActionThread(PlayerData::RootAnim,true,false,false);
4622
4623   // Re-orient the player straight up
4624   Point3F pos,vec;
4625   getTransform().getColumn(1,&vec);
4626   getTransform().getColumn(3,&pos);
4627   Point3F rot(0.0f,0.0f,-mAtan2(-vec.x,vec.y));
4628   setPosition(pos,rot);
4629
4630   // Parent function will call script
4631   Parent::onUnmount( obj, node );
4632}
4633
4634void Player::unmount()
4635{
4636   // Reset back to root position during dismount.  This copies what is
4637   // done on the server and corrects the fact that the RootAnim change
4638   // is not sent across to the client using the standard ActionMask.
4639   setActionThread(PlayerData::RootAnim,true,false,false);
4640
4641   Parent::unmount();
4642}
4643
4644
4645//----------------------------------------------------------------------------
4646
4647void Player::updateAnimation(F32 dt)
4648{
4649   // If dead then remove any image animations
4650   if ((mDamageState == Disabled || mDamageState == Destroyed) && mImageStateThread)
4651   {
4652      // Remove the image state animation
4653      mShapeInstance->destroyThread(mImageStateThread);
4654      mImageStateThread = 0;
4655   }
4656
4657   if ((isGhost() || mActionAnimation.animateOnServer) && mActionAnimation.thread)
4658      mShapeInstance->advanceTime(dt,mActionAnimation.thread);
4659   if (mRecoilThread)
4660      mShapeInstance->advanceTime(dt,mRecoilThread);
4661   if (mImageStateThread)
4662      mShapeInstance->advanceTime(dt,mImageStateThread);
4663
4664   // update any active blend clips
4665   if (isGhost())
4666      for (S32 i = 0; i < blend_clips.size(); i++)
4667         mShapeInstance->advanceTime(dt, blend_clips[i].thread);
4668   // If we are the client's player on this machine, then we need
4669   // to make sure the transforms are up to date as they are used
4670   // to setup the camera.
4671   if (isGhost())
4672   {
4673      if (getControllingClient())
4674      {
4675         updateAnimationTree(isFirstPerson());
4676         mShapeInstance->animate();
4677      }
4678      else
4679      {
4680         updateAnimationTree(false);
4681         // This addition forces recently visible players to animate their
4682         // skeleton now rather than in pre-render so that constrained effects
4683         // get up-to-date node transforms.
4684         if (didRenderLastRender())
4685            mShapeInstance->animate();
4686      }
4687   }
4688}
4689
4690void Player::updateAnimationTree(bool firstPerson)
4691{
4692   S32 mode = 0;
4693   if (firstPerson)
4694   {
4695      if (mActionAnimation.firstPerson)
4696         mode = 0;
4697//            TSShapeInstance::MaskNodeRotation;
4698//            TSShapeInstance::MaskNodePosX |
4699//            TSShapeInstance::MaskNodePosY;
4700      else
4701         mode = TSShapeInstance::MaskNodeAllButBlend;
4702   }
4703   for (U32 i = 0; i < PlayerData::NumSpineNodes; i++)
4704      if (mDataBlock->spineNode[i] != -1)
4705         mShapeInstance->setNodeAnimationState(mDataBlock->spineNode[i],mode);
4706}
4707
4708
4709//----------------------------------------------------------------------------
4710
4711bool Player::step(Point3F *pos,F32 *maxStep,F32 time)
4712{
4713   const Point3F& scale = getScale();
4714   Box3F box;
4715   VectorF offset = mVelocity * time;
4716   box.minExtents = mObjBox.minExtents + offset + *pos;
4717   box.maxExtents = mObjBox.maxExtents + offset + *pos;
4718   box.maxExtents.z += mDataBlock->maxStepHeight * scale.z + sMinFaceDistance;
4719
4720   SphereF sphere;
4721   sphere.center = (box.minExtents + box.maxExtents) * 0.5f;
4722   VectorF bv = box.maxExtents - sphere.center;
4723   sphere.radius = bv.len();
4724
4725   ClippedPolyList polyList;
4726   polyList.mPlaneList.clear();
4727   polyList.mNormal.set(0.0f, 0.0f, 0.0f);
4728   polyList.mPlaneList.setSize(6);
4729   polyList.mPlaneList[0].set(box.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
4730   polyList.mPlaneList[1].set(box.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
4731   polyList.mPlaneList[2].set(box.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
4732   polyList.mPlaneList[3].set(box.minExtents,VectorF(0.0f, -1.0f, 0.0f));
4733   polyList.mPlaneList[4].set(box.minExtents,VectorF(0.0f, 0.0f, -1.0f));
4734   polyList.mPlaneList[5].set(box.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
4735
4736   CollisionWorkingList& rList = mConvex.getWorkingList();
4737   CollisionWorkingList* pList = rList.wLink.mNext;
4738   while (pList != &rList) {
4739      Convex* pConvex = pList->mConvex;
4740      
4741      // Alright, here's the deal... a polysoup mesh really needs to be 
4742      // designed with stepping in mind.  If there are too many smallish polygons
4743      // the stepping system here gets confused and allows you to run up walls 
4744      // or on the edges/seams of meshes.
4745
4746      TSStatic *st = dynamic_cast<TSStatic *> (pConvex->getObject());
4747      bool skip = false;
4748      if (st && !st->allowPlayerStep())
4749         skip = true;
4750
4751      if ((pConvex->getObject()->getTypeMask() & StaticObjectType) != 0 && !skip)
4752      {
4753         Box3F convexBox = pConvex->getBoundingBox();
4754         if (box.isOverlapped(convexBox))
4755            pConvex->getPolyList(&polyList);
4756      }
4757      pList = pList->wLink.mNext;
4758   }
4759
4760   // Find max step height
4761   F32 stepHeight = pos->z - sMinFaceDistance;
4762   U32* vp = polyList.mIndexList.begin();
4763   U32* ep = polyList.mIndexList.end();
4764   for (; vp != ep; vp++) {
4765      F32 h = polyList.mVertexList[*vp].point.z + sMinFaceDistance;
4766      if (h > stepHeight)
4767         stepHeight = h;
4768   }
4769
4770   F32 step = stepHeight - pos->z;
4771   if (stepHeight > pos->z && step < *maxStep) {
4772      // Go ahead and step
4773      pos->z = stepHeight;
4774      *maxStep -= step;
4775      return true;
4776   }
4777
4778   return false;
4779}
4780
4781// PATHSHAPE
4782// This Function does a ray cast down to see if a pathshape object is below
4783// If so, it will attempt to attach to it.
4784void Player::updateAttachment(){
4785   Point3F rot, pos;
4786    RayInfo rInfo;
4787    MatrixF mat = getTransform();
4788    mat.getColumn(3, &pos);
4789    if (gServerContainer.castRay(Point3F(pos.x, pos.y, pos.z + 0.1f),
4790        Point3F(pos.x, pos.y, pos.z - 1.0f ),
4791        PathShapeObjectType, &rInfo))
4792    {
4793       if( rInfo.object->getTypeMask() & PathShapeObjectType) //Ramen
4794       {
4795          if (getParent() == NULL)
4796          { // ONLY do this if we are not parented
4797             //Con::printf("I'm on a pathshape object. Going to attempt attachment.");
4798             ShapeBase* col = static_cast<ShapeBase*>(rInfo.object);
4799             if (!isGhost())
4800             {
4801                this->attachToParent(col);
4802             } 
4803          }
4804       }
4805       else
4806       {
4807          //Con::printf("object %i",rInfo.object->getId());
4808       }
4809    }
4810    else
4811    {  
4812       if (getParent() !=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>)
4813       {
4814          clearProcessAfter();
4815          attachToParent(NULL);
4816       }
4817    }
4818}
4819// PATHSHAPE END
4820
4821//----------------------------------------------------------------------------
4822inline Point3F createInterpPos(const Point3F& s, const Point3F& e, const F32 t, const F32 d)
4823{
4824   Point3F ret;
4825   ret.interpolate(s, e, t/d);
4826   return ret;
4827}
4828
4829Point3F Player::_move( const F32 travelTime, Collision *outCol )
4830{
4831   // Try and move to new pos
4832   F32 totalMotion  = 0.0f;
4833   
4834   // TODO: not used?
4835   //F32 initialSpeed = mVelocity.len();
4836
4837   Point3F start;
4838   Point3F initialPosition;
4839   getTransform().getColumn(3,&start);
4840   initialPosition = start;
4841
4842   static CollisionList collisionList;
4843   static CollisionList physZoneCollisionList;
4844
4845   collisionList.clear();
4846   physZoneCollisionList.clear();
4847
4848   MatrixF collisionMatrix(true);
4849   collisionMatrix.setColumn(3, start);
4850
4851   VectorF firstNormal(0.0f, 0.0f, 0.0f);
4852   F32 maxStep = mDataBlock->maxStepHeight;
4853   F32 time = travelTime;
4854   U32 count = 0;
4855
4856   const Point3F& scale = getScale();
4857
4858   static Polyhedron sBoxPolyhedron;
4859   static ExtrudedPolyList sExtrudedPolyList;
4860   static ExtrudedPolyList sPhysZonePolyList;
4861
4862   for (; count < sMoveRetryCount; count++) {
4863      F32 speed = mVelocity.len();
4864      if (!speed && !mDeath.haveVelocity())
4865         break;
4866
4867      Point3F end = start + mVelocity * time;
4868      if (mDeath.haveVelocity()) {
4869         // Add in death movement-
4870         VectorF  deathVel = mDeath.getPosAdd();
4871         VectorF  resVel;
4872         getTransform().mulV(deathVel, & resVel);
4873         end += resVel;
4874      }
4875      Point3F distance = end - start;
4876
4877      if (mFabs(distance.x) < mScaledBox.len_x() &&
4878          mFabs(distance.y) < mScaledBox.len_y() &&
4879          mFabs(distance.z) < mScaledBox.len_z())
4880      {
4881         // We can potentially early out of this.  If there are no polys in the clipped polylist at our
4882         //  end position, then we can bail, and just set start = end;
4883         Box3F wBox = mScaledBox;
4884         wBox.minExtents += end;
4885         wBox.maxExtents += end;
4886
4887         static EarlyOutPolyList eaPolyList;
4888         eaPolyList.clear();
4889         eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
4890         eaPolyList.mPlaneList.clear();
4891         eaPolyList.mPlaneList.setSize(6);
4892         eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
4893         eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
4894         eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
4895         eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
4896         eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
4897         eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
4898
4899         // Build list from convex states here...
4900         CollisionWorkingList& rList = mConvex.getWorkingList();
4901         CollisionWorkingList* pList = rList.wLink.mNext;
4902         while (pList != &rList) {
4903            Convex* pConvex = pList->mConvex;
4904            if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
4905               Box3F convexBox = pConvex->getBoundingBox();
4906               if (wBox.isOverlapped(convexBox))
4907               {
4908                  // No need to separate out the physical zones here, we want those
4909                  //  to cause a fallthrough as well...
4910                  pConvex->getPolyList(&eaPolyList);
4911               }
4912            }
4913            pList = pList->wLink.mNext;
4914         }
4915
4916         if (eaPolyList.isEmpty())
4917         {
4918            totalMotion += (end - start).len();
4919            start = end;
4920            break;
4921         }
4922      }
4923
4924      collisionMatrix.setColumn(3, start);
4925      sBoxPolyhedron.buildBox(collisionMatrix, mScaledBox, true);
4926
4927      // Setup the bounding box for the extrudedPolyList
4928      Box3F plistBox = mScaledBox;
4929      collisionMatrix.mul(plistBox);
4930      Point3F oldMin = plistBox.minExtents;
4931      Point3F oldMax = plistBox.maxExtents;
4932      plistBox.minExtents.setMin(oldMin + (mVelocity * time) - Point3F(0.1f, 0.1f, 0.1f));
4933      plistBox.maxExtents.setMax(oldMax + (mVelocity * time) + Point3F(0.1f, 0.1f, 0.1f));
4934
4935      // Build extruded polyList...
4936      VectorF vector = end - start;
4937      sExtrudedPolyList.extrude(sBoxPolyhedron,vector);
4938      sExtrudedPolyList.setVelocity(mVelocity);
4939      sExtrudedPolyList.setCollisionList(&collisionList);
4940
4941      sPhysZonePolyList.extrude(sBoxPolyhedron,vector);
4942      sPhysZonePolyList.setVelocity(mVelocity);
4943      sPhysZonePolyList.setCollisionList(&physZoneCollisionList);
4944
4945      // Build list from convex states here...
4946      CollisionWorkingList& rList = mConvex.getWorkingList();
4947      CollisionWorkingList* pList = rList.wLink.mNext;
4948      while (pList != &rList) {
4949         Convex* pConvex = pList->mConvex;
4950         if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
4951            Box3F convexBox = pConvex->getBoundingBox();
4952            if (plistBox.isOverlapped(convexBox))
4953            {
4954               if (pConvex->getObject()->getTypeMask() & PhysicalZoneObjectType)
4955                  pConvex->getPolyList(&sPhysZonePolyList);
4956               else
4957                  pConvex->getPolyList(&sExtrudedPolyList);
4958            }
4959         }
4960         pList = pList->wLink.mNext;
4961      }
4962
4963      // Take into account any physical zones...
4964      for (U32 j = 0; j < physZoneCollisionList.getCount(); j++) 
4965      {
4966         AssertFatal(dynamic_cast<PhysicalZone*>(physZoneCollisionList[j].object), "Bad phys zone!");
4967         const PhysicalZone* pZone = (PhysicalZone*)physZoneCollisionList[j].object;
4968         if (pZone->isActive())
4969            mVelocity *= pZone->getVelocityMod();
4970      }
4971
4972      if (collisionList.getCount() != 0 && collisionList.getTime() < 1.0f) 
4973      {
4974         // Set to collision point
4975         F32 velLen = mVelocity.len();
4976
4977         F32 dt = time * getMin(collisionList.getTime(), 1.0f);
4978         start += mVelocity * dt;
4979         time -= dt;
4980
4981         totalMotion += velLen * dt;
4982
4983         bool wasFalling = mFalling;
4984         mFalling = false;
4985
4986         // Back off...
4987         if ( velLen > 0.f ) {
4988            F32 newT = getMin(0.01f / velLen, dt);
4989            start -= mVelocity * newT;
4990            totalMotion -= velLen * newT;
4991         }
4992
4993         // Try stepping if there is a vertical surface
4994         if (collisionList.getMaxHeight() < start.z + mDataBlock->maxStepHeight * scale.z) 
4995         {
4996            bool stepped = false;
4997            for (U32 c = 0; c < collisionList.getCount(); c++) 
4998            {
4999               const Collision& cp = collisionList[c];
5000               // if (mFabs(mDot(cp.normal,VectorF(0,0,1))) < sVerticalStepDot)
5001               //    Dot with (0,0,1) just extracts Z component [lh]-
5002               if (mFabs(cp.normal.z) < sVerticalStepDot)
5003               {
5004                  stepped = step(&start,&maxStep,time);
5005                  break;
5006               }
5007            }
5008            if (stepped)
5009            {
5010               continue;
5011            }
5012         }
5013
5014         // Pick the surface most parallel to the face that was hit.
5015         const Collision *collision = &collisionList[0];
5016         const Collision *cp = collision + 1;
5017         const Collision *ep = collision + collisionList.getCount();
5018         for (; cp != ep; cp++)
5019         {
5020            if (cp->faceDot > collision->faceDot)
5021               collision = cp;
5022         }
5023
5024         F32 bd = _doCollisionImpact( collision, wasFalling );
5025
5026         // Copy this collision out so
5027         // we can use it to do impacts
5028         // and query collision.
5029         *outCol = *collision;
5030         if (isServerObject() && bd > 6.8f && collision->normal.z > 0.7f)
5031         {
5032            fx_s_triggers |= PLAYER_LANDING_S_TRIGGER;
5033            setMaskBits(TriggerMask);
5034         }
5035
5036         // Subtract out velocity
5037         VectorF dv = collision->normal * (bd + sNormalElasticity);
5038         mVelocity += dv;
5039         if (count == 0)
5040         {
5041            firstNormal = collision->normal;
5042         }
5043         else
5044         {
5045            if (count == 1)
5046            {
5047               // Re-orient velocity along the crease.
5048               if (mDot(dv,firstNormal) < 0.0f &&
5049                   mDot(collision->normal,firstNormal) < 0.0f)
5050               {
5051                  VectorF nv;
5052                  mCross(collision->normal,firstNormal,&nv);
5053                  F32 nvl = nv.len();
5054                  if (nvl)
5055                  {
5056                     if (mDot(nv,mVelocity) < 0.0f)
5057                        nvl = -nvl;
5058                     nv *= mVelocity.len() / nvl;
5059                     mVelocity = nv;
5060                  }
5061               }
5062            }
5063         }
5064      }
5065      else
5066      {
5067         totalMotion += (end - start).len();
5068         start = end;
5069         break;
5070      }
5071   }
5072
5073   if (count == sMoveRetryCount)
5074   {
5075      // Failed to move
5076      start = initialPosition;
5077      mVelocity.set(0.0f, 0.0f, 0.0f);
5078   }
5079
5080   return start;
5081}
5082
5083F32 Player::_doCollisionImpact( const Collision *collision, bool fallingCollision)
5084{
5085   F32 bd = -mDot( mVelocity, collision->normal);
5086
5087   // shake camera on ground impact
5088   if( bd > mDataBlock->groundImpactMinSpeed && isControlObject() )
5089   {
5090      F32 ampScale = (bd - mDataBlock->groundImpactMinSpeed) / mDataBlock->minImpactSpeed;
5091
5092      CameraShake *groundImpactShake = new CameraShake;
5093      groundImpactShake->setDuration( mDataBlock->groundImpactShakeDuration );
5094      groundImpactShake->setFrequency( mDataBlock->groundImpactShakeFreq );
5095
5096      VectorF shakeAmp = mDataBlock->groundImpactShakeAmp * ampScale;
5097      groundImpactShake->setAmplitude( shakeAmp );
5098      groundImpactShake->setFalloff( mDataBlock->groundImpactShakeFalloff );
5099      groundImpactShake->init();
5100      gCamFXMgr.addFX( groundImpactShake );
5101   }
5102
5103   if ( ((bd > mDataBlock->minImpactSpeed && fallingCollision) || bd > mDataBlock->minLateralImpactSpeed) 
5104      && !mMountPending )
5105   {
5106      if ( !isGhost() )
5107         onImpact( collision->object, collision->normal * bd );
5108
5109      if (mDamageState == Enabled && mState != RecoverState) 
5110      {
5111         // Scale how long we're down for
5112         if (mDataBlock->landSequenceTime > 0.0f)
5113         {
5114            // Recover time is based on the land sequence
5115            setState(RecoverState);
5116         }
5117         else
5118         {
5119            // Legacy recover system
5120            F32   value = (bd - mDataBlock->minImpactSpeed);
5121            F32   range = (mDataBlock->minImpactSpeed * 0.9f);
5122            U32   recover = mDataBlock->recoverDelay;
5123            if (value < range)
5124               recover = 1 + S32(mFloor( F32(recover) * value / range) );
5125            //Con::printf("Used %d recover ticks", recover);
5126            //Con::printf("  minImpact = %g, this one = %g", mDataBlock->minImpactSpeed, bd);
5127            setState(RecoverState, recover);
5128         }
5129      }
5130   }
5131
5132   if ( isServerObject() && 
5133      (bd > (mDataBlock->minImpactSpeed / 3.0f) || bd > (mDataBlock->minLateralImpactSpeed / 3.0f )) ) 
5134   {
5135      mImpactSound = PlayerData::ImpactNormal;
5136      setMaskBits(ImpactMask);
5137   }
5138
5139   return bd;
5140}
5141
5142void Player::_handleCollision( const Collision &collision )
5143{
5144   // Track collisions
5145   if (  !isGhost() && 
5146         collision.object && 
5147         collision.object != mContactInfo.contactObject )
5148      queueCollision( collision.object, mVelocity - collision.object->getVelocity() );
5149}
5150
5151bool Player::updatePos(const F32 travelTime)
5152{
5153   PROFILE_SCOPE(Player_UpdatePos);
5154   getTransform().getColumn(3,&mDelta.posVec);
5155
5156   // When mounted to another object, only Z rotation used.
5157   if (isMounted()) {
5158      mVelocity = mMount.object->getVelocity();
5159      setPosition(Point3F(0.0f, 0.0f, 0.0f), mRot);
5160      setMaskBits(MoveMask);
5161      return true;
5162   }
5163
5164   Point3F newPos;
5165
5166   Collision col;
5167   dMemset( &col, 0, sizeof( col ) );
5168
5169   // DEBUG:
5170   //Point3F savedVelocity = mVelocity;
5171
5172   if ( mPhysicsRep )
5173   {
5174      static CollisionList collisionList;
5175      collisionList.clear();
5176
5177      newPos = mPhysicsRep->move( mVelocity * travelTime, collisionList );
5178
5179      bool haveCollisions = false;
5180      bool wasFalling = mFalling;
5181      if (collisionList.getCount() > 0)
5182      {
5183         mFalling = false;
5184         haveCollisions = true;
5185      }
5186
5187      if (haveCollisions)
5188      {
5189         // Pick the collision that most closely matches our direction
5190         VectorF velNormal = mVelocity;
5191         velNormal.normalizeSafe();
5192         const Collision *collision = &collisionList[0];
5193         F32 collisionDot = mDot(velNormal, collision->normal);
5194         const Collision *cp = collision + 1;
5195         const Collision *ep = collision + collisionList.getCount();
5196         for (; cp != ep; cp++)
5197         {
5198            F32 dp = mDot(velNormal, cp->normal);
5199            if (dp < collisionDot)
5200            {
5201               collisionDot = dp;
5202               collision = cp;
5203            }
5204         }
5205
5206         _doCollisionImpact( collision, wasFalling );
5207
5208         // Modify our velocity based on collisions
5209         for (U32 i=0; i<collisionList.getCount(); ++i)
5210         {
5211            F32 bd = -mDot( mVelocity, collisionList[i].normal );
5212            VectorF dv = collisionList[i].normal * (bd + sNormalElasticity);
5213            mVelocity += dv;
5214         }
5215
5216         // Store the last collision for use later on.  The handle collision
5217         // code only expects a single collision object.
5218         if (collisionList.getCount() > 0)
5219            col = collisionList[collisionList.getCount() - 1];
5220
5221         // We'll handle any player-to-player collision, and the last collision
5222         // with other obejct types.
5223         for (U32 i=0; i<collisionList.getCount(); ++i)
5224         {
5225            Collision& colCheck = collisionList[i];
5226            if (colCheck.object)
5227            {
5228               SceneObject* obj = static_cast<SceneObject*>(col.object);
5229               if (obj->getTypeMask() & PlayerObjectType)
5230               {
5231                  _handleCollision( colCheck );
5232               }
5233               else
5234               {
5235                  col = colCheck;
5236               }
5237            }
5238         }
5239
5240         _handleCollision( col );
5241      }
5242   }
5243   else
5244   {
5245      if ( mVelocity.isZero() )
5246         newPos = mDelta.posVec;
5247      else
5248         newPos = _move( travelTime, &col );
5249   
5250      _handleCollision( col );
5251   }
5252
5253   // DEBUG:
5254   //if ( isClientObject() )
5255   //   Con::printf( "(client) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z );
5256   //else
5257   //   Con::printf( "(server) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z );
5258
5259   // Set new position
5260   // If on the client, calc delta for backstepping
5261   if (isClientObject())
5262   {
5263      mDelta.pos = newPos;
5264     mDelta.posVec = mDelta.posVec - mDelta.pos;
5265     mDelta.dt = 1.0f;
5266   }
5267
5268   setPosition( newPos, mRot );
5269   setMaskBits( MoveMask );
5270   updateContainer();
5271
5272   if (!isGhost())  
5273   {
5274      // Collisions are only queued on the server and can be
5275      // generated by either updateMove or updatePos
5276      notifyCollision();
5277
5278      // Do mission area callbacks on the server as well
5279      checkMissionArea();
5280   }
5281
5282   // Check the total distance moved.  If it is more than 1000th of the velocity, then
5283   //  we moved a fair amount...
5284   //if (totalMotion >= (0.001f * initialSpeed))
5285      return true;
5286   //else
5287      //return false;
5288}
5289
5290
5291//----------------------------------------------------------------------------
5292 
5293void Player::_findContact( SceneObject **contactObject, 
5294                           VectorF *contactNormal, 
5295                           Vector<SceneObject*> *outOverlapObjects )
5296{
5297   Point3F pos;
5298   getTransform().getColumn(3,&pos);
5299
5300   Box3F wBox;
5301   Point3F exp(0,0,sTractionDistance);
5302   wBox.minExtents = pos + mScaledBox.minExtents - exp;
5303   wBox.maxExtents.x = pos.x + mScaledBox.maxExtents.x;
5304   wBox.maxExtents.y = pos.y + mScaledBox.maxExtents.y;
5305   wBox.maxExtents.z = pos.z + mScaledBox.minExtents.z + sTractionDistance;
5306
5307   static ClippedPolyList polyList;
5308   polyList.clear();
5309   polyList.doConstruct();
5310   polyList.mNormal.set(0.0f, 0.0f, 0.0f);
5311   polyList.setInterestNormal(Point3F(0.0f, 0.0f, -1.0f));
5312
5313   polyList.mPlaneList.setSize(6);
5314   polyList.mPlaneList[0].setYZ(wBox.minExtents, -1.0f);
5315   polyList.mPlaneList[1].setXZ(wBox.maxExtents, 1.0f);
5316   polyList.mPlaneList[2].setYZ(wBox.maxExtents, 1.0f);
5317   polyList.mPlaneList[3].setXZ(wBox.minExtents, -1.0f);
5318   polyList.mPlaneList[4].setXY(wBox.minExtents, -1.0f);
5319   polyList.mPlaneList[5].setXY(wBox.maxExtents, 1.0f);
5320   Box3F plistBox = wBox;
5321
5322   // Expand build box as it will be used to collide with items.
5323   // PickupRadius will be at least the size of the box.
5324   F32 pd = (F32)mDataBlock->pickupDelta;
5325   wBox.minExtents.x -= pd; wBox.minExtents.y -= pd;
5326   wBox.maxExtents.x += pd; wBox.maxExtents.y += pd;
5327   wBox.maxExtents.z = pos.z + mScaledBox.maxExtents.z;
5328
5329   // Build list from convex states here...
5330   CollisionWorkingList& rList = mConvex.getWorkingList();
5331   CollisionWorkingList* pList = rList.wLink.mNext;
5332   while (pList != &rList)
5333   {
5334      Convex* pConvex = pList->mConvex;
5335
5336      U32 objectMask = pConvex->getObject()->getTypeMask();
5337      
5338      if (  ( objectMask & sCollisionMoveMask ) &&
5339            !( objectMask & PhysicalZoneObjectType ) )
5340      {
5341         Box3F convexBox = pConvex->getBoundingBox();
5342         if (plistBox.isOverlapped(convexBox))
5343            pConvex->getPolyList(&polyList);
5344      }
5345      else
5346         outOverlapObjects->push_back( pConvex->getObject() );
5347
5348      pList = pList->wLink.mNext;
5349   }
5350
5351   if (!polyList.isEmpty())
5352   {
5353      // Pick flattest surface
5354      F32 bestVd = -1.0f;
5355      ClippedPolyList::Poly* poly = polyList.mPolyList.begin();
5356      ClippedPolyList::Poly* end = polyList.mPolyList.end();
5357      for (; poly != end; poly++)
5358      {
5359         F32 vd = poly->plane.z;       // i.e.  mDot(Point3F(0,0,1), poly->plane);
5360         if (vd > bestVd)
5361         {
5362            bestVd = vd;
5363            *contactObject = poly->object;
5364            *contactNormal = poly->plane;
5365         }
5366      }
5367   }
5368}
5369
5370void Player::findContact( bool *run, bool *jump, VectorF *contactNormal )
5371{
5372   SceneObject *contactObject = NULL;
5373
5374   Vector<SceneObject*> overlapObjects;
5375   if ( mPhysicsRep )
5376      mPhysicsRep->findContact( &contactObject, contactNormal, &overlapObjects );
5377   else
5378      _findContact( &contactObject, contactNormal, &overlapObjects );
5379
5380   // Check for triggers, corpses and items.
5381   const U32 filterMask = isGhost() ? sClientCollisionContactMask : sServerCollisionContactMask;
5382   for ( U32 i=0; i < overlapObjects.size(); i++ )
5383   {
5384      SceneObject *obj = overlapObjects[i];
5385      U32 objectMask = obj->getTypeMask();
5386
5387      if ( !( objectMask & filterMask ) )
5388         continue;
5389
5390      // Check: triggers, corpses and items...
5391      //
5392      if (objectMask & TriggerObjectType)
5393      {
5394         Trigger* pTrigger = static_cast<Trigger*>( obj );
5395         pTrigger->potentialEnterObject(this);
5396      }
5397      else if (objectMask & CorpseObjectType)
5398      {
5399         // If we've overlapped the worldbounding boxes, then that's it...
5400         if ( getWorldBox().isOverlapped( obj->getWorldBox() ) )
5401         {
5402            ShapeBase* col = static_cast<ShapeBase*>( obj );
5403            queueCollision(col,getVelocity() - col->getVelocity());
5404         }
5405      }
5406      else if (objectMask & ItemObjectType)
5407      {
5408         // If we've overlapped the worldbounding boxes, then that's it...
5409         Item* item = static_cast<Item*>( obj );
5410         if (  getWorldBox().isOverlapped(item->getWorldBox()) &&
5411               item->getCollisionObject() != this && 
5412               !item->isHidden() )
5413            queueCollision(item,getVelocity() - item->getVelocity());
5414      }
5415   }
5416
5417   F32 vd = (*contactNormal).z;
5418   *run = vd > mDataBlock->runSurfaceCos;
5419   *jump = vd > mDataBlock->jumpSurfaceCos;
5420
5421   mContactInfo.clear();
5422  
5423   mContactInfo.contacted = contactObject != NULL;
5424   mContactInfo.contactObject = contactObject;
5425
5426   if ( mContactInfo.contacted )
5427      mContactInfo.contactNormal = *contactNormal;
5428
5429   mContactInfo.run = *run;
5430   mContactInfo.jump = *jump;
5431}
5432
5433//----------------------------------------------------------------------------
5434
5435void Player::checkMissionArea()
5436{
5437   // Checks to see if the player is in the Mission Area...
5438   Point3F pos;
5439   MissionArea * obj = MissionArea::getServerObject();
5440
5441   if(!obj)
5442      return;
5443
5444   const RectI &area = obj->getArea();
5445   getTransform().getColumn(3, &pos);
5446
5447   if ((pos.x < area.point.x || pos.x > area.point.x + area.extent.x ||
5448       pos.y < area.point.y || pos.y > area.point.y + area.extent.y)) {
5449      if(mInMissionArea) {
5450         mInMissionArea = false;
5451         mDataBlock->onLeaveMissionArea_callback( this );
5452      }
5453   }
5454   else if(!mInMissionArea)
5455   {
5456      mInMissionArea = true;
5457      mDataBlock->onEnterMissionArea_callback( this );
5458   }
5459}
5460
5461
5462//----------------------------------------------------------------------------
5463
5464bool Player::isDisplacable() const
5465{
5466   return true;
5467}
5468
5469Point3F Player::getMomentum() const
5470{
5471   return mVelocity * getMass();
5472}
5473
5474void Player::setMomentum(const Point3F& newMomentum)
5475{
5476   Point3F newVelocity = newMomentum / getMass();
5477   mVelocity = newVelocity;
5478}
5479
5480#define  LH_HACK   1
5481// Hack for short-term soln to Training crash -
5482#if   LH_HACK
5483static U32  sBalance;
5484
5485bool Player::displaceObject(const Point3F& displacement)
5486{
5487   F32 vellen = mVelocity.len();
5488   if (vellen < 0.001f || sBalance > 16) {
5489      mVelocity.set(0.0f, 0.0f, 0.0f);
5490      return false;
5491   }
5492
5493   F32 dt = displacement.len() / vellen;
5494
5495   sBalance++;
5496
5497   bool result = updatePos(dt);
5498
5499   sBalance--;
5500
5501   getTransform().getColumn(3, &mDelta.pos);
5502   mDelta.posVec.set(0.0f, 0.0f, 0.0f);
5503
5504   return result;
5505}
5506
5507#else
5508
5509bool Player::displaceObject(const Point3F& displacement)
5510{
5511   F32 vellen = mVelocity.len();
5512   if (vellen < 0.001f) {
5513      mVelocity.set(0.0f, 0.0f, 0.0f);
5514      return false;
5515   }
5516
5517   F32 dt = displacement.len() / vellen;
5518
5519   bool result = updatePos(dt);
5520
5521   mObjToWorld.getColumn(3, &mDelta.pos);
5522   mDelta.posVec.set(0.0f, 0.0f, 0.0f);
5523
5524   return result;
5525}
5526
5527#endif
5528
5529//----------------------------------------------------------------------------
5530
5531void Player::setPosition(const Point3F& pos,const Point3F& rot)
5532{
5533   MatrixF mat;
5534   if (isMounted()) {
5535      // Use transform from mounted object
5536      //MatrixF nmat,zrot;
5537      mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
5538      //zrot.set(EulerF(0.0f, 0.0f, rot.z));
5539      //mat.mul(nmat,zrot);
5540   }
5541   else {
5542      mat.set(EulerF(0.0f, 0.0f, rot.z));
5543      mat.setColumn(3,pos);
5544   }
5545   Parent::setTransform(mat);
5546   mRot = rot;
5547
5548   if ( mPhysicsRep )
5549      mPhysicsRep->setTransform( mat );
5550}
5551
5552
5553void Player::setRenderPosition(const Point3F& pos, const Point3F& rot, F32 dt)
5554{
5555   MatrixF mat;
5556   if (isMounted()) {
5557      // Use transform from mounted object
5558      //MatrixF nmat,zrot;
5559      mMount.object->getRenderMountTransform( dt, mMount.node, mMount.xfm, &mat );
5560      //zrot.set(EulerF(0.0f, 0.0f, rot.z));
5561      //mat.mul(nmat,zrot);
5562   }
5563   else {
5564      EulerF   orient(0.0f, 0.0f, rot.z);
5565
5566      mat.set(orient);
5567      mat.setColumn(3, pos);
5568
5569      if (inDeathAnim()) {
5570         F32   boxRad = (mDataBlock->boxSize.x * 0.5f);
5571         if (MatrixF * fallMat = mDeath.fallToGround(dt, pos, rot.z, boxRad))
5572            mat = * fallMat;
5573      }
5574      else
5575         mDeath.initFall();
5576   }
5577   Parent::setRenderTransform(mat);
5578}
5579
5580//----------------------------------------------------------------------------
5581
5582void Player::setTransform(const MatrixF& mat)
5583{
5584   // This method should never be called on the client.
5585
5586   // This currently converts all rotation in the mat into
5587   // rotations around the z axis.
5588   Point3F pos,vec;
5589   mat.getColumn(1,&vec);
5590   mat.getColumn(3,&pos);
5591   Point3F rot(0.0f, 0.0f, -mAtan2(-vec.x,vec.y));
5592   setPosition(pos,rot);
5593   setMaskBits(MoveMask | NoWarpMask);
5594}
5595
5596void Player::getEyeTransform(MatrixF* mat)
5597{
5598   getEyeBaseTransform(mat, true);
5599
5600   // The shape instance is animated in getEyeBaseTransform() so we're
5601   // good here when attempting to get the eye node position on the server.
5602   S32 imageIndex = -1;
5603   S32 shapeIndex = -1;
5604   MountedImage* image = NULL;
5605   ShapeBaseImageData* data = NULL;
5606   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
5607   {
5608      image = &(mMountedImageList[i]);
5609      if (image->dataBlock) 
5610      {
5611         data = image->dataBlock;
5612         shapeIndex = getImageShapeIndex(*image);
5613         if ( data->useEyeNode && (data->animateOnServer || isGhost()) && isFirstPerson() && data->eyeMountNode[shapeIndex] != -1 && data->eyeNode[shapeIndex] != -1 )
5614         {
5615            imageIndex = i;
5616            break;
5617         }
5618      }
5619   }
5620
5621   if (imageIndex >= 0)
5622   {
5623      // Get the image's eye node's position relative to the eye mount node
5624      MatrixF mountTransform = image->shapeInstance[shapeIndex]->mNodeTransforms[data->eyeMountNode[shapeIndex]];
5625      Point3F eyeMountNodePos = mountTransform.getPosition();
5626      mountTransform = image->shapeInstance[shapeIndex]->mNodeTransforms[data->eyeNode[shapeIndex]];
5627      Point3F eyeNodePos = mountTransform.getPosition() - eyeMountNodePos;
5628
5629      // Now transform to the image's eye node (position only)
5630      MatrixF xfm(true);
5631      xfm.setPosition(eyeNodePos);
5632      mat->mul(xfm);
5633   }
5634}
5635
5636void Player::getEyeBaseTransform(MatrixF* mat, bool includeBank)
5637{
5638   // Eye transform in world space.  We only use the eye position
5639   // from the animation and supply our own rotation.
5640   MatrixF pmat,xmat,zmat;
5641
5642   if(!isGhost()) 
5643      mShapeInstance->animate();
5644
5645   xmat.set(EulerF(mHead.x, 0.0f, 0.0f));
5646
5647   if (mUseHeadZCalc)
5648      zmat.set(EulerF(0.0f, 0.0f, mHead.z));
5649   else
5650      zmat.identity();
5651
5652   if(includeBank && mDataBlock->cameraCanBank)
5653   {
5654      // Take mHead.y into account to bank the camera
5655      MatrixF imat;
5656      imat.mul(zmat, xmat);
5657      MatrixF ymat;
5658      ymat.set(EulerF(0.0f, mHead.y, 0.0f));
5659      pmat.mul(imat, ymat);
5660   }
5661   else
5662   {
5663      pmat.mul(zmat,xmat);
5664   }
5665
5666   F32 *dp = pmat;
5667
5668   F32* sp;
5669   MatrixF eyeMat(true);
5670   if (mDataBlock->eyeNode != -1)
5671   {
5672      sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode];
5673   }
5674   else
5675   {
5676      Point3F center;
5677      mObjBox.getCenter(&center);
5678      eyeMat.setPosition(center);
5679      sp = eyeMat;
5680   }
5681
5682   const Point3F& scale = getScale();
5683   dp[3] = sp[3] * scale.x;
5684   dp[7] = sp[7] * scale.y;
5685   dp[11] = sp[11] * scale.z;
5686   mat->mul(getTransform(),pmat);
5687}
5688
5689void Player::getRenderEyeTransform(MatrixF* mat)
5690{
5691   getRenderEyeBaseTransform(mat, true);
5692
5693   // Use the first image that is set to use the eye node
5694   for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
5695   {
5696      MountedImage& image = mMountedImageList[i];
5697      if (image.dataBlock) 
5698      {
5699         ShapeBaseImageData& data = *image.dataBlock;
5700         U32 shapeIndex = getImageShapeIndex(image);
5701         if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 && data.eyeNode[shapeIndex] != -1 )
5702         {
5703            // Get the eye node's position relative to the eye mount node
5704            MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
5705            Point3F eyeMountNodePos = mountTransform.getPosition();
5706            mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeNode[shapeIndex]];
5707            Point3F eyeNodePos = mountTransform.getPosition() - eyeMountNodePos;
5708
5709            // Now transform to the image's eye node (position only)
5710            MatrixF xfm(true);
5711            xfm.setPosition(eyeNodePos);
5712            mat->mul(xfm);
5713
5714            return;
5715         }
5716      }
5717   }
5718}
5719
5720void Player::getRenderEyeBaseTransform(MatrixF* mat, bool includeBank)
5721{
5722   // Eye transform in world space.  We only use the eye position
5723   // from the animation and supply our own rotation.
5724   MatrixF pmat,xmat,zmat;
5725   xmat.set(EulerF(mDelta.head.x + mDelta.headVec.x * mDelta.dt, 0.0f, 0.0f));
5726
5727   if (mUseHeadZCalc)
5728      zmat.set(EulerF(0.0f, 0.0f, mDelta.head.z + mDelta.headVec.z * mDelta.dt));
5729   else
5730      zmat.identity();
5731
5732   if(includeBank && mDataBlock->cameraCanBank)
5733   {
5734      // Take mHead.y delta into account to bank the camera
5735      MatrixF imat;
5736      imat.mul(zmat, xmat);
5737      MatrixF ymat;
5738      ymat.set(EulerF(0.0f, mDelta.head.y + mDelta.headVec.y * mDelta.dt, 0.0f));
5739      pmat.mul(imat, ymat);
5740   }
5741   else
5742   {
5743      pmat.mul(zmat,xmat);
5744   }
5745
5746   F32 *dp = pmat;
5747
5748   F32* sp;
5749   MatrixF eyeMat(true);
5750   if (mDataBlock->eyeNode != -1)
5751   {
5752      sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode];
5753   }
5754   else
5755   {
5756      // Use the center of the Player's bounding box for the eye position.
5757      Point3F center;
5758      mObjBox.getCenter(&center);
5759      eyeMat.setPosition(center);
5760      sp = eyeMat;
5761   }
5762
5763   // Only use position of eye node, and take Player's scale
5764   // into account.
5765   const Point3F& scale = getScale();
5766   dp[3] = sp[3] * scale.x;
5767   dp[7] = sp[7] * scale.y;
5768   dp[11] = sp[11] * scale.z;
5769
5770   mat->mul(getRenderTransform(), pmat);
5771}
5772
5773void Player::getMuzzleTransform(U32 imageSlot,MatrixF* mat)
5774{
5775   disableHeadZCalc();
5776
5777   MatrixF nmat;
5778   Parent::getRetractionTransform(imageSlot,&nmat);
5779   MatrixF smat;
5780   Parent::getImageTransform(imageSlot,&smat);
5781
5782   disableCollision();
5783
5784   // See if we are pushed into a wall...
5785   if (getDamageState() == Enabled) {
5786      Point3F start, end;
5787      smat.getColumn(3, &start);
5788      nmat.getColumn(3, &end);
5789
5790      RayInfo rinfo;
5791      if (getContainer()->castRay(start, end, sCollisionMoveMask, &rinfo)) {
5792         Point3F finalPoint;
5793         finalPoint.interpolate(start, end, rinfo.t);
5794         nmat.setColumn(3, finalPoint);
5795      }
5796      else
5797         Parent::getMuzzleTransform(imageSlot,&nmat);
5798   }
5799   else
5800      Parent::getMuzzleTransform(imageSlot,&nmat);
5801
5802   enableCollision();
5803
5804   enableHeadZCalc();
5805  
5806   *mat = nmat;
5807}
5808
5809void Player::getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat)
5810{
5811   disableHeadZCalc();
5812
5813   MatrixF nmat;
5814   Parent::getRenderRetractionTransform(imageSlot,&nmat);
5815   MatrixF smat;
5816   Parent::getRenderImageTransform(imageSlot,&smat);
5817
5818   disableCollision();
5819
5820   // See if we are pushed into a wall...
5821   if (getDamageState() == Enabled)
5822   {
5823      Point3F start, end;
5824      smat.getColumn(3, &start);
5825      nmat.getColumn(3, &end);
5826
5827      RayInfo rinfo;
5828      if (getContainer()->castRay(start, end, sCollisionMoveMask, &rinfo)) {
5829         Point3F finalPoint;
5830         finalPoint.interpolate(start, end, rinfo.t);
5831         nmat.setColumn(3, finalPoint);
5832      }
5833      else
5834      {
5835         Parent::getRenderMuzzleTransform(imageSlot,&nmat);
5836      }
5837   }
5838   else
5839   {
5840      Parent::getRenderMuzzleTransform(imageSlot,&nmat);
5841   }
5842
5843   enableCollision();
5844
5845   enableHeadZCalc();
5846  
5847   *mat = nmat;
5848}
5849
5850void Player::getMuzzleVector(U32 imageSlot,VectorF* vec)
5851{
5852   MatrixF mat;
5853   getMuzzleTransform(imageSlot,&mat);
5854
5855   GameConnection * gc = getControllingClient();
5856   if (gc && !gc->isAIControlled())
5857   {
5858      MountedImage& image = mMountedImageList[imageSlot];
5859
5860      bool fp = gc->isFirstPerson();
5861      if ((fp && image.dataBlock->correctMuzzleVector) ||
5862         (!fp && image.dataBlock->correctMuzzleVectorTP))
5863      {
5864         disableHeadZCalc();
5865         if (getCorrectedAim(mat, vec))
5866         {
5867            enableHeadZCalc();
5868            return;
5869         }
5870         enableHeadZCalc();
5871      }
5872   }
5873
5874   mat.getColumn(1,vec);
5875}
5876
5877void Player::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state )
5878{
5879   GFX->pushWorldMatrix();
5880
5881   MatrixF world;
5882
5883   MountedImage& image = mMountedImageList[imageSlot];
5884   ShapeBaseImageData& data = *image.dataBlock;
5885   U32 imageShapeIndex;
5886   if ( state->isShadowPass() )
5887   {
5888      // Force the standard image shapes for the shadow pass.
5889      imageShapeIndex = ShapeBaseImageData::StandardImageShape;
5890   }
5891   else
5892   {
5893      imageShapeIndex = getImageShapeIndex(image);
5894   }
5895
5896   if ( !state->isShadowPass() && isFirstPerson() && (data.useEyeOffset || (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1)) ) 
5897   {
5898      if (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1)
5899      {
5900         MatrixF nmat;
5901         getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank);
5902         MatrixF offsetMat = image.shapeInstance[imageShapeIndex]->mNodeTransforms[data.eyeMountNode[imageShapeIndex]];
5903         offsetMat.affineInverse();
5904         world.mul(nmat,offsetMat);
5905      }
5906      else
5907      {
5908         MatrixF nmat;
5909         getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank);
5910         world.mul(nmat,data.eyeOffset);
5911      }
5912
5913      if ( imageSlot == 0 )
5914      {
5915         MatrixF nmat;
5916         MatrixF smat;
5917
5918         getRenderRetractionTransform(0,&nmat);         
5919         getRenderImageTransform(0,&smat);
5920
5921         // See if we are pushed into a wall...
5922         Point3F start, end;
5923         smat.getColumn(3, &start);
5924         nmat.getColumn(3, &end);
5925
5926         Point3F displace = (start - end) * mWeaponBackFraction;
5927
5928         world.setPosition( world.getPosition() + displace );
5929      }
5930   }
5931   else 
5932   {
5933      MatrixF nmat;
5934      getRenderMountTransform( 0.0f, data.mountPoint, MatrixF::Identity, &nmat);
5935      world.mul(nmat,data.mountTransform[imageShapeIndex]);
5936   }
5937
5938   GFX->setWorldMatrix( world );
5939
5940   image.shapeInstance[imageShapeIndex]->animate();
5941   image.shapeInstance[imageShapeIndex]->render( rstate );
5942
5943   // Render the first person mount image shape?
5944   if (!state->isShadowPass() && imageShapeIndex == ShapeBaseImageData::FirstPersonImageShape && mShapeFPInstance[imageSlot])
5945   {
5946      mShapeFPInstance[imageSlot]->animate();
5947      mShapeFPInstance[imageSlot]->render( rstate );
5948   }
5949
5950   GFX->popWorldMatrix();
5951}
5952
5953// Bot aiming code calls this frequently and will work fine without the check
5954// for being pushed into a wall, which shows up on profile at ~ 3% (eight bots)
5955void Player::getMuzzlePointAI(U32 imageSlot, Point3F* point)
5956{
5957   MatrixF nmat;
5958   Parent::getMuzzleTransform(imageSlot, &nmat);
5959
5960   // If we are in one of the standard player animations, adjust the
5961   // muzzle to point in the direction we are looking.
5962   if (mActionAnimation.action < PlayerData::NumTableActionAnims)
5963   {
5964      MatrixF xmat;
5965      xmat.set(EulerF(mHead.x, 0, 0));
5966      MatrixF result;
5967      result.mul(getTransform(), xmat);
5968      F32 *sp = nmat, *dp = result;
5969      dp[3] = sp[3]; dp[7] = sp[7]; dp[11] = sp[11];
5970      result.getColumn(3, point);
5971   }
5972   else
5973      nmat.getColumn(3, point);
5974}
5975
5976void Player::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
5977{
5978   if (!mControlObject.isNull() && mControlObject == getObjectMount()) {
5979      mControlObject->getCameraParameters(min,max,off,rot);
5980      return;
5981   }
5982   const Point3F& scale = getScale();
5983   *min = mDataBlock->cameraMinDist * scale.y;
5984   *max = mDataBlock->cameraMaxDist * scale.y;
5985   off->set(0.0f, 0.0f, 0.0f);
5986   rot->identity();
5987}
5988
5989
5990//----------------------------------------------------------------------------
5991
5992Point3F Player::getVelocity() const
5993{
5994   return mVelocity;
5995}
5996
5997F32 Player::getSpeed() const
5998{
5999   return mVelocity.len();
6000}
6001
6002void Player::setVelocity(const VectorF& vel)
6003{
6004   AssertFatal( !mIsNaN( vel ), "Player::setVelocity() - The velocity is NaN!" );
6005
6006   mVelocity = vel;
6007   setMaskBits(MoveMask);
6008}
6009
6010void Player::applyImpulse(const Point3F&,const VectorF& vec)
6011{
6012   AssertFatal( !mIsNaN( vec ), "Player::applyImpulse() - The vector is NaN!" );
6013
6014   // Players ignore angular velocity
6015   VectorF vel;
6016   vel.x = vec.x / getMass();
6017   vel.y = vec.y / getMass();
6018   vel.z = vec.z / getMass();
6019
6020   // Make sure the impulse isn't too big
6021   F32 len = vel.magnitudeSafe();
6022   if (len > sMaxImpulseVelocity)
6023   {
6024      Point3F excess = vel * ( 1.0f - (sMaxImpulseVelocity / len ) );
6025      vel -= excess;
6026   }
6027
6028   setVelocity(mVelocity + vel);
6029}
6030
6031
6032//----------------------------------------------------------------------------
6033
6034bool Player::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
6035{
6036   // In standard Torque there's a rather brute force culling of all
6037   // non-enabled players (corpses) from the ray cast. But, to
6038   // demonstrate a resurrection spell, we need corpses to be
6039   // selectable, so this code change allows consideration of corpses
6040   // in the ray cast if corpsesHiddenFromRayCast is set to false.
6041   if (sCorpsesHiddenFromRayCast && getDamageState() != Enabled)
6042      return false;
6043
6044   // Collide against bounding box. Need at least this for the editor.
6045   F32 st,et,fst = 0.0f,fet = 1.0f;
6046   F32 *bmin = &mObjBox.minExtents.x;
6047   F32 *bmax = &mObjBox.maxExtents.x;
6048   F32 const *si = &start.x;
6049   F32 const *ei = &end.x;
6050
6051   for (S32 i = 0; i < 3; i++) {
6052      if (*si < *ei) {
6053         if (*si > *bmax || *ei < *bmin)
6054            return false;
6055         F32 di = *ei - *si;
6056         st = (*si < *bmin)? (*bmin - *si) / di: 0.0f;
6057         et = (*ei > *bmax)? (*bmax - *si) / di: 1.0f;
6058      }
6059      else {
6060         if (*ei > *bmax || *si < *bmin)
6061            return false;
6062         F32 di = *ei - *si;
6063         st = (*si > *bmax)? (*bmax - *si) / di: 0.0f;
6064         et = (*ei < *bmin)? (*bmin - *si) / di: 1.0f;
6065      }
6066      if (st > fst) fst = st;
6067      if (et < fet) fet = et;
6068      if (fet < fst)
6069         return false;
6070      bmin++; bmax++;
6071      si++; ei++;
6072   }
6073
6074   info->normal = start - end;
6075   info->normal.normalizeSafe();
6076   getTransform().mulV( info->normal );
6077
6078   info->t = fst;
6079   info->object = this;
6080   info->point.interpolate(start,end,fst);
6081   info->material = 0;
6082   return true;
6083}
6084
6085
6086//----------------------------------------------------------------------------
6087
6088static MatrixF IMat(1);
6089
6090bool Player::buildPolyList(PolyListContext, AbstractPolyList* polyList, const Box3F&, const SphereF&)
6091{
6092   // Collision with the player is always against the player's object
6093   // space bounding box axis aligned in world space.
6094   Point3F pos;
6095   getTransform().getColumn(3,&pos);
6096   IMat.setColumn(3,pos);
6097   polyList->setTransform(&IMat, Point3F(1.0f,1.0f,1.0f));
6098   polyList->setObject(this);
6099   polyList->addBox(mObjBox);
6100   return true;
6101}
6102
6103
6104void Player::buildConvex(const Box3F& box, Convex* convex)
6105{
6106   if (mShapeInstance == NULL)
6107      return;
6108
6109   // These should really come out of a pool
6110   mConvexList->collectGarbage();
6111
6112   Box3F realBox = box;
6113   mWorldToObj.mul(realBox);
6114   realBox.minExtents.convolveInverse(mObjScale);
6115   realBox.maxExtents.convolveInverse(mObjScale);
6116
6117   if (realBox.isOverlapped(getObjBox()) == false)
6118      return;
6119
6120   Convex* cc = 0;
6121   CollisionWorkingList& wl = convex->getWorkingList();
6122   for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
6123      if (itr->mConvex->getType() == BoxConvexType &&
6124          itr->mConvex->getObject() == this) {
6125         cc = itr->mConvex;
6126         break;
6127      }
6128   }
6129   if (cc)
6130      return;
6131
6132   // Create a new convex.
6133   BoxConvex* cp = new OrthoBoxConvex;
6134   mConvexList->registerObject(cp);
6135   convex->addToWorkingList(cp);
6136   cp->init(this);
6137
6138   mObjBox.getCenter(&cp->mCenter);
6139   cp->mSize.x = mObjBox.len_x() / 2.0f;
6140   cp->mSize.y = mObjBox.len_y() / 2.0f;
6141   cp->mSize.z = mObjBox.len_z() / 2.0f;
6142}
6143
6144
6145//----------------------------------------------------------------------------
6146
6147void Player::updateWorkingCollisionSet()
6148{
6149   // First, we need to adjust our velocity for possible acceleration.  It is assumed
6150   // that we will never accelerate more than 20 m/s for gravity, plus 10 m/s for
6151   // jetting, and an equivalent 10 m/s for jumping.  We also assume that the
6152   // working list is updated on a Tick basis, which means we only expand our
6153   // box by the possible movement in that tick.
6154   Point3F scaledVelocity = mVelocity * TickSec;
6155   F32 len    = scaledVelocity.len();
6156   F32 newLen = len + (10.0f * TickSec);
6157
6158   // Check to see if it is actually necessary to construct the new working list,
6159   // or if we can use the cached version from the last query.  We use the x
6160   // component of the min member of the mWorkingQueryBox, which is lame, but
6161   // it works ok.
6162   bool updateSet = false;
6163
6164   Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
6165   F32 l = (newLen * 1.1f) + 0.1f;  // from Convex::updateWorkingList
6166   const Point3F  lPoint( l, l, l );
6167   convexBox.minExtents -= lPoint;
6168   convexBox.maxExtents += lPoint;
6169
6170   // Check containment
6171   if (mWorkingQueryBox.minExtents.x != -1e9f)
6172   {
6173      if (mWorkingQueryBox.isContained(convexBox) == false)
6174         // Needed region is outside the cached region.  Update it.
6175         updateSet = true;
6176   }
6177   else
6178   {
6179      // Must update
6180      updateSet = true;
6181   }
6182   // Actually perform the query, if necessary
6183   if (updateSet == true) {
6184      const Point3F  twolPoint( 2.0f * l, 2.0f * l, 2.0f * l );
6185      mWorkingQueryBox = convexBox;
6186      mWorkingQueryBox.minExtents -= twolPoint;
6187      mWorkingQueryBox.maxExtents += twolPoint;
6188
6189      disableCollision();
6190
6191      //We temporarily disable the collisions of anything mounted to us so we don't accidentally walk into things we've attached to us
6192      for (SceneObject *ptr = mMount.list; ptr; ptr = ptr->getMountLink())
6193      {
6194         ptr->disableCollision();
6195      }
6196
6197      mConvex.updateWorkingList(mWorkingQueryBox,
6198         isGhost() ? sClientCollisionContactMask : sServerCollisionContactMask);
6199
6200      //And now re-enable the collisions of the mounted things
6201      for (SceneObject *ptr = mMount.list; ptr; ptr = ptr->getMountLink())
6202      {
6203         ptr->enableCollision();
6204      }
6205
6206      enableCollision();
6207   }
6208}
6209
6210
6211//----------------------------------------------------------------------------
6212
6213void Player::writePacketData(GameConnection *connection, BitStream *stream)
6214{
6215   Parent::writePacketData(connection, stream);
6216
6217   stream->writeInt(mState,NumStateBits);
6218   if (stream->writeFlag(mState == RecoverState))
6219      stream->writeInt(mRecoverTicks,PlayerData::RecoverDelayBits);
6220   if (stream->writeFlag(mJumpDelay > 0))
6221      stream->writeInt(mJumpDelay,PlayerData::JumpDelayBits);
6222
6223   Point3F pos;
6224   getTransform().getColumn(3,&pos);
6225   if (stream->writeFlag(!isMounted())) {
6226      // Will get position from mount
6227      stream->setCompressionPoint(pos);
6228      stream->write(pos.x);
6229      stream->write(pos.y);
6230      stream->write(pos.z);
6231      stream->write(mVelocity.x);
6232      stream->write(mVelocity.y);
6233      stream->write(mVelocity.z);
6234      stream->writeInt(mJumpSurfaceLastContact > 15 ? 15 : mJumpSurfaceLastContact, 4);
6235
6236      if (stream->writeFlag(!mAllowSprinting || !mAllowCrouching || !mAllowProne || !mAllowJumping || !mAllowJetJumping || !mAllowSwimming))
6237      {
6238         stream->writeFlag(mAllowJumping);
6239         stream->writeFlag(mAllowJetJumping);
6240         stream->writeFlag(mAllowSprinting);
6241         stream->writeFlag(mAllowCrouching);
6242         stream->writeFlag(mAllowProne);
6243         stream->writeFlag(mAllowSwimming);
6244      }
6245   }
6246   stream->write(mHead.x);
6247   if(stream->writeFlag(mDataBlock->cameraCanBank))
6248   {
6249      // Include mHead.y to allow for camera banking
6250      stream->write(mHead.y);
6251   }
6252   stream->write(mHead.z);
6253   stream->write(mRot.z);
6254
6255   if (mControlObject) {
6256      S32 gIndex = connection->getGhostIndex(mControlObject);
6257      if (stream->writeFlag(gIndex != -1)) {
6258         stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
6259         mControlObject->writePacketData(connection, stream);
6260      }
6261   }
6262   else
6263      stream->writeFlag(false);
6264}
6265
6266
6267void Player::readPacketData(GameConnection *connection, BitStream *stream)
6268{
6269   Parent::readPacketData(connection, stream);
6270
6271   mState = (ActionState)stream->readInt(NumStateBits);
6272   if (stream->readFlag())
6273      mRecoverTicks = stream->readInt(PlayerData::RecoverDelayBits);
6274   if (stream->readFlag())
6275      mJumpDelay = stream->readInt(PlayerData::JumpDelayBits);
6276   else
6277      mJumpDelay = 0;
6278
6279   Point3F pos,rot;
6280   if (stream->readFlag()) {
6281      // Only written if we are not mounted
6282      stream->read(&pos.x);
6283      stream->read(&pos.y);
6284      stream->read(&pos.z);
6285      stream->read(&mVelocity.x);
6286      stream->read(&mVelocity.y);
6287      stream->read(&mVelocity.z);
6288      stream->setCompressionPoint(pos);
6289     mDelta.pos = pos;
6290      mJumpSurfaceLastContact = stream->readInt(4);
6291
6292      if (stream->readFlag())
6293      {
6294         mAllowJumping = stream->readFlag();
6295         mAllowJetJumping = stream->readFlag();
6296         mAllowSprinting = stream->readFlag();
6297         mAllowCrouching = stream->readFlag();
6298         mAllowProne = stream->readFlag();
6299         mAllowSwimming = stream->readFlag();
6300      }
6301      else
6302      {
6303         mAllowJumping = true;
6304         mAllowJetJumping = true;
6305         mAllowSprinting = true;
6306         mAllowCrouching = true;
6307         mAllowProne = true;
6308         mAllowSwimming = true;
6309      }
6310   }
6311   else
6312      pos = mDelta.pos;
6313   stream->read(&mHead.x);
6314   if(stream->readFlag())
6315   {
6316      // Include mHead.y to allow for camera banking
6317      stream->read(&mHead.y);
6318   }
6319   stream->read(&mHead.z);
6320   stream->read(&rot.z);
6321   rot.x = rot.y = 0;
6322   if (!ignore_updates)
6323      setPosition(pos,rot);
6324   mDelta.head = mHead;
6325   mDelta.rot = rot;
6326
6327   if (stream->readFlag()) {
6328      S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
6329      ShapeBase* obj = static_cast<ShapeBase*>(connection->resolveGhost(gIndex));
6330      setControlObject(obj);
6331      obj->readPacketData(connection, stream);
6332   }
6333   else
6334      setControlObject(0);
6335}
6336
6337U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
6338{
6339   U32 retMask = Parent::packUpdate(con, mask, stream);
6340
6341   if (stream->writeFlag((mask & ImpactMask) && !(mask & InitialUpdateMask)))
6342      stream->writeInt(mImpactSound, PlayerData::ImpactBits);
6343
6344   if (stream->writeFlag(mask & ActionMask &&
6345         mActionAnimation.action != PlayerData::NullAnimation &&
6346         mActionAnimation.action >= PlayerData::NumTableActionAnims)) {
6347      stream->writeInt(mActionAnimation.action,PlayerData::ActionAnimBits);
6348      stream->writeFlag(mActionAnimation.holdAtEnd);
6349      stream->writeFlag(mActionAnimation.atEnd);
6350      stream->writeFlag(mActionAnimation.firstPerson);
6351      if (!mActionAnimation.atEnd) {
6352         // If somewhere in middle on initial update, must send position-
6353         F32   where = mShapeInstance->getPos(mActionAnimation.thread);
6354         if (stream->writeFlag((mask & InitialUpdateMask) != 0 && where > 0))
6355            stream->writeSignedFloat(where, 6);
6356      }
6357   }
6358
6359   if (stream->writeFlag(mask & ActionMask &&
6360         mArmAnimation.action != PlayerData::NullAnimation &&
6361         (!(mask & InitialUpdateMask) ||
6362         mArmAnimation.action != mDataBlock->lookAction))) {
6363      stream->writeInt(mArmAnimation.action,PlayerData::ActionAnimBits);
6364   }
6365   retMask = afx_packUpdate(con, mask, stream, retMask);
6366
6367   // The rest of the data is part of the control object packet update.
6368   // If we're controlled by this client, we don't need to send it.
6369   // we only need to send it if this is the initial update - in that case,
6370   // the client won't know this is the control object yet.
6371   if(stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
6372      return(retMask);
6373
6374   if (stream->writeFlag(mask & MoveMask))
6375   {
6376      stream->writeFlag(mFalling);
6377
6378      stream->writeFlag(mSwimming);
6379      stream->writeFlag(mJetting);  
6380      stream->writeInt(mPose, NumPoseBits);
6381     
6382      stream->writeInt(mState,NumStateBits);
6383      if (stream->writeFlag(mState == RecoverState))
6384         stream->writeInt(mRecoverTicks,PlayerData::RecoverDelayBits);
6385
6386      Point3F pos;
6387      getTransform().getColumn(3,&pos);
6388      stream->writeCompressedPoint(pos);
6389      F32 len = mVelocity.len();
6390      if(stream->writeFlag(len > 0.02f))
6391      {
6392         Point3F outVel = mVelocity;
6393         outVel *= 1.0f/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1afed088663f8704004425cdae2120b9b3">len</a>;
6394         stream->writeNormalVector(outVel, 10);
6395         len *= 32.0f;  // 5 bits of fraction
6396         if(len > 8191)
6397            len = 8191;
6398         stream->writeInt((S32)len, 13);
6399
6400         // constrain the range of mRot.z
6401         while (mRot.z < 0.0f)
6402            mRot.z += M_2PI_F;
6403         while (mRot.z > M_2PI_F)
6404            mRot.z -= M_2PI_F;
6405
6406
6407      }
6408      stream->writeFloat(mRot.z / M_2PI_F, 7);
6409      stream->writeSignedFloat(mHead.x / (mDataBlock->maxLookAngle - mDataBlock->minLookAngle), 6);
6410      stream->writeSignedFloat(mHead.z / mDataBlock->maxFreelookAngle, 6);
6411     mDelta.move.pack(stream);
6412      stream->writeFlag(!(mask & NoWarpMask));
6413   }
6414   // Ghost need energy to predict reliably
6415   if (mDataBlock->maxEnergy > 0.f)
6416      stream->writeFloat(getEnergyLevel() / mDataBlock->maxEnergy, EnergyLevelBits);
6417   else
6418      stream->writeFloat(0.f, EnergyLevelBits);
6419   return retMask;
6420}
6421
6422void Player::unpackUpdate(NetConnection *con, BitStream *stream)
6423{
6424   Parent::unpackUpdate(con,stream);
6425
6426   if (stream->readFlag())
6427      mImpactSound = stream->readInt(PlayerData::ImpactBits);
6428
6429   // Server specified action animation
6430   if (stream->readFlag()) {
6431      U32 action = stream->readInt(PlayerData::ActionAnimBits);
6432      bool hold = stream->readFlag();
6433      bool atEnd = stream->readFlag();
6434      bool fsp = stream->readFlag();
6435
6436      F32   animPos = -1.0f;
6437      if (!atEnd && stream->readFlag())
6438         animPos = stream->readSignedFloat(6);
6439
6440      if (isProperlyAdded()) {
6441         setActionThread(action,true,hold,true,fsp);
6442         bool  inDeath = inDeathAnim();
6443         if (atEnd)
6444         {
6445            mShapeInstance->clearTransition(mActionAnimation.thread);
6446            mShapeInstance->setPos(mActionAnimation.thread,
6447                                   mActionAnimation.forward? 1: 0);
6448            if (inDeath)
6449               mDeath.lastPos = 1.0f;
6450         }
6451         else if (animPos > 0) {
6452            mShapeInstance->setPos(mActionAnimation.thread, animPos);
6453            if (inDeath)
6454               mDeath.lastPos = animPos;
6455         }
6456
6457         // mMountPending suppresses tickDelay countdown so players will sit until
6458         // their mount, or another animation, comes through (or 13 seconds elapses).
6459         mMountPending = (S32) (inSittingAnim() ? sMountPendingTickWait : 0);
6460      }
6461      else {
6462         mActionAnimation.action = action;
6463         mActionAnimation.holdAtEnd = hold;
6464         mActionAnimation.atEnd = atEnd;
6465         mActionAnimation.firstPerson = fsp;
6466      }
6467   }
6468
6469   // Server specified arm animation
6470   if (stream->readFlag()) {
6471      U32 action = stream->readInt(PlayerData::ActionAnimBits);
6472      if (isProperlyAdded())
6473         setArmThread(action);
6474      else
6475         mArmAnimation.action = action;
6476   }
6477
6478   afx_unpackUpdate(con, stream);
6479   // Done if controlled by client ( and not initial update )
6480   if(stream->readFlag())
6481      return;
6482
6483   // MoveMask
6484   if (stream->readFlag()) {
6485      mPredictionCount = sMaxPredictionTicks;
6486      mFalling = stream->readFlag();
6487 
6488      mSwimming = stream->readFlag();
6489      mJetting = stream->readFlag();  
6490      mPose = (Pose)(stream->readInt(NumPoseBits)); 
6491     
6492      ActionState actionState = (ActionState)stream->readInt(NumStateBits);
6493      if (stream->readFlag()) {
6494         mRecoverTicks = stream->readInt(PlayerData::RecoverDelayBits);
6495         setState(actionState, mRecoverTicks);
6496      }
6497      else
6498         setState(actionState);
6499
6500      Point3F pos,rot;
6501      stream->readCompressedPoint(&pos);
6502      F32 speed = mVelocity.len();
6503      if(stream->readFlag())
6504      {
6505         stream->readNormalVector(&mVelocity, 10);
6506         mVelocity *= stream->readInt(13) / 32.0f;
6507      }
6508      else
6509      {
6510         mVelocity.set(0.0f, 0.0f, 0.0f);
6511      }
6512      
6513      rot.y = rot.x = 0.0f;
6514      rot.z = stream->readFloat(7) * M_2PI_F;
6515      mHead.x = stream->readSignedFloat(6) * (mDataBlock->maxLookAngle - mDataBlock->minLookAngle);
6516      mHead.z = stream->readSignedFloat(6) * mDataBlock->maxFreelookAngle;
6517     mDelta.move.unpack(stream);
6518
6519     mDelta.head = mHead;
6520     mDelta.headVec.set(0.0f, 0.0f, 0.0f);
6521
6522      if (stream->readFlag() && isProperlyAdded())
6523      {
6524         // Determine number of ticks to warp based on the average
6525         // of the client and server velocities.
6526        mDelta.warpOffset = pos - mDelta.pos;
6527         F32 as = (speed + mVelocity.len()) * 0.5f * TickSec;
6528         F32 dt = (as > 0.00001f) ? mDelta.warpOffset.len() / as: sMaxWarpTicks;
6529       mDelta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5f), 1.0f) : 0.0f);
6530
6531         if (mDelta.warpTicks)
6532         {
6533            // Setup the warp to start on the next tick.
6534            if (mDelta.warpTicks > sMaxWarpTicks)
6535            mDelta.warpTicks = sMaxWarpTicks;
6536         mDelta.warpOffset /= (F32)mDelta.warpTicks;
6537
6538         mDelta.rotOffset = rot - mDelta.rot;
6539
6540            // Ignore small rotation differences
6541            if (mFabs(mDelta.rotOffset.z) < 0.001f)
6542               mDelta.rotOffset.z = 0;
6543
6544            // Wrap rotation to +/-PI
6545            if(mDelta.rotOffset.z < - M_PI_F)
6546            mDelta.rotOffset.z += M_2PI_F;
6547            else if(mDelta.rotOffset.z > M_PI_F)
6548            mDelta.rotOffset.z -= M_2PI_F;
6549
6550         mDelta.rotOffset /= (F32)mDelta.warpTicks;
6551         }
6552         else
6553         {
6554            // Going to skip the warp, server and client are real close.
6555            // Adjust the frame interpolation to move smoothly to the
6556            // new position within the current tick.
6557            Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
6558            if (mDelta.dt == 0)
6559            {
6560               mDelta.posVec.set(0.0f, 0.0f, 0.0f);
6561               mDelta.rotVec.set(0.0f, 0.0f, 0.0f);
6562            }
6563            else
6564            {
6565               F32 dti = 1.0f / mDelta.dt;
6566            mDelta.posVec = (cp - pos) * dti;
6567            mDelta.rotVec.z = mRot.z - rot.z;
6568
6569               if(mDelta.rotVec.z > M_PI_F)
6570                  mDelta.rotVec.z -= M_2PI_F;
6571               else if(mDelta.rotVec.z < -M_PI_F)
6572                  mDelta.rotVec.z += M_2PI_F;
6573
6574            mDelta.rotVec.z *= dti;
6575            }
6576         mDelta.pos = pos;
6577         mDelta.rot = rot;
6578            if (!ignore_updates)
6579               setPosition(pos,rot);
6580         }
6581      }
6582      else 
6583      {
6584         // Set the player to the server position
6585         mDelta.pos = pos;
6586         mDelta.rot = rot;
6587       mDelta.posVec.set(0.0f, 0.0f, 0.0f);
6588       mDelta.rotVec.set(0.0f, 0.0f, 0.0f);
6589       mDelta.warpTicks = 0;
6590       mDelta.dt = 0.0f;
6591         if (!ignore_updates)
6592            setPosition(pos,rot);
6593      }
6594   }
6595   F32 energy = stream->readFloat(EnergyLevelBits) * mDataBlock->maxEnergy;
6596   setEnergyLevel(energy);
6597}
6598
6599
6600//----------------------------------------------------------------------------
6601DefineEngineMethod( Player, getPose, const char*, (),,
6602   "@brief Get the name of the player's current pose.\n\n"
6603
6604   "The pose is one of the following:\n\n<ul>"
6605   "<li>Stand - Standard movement pose.</li>"
6606   "<li>Sprint - Sprinting pose.</li>"
6607   "<li>Crouch - Crouch pose.</li>"
6608   "<li>Prone - Prone pose.</li>"
6609   "<li>Swim - Swimming pose.</li></ul>\n"
6610
6611   "@return The current pose; one of: \"Stand\", \"Sprint\", \"Crouch\", \"Prone\", \"Swim\"\n" )
6612{
6613   return object->getPoseName();
6614}
6615
6616DefineEngineMethod( Player, allowAllPoses, void, (),,
6617   "@brief Allow all poses a chance to occur.\n\n"
6618   "This method resets any poses that have manually been blocked from occuring.  "
6619   "This includes the regular pose states such as sprinting, crouch, being prone "
6620   "and swimming.  It also includes being able to jump and jet jump.  While this "
6621   "is allowing these poses to occur it doesn't mean that they all can due to other "
6622   "conditions.  We're just not manually blocking them from being allowed.\n"
6623   "@see allowJumping()\n"
6624   "@see allowJetJumping()\n"
6625   "@see allowSprinting()\n"
6626   "@see allowCrouching()\n"
6627   "@see allowProne()\n"
6628   "@see allowSwimming()\n" )
6629{
6630   object->allowAllPoses();
6631}
6632
6633DefineEngineMethod( Player, allowJumping, void, (bool state),,
6634   "@brief Set if the Player is allowed to jump.\n\n"
6635   "The default is to allow jumping unless there are other environmental concerns "
6636   "that prevent it.  This method is mainly used to explicitly disallow jumping "
6637   "at any time.\n"
6638   "@param state Set to true to allow jumping, false to disable it.\n"
6639   "@see allowAllPoses()\n" )
6640{
6641   object->allowJumping(state);
6642}
6643
6644DefineEngineMethod( Player, allowJetJumping, void, (bool state),,
6645   "@brief Set if the Player is allowed to jet jump.\n\n"
6646   "The default is to allow jet jumping unless there are other environmental concerns "
6647   "that prevent it.  This method is mainly used to explicitly disallow jet jumping "
6648   "at any time.\n"
6649   "@param state Set to true to allow jet jumping, false to disable it.\n"
6650   "@see allowAllPoses()\n" )
6651{
6652   object->allowJetJumping(state);
6653}
6654
6655DefineEngineMethod( Player, allowSprinting, void, (bool state),,
6656   "@brief Set if the Player is allowed to sprint.\n\n"
6657   "The default is to allow sprinting unless there are other environmental concerns "
6658   "that prevent it.  This method is mainly used to explicitly disallow sprinting "
6659   "at any time.\n"
6660   "@param state Set to true to allow sprinting, false to disable it.\n"
6661   "@see allowAllPoses()\n" )
6662{
6663   object->allowSprinting(state);
6664}
6665
6666DefineEngineMethod( Player, allowCrouching, void, (bool state),,
6667   "@brief Set if the Player is allowed to crouch.\n\n"
6668   "The default is to allow crouching unless there are other environmental concerns "
6669   "that prevent it.  This method is mainly used to explicitly disallow crouching "
6670   "at any time.\n"
6671   "@param state Set to true to allow crouching, false to disable it.\n"
6672   "@see allowAllPoses()\n" )
6673{
6674   object->allowCrouching(state);
6675}
6676
6677DefineEngineMethod( Player, allowProne, void, (bool state),,
6678   "@brief Set if the Player is allowed to go prone.\n\n"
6679   "The default is to allow being prone unless there are other environmental concerns "
6680   "that prevent it.  This method is mainly used to explicitly disallow going prone "
6681   "at any time.\n"
6682   "@param state Set to true to allow being prone, false to disable it.\n"
6683   "@see allowAllPoses()\n" )
6684{
6685   object->allowProne(state);
6686}
6687
6688DefineEngineMethod( Player, allowSwimming, void, (bool state),,
6689   "@brief Set if the Player is allowed to swim.\n\n"
6690   "The default is to allow swimming unless there are other environmental concerns "
6691   "that prevent it.  This method is mainly used to explicitly disallow swimming "
6692   "at any time.\n"
6693   "@param state Set to true to allow swimming, false to disable it.\n"
6694   "@see allowAllPoses()\n" )
6695{
6696   object->allowSwimming(state);
6697}
6698
6699//----------------------------------------------------------------------------
6700
6701DefineEngineMethod( Player, getState, const char*, (),,
6702   "@brief Get the name of the player's current state.\n\n"
6703
6704   "The state is one of the following:\n\n<ul>"
6705   "<li>Dead - The Player is dead.</li>"
6706   "<li>Mounted - The Player is mounted to an object such as a vehicle.</li>"
6707   "<li>Move - The Player is free to move.  The usual state.</li>"
6708   "<li>Recover - The Player is recovering from a fall.  See PlayerData::recoverDelay.</li></ul>\n"
6709
6710   "@return The current state; one of: \"Dead\", \"Mounted\", \"Move\", \"Recover\"\n" )
6711{
6712   return object->getStateName();
6713}
6714
6715DefineEngineMethod( Player, getDamageLocation, const char*, ( Point3F pos ),,
6716   "@brief Get the named damage location and modifier for a given world position.\n\n"
6717
6718   "the Player object can simulate different hit locations based on a pre-defined set "
6719   "of PlayerData defined percentages.  These hit percentages divide up the Player's "
6720   "bounding box into different regions.  The diagram below demonstrates how the various "
6721   "PlayerData properties split up the bounding volume:\n\n"
6722
6723   "<img src=\"images/player_damageloc.png\">\n\n"
6724
6725   "While you may pass in any world position and getDamageLocation() will provide a best-fit "
6726   "location, you should be aware that this can produce some interesting results.  For example, "
6727   "any position that is above PlayerData::boxHeadPercentage will be considered a 'head' hit, even "
6728   "if the world position is high in the sky.  Therefore it may be wise to keep the passed in point "
6729   "to somewhere on the surface of, or within, the Player's bounding volume.\n\n"
6730
6731   "@note This method will not return an accurate location when the player is "
6732   "prone or swimming.\n\n"
6733
6734   "@param pos A world position for which to retrieve a body region on this player.\n"
6735
6736   "@return a string containing two words (space separated strings), where the "
6737   "first is a location and the second is a modifier.\n\n"
6738
6739   "Posible locations:<ul>"
6740   "<li>head</li>"
6741   "<li>torso</li>"
6742   "<li>legs</li></ul>\n"
6743
6744   "Head modifiers:<ul>"
6745   "<li>left_back</li>"
6746   "<li>middle_back</li>"
6747   "<li>right_back</li>"
6748   "<li>left_middle</li>"
6749   "<li>middle_middle</li>"
6750   "<li>right_middle</li>"
6751   "<li>left_front</li>"
6752   "<li>middle_front</li>"
6753   "<li>right_front</li></ul>\n"
6754
6755   "Legs/Torso modifiers:<ul>"
6756   "<li>front_left</li>"
6757   "<li>front_right</li>"
6758   "<li>back_left</li>"
6759   "<li>back_right</li></ul>\n"
6760
6761   "@see PlayerData::boxHeadPercentage\n"
6762   "@see PlayerData::boxHeadFrontPercentage\n"
6763   "@see PlayerData::boxHeadBackPercentage\n"
6764   "@see PlayerData::boxHeadLeftPercentage\n"
6765   "@see PlayerData::boxHeadRightPercentage\n"
6766   "@see PlayerData::boxTorsoPercentage\n"
6767   )
6768{
6769   const char *buffer1;
6770   const char *buffer2;
6771
6772   object->getDamageLocation(pos, buffer1, buffer2);
6773
6774   static const U32 bufSize = 128;
6775   char *buff = Con::getReturnBuffer(bufSize);
6776   dSprintf(buff, bufSize, "%s %s", buffer1, buffer2);
6777   return buff;
6778}
6779
6780DefineEngineMethod( Player, setArmThread, bool, ( const char* name ),,
6781   "@brief Set the sequence that controls the player's arms (dynamically adjusted "
6782   "to match look direction).\n\n"
6783   "@param name Name of the sequence to play on the player's arms.\n"
6784   "@return true if successful, false if failed.\n"
6785   "@note By default the 'look' sequence is used, if available.\n")
6786{
6787   return object->setArmThread( name );
6788}
6789
6790DefineEngineMethod( Player, setActionThread, bool, ( const char* name, bool hold, bool fsp ), ( false, true ),
6791   "@brief Set the main action sequence to play for this player.\n\n"
6792   "@param name Name of the action sequence to set\n"
6793   "@param hold Set to false to get a callback on the datablock when the sequence ends (PlayerData::animationDone()).  "
6794   "When set to true no callback is made.\n"
6795   "@param fsp True if first person and none of the spine nodes in the shape should animate.  False will allow the shape's "
6796   "spine nodes to animate.\n"
6797   "@return True if succesful, false if failed\n"
6798   
6799   "@note The spine nodes for the Player's shape are named as follows:\n\n<ul>"
6800   "<li>Bip01 Pelvis</li>"
6801   "<li>Bip01 Spine</li>"
6802   "<li>Bip01 Spine1</li>"
6803   "<li>Bip01 Spine2</li>"
6804   "<li>Bip01 Neck</li>"
6805   "<li>Bip01 Head</li></ul>\n\n"
6806   
6807   "You cannot use setActionThread() to have the Player play one of the motion "
6808   "determined action animation sequences.  These sequences are chosen based on how "
6809   "the Player moves and the Player's current pose.  The names of these sequences are:\n\n<ul>"
6810   "<li>root</li>"
6811   "<li>run</li>"
6812   "<li>side</li>"
6813   "<li>side_right</li>"
6814   "<li>crouch_root</li>"
6815   "<li>crouch_forward</li>"
6816   "<li>crouch_backward</li>"
6817   "<li>crouch_side</li>"
6818   "<li>crouch_right</li>"
6819   "<li>prone_root</li>"
6820   "<li>prone_forward</li>"
6821   "<li>prone_backward</li>"
6822   "<li>swim_root</li>"
6823   "<li>swim_forward</li>"
6824   "<li>swim_backward</li>"
6825   "<li>swim_left</li>"
6826   "<li>swim_right</li>"
6827   "<li>fall</li>"
6828   "<li>jump</li>"
6829   "<li>standjump</li>"
6830   "<li>land</li>"
6831   "<li>jet</li></ul>\n\n"
6832   
6833   "If the player moves in any direction then the animation sequence set using this "
6834   "method will be cancelled and the chosen mation-based sequence will take over.  This makes "
6835   "great for times when the Player cannot move, such as when mounted, or when it doesn't matter "
6836   "if the action sequence changes, such as waving and saluting.\n"
6837   
6838   "@tsexample\n"
6839      "// Place the player in a sitting position after being mounted\n"
6840      "%player.setActionThread( \"sitting\", true, true );\n"
6841   "@endtsexample\n")
6842{
6843   return object->setActionThread( name, hold, true, fsp);
6844}
6845
6846DefineEngineMethod( Player, setControlObject, bool, ( ShapeBase* obj ),,
6847   "@brief Set the object to be controlled by this player\n\n"
6848
6849   "It is possible to have the moves sent to the Player object from the "
6850   "GameConnection to be passed along to another object.  This happens, for example "
6851   "when a player is mounted to a vehicle.  The move commands pass through the Player "
6852   "and on to the vehicle (while the player remains stationary within the vehicle).  "
6853   "With setControlObject() you can have the Player pass along its moves to any object.  "
6854   "One possible use is for a player to move a remote controlled vehicle.  In this case "
6855   "the player does not mount the vehicle directly, but still wants to be able to control it.\n"
6856
6857   "@param obj Object to control with this player\n"
6858   "@return True if the object is valid, false if not\n"
6859
6860   "@see getControlObject()\n"
6861   "@see clearControlObject()\n"
6862   "@see GameConnection::setControlObject()")
6863{
6864   if (obj) {
6865      object->setControlObject(obj);
6866      return true;
6867   }
6868   else
6869      object->setControlObject(0);
6870   return false;
6871}
6872
6873DefineEngineMethod( Player, getControlObject, S32, (),,
6874   "@brief Get the current object we are controlling.\n\n"
6875   "@return ID of the ShapeBase object we control, or 0 if not controlling an "
6876   "object.\n"
6877   "@see setControlObject()\n"
6878   "@see clearControlObject()")
6879{
6880   ShapeBase* controlObject = object->getControlObject();
6881   return controlObject ? controlObject->getId(): 0;
6882}
6883
6884DefineEngineMethod( Player, clearControlObject, void, (),,
6885   "@brief Clears the player's current control object.\n\n"
6886   "Returns control to the player. This internally calls "
6887   "Player::setControlObject(0).\n"
6888   "@tsexample\n"
6889      "%player.clearControlObject();\n"
6890      "echo(%player.getControlObject()); //<-- Returns 0, player assumes control\n"
6891      "%player.setControlObject(%vehicle);\n"
6892      "echo(%player.getControlObject()); //<-- Returns %vehicle, player controls the vehicle now.\n"
6893   "@endtsexample\n"
6894   "@note If the player does not have a control object, the player will receive all moves "
6895   "from its GameConnection.  If you're looking to remove control from the player itself "
6896   "(i.e. stop sending moves to the player) use GameConnection::setControlObject() to transfer "
6897   "control to another object, such as a camera.\n"
6898   "@see setControlObject()\n"
6899   "@see getControlObject()\n"
6900   "@see GameConnection::setControlObject()\n")
6901{
6902   object->setControlObject(0);
6903}
6904
6905DefineEngineMethod( Player, checkDismountPoint, bool, ( Point3F oldPos, Point3F pos ),,
6906   "@brief Check if it is safe to dismount at this position.\n\n"
6907
6908   "Internally this method casts a ray from oldPos to pos to determine if it hits the "
6909   "terrain, an interior object, a water object, another player, a static shape, "
6910   "a vehicle (exluding the one currently mounted), or physical zone.  If this ray "
6911   "is in the clear, then the player's bounding box is also checked for a collision at "
6912   "the pos position.  If this displaced bounding box is also in the clear, then "
6913   "checkDismountPoint() returns true.\n"
6914
6915   "@param oldPos The player's current position\n"
6916   "@param pos The dismount position to check\n"
6917   "@return True if the dismount position is clear, false if not\n"
6918   
6919   "@note The player must be already mounted for this method to not assert.\n")
6920{
6921   MatrixF oldPosMat(true);
6922   oldPosMat.setColumn(3, oldPos);
6923   MatrixF posMat(true);
6924   posMat.setColumn(3, pos);
6925   return object->checkDismountPosition(oldPosMat, posMat);
6926}
6927
6928DefineEngineMethod( Player, getNumDeathAnimations, S32, ( ),,
6929   "@brief Get the number of death animations available to this player.\n\n"
6930   "Death animations are assumed to be named death1-N using consecutive indices." )
6931{
6932   S32 count = 0;
6933   const PlayerData * db = dynamic_cast<PlayerData*>( object->getDataBlock() );
6934   if ( db )
6935   {
6936
6937      for ( S32 i = 0; i < db->actionCount; i++ )
6938         if ( db->actionList[i].death )
6939            count++;
6940   }
6941   return count;
6942}
6943
6944//----------------------------------------------------------------------------
6945void Player::consoleInit()
6946{
6947   Con::addVariable("$player::renderMyPlayer",TypeBool, &sRenderMyPlayer,
6948      "@brief Determines if the player is rendered or not.\n\n"
6949      "Used on the client side to disable the rendering of all Player objects.  This is "
6950      "mainly for the tools or debugging.\n"
6951      "@ingroup GameObjects\n");
6952   Con::addVariable("$player::renderMyItems",TypeBool, &sRenderMyItems,
6953      "@brief Determines if mounted shapes are rendered or not.\n\n"
6954      "Used on the client side to disable the rendering of all Player mounted objects.  This is "
6955      "mainly used for the tools or debugging.\n"
6956      "@ingroup GameObjects\n");
6957   Con::addVariable("$player::renderCollision", TypeBool, &sRenderPlayerCollision, 
6958      "@brief Determines if the player's collision mesh should be rendered.\n\n"
6959      "This is mainly used for the tools and debugging.\n"
6960      "@ingroup GameObjects\n");
6961
6962   Con::addVariable("$player::minWarpTicks",TypeF32,&sMinWarpTicks, 
6963      "@brief Fraction of tick at which instant warp occures on the client.\n\n"
6964      "@ingroup GameObjects\n");
6965   Con::addVariable("$player::maxWarpTicks",TypeS32,&sMaxWarpTicks, 
6966      "@brief When a warp needs to occur due to the client being too far off from the server, this is the "
6967      "maximum number of ticks we'll allow the client to warp to catch up.\n\n"
6968      "@ingroup GameObjects\n");
6969   Con::addVariable("$player::maxPredictionTicks",TypeS32,&sMaxPredictionTicks, 
6970      "@brief Maximum number of ticks to predict on the client from the last known move obtained from the server.\n\n"
6971      "@ingroup GameObjects\n");
6972
6973   Con::addVariable("$player::maxImpulseVelocity", TypeF32, &sMaxImpulseVelocity, 
6974      "@brief The maximum velocity allowed due to a single impulse.\n\n"
6975      "@ingroup GameObjects\n");
6976
6977   // Move triggers
6978   Con::addVariable("$player::jumpTrigger", TypeS32, &sJumpTrigger, 
6979      "@brief The move trigger index used for player jumping.\n\n"
6980      "@ingroup GameObjects\n");
6981   Con::addVariable("$player::crouchTrigger", TypeS32, &sCrouchTrigger, 
6982      "@brief The move trigger index used for player crouching.\n\n"
6983      "@ingroup GameObjects\n");
6984   Con::addVariable("$player::proneTrigger", TypeS32, &sProneTrigger, 
6985      "@brief The move trigger index used for player prone pose.\n\n"
6986      "@ingroup GameObjects\n");
6987   Con::addVariable("$player::sprintTrigger", TypeS32, &sSprintTrigger, 
6988      "@brief The move trigger index used for player sprinting.\n\n"
6989      "@ingroup GameObjects\n");
6990   Con::addVariable("$player::imageTrigger0", TypeS32, &sImageTrigger0, 
6991      "@brief The move trigger index used to trigger mounted image 0.\n\n"
6992      "@ingroup GameObjects\n");
6993   Con::addVariable("$player::imageTrigger1", TypeS32, &sImageTrigger1, 
6994      "@brief The move trigger index used to trigger mounted image 1 or alternate fire "
6995      "on mounted image 0.\n\n"
6996      "@ingroup GameObjects\n");
6997   Con::addVariable("$player::jumpJetTrigger", TypeS32, &sJumpJetTrigger, 
6998      "@brief The move trigger index used for player jump jetting.\n\n"
6999      "@ingroup GameObjects\n");
7000   Con::addVariable("$player::vehicleDismountTrigger", TypeS32, &sVehicleDismountTrigger, 
7001      "@brief The move trigger index used to dismount player.\n\n"
7002      "@ingroup GameObjects\n");
7003
7004   // ExtendedMove support
7005   Con::addVariable("$player::extendedMoveHeadPosRotIndex", TypeS32, &smExtendedMoveHeadPosRotIndex, 
7006      "@brief The ExtendedMove position/rotation index used for head movements.\n\n"
7007      "@ingroup GameObjects\n");
7008   afx_consoleInit();
7009}
7010
7011//--------------------------------------------------------------------------
7012void Player::calcClassRenderData()
7013{
7014   Parent::calcClassRenderData();
7015
7016   // If nothing is mounted do not perform the calculations below.  Otherwise,
7017   // we'll end up with a bad ray cast as both nmat and smat will be the
7018   // Player's transform.
7019   MountedImage& image = mMountedImageList[0];
7020   if (!image.dataBlock)
7021   {
7022      mWeaponBackFraction = 0.0f;
7023      return;
7024   }
7025
7026   disableCollision();
7027   MatrixF nmat;
7028   MatrixF smat;
7029   Parent::getRetractionTransform(0,&nmat);
7030   Parent::getImageTransform(0, &smat);
7031
7032   // See if we are pushed into a wall...
7033   Point3F start, end;
7034   smat.getColumn(3, &start);
7035   nmat.getColumn(3, &end);
7036
7037   RayInfo rinfo;
7038   if (getContainer()->castRay(start, end, sCollisionMoveMask & ~(WaterObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a1af121084d5760a7eaef4bb8828ce1cf">PhysicalZoneObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a92ac9447af6e9f9635258a47f9df1d0c">MarkerObjectType), &rinfo)) {
7039      if (rinfo.t < 1.0f)
7040         mWeaponBackFraction = 1.0f - rinfo.t;
7041      else
7042         mWeaponBackFraction = 0.0f;
7043   } else {
7044      mWeaponBackFraction = 0.0f;
7045   }
7046   enableCollision();
7047}
7048
7049//-----------------------------------------------------------------------------
7050
7051void Player::playFootstepSound( bool triggeredLeft, Material* contactMaterial, SceneObject* contactObject )
7052{
7053   if (footfallSoundOverride > 0)
7054      return;
7055   MatrixF footMat = getTransform();
7056   if( mWaterCoverage > 0.0 )
7057   {
7058      // Treading water.
7059
7060      if ( mWaterCoverage < mDataBlock->footSplashHeight )
7061         SFX->playOnce( mDataBlock->sound[ PlayerData::FootShallowSplash ], &footMat );
7062      else
7063      {
7064         if ( mWaterCoverage < 1.0 )
7065            SFX->playOnce( mDataBlock->sound[ PlayerData::FootWading ], &footMat );
7066         else
7067         {
7068            if ( triggeredLeft )
7069            {
7070               SFX->playOnce( mDataBlock->sound[ PlayerData::FootUnderWater ], &footMat );
7071               SFX->playOnce( mDataBlock->sound[ PlayerData::FootBubbles ], &footMat );
7072            }
7073         }
7074      }
7075   }
7076   else if( contactMaterial && contactMaterial->mFootstepSoundCustom )
7077   {
7078      // Footstep sound defined on material.
7079
7080      SFX->playOnce( contactMaterial->mFootstepSoundCustom, &footMat );
7081   }
7082   else
7083   {
7084      // Play default sound.
7085
7086      S32 sound = -1;
7087      if (contactMaterial && (contactMaterial->mFootstepSoundId>-1 && contactMaterial->mFootstepSoundId<PlayerData::MaxSoundOffsets))
7088         sound = contactMaterial->mFootstepSoundId;
7089      else if( contactObject && contactObject->getTypeMask() & VehicleObjectType )
7090         sound = 2;
7091
7092      if (sound>=0)
7093         SFX->playOnce(mDataBlock->sound[sound], &footMat);
7094   }
7095}
7096
7097void Player:: playImpactSound()
7098{
7099   if( mWaterCoverage == 0.0f )
7100   {
7101      Point3F pos;
7102      RayInfo rInfo;
7103      MatrixF mat = getTransform();
7104      mat.mulP(Point3F(mDataBlock->decalOffset,0.0f,0.0f), &pos);
7105
7106      if( gClientContainer.castRay( Point3F( pos.x, pos.y, pos.z + 0.01f ),
7107                                    Point3F( pos.x, pos.y, pos.z - 2.0f ),
7108                                    STATIC_COLLISION_TYPEMASK | VehicleObjectType,
7109                                    &rInfo ) )
7110      {
7111         Material* material = ( rInfo.material ? dynamic_cast< Material* >( rInfo.material->getMaterial() ) : 0 );
7112
7113         if( material && material->mImpactSoundCustom )
7114            SFX->playOnce( material->mImpactSoundCustom, &getTransform() );
7115         else
7116         {
7117            S32 sound = -1;
7118            if (material && (material->mImpactSoundId>-1 && material->mImpactSoundId<PlayerData::MaxSoundOffsets))
7119               sound = material->mImpactSoundId;
7120            else if( rInfo.object->getTypeMask() & VehicleObjectType )
7121               sound = 2; // Play metal;
7122
7123            if (sound >= 0)
7124               SFX->playOnce(mDataBlock->sound[PlayerData::ImpactStart + sound], &getTransform());
7125         }
7126      }
7127   }
7128
7129   mImpactSound = 0;
7130}
7131
7132//--------------------------------------------------------------------------
7133// Update splash
7134//--------------------------------------------------------------------------
7135
7136void Player::updateSplash()
7137{
7138   F32 speed = getVelocity().len();
7139   if( speed < mDataBlock->splashVelocity || isMounted() ) return;
7140
7141   Point3F curPos = getPosition();
7142
7143   if ( curPos.equal( mLastPos ) )
7144      return;
7145
7146   if (pointInWater( curPos )) {
7147      if (!pointInWater( mLastPos )) {
7148         Point3F norm = getVelocity();
7149         norm.normalize();
7150
7151         // make sure player is moving vertically at good pace before playing splash
7152         F32 splashAng = mDataBlock->splashAngle / 360.0;
7153         if( mDot( norm, Point3F(0.0, 0.0, -1.0) ) < splashAng )
7154            return;
7155
7156
7157         RayInfo rInfo;
7158         if (gClientContainer.castRay(mLastPos, curPos,
7159               WaterObjectType, &rInfo)) {
7160            createSplash( rInfo.point, speed );
7161            mBubbleEmitterTime = 0.0;
7162         }
7163
7164      }
7165   }
7166}
7167
7168
7169//--------------------------------------------------------------------------
7170
7171void Player::updateFroth( F32 dt )
7172{
7173   // update bubbles
7174   Point3F moveDir = getVelocity();
7175   mBubbleEmitterTime += dt;
7176
7177   if (mBubbleEmitterTime < mDataBlock->bubbleEmitTime) {
7178      if (mSplashEmitter[PlayerData::BUBBLE_EMITTER]) {
7179         Point3F emissionPoint = getRenderPosition();
7180         U32 emitNum = PlayerData::BUBBLE_EMITTER;
7181         mSplashEmitter[emitNum]->emitParticles(mLastPos, emissionPoint,
7182            Point3F( 0.0, 0.0, 1.0 ), moveDir, (U32)(dt * 1000.0));
7183      }
7184   }
7185
7186   Point3F contactPoint;
7187   if (!collidingWithWater(contactPoint)) {
7188      mLastWaterPos = mLastPos;
7189      return;
7190   }
7191
7192   F32 speed = moveDir.len();
7193   if ( speed < mDataBlock->splashVelEpsilon ) 
7194      speed = 0.0;
7195
7196   U32 emitRate = (U32) (speed * mDataBlock->splashFreqMod * dt);
7197
7198   // If we're in the water, swimming, but not
7199   // moving, then lets emit some particles because
7200   // we're treading water.  
7201   if ( mSwimming && speed == 0.0 )
7202   {
7203      emitRate = (U32) (2.0f * mDataBlock->splashFreqMod * dt);
7204   }   
7205
7206   U32 i;
7207   for ( i=0; i<PlayerData::BUBBLE_EMITTER; i++ ) {
7208      if (mSplashEmitter[i] )
7209         mSplashEmitter[i]->emitParticles( mLastWaterPos,
7210            contactPoint, Point3F( 0.0, 0.0, 1.0 ),
7211            moveDir, emitRate );
7212   }
7213   mLastWaterPos = contactPoint;
7214}
7215
7216void Player::updateWaterSounds(F32 dt)
7217{
7218   if ( mWaterCoverage < 1.0f || mDamageState != Enabled )
7219   {
7220      // Stop everything
7221      if ( mMoveBubbleSound )
7222         mMoveBubbleSound->stop();
7223      if ( mWaterBreathSound )
7224         mWaterBreathSound->stop();
7225      return;
7226   }
7227
7228   if ( mMoveBubbleSound )
7229   {
7230      // We're under water and still alive, so let's play something
7231      if ( mVelocity.len() > 1.0f )
7232      {
7233         if ( !mMoveBubbleSound->isPlaying() )
7234            mMoveBubbleSound->play();
7235
7236         mMoveBubbleSound->setTransform( getTransform() );
7237      }
7238      else
7239         mMoveBubbleSound->stop();
7240   }
7241
7242   if ( mWaterBreathSound )
7243   {
7244      if ( !mWaterBreathSound->isPlaying() )
7245         mWaterBreathSound->play();
7246
7247      mWaterBreathSound->setTransform( getTransform() );
7248   }
7249}
7250
7251
7252//--------------------------------------------------------------------------
7253// Returns true if player is intersecting a water surface
7254//--------------------------------------------------------------------------
7255bool Player::collidingWithWater( Point3F &waterHeight )
7256{
7257   if ( !mCurrentWaterObject )
7258      return false;
7259   
7260   Point3F curPos = getPosition();
7261
7262   if ( mWorldBox.maxExtents.z < mLiquidHeight )
7263      return false;
7264
7265   curPos.z = mLiquidHeight;
7266
7267   waterHeight = getPosition();
7268   waterHeight.z = mLiquidHeight;
7269
7270   return true;
7271}
7272
7273//--------------------------------------------------------------------------
7274
7275void Player::createSplash( Point3F &pos, F32 speed )
7276{
7277   if ( speed >= mDataBlock->hardSplashSoundVel )
7278      SFX->playOnce( mDataBlock->sound[PlayerData::ImpactWaterHard], &getTransform() );
7279   else if ( speed >= mDataBlock->medSplashSoundVel )
7280      SFX->playOnce( mDataBlock->sound[PlayerData::ImpactWaterMedium], &getTransform() );
7281   else
7282      SFX->playOnce( mDataBlock->sound[PlayerData::ImpactWaterEasy], &getTransform() );
7283
7284   if( mDataBlock->splash )
7285   {
7286      MatrixF trans = getTransform();
7287      trans.setPosition( pos );
7288      Splash *splash = new Splash;
7289      splash->onNewDataBlock( mDataBlock->splash, false );
7290      splash->setTransform( trans );
7291      splash->setInitialState( trans.getPosition(), Point3F( 0.0, 0.0, 1.0 ) );
7292      if (!splash->registerObject())
7293         delete splash;
7294   }
7295}
7296
7297
7298bool Player::isControlObject()
7299{
7300   GameConnection* connection = GameConnection::getConnectionToServer();
7301   if( !connection ) return false;
7302   ShapeBase *obj = dynamic_cast<ShapeBase*>(connection->getControlObject());
7303   return ( obj == this );
7304}
7305
7306
7307void Player::prepRenderImage( SceneRenderState* state )
7308{
7309   bool renderPlayer = true;
7310   bool renderItems = true;
7311
7312   /*
7313   if ( mPhysicsRep && Con::getBoolVariable("$PhysicsPlayer::DebugRender",false) )
7314   {
7315      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
7316      ri->renderDelegate.bind( mPhysicsRep, &PhysicsPlayer::renderDebug );
7317      ri->objectIndex = -1;
7318      ri->type = RenderPassManager::RIT_Editor;
7319      state->getRenderPass()->addInst( ri );
7320   }
7321   */
7322
7323   // Debug rendering for all convexes in the Players working list.
7324   if ( sRenderPlayerCollision )
7325   {
7326      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
7327      ri->renderDelegate.bind( this, &Player::renderConvex );
7328      ri->objectIndex = -1;
7329      ri->type = RenderPassManager::RIT_Editor;
7330      state->getRenderPass()->addInst( ri );
7331   }
7332
7333   GameConnection* connection = GameConnection::getConnectionToServer();
7334   if( connection && connection->getControlObject() == this && connection->isFirstPerson() )
7335   {
7336      // If we're first person and we are not rendering the player
7337      // then disable all shadow rendering... a floating gun shadow sucks.
7338      if ( state->isShadowPass() && !mDataBlock->renderFirstPerson && !mDataBlock->firstPersonShadows )
7339         return;
7340
7341      renderPlayer = mDataBlock->renderFirstPerson || !state->isDiffusePass();
7342
7343      if( !sRenderMyPlayer )
7344         renderPlayer = false;
7345      if( !sRenderMyItems )
7346         renderItems = false;
7347   }
7348
7349   // Call the protected base class to do the work 
7350   // now that we know if we're rendering the player
7351   // and mounted shapes.
7352   return ShapeBase::_prepRenderImage( state, 
7353                                       renderPlayer, 
7354                                       renderItems );
7355}
7356
7357void Player::renderConvex( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
7358{
7359   GFX->enterDebugEvent( ColorI(255,0,255), "Player_renderConvex" );
7360   mConvex.renderWorkingList();
7361   GFX->leaveDebugEvent();
7362}
7363
7364// static 
7365bool Player::sCorpsesHiddenFromRayCast = true; // this default matches stock Torque behavior.
7366
7367// static 
7368void Player::afx_consoleInit()
7369{
7370   Con::addVariable("pref::Player::corpsesHiddenFromRayCast", TypeBool, &sCorpsesHiddenFromRayCast);
7371}
7372
7373void Player::afx_init()
7374{
7375   overrideLookAnimation = false;
7376   armLookOverridePos = 0.5f;
7377   headVLookOverridePos = 0.5f;
7378   headHLookOverridePos = 0.5f;
7379   ignore_updates = false;
7380   fx_c_triggers = 0;
7381   mark_fx_c_triggers = 0;
7382   fx_s_triggers = 0;
7383   move_trigger_states = 0;
7384   z_velocity = 0.0f;
7385   mark_idle = false;
7386   idle_timer = 0.0f;
7387   mark_s_landing = false;
7388   speed_bias = 1.0f;
7389   speed_bias_goal = 1.0f;
7390   override_movement = 0;
7391   movement_data.zero();
7392   movement_op = 1;
7393   last_movement_tag = 0;
7394   footfallDecalOverride = 0;
7395   footfallSoundOverride = 0;
7396   footfallDustOverride = 0;
7397   noFootfallFX = false;
7398}
7399
7400U32 Player::afx_packUpdate(NetConnection* con, U32 mask, BitStream* stream, U32 retMask)
7401{
7402#if 0
7403   if (stream->writeFlag(mask & LookOverrideMask))
7404#else
7405   if (stream->writeFlag(mask & ActionMask))
7406#endif
7407      stream->writeFlag(overrideLookAnimation);
7408
7409   if (stream->writeFlag(mask & TriggerMask))
7410      stream->write(fx_s_triggers);
7411
7412   return retMask;
7413}
7414
7415void Player::afx_unpackUpdate(NetConnection* con, BitStream* stream)
7416{
7417   if (stream->readFlag()) // LookOverrideMask
7418      overrideLookAnimation = stream->readFlag();
7419
7420   if (stream->readFlag()) // TriggerMask
7421   {
7422      U32 mask;
7423      stream->read(&mask);
7424      mark_fx_c_triggers = mask;
7425   }
7426}
7427
7428// Code for overriding player's animation with sequences selected by the
7429// anim-clip component effect.
7430
7431void Player::restoreAnimation(U32 tag)
7432{
7433   // check if this is a blended clip
7434   if ((tag & BLENDED_CLIP) != 0)
7435   {
7436      restoreBlendAnimation(tag);
7437      return;
7438   }
7439
7440   if (tag != 0 && tag == last_anim_tag)
7441   {
7442      bool is_death_anim = ((anim_clip_flags & IS_DEATH_ANIM) != 0);
7443
7444      anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
7445
7446      if (isClientObject())
7447      {
7448         if (mDamageState != Enabled)
7449         {
7450            if (!is_death_anim)
7451            {
7452               // this is a bit hardwired and desperate,
7453               // but if he's dead he needs to look like it.
7454               setActionThread("death10", false, false, false);
7455            }
7456         }
7457         else if (mState != MoveState)
7458         {
7459            // not sure what happens here
7460         }
7461         else
7462         {
7463            pickActionAnimation();
7464         }
7465      }
7466
7467      last_anim_tag = 0;
7468      last_anim_id = -1;
7469   }
7470}
7471
7472U32 Player::getAnimationID(const char* name)
7473{
7474   for (U32 i = 0; i < mDataBlock->actionCount; i++)
7475   {
7476      PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
7477      if (dStricmp(anim.name, name) == 0)
7478         return i;
7479   }
7480
7481   Con::errorf("Player::getAnimationID() -- Player does not contain a sequence that matches the name, %s.", name);
7482   return BAD_ANIM_ID;
7483}
7484
7485U32 Player::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
7486{
7487   if (anim_id == BAD_ANIM_ID)
7488      return 0;
7489
7490   S32 seq_id = mDataBlock->actionList[anim_id].sequence;
7491   if (seq_id == -1)
7492   {
7493      Con::errorf("Player::playAnimation() problem. BAD_SEQ_ID");
7494      return 0;
7495   }
7496
7497   if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
7498      return playBlendAnimation(seq_id, pos, rate);
7499
7500   if (isClientObject())
7501   {
7502      PlayerData::ActionAnimation &anim = mDataBlock->actionList[anim_id];
7503      if (anim.sequence != -1) 
7504      {
7505         mActionAnimation.action          = anim_id;
7506         mActionAnimation.forward         = (rate >= 0);
7507         mActionAnimation.firstPerson     = false;
7508         mActionAnimation.holdAtEnd       = hold;
7509         mActionAnimation.waitForEnd      = hold? true: wait;
7510         mActionAnimation.animateOnServer = false;
7511         mActionAnimation.atEnd           = false;
7512         mActionAnimation.delayTicks      = (S32)sNewAnimationTickTime;
7513
7514         F32 transTime = (trans < 0) ? sAnimationTransitionTime : trans;  
7515
7516         mShapeInstance->setTimeScale(mActionAnimation.thread, rate);
7517         mShapeInstance->transitionToSequence(mActionAnimation.thread,anim.sequence,
7518            pos, transTime, true);
7519      }
7520   }
7521
7522   if (is_death_anim)
7523      anim_clip_flags |= IS_DEATH_ANIM;
7524   else
7525      anim_clip_flags &= ~<a href="/coding/class/classshapebase/#classshapebase_1aaccf6aef17f289e10ee8665c2fb0393dad8a89251dde3099496492fd6e571185d">IS_DEATH_ANIM</a>;
7526
7527   anim_clip_flags |= ANIM_OVERRIDDEN;
7528   last_anim_tag = unique_anim_tag_counter++;
7529   last_anim_id = anim_id;
7530
7531   return last_anim_tag;
7532}
7533
7534F32 Player::getAnimationDurationByID(U32 anim_id)
7535{
7536   if (anim_id == BAD_ANIM_ID)
7537      return 0.0f;
7538   S32 seq_id = mDataBlock->actionList[anim_id].sequence;
7539   if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
7540      return mDataBlock->mShape->sequences[seq_id].duration;
7541
7542   return 0.0f;
7543}
7544
7545bool Player::isBlendAnimation(const char* name)
7546{
7547   U32 anim_id = getAnimationID(name);
7548   if (anim_id == BAD_ANIM_ID)
7549      return false;
7550
7551   S32 seq_id = mDataBlock->actionList[anim_id].sequence;
7552   if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
7553      return mDataBlock->mShape->sequences[seq_id].isBlend();
7554
7555   return false;
7556}
7557
7558const char* Player::getLastClipName(U32 clip_tag)
7559{ 
7560   if (clip_tag != last_anim_tag || last_anim_id >= PlayerData::NumActionAnims)
7561      return "";
7562
7563   return mDataBlock->actionList[last_anim_id].name;
7564}
7565
7566void Player::unlockAnimation(U32 tag, bool force)
7567{
7568   if ((tag != 0 && tag == last_anim_lock_tag) || force)
7569      anim_clip_flags &= ~<a href="/coding/class/classshapebase/#classshapebase_1aaccf6aef17f289e10ee8665c2fb0393da23a6889006625df265e4763333c69fdf">BLOCK_USER_CONTROL</a>;
7570}
7571
7572U32 Player::lockAnimation()
7573{
7574   anim_clip_flags |= BLOCK_USER_CONTROL;
7575   last_anim_lock_tag = unique_anim_tag_counter++;
7576
7577   return last_anim_lock_tag;
7578}
7579
7580DefineEngineMethod(Player, isAnimationLocked, bool, (),, "")
7581{
7582   return object->isAnimationLocked();
7583}
7584
7585
7586void Player::setLookAnimationOverride(bool flag) 
7587{ 
7588   overrideLookAnimation = flag; 
7589#if 0
7590   setMaskBits(LookOverrideMask);
7591#else
7592   setMaskBits(ActionMask);
7593#endif
7594}
7595
7596DefineEngineMethod(Player, setLookAnimationOverride, void, (bool flag),, "")
7597{
7598   object->setLookAnimationOverride(flag);
7599}
7600
7601DefineEngineMethod(Player, copyHeadRotation, void, (Player* other_player),, "")
7602{
7603   if (other_player)
7604      object->copyHeadRotation(other_player);
7605}
7606void Player::process_client_triggers(bool triggeredLeft, bool triggeredRight)
7607{
7608   bool mark_landing = false;
7609   Point3F my_vel = getVelocity();
7610   if (my_vel.z > 5.0f)
7611      z_velocity = 1;
7612   else if (my_vel.z < -5.0f)
7613      z_velocity = -1;
7614   else
7615   {
7616      if (z_velocity < 0)
7617         mark_landing = true;
7618      z_velocity = 0.0f;
7619   }
7620
7621   fx_c_triggers = mark_fx_c_triggers;
7622   if (triggeredLeft)
7623      fx_c_triggers |= PLAYER_LF_FOOT_C_TRIGGER;
7624   if (triggeredRight)
7625      fx_c_triggers |= PLAYER_RT_FOOT_C_TRIGGER;
7626   if (mark_landing)
7627      fx_c_triggers |= PLAYER_LANDING_C_TRIGGER;
7628   if (idle_timer > 10.0f)
7629   {
7630      fx_c_triggers |= PLAYER_IDLE_C_TRIGGER;
7631      idle_timer = 0.0f;
7632   }
7633   if (fx_c_triggers & PLAYER_LANDING_S_TRIGGER)
7634   {
7635      fx_c_triggers &= ~(PLAYER_LANDING_S_TRIGGER);
7636   }
7637}
7638U32 Player::unique_movement_tag_counter = 1;
7639
7640void Player::setMovementSpeedBias(F32 bias) 
7641{ 
7642   speed_bias_goal = bias; 
7643}
7644
7645U32 Player::setMovementOverride(F32 bias, const Point3F* mov, U32 op) 
7646{ 
7647   if (mov)
7648   {
7649      movement_data = *mov;
7650      override_movement = true;
7651      movement_op = (U8)op;
7652   }
7653   else
7654      override_movement = false;
7655
7656   speed_bias_goal = bias;
7657
7658   last_movement_tag = unique_movement_tag_counter++;
7659   return last_movement_tag;
7660}
7661
7662void Player::restoreMovement(U32 tag) 
7663{ 
7664   if (tag != 0 && tag == last_movement_tag)
7665   {
7666      speed_bias_goal = 1.0;
7667      override_movement = false;
7668   }
7669}
7670
7671DefineEngineMethod(Player, setMovementSpeedBias, void, (F32 bias),, "setMovementSpeedBias(F32 bias)")
7672{
7673   object->setMovementSpeedBias(bias);
7674}
7675
7676void Player::overrideFootfallFX(bool decals, bool sounds, bool dust) 
7677{ 
7678   if (decals)
7679      footfallDecalOverride++; 
7680   if (sounds)
7681      footfallSoundOverride++; 
7682   if (dust)
7683      footfallDustOverride++; 
7684   noFootfallFX = (footfallDecalOverride > 0 && footfallSoundOverride > 0 && footfallDustOverride > 0);
7685}
7686
7687void Player::restoreFootfallFX(bool decals, bool sounds, bool dust) 
7688{ 
7689   if (decals && footfallDecalOverride) 
7690      footfallDecalOverride--; 
7691   if (sounds && footfallSoundOverride) 
7692      footfallSoundOverride--; 
7693   if (dust && footfallDustOverride) 
7694      footfallDustOverride--; 
7695   noFootfallFX = (footfallDecalOverride > 0 && footfallSoundOverride > 0 && footfallDustOverride > 0);
7696}
7697#ifdef TORQUE_OPENVR
7698void Player::setControllers(Vector<OpenVRTrackedObject*> controllerList)
7699{
7700   mControllers[0] = controllerList.size() > 0 ? controllerList[0] : NULL;
7701   mControllers[1] = controllerList.size() > 1 ? controllerList[1] : NULL;
7702}
7703
7704DefineEngineMethod(Player, setVRControllers, void, (OpenVRTrackedObject* controllerL, OpenVRTrackedObject* controllerR,, "")
7705{
7706   Vector<OpenVRTrackedObject*> list;
7707
7708   if (controllerL)
7709   {
7710      list.push_back(controllerL);
7711   }
7712   else
7713   {
7714      list.push_back(NULL);
7715   }
7716
7717   if (controllerR)
7718   {
7719      list.push_back(controllerR);
7720   }
7721   else
7722   {
7723      list.push_back(NULL);
7724   }
7725
7726   object->setControllers(list);
7727}
7728
7729#endif
7730