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

//-------------------------------------------------------- Includes
#include "debris.h"
#include "SyCollSphere.h"
#include "SyScene.h"
#include "Titan.h"
#include "database.h"
#include "debugoverlay.h"
#include "tuning.h"
#include "gameobj.h"
#include "graphic.h"
#include "registry.h"
#include "SyCSprite.h"
#include "SyHavok.h"
//---------------------------------------------- Class Declarations
//----------------------------------------- Functions Declarations

float AngleNormalize(float start);

static float l_DEBRIS_GRAVITY = -9.8f; // meters/sec
static float l_DEBRIS_LEVITATION_GRAVITY = 1.0f; // meters/sec
static float l_DEBRIS_ROTATION_SCALE_FACTOR = 0.5f;
static float l_DEBRIS_BOUNCE_FACTOR = 1.5f; // 1.0 = inelastic, 2.0 = perfectly elastic
static float l_DEBRIS_FRICTION= 0.2f; 
static float l_DEBRIS_XZ_VELOCITY = 3.0f; 
static float l_DEBRIS_Y_VELOCITY_MIN = 6.0f; 
static float l_DEBRIS_Y_VELOCITY_MAX = 12.0f; 
static float l_DEBRIS_AGE = 10.0f; // seconds
static float l_DEBRIS_FADE_TIME = 0.5f; // seconds
static float l_DEBRIS_EXPLOSION_IMPACT_FORCE = 5.0f;



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

//------------------------------------------------------------- cDebrisShard
cDebrisShard::cDebrisShard(Titan *titan) :
  mpTitan(titan),
  mActorHandle(NULL),
  mAttachedSpriteHandle(-1),
  mAttachedSpriteTimer(0.0f),
#ifdef HAVOK_ENABLED
  mRigidBody(0),
#endif
  mLastExplosionAge(0.0f),
  mLevitationID(0),
  mbUseGoalLoc(false),
  mGoalLoc(0.0f, 0.0f, 0.0f)
{
};

cDebrisShard::~cDebrisShard()
{
  SyScene *scene = mpTitan->GetScene();

  if (mActorHandle != NULL)
  {
    // SGC wait for clear
    scene->ReleaseActor(mActorHandle);

    mActorHandle = NULL;
  }

  if (-1 != mAttachedSpriteHandle)
  {
    mAttachedSpriteHandle = -1;
  }

#ifdef HAVOK_ENABLED
  if (mRigidBody)
  {
    SyScene *scene = mpTitan->GetScene();
    scene->GetCollideDev()->GetHavok()->RemoveRigidBody( mRigidBody );
    mRigidBody = 0;
  }
#endif
};
  
void 
cDebrisShard::Init(const SyVect3 &loc, const SyVect3& start_vel,const cDebrisShardMaster &master)
{
  mVelocity = start_vel;

  // todo: rotation
  SyVect3 ActorHPR;

  SyScene *scene = mpTitan->GetScene();

  mHeading = SY_ATAN2(start_vel.X,start_vel.Z);

  mRotSpeed = start_vel.Magnitude() * l_DEBRIS_ROTATION_SCALE_FACTOR;
  
  mRotAngle = SY_DEG_TO_RAD(0.0f);

  ActorHPR(mHeading,mRotAngle,0.0f);
  mActorHandle   = scene->CreateActor( loc, ActorHPR, master.mScale, master.mSpriteTemplateHandle );
  scene->SetActorCollideable(mActorHandle, false);
#ifdef HAVOK_ENABLED
  SyHavok * pHavok = scene->GetCollideDev()->GetHavok();
  pHavok->AddRigidBody( SyHavok::DEBRIS, 
                        scene->Sprite(scene->GetActorSprite( mActorHandle )), 
                        loc, 
                        ActorHPR, 
                        mRotSpeed, 
                        start_vel, 
                        &mRigidBody, 
                        0 );
#endif
  mLoc = loc;
  mbIsMoving = true;
  mRadius = master.mRadius;
  mHeight = master.mHeight;
  mBounces = 0;
  SetGravity( l_DEBRIS_GRAVITY );
}

