//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _SHAPEBASE_H_
#define _SHAPEBASE_H_

#ifndef __RESOURCE_H__
   #include "core/resource.h"
#endif
#ifndef _GAMEBASE_H_
   #include "T3D/gameBase/gameBase.h"
#endif
#ifndef _MOVEMANAGER_H_
   #include "T3D/gameBase/moveManager.h"
#endif
#ifndef _COLOR_H_
   #include "core/color.h"
#endif
#ifndef _CONVEX_H_
   #include "collision/convex.h"
#endif
#ifndef _SCENERENDERSTATE_H_
   #include "scene/sceneRenderState.h"
#endif
#ifndef _NETSTRINGTABLE_H_
   #include "sim/netStringTable.h"
#endif
#ifndef _RENDERPASSMANAGER_H_
   #include "renderInstance/renderPassManager.h"
#endif
#ifndef _TSSHAPE_H_
   #include "ts/tsShape.h"
#endif
#ifndef _BITVECTOR_H_
   #include "core/bitVector.h"
#endif
#ifndef _LIGHTINFO_H_
   #include "lighting/lightInfo.h"
#endif
#ifndef _REFLECTOR_H_
   #include "scene/reflector.h"
#endif
#ifndef _DYNAMIC_CONSOLETYPES_H_
   #include "console/dynamicTypes.h"
#endif


class GFXCubemap;
class TSShapeInstance;
class SceneRenderState;
class TSThread;
class GameConnection;
struct CameraScopeQuery;
class ParticleEmitter;
class ParticleEmitterData;
class ProjectileData;
class ExplosionData;
struct DebrisData;
class ShapeBase;
class SFXSource;
class SFXTrack;
class SFXProfile;

typedef void* Light;


//--------------------------------------------------------------------------

extern void collisionFilter(SceneObject* object,S32 key);
extern void defaultFilter(SceneObject* object,S32 key);


//--------------------------------------------------------------------------
class ShapeBaseConvex : public Convex
{
   typedef Convex Parent;
   friend class ShapeBase;
   friend class Vehicle;
   friend class RigidShape;

  protected:
   ShapeBase*  pShapeBase;
   MatrixF*    nodeTransform;

  public:
   MatrixF*    transform;
   U32         hullId;
   Box3F       box;

  public:
   ShapeBaseConvex() { mType = ShapeBaseConvexType; nodeTransform = 0; }
   ShapeBaseConvex(const ShapeBaseConvex& cv) {
      mObject    = cv.mObject;
      pShapeBase = cv.pShapeBase;
      hullId     = cv.hullId;
      nodeTransform = cv.nodeTransform;
      box        = cv.box;
      transform  = 0;
   }

   void findNodeTransform();
   const MatrixF& getTransform() const;
   Box3F getBoundingBox() const;
   Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
   Point3F      support(const VectorF& v) const;
   void         getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
   void         getPolyList(AbstractPolyList* list);
};

//--------------------------------------------------------------------------

struct ShapeBaseImageData: public GameBaseData {
  private:
   typedef GameBaseData Parent;

  public:
   enum Constants {
      MaxStates    = 31,            ///< We get one less than state bits because of
                                    /// the way data is packed.

      MaxShapes    = 2,             ///< The number of allowed shapes per image.  Only
                                    /// the first shape is required.

      MaxGenericTriggers = 4,       ///< The number of generic triggers for the image.

      StandardImageShape = 0,       ///< Shape index used for the standard image shape
      FirstPersonImageShape = 1,    ///< Shape index used for the optional first person image shape

      NumStateBits = 5,
   };
   enum LightType {
      NoLight = 0,
      ConstantLight,
      SpotLight,
      PulsingLight,
      WeaponFireLight,
      NumLightTypes
   };
   struct StateData {
      StateData();
      const char* name;             ///< State name

      /// @name Transition states
      ///
      /// @{

      ///
      struct Transition {
         S32 loaded[2];             ///< NotLoaded/Loaded
         S32 ammo[2];               ///< Noammo/ammo
         S32 target[2];             ///< target/noTarget
         S32 trigger[2];            ///< Trigger up/down
         S32 altTrigger[2];         ///< Second trigger up/down
         S32 wet[2];                ///< wet/notWet
         S32 motion[2];             ///< NoMotion/Motion
         S32 timeout;               ///< Transition after delay
         S32 genericTrigger[ShapeBaseImageData::MaxGenericTriggers][2];    ///< Generic trigger Out/In
      } transition;
      bool ignoreLoadedForReady;

      /// @}

      /// @name State attributes
      /// @{

      bool fire;                    ///< Can only have one fire state
      bool altFire;                 ///< Can only have one alternate fire state
      bool reload;                  ///< Can only have one reload state
      bool ejectShell;              ///< Should we eject a shell casing in this state?
      bool allowImageChange;        ///< Can we switch to another image while in this state?
                                    ///
                                    ///  For instance, if we have a rocket launcher, the player
                                    ///  shouldn't be able to switch out <i>while</i> firing. So,
                                    ///  you'd set allowImageChange to false in firing states, and
                                    ///  true the rest of the time.
      bool scaleAnimation;          ///< Scale animation to fit the state timeout
      bool scaleAnimationFP;        ///< Scale animation to fit the state timeout while in first person
      bool direction;               ///< Animation direction
      bool sequenceTransitionIn;    ///< Do we transition to the state's sequence when we enter the state?
      bool sequenceTransitionOut;   ///< Do we transition to the new state's sequence when we leave the state?
      bool sequenceNeverTransition; ///< Never allow a transition to this sequence.  Often used for a fire sequence.
      F32 sequenceTransitionTime;   ///< The time to transition in or out of a sequence.
      bool waitForTimeout;          ///< Require the timeout to pass before advancing to the next
                                    ///  state.
      F32 timeoutValue;             ///< A timeout value; the effect of this value is determined
                                    ///  by the flags scaleAnimation and waitForTimeout
      F32 energyDrain;              ///< Sets the energy drain rate per second of this state.
                                    ///
                                    ///  Energy is drained at energyDrain units/sec as long as
                                    ///  we are in this state.
      enum LoadedState {
         IgnoreLoaded,              ///< Don't change loaded state.
         Loaded,                    ///< Set us as loaded.
         NotLoaded,                 ///< Set us as not loaded.
         NumLoadedBits = 3          ///< How many bits to allocate to representing this state. (3 states needs 2 bits)
      } loaded;                     ///< Is the image considered loaded?
      enum SpinState {
         IgnoreSpin,                ///< Don't change spin state.
         NoSpin,                    ///< Mark us as having no spin (ie, stop spinning).
         SpinUp,                    ///< Mark us as spinning up.
         SpinDown,                  ///< Mark us as spinning down.
         FullSpin,                  ///< Mark us as being at full spin.
         NumSpinBits = 3            ///< How many bits to allocate to representing this state. (5 states needs 3 bits)
      } spin;                       ///< Spin thread state. (Used to control spinning weapons, e.g. chainguns)
      enum RecoilState {
         NoRecoil,
         LightRecoil,
         MediumRecoil,
         HeavyRecoil,
         NumRecoilBits = 3
      } recoil;                     ///< Recoil thread state.
                                    ///
                                    /// @note At this point, the only check being done is to see if we're in a
                                    ///       state which isn't NoRecoil; ie, no differentiation is made between
                                    ///       Light/Medium/Heavy recoils. Player::onImageRecoil() is the place
                                    ///       where this is handled.
      bool flashSequence[MaxShapes];///< Is this a muzzle flash sequence?
                                    ///
                                    ///  A muzzle flash sequence is used as a source to randomly display frames from,
                                    ///  so if this is a flashSequence, we'll display a random piece each frame.
      S32 sequence[MaxShapes];      ///< Main thread sequence ID.
                                    ///
                                    ///
      S32 sequenceVis[MaxShapes];   ///< Visibility thread sequence.

      StringTableEntry shapeSequence;  ///< Sequence that is played on mounting shape
      bool shapeSequenceScale;         ///< Should the mounting shape's sequence playback be scaled
                                       ///  to the length of the state.

      const char* script;           ///< Function on datablock to call when we enter this state; passed the id of
                                    ///  the imageSlot.
      ParticleEmitterData* emitter; ///< A particle emitter; this emitter will emit as long as the gun is in this
                                    ///  this state.
      SFXTrack* sound;
      F32 emitterTime;              ///<
      S32 emitterNode[MaxShapes];   ///< Node ID on the shape to emit from
   };

