/******************************************************************
  
  Module:  gameobj.cpp
  
  Author: Sean Craig
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "gameobj.h"
#include "SyScene.h"
#include "intel.h"
#include "stats.h"
#include "inventory.h"
#include "graphic.h"
#include "physics.h"
#include "registry.h"
#include "titan.h"
#include "TitanPeeringNetwork.h"
#include "netpacket.h"
#include "script.h"
#include "cameracontroller.h"
#include "script_pawn.h"
#include "debugoverlay.h"
#include "SyStr.h"
#include "SyHavok.h"

 //---------------------------------------------- Class Declarations


//--------------------------------------------------------- Globals
//--------------------------------------------------------- Locals
static const float POSITION_UPDATE_RATE = 0.05f; // time between updates.
static const float SIMULATION_DISTANCE = 100.0f; // meters
static const float SIMULATION_BORDER = 5.0f; // grey area to prevent thrashing
static const float ENTER_OWNERSHIP = (SIMULATION_DISTANCE-10.0f)*(SIMULATION_DISTANCE-10.0f);
static const float LEAVE_OWNERSHIP = (SIMULATION_DISTANCE)*(SIMULATION_DISTANCE);

//----------------------------------------- Functions Declarations
//------------------------------------ Member Functions Definitions

//------------------------------------ cGameObject

cGameObject::cGameObject() :
  mRemote(false),
  mbInScope(false),
  mLocDirty(true),
  mActorDirty(true),
  mbMarkedForDelete(false),
  mPositionUpdateTime(0.0f),
  mIntel(NULL),
  mStats(NULL),
  mInventory(NULL),
  mGraphic(NULL),
#ifdef HAVOK_ENABLED
  mRigidBody(0),
#endif
  mPhysics(NULL),
  mScript(NULL),
  mCarriedByID(ID_NONE)
{
  InitPropObject( GAMEOBJECT_CLASSID );

  mLastPacket.mbTeleport = true;
}

cGameObject::~cGameObject()
{
  delete mIntel;
  delete mStats;
  delete mInventory;
  delete mGraphic;
#ifdef HAVOK_ENABLED
  if (mRigidBody)
  {
    SyScene *scene = GetTitan()->GetScene();
    scene->GetCollideDev()->GetHavok()->RemoveRigidBody( mRigidBody );
    mRigidBody = 0;
  }
#endif
  delete mPhysics;
  delete mScript;
}

int           
cGameObject::InitPropClass()
{
  SyPropClassID ClassID = GAMEOBJECT_CLASSID;

/* Add the class */

  AddClass( ClassID, 
           "cGameObject", 
            Creator, 
            ClassID, 
            0 ); 

  AddInt32Property(ClassID,PropId_ID,SyMemberOffset(cGameObject,mID),"mID");
  SyPropEnum *propEnum;
  AddEnumProperty(ClassID,PropId_Type,SyMemberOffset(cGameObject,mType),"mType",&propEnum);
  propEnum->Add(cGameObject::OBJ_PLAYER,"PLAYER");
  propEnum->Add(cGameObject::OBJ_NPC,"NPC");
  propEnum->Add(cGameObject::OBJ_ITEM,"ITEM");
  propEnum->Add(cGameObject::OBJ_MARKER,"MARKER");
  propEnum->Add(cGameObject::OBJ_PROJECTILE,"PROJECTILE");
  propEnum->Add(cGameObject::OBJ_PROP,"PROP");
  propEnum->Add(cGameObject::OBJ_DUMMY,"DUMMY");

  AddSubObjectPtrProperty(ClassID,PropId_Intel,SyMemberOffset(cGameObject,mIntel),"mIntel");
  AddSubObjectPtrProperty(ClassID,PropId_Stats,SyMemberOffset(cGameObject,mStats),"mStats");
  AddSubObjectPtrProperty(ClassID,PropId_Inventory,SyMemberOffset(cGameObject,mInventory),"mInventory");
  AddSubObjectPtrProperty(ClassID,PropId_Graphic,SyMemberOffset(cGameObject,mGraphic),"mGraphic");
  AddSubObjectPtrProperty(ClassID,PropId_Physics,SyMemberOffset(cGameObject,mPhysics),"mPhysics");
  AddSubObjectPtrProperty(ClassID,PropId_Script,SyMemberOffset(cGameObject,mScript),"mScript");

  AddStringProperty(ClassID,PropId_Name,SyMemberOffset(cGameObject,mName),"mName");
  AddVect3Property(ClassID,PropId_HPR,SyMemberOffset(cGameObject,mPosition.GetHPR()),"mHPR");
  AddVect3Property(ClassID,PropId_Location,SyMemberOffset(cGameObject,mPosition.GetLocation()),"mLocation");

  return(0);
}

