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

//-------------------------------------------------------- Includes
#include "graphic.h"
#include "gameobj.h" 
#include "registry.h"
#include "stats.h"
#include "titan.h"
#include "syscene.h"
#include "SyEsfParse.h"
#include "SyParticleSprite2.h"

#include "animdefs.h"
#include "physics.h"

#include "inventory.h"
#include "debugoverlay.h"
#include "cameracontroller.h"
#include "SyCamera.h"
#include "TitanI.h"
#include "levelobj.h"
#include "intel.h"
#include "gameerror.h"
#include "SyHavok.h"


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

//---------------------------------------------- cGraphic

cGraphic::cGraphic()
{
  InitPropObject( mCLASSID );
}

void 
cGraphic::SetOwner(cGameObject *owner)
{
  mOwner = owner;
}


int           
cGraphic::InitPropClass()
{

/* Add the class */

  AddClass( mCLASSID, 
            "cGraphic", 
            Creator, 
            mCLASSID, 
            0 ); 
  return 0;
}

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

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

  return(pObject);
}

void cGraphic::SetVisible(bool bVisible)
{
  SyActorHandle handle = GetActorHandle();
  
  if (handle != SyActorNull)
  {
    SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();

    scene->SetActorVisibility(handle, bVisible);
  }
}

bool cGraphic::GetVisible()
{
  SyActorHandle handle = GetActorHandle();

  if (handle != SyActorNull)
  {
    SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();

    return scene->IsActorVisible(handle) != 0;
  }

  return false;
}

//---------------------------------------------- cGraphicActor
cGraphicActor::cGraphicActor()   : 
  mActorHandle(NULL)
{
  for (int index =0; index < MAX_EFFECTS; ++index)
  {
    mBodyAttachFXIndex[index] = -1;
    mAttachFXID[index] = ID_NONE;
  }

  InitPropObject( mCLASSID );
}

cGraphicActor::~cGraphicActor()
{
  Release();
};

void
cGraphicActor::SceneClearAndInit()
{
  mActorHandle = NULL;
}

void 
cGraphicActor::Release()
{
  if (mActorHandle != NULL)
  {
    SyScene *scene = mOwner->GetTitan()->GetScene();

    for (int index = 0; index < MAX_EFFECTS; ++index)
    {
      RemoveEffect(index);
    }

    scene->ReleaseActor(mActorHandle);

    mActorHandle = NULL;
  }
}
int           
cGraphicActor::InitPropClass()
{

/* Add the class */

  AddSubClass( mCLASSID, 
               cGraphic::mCLASSID,
               mCLASSID,
               "cGraphicActor", 
               Creator, 
               mCLASSID, 
               0 ); 
  return 0;
}

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

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

  return(pObject);
}

void
cGraphicActor::Reload()
{
  mActorHandle = NULL;
  Init();
}

void  
cGraphicActor::Init()
{
  // load up model...note that resource should already be loaded 
  // if we're loading from an esf file, and this should just hook
  // 'em up.

  if (mActorHandle != NULL)
  {

    return; // already init
  }

  SetActor(mOwner->GetStats()->GetModelName(), mOwner->GetStats()->GetRagdollName(), mOwner->GetStats()->GetFileName());
}

int cGraphicActor::RagdollOff(int immediate)
{
#ifdef HAVOK_ENABLED
  SySprite* pSprite = mOwner->GetTitan()->GetScene()->GetActorSpritePtr( GetActorHandle() );
  if (pSprite && pSprite->GetHavokRagdollInstance())
  {
    return SyHavok::turnOnKeyframeMode(pSprite->GetHavokRagdollInstance(), immediate);
  }
#endif
  return 1;
}

void cGraphicActor::SetStartRagdollVector( const SyVect3 & v )
{
#ifdef HAVOK_ENABLED
  SySprite* pSprite = mOwner->GetTitan()->GetScene()->GetActorSpritePtr( GetActorHandle() );
  if (pSprite && pSprite->GetHavokRagdollInstance())
  {
    SyHavok::SetStartRagdollVector(pSprite->GetHavokRagdollInstance(), v);
  }
#endif
}

int cGraphicActor::RagdollOn()
{
#ifdef HAVOK_ENABLED
  SySprite* pSprite = mOwner->GetTitan()->GetScene()->GetActorSpritePtr( GetActorHandle() );
  if (pSprite && pSprite->GetHavokRagdollInstance())
  {
    SyHavok::turnOffKeyframeMode(pSprite->GetHavokRagdollInstance());
    return 1;
  }
#endif
  return 0;
}

#ifdef HAVOK_ENABLED
int ControlRagdollLocomotionCallback( void * pParam, int iEnableRagdoll )
{  
  cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch((tGameObjectID)pParam);
  if (pObj)
  {
    if ( iEnableRagdoll )
    {
      pObj->GetGraphic()->RagdollOn();
      ((cPhysicsAnimated*)(pObj->GetPhysics()))->Ragdoll(SyVect3(0,0,0));
    }
    else
    {
      pObj->GetPhysics()->Reset();
      pObj->GetPhysics()->SetVelocity( SyVect3(0.0f,-0.01f,0.0f) );
    }
  }
  return 0;
}
#endif