   /// @name State Data
   /// Individual state data used to initialize struct array
   /// @{
   const char*             fireStateName;

   const char*             stateName                  [MaxStates];

   const char*             stateTransitionLoaded      [MaxStates];
   const char*             stateTransitionNotLoaded   [MaxStates];
   const char*             stateTransitionAmmo        [MaxStates];
   const char*             stateTransitionNoAmmo      [MaxStates];
   const char*             stateTransitionTarget      [MaxStates];
   const char*             stateTransitionNoTarget    [MaxStates];
   const char*             stateTransitionWet         [MaxStates];
   const char*             stateTransitionNotWet      [MaxStates];
   const char*             stateTransitionMotion      [MaxStates];
   const char*             stateTransitionNoMotion    [MaxStates];
   const char*             stateTransitionTriggerUp   [MaxStates];
   const char*             stateTransitionTriggerDown [MaxStates];
   const char*             stateTransitionAltTriggerUp[MaxStates];
   const char*             stateTransitionAltTriggerDown[MaxStates];
   const char*             stateTransitionGeneric0In  [MaxStates];
   const char*             stateTransitionGeneric0Out [MaxStates];
   const char*             stateTransitionGeneric1In  [MaxStates];
   const char*             stateTransitionGeneric1Out [MaxStates];
   const char*             stateTransitionGeneric2In  [MaxStates];
   const char*             stateTransitionGeneric2Out [MaxStates];
   const char*             stateTransitionGeneric3In  [MaxStates];
   const char*             stateTransitionGeneric3Out [MaxStates];
   const char*             stateTransitionTimeout     [MaxStates];
   F32                     stateTimeoutValue          [MaxStates];
   bool                    stateWaitForTimeout        [MaxStates];

   bool                    stateFire                  [MaxStates];
   bool                    stateAlternateFire         [MaxStates];
   bool                    stateReload                [MaxStates];
   bool                    stateEjectShell            [MaxStates];
   F32                     stateEnergyDrain           [MaxStates];
   bool                    stateAllowImageChange      [MaxStates];
   bool                    stateScaleAnimation        [MaxStates];
   bool                    stateScaleAnimationFP      [MaxStates];
   bool                    stateSequenceTransitionIn  [MaxStates];
   bool                    stateSequenceTransitionOut [MaxStates];
   bool                    stateSequenceNeverTransition [MaxStates];
   F32                     stateSequenceTransitionTime [MaxStates];
   bool                    stateDirection             [MaxStates];
   StateData::LoadedState  stateLoaded                [MaxStates];
   StateData::SpinState    stateSpin                  [MaxStates];
   StateData::RecoilState  stateRecoil                [MaxStates];
   const char*             stateSequence              [MaxStates];
   bool                    stateSequenceRandomFlash   [MaxStates];

   const char*             stateShapeSequence         [MaxStates];
   bool                    stateScaleShapeSequence    [MaxStates];

   bool                    stateIgnoreLoadedForReady  [MaxStates];

   SFXTrack*               stateSound                 [MaxStates];
   const char*             stateScript                [MaxStates];

   ParticleEmitterData*    stateEmitter               [MaxStates];
   F32                     stateEmitterTime           [MaxStates];
   const char*             stateEmitterNode           [MaxStates];
   /// @}
   
   /// @name Camera Shake ( while firing )
   /// @{
   bool              shakeCamera;
   VectorF           camShakeFreq;
   VectorF           camShakeAmp;         
   /// @}

   /// Maximum number of sounds this image can play at a time.
   /// Any value <= 0 indicates that it can play an infinite number of sounds.
   S32 maxConcurrentSounds; 
   
   /// If true it we will allow multiple timeout transitions to occur within
   /// a single tick ( eg. they have a very small timeout ).
   bool useRemainderDT;

   //
   bool emap;                       ///< Environment mapping on?
   bool correctMuzzleVector;        ///< Adjust 1st person firing vector to eye's LOS point?
   bool correctMuzzleVectorTP;      ///< Adjust 3rd person firing vector to camera's LOS point?
   bool firstPerson;                ///< Render the image when in first person?
   bool useFirstPersonShape;        ///< Indicates the special first person shape should be used (true when shapeNameFP and useEyeOffset are defined)
   bool useEyeOffset;               ///< In first person, should we use the eyeTransform?
   bool useEyeNode;                 ///< In first person, should we attach the camera to the image's eye node?  Player still ultimately decides on what to do,
                                    ///  especially for multiple mounted images.

   bool animateAllShapes;           ///< Indicates that all shapes should be animated in sync.
   bool animateOnServer;            ///< Indicates that the image should be animated on the server.  In most cases
                                    ///  you'll want this set if you're using useEyeNode.  You may also want to
                                    ///  set this if the muzzlePoint is animated while it shoots.  You can set this
                                    ///  to false even if these previous cases are true if the image's shape is set
                                    ///  up in the correct position and orientation in the 'root' pose and none of
                                    ///  the nodes are animated at key times, such as the muzzlePoint essentially
                                    ///  remaining at the same position at the start of the fire state (it could
                                    ///  animate just fine after the projectile is away as the muzzle vector is only
                                    ///  calculated at the start of the state).  You'll also want to set this to true
                                    ///  if you're animating the camera using an image's 'eye' node -- unless the movement
                                    ///  is very subtle and doesn't need to be reflected on the server.

   F32 scriptAnimTransitionTime;    ///< The amount of time to transition between the previous sequence and new sequence
                                    ///< when the script prefix has changed.

   StringTableEntry  shapeName;     ///< Name of shape to render.
   StringTableEntry  shapeNameFP;   ///< Name of shape to render in first person (optional).

   StringTableEntry  imageAnimPrefix;     ///< Passed along to the mounting shape to modify
                                          ///  animation sequences played in 3rd person. [optional]
   StringTableEntry  imageAnimPrefixFP;   ///< Passed along to the mounting shape to modify
                                          ///  animation sequences played in first person. [optional]

   U32               mountPoint;    ///< Mount point for the image.
   MatrixF           mountOffset;   ///< Mount point offset, so we know where the image is.
   MatrixF           eyeOffset;     ///< Offset from eye for first person.

   ProjectileData* projectile;      ///< Information about what projectile this
                                    ///  image fires.

   F32   mass;                      ///< Mass!
   bool  usesEnergy;                ///< Does this use energy instead of ammo?
   F32   minEnergy;                 ///< Minimum energy for the weapon to be operable.
   bool  accuFire;                  ///< Should we automatically make image's aim converge with the crosshair?
   bool  cloakable;                 ///< Is this image cloakable when mounted?

   /// @name Lighting
   /// @{
   S32         lightType;           ///< Indicates the type of the light.
                                    ///
                                    ///  One of: ConstantLight, PulsingLight, WeaponFireLight.
   ColorF      lightColor;
   S32         lightDuration;       ///< The duration in SimTime of Pulsing or WeaponFire type lights.
   F32         lightRadius;         ///< Extent of light.
   F32         lightBrightness;     ///< Brightness of the light ( if it is WeaponFireLight ).
   /// @}

   /// @name Shape Data
   /// @{
   Resource<TSShape> shape[MaxShapes]; ///< Shape handle
   bool shapeIsValid[MaxShapes];       ///< Indicates that the shape has been loaded and is valid

   U32 mCRC[MaxShapes];                ///< Checksum of shape.
                                       ///
                                       ///  Calculated by the ResourceManager, see
                                       ///  ResourceManager::load().
   bool computeCRC;                    ///< Should the shape's CRC be checked?
   MatrixF mountTransform[MaxShapes];  ///< Transformation to get to the mountNode.
   /// @}

   /// @name Nodes
   /// @{
   S32 retractNode[MaxShapes];   ///< Retraction node ID.
                                 ///
                                 ///  When the player bumps against an object and the image is retracted to
                                 ///  avoid having it interpenetrating the object, it is retracted towards
                                 ///  this node.
   S32 muzzleNode[MaxShapes];    ///< Muzzle node ID.
                                 ///
                                 ///
   S32 ejectNode[MaxShapes];     ///< Ejection node ID.
                                 ///
                                 ///  The eject node is the node on the image from which shells are ejected.
   S32 emitterNode[MaxShapes];   ///< Emitter node ID.
                                 ///
                                 ///  The emitter node is the node from which particles are emitted.
   S32 eyeMountNode[MaxShapes];  ///< eyeMount node ID.  Optionally used to mount an image to the player's
                                 /// eye node for first person.
   S32 eyeNode[MaxShapes];       ///< Eye node ID.  Optionally used to attach the camera to for camera motion
                                 ///  control from the image.
   /// @}