SyPropObject* 
cGameObject::Creator()  
{
  cGameObject *pObject;

  pObject = SyNew cGameObject();
  if (pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }


  return(pObject);
}

tGameObjectID          
cGameObject::Create(cGameObjectRegistry *owner,tGameID id,tObjectType newtype) // creates and registers subobjects
{
  mType = newtype;
  mID  = id;
  //mNameID.SetName(name);

  switch (newtype)
  {
    case OBJ_PLAYER:
      {
        mIntel = new cIntelPlayer();
        mStats = new cStatsCharacter();
        mInventory = new cInventoryCharacter(); 
        mGraphic = new cGraphicCharacter();
        mPhysics = new cPhysicsAnimated();
        mScript = new cScript();
      }
      break;

    case OBJ_NPC:
      {
        mIntel = new cIntelNPC();
        mStats = new cStatsCharacter();
        mInventory = new cInventoryCharacter(); 
        mGraphic = new cGraphicCharacter();
        mPhysics = new cPhysicsAnimated();
        mScript = new cScript();
      }
      break;

    case OBJ_ITEM: // in the world
      {
        mIntel = new cIntelNone();
        mStats = new cStatsItem();
        mInventory = new cInventoryNone(); // unless backpack??? 
        mGraphic = new cGraphicActor();
        mPhysics = new cPhysicsItem();
        mScript = new cScript();
      }
      break;

    case OBJ_MARKER:
      {
        mIntel = new cIntelNone();
        mStats = new cStatsMarker();
        mInventory = new cInventoryNone(); 
        mGraphic = new cGraphic();
        mPhysics = new cPhysicsStatic();
        mScript = new cScript();
      }
      break;

    case OBJ_PROJECTILE:
      {
        mIntel = new cIntelProjectile();
        mStats = new cStatsProjectile();
        mInventory = new cInventoryNone(); 
        mGraphic = new cGraphicActor();
        mPhysics = new cPhysicsProjectile();
        mScript = new cScript();
      }
      break;

    case OBJ_PROP:
      {
        mIntel = new cIntelProp();
        mStats = new cStatsProp();
        mInventory = new cInventoryNone(); 
        mGraphic = new cGraphicProp();
        mPhysics = new cPhysicsProp();
        mScript = new cScript();
      }
      break;

    case OBJ_DUMMY:
      {
        mIntel = new cIntelNone();
        mStats = new cStatsDummy();
        mInventory = new cInventoryNone(); 
        mGraphic = new cGraphicDummy();
        mPhysics = new cPhysicsStatic();
        mScript = new cScript();
      }
      break;

    default:
      SyAssert(0); // unknown object type
      return ID_NONE;
      break;
  }

  FixUp(owner);

  return GetID();
}

void
cGameObject::FixUp(cGameObjectRegistry *owner)
{     
  mOwner = owner;
  mIntel->SetOwner(this);
  mStats->SetOwner(this);
  mInventory->SetOwner(this);
  mGraphic->SetOwner(this);
  mPhysics->SetOwner(this);
  mScript->SetOwner(this);
}