void cGraphicActor::SetActor(const char* modelName, const char* ragdollName, const char* fullPath)
{
  Release();

  SyAssert(mActorHandle == NULL);

  if (fullPath == NULL)
  {
    //SyAssert(0);
    return;
  }

  SyScene *scene = mOwner->GetTitan()->GetScene();
  int32 SpriteTemplateHandle = -1;
  SyResourceType resType;
  int32 resHandle;

  if (scene->GetDictionary()->Find(SyHashResourceID(modelName), resType, resHandle))
  {
    GAME_ASSERT(ERROR_ART, SYRESOURCETYPE_CSPRITE==resType || SYRESOURCETYPE_HSPRITE==resType || SYRESOURCETYPE_SIMPLESPRITE==resType || SYRESOURCETYPE_GROUPSPRITE==resType, "Invalid GameObject actor sprite type: %d modelName '%s'", resType, modelName);
    SpriteTemplateHandle = resHandle;
  }
  else
  {
    SyESFParse& Parser = mOwner->GetTitan()->GetTitanUI()->GetESFParser();
    SpriteTemplateHandle = mOwner->GetTitan()->LoadAssetSprite (Parser, fullPath);
  }

  if (SpriteTemplateHandle == -1)
  {
    /*
     * No need to assert here, it was done for us in LoadAssetSprite
     */
    return;
  }


  const SyVect3 &ActorLoc = mOwner->GetLocation();
  const SyVect3 &ActorHPR = mOwner->GetHPR();

  float32 ActorScale = mOwner->GetStats()->GetModelScale();

  mActorHandle = scene->CreateActor( ActorLoc, ActorHPR, ActorScale, SpriteTemplateHandle);

#ifdef HAVOK_ENABLED
  //-------------------------------------------------------------------------------------------
  //
  //  ragdoll setup for the sprite
  //
  //      (1) create a ragdoll instance
  //      (2) setup the locomotion callback
  //
  //-------------------------------------------------------------------------------------------
  SySprite * pSprite = scene->GetActorSpritePtr( mActorHandle );
  SyHavokRagdollInstance * RagdollInstance = 0;
  bool bRightKindOfSprite = pSprite->GetType() == SYSPRITETYPE_HSPRITE || pSprite->GetType() == SYSPRITETYPE_CSPRITE;
  if (ragdollName && ragdollName[0]!=0)
  {
    if (!bRightKindOfSprite)
    {
      GAME_ASSERT(ERROR_ART, 0, "Ragdoll(%s) set for modelName(%s) but model is not a CSprite or HSprite.", ragdollName, modelName); 
    }
    else
    {
      RagdollInstance = scene->GetCollideDev()->GetHavok()->CreateRagdollInstance( ragdollName );
    }
  }

  if (bRightKindOfSprite)
  {
    pSprite->SetHavokRagdollInstance( RagdollInstance );
    if (RagdollInstance)
    {
      SyHavok::SetOwnerControlRagdollLocomotionCallback( RagdollInstance, (void*)mOwner->GetID(), ControlRagdollLocomotionCallback );
    }
  }
#endif
}


void  
cGraphicActor::Prerender()
{
  // update location of graphic...
  if (mActorHandle == NULL)
  {
    return;
  }
  SyScene *scene = mOwner->GetTitan()->GetScene();

  if (mOwner->IsRemote())
  {
    // if object is being carried by someone, no need to use dead reckoning pos
    if (mOwner->GetCarriedBy() != ID_NONE)
    {
      scene->SetActorLocation(mActorHandle, mOwner->GetLocation());
      scene->SetActorHPR(mActorHandle,mOwner->GetHPR());
    }
    else
    {
      // todo:  set actor dirty flag correctly for remote objects
      scene->SetActorLocation(mActorHandle,mOwner->GetPhysics()->GetRenderLocation());
      SyVect3 hpr = mOwner->GetPhysics()->GetRenderHPR();
      scene->SetActorHPR(mActorHandle, hpr);
    }
  }
  else
  {      
    if (mOwner->IsActorDirty())
    {
      scene->SetActorLocation(mActorHandle,mOwner->GetLocation());
      scene->SetActorHPR(mActorHandle,mOwner->GetHPR());
      mOwner->ClearActorDirty();
    }
  }

}

/*
void          
cGraphicActor::SetAlpha(float alpha)
{
  if (mActorHandle == NULL)
  {
    return;
  }
  static const float ALPHA_THRESHOLD = 0.99f;
  SyScene *scene = mOwner->GetTitan()->GetScene();
  scene->SetActorSpriteAlphaBlend( mActorHandle, (alpha < ALPHA_THRESHOLD), alpha );
}

float
cGraphicActor::GetAlpha()
{
  if (mActorHandle == NULL)
  {
    return 0.0f;
  }

  SyScene *scene = mOwner->GetTitan()->GetScene();
  return scene->GetActorSpriteAlphaBlend( mActorHandle );
}
*/

bool
cGraphicActor::GetIdentNodeLocation(SyRefID identNode, SyVect3* pLoc)
{
  SyAssert(pLoc!=NULL);

  SyScene* pScene = mOwner->GetTitan()->GetScene();
  SyCSprite* pSprite;

  if (pScene && 
      SyActorNull != mActorHandle &&
      pScene->GetActorSpriteType(mActorHandle) == SYSPRITETYPE_CSPRITE &&
      ((pSprite = static_cast<SyCSprite*>(pScene->GetActorSpritePtr(mActorHandle))) != NULL) &&
      pSprite->HasIdentNode(identNode))
  {
    SyMatrix44 mat;
    SyVect3 hpr;
    float scale;

    mat.Identity();
    pSprite->CalcIdentNodeWorldTransform(identNode, mActorHandle, *pScene, mat);
    mat.ConvertTo(hpr, scale, *pLoc);

    return true;
  }

  return false;
}

bool cGraphicActor::GetIdentNodeTransform(SyRefID identNode, SyMatrix44* pTransform)
{
  SyAssert(pTransform!=NULL);

  SyScene* pScene = mOwner->GetTitan()->GetScene();
  SyCSprite* pSprite;

  if (pScene && 
      SyActorNull != mActorHandle &&
      pScene->GetActorSpriteType(mActorHandle) == SYSPRITETYPE_CSPRITE &&
      ((pSprite = static_cast<SyCSprite*>(pScene->GetActorSpritePtr(mActorHandle))) != NULL) &&
      pSprite->HasIdentNode(identNode))
  {
    pTransform->Identity();
    pSprite->CalcIdentNodeWorldTransform(identNode, mActorHandle, *pScene, *pTransform);

    return true;
  }

  return false;
}

