item.cpp

Engine/source/T3D/item.cpp

More...

Public Functions

ConsoleDocClass(Item , "@brief Base <a href="/coding/class/classitem/">Item</a> class. Uses the <a href="/coding/class/structitemdata/">ItemData</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> common <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">properties.\n\n</a>" "Items represent an object in the world)
ConsoleDocClass(ItemData , "@brief Stores properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an individual <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n\n</a>" "Items represent an object in the world, usually one that the player will interact with. " "One example is a health kit on the group that is automatically picked up when the player " "comes into contact with <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n\n</a>" "<a href="/coding/class/structitemdata/">ItemData</a> provides the common properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> a set of Items. These properties include a " "DTS or DAE model used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> render the <a href="/coding/class/classitem/">Item</a> in the world, its physical properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> when the " "<a href="/coding/class/classitem/">Item</a> interacts with the  world, and any lights that emit " "from the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Item.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/structitemdata/">ItemData</a>(HealthKitSmall)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " category=\"Health\";\n" "   className = \"HealthPatch\";\n" "   shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n" "   gravityMod = \"1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   mass = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   friction = 1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   elasticity = 0.3;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   density = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   drag = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxVelocity = \"10.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emap = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   sticky = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   dynamicType = \"0\"\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>;" "   lightOnlyStatic = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightType = \"NoLight\";\n" "   lightColor = \"1.0 1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightTime = 1000;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightRadius = 10.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   simpleServerCollision = true;" "   // Dynamic properties used by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts\n\n</a>" "   pickupName = \"a small health kit\";\n" "   repairAmount = 50;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
DefineEngineMethod(Item , getLastStickyNormal , const char * , () )
DefineEngineMethod(Item , getLastStickyPos , const char * , () )
DefineEngineMethod(Item , isAtRest , bool , () , "@brief Is the object at rest (ie, no longer moving)?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the object is at rest)
DefineEngineMethod(Item , isRotating , bool , () , "@brief Is the object still rotating?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the object is still rotating)
DefineEngineMethod(Item , isStatic , bool , () , "@brief Is the object static (ie, non-movable)?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the object is static)
DefineEngineMethod(Item , setCollisionTimeout , bool , (S32 ignoreColObj) , (NULL) , "@brief Temporarily disable collisions against a specific <a href="/coding/class/classshapebase/">ShapeBase</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "This is useful <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> prevent a player from immediately picking up an <a href="/coding/class/classitem/">Item</a> they have " "just thrown. Only one object may be on the timeout list at a time. The timeout is " "defined as 15 <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ticks.\n\n</a>" "@param objectID <a href="/coding/class/classshapebase/">ShapeBase</a> object ID <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable collisions <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">against.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the <a href="/coding/class/classshapebase/">ShapeBase</a> object requested could be found)
IMPLEMENT_CALLBACK(Item , onEnterLiquid , void , (const char *objID, F32 waterCoverage, const char *liquidType) , (objID, waterCoverage, liquidType) , "Informs an <a href="/coding/class/classitem/">Item</a> object that it has entered liquid, along with information about the liquid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n</a>" " @param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @param waterCoverage How much coverage of water this <a href="/coding/class/classitem/">Item</a> object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">has.\n</a>" " @param liquidType The type of liquid that this <a href="/coding/class/classitem/">Item</a> object has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered.\n</a>" " @note Server side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n</a>" " @see Item, <a href="/coding/class/structitemdata/">ItemData</a> , <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">WaterObject\n</a>" )
IMPLEMENT_CALLBACK(Item , onLeaveLiquid , void , (const char *objID, const char *liquidType) , (objID, liquidType) , "Informs an <a href="/coding/class/classitem/">Item</a> object that it has left a liquid, along with information about the liquid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n</a>" " @param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @param liquidType The type of liquid that this <a href="/coding/class/classitem/">Item</a> object has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left.\n</a>" " @note Server side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n</a>" " @see Item, <a href="/coding/class/structitemdata/">ItemData</a> , <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">WaterObject\n</a>" )
IMPLEMENT_CALLBACK(Item , onStickyCollision , void , (const char *objID) , (objID) , "@brief Informs the <a href="/coding/class/classitem/">Item</a> object that it is now sticking <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "This callback is only called <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the <a href="/coding/class/structitemdata/#structitemdata_1a351749d1f39ac3c5342334188c44c8c3">ItemData::sticky</a> property <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/class/classitem/">Item</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" "@param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> ID this <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@note Server side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n</a>" "@see Item, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ItemData\n</a>" )
ImplementEnumType(ItemLightType , "@brief The type of light the <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">has\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )

Detailed Description

Public Variables

MatrixF IMat (1)
const F32 sAtRestVelocity 
const U32 sClientCollisionMask 
const S32 sCollisionTimeout 
S32 sMaxWarpTicks 
F32 sMinWarpTicks 
const F32 sRotationSpeed 
const U32 sServerCollisionMask 

Public Functions

ConsoleDocClass(Item , "@brief Base <a href="/coding/class/classitem/">Item</a> class. Uses the <a href="/coding/class/structitemdata/">ItemData</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> common <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">properties.\n\n</a>" "Items represent an object in the world)

ConsoleDocClass(ItemData , "@brief Stores properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an individual <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n\n</a>" "Items represent an object in the world, usually one that the player will interact with. " "One example is a health kit on the group that is automatically picked up when the player " "comes into contact with <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n\n</a>" "<a href="/coding/class/structitemdata/">ItemData</a> provides the common properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> a set of Items. These properties include a " "DTS or DAE model used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> render the <a href="/coding/class/classitem/">Item</a> in the world, its physical properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> when the " "<a href="/coding/class/classitem/">Item</a> interacts with the  world, and any lights that emit " "from the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Item.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/structitemdata/">ItemData</a>(HealthKitSmall)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " category=\"Health\";\n" "   className = \"HealthPatch\";\n" "   shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n" "   gravityMod = \"1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   mass = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   friction = 1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   elasticity = 0.3;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   density = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   drag = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxVelocity = \"10.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emap = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   sticky = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   dynamicType = \"0\"\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>;" "   lightOnlyStatic = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightType = \"NoLight\";\n" "   lightColor = \"1.0 1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightTime = 1000;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightRadius = 10.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   simpleServerCollision = true;" "   // Dynamic properties used by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts\n\n</a>" "   pickupName = \"a small health kit\";\n" "   repairAmount = 50;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )

DefineEngineMethod(Item , getLastStickyNormal , const char * , () )

DefineEngineMethod(Item , getLastStickyPos , const char * , () )

DefineEngineMethod(Item , isAtRest , bool , () , "@brief Is the object at rest (ie, no longer moving)?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the object is at rest)

DefineEngineMethod(Item , isRotating , bool , () , "@brief Is the object still rotating?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the object is still rotating)

DefineEngineMethod(Item , isStatic , bool , () , "@brief Is the object static (ie, non-movable)?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the object is static)

DefineEngineMethod(Item , setCollisionTimeout , bool , (S32 ignoreColObj) , (NULL) , "@brief Temporarily disable collisions against a specific <a href="/coding/class/classshapebase/">ShapeBase</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "This is useful <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> prevent a player from immediately picking up an <a href="/coding/class/classitem/">Item</a> they have " "just thrown. Only one object may be on the timeout list at a time. The timeout is " "defined as 15 <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ticks.\n\n</a>" "@param objectID <a href="/coding/class/classshapebase/">ShapeBase</a> object ID <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> disable collisions <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">against.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the <a href="/coding/class/classshapebase/">ShapeBase</a> object requested could be found)

IMPLEMENT_CALLBACK(Item , onEnterLiquid , void , (const char *objID, F32 waterCoverage, const char *liquidType) , (objID, waterCoverage, liquidType) , "Informs an <a href="/coding/class/classitem/">Item</a> object that it has entered liquid, along with information about the liquid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n</a>" " @param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @param waterCoverage How much coverage of water this <a href="/coding/class/classitem/">Item</a> object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">has.\n</a>" " @param liquidType The type of liquid that this <a href="/coding/class/classitem/">Item</a> object has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered.\n</a>" " @note Server side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n</a>" " @see Item, <a href="/coding/class/structitemdata/">ItemData</a> , <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">WaterObject\n</a>" )

IMPLEMENT_CALLBACK(Item , onLeaveLiquid , void , (const char *objID, const char *liquidType) , (objID, liquidType) , "Informs an <a href="/coding/class/classitem/">Item</a> object that it has left a liquid, along with information about the liquid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n</a>" " @param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @param liquidType The type of liquid that this <a href="/coding/class/classitem/">Item</a> object has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left.\n</a>" " @note Server side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n</a>" " @see Item, <a href="/coding/class/structitemdata/">ItemData</a> , <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">WaterObject\n</a>" )

IMPLEMENT_CALLBACK(Item , onStickyCollision , void , (const char *objID) , (objID) , "@brief Informs the <a href="/coding/class/classitem/">Item</a> object that it is now sticking <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "This callback is only called <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the <a href="/coding/class/structitemdata/#structitemdata_1a351749d1f39ac3c5342334188c44c8c3">ItemData::sticky</a> property <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/class/classitem/">Item</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" "@param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> ID this <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@note Server side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n</a>" "@see Item, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ItemData\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(ItemData )

IMPLEMENT_CO_NETOBJECT_V1(Item )

ImplementEnumType(ItemLightType , "@brief The type of light the <a href="/coding/class/classitem/">Item</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">has\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#include "platform/platform.h"
  25#include "T3D/item.h"
  26
  27#include "core/stream/bitStream.h"
  28#include "math/mMath.h"
  29#include "console/console.h"
  30#include "console/consoleTypes.h"
  31#include "sim/netConnection.h"
  32#include "collision/boxConvex.h"
  33#include "collision/earlyOutPolyList.h"
  34#include "collision/extrudedPolyList.h"
  35#include "math/mPolyhedron.h"
  36#include "math/mathIO.h"
  37#include "lighting/lightInfo.h"
  38#include "lighting/lightManager.h"
  39#include "T3D/physics/physicsPlugin.h"
  40#include "T3D/physics/physicsBody.h"
  41#include "T3D/physics/physicsCollision.h"
  42#include "ts/tsShapeInstance.h"
  43#include "console/engineAPI.h"
  44
  45
  46const F32 sRotationSpeed = 6.0f;        // Secs/Rotation
  47const F32 sAtRestVelocity = 0.15f;      // Min speed after collision
  48const S32 sCollisionTimeout = 15;       // Timout value in ticks
  49
  50// Client prediction
  51static F32 sMinWarpTicks = 0.5 ;        // Fraction of tick at which instant warp occures
  52static S32 sMaxWarpTicks = 3;           // Max warp duration in ticks
  53
  54F32 Item::mGravity = -20.0f;
  55
  56const U32 sClientCollisionMask = (TerrainObjectType     |
  57                                  StaticShapeObjectType |
  58                                  VehicleObjectType     |  
  59                                  PlayerObjectType);
  60
  61const U32 sServerCollisionMask = (sClientCollisionMask);
  62
  63const S32 Item::csmAtRestTimer = 64;
  64
  65//----------------------------------------------------------------------------
  66
  67IMPLEMENT_CO_DATABLOCK_V1(ItemData);
  68
  69ConsoleDocClass( ItemData,
  70   "@brief Stores properties for an individual Item type.\n\n"   
  71
  72   "Items represent an object in the world, usually one that the player will interact with.  "
  73   "One example is a health kit on the group that is automatically picked up when the player "
  74   "comes into contact with it.\n\n"
  75
  76   "ItemData provides the common properties for a set of Items.  These properties include a "
  77   "DTS or DAE model used to render the Item in the world, its physical properties for when the "
  78   "Item interacts with the world (such as being tossed by the player), and any lights that emit "
  79   "from the Item.\n\n"
  80
  81   "@tsexample\n"
  82      "datablock ItemData(HealthKitSmall)\n"
  83      "{\n"
  84      "   category =\"Health\";\n"
  85      "   className = \"HealthPatch\";\n"
  86      "   shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n"
  87      "   gravityMod = \"1.0\";\n"
  88      "   mass = 2;\n"
  89      "   friction = 1;\n"
  90      "   elasticity = 0.3;\n"
  91      "   density = 2;\n"
  92      "   drag = 0.5;\n"
  93      "   maxVelocity = \"10.0\";\n"
  94      "   emap = true;\n"
  95      "   sticky = false;\n"
  96      "   dynamicType = \"0\"\n;"
  97      "   lightOnlyStatic = false;\n"
  98      "   lightType = \"NoLight\";\n"
  99      "   lightColor = \"1.0 1.0 1.0 1.0\";\n"
 100      "   lightTime = 1000;\n"
 101      "   lightRadius = 10.0;\n"
 102      "   simpleServerCollision = true;"
 103      "   // Dynamic properties used by the scripts\n\n"
 104      "   pickupName = \"a small health kit\";\n"
 105      "   repairAmount = 50;\n"
 106      "};\n"
 107   "@endtsexample\n"
 108
 109   "@ingroup gameObjects\n"
 110);
 111
 112
 113ItemData::ItemData()
 114{
 115   shadowEnable = true;
 116
 117
 118   friction = 0;
 119   elasticity = 0;
 120
 121   sticky = false;
 122   gravityMod = 1.0;
 123   maxVelocity = 25.0f;
 124
 125   density = 2;
 126   drag = 0.5;
 127
 128   lightOnlyStatic = false;
 129   lightType = Item::NoLight;
 130   lightColor.set(1.f,1.f,1.f,1.f);
 131   lightTime = 1000;
 132   lightRadius = 10.f; 
 133
 134   simpleServerCollision = true;
 135}
 136
 137ImplementEnumType( ItemLightType,
 138   "@brief The type of light the Item has\n\n"
 139   "@ingroup gameObjects\n\n")
 140   { Item::NoLight,           "NoLight",        "The item has no light attached.\n" },
 141   { Item::ConstantLight,     "ConstantLight",  "The item has a constantly emitting light attached.\n" },
 142   { Item::PulsingLight,      "PulsingLight",   "The item has a pulsing light attached.\n" }
 143EndImplementEnumType;
 144
 145void ItemData::initPersistFields()
 146{
 147   addField("friction",          TypeF32,       Offset(friction,           ItemData), "A floating-point value specifying how much velocity is lost to impact and sliding friction.");
 148   addField("elasticity",        TypeF32,       Offset(elasticity,         ItemData), "A floating-point value specifying how 'bouncy' this ItemData is.");
 149   addField("sticky",            TypeBool,      Offset(sticky,             ItemData), 
 150      "@brief If true, ItemData will 'stick' to any surface it collides with.\n\n"
 151      "When an item does stick to a surface, the Item::onStickyCollision() callback is called.  The Item has methods to retrieve "
 152      "the world position and normal the Item is stuck to.\n"
 153      "@note Valid objects to stick to must be of StaticShapeObjectType.\n");
 154   addField("gravityMod",        TypeF32,       Offset(gravityMod,         ItemData), "Floating point value to multiply the existing gravity with, just for this ItemData.");
 155   addField("maxVelocity",       TypeF32,       Offset(maxVelocity,        ItemData), "Maximum velocity that this ItemData is able to move.");
 156
 157   addField("lightType",         TYPEID< Item::LightType >(),      Offset(lightType, ItemData), "Type of light to apply to this ItemData. Options are NoLight, ConstantLight, PulsingLight. Default is NoLight." );
 158   addField("lightColor",        TypeColorF,    Offset(lightColor,         ItemData),
 159      "@brief Color value to make this light. Example: \"1.0,1.0,1.0\"\n\n"
 160      "@see lightType\n");
 161   addField("lightTime",         TypeS32,       Offset(lightTime,          ItemData), 
 162      "@brief Time value for the light of this ItemData, used to control the pulse speed of the PulsingLight LightType.\n\n"
 163      "@see lightType\n");
 164   addField("lightRadius",       TypeF32,       Offset(lightRadius,        ItemData), 
 165      "@brief Distance from the center point of this ItemData for the light to affect\n\n"
 166      "@see lightType\n");
 167   addField("lightOnlyStatic",   TypeBool,      Offset(lightOnlyStatic,    ItemData), 
 168      "@brief If true, this ItemData will only cast a light if the Item for this ItemData has a static value of true.\n\n"
 169      "@see lightType\n");
 170
 171   addField("simpleServerCollision",   TypeBool,  Offset(simpleServerCollision,    ItemData), 
 172      "@brief Determines if only simple server-side collision will be used (for pick ups).\n\n"
 173      "If set to true then only simple, server-side collision detection will be used.  This is often the case "
 174      "if the item is used for a pick up object, such as ammo.  If set to false then a full collision volume "
 175      "will be used as defined by the shape.  The default is true.\n"
 176      "@note Only applies when using a physics library.\n"
 177      "@see TurretShape and ProximityMine for examples that should set this to false to allow them to be "
 178      "shot by projectiles.\n");
 179
 180   Parent::initPersistFields();
 181}
 182
 183void ItemData::packData(BitStream* stream)
 184{
 185   Parent::packData(stream);
 186   stream->writeFloat(friction, 10);
 187   stream->writeFloat(elasticity, 10);
 188   stream->writeFlag(sticky);
 189   if(stream->writeFlag(gravityMod != 1.0))
 190      stream->writeFloat(gravityMod, 10);
 191   if(stream->writeFlag(maxVelocity != -1))
 192      stream->write(maxVelocity);
 193
 194   if(stream->writeFlag(lightType != Item::NoLight))
 195   {
 196      AssertFatal(Item::NumLightTypes < (1 << 2), "ItemData: light type needs more bits");
 197      stream->writeInt(lightType, 2);
 198      stream->writeFloat(lightColor.red, 7);
 199      stream->writeFloat(lightColor.green, 7);
 200      stream->writeFloat(lightColor.blue, 7);
 201      stream->writeFloat(lightColor.alpha, 7);
 202      stream->write(lightTime);
 203      stream->write(lightRadius);
 204      stream->writeFlag(lightOnlyStatic);
 205   }
 206
 207   stream->writeFlag(simpleServerCollision);
 208}
 209
 210void ItemData::unpackData(BitStream* stream)
 211{
 212   Parent::unpackData(stream);
 213   friction = stream->readFloat(10);
 214   elasticity = stream->readFloat(10);
 215   sticky = stream->readFlag();
 216   if(stream->readFlag())
 217      gravityMod = stream->readFloat(10);
 218   else
 219      gravityMod = 1.0;
 220
 221   if(stream->readFlag())
 222      stream->read(&maxVelocity);
 223   else
 224      maxVelocity = -1;
 225
 226   if(stream->readFlag())
 227   {
 228      lightType = stream->readInt(2);
 229      lightColor.red = stream->readFloat(7);
 230      lightColor.green = stream->readFloat(7);
 231      lightColor.blue = stream->readFloat(7);
 232      lightColor.alpha = stream->readFloat(7);
 233      stream->read(&lightTime);
 234      stream->read(&lightRadius);
 235      lightOnlyStatic = stream->readFlag();
 236   }
 237   else
 238      lightType = Item::NoLight;
 239
 240   simpleServerCollision = stream->readFlag();
 241}
 242
 243
 244//----------------------------------------------------------------------------
 245
 246IMPLEMENT_CO_NETOBJECT_V1(Item);
 247
 248ConsoleDocClass( Item,
 249   "@brief Base Item class. Uses the ItemData datablock for common properties.\n\n"   
 250
 251   "Items represent an object in the world, usually one that the player will interact with.  "
 252   "One example is a health kit on the group that is automatically picked up when the player "
 253   "comes into contact with it.\n\n"
 254
 255   "@tsexample\n"
 256      "// This is the \"health patch\" dropped by a dying player.\n"
 257      "datablock ItemData(HealthKitPatch)\n"
 258      "{\n"
 259      "   // Mission editor category, this datablock will show up in the\n"
 260      "   // specified category under the \"shapes\" root category.\n"
 261      "   category = \"Health\";\n\n"
 262      "   className = \"HealthPatch\";\n\n"
 263      "   // Basic Item properties\n"
 264      "   shapeFile = \"art/shapes/items/patch/healthpatch.dts\";\n"
 265      "   mass = 2;\n"
 266      "   friction = 1;\n"
 267      "   elasticity = 0.3;\n"
 268      "   emap = true;\n\n"
 269      "   // Dynamic properties used by the scripts\n"
 270      "   pickupName = \"a health patch\";\n"
 271      "   repairAmount = 50;\n"
 272      "};\n\n"
 273
 274      "%obj = new Item()\n"
 275      "{\n"
 276      "  dataBlock = HealthKitSmall;\n"
 277      "  parentGroup = EWCreatorWindow.objectGroup;\n"
 278      "  static = true;\n"
 279      "  rotate = true;\n"
 280      "};\n"
 281   "@endtsexample\n\n"
 282
 283   "@see ItemData\n"
 284
 285   "@ingroup gameObjects\n"
 286);
 287
 288IMPLEMENT_CALLBACK( Item, onStickyCollision, void, ( const char* objID ),( objID ),
 289   "@brief Informs the Item object that it is now sticking to another object.\n\n"
 290   "This callback is only called if the ItemData::sticky property for this Item is true.\n"
 291   "@param objID Object ID this Item object.\n"
 292   "@note Server side only.\n"
 293   "@see Item, ItemData\n"
 294);
 295
 296IMPLEMENT_CALLBACK( Item, onEnterLiquid, void, ( const char* objID, F32 waterCoverage, const char* liquidType ),( objID, waterCoverage, liquidType ),
 297   "Informs an Item object that it has entered liquid, along with information about the liquid type.\n"
 298   "@param objID Object ID for this Item object.\n"
 299   "@param waterCoverage How much coverage of water this Item object has.\n"
 300   "@param liquidType The type of liquid that this Item object has entered.\n"
 301   "@note Server side only.\n"
 302   "@see Item, ItemData, WaterObject\n"
 303);
 304
 305IMPLEMENT_CALLBACK( Item, onLeaveLiquid, void, ( const char* objID, const char* liquidType ),( objID, liquidType ),
 306   "Informs an Item object that it has left a liquid, along with information about the liquid type.\n"
 307   "@param objID Object ID for this Item object.\n"
 308   "@param liquidType The type of liquid that this Item object has left.\n"
 309   "@note Server side only.\n"
 310   "@see Item, ItemData, WaterObject\n"
 311);
 312
 313
 314Item::Item()
 315{
 316   mTypeMask |= ItemObjectType | DynamicShapeObjectType;
 317   mDataBlock = 0;
 318   mStatic = false;
 319   mRotate = false;
 320   mVelocity = VectorF(0,0,0);
 321   mAtRest = true;
 322   mAtRestCounter = 0;
 323   mInLiquid = false;
 324   delta.warpTicks = 0;
 325   delta.dt = 1;
 326   mCollisionObject = 0;
 327   mCollisionTimeout = 0;
 328   mPhysicsRep = NULL;
 329
 330   mConvex.init(this);
 331   mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
 332   mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
 333
 334   mLight = NULL;
 335
 336   mSubclassItemHandlesScene = false;
 337}
 338
 339Item::~Item()
 340{
 341   SAFE_DELETE(mLight);
 342}
 343
 344
 345//----------------------------------------------------------------------------
 346
 347bool Item::onAdd()
 348{
 349   if (!Parent::onAdd() || !mDataBlock)
 350      return false;
 351
 352   if (mStatic)
 353      mAtRest = true;
 354   mObjToWorld.getColumn(3,&delta.pos);
 355
 356   // Setup the box for our convex object...
 357   mObjBox.getCenter(&mConvex.mCenter);
 358   mConvex.mSize.x = mObjBox.len_x() / 2.0;
 359   mConvex.mSize.y = mObjBox.len_y() / 2.0;
 360   mConvex.mSize.z = mObjBox.len_z() / 2.0;
 361   mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
 362   mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
 363
 364   if( !isHidden() && !mSubclassItemHandlesScene )
 365      addToScene();
 366
 367   if (isServerObject())
 368   {
 369      if (!mSubclassItemHandlesScene)
 370         scriptOnAdd();
 371   }
 372   else if (mDataBlock->lightType != NoLight)
 373   {
 374      mDropTime = Sim::getCurrentTime();
 375   }
 376
 377   _updatePhysics();
 378
 379   return true;
 380}
 381
 382void Item::_updatePhysics()
 383{
 384   SAFE_DELETE( mPhysicsRep );
 385
 386   if ( !PHYSICSMGR )
 387      return;
 388
 389   if (mDataBlock->simpleServerCollision)
 390   {
 391      // We only need the trigger on the server.
 392      if ( isServerObject() )
 393      {
 394         PhysicsCollision *colShape = PHYSICSMGR->createCollision();
 395         colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
 396
 397         PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
 398         mPhysicsRep = PHYSICSMGR->createBody();
 399         mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
 400         mPhysicsRep->setTransform( getTransform() );
 401      }
 402   }
 403   else
 404   {
 405      if ( !mShapeInstance )
 406         return;
 407
 408      PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
 409
 410      if ( colShape )
 411      {
 412         PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
 413         mPhysicsRep = PHYSICSMGR->createBody();
 414         mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
 415         mPhysicsRep->setTransform( getTransform() );
 416      }
 417   }
 418}
 419
 420bool Item::onNewDataBlock( GameBaseData *dptr, bool reload )
 421{
 422   mDataBlock = dynamic_cast<ItemData*>(dptr);
 423   if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
 424      return false;
 425
 426   if (!mSubclassItemHandlesScene)
 427      scriptOnNewDataBlock();
 428
 429   if ( isProperlyAdded() )
 430      _updatePhysics();
 431
 432   return true;
 433}
 434
 435void Item::onRemove()
 436{
 437   mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
 438   mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
 439
 440   SAFE_DELETE( mPhysicsRep );
 441
 442   if (!mSubclassItemHandlesScene)
 443   {
 444      scriptOnRemove();
 445      removeFromScene();
 446   }
 447
 448   Parent::onRemove();
 449}
 450
 451void Item::onDeleteNotify( SimObject *obj )
 452{
 453   if ( obj == mCollisionObject ) 
 454   {
 455      mCollisionObject = NULL;
 456      mCollisionTimeout = 0;
 457   }
 458
 459   Parent::onDeleteNotify( obj );
 460}
 461
 462// Lighting: -----------------------------------------------------------------
 463
 464void Item::registerLights(LightManager * lightManager, bool lightingScene)
 465{
 466   if(lightingScene)
 467      return;
 468
 469   if(mDataBlock->lightOnlyStatic && !mStatic)
 470      return;
 471
 472   F32 intensity;
 473   switch(mDataBlock->lightType)
 474   {
 475      case ConstantLight:
 476         intensity = mFadeVal;
 477         break;
 478
 479      case PulsingLight:
 480      {
 481         S32 delta = Sim::getCurrentTime() - mDropTime;
 482         intensity = 0.5f + 0.5f * mSin(M_PI_F * F32(delta) / F32(mDataBlock->lightTime));
 483         intensity = 0.15f + intensity * 0.85f;
 484         intensity *= mFadeVal;  // fade out light on flags
 485         break;
 486      }
 487
 488      default:
 489         return;
 490   }
 491
 492   // Create a light if needed
 493   if (!mLight)
 494   {
 495      mLight = lightManager->createLightInfo();
 496   }   
 497   mLight->setColor( mDataBlock->lightColor * intensity );
 498   mLight->setType( LightInfo::Point );
 499   mLight->setRange( mDataBlock->lightRadius );
 500   mLight->setPosition( getBoxCenter() );
 501
 502   lightManager->registerGlobalLight( mLight, this );
 503}
 504
 505
 506//----------------------------------------------------------------------------
 507
 508Point3F Item::getVelocity() const
 509{
 510   return mVelocity;
 511}
 512
 513void Item::setVelocity(const VectorF& vel)
 514{
 515   mVelocity = vel;
 516
 517   // Clamp against the maximum velocity.
 518   if ( mDataBlock->maxVelocity > 0 )
 519   {
 520      F32 len = mVelocity.magnitudeSafe();
 521      if ( len > mDataBlock->maxVelocity )
 522      {
 523         Point3F excess = mVelocity * ( 1.0f - (mDataBlock->maxVelocity / len ) );
 524         mVelocity -= excess;
 525      }
 526   }
 527
 528   setMaskBits(PositionMask);
 529   mAtRest = false;
 530   mAtRestCounter = 0;
 531}
 532
 533void Item::applyImpulse(const Point3F&,const VectorF& vec)
 534{
 535   // Items ignore angular velocity
 536   VectorF vel;
 537   vel.x = vec.x / mDataBlock->mass;
 538   vel.y = vec.y / mDataBlock->mass;
 539   vel.z = vec.z / mDataBlock->mass;
 540   setVelocity(vel);
 541}
 542
 543void Item::setCollisionTimeout(ShapeBase* obj)
 544{
 545   if (mCollisionObject)
 546      clearNotify(mCollisionObject);
 547   deleteNotify(obj);
 548   mCollisionObject = obj;
 549   mCollisionTimeout = sCollisionTimeout;
 550   setMaskBits(ThrowSrcMask);
 551}
 552
 553
 554//----------------------------------------------------------------------------
 555
 556void Item::processTick(const Move* move)
 557{
 558   Parent::processTick(move);
 559
 560   if ( isMounted() )
 561      return;
 562
 563   //
 564   if (mCollisionObject && !--mCollisionTimeout)
 565      mCollisionObject = 0;
 566
 567   // Warp to catch up to server
 568   if (delta.warpTicks > 0)
 569   {
 570      delta.warpTicks--;
 571
 572      // Set new pos.
 573      MatrixF mat = mObjToWorld;
 574      mat.getColumn(3,&delta.pos);
 575      delta.pos += delta.warpOffset;
 576      mat.setColumn(3,delta.pos);
 577      Parent::setTransform(mat);
 578
 579      // Backstepping
 580      delta.posVec.x = -delta.warpOffset.x;
 581      delta.posVec.y = -delta.warpOffset.y;
 582      delta.posVec.z = -delta.warpOffset.z;
 583   }
 584   else
 585   {
 586      if (isServerObject() && mAtRest && (mStatic == false && mDataBlock->sticky == false))
 587      {
 588         if (++mAtRestCounter > csmAtRestTimer)
 589         {
 590            mAtRest = false;
 591            mAtRestCounter = 0;
 592            setMaskBits(PositionMask);
 593         }
 594      }
 595
 596      if (!mStatic && !mAtRest && isHidden() == false)
 597      {
 598         updateVelocity(TickSec);
 599         updateWorkingCollisionSet(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
 600         updatePos(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
 601      }
 602      else
 603      {
 604         // Need to clear out last updatePos or warp interpolation
 605         delta.posVec.set(0,0,0);
 606      }
 607   }
 608}
 609
 610void Item::interpolateTick(F32 dt)
 611{
 612   Parent::interpolateTick(dt);
 613   if ( isMounted() )
 614      return;
 615
 616   // Client side interpolation
 617   Point3F pos = delta.pos + delta.posVec * dt;
 618   MatrixF mat = mRenderObjToWorld;
 619   mat.setColumn(3,pos);
 620   setRenderTransform(mat);
 621   delta.dt = dt;
 622}
 623
 624
 625//----------------------------------------------------------------------------
 626
 627void Item::setTransform(const MatrixF& mat)
 628{
 629   Point3F pos;
 630   mat.getColumn(3,&pos);
 631   MatrixF tmat;
 632   if (!mRotate) {
 633      // Forces all rotation to be around the z axis
 634      VectorF vec;
 635      mat.getColumn(1,&vec);
 636      tmat.set(EulerF(0,0,-mAtan2(-vec.x,vec.y)));
 637   }
 638   else
 639      tmat.identity();
 640   tmat.setColumn(3,pos);
 641   Parent::setTransform(tmat);
 642   if (!mStatic)
 643   {
 644      mAtRest = false;
 645      mAtRestCounter = 0;
 646   }
 647
 648   if ( mPhysicsRep )
 649      mPhysicsRep->setTransform( getTransform() );
 650
 651   setMaskBits(RotationMask | PositionMask | NoWarpMask);
 652}
 653
 654
 655//----------------------------------------------------------------------------
 656void Item::updateWorkingCollisionSet(const U32 mask, const F32 dt)
 657{
 658   // It is assumed that we will never accelerate more than 10 m/s for gravity...
 659   //
 660   Point3F scaledVelocity = mVelocity * dt;
 661   F32 len    = scaledVelocity.len();
 662   F32 newLen = len + (10 * dt);
 663
 664   // Check to see if it is actually necessary to construct the new working list,
 665   //  or if we can use the cached version from the last query.  We use the x
 666   //  component of the min member of the mWorkingQueryBox, which is lame, but
 667   //  it works ok.
 668   bool updateSet = false;
 669
 670   Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
 671   F32 l = (newLen * 1.1) + 0.1;  // from Convex::updateWorkingList
 672   convexBox.minExtents -= Point3F(l, l, l);
 673   convexBox.maxExtents += Point3F(l, l, l);
 674
 675   // Check containment
 676   {
 677      if (mWorkingQueryBox.minExtents.x != -1e9)
 678      {
 679         if (mWorkingQueryBox.isContained(convexBox) == false)
 680         {
 681            // Needed region is outside the cached region.  Update it.
 682            updateSet = true;
 683         }
 684         else
 685         {
 686            // We can leave it alone, we're still inside the cached region
 687         }
 688      }
 689      else
 690      {
 691         // Must update
 692         updateSet = true;
 693      }
 694   }
 695
 696   // Actually perform the query, if necessary
 697   if (updateSet == true)
 698   {
 699      mWorkingQueryBox = convexBox;
 700      mWorkingQueryBox.minExtents -= Point3F(2 * l, 2 * l, 2 * l);
 701      mWorkingQueryBox.maxExtents += Point3F(2 * l, 2 * l, 2 * l);
 702
 703      disableCollision();
 704      if (mCollisionObject)
 705         mCollisionObject->disableCollision();
 706
 707      mConvex.updateWorkingList(mWorkingQueryBox, mask);
 708
 709      if (mCollisionObject)
 710         mCollisionObject->enableCollision();
 711      enableCollision();
 712   }
 713}
 714
 715void Item::updateVelocity(const F32 dt)
 716{
 717   // Acceleration due to gravity
 718   mVelocity.z += (mGravity * mDataBlock->gravityMod) * dt;
 719   F32 len;
 720   if (mDataBlock->maxVelocity > 0 && (len = mVelocity.len()) > (mDataBlock->maxVelocity * 1.05)) {
 721      Point3F excess = mVelocity * (1.0 - (mDataBlock->maxVelocity / len ));
 722      excess *= 0.1f;
 723      mVelocity -= excess;
 724   }
 725
 726   // Container buoyancy & drag
 727   mVelocity.z -= mBuoyancy * (mGravity * mDataBlock->gravityMod * mGravityMod) * dt;
 728   mVelocity   -= mVelocity * mDrag * dt;
 729}
 730
 731
 732void Item::updatePos(const U32 /*mask*/, const F32 dt)
 733{
 734   // Try and move
 735   Point3F pos;
 736   mObjToWorld.getColumn(3,&pos);
 737   delta.posVec = pos;
 738
 739   bool contact = false;
 740   bool nonStatic = false;
 741   bool stickyNotify = false;
 742   CollisionList collisionList;
 743   F32 time = dt;
 744
 745   static Polyhedron sBoxPolyhedron;
 746   static ExtrudedPolyList sExtrudedPolyList;
 747   static EarlyOutPolyList sEarlyOutPolyList;
 748   MatrixF collisionMatrix(true);
 749   Point3F end = pos + mVelocity * time;
 750   U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask;
 751
 752   // Part of our speed problem here is that we don't track contact surfaces, like we do
 753   //  with the player.  In order to handle the most common and performance impacting
 754   //  instance of this problem, we'll use a ray cast to detect any contact surfaces below
 755   //  us.  This won't be perfect, but it only needs to catch a few of these to make a
 756   //  big difference.  We'll cast from the top center of the bounding box at the tick's
 757   //  beginning to the bottom center of the box at the end.
 758   Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
 759                     (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
 760                     mObjBox.maxExtents.z);
 761   Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
 762                   (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
 763                   mObjBox.minExtents.z);
 764   collisionMatrix.setColumn(3, pos);
 765   collisionMatrix.mulP(startCast);
 766   collisionMatrix.setColumn(3, end);
 767   collisionMatrix.mulP(endCast);
 768   RayInfo rinfo;
 769   bool doToughCollision = true;
 770   disableCollision();
 771   if (mCollisionObject)
 772      mCollisionObject->disableCollision();
 773   if (getContainer()->castRay(startCast, endCast, mask, &rinfo))
 774   {
 775      F32 bd = -mDot(mVelocity, rinfo.normal);
 776
 777      if (bd >= 0.0)
 778      {
 779         // Contact!
 780         if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
 781            mVelocity.set(0, 0, 0);
 782            mAtRest = true;
 783            mAtRestCounter = 0;
 784            stickyNotify = true;
 785            mStickyCollisionPos    = rinfo.point;
 786            mStickyCollisionNormal = rinfo.normal;
 787            doToughCollision = false;;
 788         } else {
 789            // Subtract out velocity into surface and friction
 790            VectorF fv = mVelocity + rinfo.normal * bd;
 791            F32 fvl = fv.len();
 792            if (fvl) {
 793               F32 ff = bd * mDataBlock->friction;
 794               if (ff < fvl) {
 795                  fv *= ff / fvl;
 796                  fvl = ff;
 797               }
 798            }
 799            bd *= 1 + mDataBlock->elasticity;
 800            VectorF dv = rinfo.normal * (bd + 0.002);
 801            mVelocity += dv;
 802            mVelocity -= fv;
 803
 804            // Keep track of what we hit
 805            contact = true;
 806            U32 typeMask = rinfo.object->getTypeMask();
 807            if (!(typeMask & StaticObjectType))
 808               nonStatic = true;
 809            if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
 810               ShapeBase* col = static_cast<ShapeBase*>(rinfo.object);
 811               queueCollision(col,mVelocity - col->getVelocity());
 812            }
 813         }
 814      }
 815   }
 816   enableCollision();
 817   if (mCollisionObject)
 818      mCollisionObject->enableCollision();
 819
 820   if (doToughCollision)
 821   {
 822      U32 count;
 823      for (count = 0; count < 3; count++)
 824      {
 825         // Build list from convex states here...
 826         end = pos + mVelocity * time;
 827
 828
 829         collisionMatrix.setColumn(3, end);
 830         Box3F wBox = getObjBox();
 831         collisionMatrix.mul(wBox);
 832         Box3F testBox = wBox;
 833         Point3F oldMin = testBox.minExtents;
 834         Point3F oldMax = testBox.maxExtents;
 835         testBox.minExtents.setMin(oldMin + (mVelocity * time));
 836         testBox.maxExtents.setMin(oldMax + (mVelocity * time));
 837
 838         sEarlyOutPolyList.clear();
 839         sEarlyOutPolyList.mNormal.set(0,0,0);
 840         sEarlyOutPolyList.mPlaneList.setSize(6);
 841         sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0));
 842         sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0));
 843         sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0));
 844         sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0));
 845         sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1));
 846         sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1));
 847
 848         CollisionWorkingList& eorList = mConvex.getWorkingList();
 849         CollisionWorkingList* eopList = eorList.wLink.mNext;
 850         while (eopList != &eorList) {
 851            if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0)
 852            {
 853               Box3F convexBox = eopList->mConvex->getBoundingBox();
 854               if (testBox.isOverlapped(convexBox))
 855               {
 856                  eopList->mConvex->getPolyList(&sEarlyOutPolyList);
 857                  if (sEarlyOutPolyList.isEmpty() == false)
 858                     break;
 859               }
 860            }
 861            eopList = eopList->wLink.mNext;
 862         }
 863         if (sEarlyOutPolyList.isEmpty())
 864         {
 865            pos = end;
 866            break;
 867         }
 868
 869         collisionMatrix.setColumn(3, pos);
 870         sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true);
 871
 872         // Build extruded polyList...
 873         VectorF vector = end - pos;
 874         sExtrudedPolyList.extrude(sBoxPolyhedron, vector);
 875         sExtrudedPolyList.setVelocity(mVelocity);
 876         sExtrudedPolyList.setCollisionList(&collisionList);
 877
 878         CollisionWorkingList& rList = mConvex.getWorkingList();
 879         CollisionWorkingList* pList = rList.wLink.mNext;
 880         while (pList != &rList) {
 881            if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0)
 882            {
 883               Box3F convexBox = pList->mConvex->getBoundingBox();
 884               if (testBox.isOverlapped(convexBox))
 885               {
 886                  pList->mConvex->getPolyList(&sExtrudedPolyList);
 887               }
 888            }
 889            pList = pList->wLink.mNext;
 890         }
 891
 892         if (collisionList.getTime() < 1.0)
 893         {
 894            // Set to collision point
 895            F32 dt = time * collisionList.getTime();
 896            pos += mVelocity * dt;
 897            time -= dt;
 898
 899            // Pick the most resistant surface
 900            F32 bd = 0;
 901            const Collision* collision = 0;
 902            for (S32 c = 0; c < collisionList.getCount(); c++) {
 903               const Collision &cp = collisionList[c];
 904               F32 dot = -mDot(mVelocity,cp.normal);
 905               if (dot > bd) {
 906                  bd = dot;
 907                  collision = &cp;
 908               }
 909            }
 910
 911            if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
 912               mVelocity.set(0, 0, 0);
 913               mAtRest = true;
 914               mAtRestCounter = 0;
 915               stickyNotify = true;
 916               mStickyCollisionPos    = collision->point;
 917               mStickyCollisionNormal = collision->normal;
 918               break;
 919            } else {
 920               // Subtract out velocity into surface and friction
 921               if (collision) {
 922                  VectorF fv = mVelocity + collision->normal * bd;
 923                  F32 fvl = fv.len();
 924                  if (fvl) {
 925                     F32 ff = bd * mDataBlock->friction;
 926                     if (ff < fvl) {
 927                        fv *= ff / fvl;
 928                        fvl = ff;
 929                     }
 930                  }
 931                  bd *= 1 + mDataBlock->elasticity;
 932                  VectorF dv = collision->normal * (bd + 0.002);
 933                  mVelocity += dv;
 934                  mVelocity -= fv;
 935
 936                  // Keep track of what we hit
 937                  contact = true;
 938                  U32 typeMask = collision->object->getTypeMask();
 939                  if (!(typeMask & StaticObjectType))
 940                     nonStatic = true;
 941                  if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
 942                     ShapeBase* col = static_cast<ShapeBase*>(collision->object);
 943                     queueCollision(col,mVelocity - col->getVelocity());
 944                  }
 945               }
 946            }
 947         }
 948         else
 949         {
 950            pos = end;
 951            break;
 952         }
 953      }
 954      if (count == 3)
 955      {
 956         // Couldn't move...
 957         mVelocity.set(0, 0, 0);
 958      }
 959   }
 960
 961   // If on the client, calculate delta for backstepping
 962   if (isGhost()) {
 963      delta.pos     = pos;
 964      delta.posVec -= pos;
 965      delta.dt = 1;
 966   }
 967
 968   // Update transform
 969   MatrixF mat = mObjToWorld;
 970   mat.setColumn(3,pos);
 971   Parent::setTransform(mat);
 972   enableCollision();
 973   if (mCollisionObject)
 974      mCollisionObject->enableCollision();
 975   updateContainer();
 976
 977   if ( mPhysicsRep )
 978      mPhysicsRep->setTransform( mat );
 979
 980   //
 981   if (contact) {
 982      // Check for rest condition
 983      if (!nonStatic && mVelocity.len() < sAtRestVelocity) {
 984         mVelocity.x = mVelocity.y = mVelocity.z = 0;
 985         mAtRest = true;
 986         mAtRestCounter = 0;
 987      }
 988
 989      // Only update the client if we hit a non-static shape or
 990      // if this is our final rest pos.
 991      if (nonStatic || mAtRest)
 992         setMaskBits(PositionMask);
 993   }
 994
 995   // Collision callbacks. These need to be processed whether we hit
 996   // anything or not.
 997   if (!isGhost())
 998   {
 999      SimObjectPtr<Item> safePtr(this);
1000      if (stickyNotify)
1001      {
1002         notifyCollision();
1003         if(bool(safePtr))
1004          onStickyCollision_callback( getIdString() );
1005      }
1006      else
1007         notifyCollision();
1008
1009      // water
1010      if(bool(safePtr))
1011      {
1012         if(!mInLiquid && mWaterCoverage != 0.0f)
1013         {
1014         onEnterLiquid_callback( getIdString(), mWaterCoverage, mLiquidType.c_str() );
1015            mInLiquid = true;
1016         }
1017         else if(mInLiquid && mWaterCoverage == 0.0f)
1018         {
1019          onLeaveLiquid_callback(getIdString(), mLiquidType.c_str());
1020            mInLiquid = false;
1021         }
1022      }
1023   }
1024}
1025
1026
1027//----------------------------------------------------------------------------
1028
1029static MatrixF IMat(1);
1030
1031bool Item::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF&)
1032{
1033   if ( context == PLC_Decal )
1034      return false;
1035
1036   // Collision with the item is always against the item's object
1037   // space bounding box axis aligned in world space.
1038   Point3F pos;
1039   mObjToWorld.getColumn(3,&pos);
1040   IMat.setColumn(3,pos);
1041   polyList->setTransform(&IMat, mObjScale);
1042   polyList->setObject(this);
1043   polyList->addBox(mObjBox);
1044   return true;
1045}
1046
1047
1048//----------------------------------------------------------------------------
1049
1050U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
1051{
1052   U32 retMask = Parent::packUpdate(connection,mask,stream);
1053
1054   if (stream->writeFlag(mask & InitialUpdateMask)) {
1055      stream->writeFlag(mRotate);
1056      stream->writeFlag(mStatic);
1057      if (stream->writeFlag(getScale() != Point3F(1, 1, 1)))
1058         mathWrite(*stream, getScale());
1059   }
1060
1061   if (mask & ThrowSrcMask && mCollisionObject) {
1062      S32 gIndex = connection->getGhostIndex(mCollisionObject);
1063      if (stream->writeFlag(gIndex != -1))
1064         stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
1065   }
1066   else
1067      stream->writeFlag(false);
1068
1069   if (stream->writeFlag(mask & RotationMask && !mRotate)) {
1070      // Assumes rotation is about the Z axis
1071      AngAxisF aa(mObjToWorld);
1072      stream->writeFlag(aa.axis.z < 0);
1073      stream->write(aa.angle);
1074   }
1075
1076   if (stream->writeFlag(mask & PositionMask)) {
1077      Point3F pos;
1078      mObjToWorld.getColumn(3,&pos);
1079      mathWrite(*stream, pos);
1080      if (!stream->writeFlag(mAtRest)) {
1081         mathWrite(*stream, mVelocity);
1082      }
1083      stream->writeFlag(!(mask & NoWarpMask));
1084   }
1085   return retMask;
1086}
1087
1088void Item::unpackUpdate(NetConnection *connection, BitStream *stream)
1089{
1090   Parent::unpackUpdate(connection,stream);
1091
1092   // InitialUpdateMask
1093   if (stream->readFlag()) {
1094      mRotate = stream->readFlag();
1095      mStatic = stream->readFlag();
1096      if (stream->readFlag())
1097         mathRead(*stream, &mObjScale);
1098      else
1099         mObjScale.set(1, 1, 1);
1100   }
1101
1102   // ThrowSrcMask && mCollisionObject
1103   if (stream->readFlag()) {
1104      S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
1105      setCollisionTimeout(static_cast<ShapeBase*>(connection->resolveGhost(gIndex)));
1106   }
1107
1108   MatrixF mat = mObjToWorld;
1109
1110   // RotationMask && !mRotate
1111   if (stream->readFlag()) {
1112      // Assumes rotation is about the Z axis
1113      AngAxisF aa;
1114      aa.axis.set(0.0f, 0.0f, stream->readFlag() ? -1.0f : 1.0f);
1115      stream->read(&aa.angle);
1116      aa.setMatrix(&mat);
1117      Point3F pos;
1118      mObjToWorld.getColumn(3,&pos);
1119      mat.setColumn(3,pos);
1120   }
1121
1122   // PositionMask
1123   if (stream->readFlag()) {
1124      Point3F pos;
1125      mathRead(*stream, &pos);
1126      F32 speed = mVelocity.len();
1127      if ((mAtRest = stream->readFlag()) == true)
1128         mVelocity.set(0.0f, 0.0f, 0.0f);
1129      else
1130         mathRead(*stream, &mVelocity);
1131
1132      if (stream->readFlag() && isProperlyAdded()) {
1133         // Determin number of ticks to warp based on the average
1134         // of the client and server velocities.
1135         delta.warpOffset = pos - delta.pos;
1136         F32 as = (speed + mVelocity.len()) * 0.5f * TickSec;
1137         F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;
1138         delta.warpTicks = (S32)((dt > sMinWarpTicks)? getMax(mFloor(dt + 0.5f), 1.0f): 0.0f);
1139
1140         if (delta.warpTicks)
1141         {
1142            // Setup the warp to start on the next tick, only the
1143            // object's position is warped.
1144            if (delta.warpTicks > sMaxWarpTicks)
1145               delta.warpTicks = sMaxWarpTicks;
1146            delta.warpOffset /= (F32)delta.warpTicks;
1147         }
1148         else {
1149            // Going to skip the warp, server and client are real close.
1150            // Adjust the frame interpolation to move smoothly to the
1151            // new position within the current tick.
1152            Point3F cp = delta.pos + delta.posVec * delta.dt;
1153            VectorF vec = delta.pos - cp;
1154            F32 vl = vec.len();
1155            if (vl) {
1156               F32 s = delta.posVec.len() / vl;
1157               delta.posVec = (cp - pos) * s;
1158            }
1159            delta.pos = pos;
1160            mat.setColumn(3,pos);
1161         }
1162      }
1163      else {
1164         // Set the item to the server position
1165         delta.warpTicks = 0;
1166         delta.posVec.set(0,0,0);
1167         delta.pos = pos;
1168         delta.dt = 0;
1169         mat.setColumn(3,pos);
1170      }
1171   }
1172   Parent::setTransform(mat);
1173}
1174
1175DefineEngineMethod( Item, isStatic, bool, (),, 
1176   "@brief Is the object static (ie, non-movable)?\n\n"   
1177   "@return True if the object is static, false if it is not.\n"
1178   "@tsexample\n"
1179      "// Query the item on if it is or is not static.\n"
1180      "%isStatic = %itemData.isStatic();\n\n"
1181   "@endtsexample\n\n"
1182   "@see static\n"
1183   )
1184{
1185   return object->isStatic();
1186}
1187
1188DefineEngineMethod( Item, isAtRest, bool, (),, 
1189   "@brief Is the object at rest (ie, no longer moving)?\n\n"   
1190   "@return True if the object is at rest, false if it is not.\n"
1191   "@tsexample\n"
1192      "// Query the item on if it is or is not at rest.\n"
1193      "%isAtRest = %item.isAtRest();\n\n"
1194   "@endtsexample\n\n"
1195   )
1196{
1197   return object->isAtRest();
1198}
1199
1200DefineEngineMethod( Item, isRotating, bool, (),, 
1201   "@brief Is the object still rotating?\n\n"   
1202   "@return True if the object is still rotating, false if it is not.\n"
1203   "@tsexample\n"
1204      "// Query the item on if it is or is not rotating.\n"
1205      "%isRotating = %itemData.isRotating();\n\n"
1206   "@endtsexample\n\n"
1207   "@see rotate\n"
1208   )
1209{
1210   return object->isRotating();
1211}
1212
1213DefineEngineMethod( Item, setCollisionTimeout, bool, (S32 ignoreColObj),(NULL), 
1214   "@brief Temporarily disable collisions against a specific ShapeBase object.\n\n"
1215
1216   "This is useful to prevent a player from immediately picking up an Item they have "
1217   "just thrown.  Only one object may be on the timeout list at a time.  The timeout is "
1218   "defined as 15 ticks.\n\n"
1219
1220   "@param objectID ShapeBase object ID to disable collisions against.\n"
1221   "@return Returns true if the ShapeBase object requested could be found, false if it could not.\n"
1222
1223   "@tsexample\n"
1224      "// Set the ShapeBase Object ID to disable collisions against\n"
1225      "%ignoreColObj = %player.getID();\n\n"
1226      "// Inform this Item object to ignore collisions temproarily against the %ignoreColObj.\n"
1227      "%item.setCollisionTimeout(%ignoreColObj);\n\n"
1228   "@endtsexample\n\n"
1229   )
1230{
1231   ShapeBase* source = NULL;
1232   if (Sim::findObject(ignoreColObj,source)) {
1233      object->setCollisionTimeout(source);
1234      return true;
1235   }
1236   return false;
1237}
1238
1239
1240DefineEngineMethod( Item, getLastStickyPos, const char*, (),, 
1241   "@brief Get the position on the surface on which this Item is stuck.\n\n"   
1242   "@return Returns The XYZ position of where this Item is stuck.\n"
1243   "@tsexample\n"
1244      "// Acquire the position where this Item is currently stuck\n"
1245      "%stuckPosition = %item.getLastStickPos();\n\n"
1246   "@endtsexample\n\n"
1247   "@note Server side only.\n"
1248   )
1249{
1250   static const U32 bufSize = 256;
1251   char* ret = Con::getReturnBuffer(bufSize);
1252   if (object->isServerObject())
1253      dSprintf(ret, bufSize, "%g %g %g",
1254               object->mStickyCollisionPos.x,
1255               object->mStickyCollisionPos.y,
1256               object->mStickyCollisionPos.z);
1257   else
1258      dStrcpy(ret, "0 0 0");
1259
1260   return ret;
1261}
1262
1263DefineEngineMethod( Item, getLastStickyNormal, const char *, (),, 
1264   "@brief Get the normal of the surface on which the object is stuck.\n\n"   
1265   "@return Returns The XYZ normal from where this Item is stuck.\n"
1266   "@tsexample\n"
1267      "// Acquire the position where this Item is currently stuck\n"
1268      "%stuckPosition = %item.getLastStickPos();\n\n"
1269   "@endtsexample\n\n"
1270   "@note Server side only.\n"
1271   )
1272{
1273   static const U32 bufSize = 256;
1274   char* ret = Con::getReturnBuffer(bufSize);
1275   if (object->isServerObject())
1276      dSprintf(ret, bufSize, "%g %g %g",
1277               object->mStickyCollisionNormal.x,
1278               object->mStickyCollisionNormal.y,
1279               object->mStickyCollisionNormal.z);
1280   else
1281      dStrcpy(ret, "0 0 0");
1282
1283   return ret;
1284}
1285
1286//----------------------------------------------------------------------------
1287
1288bool Item::_setStatic(void *object, const char *index, const char *data)
1289{
1290   Item *i = static_cast<Item*>(object);
1291   i->mAtRest = dAtob(data);
1292   i->setMaskBits(InitialUpdateMask | PositionMask);
1293   return true;
1294}
1295
1296bool Item::_setRotate(void *object, const char *index, const char *data)
1297{
1298   Item *i = static_cast<Item*>(object);
1299   i->setMaskBits(InitialUpdateMask | RotationMask);
1300   return true;
1301}
1302
1303void Item::initPersistFields()
1304{
1305   addGroup("Misc"); 
1306   addProtectedField("static", TypeBool, Offset(mStatic, Item), &_setStatic, &defaultProtectedGetFn, "If true, the object is not moving in the world.\n");
1307   addProtectedField("rotate", TypeBool, Offset(mRotate, Item), &_setRotate, &defaultProtectedGetFn, "If true, the object will automatically rotate around its Z axis.\n");
1308   endGroup("Misc");
1309
1310   Parent::initPersistFields();
1311}
1312
1313void Item::consoleInit()
1314{
1315   Con::addVariable("Item::minWarpTicks",TypeF32,&sMinWarpTicks,
1316      "@brief Fraction of tick at which instant warp occures on the client.\n\n"
1317      "@ingroup GameObjects");
1318   Con::addVariable("Item::maxWarpTicks",TypeS32,&sMaxWarpTicks, 
1319      "@brief When a warp needs to occur due to the client being too far off from the server, this is the "
1320      "maximum number of ticks we'll allow the client to warp to catch up.\n\n"
1321      "@ingroup GameObjects");
1322}
1323
1324//----------------------------------------------------------------------------
1325
1326void Item::prepRenderImage( SceneRenderState* state )
1327{
1328   // Items do NOT render if destroyed
1329   if (getDamageState() == Destroyed)
1330      return;
1331
1332   Parent::prepRenderImage( state );
1333}
1334
1335void Item::buildConvex(const Box3F& box, Convex* convex)
1336{
1337   if (mShapeInstance == NULL)
1338      return;
1339
1340   // These should really come out of a pool
1341   mConvexList->collectGarbage();
1342
1343   if (box.isOverlapped(getWorldBox()) == false)
1344      return;
1345
1346   // Just return a box convex for the entire shape...
1347   Convex* cc = 0;
1348   CollisionWorkingList& wl = convex->getWorkingList();
1349   for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
1350      if (itr->mConvex->getType() == BoxConvexType &&
1351          itr->mConvex->getObject() == this) {
1352         cc = itr->mConvex;
1353         break;
1354      }
1355   }
1356   if (cc)
1357      return;
1358
1359   // Create a new convex.
1360   BoxConvex* cp = new BoxConvex;
1361   mConvexList->registerObject(cp);
1362   convex->addToWorkingList(cp);
1363   cp->init(this);
1364
1365   mObjBox.getCenter(&cp->mCenter);
1366   cp->mSize.x = mObjBox.len_x() / 2.0f;
1367   cp->mSize.y = mObjBox.len_y() / 2.0f;
1368   cp->mSize.z = mObjBox.len_z() / 2.0f;
1369}
1370
1371void Item::advanceTime(F32 dt)
1372{
1373   Parent::advanceTime(dt);
1374   if ( isMounted() )
1375      return;
1376
1377   if( mRotate )
1378   {
1379      F32 r = (dt / sRotationSpeed) * M_2PI;
1380      Point3F pos = mRenderObjToWorld.getPosition();
1381      MatrixF rotMatrix;
1382      if( mRotate )
1383      {
1384         rotMatrix.set( EulerF( 0.0, 0.0, r ) );
1385      }
1386      else
1387      {
1388         rotMatrix.set( EulerF( r * 0.5, 0.0, r ) );
1389      }
1390      MatrixF mat = mRenderObjToWorld;
1391      mat.setPosition( pos );
1392      mat.mul( rotMatrix );
1393      setRenderTransform(mat);
1394   }
1395
1396}
1397