   /// @name Animation
   /// @{
   S32 spinSequence[MaxShapes];     ///< ID of the spin animation sequence.
   S32 ambientSequence[MaxShapes];  ///< ID of the ambient animation sequence.

   bool isAnimated[MaxShapes];      ///< This image contains at least one animated states
   bool hasFlash[MaxShapes];        ///< This image contains at least one muzzle flash animation state
   S32 fireState;                   ///< The ID of the fire state.
   S32 altFireState;                ///< The ID of the alternate fire state.
   S32 reloadState;                 ///< The ID of the reload state
   /// @}

   /// @name Shell casing data
   /// @{
   DebrisData *   casing;              ///< Information about shell casings.

   S32            casingID;            ///< ID of casing datablock.
                                       ///
                                       ///  When the network tells the client about the casing, it
                                       ///  it transmits the ID of the datablock. The datablocks
                                       ///  having previously been transmitted, all the client
                                       ///  needs to do is call Sim::findObject() and look up the
                                       ///  the datablock.

   Point3F        shellExitDir;        ///< Vector along which to eject shells from the image.
   F32            shellExitVariance;   ///< Variance from this vector in degrees.
   F32            shellVelocity;       ///< Velocity with which to eject shell casings.
   /// @}

   /// @name State Array
   ///
   /// State array is initialized onAdd from the individual state
   /// struct array elements.
   ///
   /// @{
   StateData state[MaxStates];   ///< Array of states.
   bool      statesLoaded;       ///< Are the states loaded yet?
   /// @}

   /// @name Infrastructure
   ///
   /// Miscellaneous inherited methods.
   /// @{

   DECLARE_CONOBJECT(ShapeBaseImageData);
   ShapeBaseImageData();
   ~ShapeBaseImageData();
   bool onAdd();
   bool preload(bool server, String &errorStr);
   S32 lookupState(const char* name);  ///< Get a state by name.
   static void initPersistFields();
   virtual void packData(BitStream* stream);
   virtual void unpackData(BitStream* stream);
   
   void inspectPostApply();

   /// @}

   /// @name Callbacks
   /// @{
   DECLARE_CALLBACK( void, onMount, ( ShapeBase* obj, S32 slot, F32 dt ) );
   DECLARE_CALLBACK( void, onUnmount, ( ShapeBase* obj, S32 slot, F32 dt ) );
   /// @}
};

typedef ShapeBaseImageData::LightType ShapeBaseImageLightType;
typedef ShapeBaseImageData::StateData::LoadedState ShapeBaseImageLoadedState;
typedef ShapeBaseImageData::StateData::SpinState ShapeBaseImageSpinState;
typedef ShapeBaseImageData::StateData::RecoilState ShapeBaseImageRecoilState;

DefineEnumType( ShapeBaseImageLightType );
DefineEnumType( ShapeBaseImageLoadedState );
DefineEnumType( ShapeBaseImageSpinState );
DefineEnumType( ShapeBaseImageRecoilState );

//--------------------------------------------------------------------------
/// @nosubgrouping
struct ShapeBaseData : public GameBaseData {
  private:
   typedef GameBaseData Parent;
   
   static bool _setMass( void* object, const char* index, const char* data );

public:
   /// Various constants relating to the ShapeBaseData
   enum Constants {
      MaxCollisionShapes = 8,
      AIRepairNode = 31
   };

   // TODO: These are only really used in Basic Lighting
   // mode... we should probably move them somewhere else.
   bool shadowEnable;
   U32 shadowSize;
   F32 shadowMaxVisibleDistance;
   F32 shadowProjectionDistance;
   F32 shadowSphereAdjust;


   StringTableEntry  shapeName;
   StringTableEntry  cloakTexName;

   String cubeDescName;
   U32 cubeDescId;
   ReflectorDesc *reflectorDesc;

   /// @name Destruction
   ///
   /// Everyone likes to blow things up!
   /// @{
   DebrisData *      debris;
   S32               debrisID;
   StringTableEntry  debrisShapeName;
   Resource<TSShape> debrisShape;

   ExplosionData*    explosion;
   S32               explosionID;

   ExplosionData*    underwaterExplosion;
   S32               underwaterExplosionID;
   /// @}

   /// @name Physical Properties
   /// @{
   F32 mass;
   F32 drag;
   F32 density;
   F32 maxEnergy;
   F32 maxDamage;
   F32 repairRate;                  ///< Rate per tick.

   F32 disabledLevel;
   F32 destroyedLevel;
   /// @}

   /// @name 3rd Person Camera
   /// @{
   F32 cameraMaxDist;               ///< Maximum distance from eye
   F32 cameraMinDist;               ///< Minumumistance from eye
   /// @}

   /// @name Camera FOV
   ///
   /// These are specified in degrees.
   /// @{
   F32 cameraDefaultFov;            ///< Default vertical FOV in degrees.
   F32 cameraMinFov;                ///< Min vertical FOV allowed in degrees.
   F32 cameraMaxFov;                ///< Max vertical FOV allowed in degrees.
   /// @}

   /// @name Data initialized on preload
   /// @{

   Resource<TSShape> mShape;         ///< Shape handle
   U32 mCRC;
   bool computeCRC;

   S32 eyeNode;                         ///< Shape's eye node index
   S32 earNode;                         ///< Shape's ear node index
   S32 cameraNode;                      ///< Shape's camera node index
   S32 mountPointNode[SceneObject::NumMountPoints];  ///< Node index of mountPoint
   S32 debrisDetail;                    ///< Detail level used to generate debris
   S32 damageSequence;                  ///< Damage level decals
   S32 hulkSequence;                    ///< Destroyed hulk

   bool              observeThroughObject;   // observe this object through its camera transform and default fov

   /// @name Collision Data
   /// @{
   Vector<S32>   collisionDetails;  ///< Detail level used to collide with.
                                    ///
                                    /// These are detail IDs, see TSShape::findDetail()
   Vector<Box3F> collisionBounds;   ///< Detail level bounding boxes.

   Vector<S32>   LOSDetails;        ///< Detail level used to perform line-of-sight queries against.
                                    ///
                                    /// These are detail IDs, see TSShape::findDetail()
   /// @}

   /// @name Misc. Settings
   /// @{
   bool firstPersonOnly;            ///< Do we allow only first person view of this image?
   bool useEyePoint;                ///< Do we use this object's eye point to view from?
   bool isInvincible;               ///< If set, object cannot take damage (won't show up with damage bar either)
   bool renderWhenDestroyed;        ///< If set, will not render this object when destroyed.

   bool inheritEnergyFromMount;

   /// @}

   virtual bool preload(bool server, String &errorStr);
   void computeAccelerator(U32 i);
   S32  findMountPoint(U32 n);

   /// @name Infrastructure
   /// The derived class should provide the following:
   /// @{
   DECLARE_CONOBJECT(ShapeBaseData);
   ShapeBaseData();
   ~ShapeBaseData();
   static void initPersistFields();
   virtual void packData(BitStream* stream);
   virtual void unpackData(BitStream* stream);
   /// @}

   /// @name Callbacks
   /// @{
   DECLARE_CALLBACK( void, onEnabled, ( ShapeBase* obj, const char* lastState ) );
   DECLARE_CALLBACK( void, onDisabled, ( ShapeBase* obj, const char* lastState ) );
   DECLARE_CALLBACK( void, onDestroyed, ( ShapeBase* obj, const char* lastState ) );
   DECLARE_CALLBACK( void, onImpact, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) );
   DECLARE_CALLBACK( void, onCollision, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) );
   DECLARE_CALLBACK( void, onDamage, ( ShapeBase* obj, F32 delta ) );
   DECLARE_CALLBACK( void, onTrigger, ( ShapeBase* obj, S32 index, bool state ) );
   DECLARE_CALLBACK( void, onEndSequence, ( ShapeBase* obj, S32 slot ) );
   DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) );
   /// @}
};


//----------------------------------------------------------------------------

class WaterObject;
class CameraShake;

/// ShapeBase is the renderable shape from which most of the scriptable objects
/// are derived, including the player, vehicle and items classes.  ShapeBase
/// provides basic shape loading, audio channels, and animation as well as damage
/// (and damage states), energy, and the ability to mount images and objects.
///
/// @nosubgrouping
class ShapeBase : public GameBase, public ISceneLight
{
   friend class ShapeBaseConvex;
   friend struct ShapeBaseImageData;
   friend void waterFind(SceneObject*, void*);
   friend void physicalZoneFind(SceneObject*, void*);

public:
   typedef GameBase Parent;

