/******************************************************************
  
  Module:  physics.h
  
  Author: Sean Craig
  
  Description: Classes responsible for moving objects through the 
  world and colliding them with the rest of the world.  Velocities
  can be set from an animation or forces in the world. 

  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

#ifndef PHYSICS_H
#define PHYSICS_H

//-------------------------------------------------------- Includes
#include "SyPropObject.h"
#include "SyCollSurfType.h"
#include "gameobj.h"
#include "rulepacket.h"

//-------------------------------------------------------- Typedefs
//---------------------------------------------- Class Declarations
class cGameObject;
class SyScene;
class SyCollSphere;
class cSpellMaster;

class cPhysics : public SyPropObject
{
public:
  typedef enum _eLocomotionMethod
  {
    LOCO_ANIMATION,       /* playing an animation */
    LOCO_JUMP,            /* falling or jumping */
    LOCO_PUSHED,          
    LOCO_PROJECTILE,
    LOCO_CUTSCENE,
    LOCO_RAGDOLL,            /* falling or jumping */
  } eLocomotionMethod;

  static const float GRAVITY;

  cPhysics();
  virtual ~cPhysics(){};

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID             =  0xFFFF5001;


  // public interface

  virtual void Init();
  virtual void PostInit(){}  // after network object created
  virtual void Exit(){}
  virtual void Reset(){}
  virtual void Update(float time){}
  virtual void UpdateOutOfScope(float time){}
  virtual void ClearBouncingAround() {}
  virtual int SetKeyframed( SyVect3& loc, SyVect3& hpr) { return 0; } /* returns 1 if the loc OR hpr is changed */

  virtual bool CheckForDelete(){return false;}; // this is the ONLY place where objects can delete themselves.

  virtual const SyVect3& GetVelocity();
  virtual void SetVelocity(const SyVect3& velocity);

  void SetOwner(cGameObject *);
  virtual void SetCollideable(bool collide);
  virtual bool IsCollideable() { return mCollideable; }
  virtual float GetCollisionRadius(){return 0.0f;};

  virtual eLocomotionMethod GetLocomotion(){return LOCO_PUSHED;};

  virtual void NetworkSetLocation(const SyVect3& location, bool bTeleport);
  virtual void NetworkSetHPR(const SyVect3& hpr);
  virtual void NetworkSetVelocity(const SyVect3& velocity){};
  virtual bool NetworkReceiveMessage(const char *packet,int size){return false;}; // someone's making a request of the owner 


  virtual const SyVect3& GetRenderLocation();
  virtual const SyVect3& GetRenderHPR();
  
  virtual bool           SetSafeLocation(const SyVect3 &loc); // finds a safe location near this spot

  virtual void           Knockback(const SyVect3 &source_loc,float XZamount,float Yamount,bool bKnockdown,float getUpTime){};

  virtual void           Nudge(){}; // let's everyone know physics needs updating...
  virtual bool           IsMoving() {return false;};
  virtual bool           IsPushing(){return false;};

protected:
  bool               mCollideable;
  cGameObject       *mOwner;
};


class cPhysicsStatic : public cPhysics
{
public:
  cPhysicsStatic();
  virtual ~cPhysicsStatic(){};

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID       =  0xFFFF5002;


  // public interface

};

// moving objects (such as game items)
class cPhysicsDynamic : public cPhysics
{
public:
  cPhysicsDynamic();
  virtual ~cPhysicsDynamic(){};
  virtual void Init();
  virtual void PostInit();  // after network object created
  virtual void Reset();

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID      =  0xFFFF5007;
  static const SyPropID PropId_Velocity = 0x1000;

  bool                 Push(float time,SyVect3 &displacement);
  virtual void         Update(float time);
  virtual void         UpdateOutOfScope(float time);

  virtual void         SetVelocity(const SyVect3 &velocity);
  virtual const SyVect3 &GetVelocity() {return mVelocity;};

  bool IsJustBouncingAround();
  void ClearBouncingAround() {mbIsUnderHavokControl=false;}
  int SetKeyframed( SyVect3& loc, SyVect3& hpr);

  void SetCollideable(bool collide);

#ifdef HAVOK_ENABLED
  void UpdateHavok(float time);
#endif