void cDebrisShard::SetAlpha(float alpha)
{
  if (SyActorNull != mActorHandle)
  {
    mpTitan->GetScene()->SetActorSpriteAlphaBlend(mActorHandle, 1, alpha);
  }
}


void 
cDebrisShard::Update(float time)
{
  SyScene *scene = mpTitan->GetScene();

#ifdef HAVOK_ENABLED
  if (mbUseGoalLoc)
  {
    SyVect3 velocity = mVelocity;
    SyVect3 displacement;

    displacement.MulAdd(velocity,time);

    float accel = mGravityValue * time;    
    mVelocity.Y += accel;
    mLoc += displacement;

    scene->SetActorLocation(mActorHandle,mLoc);

    float old_rot = mRotAngle;
    mRotAngle += mRotSpeed * time;
    if (mBounces > 1 && ((mRotAngle < 0)!= (old_rot< 0)))
    {
      mRotAngle = 0;
      mRotSpeed = 0;
    }
    mRotAngle = AngleNormalize(mRotAngle);

    SyVect3 hpr;
    hpr(mHeading,mRotAngle,0);
    scene->SetActorHPR(mActorHandle,hpr);

    DEBUGOVERLAY_DRAWLINE(0, "Spell", mGoalLoc, mLoc, cDebugOverlay::YELLOW);

    // near or past goal location
    if (((mGoalLoc-mLoc) ^ mVelocity) < 0.25f)
    {
      mbIsMoving = false;
    }
  }
  else
  {
    SyVect3 lastLoc = mLoc;
    SyHavok *havok = scene->GetCollideDev()->GetHavok();
    havok->GetPosition( mRigidBody, mLoc );
    scene->SetActorLocation(mActorHandle,mLoc);

    mbIsMoving = lastLoc != mLoc ? true:false;
    mVelocity.Sub( mLoc, lastLoc );
    
    SyMatrix44 m;
    havok->GetMatrix( mRigidBody, m );
    SyVect3 hpr,scale,loc;
    m.ConvertTo( hpr, scale, loc );
    scene->SetActorTransform(mActorHandle,hpr,1.0f,loc);
  }

#else  //HAVOK_ENABLED not defined

  if (mbIsMoving)
  {
    SyVect3 velocity = mVelocity;
    SyVect3 displacement;

    displacement.MulAdd(velocity,time);

    float accel = mGravityValue * time;    
    mVelocity.Y += accel;

    if (mbUseGoalLoc)
    {
      mLoc += displacement;
    }
    else
    {
      _CollisionTest(displacement);
    }

    scene->SetActorLocation(mActorHandle,mLoc);

    float mag = mVelocity.Magnitude();
    if (mag < 1.0f) mag = 1.0f;
    SyWaterSystem * pWaterSystem = scene->GetWaterSystem();
    if (pWaterSystem)
    {
      pWaterSystem->DemoPlunk( mLoc, 0.07f * mag );
    }

    float old_rot = mRotAngle;
    mRotAngle += mRotSpeed * time;
    if (mBounces > 1 && ((mRotAngle < 0)!= (old_rot< 0)))
    {
      mRotAngle = 0;
      mRotSpeed = 0;
    }
    mRotAngle = AngleNormalize(mRotAngle);

    SyVect3 hpr;
    hpr(mHeading,mRotAngle,0);
    scene->SetActorHPR(mActorHandle,hpr);

    if (mbUseGoalLoc)
    {
      DEBUGOVERLAY_DRAWLINE(0, "Spell", mGoalLoc, mLoc, cDebugOverlay::YELLOW);

      // near or past goal location
      if (((mGoalLoc-mLoc) ^ mVelocity) < 0.25f)
      {
        mbIsMoving = false;
      }
    }
  }
#endif //HAVOK_ENABLED

  SyWaterSystem * pWaterSystem = scene->GetWaterSystem();
  if (pWaterSystem)
  {
    float mag = mVelocity.Magnitude();
    if (mag < 1.0f)
    {
      mag = 1.0f;
    }

    if ( pWaterSystem->DemoPlunk( mLoc, 0.07f * mag ) )
    {
      /* float? */
    }
  }

  if (-1 != mAttachedSpriteHandle)
  {
    mAttachedSpriteTimer += time;

    static const float DEBRIS_ATTACH_EFFECT_LIFETIME = 10.0f;
    if (mAttachedSpriteTimer > DEBRIS_ATTACH_EFFECT_LIFETIME)
    {
      int actorSpriteHandle = scene->GetActorSprite(mActorHandle);

      if (-1 != actorSpriteHandle)
      {
        SySprite* pActorSprite = scene->Sprite(actorSpriteHandle);

        if (pActorSprite && pActorSprite->GetType() == SYSPRITETYPE_GROUPSPRITE)
        {
          int subSpriteHandle;
          int index = 0;

          while (index < static_cast<SyGroupSprite*>(pActorSprite)->GetNumMembers())
          {
            subSpriteHandle = static_cast<SyGroupSprite*>(pActorSprite)->GetMemberSprite(index);

            if (-1 != subSpriteHandle && subSpriteHandle == mAttachedSpriteHandle)
            {
              static_cast<SyGroupSprite*>(pActorSprite)->RemoveMember(index, *scene);
              break;
            }
            else
            {
              ++index;
            }
          }
        }
      }

      mAttachedSpriteHandle = -1;
    }
  }
}