   enum PublicConstants {
      ThreadSequenceBits = 6,
      MaxSequenceIndex = (1 << ThreadSequenceBits) - 1,
      EnergyLevelBits = 5,
      DamageLevelBits = 6,
      DamageStateBits = 2,
      // The thread and image limits should not be changed without
      // also changing the ShapeBaseMasks enum values declared
      // further down.
      MaxSoundThreads =  4,            ///< Should be a power of 2
      MaxScriptThreads = 4,            ///< Should be a power of 2
      MaxMountedImages = 4,            ///< Should be a power of 2
      MaxImageEmitters = 3,
      NumImageBits = 3,
      ShieldNormalBits = 8,
      CollisionTimeoutValue = 250      ///< Timeout in ms.
   };

   /// This enum indexes into the sDamageStateName array
   enum DamageState {
      Enabled,
      Disabled,
      Destroyed,
      NumDamageStates,
      NumDamageStateBits = 2,   ///< Should be log2 of the number of states.
   };

protected:
   ShapeBaseData*    mDataBlock;                ///< Datablock
   bool              mIsAiControlled;           ///< Is this object AI controlled?
   //GameConnection*   mControllingClient;        ///< Controlling client
   ShapeBase*        mControllingObject;        ///< Controlling object
   bool              mTrigger[MaxTriggerKeys];  ///< What triggers are set, if any.


   /// @name Scripted Sound
   /// @{
   struct Sound {
      bool play;                    ///< Are we playing this sound?
      SimTime timeout;              ///< Time until we stop playing this sound.
      SFXTrack* profile;            ///< Profile on server
      SFXSource* sound;             ///< Sound on client
   };
   Sound mSoundThread[MaxSoundThreads];
   /// @}

   /// @name Scripted Animation Threads
   /// @{

   struct Thread {
      /// State of the animation thread.
      enum State {
         Play, Stop, Pause, Destroy
      };
      TSThread* thread; ///< Pointer to 3space data.
      U32 state;        ///< State of the thread
                        ///
                        ///  @see Thread::State
      S32 sequence;     ///< The animation sequence which is running in this thread.
	  F32 timescale;    ///< Timescale
      U32 sound;        ///< Handle to sound.
      bool atEnd;       ///< Are we at the end of this thread?
      F32 position;
   };
   Thread mScriptThread[MaxScriptThreads];

   /// @}

   /// @name Invincibility
   /// @{
   F32 mInvincibleCount;
   F32 mInvincibleTime;
   F32 mInvincibleSpeed;
   F32 mInvincibleDelta;
   F32 mInvincibleEffect;
   F32 mInvincibleFade;
   bool mInvincibleOn;
   /// @}

   /// @name Motion
   /// @{
   bool mMoveMotion;    ///< Indicates that a Move has come in requesting x, y or z motion
   /// @}

protected:

   // ShapeBase pointer to our mount object if it is ShapeBase, else it is NULL.
   ShapeBase *mShapeBaseMount;

   /// @name Mounted Images
   /// @{

   /// An image mounted on a shapebase.
   struct MountedImage {
      ShapeBaseImageData* dataBlock;
      ShapeBaseImageData::StateData *state;
      ShapeBaseImageData* nextImage;
      NetStringHandle skinNameHandle;
      NetStringHandle nextSkinNameHandle;
      String appliedSkinName;
      NetStringHandle scriptAnimPrefix;      ///< The script based anim prefix

      /// @name State
      ///
      /// Variables tracking the state machine
      /// representing this specific mounted image.
      /// @{

      bool loaded;                  ///< Is the image loaded?
      bool nextLoaded;              ///< Is the next state going to result in the image being loaded?
      F32 delayTime;                ///< Time till next state.
      F32 rDT;                      ///< Remainder delta time. Used internally.
      U32 fireCount;                ///< Fire skip count.
                                    ///
                                    /// This is incremented every time the triggerDown bit is changed,
                                    /// so that the engine won't be too confused if the player toggles the
                                    /// trigger a bunch of times in a short period.
                                    ///
                                    /// @note The network deals with this variable at 3-bit precision, so it
                                    /// can only range 0-7.
                                    ///
                                    /// @see ShapeBase::setImageState()
      U32 altFireCount;             ///< Alternate fire skip count.
                                    ///< @see fireCount

      U32 reloadCount;              ///< Reload skip count.
                                    ///< @see fireCount

      bool triggerDown;             ///< Is the trigger down?
      bool altTriggerDown;          ///< Is the second trigger down?

      bool ammo;                    ///< Do we have ammo?
                                    ///
                                    /// May be true based on either energy OR ammo.

      bool target;                  ///< Have we acquired a targer?
      bool wet;                     ///< Is the weapon wet?

      bool motion;                  ///< Is the player in motion?

      bool genericTrigger[ShapeBaseImageData::MaxGenericTriggers];   ///< Generic triggers not assigned to anything in particular.  These
                                                                     ///  may be used to indicate some transition should occur.
      /// @}

      /// @name 3space
      ///
      /// Handles to threads and shapeinstances in the 3space system.
      /// @{
      TSShapeInstance* shapeInstance[ShapeBaseImageData::MaxShapes];
      TSThread *ambientThread[ShapeBaseImageData::MaxShapes];
      TSThread *visThread[ShapeBaseImageData::MaxShapes];
      TSThread *animThread[ShapeBaseImageData::MaxShapes];
      TSThread *flashThread[ShapeBaseImageData::MaxShapes];
      TSThread *spinThread[ShapeBaseImageData::MaxShapes];

      bool doAnimateAllShapes;      ///< Should all threads animate across all shapes to keep them in sync?
      bool forceAnimateAllShapes;   ///< If the mounted image's owner is being controlled by the client
                                    ///  and the image's datablock animateAllShapes field is true
                                    ///  then set this to true and pass along to the client.  This will help
                                    ///  in the cases where the client's control object is ghosted but does
                                    ///  not yet have its controlling client set correctly due to networking
                                    ///  order of operations.  All this for the MountedImage::updateDoAnimateAllShapes()
                                    ///  optimization.
      U32 lastShapeIndex;           ///< Tracks the last shape index.
      /// @}

      /// @name Effects
      ///
      /// Variables relating to lights, sounds, and particles.
      /// @{
      SimTime lightStart;     ///< Starting time for light flashes.
      LightInfo* lightInfo;   ///< The real light (if any) associated with this weapon image.

      Vector<SFXSource*> mSoundSources; ///< Vector of currently playing sounds
      void updateSoundSources(const MatrixF& renderTransform);  
      void addSoundSource(SFXSource* source);

      /// Represent the state of a specific particle emitter on the image.
      struct ImageEmitter {
         S32 node;
         F32 time;
         SimObjectPtr<ParticleEmitter> emitter;
      };
      ImageEmitter emitter[MaxImageEmitters];

      /// @}

      //
      MountedImage();
      ~MountedImage();

      void updateDoAnimateAllShapes(const ShapeBase* owner);
   };
   MountedImage mMountedImageList[MaxMountedImages];

   /// @}

   /// @name Render settings
   /// @{

   TSShapeInstance*  mShapeInstance;
   Convex *          mConvexList;
   NetStringHandle   mSkinNameHandle;
   String            mAppliedSkinName;

   NetStringHandle mShapeNameHandle;   ///< Name sent to client
   /// @}

   /// @name Physical Properties
   /// @{

   F32 mEnergy;                     ///< Current enery level.
   F32 mRechargeRate;               ///< Energy recharge rate (in units/tick).

   F32 mMass;                       ///< Mass.
   F32 mOneOverMass;                ///< Inverse of mass.
                                    /// @note This is used to optimize certain physics calculations.

   /// @}

   /// @name Physical Properties
   ///
   /// Properties for the current object, which are calculated
   /// based on the container we are in.
   ///
   /// @see ShapeBase::updateContainer()
   /// @see ShapeBase::mContainer
   /// @{
   F32 mDrag;                       ///< Drag.
   F32 mBuoyancy;                   ///< Buoyancy factor.
   String mLiquidType;              ///< Type of liquid (if any) we are in.
   F32 mLiquidHeight;               ///< Height of liquid around us (from 0..1).
   F32 mWaterCoverage;              ///< Percent of this object covered by water

   Point3F mAppliedForce;
   F32 mGravityMod;
   /// @}