#ifdef HAVOK_ENABLED
void
cGameObject::SetRigidBody(cPhysics*physics)
{
  // BH - this was checked before entering this function, but I am relying on it later, so assert...
  SyAssert( OBJ_PROP == mType );

  SyScene* scene = GetTitan()->GetScene();
  SyHavok* havok = scene->GetCollideDev()->GetHavok();
  SyActorHandle actorHandle = mGraphic->GetActorHandle();

  if (actorHandle!=SyActorNull)
  {
    SyVect3 zeroVelocity(0.0f,0.0f,0.0f);

    const cStatsPropMaster *propMaster =  static_cast< cStatsProp* >( mStats )->GetMaster();

    if ( havok->AddRigidBody( SyHavok::PROP, 
                              scene->Sprite(scene->GetActorSprite( actorHandle )), 
                              GetLocation(), 
                              GetHPR(), 
                              0.0f, 
                              zeroVelocity, 
                              &mRigidBody, 
                              1,
                              propMaster->mPhysicsMass,
                              propMaster->mPhysicsFriction) )
    {
      SyMatrix44 m;
      havok->GetMatrix( GetRigidBody(), m );
      SyVect3 hpr,scale,loc;
      m.ConvertTo( hpr, scale, loc );
      SetLocation( loc );
      SetHPR( hpr );
      //physics->Nudge();
    }
  }
}
#endif

void
cGameObject::Nudge()
{
  GetPhysics()->Nudge();
}

void        
cGameObject::Init()
{
  mStats->Init();
  mInventory->Init();
  mGraphic->Init();
  mPhysics->Init();
  mScript->Init();
  mIntel->Init();   // AI update has dependencies on stats being initialized

  if (mGraphic->GetActorHandle()!=SyActorNull)
  {
    GetTitan()->GetScene()->SetActorID(mGraphic->GetActorHandle(), GetID());
  }
#ifdef HAVOK_ENABLED
  if (mType == OBJ_PROP
    && static_cast< cStatsProp* >(mStats)->IsSimulatable())
  {
    SetRigidBody( mPhysics );
  }
#endif
}

void
cGameObject::PostInit() // has to happen after network create
{
  mStats->PostInit();
  mPhysics->PostInit();

  if (mType == OBJ_PROP || mType == OBJ_NPC || mType == OBJ_PLAYER || mType == OBJ_MARKER)
  {
    GetTitan()->GetScriptSys()->ScriptEvent( PET_INIT, GetID(), ID_NONE );
  }

  mGraphic->SetDirty();
}

void        
cGameObject::Exit()
{
  mIntel->Exit();
  mStats->Exit();
  mInventory->Exit();
  mGraphic->Exit();
  mPhysics->Exit();
  mScript->Exit();
}

void              
cGameObject::Reload() // after level transition
{
  mGraphic->Reload();
}


void
cGameObject::Reset()
{
  mStats->Reset(); // goes first to reset death flag
  mIntel->Reset();
  mGraphic->Reset();
  mPhysics->Reset();
  mScript->Reset();
}

void cGameObject::EnterScope()
{
  mIntel->EnterScope();
  mGraphic->EnterScope();
}

void cGameObject::ExitScope()
{
  mIntel->ExitScope();
  mGraphic->ExitScope();
}

void
cGameObject::UpdateBeforePhysics(float deltatime)
{
  // first check to see we're in scope
  UpdateScope();

  if (mbInScope)
  {
    mStats->Update(deltatime);  
    mInventory->Update(deltatime);
    mIntel->Update(deltatime);      //  read controller
    mScript->Update(deltatime);

    // different call order depending on whether physics is driving animation or verse vica
    if (mPhysics->GetLocomotion()==cPhysics::LOCO_ANIMATION || mPhysics->GetLocomotion()==cPhysics::LOCO_CUTSCENE )
    {
      mGraphic->Update(deltatime);  //  update animation state machine
    }
    else
    {
      /* update graphic after physics */
    }
  }
}

void        
cGameObject::UpdatePhysics(float deltatime) // once/frame
{
  /* step havok */

  if (mbInScope)
  {
    mPhysics->Update(deltatime);  //  update location
  }
  else
  {
    // update dead reckoning to make sure our rendered position is still valid
    mPhysics->UpdateOutOfScope(deltatime);
  }
}