void 
cDebrisShard::_CollisionTest(const SyVect3 &displacement)
{
  SyVect3       Start, End;
  SyCollSphere  CollSphere;

  SyScene *scene = mpTitan->GetScene();

  SySceneFilter Filter;
  Filter.Init(0);

  Start = mLoc;
  Start.Y += mHeight;
  End = Start +  displacement;

  
  CollSphere.Init( Start, End,mRadius);
  // Collide with the world 

  if (scene->Collide( CollSphere, Filter ))
  {
    // dot product
    SyVect3 normal = CollSphere.GetHitNormal();
    float dot = mVelocity ^ normal; 
    SyVect3 restitution;
    restitution.Mul(normal,(l_DEBRIS_BOUNCE_FACTOR * dot));
    mVelocity -= restitution; 

    // finish out motion?

    End = CollSphere.GetHitPoint();
    float MIN_VELOCITY = 0.3f;
    float MIN_FLATNESS = 0.6f;
    
    if (normal.Y > MIN_FLATNESS)
    {
      float XZSpeed = mVelocity.X * mVelocity.X + mVelocity.Z * mVelocity.Z;
      if (XZSpeed < MIN_VELOCITY)
      {
        mRotSpeed = 0;
        mRotAngle = 0;
        mbIsMoving = false;
      }

      mBounces++;
      if (mBounces == 1)
      {
        mRotSpeed = -mRotSpeed;
      }
      else if (mBounces ==2)
      {
        if ((mRotAngle > 0 && mRotSpeed > 0) ||
            (mRotAngle < 0 && mRotSpeed < 0))
        {
          mRotSpeed = -mRotSpeed;
        }
      }
      else
      {
         mRotSpeed = 0;
         mRotAngle = 0;
      }
    }

    // friction
    mVelocity *= (1.0f - l_DEBRIS_FRICTION);
    End.Y -= mHeight;
    mLoc = End;

    return;
    
  }

  // Retrieve the result of the collision/skitters 

  End = CollSphere.GetHitPoint();
  End.Y -= mHeight;
  mLoc = End;
  return;
}