void 
cGraphicActor::AddEffect(int index, tGameID effectID)
{
  if (mActorHandle == NULL || 
      ID_NONE == effectID ||
      index < 0 ||
      index >= MAX_EFFECTS ||
      mBodyAttachFXIndex[index] >= 0)
  {
    return;
  }

  SyAssert(ID_NONE == mAttachFXID[index]);
  SyScene *pScene = mOwner->GetTitan()->GetScene();

  SySprite* pMySprite = pScene->GetActorSpritePtr(mActorHandle);
  SyAssert(pMySprite!=NULL); 

  if (!pMySprite)
  {
    return;
  }

  int effectResourceIndex;
  SyResourceType resType;

  if (pScene->GetDictionary()->Find(effectID, resType, effectResourceIndex))
  {  
    GAME_ASSERT(ERROR_ART, SYRESOURCETYPE_GROUPSPRITE == resType || SYRESOURCETYPE_PARTICLESPRITE2 == resType, "Invalid resource type while attaching effect to actor");

    mAttachFXID[index] = effectID;
    int attachSpriteHandle = pScene->CopySprite(effectResourceIndex, 0);

    if (attachSpriteHandle != -1)
    {
      int meshSpriteHandle = -1;

      if (pMySprite->GetType() == SYSPRITETYPE_CSPRITE)
      {
        if (0 == index &&
            (mOwner->GetType()==cGameObject::OBJ_NPC || mOwner->GetType()==cGameObject::OBJ_PLAYER) &&
            static_cast<SyCSprite*>(pMySprite)->HasIdentNode(CHAR_NODE_CHEST))
        {
          mBodyAttachFXIndex[index] = static_cast<SyCSprite*>(pMySprite)->Attach(attachSpriteHandle, CHAR_NODE_CHEST, *pScene);
        }
        else
        {
          mBodyAttachFXIndex[index] = static_cast<SyCSprite*>(pMySprite)->Attach(attachSpriteHandle, *pScene);
        }
      }
      else
      {
        if (pMySprite->GetType() != SYSPRITETYPE_GROUPSPRITE)
        {
          int groupSpriteID = pScene->CreateSprite(SYSPRITETYPE_GROUPSPRITE, 0);
          SyGroupSprite* pGroupSprite = static_cast<SyGroupSprite*>(pScene->Sprite(groupSpriteID));
          SyAssert(pGroupSprite != NULL);

          pGroupSprite->AddMember(pScene->GetActorSprite(mActorHandle), *pScene);
          pScene->SetActorSprite(mActorHandle, groupSpriteID);
          pGroupSprite = static_cast<SyGroupSprite*>(pScene->GetActorSpritePtr(mActorHandle));
          pGroupSprite->AddMember(attachSpriteHandle, *pScene);
        }
        else
        {
          int subSpriteHandle;
          int i = 0;
          SySprite* pSubSprite;
          while (i < static_cast<SyGroupSprite*>(pMySprite)->GetNumMembers())
          {
            subSpriteHandle = static_cast<SyGroupSprite*>(pMySprite)->GetMemberSprite(i);

            if (-1 != subSpriteHandle)
            {
              pSubSprite = pScene->Sprite(subSpriteHandle);

              if (pSubSprite && pSubSprite->GetType() == SYSPRITETYPE_SIMPLE)
              {
                meshSpriteHandle = subSpriteHandle;
                break;
              }
            }

            ++i;
          }

          static_cast<SyGroupSprite*>(pMySprite)->AddMember(attachSpriteHandle, *pScene);
        }

        mBodyAttachFXIndex[index] = attachSpriteHandle;
        SetMeshEmitter(attachSpriteHandle, meshSpriteHandle, *pScene);
      }
    }
  }
  else
  {
    GAME_ASSERT(ERROR_ART, false, "Could not find particle effect resource to attach to actor");    
  }
}

void 
cGraphicActor::RemoveEffect(int index, tGameID effectID)
{
  if (mActorHandle == NULL ||
      index < 0 ||
      index >= MAX_EFFECTS ||
      mAttachFXID[index] != effectID)
  {
    return;
  }

  RemoveEffect(index);
}

void 
cGraphicActor::RemoveEffect(int index)
{
  if (mActorHandle == NULL ||
      index < 0 ||
      index >= MAX_EFFECTS ||
      mBodyAttachFXIndex[index] < 0)
  {
    return;
  }

  SyScene *pScene = mOwner->GetTitan()->GetScene();

  SySprite* pMySprite = pScene->GetActorSpritePtr(mActorHandle);
  SyAssert(pMySprite!=NULL); 

  if (!pMySprite)
  {
    return;
  }

  if (pMySprite->GetType() == SYSPRITETYPE_CSPRITE)
  {
    static_cast<SyCSprite*>(pMySprite)->Detach(mBodyAttachFXIndex[index], *pScene);
  }
  else if (pMySprite->GetType() == SYSPRITETYPE_GROUPSPRITE)
  {
    bool bFoundSprite = false;
    int subSpriteHandle;
    int i = 0;
    while (i < static_cast<SyGroupSprite*>(pMySprite)->GetNumMembers())
    {
      subSpriteHandle = static_cast<SyGroupSprite*>(pMySprite)->GetMemberSprite(i);

      if (-1 != subSpriteHandle && subSpriteHandle == mBodyAttachFXIndex[index])
      {
        static_cast<SyGroupSprite*>(pMySprite)->RemoveMember(i, *pScene);
        bFoundSprite = true;
      }
      else
      {
        ++i;
      }
    }

    GAME_ASSERT(ERROR_CODE, bFoundSprite, "Could not find comp effect sprite attached to actor in order to remove");
  }

  mBodyAttachFXIndex[index] = -1;
  mAttachFXID[index] = ID_NONE;
}

//---------------------------------------------- cGraphicCharacter

static const int lSlots[EQUIP_UNEQUIPPED+1] =
{
  CHAR_ATTACH_MELEE,        //    EQUIP_MELEE,
  CHAR_ATTACH_RANGED,       //    EQUIP_RANGED,
  CHAR_ATTACH_HEAD,         //    EQUIP_HEAD,
  CHAR_ATTACH_BODY,         //    EQUIP_CHEST,
  0,                   //    EQUIP_SHOULDERS,
  CHAR_ATTACH_FEET,         //    EQUIP_LEGS,
  0,                   //    EQUIP_FEET,
  0,                   //    EQUIP_BELT,
  0,                   //    EQUIP_NECKLACE,
  CHAR_ATTACH_SHIELD,  //    EQUIP_L_RING - for dual wielding
  0,                   //    EQUIP_R_RING,
  0,                   //    EQUIP_UNEQUIPPED,
};