void
cGameObject::UpdateAfterPhysics(float deltatime)
{
  if (mbInScope)
  {
    // different call order depending on whether physics is driving animation or verse vica
    if (mPhysics->GetLocomotion()==cPhysics::LOCO_ANIMATION || mPhysics->GetLocomotion()==cPhysics::LOCO_CUTSCENE )
    {
      /* updated mGraphic before physics */
    }
    else
    {
      mGraphic->Update(deltatime);  //  update animation state machine
    }
    NetworkUpdate(deltatime);
  }
  UpdateOwnership();
  UpdateExtents();

}
void cGameObject::Activate(cGameObject *activater)
{
  SyAssert(mStats);

  mStats->Activate(activater);
}

void cGameObject::Open()
{
  SyAssert(mIntel);

  mIntel->Open();
}

void              
cGameObject::UpdateExtents()
{
  if (GetType() == OBJ_MARKER)
  {
    // markers don't have extents
    return;
  }
  if (mLocDirty)
  {
    float loc = GetLocation().X;

    bool bUpdatedFromBBox = false;

    if (OBJ_PROP == mType)
    {
      SyActorHandle actorHandle = mGraphic->GetActorHandle();
      if (SyActorNull != actorHandle)
      {
        SySprite* pSprite = GetTitan()->GetScene()->GetActorSpritePtr(actorHandle);

        if (pSprite)
        {
          SyMatrix44 transform;
          transform.Transform(GetHPR(), 1.0f, GetLocation());

          SyVect3 corners[8];
          pSprite->GetBBox().Transform(transform, corners);

          if (corners[0].X >= corners[1].X && corners[0].X >= corners[2].X && corners[0].X >= corners[3].X)
          {
            mMaxX = corners[0].X;
          }
          else if (corners[1].X >= corners[0].X && corners[1].X >= corners[2].X && corners[1].X >= corners[3].X)
          {
            mMaxX = corners[1].X;
          }
          else if (corners[2].X >= corners[0].X && corners[2].X >= corners[1].X && corners[2].X >= corners[3].X)
          {
            mMaxX = corners[2].X;
          }
          else
          {
            mMaxX = corners[3].X;
          }

          if (corners[0].X <= corners[1].X && corners[0].X <= corners[2].X && corners[0].X <= corners[3].X)
          {
            mMinX = corners[0].X;
          }
          else if (corners[1].X <= corners[0].X && corners[1].X <= corners[2].X && corners[1].X <= corners[3].X)
          {
            mMinX = corners[1].X;
          }
          else if (corners[2].X <= corners[0].X && corners[2].X <= corners[1].X && corners[2].X <= corners[3].X)
          {
            mMinX = corners[2].X;
          }
          else
          {
            mMinX = corners[3].X;
          }

          bUpdatedFromBBox = true;
        }
      }
    }

    if (!bUpdatedFromBBox)
    {
      float radius = 1.0f;
      if (mPhysics != NULL) radius = mPhysics->GetCollisionRadius();
      if (radius <=0) radius = 0.01f;

      mMaxX = loc + radius;
      mMinX = loc - radius; 
    }

    mLocDirty = false;

    cGameObject *next = mOwner->NextMin(this);
    while (next != NULL && next->GetMinX() < mMinX)
    {
      mOwner->ReinsertAfterMin(next,this); 
      next = mOwner->NextMin(this);
    }

    cGameObject *prev = mOwner->PrevMin(this);
    while (prev != NULL && prev->GetMinX() > mMinX)
    {
      mOwner->ReinsertAfterMin(this,prev); 
      prev = mOwner->PrevMin(this);
    }

    next = mOwner->NextMax(this);
    while (next != NULL && next->GetMaxX() < mMaxX)
    {
      mOwner->ReinsertAfterMax(next,this); 
      next = mOwner->NextMax(this);
    }

    prev = mOwner->PrevMax(this);
    while (prev != NULL && prev->GetMaxX() > mMaxX)
    {
      mOwner->ReinsertAfterMax(this,prev); 
      prev = mOwner->PrevMax(this);
    }
  }
}


bool              
cGameObject::SetSafeLocation(const SyVect3 &loc) // finds a safe location near this spot
{
  bool bFoundSafeLoc = mPhysics->SetSafeLocation(loc);

  if (bFoundSafeLoc)
  {
    mLastPacket.mbTeleport = true;
  }

  return bFoundSafeLoc;
}