void 
cDebrisShard::Explosion(const SyVect3 &loc, float radius, float age, tGameID attachFXID)
{
  if (age < mLastExplosionAge)
  {
    return;
  };

  if (loc.Distance(mLoc) > radius)
  {
    return;
  }

  mLastExplosionAge = age;
  SyVect3 away;
  away.Sub(mLoc,loc);
  away.Normalize();
  
  away *= l_DEBRIS_EXPLOSION_IMPACT_FORCE;
  mVelocity += away;
  mbIsMoving = true;

  SyScene* pScene = mpTitan->GetScene();

#ifdef HAVOK_ENABLED
  if (mRigidBody)
  {
    SyHavok *pHavok = pScene->GetCollideDev()->GetHavok();
    pHavok->LaunchRigidBody(mRigidBody, mLoc, mRotSpeed, away, SyVect3(0.0f, 0.0f, 0.0f));
  }
#endif

  // attach effect
  if (ID_NONE != attachFXID &&
      SyActorNull != mActorHandle &&
      -1 == mAttachedSpriteHandle)
  {
    mAttachedSpriteTimer = 0.0f;

    int32 actorSpriteHandle = pScene->GetActorSprite(mActorHandle);
    int32 emitterMeshSpriteHandle = actorSpriteHandle;

    if (-1 != actorSpriteHandle)
    {
      SySprite* pActorSprite = pScene->Sprite(actorSpriteHandle);

      if (pActorSprite && pActorSprite->GetType() != SYSPRITETYPE_GROUPSPRITE)
      {
        int32 newActorSpriteHandle = pScene->CreateSprite(SYSPRITETYPE_GROUPSPRITE, 0);

        if (-1 != newActorSpriteHandle)
        {
          SyGroupSprite* pNewActorSprite = static_cast<SyGroupSprite*>(pScene->Sprite(newActorSpriteHandle));
          SyAssert(pNewActorSprite!=NULL);
          if (pNewActorSprite)
          {
            pNewActorSprite->AddMember(actorSpriteHandle, *pScene);
            if (pScene->SetActorSprite(mActorHandle, newActorSpriteHandle) != -1)
            {
              pActorSprite = pNewActorSprite;
            }
            else
            {
              pScene->ReleaseSprite(newActorSpriteHandle);
            }
          }
        }
      }
      else
      {
        emitterMeshSpriteHandle = -1;

        if (pActorSprite && pActorSprite->GetType() == SYSPRITETYPE_GROUPSPRITE)
        {
          int numMembers = static_cast<SyGroupSprite*>(pActorSprite)->GetNumMembers();
          for (int i=0; i<numMembers; ++i)
          {
            int subSpriteHandle = static_cast<SyGroupSprite*>(pActorSprite)->GetMemberSprite(i);

            if (-1 != subSpriteHandle)
            {
              SySprite* pSubSprite = pScene->Sprite(subSpriteHandle);
              if (pSubSprite && pSubSprite->GetType() == SYSPRITETYPE_SIMPLE)
              {
                emitterMeshSpriteHandle = subSpriteHandle;
                break;
              }
            }
          }
        }
      }

      if (pActorSprite && pActorSprite->GetType() == SYSPRITETYPE_GROUPSPRITE)
      {
        int fxResourceIndex;
        SyResourceType resType;
        if (pScene->GetDictionary()->Find(attachFXID, resType, fxResourceIndex))
        {  
          mAttachedSpriteHandle = pScene->CopySprite(fxResourceIndex, 0);

          if (-1 != mAttachedSpriteHandle)
          {
            if (-1 != emitterMeshSpriteHandle)
            {
              cGraphic::SetMeshEmitter(mAttachedSpriteHandle, emitterMeshSpriteHandle, *pScene);
            }

            static_cast<SyGroupSprite*>(pActorSprite)->AddMember(mAttachedSpriteHandle, *pScene);
          }
          else
          {
            SyAssertf(false, "Could not copy effect sprite to attach to debris");
          }
        }
        else
        {
          SyAssertf(false, "Could not find particle effect resource to attach to item on actor");    
        }
      }
    }
  }
}

void 
cDebrisShard::StartLevitation(const SyVect3 &loc, float radius,int id)
{
  if (mLevitationID == id) return; // already affected
  
  if (loc.Distance(mLoc) > radius)
  {
    return;
  }

  mLevitationID = id;
  SetGravity( l_DEBRIS_LEVITATION_GRAVITY );
  mbIsMoving = true;
}

void 
cDebrisShard::EndLevitation(int id)
{
  if (mLevitationID != id)
  {
    return;
  }

  SetGravity( l_DEBRIS_GRAVITY );
  mLevitationID = 0;
}