static const char* lSlotNames[EQUIP_UNEQUIPPED+1] =
{
  "Melee",        //    EQUIP_MELEE,
  "Ranged",       //    EQUIP_RANGED,
  "Head",         //    EQUIP_HEAD,
  "Body",         //    EQUIP_CHEST,
  "Unused?",                   //    EQUIP_SHOULDERS,
  "Feet",         //    EQUIP_LEGS,
  "Unused?",                   //    EQUIP_FEET,
  "Unused?",                   //    EQUIP_BELT,
  "Unused?",                   //    EQUIP_NECKLACE,
  "Unused?",                   //    EQUIP_L_RING,
  "Unused?",                   //    EQUIP_R_RING,
  "Unused?",                   //    EQUIP_UNEQUIPPED,
};

cGraphicCharacter::cGraphicCharacter()   : 
  mAnimController(NULL),
  mAnimDisplacement(0,0,0),
  mDirty(false),
  mWpnEmitterMeshSpriteHandle(-1),
  mWpnLeftEmitterMeshSpriteHandle(-1)
{
  for (int i=0; i<NUM_ATTACH_SLOTS; ++i)
  {
    mItemFXSpriteHandles[i] = -1;
    mItemFXIDs[i] = ID_NONE;
  }

  InitPropObject( mCLASSID );
}

cGraphicCharacter::~cGraphicCharacter()
{
  delete mAnimController;
  mAnimController = NULL;

  if (mWpnEmitterMeshSpriteHandle != -1)
  {
    mOwner->GetTitan()->GetScene()->ReleaseSprite(mWpnEmitterMeshSpriteHandle);
    mWpnEmitterMeshSpriteHandle = -1;
  }

  if (mWpnLeftEmitterMeshSpriteHandle != -1)
  {
    mOwner->GetTitan()->GetScene()->ReleaseSprite(mWpnLeftEmitterMeshSpriteHandle);
    mWpnLeftEmitterMeshSpriteHandle = -1;
  }
}

int           
cGraphicCharacter::InitPropClass()
{
/* Add the class */

  AddSubClass( mCLASSID, 
               cGraphicActor::mCLASSID,
               mCLASSID,
               "cGraphicCharacter", 
               Creator, 
               mCLASSID, 
               0 ); 

  return 0;
}

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

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

  return(pObject);
}


void  
cGraphicCharacter::Init()
{
  // load up model...note that resource should already be loaded 
  // if we're loading from an esf file, and this should just hook
  // 'em up.

  if (mActorHandle != NULL)
  {
    return; // already init
  }

  cGraphicActor::Init();

  if (mAnimController == NULL)
  {
    mAnimController = cAnimCharControllerInterface::Allocate();
    mAnimController->Init(mOwner);
  }
  else
  {
    mAnimController->Reset();
  }

  SetDirty();
}


void          
cGraphicCharacter::Reset()
{
  if (mAnimController)
  {
    mAnimController->Reset();
  }
  RagdollOff(1);
}

void 
cGraphicCharacter::Exit()
{
  if (mAnimController)
  {
    mAnimController->Exit();
  }
}

bool
cGraphicCharacter::CheckForDelete()
{
  if (mAnimController)
  {
    return mAnimController->CheckForDelete();
  }

  return false;
}

bool cGraphicCharacter::GetEquipSlotTransform(int slot, SyMatrix44* pTransform)
{
  GAME_ASSERT(ERROR_CODE, slot>=EQUIP_MELEE && slot < EQUIP_UNEQUIPPED, "Bad equip slot in cGraphicCharacter::GetEquipSlotTransform %d", slot);
  SyScene* pScene = mOwner->GetTitan()->GetScene();
  SyCSprite* pSprite;

  if (pScene && 
      SyActorNull != mActorHandle &&
      pScene->GetActorSpriteType(mActorHandle) == SYSPRITETYPE_CSPRITE &&
      ((pSprite = static_cast<SyCSprite*>(pScene->GetActorSpritePtr(mActorHandle))) != NULL) &&
       pSprite->HasAttachSlot(lSlots[slot]))
  {
    pTransform->Identity();
    pSprite->CalcAttachSlotWorldTransform(lSlots[slot], mActorHandle, *pScene, *pTransform);
    return true;
  }

  return false;
}

void  
cGraphicCharacter::Update(float delta)
{
  mAnimController->Update(delta);

  if (mDirty)
  {
    RecalculateWeaponAttachments();
    mDirty = false;
  }

  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();
  SyCSprite* cSprite = (SyCSprite*) scene->GetActorSpritePtr( mActorHandle);
  SyAssert(cSprite!=NULL); 

  SyMatrix44 transform;
  SyVect3 Rotation,Scale, Translation;
  cSprite->CalcMotionDeltaTransform(*scene,transform);
  transform.ConvertTo(Rotation, Scale, Translation);
  float heading = mOwner->GetHeading();

  mAnimDisplacement.X = (SY_SIN(heading) * Translation.Z + SY_COS(heading) * Translation.X);
  mAnimDisplacement.Y = 0;
  mAnimDisplacement.Z = (SY_COS(heading) * Translation.Z - SY_SIN(heading) * Translation.X);
}

const SyVect3 &                  
cGraphicCharacter::GetDisplacement() 
{
  return mAnimDisplacement;
}

void 
cGraphicCharacter::EnterScope()
{
  // Called when object comes into focus

  // throws away motion transforms, as object is paw
  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();
  SyCSprite* cSprite = (SyCSprite*)  scene->GetActorSpritePtr( mActorHandle);
  SyAssert(cSprite!=NULL); 
  SyMatrix44 transform;
  SyVect3 Rotation,Scale, Translation;
  cSprite->CalcMotionDeltaTransform(*scene,transform);

  if (mAnimController)
  {
    mAnimController->EnterScope();
  }
}

void
cGraphicCharacter::ExitScope()
{
  if (mAnimController)
  {
    mAnimController->ExitScope();
  }
}