bool              
cGameObject::CheckForDelete()
{
  if (mPhysics->CheckForDelete())
  {
    return true;
  }

  if (mGraphic->CheckForDelete())
  {
    return true;
  }

  if (mbMarkedForDelete && IsLocal())
  {
    return true;
  }

  return false;
}

void cGameObject::MarkForDelete()
{
  if (IsLocal())
  {
    mbMarkedForDelete = true;
  }
}
void        
cGameObject::Prerender() // once/frame
{
  if (ID_NONE != mCarriedByID)
  {
    mPhysics->ClearBouncingAround();
    cGameObject* pCarriedBy = mOwner->Fetch(mCarriedByID);

    if (pCarriedBy && !pCarriedBy->GetStats()->IsDead())
    {
      SyActorHandle carriedByHandle = pCarriedBy->GetGraphic()->GetActorHandle();
      SyScene* pScene = mOwner->GetTitan()->GetScene();
      SyCSprite* pSprite = NULL;

      if (!pScene || 
          SyActorNull == carriedByHandle ||
          pScene->GetActorSpriteType(carriedByHandle) != SYSPRITETYPE_CSPRITE ||
          ((pSprite = static_cast<SyCSprite*>(pScene->GetActorSpritePtr(carriedByHandle))) == NULL))
      {
        SyAssertf(false, "Bad object attachment");
        return;
      }

      SyMatrix44 mat;
      SyVect3 rightHPR, rightLoc, leftHPR, leftLoc, loc, hpr;
      float scale;

      pCarriedBy->GetGraphic()->Prerender();
      mat.Identity();
      pSprite->CalcIdentNodeWorldTransform(CHAR_NODE_RIGHT_HAND_CARRY, carriedByHandle, *pScene, mat);
      mat.ConvertTo(rightHPR, scale, rightLoc);

      mat.Identity();
      pSprite->CalcIdentNodeWorldTransform(CHAR_NODE_LEFT_HAND_CARRY, carriedByHandle, *pScene, mat);
      mat.ConvertTo(leftHPR, scale, leftLoc);

      loc = rightLoc + leftLoc;
      loc *= 0.5f;
      hpr = pCarriedBy->GetHPR();

      // total hack to handle table offsets - BP
      cStatsProp* pStats = prop_cast<cStatsProp*>(GetStats());
      if (pStats && SyStr::Strnicmp(pStats->GetMasterName(), "table", 5) == 0)
      {
        // lower the table slowly to match the character's anim
        cGraphicCharacter* pCarriedByGraphic = prop_cast<cGraphicCharacter*>(pCarriedBy->GetGraphic());
        if (pCarriedByGraphic)
        {
          float t = 1.0f;
          if (pCarriedByGraphic->GetAnimController()->GetAnimState() == AS_PICKUP_PROP)
          {
            t = pCarriedByGraphic->GetAnimController()->GetAnimTime();

            /* 0.0 to 1.0 seconds - just use y */
            /* 1.0 to 1.2 seconds - fade from y to y-0.75 meters */
            /* 1.2 to ... seconds - continue using y-0.75 meters */
            if (t < 1.0f)
            {
              t = 0.0f;
            }
            else if (t < 1.2f)
            {
              t = ((t - 1.0f)*5.0f);
            }
            else
            {
              t = 1.0f;
            }
          }
          else if (pCarriedByGraphic->GetAnimController()->GetAnimState() == AS_THROW_PROP)
          {
            t = pCarriedByGraphic->GetAnimController()->GetAnimTime();

            /* 0.0 to 0.1 seconds - -0.75+y meters */
            /* 0.1 to 0.3 seconds - fade -0.75+y to y meters */
            /* 0.3 ... - just use y */
            if (t < 0.1f)
            {
              t = 1.0f;
            }
            else if (t < 0.3f)
            {
              t = 1.0f - ((t - 0.1f)*5.0f);
            }
            else
            {
              t = 0.0f;
            }
          }

          loc.Y -= t*0.75f;
        }

        /* rotate heading 90 degrees */
        hpr.X = hpr.X+(SY_PI*0.5f);
      }

      mPhysics->SetCollideable( 0 );
      mPhysics->SetKeyframed( loc, hpr );
      SetLocation(loc);
      SetHPR(hpr);
    }
    else
    {
      mPhysics->SetCollideable( 1 );
      mCarriedByID = ID_NONE;
    }
  }

  mGraphic->Prerender(); // update visual location in scene from game object
}