void cDebrisShard::SetGravity( float g )
{
  mGravityValue = g;
#ifdef HAVOK_ENABLED
  mpTitan->GetScene()->GetCollideDev()->GetHavok()->SetGravity( mRigidBody, mGravityValue );
#endif
}

void
cDebrisShard::SetGoalLocation(const SyVect3& goalLoc, float duration)
{
  SyAssert(duration>0.0f);

  mbUseGoalLoc = true;
  mGoalLoc = goalLoc;
  SetGravity( 0.0f );
  mbIsMoving = true;

  mVelocity = mGoalLoc - mLoc;
  float dist = mVelocity.NormalizeMagn();

  if (duration>0.0f)
  {
    mVelocity *= (dist/duration);
  }

#ifdef HAVOK_ENABLED
  mpTitan->GetScene()->GetCollideDev()->GetHavok()->SetPropCollideable( mRigidBody, false );
#endif
}

//----------------------------------------------------------- cDebris

cDebris::cDebris(Titan *pTitan) :
  mpTitan(pTitan),
  mAge(0.0f),
  mSourceID(ID_NONE)
{
}

cDebris::~cDebris()
{
  for (int ii=mShards.Begin();ii!=mShards.End();ii=mShards.Next(ii))
  {
    delete mShards(ii);
  }
  mShards.Clear();
}

void
cDebris::_NewShard(cDebrisShardMaster  *master, const SyVect3 &loc, const SyVect3 &force, float RandomMag )
{
  // randomize start vel...
  static const float l_DEBRIS_XZ_VELOCITY = 2.5f;
  static const float l_DEBRIS_Y_VELOCITY_MIN = 2.5f;
  static const float l_DEBRIS_Y_VELOCITY_MAX = 2.5f;

  SyVect3 start_vel = force;
  start_vel.X += RandomMag * ((mpTitan->RandomFloat() * l_DEBRIS_XZ_VELOCITY * 2) - l_DEBRIS_XZ_VELOCITY);
  start_vel.Y += RandomMag * ((mpTitan->RandomFloat() * (l_DEBRIS_Y_VELOCITY_MAX - l_DEBRIS_Y_VELOCITY_MIN)) + 
                 l_DEBRIS_Y_VELOCITY_MIN);
  start_vel.Z += RandomMag * ((mpTitan->RandomFloat() * l_DEBRIS_XZ_VELOCITY * 2) - l_DEBRIS_XZ_VELOCITY);

  cDebrisShard *newshard = new cDebrisShard(mpTitan);
  newshard->Init(loc,start_vel,*master);
  mShards.Add(newshard);
}



void 
cDebris::Init(cGameObject *obj, const SyVect3& force,const cDebrisMaster &master,float RandomMag)
{
  SyAssert(obj!=NULL);

  mSourceID = obj->GetID();

  for (int ii=master.mShardMasters.Begin();ii!=master.mShardMasters.End();ii=master.mShardMasters.Next(ii))
  {
    cDebrisShardMaster *shard_master = master.mShardMasters(ii);

    if (-1 == shard_master->mSpriteTemplateHandle)
    {
      continue;
    }

    for (int shard_index = 0;shard_index<shard_master->mQuantity;++shard_index)
    {
      SyVect3 start_loc = obj->GetLocation();

      bool randomize = false;

      if (shard_master->mNodeID ==0)
      {
        randomize = true;
      }
      else
      {
        // get actor
        SyActorHandle handle = obj->GetGraphic()->GetActorHandle();

        SyScene *scene = mpTitan->GetScene();
        SySprite *sprite = scene->GetActorSpritePtr(handle);

        if (sprite== NULL || sprite->GetType() != SYSPRITETYPE_CSPRITE)
        {
          randomize =true;
        }
        else
        {
          SyCSprite *csprite = static_cast<SyCSprite *>(sprite);

          SyMatrix44 ansMatrix;
          SyMatrix44 parentMatrix;

          scene->GetActorTransform(handle,parentMatrix);
          csprite->CalcNodeTransformFromRefID(shard_master->mNodeID,
                                              parentMatrix,
                                              *scene,
                                              ansMatrix);

          ansMatrix.GetTranslation(start_loc);
        }
      }

      if (randomize)
      {
        start_loc.X += (mpTitan->RandomFloat() * 2.0f * master.mRadius) - master.mRadius;
        start_loc.Y += (mpTitan->RandomFloat() * 2.0f * master.mRadius);
        start_loc.Z += (mpTitan->RandomFloat() * 2.0f * master.mRadius) - master.mRadius;
      }

      _NewShard(shard_master,start_loc,force,RandomMag);
    }
  }
}