void
cGraphicCharacter::AddItemEffect(int equipSlot, tGameID fxID)
{
  SyAssert(equipSlot >= EQUIP_MELEE && equipSlot < EQUIP_UNEQUIPPED);
  SyAssert(equipSlot < NUM_ATTACH_SLOTS);  

  if (EQUIP_MELEE != equipSlot ||
      ID_NONE == fxID ||
      -1 != mItemFXSpriteHandles[equipSlot])
  {
    return; // currently only melee weapons support attached particles
  }

  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();
  SyCSprite* cSprite = (SyCSprite*)  scene->GetActorSpritePtr( mOwner->GetGraphic()->GetActorHandle());
  SyAssert(cSprite!=NULL); 

  if (!cSprite->HasAttachSlot(lSlots[equipSlot]))
  {
    return;  // no item to attach effects to
  }

  int32 itemSpriteHandle = cSprite->GetAttachSlotSprite(lSlots[equipSlot]);

  if (-1 == itemSpriteHandle)
  {
    return;
  }

  SySprite* pItemSprite = scene->Sprite(itemSpriteHandle);

  if (!pItemSprite || pItemSprite->GetType() != SYSPRITETYPE_GROUPSPRITE)
  {
    return;
  }

  int32 emitterMeshSpriteHandle = -1;
  SySprite* pEmitterMeshSprite = NULL;

  // first see if we have a specific emitter mesh
  if (-1 != mWpnEmitterMeshSpriteHandle)
  {
    emitterMeshSpriteHandle = mWpnEmitterMeshSpriteHandle;
    pEmitterMeshSprite = scene->Sprite(mWpnEmitterMeshSpriteHandle);
  }

  if (!pEmitterMeshSprite || pEmitterMeshSprite->GetType() != SYSPRITETYPE_SIMPLE)
  {
    // if we don't have an emitter mesh, use the basic weapon mesh
    if (static_cast<SyGroupSprite*>(pItemSprite)->GetNumMembers() > 0)
    {
      emitterMeshSpriteHandle = static_cast<SyGroupSprite*>(pItemSprite)->GetMemberSprite(0);
      pEmitterMeshSprite = scene->Sprite(emitterMeshSpriteHandle);
    }
  }

  int fxResourceIndex;
  SyResourceType resType;
  if (scene->GetDictionary()->Find(fxID, resType, fxResourceIndex))
  {  
    GAME_ASSERT(ERROR_ART, SYRESOURCETYPE_GROUPSPRITE == resType || SYRESOURCETYPE_PARTICLESPRITE2 == resType, "Invalid resource type while attaching effect to actor");

    int attachFXSpriteHandle = scene->CopySprite(fxResourceIndex, 0);

    if (attachFXSpriteHandle != -1)
    {
      if (pEmitterMeshSprite && pEmitterMeshSprite->GetType() == SYSPRITETYPE_SIMPLE)
      {
        SetMeshEmitter(attachFXSpriteHandle, emitterMeshSpriteHandle, *scene);
      }

      static_cast<SyGroupSprite*>(pItemSprite)->AddMember(attachFXSpriteHandle, *scene);

      mItemFXSpriteHandles[equipSlot] = attachFXSpriteHandle;
      mItemFXIDs[equipSlot] = fxID;
    }
    else
    {
      GAME_ASSERT(ERROR_ART, false, "Could not copy effect sprite to attach to character item");
    }
  }
  else
  {
    GAME_ASSERT(ERROR_ART, false, "Could not find particle effect resource to attach to item on actor");    
  }
}

void
cGraphicCharacter::RemoveItemEffect(int equipSlot, tGameID fxID)
{
  SyAssert(equipSlot >= EQUIP_MELEE && equipSlot < EQUIP_UNEQUIPPED);
  SyAssert(equipSlot < NUM_ATTACH_SLOTS);  

  if (EQUIP_MELEE != equipSlot ||  // currently only melee weapons support attached particles
      ID_NONE == fxID ||
      -1 == mItemFXSpriteHandles[equipSlot] ||
      fxID != mItemFXIDs[equipSlot])
  {
    return; 
  }

  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();
  SyCSprite* cSprite = (SyCSprite*)  scene->GetActorSpritePtr( mOwner->GetGraphic()->GetActorHandle());
  SyAssert(cSprite!=NULL); 

  if (!cSprite->HasAttachSlot(lSlots[equipSlot]))
  {
    return;  // no item to attach effects to
  }

  int32 itemSpriteHandle = cSprite->GetAttachSlotSprite(lSlots[equipSlot]);
  SySprite* pItemSprite = scene->Sprite(itemSpriteHandle);

  if (!pItemSprite || pItemSprite->GetType() != SYSPRITETYPE_GROUPSPRITE)
  {
    return;
  }

  int subSpriteHandle;
  int index = 0;

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

    if (-1 != subSpriteHandle && subSpriteHandle == mItemFXSpriteHandles[equipSlot])
    {
      static_cast<SyGroupSprite*>(pItemSprite)->RemoveMember(index, *scene);
      mItemFXSpriteHandles[equipSlot] = -1;
      mItemFXIDs[equipSlot] = ID_NONE;
      break;
    }
    else
    {
      ++index;
    }
  }
}

