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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> set of Items. These properties include <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "DTS or DAE model used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> 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_1a4e4fa7e3399708e0777b5308db01278c">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_1a4e4fa7e3399708e0777b5308db01278c">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_1a4e4fa7e3399708e0777b5308db01278c">if</a> the object is static)
DefineEngineMethod(Item , setCollisionTimeout , bool , (S32 ignoreColObj) , "@brief Temporarily disable collisions against <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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_1a4e4fa7e3399708e0777b5308db01278c">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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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_1a4e4fa7e3399708e0777b5308db01278c">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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> set of Items. These properties include <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "DTS or DAE model used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> 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_1a4e4fa7e3399708e0777b5308db01278c">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_1a4e4fa7e3399708e0777b5308db01278c">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_1a4e4fa7e3399708e0777b5308db01278c">if</a> the object is static)

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