void 
cDebris::Update(float time)
{
  bool bUseAlpha = mAge >= l_DEBRIS_AGE;
  float alpha = 0.0f;

  if (bUseAlpha)
  {
    alpha = 1.0f - ((mAge - l_DEBRIS_AGE)/l_DEBRIS_FADE_TIME);
  }

  for (int ii=mShards.Begin();ii!=mShards.End();ii=mShards.Next(ii))
  {
    mShards(ii)->Update(time);

    if (bUseAlpha)
    {
      mShards(ii)->SetAlpha(alpha);
    }
  }

  mAge += time;

  for (int ii=mShards.Begin();ii!=mShards.End();)
  {
    if (mShards(ii)->IsUsingGoalLocation() && !mShards(ii)->IsMoving())
    {
      delete mShards(ii);
      ii=mShards.ReplaceLast(ii);
    }
    else
    {
      ii=mShards.Next(ii);
    }
  }
}

void    
cDebris::Explosion(const SyVect3 &loc, float radius, float explosionAge, tGameID attachFXID)
{
  for (int ii=mShards.Begin();ii!=mShards.End();ii=mShards.Next(ii))
  {
    mShards(ii)->Explosion(loc,radius,explosionAge, attachFXID);
  }
}

void    
cDebris::StartLevitation(const SyVect3 &loc, float radius, int id)
{
  for (int ii=mShards.Begin();ii!=mShards.End();ii=mShards.Next(ii))
  {
    mShards(ii)->StartLevitation(loc,radius,id);
  }
}

void    
cDebris::EndLevitation(int id)
{
  for (int ii=mShards.Begin();ii!=mShards.End();ii=mShards.Next(ii))
  {
    mShards(ii)->EndLevitation(id);
  }
}

void 
cDebris::SetGoalLocation(const SyVect3& goalLoc, float duration)
{
  mAge = 0.0f;
  for (int ii=mShards.Begin();ii!=mShards.End();ii=mShards.Next(ii))
  {
    mShards(ii)->SetGoalLocation(goalLoc, duration);
  }
}

bool 
cDebris::IsDead()
{
  return mAge > (l_DEBRIS_AGE+l_DEBRIS_FADE_TIME);
}

//----------------------------------------------------------- cDebrisShardMaster


cDebrisShardMaster::cDebrisShardMaster() :
  mModelName(NULL),
  mSpriteTemplateHandle(NULL),
  mQuantity(1),
  mRadius(0.25f),
  mScale(1.0f),
  mHeight(0.25f),
  mNodeID(0)

{
}

cDebrisShardMaster::  ~cDebrisShardMaster()
{
  delete mModelName;
}



//----------------------------------------------------------- cDebrisMaster

cDebrisMaster::cDebrisMaster() 
{
}

cDebrisMaster::~cDebrisMaster()
{
  for (int ii=mShardMasters.Begin();ii!=mShardMasters.End();ii=mShardMasters.Next(ii))
  {
    delete mShardMasters(ii);
  }
  mShardMasters.Clear();
};

//----------------------------------------------------------- cDebrisSys


cDebrisSys::cDebrisSys(Titan *pTitan) :
  mpTitan(pTitan),
  mLevitationID(1)
{
}

cDebrisSys::~cDebrisSys()
{
  Clear();

}

void    
cDebrisSys::Init()
{
 
}

void    
cDebrisSys::Clear()
{
  for (int ii=mDebris.Begin();ii!=mDebris.End();ii=mDebris.Next(ii))
  {
    delete mDebris(ii);
  }
  mDebris.Clear();
}