void                  
cGraphicCharacter::RecalculateWeaponAttachments()
{
  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();
  SyCSprite* cSprite = (SyCSprite*)  scene->GetActorSpritePtr( mOwner->GetGraphic()->GetActorHandle());
  SyAssert(cSprite!=NULL); 
  SyESFParse& Parser = mOwner->GetTitan()->GetTitanUI()->GetESFParser();
  SyString attachModelFilename, attachLeftModelFilename, emitterModelFilename, emitterLeftModelFilename;
  cInventoryCharacter *inv = (cInventoryCharacter*) mOwner->GetInventory();

  for (int ii=EQUIP_MELEE;ii!=EQUIP_UNEQUIPPED;++ii)
  {
    if (EQUIP_L_RING == ii)
    {
      continue;
    }

    cItem *item = inv->GetEquippedItem((eEquipSlot)ii);
    if (item == NULL)
    {
      cSprite->DetachFromSlot(lSlots[ii],*scene );
      continue;
    }

    item->GetAttachModelFilename(mOwner, &attachModelFilename, &attachLeftModelFilename);
    if (attachModelFilename.Length() <= 0)
    {
      cSprite->DetachFromSlot(lSlots[ii],*scene );
      continue;
    }

    int32 attachSpriteID = mOwner->GetTitan()->LoadAssetSprite(Parser, attachModelFilename.AsChar());
    if (attachSpriteID < 0)
    {
      continue;
    }

    if (EQUIP_MELEE == ii)
    {
      // create grouped sprite of weapon and particle effect 
      // using special emitter mesh if it exists
      item->GetEmitterModelFilename(mOwner, &emitterModelFilename, &emitterLeftModelFilename);
      int32 emitterMeshSpriteHandle = -1;
      
      if (emitterModelFilename.Length() > 0)
      {
        emitterMeshSpriteHandle = mOwner->GetTitan()->LoadAssetSprite (Parser, emitterModelFilename.AsChar(), true);
      }

      tGameID fxID = item->GetAttachFX(mOwner);

      int groupSpriteID = scene->CreateSprite(SYSPRITETYPE_GROUPSPRITE, 0);
      SyGroupSprite* pGroupSprite = static_cast<SyGroupSprite*>(scene->Sprite(groupSpriteID));
      SyAssert(pGroupSprite != NULL);

      pGroupSprite->AddMember(attachSpriteID, *scene);
      attachSpriteID = groupSpriteID;

      if (-1 != mWpnEmitterMeshSpriteHandle)
      {
        scene->ReleaseSprite(mWpnEmitterMeshSpriteHandle);
        mWpnEmitterMeshSpriteHandle = -1;
      }

      // add reference to emitter mesh to use for item-based particle effects
      if (-1 != emitterMeshSpriteHandle)
      {
        mWpnEmitterMeshSpriteHandle = emitterMeshSpriteHandle;
        scene->ShareSprite(mWpnEmitterMeshSpriteHandle);
      }

      GAME_ASSERT(ERROR_ART, cSprite->HasAttachSlot(lSlots[ii]) != 0, "Character esf '%s' does not have attach slot %s", mOwner->GetStats()->GetModelName(), lSlotNames[ii]);
      cSprite->AttachToSlot(attachSpriteID, lSlots[ii], *scene );

      // attach magical effect to the weapon group sprite
      if (ID_NONE != fxID)
      {
        AddItemEffect(ii, fxID);
      }

      // hacky dual wielding
      if (attachLeftModelFilename.Length() > 0)
      {
        int32 attachLeftSpriteID = mOwner->GetTitan()->LoadAssetSprite(Parser, attachLeftModelFilename.AsChar());
        if (attachLeftSpriteID != -1)
        {
          int32 emitterLeftMeshSpriteHandle = -1;

          if (emitterLeftModelFilename.Length() > 0)
          {
            emitterLeftMeshSpriteHandle = mOwner->GetTitan()->LoadAssetSprite (Parser, emitterLeftModelFilename.AsChar(), true);
          }

          int leftGroupSpriteID = scene->CreateSprite(SYSPRITETYPE_GROUPSPRITE, 0);
          SyGroupSprite* pLeftGroupSprite = static_cast<SyGroupSprite*>(scene->Sprite(leftGroupSpriteID));
          SyAssert(pLeftGroupSprite != NULL);

          pLeftGroupSprite->AddMember(attachLeftSpriteID, *scene);
          attachLeftSpriteID = leftGroupSpriteID;

          if (-1 != mWpnLeftEmitterMeshSpriteHandle)
          {
            scene->ReleaseSprite(mWpnLeftEmitterMeshSpriteHandle);
            mWpnLeftEmitterMeshSpriteHandle = -1;
          }

          // add reference to emitter mesh to use for item-based particle effects
          if (-1 != emitterLeftMeshSpriteHandle)
          {
            mWpnLeftEmitterMeshSpriteHandle = emitterLeftMeshSpriteHandle;
            scene->ShareSprite(mWpnLeftEmitterMeshSpriteHandle);
          }

          GAME_ASSERT(ERROR_ART, cSprite->HasAttachSlot(lSlots[EQUIP_L_RING]) != 0, "Character esf '%s' does not have attach slot Shield", mOwner->GetStats()->GetModelName());
          cSprite->AttachToSlot(attachLeftSpriteID, lSlots[EQUIP_L_RING], *scene );

          if (ID_NONE != fxID)
          {
            // fake Left ring slot as shield attachement for dual wielding effects
            AddItemEffect(EQUIP_L_RING, fxID);
          }
        }
      }
    }
    else
    {
      cSprite->AttachToSlot(attachSpriteID, lSlots[ii], *scene );
    }
  }

  // clear skins

  // advanced skin to base skin lookup
  // (i could have done it with a mod operator but i think this is clearer)

  int32 lBaseSkins[NUM_SKINS] = 
  {
    SKIN_NONE,
    SKIN_BODY,
    SKIN_BASE_HELMET,
    SKIN_BASE_SHOULDERS,
    SKIN_BASE_CHEST,
    SKIN_BASE_LEGS,
    SKIN_BASE_BOOTS,
    SKIN_BASE_HANDS,

    // light set
    SKIN_BASE_HELMET,
    SKIN_BASE_SHOULDERS,
    SKIN_BASE_CHEST,
    SKIN_BASE_LEGS,
    SKIN_BASE_BOOTS,

    // medium set
    SKIN_BASE_HELMET,
    SKIN_BASE_SHOULDERS,
    SKIN_BASE_CHEST,
    SKIN_BASE_LEGS,
    SKIN_BASE_BOOTS,

    // heavy set
    SKIN_BASE_HELMET,
    SKIN_BASE_SHOULDERS,
    SKIN_BASE_CHEST,
    SKIN_BASE_LEGS,
    SKIN_BASE_BOOTS,

    // hands
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS,
    SKIN_BASE_HANDS
  };

  #define NUM_BASE_SKINS (sizeof(lBaseSkins)/sizeof(lBaseSkins[0]))

  SyAssertf(NUM_BASE_SKINS==NUM_SKINS,"Added a skin and forgot to modify base skin table?");

  // detach extra armor skins
  for (int ii=SKIN_LIGHT_HELMET;ii<NUM_SKINS;++ii)
  {
    cSprite->DetachSkinID(ii,*scene);
  }

  // make sure base skins are attached
  for (int ii=SKIN_BODY;ii<=SKIN_BASE_HANDS;++ii)
  {
    cSprite->AttachSkinID(ii,*scene);
  }

  // attach skins from items
  for (int ii=EQUIP_MELEE;ii!=EQUIP_UNEQUIPPED;++ii)
  {
    cItem *item = inv->GetEquippedItem((eEquipSlot)ii);
    if (item != NULL)
    {
      SyRefID id = item->GetMaster()->mAttachSkin;
      if (id != 0)
      {
        // clear base version...
        cSprite->DetachSkinID(lBaseSkins[id],*scene);

        if (cSprite->HasSkin(id))
        {
          cSprite->AttachSkinID(id,*scene);
        }
      }
    }
  }

  cStatsCharacter *stats = static_cast<cStatsCharacter*>(mOwner->GetStats());
  for (int i=0; i<cStatsCharacterMaster::MAX_ATTACHMENTS; ++i)
  {
    if (stats->GetMaster()->mAttachmentModelNames[i].Length() > 0 &&
        cSprite->GetAttachSlotSprite(i+1) == -1)
    {
      SyResourceType resType;
      int32 spriteHandle = -1;
      if (!scene->GetDictionary()->Find(stats->GetMaster()->mAttachmentModelNames[i].AsChar(), resType, spriteHandle))
      {
        SyPathname path;
        path.Path("game_assets/art/items");
        path.Base(stats->GetMaster()->mAttachmentModelNames[i].AsChar());
        path.Extension("esf");

        spriteHandle = mOwner->GetTitan()->LoadAssetSprite (Parser, path.Full().AsChar());
      }
      else
      {
        GAME_ASSERT(ERROR_ART, SYRESOURCETYPE_GROUPSPRITE == resType || SYRESOURCETYPE_SIMPLESPRITE == resType, "Invalid resource type while attaching object '%s' to character", stats->GetMaster()->mAttachmentModelNames[i].AsChar());

        if (SYRESOURCETYPE_GROUPSPRITE != resType && SYRESOURCETYPE_SIMPLESPRITE != resType)
        {
          spriteHandle = -1;
        }
      }

      if (-1 != spriteHandle)
      {
        SySprite* pSprite = scene->Sprite(spriteHandle);
        if (pSprite)
        {
          if (0==i && pSprite->GetType() != SYSPRITETYPE_GROUPSPRITE)
          {
            // make melee weapon model a groupsprite for item effects
            int groupSpriteID = scene->CreateSprite(SYSPRITETYPE_GROUPSPRITE, 0);
            SyGroupSprite* pGroupSprite = static_cast<SyGroupSprite*>(scene->Sprite(groupSpriteID));
            SyAssert(pGroupSprite != NULL);

            pGroupSprite->AddMember(spriteHandle, *scene);
            spriteHandle = groupSpriteID;
          }

          cSprite->AttachToSlot(spriteHandle, i+1, *scene);
        }
      }
    }
  }  
}