  int                  CalcFloor( SyVect3&        Point, 
                                  SyScene&        Scene, 
                                  SyVect3&        Floor, 
                                  SyVect3&        FloorNormal,
                                  SyCollSurfType& FloorSurfaceType ,
                                  float           radius,
                                  float           height);
  virtual void      Nudge(){mIsMovingCountdown = 4;}; // let's everyone know physics needs updating...
  virtual bool      IsMoving() {return (mIsMovingCountdown != 0);};
  virtual const SyVect3& GetRenderLocation(){return mRenderLocation;};
  virtual const SyVect3& GetRenderHPR(){return mRenderHPR;};
  virtual void      NetworkSetLocation(const SyVect3 &location, bool bTeleport);
  virtual void      NetworkSetVelocity(const SyVect3 &velocity);
  virtual bool      NetworkReceiveMessage(const char *packet,int size); // someone's making a request of the owner 

protected:
  void UpdateRemoteRenderLocation(float time);

  void StartFallingDamageCheck();
  void StopFallingDamageCheck();

  virtual void         CollisionTest(float time,SyVect3 &displacement);
  void                 CollisionTestPush(float time,SyVect3 &displacement);

  int                  mIsMovingCountdown;
  SyVect3              mVelocity;

  // network variables

  SyVect3              mRenderLocation;
  SyVect3              mRenderOffset;
  SyVect3              mRenderHPR;
  float                mNetworkUpdateTime;
  float                mLastValidFloorY;
  bool                 mbFalling;
  bool                 mbIsUnderHavokControl;
};

// barrels, sometimes immoveable things
class cPhysicsProp : public cPhysicsDynamic
{
public:
  cPhysicsProp();
  virtual ~cPhysicsProp(){};

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID         =  0xFFFF5006;

  virtual void         PostInit();
  virtual void         Update(float time);
  virtual void         Reset();
  virtual void         Nudge();
  virtual bool         SetSafeLocation(const SyVect3 &loc); // finds a safe location near this spot

          void         Throw(cGameObject* pThrower, const SyVect3& targetPos);

          float        GetExtentInDirection(const SyVect3& loc, const SyVect3& dir, float width=1.0f, SyVect3* pHitPoint=NULL);
  
          void         Impulse(const SyVect3& force, float rotSpeed);

          void         ApplyThrowDamage(cGameObject* pHit);

  virtual void         Knockback(const SyVect3 &source_loc,float XZamount,float Yamount,bool bKnockdown,float getUpTime);

protected:
  virtual void         CollisionTest(float time,SyVect3 &displacement);

  tGameObjectID        mThrowerID;
  tGameObjectID        mLastHitID;
  int                  mDamageAmount;
};

#ifdef HAVOK_ENABLED
class SyHavokPhantom;
#endif

class cPhysicsAnimated : public cPhysicsDynamic
{
public:

  cPhysicsAnimated();
  virtual ~cPhysicsAnimated();

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID     =  0xFFFF5003;

  
  // public interface
  virtual void Init();
  virtual void Update(float time);
  virtual void Reset();

  virtual float GetCollisionRadius(){return mCollisionRadius;};
  virtual void Nudge(); // let's everyone know physics needs updating...
  virtual void Jump(const SyVect3 &force);
  virtual void Ragdoll(const SyVect3 &force);
  virtual void Impulse(const SyVect3 &force);
  virtual void Impact(const SyVect3 &dir,float mag);
  virtual eLocomotionMethod GetLocomotion(){return meLocomotion;};
          void SetLocomotion(eLocomotionMethod eLM){meLocomotion = eLM;};
          


  virtual bool           IsPushing(){return mPushing;};
  virtual cGameObject*   GetPushingTarget(){return mPushingTarget;};


  virtual bool           SetSafeLocation(const SyVect3 &loc); // finds a safe location near this spot
  virtual void           Knockback(const SyVect3 &source_loc, float XZamount, float Yamount, bool bKnockdown, float getUpTime);
  virtual bool           IsMoving() {return true;};

          void           Throw(tGameObjectID throwerID, float heading, float XZamount, float Yamount);
          tGameObjectID  GetThrowerID() { return mThrowerID; }
          tGameObjectID  GetFloorPropID() { return mFloorPropID; }