void    
cDebrisSys::Update(float time)
{
  for (int ii=mDebris.Begin();ii!=mDebris.End();ii=mDebris.Next(ii))
  {
    mDebris(ii)->Update(time);
  }
  int curIndex;
  for (curIndex = mDebris.Begin();curIndex != mDebris.End(); )
  {
    cDebris *debris = mDebris(curIndex);
    if (debris->IsDead())
    {
      delete debris;
      curIndex = mDebris.ReplaceLast(curIndex);
    }
    else 
    {
      curIndex = mDebris.Next(curIndex);
    }
  }
}

void    
cDebrisSys::Spawn(cGameObject *obj, const SyVect3 &force,tGameID debrisID,float RandomMag)
{
  static const int MAX_DEBRIS_GROUPS = 32;

  if (mDebris.Size() > MAX_DEBRIS_GROUPS)
  {
    return;
  }

  const cDebrisMaster *master = mpTitan->GetDatabaseSys()->GetDebrisMaster(debrisID);

  if (master == NULL)
  {
    SyAssertf(0,"Unknown debris type");
    return;
  }

  cDebris *newdebris = new cDebris(mpTitan);

  newdebris->Init(obj,force,*master,RandomMag);
  mDebris.Add(newdebris);
}

bool    
cDebrisSys::Exists(tGameID debrisID)
{
  return  (mpTitan->GetDatabaseSys()->GetDebrisMaster(debrisID) != NULL);
}

void    
cDebrisSys::Explosion(const SyVect3 &loc, float radius, float explosionAge, tGameID attachFXID)
{
  for (int curIndex = mDebris.Begin();curIndex != mDebris.End();curIndex = mDebris.Next(curIndex) )
  {
    mDebris(curIndex)->Explosion(loc,radius,explosionAge, attachFXID);
  }
}

void    
cDebrisSys::StartLevitation(const SyVect3 &loc, float radius, int id)
{
  for (int ii=mDebris.Begin();ii!=mDebris.End();ii=mDebris.Next(ii))
  {
    mDebris(ii)->StartLevitation(loc,radius,id);
  }
}

void    
cDebrisSys::EndLevitation(int id)
{
  for (int ii=mDebris.Begin();ii!=mDebris.End();ii=mDebris.Next(ii))
  {
    mDebris(ii)->EndLevitation(id);
  }
}

void
cDebrisSys::ReForm(tGameObjectID objID, float duration)
{
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(objID);

  if (!pObj)
  {
    return;
  }

  SyVect3 goalLoc(pObj->GetLocation());
  goalLoc.Y += 1.0f;

  for (int ii=mDebris.Begin();ii!=mDebris.End();ii=mDebris.Next(ii))
  {
    if (mDebris(ii)->GetSourceID() == objID)
    {
      mDebris(ii)->SetGoalLocation(goalLoc, duration);
    }
  }
}

void 
Debris_RegisterTuningVariables()
{
  gTuningSys.AddFloat(&l_DEBRIS_GRAVITY,"Debris_Gravity");
  gTuningSys.AddFloat(&l_DEBRIS_ROTATION_SCALE_FACTOR,"Debris_Rotation");
  gTuningSys.AddFloat(&l_DEBRIS_BOUNCE_FACTOR,"Debris_BounceFactor");
  gTuningSys.AddFloat(&l_DEBRIS_FRICTION,"Debris_Friction");
  gTuningSys.AddFloat(&l_DEBRIS_XZ_VELOCITY,"Debris_XZ_Velocity");
  gTuningSys.AddFloat(&l_DEBRIS_Y_VELOCITY_MIN,"Debris_Y_Velocity_Min");
  gTuningSys.AddFloat(&l_DEBRIS_Y_VELOCITY_MAX,"Debris_Y_Velocity_Max");
  gTuningSys.AddFloat(&l_DEBRIS_AGE,"Debris_Y_Age");
  gTuningSys.AddFloat(&l_DEBRIS_EXPLOSION_IMPACT_FORCE,"Debris_Explosion_Impact_Force");

}

// EOF