void          
cGraphicCharacter::Prerender()
{
  cGraphicActor::Prerender();
}

void
cGraphicCharacter::DrawRing(SyCamera &camera)
{

  SyVect3 pos;
  SyCollSurfType type;
  SyVect3 normal(0.0f,1.0f,0.0f);

  cPhysicsAnimated *anim = prop_cast<cPhysicsAnimated *>(mOwner->GetPhysics());
  anim->GetRingLocation(pos,normal,type);

  if (type == SYCOLLSURFTYPE_NONE) return; // don't draw ring
  pos.Y += 0.05f; // avoid z fighting.



  SyRaster *raster = mOwner->GetTitan()->GetScene()->GetRasterDev();

  SyMatrix44 identityMat;
  identityMat.Identity();

  raster->SetWorld(identityMat);
  raster->SetProjection( camera.GetProjection() );
  raster->SetView(camera.GetView() );



  //raster->SetSolidFillMaterial( );
  //raster->SetSolidFillUIMaterial();



  SyVect2 minUV;
  SyVect2 maxUV;
  int32 handle;

  mOwner->GetTitan()->GetRegistry()->GetLevelObject()->GetHighlightMaterial(handle,minUV,maxUV);
  raster->SetMaterial(handle);

  float size = 0.7f;
  SyVect3 width(size,0.0f,0.0f);
  SyVect3 height;

  height.Cross(normal,width);
  width.Cross(height,normal);
  
  SyColor32F colors[] = 
  {
    SyColor32F(0.7f,0.0f,0.0f,1.0f),
    SyColor32F(0.0f,0.7f,0.0f,1.0f),
    SyColor32F(0.0f,0.0f,0.7f,1.0f),
    SyColor32F(1.0f,1.0f,0.0f,1.0f),
  };

  int index;
  if (mOwner->IsLocal())
  {
    index = prop_cast<cIntelPlayer *>(mOwner->GetIntel())->GetControllerID();
    if (index > 2) index = 2;
  }
  else
  {
    index = 3;
  }
  uint16* pIndices = NULL;
  void* pVertData = NULL;
  if (raster->BeginIndexTris(SYVERTEXTYPE_VUNC, 2, 4, &pIndices, &pVertData) < 0)
  {
    return ;
  }

  SyVertexVUNC* pVert = (SyVertexVUNC*)pVertData;

  pVert->V.Sub(pos,width);
  pVert->V.Sub(pVert->V,height);
  pVert->C = colors[index];
  pVert->N = normal;
  pVert->UV0(0) = minUV.X;
  pVert->UV0(1) = minUV.Y;
  pVert++;

  pVert->V.Sub(pos,width);
  pVert->V.Add(pVert->V,height);
  pVert->C = colors[index];
  pVert->N = normal;
  pVert->UV0(0) = minUV.X;
  pVert->UV0(1) = maxUV.Y;
  pVert++;

  pVert->V.Add(pos,width);
  pVert->V.Add(pVert->V,height);
  pVert->C = colors[index];
  pVert->N = normal;
  pVert->UV0(0) = maxUV.X;
  pVert->UV0(1) = maxUV.Y;
  pVert++;

  pVert->V.Add(pos,width);
  pVert->V.Sub(pVert->V,height);
  pVert->C = colors[index];
  pVert->N = normal;
  pVert->UV0(0) = maxUV.X;
  pVert->UV0(1) = minUV.Y;
  pVert++;

  pIndices[0] = 0;
  pIndices[1] = 1;
  pIndices[2] = 2;

  pIndices[3] = 0;
  pIndices[4] = 2;
  pIndices[5] = 3;

  raster->EndIndexTris();

  return;
};