Titan *           
cGameObject::GetTitan() const
{
  return mOwner->GetTitan();
}

float
cGameObject::GetDistance(cGameObject *other) const
{
  const SyVect3 &loc = GetLocation();
  const SyVect3 &other_loc = other->GetLocation();
  float distance = loc.Distance(other_loc);
  distance -= GetPhysics()->GetCollisionRadius();
  distance -= other->GetPhysics()->GetCollisionRadius();
  distance = SY_MAX(distance, 0.0f);
  return distance;
}

float
cGameObject::GetDistance(const SyVect3& other_loc) const
{
  const SyVect3 &loc = GetLocation();
  float distance = loc.Distance(other_loc);
  distance -= GetPhysics()->GetCollisionRadius();
  distance = SY_MAX(distance, 0.0f);
  return distance;
}

float  
cGameObject::GetDistanceInDirection(const SyVect3& other_loc, 
                                    const SyVect3& dir,
                                    float width, SyVect3* pHitPoint) const
{
  const SyVect3 &loc = GetLocation();
  float distance = 0.0f;

  if (pHitPoint)
  {
    *pHitPoint = loc;
  }

  if (OBJ_PROP == mType)
  {
    distance = static_cast<cPhysicsProp*>(mPhysics)->GetExtentInDirection(other_loc, dir, width, pHitPoint);
  }
  else
  {
    distance = loc.Distance(other_loc) - GetPhysics()->GetCollisionRadius();
  }

  distance = SY_MAX(distance, 0.0f);
  return distance;
}

float
cGameObject::GetHeadingTowards(cGameObject *other) const
{
  return GetHeadingTowards(other->GetLocation());
}

float 
cGameObject::GetHeadingTowards(const SyVect3 &other_loc) const
{
  const SyVect3 &loc = GetLocation();
  SyVect3 delta;
  delta.Sub(other_loc,loc);
  return SY_ATAN2(delta.X,delta.Z);
}

float
cGameObject::GetPitchTowards(cGameObject *other) const
{
  return GetPitchTowards(other->GetLocation());
}

float
cGameObject::GetPitchTowards(const SyVect3 &other_loc) const
{
  const SyVect3 &loc = GetLocation();
  
  SyVect3 delta;
  delta.Sub(other_loc,loc);
  return SY_ATAN2( delta.Y, SY_SQRT(delta.X * delta.X + delta.Z * delta.Z));
}

tGameID           
cGameObject::GetNameID() const
{
  return SyHashResourceID(mName.AsChar());
}

int               
cGameObject::Reset( SyObjFile& File )// resets to state in the file 
{
  int controllerID = -1;

  if (mIntel && OBJ_PLAYER == mType)
  {
    controllerID = static_cast<cIntelPlayer*>(mIntel)->GetControllerID();
  }

  delete mIntel;
  delete mStats;
  delete mInventory;
  delete mGraphic;
  delete mPhysics;
  delete mScript;

  mIntel = NULL;
  mStats = NULL;
  mInventory = NULL;
  mGraphic = NULL;
  mPhysics = NULL;
  mScript = NULL;

  int readResult = Read(File);
  
  if (readResult >= 0 && mIntel && OBJ_PLAYER == mType)
  {
    static_cast<cIntelPlayer*>(mIntel)->SetControllerID(controllerID);
  }

  return readResult;
}

int               
cGameObject::GetState( SyObjFile& File ) // resets to state in the file 
{
  return Write(File,SYPROPSERIALTYPE_STANDARD);
}