   F32 mDamageFlash;
   F32 mWhiteOut;

   bool mFlipFadeVal;

   /// Last shield direction (cur. unused)
   Point3F mShieldNormal;

   /// Camera shake caused by weapon fire.
   CameraShake *mWeaponCamShake;

 public:

   /// @name Collision Notification
   /// This is used to keep us from spamming collision notifications. When
   /// a collision occurs, we add to this list; then we don't notify anyone
   /// of the collision until the CollisionTimeout expires (which by default
   /// occurs in 1/10 of a second).
   ///
   /// @see notifyCollision(), queueCollision()
   /// @{
   struct CollisionTimeout 
   {
      CollisionTimeout* next;
      SceneObject* object;
      U32 objectNumber;
      SimTime expireTime;
      VectorF vector;
   };
   CollisionTimeout* mTimeoutList;
   static CollisionTimeout* sFreeTimeoutList;

   /// Go through all the items in the collision queue and call onCollision on them all
   /// @see onCollision
   void notifyCollision();

   /// Add a collision to the queue of collisions waiting to be handled @see onCollision
   /// @param   object   Object collision occurs with
   /// @param   vec      Vector along which collision occurs
   void queueCollision( SceneObject *object, const VectorF &vec);

   /// @see SceneObject
   virtual void onCollision( SceneObject *object, const VectorF &vec );

   /// @}
 protected:

   /// @name Damage
   /// @{
   F32  mDamage;
   F32  mRepairRate;
   F32  mRepairReserve;
   DamageState mDamageState;
   TSThread *mDamageThread;
   TSThread *mHulkThread;
   VectorF damageDir;
   /// @}

   /// @name Cloaking
   /// @{
   bool mCloaked;
   F32  mCloakLevel;
//   TextureHandle mCloakTexture;
   /// @}

   /// @name Fading
   /// @{
   bool  mFadeOut;
   bool  mFading;
   F32   mFadeVal;
   F32   mFadeElapsedTime;
   F32   mFadeTime;
   F32   mFadeDelay;
public:
   F32   getFadeVal() { return mFadeVal; }
   /// @}
protected:

   /// @name Control info
   /// @{
   F32  mCameraFov;           ///< The camera vertical FOV in degrees.
   bool mIsControlled;        ///< Client side controlled flag

   /// @}
public:
   static U32 sLastRenderFrame;

protected:

   U32 mLastRenderFrame;
   F32 mLastRenderDistance;

   /// Do a reskin if necessary.
   virtual void reSkin();

   /// This recalculates the total mass of the object, and all mounted objects
   void updateMass();

   /// @name Image Manipulation
   /// @{

   /// Utility function to call script functions which have to do with ShapeBase
   /// objects.
   /// @param   imageSlot  Image Slot id
   /// @param   function   Function
   void scriptCallback(U32 imageSlot,const char* function);

   /// Assign a ShapeBaseImage to an image slot
   /// @param   imageSlot   Image Slot ID
   /// @param   imageData   ShapeBaseImageData to assign
   /// @param   skinNameHandle Skin texture name
   /// @param   loaded      Is the image loaded?
   /// @param   ammo        Does the image have ammo?
   /// @param   triggerDown Is the trigger on this image down?
   /// @param   altTriggerDown Is the second trigger on this image down?
   /// @param   target      Does the image have a target?
   virtual void setImage(  U32 imageSlot, 
                           ShapeBaseImageData* imageData, 
                           NetStringHandle &skinNameHandle,
                           bool loaded = true, bool ammo = false, 
                           bool triggerDown = false,
                           bool altTriggerDown = false,
                           bool motion = false,
                           bool genericTrigger0 = false,
                           bool genericTrigger1 = false,
                           bool genericTrigger2 = false,
                           bool genericTrigger3 = false,
                           bool target = false );

   /// Clear out an image slot
   /// @param   imageSlot   Image slot id
   void resetImageSlot(U32 imageSlot);

   /// Get the firing action state of the image
   /// @param   imageSlot   Image slot id
   U32  getImageFireState(U32 imageSlot);

   /// Get the alternate firing action state of the image
   /// @param   imageSlot   Image slot id
   U32  getImageAltFireState(U32 imageSlot);

   /// Get the reload action state of the image
   /// @param   imageSlot   Image slot id
   U32  getImageReloadState(U32 imageSlot);

   /// Sets the state of the image by state index
   /// @param   imageSlot   Image slot id
   /// @param   state       State id
   /// @param   force       Force image to state or let it finish then change
   void setImageState(U32 imageSlot, U32 state, bool force = false);

   void updateAnimThread(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState=NULL);

   /// Get the animation prefix for the image
   /// @param   imageSlot        Image slot id
   /// @param   imageShapeIndex  Shape index (1st person, etc.) used to look up the prefix text
   virtual const char* getImageAnimPrefix(U32 imageSlot, S32 imageShapeIndex) { return ""; }

   /// Advance animation on a image
   /// @param   imageSlot   Image slot id
   /// @param   dt          Change in time since last animation update
   void updateImageAnimation(U32 imageSlot, F32 dt);

   /// Advance state of image
   /// @param   imageSlot   Image slot id
   /// @param   dt          Change in time since last state update
   void updateImageState(U32 imageSlot,F32 dt);

   /// Start up the particle emitter for the this shapebase
   /// @param   image   Mounted image
   /// @param   state   State of shape base image
   void startImageEmitter(MountedImage &image,ShapeBaseImageData::StateData &state);

   /// Get light information for a mounted image
   /// @param   imageSlot   Image slot id
   Light* getImageLight(U32 imageSlot);

   /// Get the shape index to use for a mounted image
   /// @param   image   Mounted image
   U32 getImageShapeIndex(const MountedImage& image) const;

   /// @}

   /// Prune out non looping sounds from the sound manager which have expired
   void updateServerAudio();

   /// Updates the audio state of the supplied sound
   /// @param   st   Sound
   void updateAudioState(Sound& st);

   /// Recalculates the spacial sound based on the current position of the object
   /// emitting the sound.
   void updateAudioPos();

   /// Update bouyency and drag properties
   void updateContainer();

   /// @name Events
   /// @{
   virtual void onDeleteNotify(SimObject*);
   virtual void onImage(U32 imageSlot, bool unmount);
   virtual void onImageRecoil(U32 imageSlot,ShapeBaseImageData::StateData::RecoilState);
   virtual void onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue);
   virtual void onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset=false);
   virtual void onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt);
   virtual void ejectShellCasing( U32 imageSlot );
   virtual void updateDamageLevel();
   virtual void updateDamageState();
   virtual void blowUp();
   virtual void onImpact(SceneObject* obj, VectorF vec);
   virtual void onImpact(VectorF vec);
   /// @}

   /// The inner prep render function that does the 
   /// standard work to render the shapes.
   void _prepRenderImage(  SceneRenderState* state, 
                           bool renderSelf, 
                           bool renderMountedImages );

   /// Renders the shape bounds as well as the 
   /// bounds of all mounted shape images.
   void _renderBoundingBox( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* );

   void emitDust( ParticleEmitter* emitter, F32 triggerHeight, const Point3F& offset, U32 numMilliseconds, const Point3F& axis = Point3F::Zero );

public:
   ShapeBase();
   ~ShapeBase();

   TSShapeInstance* getShapeInstance() { return mShapeInstance; }

   static void initPersistFields();
   static bool _setFieldSkin( void *object, const char *index, const char *data );
   static const char *_getFieldSkin( void *object, const char *data );

   /// @name Network state masks
   /// @{

   ///
   enum ShapeBaseMasks {
      NameMask        = Parent::NextFreeMask,
      DamageMask      = Parent::NextFreeMask << 1,
      NoWarpMask      = Parent::NextFreeMask << 2,
      CloakMask       = Parent::NextFreeMask << 3,
      ShieldMask      = Parent::NextFreeMask << 4,
      InvincibleMask  = Parent::NextFreeMask << 5,
      SkinMask        = Parent::NextFreeMask << 6,
      MeshHiddenMask  = Parent::NextFreeMask << 7,
      SoundMaskN      = Parent::NextFreeMask << 8,       ///< Extends + MaxSoundThreads bits
      ThreadMaskN     = SoundMaskN  << MaxSoundThreads,  ///< Extends + MaxScriptThreads bits
      ImageMaskN      = ThreadMaskN << MaxScriptThreads, ///< Extends + MaxMountedImage bits
      NextFreeMask    = ImageMaskN  << MaxMountedImages
   };

   enum BaseMaskConstants {
      SoundMask      = (SoundMaskN << MaxSoundThreads) - SoundMaskN,
      ThreadMask     = (ThreadMaskN << MaxScriptThreads) - ThreadMaskN,
      ImageMask      = (ImageMaskN << MaxMountedImages) - ImageMaskN
   };

   /// @}

   static F32  sWhiteoutDec;
   static F32  sDamageFlashDec;
   static F32  sFullCorrectionDistance;
   static F32  sCloakSpeed;               // Time to cloak, in seconds
      
   CubeReflector mCubeReflector;

   /// @name Initialization
   /// @{

   bool onAdd();
   void onRemove();
   void onSceneRemove();
   static void consoleInit();
   bool onNewDataBlock( GameBaseData *dptr, bool reload );

   /// @}

   /// @name Name & Skin tags
   /// @{
   void setShapeName(const char*);
   const char* getShapeName();
   void setSkinName(const char*);
   const char* getSkinName();
   /// @}

   /// @name Mesh Visibility
   /// @{
   
