player.cpp
Public Defines
define
LH_HACK() 1
Public Enumerations
enum
PlayerConstants { JumpSkipContactsMax = 8 }
Public Variables
Public Functions
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(¢er); 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(¢er); 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