void              
cGameObject::NetworkReceiveBroadcast(const char *packet,int packetlen)
{
  cNetPacket::eNetPacketType type;

  SyAssertf(IsRemote(),"Network Broadcast on local object?");

  SyPack::Unpack8u(packet, &type);      // note: pack-format must match cNetPacket::Pack

  switch (type)
  {
  case cNetPacket::NET_POSITION:
    {
      cNetPositionPacket position_packet;
      position_packet.UnpackBuffer(packet, packetlen);

      mPhysics->NetworkSetLocation(position_packet.mLocation, position_packet.mbTeleport);
      mPhysics->NetworkSetVelocity(position_packet.mVelocity);
      mPhysics->NetworkSetHPR(position_packet.mHPR);

      cGraphicCharacter* pCharGraphic = prop_cast<cGraphicCharacter *>(mGraphic);
      if (pCharGraphic != NULL)
      {
        cAnimCharControllerInput *input = pCharGraphic->GetAnimInput();
        if (input !=NULL)
        {
          input->mHeadingRequest = position_packet.mHeadingRequest;
          input->mSpeedRequest = position_packet.mSpeedRequest;
        }
      }
    }
    break;

  case cNetPacket::NET_ANIMSTATE:
    {
      mGraphic->NetworkReceiveBroadcast(packet,packetlen);
    }
    break;

  case cNetPacket::NET_ADD_CONDITION: 
  case cNetPacket::NET_REMOVE_CONDITION: 
  case cNetPacket::NET_REMOVE_CONDITIONS_FROM_SOURCE: 
  case cNetPacket::NET_RESET_OBJECT: 
  case cNetPacket::NET_DAMAGE:
  case cNetPacket::NET_CAST_SPELL:
  case cNetPacket::NET_ACTIVATE_CONFIRM:
    {
      mStats->NetworkReceiveBroadcast(packet,packetlen);
    }
    break;
  default:
  SyAssertf(0,"Unknown packet type");
  break;
  }

}

void              
cGameObject::NetworkReceiveMessage(const char *packet,int packetlen)// the owning peer of this object has broadcast an update
{
  cNetPacket::eNetPacketType type;

  SyAssertf(IsLocal(),"Network Message on remote object?");

  SyPack::Unpack8u(packet, &type);      // note: pack-format must match cNetPacket::Pack

  switch (type)
  {
  case cNetPacket::NET_ADD_CONDITION: 
  case cNetPacket::NET_REMOVE_CONDITION: 
  case cNetPacket::NET_REMOVE_CONDITIONS_FROM_SOURCE: 
  case cNetPacket::NET_RESET_OBJECT: 
  case cNetPacket::NET_ACTIVATE_REQUEST:
    {
      mStats->NetworkReceiveMessage(packet,packetlen);
    }
    break;
  case cNetPacket::NET_KNOCKBACK:
  case cNetPacket::NET_PUSH_REQUEST:
    {
      mPhysics->NetworkReceiveMessage(packet,packetlen);
    }
    break;
  default:
  SyAssertf(0,"Unknown packet type");
  break;
  }
}

bool
cGameObject::NetworkNeedToSendPacket()
{
  return (mPhysics->IsMoving() || mLastPacket.mbTeleport);
}

void              
cGameObject::NetworkUpdate(float time)
{
  if (!mRemote)
  {
    mPositionUpdateTime += time;
    if(mPositionUpdateTime > POSITION_UPDATE_RATE)
    {
      mPositionUpdateTime = 0.0f;
      if (NetworkNeedToSendPacket())
      {
        mLastPacket.mLocation = GetLocation();
        mLastPacket.mVelocity = mPhysics->GetVelocity();
        mLastPacket.mHPR      = GetHPR();

        cGraphicCharacter* pCharGraphic = prop_cast<cGraphicCharacter *>(mGraphic);
        if (pCharGraphic != NULL && pCharGraphic->GetAnimInput() !=NULL)
        {
          mLastPacket.mHeadingRequest = pCharGraphic->GetAnimInput()->mHeadingRequest;
          mLastPacket.mSpeedRequest = pCharGraphic->GetAnimInput()->mSpeedRequest;
        }

        char buf[8192];
        int len = mLastPacket.PackBuffer(buf, sizeof(buf));
        GetTitan()->GetPeeringNetwork()->ObjectBroadcast(mID, buf, len);

        mLastPacket.mbTeleport = false;
      }
    }
  }
}