protected:

   /// A bit vector of the meshes forced to be hidden.
   BitVector mMeshHidden;

   /// Sync the shape instance with the hidden mesh bit vector.
   void _updateHiddenMeshes();               

public:

   /// Change the hidden state on all the meshes.
   void setAllMeshesHidden( bool forceHidden );  

   /// Set the force hidden state on a mesh.
   void setMeshHidden( S32 meshIndex, bool forceHidden ); 
                        
   /// Set the force hidden state on a named mesh.
   void setMeshHidden( const char *meshName, bool forceHidden ); 
   
#ifndef TORQUE_SHIPPING

   /// Prints the list of meshes and their visibility state
   /// to the console for debugging purposes.
   void dumpMeshVisibility();
                      
#endif   

   /// @}

public:

   /// @name Basic attributes
   /// @{

   /// Sets the amount of damage on this object.
   void setDamageLevel(F32 damage);

   /// Changes the object's damage state.
   /// @param   state   New state of the object
   void setDamageState(DamageState state);

   /// Changes the object's damage state, based on a named state.
   /// @see setDamageState
   /// @param   state   New state of the object as a string.
   bool setDamageState(const char* state);

   /// Returns the name of the current damage state as a string.
   const char* getDamageStateName();

   /// Returns the current damage state.
   DamageState getDamageState() { return mDamageState; }

   /// Returns true if the object is destroyed.
   bool isDestroyed() { return mDamageState == Destroyed; }

   /// Sets the rate at which the object regenerates damage.
   ///
   /// @param  rate  Repair rate in units/second.
   void setRepairRate(F32 rate) { mRepairRate = rate; }

   /// Returns damage amount.
   F32  getDamageLevel()  { return mDamage; }

   /// Returns the damage percentage.
   ///
   /// @return Damage factor, between 0.0 - 1.0
   F32  getDamageValue();
 
   /// Returns the datablock.maxDamage value  
   F32 getMaxDamage(); 

   /// Returns the rate at which the object regenerates damage
   F32  getRepairRate() { return mRepairRate; }

   /// Adds damage to an object
   /// @param   amount   Amount of of damage to add
   void applyDamage(F32 amount);

   /// Removes damage to an object
   /// @param   amount   Amount to repair object by
   void applyRepair(F32 amount);

   /// Sets the direction from which the damage is coming
   /// @param   vec   Vector indicating the direction of the damage
   void setDamageDir(const VectorF& vec)  { damageDir = vec; }

   /// Sets the level of energy for this object
   /// @param   energy   Level of energy to assign to this object
   virtual void setEnergyLevel(F32 energy);

   /// Sets the rate at which the energy replentishes itself
   /// @param   rate   Rate at which energy restores
   void setRechargeRate(F32 rate) { mRechargeRate = rate; }

   /// Returns the amount of energy in the object
   F32  getEnergyLevel();

   /// Returns the percentage of energy, 0.0 - 1.0
   F32  getEnergyValue();

   /// Returns the recharge rate
   F32  getRechargeRate() { return mRechargeRate; }

   /// @}

   /// @name Script sounds
   /// @{

   /// Plays an audio sound from a mounted object
   /// @param   slot    Mount slot ID
   /// @param   track   Audio track to play
   void playAudio(U32 slot,SFXTrack* track);
   void playAudio( U32 slot, SFXProfile* profile ) { playAudio( slot, ( SFXTrack* ) profile ); }

   /// Stops audio from a mounted object
   /// @param   slot   Mount slot ID
   void stopAudio(U32 slot);
   /// @}

   /// @name Script animation
   /// @{

   const char *getThreadSequenceName( U32 slot );

   /// Sets the animation thread for a mounted object
   /// @param   slot   Mount slot ID
   /// @param    seq   Sequence id
   /// @param   reset   Reset the sequence
   bool setThreadSequence(U32 slot, S32 seq, bool reset = true);

   /// Update the animation thread
   /// @param   st   Thread to update
   void updateThread(Thread& st);

   /// Stop the current thread from playing on a mounted object
   /// @param   slot   Mount slot ID
   bool stopThread(U32 slot);

   /// Destroys the given animation thread
   /// @param   slot   Mount slot ID
   bool destroyThread(U32 slot);

   /// Pause the running animation thread
   /// @param   slot   Mount slot ID
   bool pauseThread(U32 slot);

   /// Start playing the running animation thread again
   /// @param   slot   Mount slot ID
   bool playThread(U32 slot);

   /// Set the thread position
   /// @param   slot   Mount slot ID
   /// @param   pos    Position
   bool setThreadPosition( U32 slot, F32 pos );

   /// Toggle the thread as reversed or normal (For example, sidestep-right reversed is sidestep-left)
   /// @param   slot   Mount slot ID
   /// @param   forward   True if the animation is to be played normally
   bool setThreadDir(U32 slot,bool forward);

   /// Set the thread time scale
   /// @param   slot   Mount slot ID
   /// @param   timescale   Timescale
   bool setThreadTimeScale( U32 slot, F32 timeScale );

   /// Start the sound associated with an animation thread
   /// @param   thread   Thread
   void startSequenceSound(Thread& thread);

   /// Stop the sound associated with an animation thread
   /// @param   thread   Thread
   void stopThreadSound(Thread& thread);

   /// Advance all animation threads attached to this shapebase
   /// @param   dt   Change in time from last call to this function
   void advanceThreads(F32 dt);
   /// @}

   /// @name Cloaking
   /// @{

   /// Force uncloaking of object
   /// @param   reason   Reason this is being forced to uncloak, this is passed directly to script control
   void forceUncloak(const char *reason);

   /// Set cloaked state of object
   /// @param   cloaked   True if object is cloaked
   void setCloakedState(bool cloaked);

   /// Returns true if object is cloaked
   bool getCloakedState();

   /// Returns level of cloaking, as it's not an instant "now you see it, now you don't"
   F32 getCloakLevel();
   /// @}

   /// @name Mounted objects
   /// @{   
   virtual void onMount( SceneObject *obj, S32 node );   
   virtual void onUnmount( SceneObject *obj,S32 node );   
   virtual void getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat );
   virtual void getRenderMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat );
   /// @}

   /// Returns where the AI should be to repair this object
   ///
   /// @note Legacy code from Tribes 2, but still works
   Point3F getAIRepairPoint();

   /// @name Mounted Images
   /// @{

   /// Mount an image (ShapeBaseImage) onto an image slot
   /// @param   image   ShapeBaseImage to mount
   /// @param   imageSlot Image mount point
   /// @param   loaded    True if weapon is loaded (it assumes it's a weapon)
   /// @param   skinNameHandle   Skin name for object
   virtual bool mountImage(ShapeBaseImageData* image,U32 imageSlot,bool loaded, NetStringHandle &skinNameHandle);

   /// Unmount an image from a slot
   /// @param   imageSlot   Mount point
   virtual bool unmountImage(U32 imageSlot);

   /// Gets the information on the image mounted in a slot
   /// @param   imageSlot   Mount point
   ShapeBaseImageData* getMountedImage(U32 imageSlot);

   /// Gets the mounted image on on a slot
   /// @param   imageSlot   Mount Point
   MountedImage* getImageStruct(U32 imageSlot);

   TSShapeInstance* getImageShapeInstance(U32 imageSlot)
   {
      const MountedImage &image = mMountedImageList[imageSlot];
      U32 imageShapeIndex = getImageShapeIndex(image);
      if(image.dataBlock && image.shapeInstance[imageShapeIndex])
         return image.shapeInstance[imageShapeIndex];
      return NULL;
   }

   /// Gets the next image which will be put in an image slot
   /// @see setImageState
   /// @param   imageSlot   mount Point
   ShapeBaseImageData* getPendingImage(U32 imageSlot);


   /// Returns true if the mounted image is firing
   /// @param   imageSlot   Mountpoint
   bool isImageFiring(U32 imageSlot);

   /// Returns true if the mounted image is alternate firing
   /// @param   imageSlot   Mountpoint
   bool isImageAltFiring(U32 imageSlot);

   /// Returns true if the mounted image is reloading
   /// @param   imageSlot   Mountpoint
   bool isImageReloading(U32 imageSlot);

   /// This will return true if, when triggered, the object will fire.
   /// @param   imageSlot   mount point
   /// @param   ns          Used internally for recursion, do not mess with
   /// @param   depth       Used internally for recursion, do not mess with
   bool isImageReady(U32 imageSlot,U32 ns = (U32)-1,U32 depth = 0);

   /// Returns true if the specified image is mounted
   /// @param   image   ShapeBase image
   bool isImageMounted(ShapeBaseImageData* image);

   /// Returns the slot which the image specified is mounted on
   /// @param   image   Image to test for
   S32 getMountSlot(ShapeBaseImageData* image);

   /// Returns the skin for the image in a slot
   /// @param   imageSlot   Image slot to get the skin from
   NetStringHandle getImageSkinTag(U32 imageSlot);

   /// Check if the given state exists on the mounted Image
   /// @param   imageSlot   Image slot id
   /// @param   state       Image state to check for
   bool hasImageState(U32 imageSlot, const char* state);

   /// Returns the image state as a string
   /// @param   imageSlot   Image slot to check state
   const char* getImageState(U32 imageSlot);

   /// Sets the generic trigger state of the image
   /// @param   imageSlot   Image slot
   /// @param   trigger     Generic trigger number 0-3
   /// @param   state       True if generic trigger is down
   void setImageGenericTriggerState(U32 imageSlot, U32 trigger, bool state);

   /// Returns the generic trigger state of the image
   /// @param   imageSlot   Image slot
   /// @param   trigger     Generic trigger number 0-3
   bool getImageGenericTriggerState(U32 imageSlot, U32 trigger);

   /// Sets the trigger state of the image (Ie trigger pulled down on gun)
   /// @param   imageSlot   Image slot
   /// @param   trigger     True if trigger is down
   void setImageTriggerState(U32 imageSlot,bool trigger);

   /// Returns the trigger state of the image
   /// @param   imageSlot   Image slot
   bool getImageTriggerState(U32 imageSlot);

   /// Sets the alt trigger state of the image (Ie trigger pulled down on gun)
   /// @param   imageSlot   Image slot
   /// @param   trigger     True if trigger is down
   void setImageAltTriggerState( U32 imageSlot, bool trigger );

   /// Returns the alt trigger state of the image
   /// @param   imageSlot   Image slot
   bool getImageAltTriggerState( U32 imageSlot );

   /// Sets the flag if the image uses ammo or energy
   /// @param   imageSlot   Image slot
   /// @param   ammo        True if the weapon uses ammo, not energy
   void setImageAmmoState(U32 imageSlot,bool ammo);

   /// Returns true if the image uses ammo, not energy
   /// @param   imageSlot   Image slot
   bool getImageAmmoState(U32 imageSlot);

   /// Sets the image as wet or not, IE if you wanted a gun not to function underwater
   /// @param   imageSlot   Image slot
   /// @param   wet         True if image is wet
   void setImageWetState(U32 imageSlot,bool wet);

   /// Returns true if image is wet
   /// @param   imageSlot   image slot
   bool getImageWetState(U32 imageSlot);

   /// Sets the image as in motion or not, IE if you wanted a gun not to sway while the player is in motion
   /// @param   imageSlot   Image slot
   /// @param   motion     True if image is in motion
   void setImageMotionState(U32 imageSlot,bool motion);

   /// Returns true if image is in motion
   /// @param   imageSlot   image slot
   bool getImageMotionState(U32 imageSlot);

   /// Sets the flag if the image has a target
   /// @param   imageSlot   Image slot
   /// @param   ammo        True if the weapon has a target
   void setImageTargetState(U32 imageSlot,bool ammo);

   /// Returns true if the image has a target
   /// @param   imageSlot   Image slot
   bool getImageTargetState(U32 imageSlot);

   /// Sets the flag of if the image is loaded with ammo
   /// @param   imageSlot   Image slot
   /// @param   loaded      True if object is loaded with ammo
   void setImageLoadedState(U32 imageSlot,bool loaded);

   /// Returns true if object is loaded with ammo
   /// @param   imageSlot   Image slot
   bool getImageLoadedState(U32 imageSlot);

   /// Set the script animation prefix for the image
   /// @param   imageSlot        Image slot id
   /// @param   prefix           The prefix applied to the image
   void setImageScriptAnimPrefix(U32 imageSlot, NetStringHandle prefix);

   /// Get the script animation prefix for the image
   /// @param   imageSlot        Image slot id
   /// @param   imageShapeIndex  Shape index (1st person, etc.) used to look up the prefix text
   NetStringHandle getImageScriptAnimPrefix(U32 imageSlot);

   /// Modify muzzle, if needed, to aim at whatever is straight in front of eye.
   /// Returns true if result is actually modified.
   /// @param   muzMat   Muzzle transform (in/out)
   /// @param   result   Corrected muzzle vector (out)
   bool getCorrectedAim(const MatrixF& muzMat, VectorF* result);

   /// Gets the muzzle vector of a specified slot
   /// @param   imageSlot   Image slot to check transform for
   /// @param   vec   Muzzle vector (out)
   virtual void getMuzzleVector(U32 imageSlot,VectorF* vec);

   /// Gets the point of the muzzle of the image
   /// @param   imageSlot   Image slot
   /// @param   pos   Muzzle point (out)
   void getMuzzlePoint(U32 imageSlot,Point3F* pos);

   /// @}

   /// @name Transforms
   /// @{

   /// Gets the minimum viewing distance, maximum viewing distance, camera offsetand rotation
   /// for this object, if the world were to be viewed through its eyes
   /// @param   min   Minimum viewing distance
   /// @param   max   Maximum viewing distance
   /// @param   offset Offset of the camera from the origin in local space
   /// @param   rot   Rotation matrix
   virtual void getCameraParameters(F32 *min,F32* max,Point3F* offset,MatrixF* rot);

   /// Gets the camera to world space transform matrix
   /// @todo Find out what pos does
   /// @param   pos   TODO: Find out what this does
   /// @param   mat   Camera transform (out)
   virtual void getCameraTransform(F32* pos,MatrixF* mat);

   /// Gets the index of a node inside a mounted image given the name
   /// @param   imageSlot   Image slot
   /// @param   nodeName    Node name
   S32 getNodeIndex(U32 imageSlot,StringTableEntry nodeName);

   /// @}

   /// @name Object Transforms
   /// @{

   /// Returns the eye transform of this shape, IE the eyes of a player
   /// @param   mat   Eye transform (out)
   virtual void getEyeTransform(MatrixF* mat);

   /// Returns the eye transform of this shape without including mounted images, IE the eyes of a player
   /// @param   mat   Eye transform (out)
   virtual void getEyeBaseTransform(MatrixF* mat);

   /// The retraction transform is the muzzle transform in world space.
   ///
   /// If the gun is pushed back, for instance, if the player ran against something,
   /// the muzzle of the gun gets pushed back towards the player, towards this location.
   /// @param   imageSlot   Image slot
   /// @param   mat   Transform (out)
   virtual void getRetractionTransform(U32 imageSlot,MatrixF* mat);

   /// Muzzle transform of mounted object in world space
   /// @param   imageSlot   Image slot
   /// @param   mat         Muzzle transform (out)
   virtual void getMuzzleTransform(U32 imageSlot,MatrixF* mat);

   /// Gets the transform of a mounted image in world space
   /// @param   imageSlot   Image slot
   /// @param   mat    Transform (out)
   virtual void getImageTransform(U32 imageSlot,MatrixF* mat);

   /// Gets the transform of a node on a mounted image in world space
   /// @param   imageSlot   Image Slot
   /// @param   node    node on image
   /// @param   mat   Transform (out)
   virtual void getImageTransform(U32 imageSlot,S32 node, MatrixF* mat);

   /// Gets the transform of a node on a mounted image in world space
   /// @param   imageSlot   Image Slot
   /// @param   nodeName    Name of node on image
   /// @param   mat         Transform (out)
   virtual void getImageTransform(U32 imageSlot, StringTableEntry nodeName, MatrixF* mat);

   ///@}

   /// @name Render transforms
   /// Render transforms are different from object transforms in that the render transform of an object
   /// is where, in world space, the object is actually rendered. The object transform is the
   /// absolute position of the object, as in where it should be.
   ///
   /// The render transforms typically vary from object transforms due to client side prediction.
   ///
   /// Other than that, these functions are identical to their object-transform counterparts
   ///
   /// @note These are meaningless on the server.
   /// @{
   virtual void getRenderRetractionTransform(U32 index,MatrixF* mat);   
   virtual void getRenderMuzzleTransform(U32 index,MatrixF* mat);   
   virtual void getRenderImageTransform(U32 imageSlot,MatrixF* mat,bool noEyeOffset=false);
   virtual void getRenderImageTransform(U32 index,S32 node, MatrixF* mat);
   virtual void getRenderImageTransform(U32 index, StringTableEntry nodeName, MatrixF* mat);
   virtual void getRenderMuzzleVector(U32 imageSlot,VectorF* vec);
   virtual void getRenderMuzzlePoint(U32 imageSlot,Point3F* pos);
   virtual void getRenderEyeTransform(MatrixF* mat);
   virtual void getRenderEyeBaseTransform(MatrixF* mat);
   /// @}



   /// @name Screen Flashing
   /// @{

   /// Returns the level of screenflash that should be used
   virtual F32  getDamageFlash() const;

   /// Sets the flash level
   /// @param   amt   Level of flash
   virtual void setDamageFlash(const F32 amt);

   /// White out is the flash-grenade blindness effect
   /// This returns the level of flash to create
   virtual F32  getWhiteOut() const;

   /// Set the level of flash blindness
   virtual void setWhiteOut(const F32);
   /// @}

   /// @name Invincibility effect
   /// This is the screen effect when invincible in the HUD
   /// @see GameRenderFilters()
   /// @{

   /// Returns the level of invincibility effect
   virtual F32 getInvincibleEffect() const;

   /// Initializes invincibility effect and interpolation parameters
   ///
   /// @param   time   Time it takes to become invincible
   /// @param   speed  Speed at which invincibility effects progress
   virtual void setupInvincibleEffect(F32 time, F32 speed);

   /// Advance invincibility effect animation
   /// @param   dt   Time since last call of this function
   virtual void updateInvincibleEffect(F32 dt);

   /// @}

   /// @name Movement & velocity
   /// @{

   /// Sets the velocity of this object
   /// @param   vel   Velocity vector
   virtual void setVelocity(const VectorF& vel);

   /// Applies an impulse force to this object
   /// @param   pos   Position where impulse came from in world space
   /// @param   vec   Velocity vector (Impulse force F = m * v)
   virtual void applyImpulse(const Point3F& pos,const VectorF& vec);

   /// @}

   /// @name Cameras and Control
   /// @{

   /// Returns the object controlling this object
   ShapeBase* getControllingObject()   { return mControllingObject; }

   /// Sets the controlling object
   /// @param   obj   New controlling object
   virtual void setControllingObject(ShapeBase* obj);
   
   ///
   virtual void setControllingClient( GameConnection* connection );

   /// Returns the object this is controlling
   virtual ShapeBase* getControlObject();

   /// sets the object this is controlling
   /// @param   obj   New controlled object
   virtual void setControlObject(ShapeBase *obj);

   /// Returns true if this object is controlling by something
   bool isControlled() { return(mIsControlled); }

   /// Returns true if this object is being used as a camera in first person
   bool isFirstPerson() const;

   /// Returns true if the camera uses this objects eye point (defined by modeler)
   bool useObjsEyePoint() const;

   /// Returns true if this object can only be used as a first person camera
   bool onlyFirstPerson() const;

   /// Returns the vertical field of view in degrees for 
   /// this object if used as a camera.
   virtual F32 getCameraFov() { return mCameraFov; }

   /// Returns the default vertical field of view in degrees
   /// if this object is used as a camera.
   virtual F32 getDefaultCameraFov() { return mDataBlock->cameraDefaultFov; }

   /// Sets the vertical field of view in degrees for this 
   /// object if used as a camera.
   /// @param   yfov  The vertical FOV in degrees to test.
   virtual void setCameraFov(F32 fov);

   /// Returns true if the vertical FOV in degrees is within 
   /// allowable parameters of the datablock.
   /// @param   yfov  The vertical FOV in degrees to test.
   /// @see ShapeBaseData::cameraMinFov
   /// @see ShapeBaseData::cameraMaxFov
   virtual bool isValidCameraFov(F32 fov);
   /// @}


   void processTick(const Move *move);
   void advanceTime(F32 dt);

   /// @name Rendering
   /// @{

   /// Returns the renderable shape of this object
   TSShape const* getShape();

   /// @see SceneObject
   virtual void prepRenderImage( SceneRenderState* state );

   /// Used from ShapeBase::_prepRenderImage() to submit render 
   /// instances for the main shape or its mounted elements.
   virtual void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex );

   /// Preprender logic
   virtual void calcClassRenderData() { }

   /// Virtualize this so other classes may override it for custom reasons.
   virtual void renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state );
   /// @}

   /// Control object scoping
   void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);

   bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
   bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info);
   bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere);
   void buildConvex(const Box3F& box, Convex* convex);

   /// @name Rendering
   /// @{

   /// Increments the last rendered frame number
   static void incRenderFrame()    { sLastRenderFrame++; }

   /// Returns true if the last frame calculated rendered
   bool didRenderLastRender() { return mLastRenderFrame == sLastRenderFrame; }

   /// Sets the state of this object as hidden or not. If an object is hidden
   /// it is removed entirely from collisions, it is not ghosted and is
   /// essentially "non existant" as far as simulation is concerned.
   /// @param   hidden   True if object is to be hidden
   virtual void setHidden(bool hidden);

   /// Returns true if this object can be damaged
   bool isInvincible();

   /// Start fade of object in/out
   /// @param   fadeTime Time fade should take
   /// @param   fadeDelay Delay before starting fade
   /// @param   fadeOut   True if object is fading out, false if fading in.
   void startFade( F32 fadeTime, F32 fadeDelay = 0.0, bool fadeOut = true );

   /// Traverses mounted objects and registers light sources with the light manager
   /// @param   lightManager   Light manager to register with
   /// @param   lightingScene  Set to true if the scene is being lit, in which case these lights will not be used
   //void registerLights(LightManager * lightManager, bool lightingScene);

   // ISceneLight
   virtual void submitLights( LightManager *lm, bool staticLighting );
   virtual LightInfo* getLight() { return NULL; }

   /// @}

   /// Returns true if the point specified is in the water
   /// @param   point    Point to test in world space
   bool pointInWater( Point3F &point );

   /// Returns the percentage of this object covered by water
   F32 getWaterCoverage()  { return mWaterCoverage; }

   /// Returns the height of the liquid on this object
   F32 getLiquidHeight()  { return mLiquidHeight; }

   virtual WaterObject* getCurrentWaterObject();

   void setCurrentWaterObject( WaterObject *obj );

   virtual F32 getMass() const { return mMass; }

   /// @name Network
   /// @{

   F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips);
   U32  packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
   void unpackUpdate(NetConnection *conn, BitStream *stream);
   void writePacketData(GameConnection *conn, BitStream *stream);
   void readPacketData(GameConnection *conn, BitStream *stream);

   /// @}

   DECLARE_CONOBJECT(ShapeBase);

protected:
   DECLARE_CALLBACK( F32, validateCameraFov, (F32 fov) );

};


//------------------------------------------------------------------------------
// inlines
//------------------------------------------------------------------------------

inline bool ShapeBase::getCloakedState()
{
   return(mCloaked);
}

inline F32 ShapeBase::getCloakLevel()
{
   return(mCloakLevel);
}

inline const char* ShapeBase::getShapeName()
{
   return mShapeNameHandle.getString();
}

inline const char* ShapeBase::getSkinName()
{
   return mSkinNameHandle.getString();
}

inline WaterObject* ShapeBase::getCurrentWaterObject()
{
   if ( isMounted() && mShapeBaseMount )   
      return mShapeBaseMount->getCurrentWaterObject();
   
   return mCurrentWaterObject;
}

#endif  // _H_SHAPEBASE_