//---------------------------------------------- cGraphicProp
cGraphicProp::cGraphicProp()
: cGraphicActor(),
  mAnimController(NULL)
{
  InitPropObject( mCLASSID );
}

cGraphicProp::~cGraphicProp()
{
  delete mAnimController;
  mAnimController = NULL;
};

int           
cGraphicProp::InitPropClass()
{
  /* Add the class */

  AddSubClass( mCLASSID, 
    cGraphicActor::mCLASSID,
    mCLASSID,
    "cGraphicProp", 
    Creator, 
    mCLASSID, 
    0 ); 
  return 0;
}

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

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

  return(pObject);
}

void  
cGraphicProp::Init()
{
  // load up model...note that resource should already be loaded 
  // if we're loading from an esf file, and this should just hook
  // 'em up.

  cGraphicActor::Init();

  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();

  if (mActorHandle != NULL &&
      scene->GetActorSpriteType(mActorHandle) == SYSPRITETYPE_CSPRITE &&
      mAnimController == NULL)
  {
    // only create anim controller if we support anims
    mAnimController = cAnimPropControllerInterface::Allocate();
    SyAssert(mAnimController);
    if (mAnimController != NULL)
    {
      mAnimController->Init(mOwner);
    }
  }
}

void  
cGraphicProp::Reset()
{
  if (mAnimController)
  {
    mAnimController->Reset();
  }
}


void  
cGraphicProp::Update(float delta)
{
  if (mAnimController)
  {
    mAnimController->Update(delta);
  }
}

void
cGraphicProp::NetworkReceiveBroadcast(const char *packet, int size)
{
  if (mAnimController)
  {
    mAnimController->NetworkReceiveBroadcast(packet,size);
  }
}

//---------------------------------------------- cGraphicDummy
cGraphicDummy::cGraphicDummy()
: cGraphicActor()
{
  InitPropObject( mCLASSID );
}

cGraphicDummy::~cGraphicDummy()
{
}

int           
cGraphicDummy::InitPropClass()
{
  /* Add the class */

  AddSubClass( mCLASSID, 
    cGraphicActor::mCLASSID,
    mCLASSID,
    "cGraphicDummy", 
    Creator, 
    mCLASSID, 
    0 ); 
  return 0;
}

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

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

  return(pObject);
}

void  
cGraphicDummy::Init()
{
  if (SyActorNull == mActorHandle)
  {
    SyScene* pScene = mOwner->GetTitan()->GetScene();
    int32 groupSpriteID = pScene->CreateSprite(SYSPRITETYPE_GROUPSPRITE, 0);
    mActorHandle = pScene->CreateActor( mOwner->GetLocation(), mOwner->GetHPR(), 1.0f, groupSpriteID);
  }
}



//--------------------------------------------------------- Globals
//----------------------------------------- Functions Declarations
void 
RegPropClasses_Graphic()
{
  cGraphic::InitPropClass();
  cGraphicActor::InitPropClass();
  cGraphicCharacter::InitPropClass();
  cGraphicProp::InitPropClass();
  cGraphicDummy::InitPropClass();
}

int GetAttachSlotFromEquip (int i)
{
  int result = lSlots[i];
  return result;
}
//------------------------------------ Member Functions Definitions

void 
cGraphic::SetMeshEmitter(int32 fxSpriteHandle, int32 emitterMeshSpriteHandle, SyScene& scene)
{
  if (-1 == fxSpriteHandle || -1 == emitterMeshSpriteHandle)
  {
    return;
  }

  SySprite* pFXSprite = scene.Sprite(fxSpriteHandle);

  if (!pFXSprite)
  {
    return;
  }

  if (SYSPRITETYPE_PARTICLESPRITE2 == pFXSprite->GetType())
  {
    SyParticleEffect2* pEffect = scene.GetParticleSystem2()->Effect(static_cast<SyParticleSprite2*>(pFXSprite)->GetEffectHandle());
    SyAssert(pEffect != NULL);

    if (pEffect)
    {
      int emitterIndex = pEffect->GetFirstEmitter();
      int numEmitters = pEffect->GetNumEmitters();
      SyParticleEmitter2* pEmitter;
      while(emitterIndex >= 0 && emitterIndex < numEmitters)
      {
        pEmitter = pEffect->Emitter(emitterIndex);
        SyAssert(pEmitter != NULL);

        if (pEmitter != NULL &&
            SYPARTICLEEMISSIONMODULETYPE_MESH == pEmitter->GetEmissionModuleType())
        {
          SyAssert(pEmitter->EmissionModule() != NULL);
          static_cast<SyParticleMeshEmissionModule*>(pEmitter->EmissionModule())->SetSprite(emitterMeshSpriteHandle, scene);
        }

        emitterIndex = pEffect->GetNextEmitter(emitterIndex);
      }
    }
  }
  else if (SYSPRITETYPE_GROUPSPRITE == pFXSprite->GetType())
  {
    int numMembers = static_cast<SyGroupSprite*>(pFXSprite)->GetNumMembers();

    for (int i=0; i < numMembers; ++i)
    {
      // make sure nothing got deleted out from under us
      SyAssert(numMembers == static_cast<SyGroupSprite*>(pFXSprite)->GetNumMembers());

      SetMeshEmitter(static_cast<SyGroupSprite*>(pFXSprite)->GetMemberSprite(i), emitterMeshSpriteHandle, scene);
    }
  }
}

// EOF