void cGameObject::SetRemote(bool bRemote)
{
  mRemote = bRemote;

  if (bRemote)
  {
    mIntel->OnLoseOwnership();
  }
  else
  {
    mIntel->OnGainOwnership();
  }
}

void
cGameObject::UpdateOwnership()
{
  if (OBJ_PLAYER == mType ||
      OBJ_MARKER == mType ||
      OBJ_DUMMY == mType)
  {
    return;
  }

  if (IsRemote())
  {
    return;
  }

  cGameObjectRegistry* pRegistry = GetRegistry();
  cGameObject* pPlayer;
  cGameObject *owner = NULL;
  cGameObject* closestPlayer = NULL;
  float distSqr, closestDistSqr = 0.0f;
  bool bOutsideOwnerRadius = false;

  int myPeerId = GetTitan()->GetPeeringNetwork()->GetObjectPeerId(GetID());

  pPlayer = pRegistry->BeginType(OBJ_PLAYER);
  while (pPlayer != NULL)
  {
    SyAssertf(pPlayer->GetType() == OBJ_PLAYER, "Non player object in player list???");
        
    distSqr = pPlayer->GetLocation().DistanceSquared(GetLocation());

    if (GetTitan()->GetPeeringNetwork()->GetObjectPeerId(pPlayer->GetID()) == myPeerId)
    {
      owner = pPlayer;
      if(distSqr > LEAVE_OWNERSHIP)
      {
        bOutsideOwnerRadius = true;
      }
      else
      {
        break;
      }
    }

    if (distSqr <= ENTER_OWNERSHIP &&
        (NULL==closestPlayer || distSqr < closestDistSqr))
    {
      closestPlayer = pPlayer;
      closestDistSqr = distSqr;
    }
    pPlayer = pRegistry->NextType(pPlayer);
  }

  SyAssert(owner != NULL);

  if (bOutsideOwnerRadius && 
      owner != closestPlayer &&
      NULL != closestPlayer)
  {
    int newPeerId = GetTitan()->GetPeeringNetwork()->GetObjectPeerId(closestPlayer->GetID());
    pRegistry->NetworkRequestOwnership(GetID(), newPeerId);
  }
}

void
cGameObject::UpdateScope()
{
  SyVect3 camera_loc = GetTitan()->GetCameraController()->GetCamera()->GetLocation();

  float distanceSquared = camera_loc.DistanceSquared(GetLocation());
  if (mbInScope)
  {
    if (distanceSquared > SIMULATION_DISTANCE*SIMULATION_DISTANCE)
    {
      mbInScope = false;
      ExitScope();
    }
  }
  else
  {
    if (distanceSquared < (SIMULATION_DISTANCE-SIMULATION_BORDER)*(SIMULATION_DISTANCE-SIMULATION_BORDER))
    {
      mbInScope = true;
      EnterScope();
    }
  }
}

void
cGameObject::SetCarriedBy(tGameObjectID id)
{
  mCarriedByID = id;

  if (ID_NONE == mCarriedByID)
  {
    SyVect3 loc = GetLocation();
    SyVect3 hpr = GetHPR();
    if ( GetPhysics()->SetKeyframed( loc, hpr ) )
    {
      SetLocation( loc );
      SetHPR( hpr );
    }
    GetPhysics()->SetCollideable(true);
  }
  else
  {
    GetPhysics()->SetCollideable(false);

    SyVect3 loc = GetLocation();
    SyVect3 hpr = GetHPR();
    if ( GetPhysics()->SetKeyframed( loc, hpr ) )
    {
      SetLocation( loc );
      SetHPR( hpr );
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////////////// Global Funcs

void 
RegPropClasses_GameObj()
{
  cGameObject::InitPropClass();
  RegPropClasses_Intel();
  RegPropClasses_Stats();
  RegPropClasses_Inventory();
  RegPropClasses_Graphic();
  RegPropClasses_Physics();
  RegPropClasses_Script();
}
// EOF