          void           GetRingLocation(SyVect3 &floor,SyVect3 &normal,SyCollSurfType &type);
protected:


  virtual void         CollisionTest(float time,SyVect3 &displacement);
          bool         CheckLocation(SyVect3 &loc);
          void         _CollisionTestWorld(float time, SyVect3 &Start,SyVect3 &End);
          bool         PushTarget(float time, SyVect3 &destination,SyCollSphere &CollSphere);


          int                  CalcFloor( SyVect3&        Point, 
                                          SyScene&        Scene, 
                                          SyVect3&        Floor, 
                                          SyVect3&        FloorNormal,
                                          SyCollSurfType& FloorSurfaceType,
                                          tGameObjectID&  floorPropID);

  eLocomotionMethod    meLocomotion;
  float32              mCollisionHeight;
  float32              mCollisionRadius;
  float32              mHeight;               // The height of the character in meters
  float32              mFloorCollRadius;     // Radius of collision sphere used during floor/ceiling checks
  float32              mMinStepSize;              // Minimum distance to step up or down
  float32              mMaxStepSize;              // Maximum distance to step up or down
  SyVect3              mJumpStartVel;
  SyVect3              mJumpPushVel;
  bool                 mPushing;  // true if we're currently pushing up against with something (forces a walk)
  cGameObject*         mPushingTarget;
  tGameObjectID        mThrowerID;
  tGameObjectID        mFloorPropID;
  SyVect3              mLastFloorPropLocation;

  SyVect3              mImpactDir; // from getting pushed by an attack
  float                mImpactMag; // from getting pushed by an attack
  int                  mRecursionDepth;

#ifdef HAVOK_ENABLED
  SyHavokPhantom*      mpPhantom;
#endif
};

class cPhysicsProjectile : public cPhysicsDynamic
{
public:
  cPhysicsProjectile();
  virtual ~cPhysicsProjectile(){};

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID   =  0xFFFF5005;

  virtual void Update(float time);
  virtual void Exit();

  virtual eLocomotionMethod GetLocomotion(){return LOCO_PROJECTILE;};
  virtual bool CheckForDelete(); // this is the ONLY place where objects can delete themselves.

  virtual void SetCollideable(bool collide);

  void Launch(cGameObject *pSourceObj, const SyVect3 &targetLoc, const SyVect3* pOverrideSourcePos = NULL);
  tGameObjectID GetSourceID() { return mSourceID; }
  void SetLifetime(float t)   { mTimer = t; }
 
  static void Shoot(tGameID projectileMasterID,
                    cGameObject* pOwner,
                    const SyVect3& targetPos,
                    cGameObject* pTarget = NULL,
                    const cSpellMaster* pSpellMaster = NULL,
                    const SyVect3* overrideSourcePos = NULL);

protected:

  void                 CollisionTest(float time,SyVect3 &displacement);
  int                  CalcFloor( SyVect3&        Point, 
                                  SyScene&        Scene, 
                                  SyVect3&        Floor, 
                                  SyVect3&        FloorNormal,
                                  SyCollSurfType& FloorSurfaceType);

  bool                 CanHit(cGameObject* pHit);

protected:
  tGameObjectID        mSourceID;
  float                mTimer; // to expire old projectiles
  bool                 mHitGround;
  float                mHeadingTowards; // towards target
  tGameObjectID        mLastHitID; // last entity hit, to prevent multiple hits 
  cDamagePacket        mDmgPacket;
  tGameID              mSpellID;
  int                  mEffectScriptHandle;
  SyVect3              mPinCushionHPR;
  int                  mPinCushionNode;
};

// items (bounce when they are dropped, then when at rest they bob up and down
class cPhysicsItem : public cPhysicsDynamic
{
public:
  cPhysicsItem();

  /* Property Class Support */
  static int           InitPropClass();
  static SyPropObject* Creator();
  static const int     mCLASSID      =  0xFFFF5008;

  virtual void         Update(float time);

protected:
  float mGroundHeight;
  float mBob;
};

//------------------------------------------- Function Declarations
void RegPropClasses_Physics();
void Physics_RegisterTuningVariables();
//--------------------------------------------------------- Globals
//------------------------------------------------ Inline Functions

#endif
