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

//-------------------------------------------------------- Includes
#include "areaeffect.h"
#include "registry.h"
#include "stats.h"
#include "titan.h"
#include "database.h"
#include "intel.h"
#include "graphic.h"
#include "script_pawn.h"
#include "physics.h"
#include "debris.h"
#include "spell.h"
#include "debugoverlay.h"
#include "ai/behaviortypes.h"
#include "ai/aiblackboard.h"
#include "SyScene.h"
#include "gameerror.h"

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

cGameEffect::cGameEffect()
: mSource(ID_NONE),
  mpOwner(NULL),
  mPercentChance(100),
  mTargetRace(ID_NONE)
{
  InitPropObject( mCLASSID );
}

int cGameEffect::InitPropClass()
{
  AddClass( mCLASSID, 
    "cGameEffect", 
    NULL,     // abstract base class has no creator
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_SourceID,SyMemberOffset(cGameEffect,mSource),"mSource");
  AddInt32Property(mCLASSID,PropId_PercentChance,SyMemberOffset(cGameEffect,mPercentChance),"mPercentChance");
  AddInt32Property(mCLASSID,PropId_TargetRace,SyMemberOffset(cGameEffect,mTargetRace),"mTargetRace");

  return 0;
}

bool cGameEffect::TargetHasBlocked(cGameObject* pTarget)
{
  SyAssert(mpOwner);

  if (mpOwner && mpOwner->GetSpellID() != ID_NONE)
  {
    cGraphicCharacter* pCharGraphic = prop_cast<cGraphicCharacter*>(pTarget->GetGraphic());
    const cSpellMaster* pSpell = pTarget->GetTitan()->GetDatabaseSys()->GetSpellMaster(mpOwner->GetSpellID());
    SyAssert(pSpell!=NULL);

    if (pSpell && pCharGraphic)
    {
      if (pSpell->mbBlockable && pCharGraphic->GetAnimController()->IsBlocking())
      {
        return true;
      }
      else if (pSpell->mbJumpable)
      {
        eAnimState animState = pCharGraphic->GetAnimController()->GetAnimState();
        
        if (AS_IN_AIR == animState || AS_RUN_IN_AIR == animState || AS_ATTACK_JUMP == animState)
        {
          return true;
        }
      }
    }
  }

  return false;
}

bool 
cGameEffect::TargetMatchesRace(cGameObject* pTarget)
{
  return ID_NONE == mTargetRace || !pTarget || pTarget->GetStats()->GetRace() == mTargetRace;
}
//---------------------------------------------------- cGameEffect_ScriptCall

cGameEffect_ScriptCall::cGameEffect_ScriptCall() :
cGameEffect(),
mOwner(ID_NONE)
{
  InitPropObject( mCLASSID );
}

int cGameEffect_ScriptCall::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_ScriptCall", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_OwnerID,SyMemberOffset(cGameEffect_ScriptCall,mOwner),"mOwner");

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_ScriptCall::OnEnter(tGameObjectID id)
{
  Titan::Get()->GetScriptSys()->ScriptEvent(PET_TRIGGER_ENTER,mOwner,id);
  return true;
}

void  
cGameEffect_ScriptCall::OnExit(tGameObjectID id)
{
  Titan::Get()->GetScriptSys()->ScriptEvent(PET_TRIGGER_EXIT,mOwner,id);
}


//---------------------------------------------------- cGameEffect_Damage 


cGameEffect_Damage::cGameEffect_Damage() :
  cGameEffect(),
  mDamage(0),
  mDamageType(DT_NONE)
{
  InitPropObject( mCLASSID );
}

int cGameEffect_Damage::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Damage", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_Damage,SyMemberOffset(cGameEffect_Damage,mDamage),"mDamage");

  SyPropEnum *propEnum;
  AddEnumProperty(mCLASSID,PropId_DamageType,SyMemberOffset(cGameEffect_Damage,mDamageType),"mDamageType",&propEnum);
  propEnum->Add(DT_NONE,"DT_NONE");
  propEnum->Add(DT_PHYSICAL,"DT_PHYSICAL");
  propEnum->Add(DT_MAGICAL,"DT_MAGICAL");
  propEnum->Add(NUM_DAMAGE_TYPES,"NUM_DAMAGE_TYPES");

  return 0;
}

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

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

  return(pObject);
}

void  
cGameEffect_Damage::SetDamage(int damage,eDamageType normalType)
{
  mDamage = damage;
  mDamageType = normalType;
};

  
bool  
cGameEffect_Damage::OnEnter(tGameObjectID target_id)
{
  cGameObject *target_obj = Titan::Get()->GetRegistry()->Fetch(target_id);

  if (target_obj == NULL ||
      target_obj->IsRemote() ||
      !TargetMatchesRace(target_obj))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    if (!TargetHasBlocked(target_obj))
    {
      cDamagePacket damage;

      damage.AddTotal(mDamage,"Effect","Area");
      damage.mbRanged = true;
      damage.mAttackerID = mSource;
      damage.mDefenderID = target_id;
      damage.mPacketType = cRulePacket::CALC_DAMAGE_REDUCED;
      damage.mbMagic = true;
      damage.mSpellID = mpOwner->GetSpellID();
      damage.SetDamageType(mDamageType);

      damage.mAgentID = mSource;
      target_obj->GetStats()->ProcessRulePacket(&damage);

      damage.mPacketType = cRulePacket::EVENT_HIT;
      target_obj->GetStats()->ApplyDamage(&damage);
    }

    return true; // true to play impact fx
  }

  return false;
}

void
cGameEffect_Damage::Update(float time)
{
  mpOwner->Explosion();
}


//---------------------------------------------------- cGameEffect_Heal 
cGameEffect_Heal::cGameEffect_Heal()
: cGameEffect(),
  mHealing(0),
  mbIsPctMaxHealth(false)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Heal::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Heal", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_Healing,SyMemberOffset(cGameEffect_Heal,mHealing),"mHealing");
  AddBoolProperty(mCLASSID,PropId_IsPctMaxHealth,SyMemberOffset(cGameEffect_Heal,mbIsPctMaxHealth),"mbIsPctMaxHealth");

  return 0;
}

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

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

  return(pObject);
}

void  
cGameEffect_Heal::SetHealing(int amt, bool bIsPctMaxHealth)
{
  mHealing = amt;
  mbIsPctMaxHealth = bIsPctMaxHealth;
}


bool  
cGameEffect_Heal::OnEnter(tGameObjectID target_id)
{
  cGameObject *target_obj = Titan::Get()->GetRegistry()->Fetch(target_id);

  if (target_obj == NULL ||
      target_obj->IsRemote() ||
      target_obj->GetStats()->IsDead() ||
      !TargetMatchesRace(target_obj))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    if (!TargetHasBlocked(target_obj))
    {
      cCalcPacket packet;
      packet.SetTotal(mHealing, "Effect", "Area");

      cStatsCharacter* pTargetCharStats = prop_cast<cStatsCharacter*>(target_obj->GetStats());

      if (pTargetCharStats)
      {
        if (prop_cast<cAreaEffect_PowerUp*>(mpOwner) != NULL)
        {
          cCalcPacket bonusHealthAbsorb;
          bonusHealthAbsorb.mPacketType = cRulePacket::CALC_HEALTH_ABSORB;
          pTargetCharStats->ProcessRulePacket(&bonusHealthAbsorb);
          packet.MultiplyTotal(1.0f + ((float)bonusHealthAbsorb.GetTotal()) * 0.01f, "Effect", "Area");
        }

        if (mbIsPctMaxHealth)
        {
          int maxHealth = pTargetCharStats->CalcMaxHealth();
          packet.MultiplyTotal(((float)maxHealth) * 0.01f, "Effect", "Area");
        }
      }

      target_obj->GetStats()->ApplyHealing(&packet);
    }

    return true; // true to play impact fx
  }

  return false;
}

//---------------------------------------------------- cGameEffect_ManaCost 
cGameEffect_ManaCost::cGameEffect_ManaCost() :
cGameEffect(),
mCost(0),
mbIsPctMaxMana(false)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_ManaCost::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_ManaCost", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_Cost,SyMemberOffset(cGameEffect_ManaCost,mCost),"mCost");
  AddBoolProperty(mCLASSID,PropId_IsPctMaxMana,SyMemberOffset(cGameEffect_ManaCost,mbIsPctMaxMana),"mbIsPctMaxMana");

  return 0;
}

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

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

  return(pObject);
}

void  
cGameEffect_ManaCost::SetCost(int cost, bool bIsPctMaxMana)
{
  mCost = cost;
  mbIsPctMaxMana = bIsPctMaxMana;
}


bool  
cGameEffect_ManaCost::OnEnter(tGameObjectID target_id)
{
  cGameObject *target_obj = Titan::Get()->GetRegistry()->Fetch(target_id);

  if (target_obj == NULL ||
      target_obj->IsRemote() ||
      !TargetMatchesRace(target_obj))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    if (!TargetHasBlocked(target_obj))
    {
      cCalcPacket packet;
      packet.SetTotal(mCost,"Effect","Area");
      packet.mPacketType = cRulePacket::EVENT_APPLY_MANA_COST;

      cStatsCharacter* pTargetCharStats = prop_cast<cStatsCharacter*>(target_obj->GetStats());

      if (pTargetCharStats)
      {
        if (prop_cast<cAreaEffect_PowerUp*>(mpOwner) != NULL)
        {
          cCalcPacket bonusManaAbsorb;
          bonusManaAbsorb.mPacketType = cRulePacket::CALC_MANA_ABSORB;
          pTargetCharStats->ProcessRulePacket(&bonusManaAbsorb);
          packet.MultiplyTotal(1.0f+((float)bonusManaAbsorb.GetTotal()) * 0.01f, "Effect", "Area");
        }

        if (mbIsPctMaxMana)
        {
          int maxMana = pTargetCharStats->CalcMaxMana();
          packet.MultiplyTotal(((float)maxMana) * 0.01f, "Effect", "Area");
        }
      }

      target_obj->GetStats()->ApplyManaCost(&packet);
    }
    
    return true; // true to play impact fx
  }

  return false;
}

//---------------------------------------------------- cGameEffect_AddEssence 
cGameEffect_AddEssence::cGameEffect_AddEssence() :
cGameEffect(),
mAmt(0)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_AddEssence::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_AddEssence", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_Amt,SyMemberOffset(cGameEffect_AddEssence,mAmt),"mAmt");

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_AddEssence::OnEnter(tGameObjectID target_id)
{
  cGameObject *target_obj = Titan::Get()->GetRegistry()->Fetch(target_id);

  if (target_obj == NULL ||
      target_obj->IsRemote() ||
      !TargetMatchesRace(target_obj) ||
      (target_obj->GetType() != cGameObject::OBJ_NPC &&
       target_obj->GetType() != cGameObject::OBJ_PLAYER))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    if (!TargetHasBlocked(target_obj))
    {
      cCalcPacket packet;
      packet.SetTotal(mAmt,"Effect","Area");

      cStatsCharacter* pTargetCharStats = prop_cast<cStatsCharacter*>(target_obj->GetStats());

      if (pTargetCharStats)
      {
        if (prop_cast<cAreaEffect_PowerUp*>(mpOwner) != NULL)
        {
          cCalcPacket bonusEssenceAbsorb;
          bonusEssenceAbsorb.mPacketType = cRulePacket::CALC_ESSENCE_ABSORB;
          pTargetCharStats->ProcessRulePacket(&bonusEssenceAbsorb);
          packet.MultiplyTotal(1.0f + ((float)bonusEssenceAbsorb.GetTotal()) * 0.01f, "Effect", "Area");
        }

        pTargetCharStats->AddEssence(&packet);
      }
    }

    return true; // true to play impact fx
  }

  return false;
}


//---------------------------------------------------- cGameEffect_Kill 
cGameEffect_Kill::cGameEffect_Kill()
: cGameEffect()
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Kill::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Kill", 
    Creator, 
    mCLASSID, 
    0 ); 

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_Kill::OnEnter(tGameObjectID target_id)
{
  cGameObject *pTarget = Titan::Get()->GetRegistry()->Fetch(target_id);
  if (pTarget == NULL || 
      pTarget->IsRemote() ||
      !TargetMatchesRace(pTarget) ||
      (pTarget->GetType() != cGameObject::OBJ_NPC &&
       pTarget->GetType() != cGameObject::OBJ_PLAYER))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance &&
      !pTarget->GetStats()->QueryFlag("Kill Immunity"))
  {
    if (!TargetHasBlocked(pTarget))
    {
      pTarget->GetStats()->Die(mSource);
      
      cGraphicCharacter *pCharGraphic = prop_cast<cGraphicCharacter*>(pTarget->GetGraphic());

      if (pCharGraphic)
      {
        pCharGraphic->GetAnimController()->GetInput()->mDeath = true;
        pCharGraphic->GetAnimController()->GetInput()->mHitReactTarget = mSource;
      }
    }

    return true;
  }

  return false;
}

//---------------------------------------------------- cGameEffect_Attack 


cGameEffect_Attack::cGameEffect_Attack() :
  cGameEffect(),
  mAttackIndex(0)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Attack::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Attack", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_AttackIndex,SyMemberOffset(cGameEffect_Attack,mAttackIndex),"mAttackIndex");

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_Attack::OnEnter(tGameObjectID target_id)
{
  cGameObject *pObj = Titan::Get()->GetRegistry()->Fetch(mSource);
  cGameObject *pTarget = Titan::Get()->GetRegistry()->Fetch(target_id);

  if (pObj == NULL ||
      pTarget == NULL ||
      !TargetMatchesRace(pTarget))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    SyAssert(prop_cast<cStatsCharacter*>(pObj->GetStats())!=NULL);
    ((cStatsCharacter *)pObj->GetStats())->AttackFrame(target_id, (eComboType)mAttackIndex, true);
    
    return true; // true to play impact fx
  }

  return false;
}


void  
cGameEffect_Attack::Update(float time)
{
  mpOwner->Explosion();
}

//------------------------------------------------------ cGameEffect_Knockback

cGameEffect_Knockback::cGameEffect_Knockback() :
  cGameEffect(),
    mLocation(0.0f, 0.0f, 0.0f),
    mXZAmount(8.0f),
    mYAmount(3.5f),
    mbKnockdown(true),
    mKnockdownTime(0.0f)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Knockback::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Knockback", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_Location,SyMemberOffset(cGameEffect_Knockback,mLocation),"mLocation");
  AddFloat32Property(mCLASSID,PropId_XZAmount,SyMemberOffset(cGameEffect_Knockback,mXZAmount),"mXZAmount");
  AddFloat32Property(mCLASSID,PropId_YAmount,SyMemberOffset(cGameEffect_Knockback,mYAmount),"mYAmount");
  AddBoolProperty(mCLASSID,PropId_Knockdown,SyMemberOffset(cGameEffect_Knockback,mbKnockdown),"mbKnockdown");
  AddFloat32Property(mCLASSID,PropId_KnockdownTime,SyMemberOffset(cGameEffect_Knockback,mKnockdownTime),"mKnockdownTime");

  return 0;
}

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

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

  return(pObject);
}

void  
cGameEffect_Knockback::Update(float time)
{
  mpOwner->Explosion();
}

bool  
cGameEffect_Knockback::OnEnter(tGameObjectID target_id)
{
  cGameObject *target_obj = Titan::Get()->GetRegistry()->Fetch(target_id);

  if (target_obj == NULL ||
      target_obj->IsRemote() ||
      !TargetMatchesRace(target_obj))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    SyVect3 loc = mLocation;

    if (loc.Magnitude() == 0.0f)
    {
      cGameObject *pSource = Titan::Get()->GetRegistry()->Fetch(mpOwner->GetSource());
      if (pSource)
      {
        loc = pSource->GetLocation();
      }
    }

    bool bIsBlocking = false;
    bool bDown = mbKnockdown;
    
    if (mpOwner && mpOwner->GetSpellID() != ID_NONE)
    {
      bIsBlocking = TargetHasBlocked(target_obj);

      if (bDown && bIsBlocking)
      {
        bDown = false;
      }
    }
    else
    {
      cGraphicCharacter* pTargetCharGraphic = prop_cast<cGraphicCharacter*>(target_obj->GetGraphic());
      bIsBlocking = pTargetCharGraphic ? pTargetCharGraphic->GetAnimController()->IsBlocking() : false;
    }

    if (bIsBlocking && !bDown)
    {
      cPhysicsAnimated* pTargetCharPhysics = prop_cast<cPhysicsAnimated*>(target_obj->GetPhysics());

      if (pTargetCharPhysics != NULL)
      {
        SyVect3 towards(target_obj->GetLocation() - loc);
        towards.Y = 0.0f;
        towards.Normalize();
        pTargetCharPhysics->Impact(towards, mXZAmount*0.75f);
      }
    }
    else
    {
      float mult = bIsBlocking ? 0.75f : 1.0f;
      target_obj->GetPhysics()->Knockback(loc, mXZAmount*mult, mYAmount*mult, bDown, mKnockdownTime);
    }

    return true;
  }

  return false;
}

//------------------------------------------------------ cGameEffect_Vortex

cGameEffect_Vortex::cGameEffect_Vortex()
: cGameEffect(),
  mLocation(0.0f, 0.0f, 0.0f),
  mSpeed(5.0f)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Vortex::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Vortex", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_Location,SyMemberOffset(cGameEffect_Vortex,mLocation),"mLocation");
  AddFloat32Property(mCLASSID,PropId_Speed,SyMemberOffset(cGameEffect_Vortex,mSpeed),"mSpeed");

  return 0;
}

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

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

  return(pObject);
}

void  
cGameEffect_Vortex::Update(tGameObjectID id, float time)
{
  cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(id);

  if (!pObj ||
      pObj->IsRemote() ||
      !TargetMatchesRace(pObj) ||
      pObj->GetStats()->QueryFlag("Vortex Immunity") ||
      TargetHasBlocked(pObj))
  {
    return;
  }

  cStatsProp* pPropStats = prop_cast<cStatsProp*>(pObj->GetStats());

  if (pPropStats && !pPropStats->IsSimulatable())
  {
    return;
  }

  SyVect3 dir(mLocation-pObj->GetLocation());
  float dist = dir.NormalizeMagn();

  if (dist >= pObj->GetPhysics()->GetCollisionRadius())
  {
    float moveDist = mSpeed * time;
    moveDist = SY_MIN(moveDist, (dist-pObj->GetPhysics()->GetCollisionRadius()));
    dir *= moveDist;
    pObj->SetLocation(pObj->GetLocation()+dir);
  }
}



//------------------------------------------------------ cGameEffect_AddCondition

cGameEffect_AddCondition::cGameEffect_AddCondition()
: cGameEffect(),
  mConditionName(0),
  mSpellID(ID_NONE),
  mDuration(0.0f),
  mEffectID(ID_NONE),
  mStrength(0),
  mParam(0),
  mbAreaOnly(false),
  mItemMasterID(ID_NONE)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_AddCondition::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_AddCondition", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddStringProperty(mCLASSID, PropId_ConditionName,SyMemberOffset(cGameEffect_AddCondition,mConditionName),"mConditionName");
  AddInt32Property(mCLASSID,PropId_SpellID,SyMemberOffset(cGameEffect_AddCondition,mSpellID),"mSpellID");
  AddFloat32Property(mCLASSID,PropId_Duration,SyMemberOffset(cGameEffect_AddCondition,mDuration),"mDuration");
  AddInt32Property(mCLASSID,PropId_EffectID,SyMemberOffset(cGameEffect_AddCondition,mEffectID),"mEffectID");
  AddInt32Property(mCLASSID,PropId_Strength,SyMemberOffset(cGameEffect_AddCondition,mStrength),"mStrength");
  AddInt32Property(mCLASSID,PropId_Param,SyMemberOffset(cGameEffect_AddCondition,mParam),"mParam");
  AddBoolProperty(mCLASSID,PropId_AreaOnly,SyMemberOffset(cGameEffect_AddCondition,mbAreaOnly),"mbAreaOnly");
  AddInt32Property(mCLASSID,PropId_ItemMasterID,SyMemberOffset(cGameEffect_AddCondition,mItemMasterID),"mItemMasterID");

  return 0;
}

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

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

  return(pObject);
}

void
cGameEffect_AddCondition::SetCondition(const char* name,
                                       tGameID spellID,
                                       tGameID itemMasterID,
                                       bool bAreaOnly,
                                       float duration,
                                       tGameID effectID,
                                       int strength,
                                       int param)
{
  SyAssertf(name && strlen(name)<31, "Bad Condition name %s", name);

  mConditionName = name;
  mSpellID = spellID;
  mDuration = duration;
  mEffectID = effectID;
  mStrength = strength;
  mParam = param;
  mbAreaOnly = bAreaOnly;
  mItemMasterID = itemMasterID;
}

bool
cGameEffect_AddCondition::OnEnter(tGameObjectID id)
{
  cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(id);

  if (!pObj ||
      pObj->IsRemote() ||
      !TargetMatchesRace(pObj))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    if (!TargetHasBlocked(pObj))

    {
      pObj->GetStats()->AddCondition(mConditionName.AsChar(), mSpellID, mSource, mItemMasterID, mDuration, mEffectID, mStrength, mParam);
    }

    return true; // true to play impact fx
  }

  return false;
}

void cGameEffect_AddCondition::OnExit(tGameObjectID id)
{
  if (mbAreaOnly)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(id);

    if (pObj && !pObj->IsRemote())
    {
      pObj->GetStats()->RemoveCondition(mConditionName.AsChar(), 0, mSource, mItemMasterID);
    }
  }
}

//------------------------------------------------------ cGameEffect_Nudge

cGameEffect_Nudge::cGameEffect_Nudge()
: cGameEffect()
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Nudge::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Nudge", 
    Creator, 
    mCLASSID, 
    0 ); 

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_Nudge::OnEnter(tGameObjectID id)
{
  cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(id);
  pObj->GetPhysics()->Nudge();
  return true;
}

//---------------------------------------------------- cGameEffect_Resurrect 
cGameEffect_Resurrect::cGameEffect_Resurrect()
: cGameEffect()
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_Resurrect::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Resurrect", 
    Creator, 
    mCLASSID, 
    0 ); 

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_Resurrect::OnEnter(tGameObjectID target_id)
{
  cGameObject *pTarget = Titan::Get()->GetRegistry()->Fetch(target_id);
  if (pTarget == NULL ||
      pTarget->IsRemote() ||
      !pTarget->GetStats()->IsDead())
  {
    return false;
  }

  if (mPercentChance > 0 && Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    pTarget->GetStats()->AddCondition("Resurrecting", mpOwner->GetSpellID(), mpOwner->GetSource(), false,0.25f, ID_NONE);
    pTarget->GetTitan()->GetDebrisSys()->ReForm(target_id, 0.35f);

    return true;
  }

  return false;
}


//------------------------------------------------------ cGameEffect_Levitate

cGameEffect_Levitate::cGameEffect_Levitate() 
{
};


void  
cGameEffect_Levitate::Update(float time)
{
  mpOwner->Levitate();
}; 

 // Reflection Support via SyPropObject
int
cGameEffect_Levitate::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_Levitate", 
    Creator, 
    mCLASSID, 
    0 ); 

  return 0;
}

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

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

  return(pObject);
};

//---------------------------------------------------- cGameEffect_CastSpell 
cGameEffect_CastSpell::cGameEffect_CastSpell()
: cGameEffect(),
  mSpellID(ID_NONE)
{
  InitPropObject( mCLASSID );
}

int
cGameEffect_CastSpell::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cGameEffect::mCLASSID,
    mCLASSID,
    "cGameEffect_CastSpell", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_SpellID,SyMemberOffset(cGameEffect_CastSpell,mSpellID),"mSpellID");

  return 0;
}

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

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

  return(pObject);
}

bool  
cGameEffect_CastSpell::OnEnter(tGameObjectID target_id)
{
  SyAssert(mSpellID != ID_NONE);
  cGameObject *pTarget = Titan::Get()->GetRegistry()->Fetch(target_id);
  if (pTarget == NULL ||
      pTarget->IsRemote() ||
      !TargetMatchesRace(pTarget))
  {
    return false;
  }

  if (mPercentChance > 0 &&
      Titan::Get()->Random(1, 100) <= mPercentChance)
  {
    if (!TargetHasBlocked(pTarget))
    {
      SyAssertf(mpOwner!=NULL, "cGameEffect_CastSpell does not have area effect owner");
      const cSpellMaster* pSpell = pTarget->GetTitan()->GetDatabaseSys()->GetSpellMaster(mSpellID);
      cGameObject* pSource = pTarget->GetTitan()->GetRegistry()->Fetch(mpOwner->GetSource());

      SyAssert(pSpell);

      if (pSpell && pSource)
      {
        pSpell->CastSpell(pSource, pTarget);
      }
    }

    return true; // true to play impact fx
  }

  return false;
}


//------------------------------------------------------ cAreaEffect


cAreaEffect::cAreaEffect(): 
  mSource(ID_NONE),
  mSpellID(ID_NONE),
  mEffectTargets(EFFECT_FACTION_ANY),
  mID(0),
  mDuration(-1.0f),
  mLevitationID(0),
  mMaxTargets(0),
  mbTerminateOnSourceDeath(false),
  mbIgnoreSource(true),
  mbExplodedThisFrame(false),
  mbMarkedForDelete(false),
  mMovesWithObject(ID_NONE),
  mMovesWithFwdOffset(0.0f),
  mFXHandle(-1)
{
  InitPropObject( mCLASSID );

  mBirthtime = cAreaEffectSys::Get()->GetTotalTime();
}

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

  if (0 != mLevitationID)
  {
    Titan::Get()->GetDebrisSys()->EndLevitation(mLevitationID);
  }

  if (-1 != mFXHandle)
  {
    Titan::Get()->GetScene()->GetFXScriptSystem()->StopPlayback(mFXHandle);
    mFXHandle = -1;
  }
}

int
cAreaEffect::InitPropClass()
{
  AddClass( mCLASSID, 
    "cAreaEffect", 
    NULL,     // CRO: abstract base class has no creator
    mCLASSID, 
    0 ); 

  AddVectorProperty(mCLASSID,PropId_Included,SyMemberOffset(cAreaEffect,mIncluded),"mIncluded",SYPROPTYPE_UINT32);
  AddSubObjectPtrVectorProperty<cGameEffect>(mCLASSID, PropId_Effects, SyMemberOffset(cAreaEffect,mEffects), "mEffects");
  AddInt32Property(mCLASSID,PropId_Source,SyMemberOffset(cAreaEffect,mSource),"mSource");
  AddInt32Property(mCLASSID,PropId_SpellID,SyMemberOffset(cAreaEffect,mSpellID),"mSpellID");

  SyPropEnum *propEnum;
  AddEnumProperty(mCLASSID,PropId_EffectTargets,SyMemberOffset(cAreaEffect,mEffectTargets),"mEffectTargets",&propEnum);
  propEnum->Add(EFFECT_FACTION_ANY,"EFFECT_FACTION_ANY");
  propEnum->Add(EFFECT_FACTION_ALLIES,"EFFECT_FACTION_ALLIES");
  propEnum->Add(EFFECT_FACTION_ALLIES_AND_PROPS,"EFFECT_FACTION_ALLIES_AND_PROPS");
  propEnum->Add(EFFECT_FACTION_PLAYERS,"EFFECT_FACTION_PLAYERS");
  propEnum->Add(EFFECT_FACTION_MONSTERS,"EFFECT_FACTION_MONSTERS");
  propEnum->Add(EFFECT_FACTION_MONSTERS_AND_PROPS,"EFFECT_FACTION_MONSTERS_AND_PROPS");
  propEnum->Add(EFFECT_FACTION_CHARACTERS,"EFFECT_FACTION_CHARACTERS");

  AddInt32Property(mCLASSID,PropId_ID,SyMemberOffset(cAreaEffect,mID),"mID");
  AddFloat32Property(mCLASSID,PropId_Duration,SyMemberOffset(cAreaEffect,mDuration),"mDuration");
  AddInt32Property(mCLASSID,PropId_MaxTargets,SyMemberOffset(cAreaEffect,mMaxTargets),"mMaxTargets");
  AddBoolProperty(mCLASSID, PropId_TerminateOnSourceDeath,SyMemberOffset(cAreaEffect,mbTerminateOnSourceDeath),"mbTerminateOnSourceDeath");
  AddBoolProperty(mCLASSID, PropId_IgnoreSource,SyMemberOffset(cAreaEffect,mbIgnoreSource),"mbIgnoreSource");
  AddBoolProperty(mCLASSID,PropId_Explosion,SyMemberOffset(cAreaEffect,mbExplodedThisFrame),"mbExplodedThisFrame");
  AddInt32Property(mCLASSID,PropId_MovesWithObject,SyMemberOffset(cAreaEffect,mMovesWithObject),"mMovesWithObject");
  AddFloat32Property(mCLASSID,PropId_MovesWithForwardOffset,SyMemberOffset(cAreaEffect,mMovesWithFwdOffset),"mMovesWithFwdOffset");

  return 0;
}


void 
cAreaEffect::AddGameEffect(cGameEffect *effect) 
{
  effect->SetOwner(this);
  mEffects.Add(effect);
}

void 
cAreaEffect::SetMovesWithObject(tGameObjectID id,
                                       float forwardOffset)
{
  mMovesWithObject = id;
  mMovesWithFwdOffset = forwardOffset;
  UpdateMovesWithObject();
}

bool
cAreaEffect::IsExpired() 
{  
  if (mbTerminateOnSourceDeath)
  {
    cGameObject* pSource = Titan::Get()->GetRegistry()->Fetch(mSource);
    return !pSource || pSource->GetStats()->IsDead() || mDuration == 0.0f;
  }

  return mDuration == 0.0f;
}

void 
cAreaEffect::Update(float time)
{
  mbExplodedThisFrame = false;

  UpdateMovesWithObject();

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

  UpdateExtents();

  if (mDuration > 0.0f)
  {
    mDuration -= time;

    if (mDuration < 0.0f)
    {
      mDuration = 0.0f;
    }
  }

  if (-1 == mFXHandle && ID_NONE != mSpellID)
  {
    const cSpellMaster* pSpell = Titan::Get()->GetDatabaseSys()->GetSpellMaster(mSpellID);

    if (pSpell && ID_NONE != pSpell->mAreaFXID)
    {
      int resHandle = -1;
      if (Titan::Get()->GetScene()->GetDictionary()->FindTyped(pSpell->mAreaFXID, SYRESOURCETYPE_FXSCRIPT, resHandle))
      {
        if (ID_NONE != mMovesWithObject)
        {
          cGameObject* pMovesWith = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);
          if (pMovesWith)
          {
            SyActorHandle actorHandle = pMovesWith->GetGraphic()->GetActorHandle();

            if (SyActorNull != actorHandle)
            {
              mFXHandle = Titan::Get()->GetScene()->GetFXScriptSystem()->PlayScript(resHandle, 1, &actorHandle);
            }
          }
        }
        else
        {
          mFXHandle = Titan::Get()->GetScene()->GetFXScriptSystem()->PlayScriptAtLocation(resHandle, GetCenter(), SyVect3(0.0f, 0.0f, 0.0f), 1.0f);
        }
      }
    }
  }
}

void  
cAreaEffect::UpdateExtents()
{
  SyVect3 loc = GetCenter();
  float radius = GetRadius();

  if (radius <= 0.0f)
  {
    radius = 0.01f;
  }

  mMaxX = loc.X + radius;
  mMinX = loc.X - radius;

  cAreaEffectSys * pSys = cAreaEffectSys::Get();

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

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

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

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

void 
cAreaEffect::AddObject(cGameObject *pCurObj)
{
  if (pCurObj==NULL) 
  {
    return;
  }

  if (IgnoreSource() && pCurObj->GetID() == mSource)
  {
    return;
  }

  if (!pCurObj->GetIntel()->IsSpawned())
  {
    return;
  }

  if (!IsTargetedFaction(pCurObj))
  {
    return;
  }

  if (!IsInside(pCurObj))
  {
    return;
  }

  if (mMaxTargets > 0 && mIncluded.Size() >= mMaxTargets)
  {
    return;
  }

  if (!IsIncluded(pCurObj))
  {
    OnEnter(pCurObj);
    mIncluded.Add(pCurObj->GetID());
  }
}


void 
cAreaEffect::AddAllOpenObjects()
{
  //  Use scene query to get proximal actors
  float radius = GetRadius(); 
  cGameObjectRegistry *pRegistry = Titan::Get()->GetRegistry();

  if (radius > 0.0f) // if radius < 0, it's a single actor effect or not one we have to iterate over
  {
   cGameObject *pCurObj = pRegistry->BeginOpen();

   for (;pCurObj!=NULL;pCurObj=pRegistry->NextOpen(pCurObj))
   {
     AddObject(pCurObj);
   }
  }
}

void
cAreaEffect::UpdateIncluded(float time)
{
  for (int ii=mIncluded.Begin();ii!=mIncluded.End();)
  {
    cGameObjectRegistry *pRegistry = Titan::Get()->GetRegistry();
    cGameObject *obj = pRegistry->Fetch(mIncluded(ii));
    if (obj == NULL || !IsInside(obj))
    {
      if (obj != NULL)
      {
        OnExit(obj);
      }
      ii=mIncluded.ReplaceLast(ii); 
    }
    else
    {
      if (obj != NULL)
      {
        UpdateEffect(obj,time);
      }
      ii= mIncluded.Next(ii);
    }
  }
}

bool
cAreaEffect::IsTargetedFaction(cGameObject *obj)
{
  if (EFFECT_FACTION_ANY == mEffectTargets)
  {
    return true;
  }
  else
  {
    cGameObject::tObjectType type = obj->GetType();
    cGameObject* pSource = obj->GetRegistry()->Fetch(mSource);

    if (EFFECT_FACTION_CHARACTERS == mEffectTargets)
    {
      return cGameObject::OBJ_NPC == type || cGameObject::OBJ_PLAYER == type;
    }
    else if (cGameObject::OBJ_NPC == type)
    {
      if (static_cast<cStatsCharacter*>(obj->GetStats())->GetFaction() == NPCFACTION_ALLY &&
          (EFFECT_FACTION_ALLIES == mEffectTargets || EFFECT_FACTION_ALLIES_AND_PROPS == mEffectTargets))
      {
        return true;
      }
      else if (static_cast<cStatsCharacter*>(obj->GetStats())->GetFaction() == NPCFACTION_MONSTER &&
               (EFFECT_FACTION_MONSTERS == mEffectTargets || EFFECT_FACTION_MONSTERS_AND_PROPS == mEffectTargets))
      {
        return true;
      }
      else
      {
        return false;
      }
    }
    else if (cGameObject::OBJ_PLAYER == type &&
            (EFFECT_FACTION_ALLIES == mEffectTargets || 
             EFFECT_FACTION_PLAYERS == mEffectTargets ||
             EFFECT_FACTION_ALLIES_AND_PROPS == mEffectTargets ||
             (pSource && pSource->GetType()==cGameObject::OBJ_PLAYER && pSource->GetTitan()->GetPvPEnabled())))
    {
      return true;
    }
    else if (cGameObject::OBJ_PROP == type && 
             (EFFECT_FACTION_ANY == mEffectTargets ||
              EFFECT_FACTION_MONSTERS_AND_PROPS == mEffectTargets ||
              EFFECT_FACTION_ALLIES_AND_PROPS == mEffectTargets))
    {
      SyAssert(prop_cast<cStatsProp*>(obj->GetStats())!=NULL);
      return true;
    }
  }

  return false;
}

void
cAreaEffect::Explosion()
{
  if (!mbExplodedThisFrame)
  {
    // find fx to attach to debris
    tGameID debrisFXID = ID_NONE;

    if (ID_NONE != mSpellID)
    {
      const cSpellMaster* pSpell = Titan::Get()->GetDatabaseSys()->GetSpellMaster(mSpellID);
      SyAssert(pSpell!=NULL);

      if (pSpell && ID_NONE != pSpell->mImpactFXSetID)
      {
        const cImpactFXSet* pImpacts = Titan::Get()->GetDatabaseSys()->GetImpactFXSet(pSpell->mImpactFXSetID);
        SyAssert(pImpacts);

        if (pImpacts)
        {
          debrisFXID = pImpacts->mDebrisFXID;
        }
      }
    }

    DoExplosion(debrisFXID);
    mbExplodedThisFrame = true;
  }
}

void  
cAreaEffect::OnEnter(cGameObject* obj)
{
  bool bImpact = false;

  for (int ii = mEffects.Begin();ii!=mEffects.End();ii=mEffects.Next(ii))
  {
    if (mEffects(ii)->OnEnter(obj->GetID()))
    {
      bImpact = true;
    }
  }

  if (bImpact
    && ID_NONE != mSpellID)
  {
    // script callback:
    static const int32 ID_LastSpellAreaEntered = SyHashResourceID("LAST_SPELL_AREA_ENTERED");
    obj->GetStats()->Value_Set(ID_LastSpellAreaEntered, mSpellID);

    Titan::Get()->GetScriptSys()->ScriptEvent( PET_SPELL_AREA_ENTER, obj->GetID(), 0 );

    PlayImpactFX(obj);
  }
}

void cAreaEffect::PlayImpactFX(cGameObject* pTarget)
{
  const cSpellMaster* pSpell = pTarget->GetTitan()->GetDatabaseSys()->GetSpellMaster(mSpellID);
  SyAssert(pSpell!=NULL);

  if (pSpell)
  {
    pSpell->PlayImpact(pTarget);
  }
}

void  
cAreaEffect::OnExit(cGameObject* obj)
{
  for (int ii = mEffects.Begin();ii!=mEffects.End();ii=mEffects.Next(ii))
  {
    mEffects(ii)->OnExit(obj->GetID());
  }

  if (ID_NONE != mSpellID)
  {
    // script callback:
    static const int32 ID_LastSpellAreaLeft = SyHashResourceID("LAST_SPELL_AREA_LEFT");
    obj->GetStats()->Value_Set(ID_LastSpellAreaLeft, mSpellID);

    Titan::Get()->GetScriptSys()->ScriptEvent( PET_SPELL_AREA_EXIT, obj->GetID(), 0 );
  }
}

void
cAreaEffect::ExitAll()
{
  for (int ii = mEffects.Begin();ii!=mEffects.End();ii=mEffects.Next(ii))
  {
    for (int jj=mIncluded.Begin();jj!=mIncluded.End();jj=mIncluded.Next(jj))
    {
      mEffects(ii)->OnExit(mIncluded(jj));
    }
  }
}

void  
cAreaEffect::UpdateEffect(cGameObject* obj,float time)
{
  for (int ii = mEffects.Begin();ii!=mEffects.End();ii=mEffects.Next(ii))
  {
    mEffects(ii)->Update(obj->GetID(),time);
  }
};

bool  
cAreaEffect::IsIncluded(cGameObject *obj)
{
  for (int ii=mIncluded.Begin();ii!=mIncluded.End();ii= mIncluded.Next(ii))
  {
    if (mIncluded(ii)==obj->GetID())
    {
      return true;
    }
  }
  return false;
}

void cAreaEffect::ProcessPacket(cRulePacket *packet)
{
  SyAssert(packet);
  cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(packet->mObjectID);

  if (pObj && IsIncluded(pObj))
  {
    for (int ii = mEffects.Begin();ii!=mEffects.End();ii=mEffects.Next(ii))
    {
      mEffects(ii)->ProcessPacket(packet);
    }
  }
}

//------------------------------------------------------------ cAreaEffect_Radius  

cAreaEffect_Radius::cAreaEffect_Radius() :
 cAreaEffect(),
 mRadius(0.0f),
 mbNotifyAI(false)
{
  InitPropObject( mCLASSID );
}

cAreaEffect_Radius::~cAreaEffect_Radius()
{
  if (mbNotifyAI)
  {
    cAIBlackboard::Get()->RemoveRecords(cAIBlackboard::BBR_NEGATIVE_AREAEFFECT, GetSource());
  }
}

int cAreaEffect_Radius::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_Radius", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_Location,SyMemberOffset(cAreaEffect_Radius,mLocation),"mLocation");
  AddFloat32Property(mCLASSID,PropId_Radius,SyMemberOffset(cAreaEffect_Radius,mRadius),"mRadius");
  AddBoolProperty(mCLASSID,PropId_NotifyAI,SyMemberOffset(cAreaEffect_Radius,mbNotifyAI),"mbNotifyAI");

  return 0;
}

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

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

  return(pObject);
}

bool  
cAreaEffect_Radius::IsInside(cGameObject *obj)
{         
  SyVect3 toObj(obj->GetLocation()-mLocation);
  toObj.Normalize();

  float dist = obj->GetDistanceInDirection(mLocation, toObj); // includes collision radius in test

  return (dist < mRadius);
}

void 
cAreaEffect_Radius::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mLocation = pObj->GetLocation();
      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mLocation.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mLocation.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mLocation.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }

      if (mbNotifyAI)
      {
        cAIBlackboard::Get()->AddRecord(cAIBlackboard::BBR_NEGATIVE_AREAEFFECT, GetSource(), ID_NONE, (unsigned int)(mRadius), mLocation);
      }
    }
  }
}

void
cAreaEffect_Radius::Update(float time)
{
  DEBUGOVERLAY_DRAWCYLINDER(mSource, "AreaEffect", mLocation, mRadius, SyVect3(0.0f, 3.0f, 0.0f), cDebugOverlay::PURPLE);

  cAreaEffect::Update(time);
}


void 
cAreaEffect_Radius::DoExplosion(tGameID debrisFXID) 
{
  Titan::Get()->GetDebrisSys()->Explosion(mLocation, mRadius+0.5f, mBirthtime, debrisFXID);
}

void 
cAreaEffect_Radius::Levitate() // levitate debris; called from effect 
{
  if (mLevitationID == 0)
  {
    mLevitationID = Titan::Get()->GetDebrisSys()->allocateLevitation();
  }
  Titan::Get()->GetDebrisSys()->StartLevitation(mLocation,mRadius,mLevitationID);
}

void cAreaEffect_Radius::SetAINegativeAreaEffect(bool bNotifyAI)
{
  if (mbNotifyAI != bNotifyAI)
  {
    mbNotifyAI = bNotifyAI;

    if (mbNotifyAI)
    {
      cAIBlackboard::Get()->AddRecord(cAIBlackboard::BBR_NEGATIVE_AREAEFFECT, GetSource(), ID_NONE, (unsigned int)(mRadius), mLocation);
    }
    else
    {
      cAIBlackboard::Get()->RemoveRecords(cAIBlackboard::BBR_NEGATIVE_AREAEFFECT, GetSource());
    }
  }
}

bool cAreaEffect_Radius::IsLineOfSightBlocked(const SyVect3& start, const SyVect3& end, SyVect3& hit)
{
  if (!mbNotifyAI)
  {
    return false;
  }
  else
  {
    float a = (end.X - start.X)*(end.X - start.X) + (end.Y - start.Y)*(end.Y - start.Y) + (end.Z - start.Z)*(end.Z - start.Z);
    float b = 2.0f*((end.X - start.X)*(start.X - mLocation.X) + (end.Y - start.Y)*(start.Y - mLocation.Y) + (end.Z - start.Z)*(start.Z - mLocation.Z));
    float c = mLocation.X*mLocation.X + mLocation.Y*mLocation.Y + mLocation.Z*mLocation.Z + start.X*start.X + start.Y*start.Y + start.Z*start.Z - 2.0f*(mLocation.X*start.X + mLocation.Y*start.Y + mLocation.Z*start.Z) - (mRadius*mRadius); 

    return (b*b - 4*a*c) > 0;
  }
}

//------------------------------------------------------------ cAreaEffect_PowerUp  

cAreaEffect_PowerUp::cAreaEffect_PowerUp() :
cAreaEffect_Radius(),
mProjectileID(ID_NONE),
mbTestHealth(false),
mbTestMana(false),
mbTestEssence(false),
mbUsed(false)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_PowerUp::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect_Radius::mCLASSID,
    mCLASSID,
    "cAreaEffect_PowerUp", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_ProjectileID,SyMemberOffset(cAreaEffect_PowerUp,mProjectileID),"mProjectileID");
  AddBoolProperty(mCLASSID,PropId_TestHealth,SyMemberOffset(cAreaEffect_PowerUp,mbTestHealth),"mbTestHealth");
  AddBoolProperty(mCLASSID,PropId_TestMana,SyMemberOffset(cAreaEffect_PowerUp,mbTestMana),"mbTestMana");
  AddBoolProperty(mCLASSID,PropId_TestEssence,SyMemberOffset(cAreaEffect_PowerUp,mbTestEssence),"mbTestEssence");
  AddBoolProperty(mCLASSID,PropId_Used,SyMemberOffset(cAreaEffect_PowerUp,mbUsed),"mbUsed");

  return 0;
}

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

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

  return(pObject);
}


bool  
cAreaEffect_PowerUp::IsExpired()
{
  bool bExpired = cAreaEffect_Radius::IsExpired() || mbUsed;

  cGameObject* pProj = Titan::Get()->GetRegistry()->Fetch(mProjectileID);

  if (pProj && bExpired)
  {
    pProj->GetStats()->Die(ID_NONE);
    static_cast<cPhysicsProjectile*>(pProj->GetPhysics())->SetLifetime(0.0f);
  }
  else if (ID_NONE != mProjectileID &&
           (!pProj || pProj->GetStats()->IsDead()))
  {
    bExpired = true;
  }

  return bExpired;
}


bool  
cAreaEffect_PowerUp::IsInside(cGameObject *obj)
{         
  cGameObject::tObjectType type = obj->GetType();

  if (type != cGameObject::OBJ_PLAYER)
  {
    return false;
  }

  SyVect3 delta (obj->GetLocation()-mLocation);

  if (delta.Y < 2.0f)
  {
    delta.Y = 0.0f;
  }

  float distance = delta.Magnitude() - obj->GetPhysics()->GetCollisionRadius();

  if (distance > mRadius)
  {
    return false;
  }

  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(obj->GetStats());

  if (mbTestHealth && pStats->GetHealth() == pStats->CalcMaxHealth())
  {
    return false;
  }

  if (mbTestMana && pStats->GetMana() == pStats->CalcMaxMana())
  {
    return false;
  }

// anybody can pick up more cash
//  if (mbTestEssence)
//  {
//    return false;
//  }

  if (!mbUsed)
  {
    mbUsed = true;

    cGameObject* pProj = obj->GetTitan()->GetRegistry()->Fetch(mMovesWithObject);
    if (pProj)
    {
      SyAssert(prop_cast<cStatsProjectile*>(pProj->GetStats())!=NULL);
      const cStatsProjectileMaster* pMaster = static_cast<cStatsProjectile*>(pProj->GetStats())->GetMaster();
      if (ID_NONE != pMaster->mImpactFXSetID)
      {
        const cImpactFXSet* pImpacts = obj->GetTitan()->GetDatabaseSys()->GetImpactFXSet(pMaster->mImpactFXSetID);
        SyAssert(pImpacts!=NULL);
        if (pImpacts)
        {
          pImpacts->PlayImpact(obj, true);
        }
      }   
    }
  }

  return true;
}

//------------------------------------------------------------ cAreaEffect_Oriented3DBoundingBox  

cAreaEffect_3DBBox::cAreaEffect_3DBBox()  : 
    cAreaEffect()
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_3DBBox::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_2DBBox", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_Min,SyMemberOffset(cAreaEffect_3DBBox,mMin),"mMin");
  AddVect3Property(mCLASSID,PropId_Max,SyMemberOffset(cAreaEffect_3DBBox,mMax),"mMax");

  return 0;
}

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

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

  return(pObject);
}

void
cAreaEffect_3DBBox::Update(float time)
{
  cAreaEffect::Update(time);

  DEBUGOVERLAY_DRAWBBOX(mSource, "AreaEffect", mMin, mMax, cDebugOverlay::ORANGE);
}

bool  
cAreaEffect_3DBBox::IsInside(cGameObject *obj)
{
  const SyVect3 &loc = obj->GetLocation();

  if ((loc.X < mMin.X) || (loc.X > mMax.X) || 
      (loc.Y < mMin.Y) || (loc.Y > mMax.Y) ||
      (loc.Z < mMin.Z) || (loc.Z > mMax.Z))
  {
    return false;
  }
  return true;
}

SyVect3 
cAreaEffect_3DBBox::GetCenter()
{
  SyVect3 center;
  center.Add(mMin,mMax);
  center.Mul(center,0.5f);
  return center;
};

float  
cAreaEffect_3DBBox::GetRadius()
{
  return (mMax.X - mMin.X)*0.5f;
}

//------------------------------------------------------------ cAreaEffect_Burst  

cAreaEffect_Burst::cAreaEffect_Burst()
 : cAreaEffect_Radius(),
   mSpeed(0.0f),
   mMaxRadius(1.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_Burst::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect_Radius::mCLASSID,
    mCLASSID,
    "cAreaEffect_Burst", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddFloat32Property(mCLASSID,PropId_Speed,SyMemberOffset(cAreaEffect_Burst,mSpeed),"mSpeed");
  AddFloat32Property(mCLASSID,PropId_MaxRadius,SyMemberOffset(cAreaEffect_Burst,mMaxRadius),"mMaxRadius");

  return 0;
}

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

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

  return(pObject);
}

void  
cAreaEffect_Burst::Update(float time)
{
  mRadius += time * mSpeed;

  if (mRadius > mMaxRadius)
  {
    mRadius = mMaxRadius;
  }

  cAreaEffect_Radius::Update(time);
}

bool  
cAreaEffect_Burst::IsExpired()
{
  return cAreaEffect::IsExpired() || mRadius == mMaxRadius;
}

//------------------------------------------------------------ cAreaEffect_Chain  
cAreaEffect_Chain::cAreaEffect_Chain()
: cAreaEffect(),
  mTargetDelay(1.0f),
  mTargetDelayTimer(1.0f),
  mMaxDist(5.0f),
  mNextTargetID(ID_NONE)
{
  InitPropObject( mCLASSID );
  mMaxTargets = 1;
}

int cAreaEffect_Chain::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_Chain", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddFloat32Property(mCLASSID,PropId_TargetDelay,SyMemberOffset(cAreaEffect_Chain,mTargetDelay),"mTargetDelay");
  AddFloat32Property(mCLASSID,PropId_TargetDelayTimer,SyMemberOffset(cAreaEffect_Chain,mTargetDelayTimer),"mTargetDelayTimer");
  AddFloat32Property(mCLASSID,PropId_MaxDist,SyMemberOffset(cAreaEffect_Chain,mMaxDist),"mMaxDist");
  AddInt32Property(mCLASSID,PropId_NextTargetID,SyMemberOffset(cAreaEffect_Chain,mNextTargetID),"mNextTargetID");

  return 0;
}

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

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

  return(pObject);
}

void
cAreaEffect_Chain::Update(float time)
{
  cAreaEffect::Update(time);

  mTargetDelayTimer += time;

  if ((mIncluded.Size() < mMaxTargets ) && (ID_NONE == mNextTargetID))
  {
    mNextTargetID = SelectNextTarget();

    if (mDuration < 0.0f && ID_NONE == mNextTargetID)
    {
      mDuration = 0.0f; // negative duration implies we end when we can't find anymore targets
    }
  }
  else if (mIncluded.Size() >= mMaxTargets && mDuration < 0.0f)
  {
    mDuration = 0.0f; // negative duration implies we end when we hit max targets
  }
}

bool
cAreaEffect_Chain::IsInside(cGameObject *obj)
{
  bool bInside = IsIncluded(obj);

  if (!bInside && mTargetDelayTimer >= mTargetDelay)
  {
    if (obj->GetID() == mNextTargetID)
    {
      mTargetDelayTimer = 0.0f;
      mNextTargetID = ID_NONE;
      return true;
    }
  }

  return bInside;
}

tGameObjectID cAreaEffect_Chain::SelectNextTarget()
{
  SyAssertf(ID_NONE != mSource, "Chain lightning effect needs a source");

  if (ID_NONE == mSource)
  {
    return ID_NONE;
  }

  cGameObjectRegistry* pRegistry = Titan::Get()->GetRegistry();
  cGameObject* pSourceObj = NULL;
  pSourceObj = Titan::Get()->GetRegistry()->Fetch(mSource);

  if (!pSourceObj)
  {
    return ID_NONE;
  }

  cIntel* pIntel = pSourceObj->GetIntel();

  // use last target for position tests if we have one
  if (mIncluded.Size() > 0)
  {
    pSourceObj = Titan::Get()->GetRegistry()->Fetch(mIncluded(mIncluded.Prev(mIncluded.End()))); 
    if (!pSourceObj)
    {
      return ID_NONE;   // must've been deleted
    }    
  }

  float dist, bestDist = 0.0f;
  cGameObject *best= NULL;

  // SGC:  This is a good candidate for optimization
  cGameObject* pCurObj = pRegistry->Begin();
  for(;pCurObj != NULL;pCurObj= pRegistry->Next(pCurObj))
  {

    if (!pCurObj || !pIntel->IsTargetable(pCurObj) || IsIncluded(pCurObj))
    {
      continue;
    }
    
    dist = pCurObj->GetDistance(pSourceObj);

    if (dist < mMaxDist &&
        (best == NULL || dist < bestDist))
    {
      bestDist = dist;
      best= pCurObj;
    }
  }

  if (best != NULL)
  {
    return best->GetID();
  }

  return ID_NONE;
}

SyVect3 
cAreaEffect_Chain::GetCenter()
{
  return SyVect3(0,0,0);
}

float  
cAreaEffect_Chain::GetRadius()
{
  return -1.0f;
}


//------------------------------------------------------------ cAreaEffect_Arc  

cAreaEffect_Arc::cAreaEffect_Arc() :
  cAreaEffect(),
  mSpeed(1.0f),
  mRadius(1.0f),
  mStart(0.0f),
  mEnd(0.0f),
  mCur(0.0f),
  mLength(SY_PI)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_Arc::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_Arc", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddFloat32Property(mCLASSID,PropId_Speed,SyMemberOffset(cAreaEffect_Arc,mSpeed),"mSpeed");
  AddFloat32Property(mCLASSID,PropId_Radius,SyMemberOffset(cAreaEffect_Arc,mRadius),"mRadius");
  AddFloat32Property(mCLASSID,PropId_Start,SyMemberOffset(cAreaEffect_Arc,mStart),"mStart");
  AddVect3Property(mCLASSID,PropId_Loc,SyMemberOffset(cAreaEffect_Arc,mLoc),"mLoc");
  AddFloat32Property(mCLASSID,PropId_End,SyMemberOffset(cAreaEffect_Arc,mEnd),"mEnd");
  AddFloat32Property(mCLASSID,PropId_Cur,SyMemberOffset(cAreaEffect_Arc,mCur),"mCur");
  AddFloat32Property(mCLASSID,PropId_Length,SyMemberOffset(cAreaEffect_Arc,mLength),"mLength");

  return 0;
}

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

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

  return(pObject);
}

void          
cAreaEffect_Arc::SetArc(const SyVect3 &loc,
                        float speed,
                        float radius,
                        float start,
                        float end,
                        float length)
{
  SyAssertf(speed > 0.0001f,"Bad arc speed");
  SyAssertf(length > 0.0001f,"Bad arc length");

  mSpeed = speed;
  mRadius = radius;
  mStart = start;
  mEnd = end;
  mCur = start;
  mLength = length;
  mLoc = loc;

  if (mStart > mEnd)
  {
    mSpeed = -mSpeed;
  }
}

void 
cAreaEffect_Arc::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mLoc = pObj->GetLocation();
      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mLoc.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mLoc.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mLoc.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }
    }
  }
}

void cAreaEffect_Arc::DoExplosion(tGameID debrisFXID) 
{
  SyVect3 center = mLoc;
  center.X += SY_SIN(mCur) * (mRadius*0.5f+0.2f);
  center.Z += SY_COS(mCur) * (mRadius*0.5f+0.2f);

  DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", center, mRadius*0.5f, cDebugOverlay::GRAY);
  Titan::Get()->GetDebrisSys()->Explosion(center, mRadius*0.5f, mBirthtime, debrisFXID);
}

void  
cAreaEffect_Arc::Update(float time)
{
  mCur += time * mSpeed;

  cAreaEffect::Update(time);

#ifdef _DRAWDEBUGOVERLAY
  float arcRemain = 0.0f;

  float trail = mLength;
  if (SY_FABS(mCur-mStart) < mLength)
  {
    trail = mSpeed > 0 ? mCur-mStart : -mStart+mCur;
  }

  SyVect3 start, end;
  start.X = SY_SIN( mCur );
  start.Y = 0.0f;
  start.Z = SY_COS( mCur );
  start *= mRadius;
  start += mLoc;
  start.Y += 1.0f;


  while (SY_FABS(arcRemain) <= SY_FABS(mCur-mStart) && 
         SY_FABS(arcRemain) <= mLength)
  {
    arcRemain += SY_DEG_TO_RAD((mSpeed>0 ? -10.0f : 10.0f));

    end.X = SY_SIN( mCur+arcRemain );
    end.Y = 0.0f;
    end.Z = SY_COS( mCur+arcRemain );
    end *= mRadius;
    end += mLoc;
    end.Y += 1.0f;

    DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::CYAN);
    start = end;
  }
#endif
}

bool  
cAreaEffect_Arc::IsExpired()
{
  if (cAreaEffect::IsExpired())
  {
    return true;
  }
  else if (mSpeed > 0)
  {
    return (mCur > mEnd);
  }
  else
  {
    return (mCur < mEnd);
  }
}

bool  
cAreaEffect_Arc::IsInside(cGameObject *obj)
{
  SyVect3 dir, hitPoint;
  dir.HPR(mCur, 0.0f, 0.0f);

  float dist = obj->GetDistanceInDirection(mLoc, dir, 0.5f, &hitPoint); // includes collision radius in test

  if (dist > mRadius)
  {
    return false;
  }

  SyVect3 delta(hitPoint - mLoc);
  float heading = SY_ATAN2(delta.X,delta.Z);
  float current = mCur;
  float trail = mLength;

  if (SY_FABS(mCur-mStart) < mLength)
  {
    trail = mSpeed > 0 ? mCur-mStart : mStart-mCur;
  }

  while (current >= SY_PI * 2.0f)
  {
    current -= SY_PI * 2.0f;
  }

  while (current <= -SY_PI * 2.0f)
  {
    current += SY_PI * 2.0f;
  }

  // need to normalize?
  if (mSpeed > 0)
  {
    if (heading <= current && 
        heading >= current - trail)
    {
      return true;
    }
  }
  else
  {    
    if (heading >= current &&
        heading <= current + trail)
    {
      return true;
    }
  }

  return false;
}


//------------------------------------------------------------ cAreaEffect_WaveAngle  

cAreaEffect_WaveAngle::cAreaEffect_WaveAngle() :
cAreaEffect(),
mStartLoc(0.0f, 0.0f, 0.0f),
mDir(1.0f, 0.0f, 0.0f),
mSpeed(0.0f),
mRange(0.0f),
mAngle(0.0f),
mWidth(0.0f),
mDist(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_WaveAngle::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_WaveAngle", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_StartLoc,SyMemberOffset(cAreaEffect_WaveAngle,mStartLoc),"mStartLoc");
  AddVect3Property(mCLASSID,PropId_Dir,SyMemberOffset(cAreaEffect_WaveAngle,mDir),"mDir");
  AddFloat32Property(mCLASSID,PropId_Speed,SyMemberOffset(cAreaEffect_WaveAngle,mSpeed),"mSpeed");
  AddFloat32Property(mCLASSID,PropId_Range,SyMemberOffset(cAreaEffect_WaveAngle,mRange),"mRange");
  AddFloat32Property(mCLASSID,PropId_Angle,SyMemberOffset(cAreaEffect_WaveAngle,mAngle),"mAngle");
  AddFloat32Property(mCLASSID,PropId_Width,SyMemberOffset(cAreaEffect_WaveAngle,mWidth),"mWidth");
  AddFloat32Property(mCLASSID,PropId_Dist,SyMemberOffset(cAreaEffect_WaveAngle,mDist),"mDist");

  return 0;
}

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

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

  return(pObject);
}

void          
cAreaEffect_WaveAngle::SetWave(const SyVect3& start, const SyVect3& dir, float speed, float range, float angle, float width)
{
  SyAssert(angle > 0.0f && angle <= SY_PI*2.0f);
  mStartLoc = start;
  mDir = dir;
  mSpeed = speed;
  mRange = range;
  mAngle = angle;
  mWidth = width;

  mDir.Normalize();
}

void cAreaEffect_WaveAngle::DoExplosion(tGameID debrisFXID) 
{
  SyVect3 center = mStartLoc;
  center.MulAdd(mDir, mDist);

  DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", center, mWidth*0.5f, cDebugOverlay::GRAY);
  Titan::Get()->GetDebrisSys()->Explosion(center, mWidth*0.5f, mBirthtime, debrisFXID);
}

void 
cAreaEffect_WaveAngle::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mStartLoc = pObj->GetLocation();
      mDir.HPR(pObj->GetHeading(), 0.0f, 0.0f);
      mDir.Normalize();
      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mStartLoc.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mStartLoc.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mStartLoc.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }
    }
  }
}

void  
cAreaEffect_WaveAngle::Update(float time)
{
  mDist += time * mSpeed;

  cAreaEffect::Update(time);

#ifdef _DRAWDEBUGOVERLAY
  SyVect3 start, end;
  float heading = SY_ATAN2(mDir.X, mDir.Z);

  start.X = SY_SIN( heading-(mAngle*0.5f) );
  start.Y = 0.0f;
  start.Z = SY_COS( heading-(mAngle*0.5f) );
  start *= mDist;
  start += mStartLoc;
  start.Y += 1.0f;

  end.X = SY_SIN( heading );
  end.Y = 0.0f;
  end.Z = SY_COS( heading );
  end *= mDist;
  end += mStartLoc;
  end.Y += 1.0f;

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::YELLOW);

  start = end;
  end.X = SY_SIN( heading+(mAngle*0.5f) );
  end.Y = 0.0f;
  end.Z = SY_COS( heading+(mAngle*0.5f) );
  end *= mDist;
  end += mStartLoc;
  end.Y += 1.0f;

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::YELLOW);
#endif
}

bool  
cAreaEffect_WaveAngle::IsExpired()
{
  return cAreaEffect::IsExpired() || mDist > mRange;
}

bool  
cAreaEffect_WaveAngle::IsInside(cGameObject *obj)
{
  SyVect3 dir, hitPoint;
  dir.HPR(mAngle, 0.0f, 0.0f);

  float dist = obj->GetDistanceInDirection(mStartLoc, dir, 0.5f, &hitPoint);

  if (dist > mDist)
  {
    return false;
  }

  SyVect3 toObj(hitPoint-mStartLoc);
  toObj.Normalize();
 
  return ((toObj^mDir) >= SY_COS((mAngle*0.5f)));
}

//------------------------------------------------------------ cAreaEffect_Beam  

cAreaEffect_Beam::cAreaEffect_Beam() :
cAreaEffect(),
mStartLoc(0.0f, 0.0f, 0.0f),
mForwardDir(1.0f, 0.0f, 0.0f),
mRightDir(0.0f, 0.0f, 1.0f),
mSpeed(0.0f),
mRange(0.0f),
mWidth(0.0f),
mDist(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_Beam::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_Beam", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_StartLoc,SyMemberOffset(cAreaEffect_Beam,mStartLoc),"mStartLoc");
  AddVect3Property(mCLASSID,PropId_ForwardDir,SyMemberOffset(cAreaEffect_Beam,mForwardDir),"mForwardDir");
  AddVect3Property(mCLASSID,PropId_RightDir,SyMemberOffset(cAreaEffect_Beam,mRightDir),"mRightDir");
  AddFloat32Property(mCLASSID,PropId_Speed,SyMemberOffset(cAreaEffect_Beam,mSpeed),"mSpeed");
  AddFloat32Property(mCLASSID,PropId_Range,SyMemberOffset(cAreaEffect_Beam,mRange),"mRange");
  AddFloat32Property(mCLASSID,PropId_Width,SyMemberOffset(cAreaEffect_Beam,mWidth),"mWidth");
  AddFloat32Property(mCLASSID,PropId_Dist,SyMemberOffset(cAreaEffect_Beam,mDist),"mDist");

  return 0;
}

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

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

  return(pObject);
}

void          
cAreaEffect_Beam::SetBeam(const SyVect3& start,
                          const SyVect3& dir,
                          float speed,
                          float range,
                          float width)
{
  mStartLoc = start;
  mForwardDir = dir;
  mSpeed = speed;
  mRange = range;
  mWidth = width;

  mForwardDir.Normalize();

  mRightDir.Cross(mForwardDir, SyVect3(0.0f, 1.0f, 0.0f));
  SyAssert(mRightDir.Magnitude() > 0.0f);
  mRightDir.Normalize();
}

void 
cAreaEffect_Beam::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mStartLoc = pObj->GetLocation();
      mForwardDir.HPR(pObj->GetHeading(), 0.0f, 0.0f);
      mForwardDir.Normalize();
      mRightDir.Cross(mForwardDir, SyVect3(0.0f, 1.0f, 0.0f));
      SyAssert(mRightDir.Magnitude() > 0.0f);
      mRightDir.Normalize();

      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mStartLoc.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mStartLoc.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mStartLoc.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }
    }
  }
}

void cAreaEffect_Beam::DoExplosion(tGameID debrisFXID) 
{
  SyVect3 center = mStartLoc;
  center.MulAdd(mForwardDir, mDist);

  DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", center, mWidth*0.5f, cDebugOverlay::GRAY);
  Titan::Get()->GetDebrisSys()->Explosion(center, mWidth*0.5f, mBirthtime, debrisFXID);
}

void  
cAreaEffect_Beam::Update(float time)
{
  mDist += time * mSpeed;

  cAreaEffect::Update(time);

#ifdef _DRAWDEBUGOVERLAY
  SyVect3 start, end, up(0.0f, 1.0f, 0.0f);

  start = end = mStartLoc + up;
  start.MulAdd(mRightDir, mWidth*0.5f);
  end.MulAdd(mRightDir, -mWidth*0.5f);

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::CYAN);

  start = end = mStartLoc + up;
  start.MulAdd(mRightDir, mWidth*0.5f);
  end.MulAdd(mRightDir, mWidth*0.5f);
  end.MulAdd(mForwardDir, mDist);

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::CYAN);

  start = end = mStartLoc + up;
  start.MulAdd(mRightDir, -mWidth*0.5f);
  end.MulAdd(mRightDir, -mWidth*0.5f);
  end.MulAdd(mForwardDir, mDist);

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::CYAN);

  start = end = mStartLoc + up;
  start.MulAdd(mRightDir, mWidth*0.5f);
  start.MulAdd(mForwardDir, mDist);
  end.MulAdd(mRightDir, -mWidth*0.5f);
  end.MulAdd(mForwardDir, mDist);

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::CYAN);
#endif
}

bool  
cAreaEffect_Beam::IsExpired()
{
  return cAreaEffect::IsExpired() || mDist > mRange;
}

bool  
cAreaEffect_Beam::IsInside(cGameObject *obj)
{
  SyVect3 hitPoint, toObj;
  float dist = obj->GetDistanceInDirection(mStartLoc, mForwardDir, mWidth, &hitPoint);

  toObj = hitPoint - mStartLoc;
  toObj.Normalize();
  toObj *= dist;

  float proj = toObj ^ mForwardDir;
  if ((proj < 0.0f) || (proj > mDist))
  {
    return false;
  }

  proj = toObj ^ mRightDir;

  if (SY_FABS(proj) > (mWidth*0.5f ))
  {
    return false;
  }

  return true;
}

SyVect3 
cAreaEffect_Beam::GetCenter()
{
  SyVect3 middle;
  middle.Mul(mForwardDir,mDist*0.5f);
  middle.Add(mStartLoc,middle);
  return middle;
  
}

float  
cAreaEffect_Beam::GetRadius()
{
  return mWidth * 0.5f;
}

//------------------------------------------------------------ cAreaEffect_WaveLine  

cAreaEffect_WaveLine::cAreaEffect_WaveLine() :
cAreaEffect(),
mStartLoc(0.0f, 0.0f, 0.0f),
mForwardDir(1.0f, 0.0f, 0.0f),
mRightDir(0.0f, 0.0f, 1.0f),
mSpeed(0.0f),
mRange(0.0f),
mWidth(0.0f),
mDist(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_WaveLine::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_WaveLine", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_StartLoc,SyMemberOffset(cAreaEffect_WaveLine,mStartLoc),"mStartLoc");
  AddVect3Property(mCLASSID,PropId_ForwardDir,SyMemberOffset(cAreaEffect_WaveLine,mForwardDir),"mForwardDir");
  AddVect3Property(mCLASSID,PropId_RightDir,SyMemberOffset(cAreaEffect_WaveLine,mRightDir),"mRightDir");
  AddFloat32Property(mCLASSID,PropId_Speed,SyMemberOffset(cAreaEffect_WaveLine,mSpeed),"mSpeed");
  AddFloat32Property(mCLASSID,PropId_Range,SyMemberOffset(cAreaEffect_WaveLine,mRange),"mRange");
  AddFloat32Property(mCLASSID,PropId_Width,SyMemberOffset(cAreaEffect_WaveLine,mWidth),"mWidth");
  AddFloat32Property(mCLASSID,PropId_Dist,SyMemberOffset(cAreaEffect_WaveLine,mDist),"mDist");

  return 0;
}

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

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

  return(pObject);
}

void          
cAreaEffect_WaveLine::SetWave(const SyVect3& start,
                              const SyVect3& dir,
                              float speed,
                              float range,
                              float width)
{
  mStartLoc = start;
  mForwardDir = dir;
  mSpeed = speed;
  mRange = range;
  mWidth = width;

  mForwardDir.Normalize();

  mRightDir.Cross(mForwardDir, SyVect3(0.0f, 1.0f, 0.0f));
  SyAssert(mRightDir.Magnitude() > 0.0f);
  mRightDir.Normalize();
}

void 
cAreaEffect_WaveLine::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mStartLoc = pObj->GetLocation();
      mForwardDir.HPR(pObj->GetHeading(), 0.0f, 0.0f);
      mForwardDir.Normalize();
      mRightDir.Cross(mForwardDir, SyVect3(0.0f, 1.0f, 0.0f));
      SyAssert(mRightDir.Magnitude() > 0.0f);
      mRightDir.Normalize();

      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mStartLoc.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mStartLoc.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mStartLoc.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }
    }
  }
}

void  
cAreaEffect_WaveLine::Update(float time)
{
  mDist += time * mSpeed;

  cAreaEffect::Update(time);

#ifdef _DRAWDEBUGOVERLAY
  SyVect3 start, end, up(0.0f, 1.0f, 0.0f);

  start = end = mStartLoc + up;
  start.MulAdd(mRightDir, mWidth*0.5f);
  start.MulAdd(mForwardDir, mDist);
  end.MulAdd(mRightDir, -mWidth*0.5f);
  end.MulAdd(mForwardDir, mDist);

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", start, end, cDebugOverlay::YELLOW);
#endif
}

bool  
cAreaEffect_WaveLine::IsExpired()
{
  return cAreaEffect::IsExpired() || mDist > mRange;
}

void cAreaEffect_WaveLine::DoExplosion(tGameID debrisFXID) 
{
  SyVect3 center = mStartLoc;
  center.MulAdd(mForwardDir, mDist);

  DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", center, mWidth*0.5f, cDebugOverlay::GRAY);
  Titan::Get()->GetDebrisSys()->Explosion(center, mWidth*0.5f, mBirthtime, debrisFXID);
}

bool  
cAreaEffect_WaveLine::IsInside(cGameObject *obj)
{
  SyVect3 toObj, hitPoint;
  float dist = obj->GetDistanceInDirection(mStartLoc, mForwardDir, mWidth, &hitPoint);
  toObj = hitPoint - mStartLoc;
  toObj.Normalize();
  toObj *= dist;

  float proj = toObj ^ mForwardDir;
  if ((proj < 0.0f) || (proj > mDist))
  {
    return false;
  }

  proj = toObj ^ mRightDir;

  if (SY_FABS(proj) > (mWidth*0.5f))
  {
    return false;
  }

  return true;
}

//------------------------------------------------------------ cAreaEffect_SingleTarget
cAreaEffect_SingleTarget::cAreaEffect_SingleTarget()
: cAreaEffect(),
  mTargetID(ID_NONE)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_SingleTarget::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_SingleTarget", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddInt32Property(mCLASSID,PropId_TargetID,SyMemberOffset(cAreaEffect_SingleTarget,mTargetID),"mTargetID");

  return 0;
}

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

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

  return(pObject);
}

bool cAreaEffect_SingleTarget::IsInside(cGameObject *pObj)
{
  SyAssertf(mTargetID != ID_NONE, "Bad target ID for cAreaEffect_InstantSingle");
  return (pObj && pObj->GetID() == mTargetID);
}

void cAreaEffect_SingleTarget::Update(float time)
{
  cGameObject* pTarget = Titan::Get()->GetRegistry()->Fetch(mTargetID);

  if (!pTarget)
  {
    return;
  }

  if (!IsIncluded(pTarget))
  {
    OnEnter(pTarget);
    mIncluded.Add(mTargetID);
  }

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

  UpdateIncluded(time);

  if (mDuration > 0.0f)
  {
    mDuration -= time;

    if (mDuration < 0.0f)
    {
      mDuration = 0.0f;
    }
  }
}


//------------------------------------------------------------ cAreaEffect_Trail
cAreaEffect_Trail::cAreaEffect_Trail()
: cAreaEffect(),
  mLength(5.0f),
  mWidth(1.0f),
  mSampleTimer(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_Trail::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_Trail", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVectorProperty(mCLASSID,PropId_SamplePoints,SyMemberOffset(cAreaEffect_Trail,mSamplePoints),"mSamplePoints",SYPROPTYPE_VECT3);
  AddFloat32Property(mCLASSID,PropId_Length,SyMemberOffset(cAreaEffect_Trail,mLength),"mLength");
  AddFloat32Property(mCLASSID,PropId_Width,SyMemberOffset(cAreaEffect_Trail,mWidth),"mWidth");
  AddFloat32Property(mCLASSID,PropId_SampleTimer,SyMemberOffset(cAreaEffect_Trail,mSampleTimer),"mSampleTimer");

  return 0;
}

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

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

  return(pObject);
}

bool cAreaEffect_Trail::IsInside(cGameObject *pObj)
{
  SyVect3 loc = pObj->GetLocation();
  SyVect3 line, toObj;
  float widthSqr = mWidth*mWidth;

  cGameObject* pSource = Titan::Get()->GetRegistry()->Fetch(mSource);
  SyAssertf(pSource != NULL, "Trail area effect has no source");

  if (pSource && mSamplePoints.Size() > 0)
  {
    line = mSamplePoints(mSamplePoints.Begin()) - pSource->GetLocation();
    toObj = loc-pSource->GetLocation();

    line.Normalize();
    line *= toObj ^ line;
    if (toObj.DistanceSquared(line) < widthSqr)
    {
      return true;
    }
  }

  for (int i=mSamplePoints.Begin(); i!=mSamplePoints.End(); i=mSamplePoints.Next(i))
  {
    if (mSamplePoints.Next(i) != mSamplePoints.End())
    {
      line = mSamplePoints(mSamplePoints.Next(i)) - mSamplePoints(i);
      toObj = loc-mSamplePoints(i);

      line.Normalize();
      line *= toObj ^ line;
      if (toObj.DistanceSquared(line) < widthSqr)
      {
        return true;
      }
    }
    else
    {
      if (mSamplePoints(i).DistanceSquared(loc) < widthSqr)
      {
        return true;
      }
    }
  }

  return false;
}

void cAreaEffect_Trail::Update(float time)
{
  cAreaEffect::Update(time);

  cGameObject* pSource = Titan::Get()->GetRegistry()->Fetch(mSource);
  SyAssertf(pSource != NULL, "Trail area effect has no source");

  if (!pSource)
  {
    return;
  }

  if (mSamplePoints.Size() > 0)
  {
    int i = mSamplePoints.Begin();
    float length = mLength-(pSource->GetLocation()-mSamplePoints(i)).Magnitude();

    DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", pSource->GetLocation()+SyVect3(0.0f, 1.0f, 0.0f), mSamplePoints(i)+SyVect3(0.0f, 1.0f, 0.0f), cDebugOverlay::WHITE);

    while (i != mSamplePoints.End())
    {
      if (length < 0.0f)
      {
        i = mSamplePoints.ReplaceLast(i);
      }
      else
      {
        if (mSamplePoints.Next(i) != mSamplePoints.End())
        {
          DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", mSamplePoints(i)+SyVect3(0.0f, 1.0f, 0.0f), mSamplePoints(mSamplePoints.Next(i))+SyVect3(0.0f, 1.0f, 0.0f), cDebugOverlay::WHITE);

          length -= (mSamplePoints(i)-mSamplePoints(mSamplePoints.Next(i))).Magnitude();
        }

        i = mSamplePoints.Next(i);
      }
    }
  }

  static const float SAMPLE_TIME = 1.0f;
  mSampleTimer += time;

  if (mSampleTimer >= SAMPLE_TIME)
  {
    mSampleTimer = 0.0f;

    cGameObject* pSource = Titan::Get()->GetRegistry()->Fetch(mSource);
    SyAssertf(pSource != NULL, "Trail area effect has no source");

    if (pSource)
    {
      mSamplePoints.Add();

      for (int i = mSamplePoints.Prev(mSamplePoints.End()); i!=0; i=mSamplePoints.Prev(i))
      {
        mSamplePoints(i) = mSamplePoints(mSamplePoints.Prev(i));
      }

      mSamplePoints(0) = pSource->GetLocation();
    }
  }

}

SyVect3 
cAreaEffect_Trail::GetCenter()
{
  cGameObject* pSource = Titan::Get()->GetRegistry()->Fetch(mSource);
  return pSource->GetLocation();
}

float  
cAreaEffect_Trail::GetRadius()
{
  return mWidth + mLength;
}

//------------------------------------------------------------ cAreaEffect_2DOrientedBox  

cAreaEffect_2DOrientedBox::cAreaEffect_2DOrientedBox()
: cAreaEffect(),
  mCenter(0.0f, 0.0f, 0.0f),
  mLengthAxis(1.0f, 0.0f, 0.0f),
  mWidthAxis(0.0f, 0.0f, 1.0f),
  mHalfLength(0.0f),
  mHalfWidth(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_2DOrientedBox::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_2DOrientedBox", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_Center,SyMemberOffset(cAreaEffect_2DOrientedBox,mCenter),"mCenter");
  AddVect3Property(mCLASSID,PropId_LengthAxis,SyMemberOffset(cAreaEffect_2DOrientedBox,mLengthAxis),"mLengthAxis");
  AddVect3Property(mCLASSID,PropId_WidthAxis,SyMemberOffset(cAreaEffect_2DOrientedBox,mWidthAxis),"mWidthAxis");
  AddFloat32Property(mCLASSID,PropId_HalfLength,SyMemberOffset(cAreaEffect_2DOrientedBox,mHalfLength),"mHalfLength");
  AddFloat32Property(mCLASSID,PropId_HalfWidth,SyMemberOffset(cAreaEffect_2DOrientedBox,mHalfWidth),"mHalfWidth");

  return 0;
}

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

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

  return(pObject);
}

void
cAreaEffect_2DOrientedBox::SetHalfLength(const SyVect3 &halfLength)
{
  mLengthAxis = halfLength;
  mHalfLength = mLengthAxis.NormalizeMagn();
  mHalfLength = SY_FABS(mHalfLength);
}

void
cAreaEffect_2DOrientedBox::SetHalfWidth(const SyVect3 &halfWidth)
{
  mWidthAxis = halfWidth;
  mHalfWidth = mWidthAxis.NormalizeMagn();
  mHalfWidth = SY_FABS(mHalfWidth);
}

void 
cAreaEffect_2DOrientedBox::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mCenter = pObj->GetLocation();
      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mCenter.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mCenter.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mCenter.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }
    }
  }
}

void cAreaEffect_2DOrientedBox::DoExplosion(tGameID debrisFXID) 
{
  int numCircles = SY_MAX(SY_ROUND(mHalfLength/mHalfWidth), 1);

  SyVect3 start(mCenter), explosionCenter;
  start.MulAdd(mLengthAxis, -mHalfLength);

  for (int i=0; i<numCircles; ++i)
  {
    explosionCenter = start;
    explosionCenter.MulAdd(mLengthAxis, mHalfWidth+(mHalfWidth*2.0f*((float)i)));
    DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", explosionCenter, mHalfWidth+0.25f, cDebugOverlay::GRAY);
    Titan::Get()->GetDebrisSys()->Explosion(explosionCenter, mHalfWidth+0.25f, mBirthtime, debrisFXID);
  }
}

void
cAreaEffect_2DOrientedBox::Update(float time)
{
  cAreaEffect::Update(time);

#ifdef _DRAWDEBUGOVERLAY
  SyVect3 halfLength(mLengthAxis), halfWidth(mWidthAxis);
  halfLength *= mHalfLength;
  halfWidth *= mHalfWidth;
  DEBUGOVERLAY_DRAWORIENTEDBOX(mSource, "AreaEffect", mCenter, halfLength, halfWidth, SyVect3(0.0f, 3.0f, 0.0f), cDebugOverlay::ORANGE);
#endif
}

bool  
cAreaEffect_2DOrientedBox::IsInside(cGameObject *obj)
{
  SyVect3 hitPoint, centerToObj;
  float dist = obj->GetDistanceInDirection(mCenter, mLengthAxis, mHalfWidth*2.0f, &hitPoint);

  centerToObj = hitPoint - mCenter;
  centerToObj.Normalize();
  centerToObj *= dist;

  float lengthProj = centerToObj ^ mLengthAxis;

  if (SY_FABS(lengthProj) > (mHalfLength))
  {
    return false;
  }

  float widthProj = centerToObj ^ mWidthAxis;
  if (SY_FABS(widthProj) > (mHalfWidth))
  {
    return false;
  }

  return true;
}

bool Test2DLineSegmentIntersect(float x1, float y1,
                                float x2, float y2,
                                float x3, float y3,
                                float x4, float y4,
                                float& xi, float yi)
{
  float denom = ((y4-y3)*(x2-x1)) - ((x4-x3)*(y2-y1));

  if (0.0f == denom)
  {
    return false;
  }
 
  float ua = (((x4-x3)*(y1-y3)) - ((y4-y3)*(x1-x3)))/denom;
  if (ua < 0.0f || ua > 1.0f)
  {
    return false;
  }

  float ub = (((x2-x1)*(y1-y3)) - ((y2-y1)*(x1-x3)))/denom;
  if (ub < 0.0f || ub > 1.0f)
  {
    return false;
  }

  xi = x1 + ua*(x2-x1);
  yi = y1 + ua*(y2-y1);
  return true;
}


bool 
cAreaEffect_2DOrientedBox::IsLineOfSightBlocked(const SyVect3& start, const SyVect3& end, SyVect3& hit)
{
  SyVect3 startToCenter(mCenter-start), dir(end-start);
  dir.Y = 0.0f;
  dir.Normalize();

  float lengthProj = (startToCenter^mLengthAxis);
  float widthProj = (startToCenter^mWidthAxis);

  if (SY_FABS(lengthProj) <= mHalfLength &&
      SY_FABS(widthProj) <= mHalfWidth)
  {
    // when you start inside the box, you can always see out;
    return false;
  }

  SyVect3 verts[4];
  verts[0] = verts[1] = mCenter;
  verts[0].MulAdd(mLengthAxis, mHalfLength);
  verts[0].MulAdd(mWidthAxis, mHalfWidth);
  verts[1].MulAdd(mLengthAxis, mHalfLength);
  verts[1].MulAdd(mWidthAxis, -mHalfWidth);

  hit.Y = 0.0f;

  if ( Test2DLineSegmentIntersect(verts[0].X, verts[0].Z, 
                                  verts[1].X, verts[1].Z,
                                  start.X, start.Z,
                                  end.X, end.Z,
                                  hit.X, hit.Z) )
  {
    return true;
  }

  verts[2] = mCenter;
  verts[2].MulAdd(mLengthAxis, -mHalfLength);
  verts[2].MulAdd(mWidthAxis, -mHalfWidth);

  if ( Test2DLineSegmentIntersect(verts[1].X, verts[1].Z, 
                                  verts[2].X, verts[2].Z,
                                  start.X, start.Z,
                                  end.X, end.Z,
                                  hit.X, hit.Z) )
  {
    return true;
  }

  verts[3] = mCenter;
  verts[3].MulAdd(mLengthAxis, -mHalfLength);
  verts[3].MulAdd(mWidthAxis, mHalfWidth);

  if ( Test2DLineSegmentIntersect(verts[2].X, verts[2].Z, 
                                  verts[3].X, verts[3].Z,
                                  start.X, start.Z,
                                  end.X, end.Z,
                                  hit.X, hit.Z) )
  {
    return true;
  }

  if ( Test2DLineSegmentIntersect(verts[3].X, verts[3].Z, 
                                  verts[0].X, verts[0].Z,
                                  start.X, start.Z,
                                  end.X, end.Z,
                                  hit.X, hit.Z) )
  {
    return true;
  }

  return false;
}

float  
cAreaEffect_2DOrientedBox::GetRadius()
{
  return mHalfLength + mHalfWidth;
}
//------------------------------------------------------------ cAreaEffect_Cone  

cAreaEffect_Cone::cAreaEffect_Cone() :
cAreaEffect(),
mStartLoc(0.0f, 0.0f, 0.0f),
mDir(1.0f, 0.0f, 0.0f),
mRange(0.0f),
mAngle(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_Cone::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect::mCLASSID,
    mCLASSID,
    "cAreaEffect_Cone", 
    Creator, 
    mCLASSID, 
    0 ); 

  AddVect3Property(mCLASSID,PropId_StartLoc,SyMemberOffset(cAreaEffect_Cone,mStartLoc),"mStartLoc");
  AddVect3Property(mCLASSID,PropId_Dir,SyMemberOffset(cAreaEffect_Cone,mDir),"mDir");
  AddFloat32Property(mCLASSID,PropId_Range,SyMemberOffset(cAreaEffect_Cone,mRange),"mRange");
  AddFloat32Property(mCLASSID,PropId_Angle,SyMemberOffset(cAreaEffect_Cone,mAngle),"mAngle");

  return 0;
}

SyPropObject* cAreaEffect_Cone::Creator()
{
  SyPropObject *pObject = SyNew cAreaEffect_Cone();
  SyAssert(pObject != NULL);
  return pObject;
}

void          
cAreaEffect_Cone::SetCone(const SyVect3& start, const SyVect3& dir, float range, float angle)
{
  SyAssert(angle > 0.0f && angle <= SY_PI*2.0f);
  mStartLoc = start;
  mDir = dir;
  mRange = range;
  mAngle = angle;
  mDir.Normalize();
}

void 
cAreaEffect_Cone::UpdateMovesWithObject()
{
  if (ID_NONE != mMovesWithObject)
  {
    cGameObject* pObj = Titan::Get()->GetRegistry()->Fetch(mMovesWithObject);

    if (pObj)
    {
      mStartLoc = pObj->GetLocation();
      mDir.HPR(pObj->GetHeading(), 0.0f, 0.0f);
      mDir.Normalize();

      cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

      if (pGraphic)
      {
        // compensate for being one frame behind in updates
        mStartLoc.MulAdd(pGraphic->GetDisplacement(), 2.0f);
      }

      if (mMovesWithFwdOffset > 0.00001f)
      {
        float heading = pObj->GetHeading();
        mStartLoc.X += SY_SIN(heading) * mMovesWithFwdOffset;
        mStartLoc.Z += SY_COS(heading) * mMovesWithFwdOffset;
      }
    }
  }
}

void  
cAreaEffect_Cone::Update(float time)
{
  cAreaEffect::Update(time);

#ifdef _DRAWDEBUGOVERLAY
  SyVect3 coneEnd1, coneEnd2, coneMid;
  float heading = SY_ATAN2(mDir.X, mDir.Z);

  coneEnd1.X = SY_SIN( heading-(mAngle*0.5f) );
  coneEnd1.Y = 0.0f;
  coneEnd1.Z = SY_COS( heading-(mAngle*0.5f) );
  coneEnd1 *= mRange;
  coneEnd1 += mStartLoc;
  coneEnd1.Y += 1.0f;

  coneMid.X = SY_SIN( heading );
  coneMid.Y = 0.0f;
  coneMid.Z = SY_COS( heading );
  coneMid *= mRange;
  coneMid += mStartLoc;
  coneMid.Y += 1.0f;

  coneEnd2.X = SY_SIN( heading+(mAngle*0.5f) );
  coneEnd2.Y = 0.0f;
  coneEnd2.Z = SY_COS( heading+(mAngle*0.5f) );
  coneEnd2 *= mRange;
  coneEnd2 += mStartLoc;
  coneEnd2.Y += 1.0f;

  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", coneEnd1, coneMid, cDebugOverlay::WHITE);
  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", coneEnd1, mStartLoc+SyVect3(0.0f, 1.0f, 0.0f), cDebugOverlay::WHITE);
  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", coneEnd2, coneMid, cDebugOverlay::WHITE);
  DEBUGOVERLAY_DRAWLINE(mSource, "AreaEffect", coneEnd2, mStartLoc+SyVect3(0.0f, 1.0f, 0.0f), cDebugOverlay::WHITE);
#endif
}

bool  
cAreaEffect_Cone::IsInside(cGameObject *obj)
{
  if (mMaxTargets > 0)
  {
    return mIncluded.Size() < mMaxTargets && IsInsideCone(obj);
  }

  return IsInsideCone(obj);
}

bool  
cAreaEffect_Cone::IsInsideCone(cGameObject *obj)
{
  SyVect3 toObj(obj->GetLocation()-mStartLoc);
  toObj.Normalize();

  float dist = obj->GetDistanceInDirection(mStartLoc, toObj);

  if (dist > mRange)
  {
    return false;
  }
  
  return ((toObj^mDir) >= SY_COS((mAngle*0.5f)));
}

void cAreaEffect_Cone::DoExplosion(tGameID debrisFXID) 
{
  SyVect3 center = mStartLoc;
  center.MulAdd(mDir, mRange*0.5f);

  float width = mAngle/(mRange*0.5f);
  DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", center, width*0.5f, cDebugOverlay::GRAY);
  Titan::Get()->GetDebrisSys()->Explosion(center, width*0.5f, mBirthtime, debrisFXID);
}

//------------------------------------------------------------ cAreaEffect_MeteorShower  

cAreaEffect_MeteorShower::cAreaEffect_MeteorShower()
: cAreaEffect_Radius(),
  mSpellID(ID_NONE),
  mTimeBetweenShots(1.0f),
  mRandAdditionalTime(0.0f),
  mTimer(0.0f)
{
  InitPropObject( mCLASSID );
}

int cAreaEffect_MeteorShower::InitPropClass()
{
  AddSubClass( mCLASSID, 
    cAreaEffect_Radius::mCLASSID,
    mCLASSID,
    "cAreaEffect_MeteorShower", 
    Creator, 
    mCLASSID, 
    0 ); 

  return 0;
}

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

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

  return(pObject);
}

void cAreaEffect_MeteorShower::SetSpell(tGameID spellID)
{
  mSpellID = spellID;
  mTimer = 0.0f;
  mRandAdditionalTime = 0.0f;

  GAME_ASSERT(ERROR_CODE, ID_NONE != mSpellID, "Bad spell for cAreaEffect_MeteorShower");

  const cSpellMaster* pSpell = Titan::Get()->GetDatabaseSys()->GetSpellMaster(mSpellID);

  if (pSpell && pSpell->mDeliveryDuration > 0.0f)
  {
    GAME_ASSERT(ERROR_DESIGN, ID_NONE != pSpell->mProjectileTypeID, "No projectile for Meteor Shower spell '%s'", pSpell->mID.GetName());
    mTimeBetweenShots = ((float)pSpell->mMaxTargets)/pSpell->mDeliveryDuration;
  }
  else
  {
    mTimeBetweenShots = 1.0f;
  }
}
void  
cAreaEffect_MeteorShower::Update(float time)
{
  mTimer += time;

  if (mTimer > mTimeBetweenShots + mRandAdditionalTime)
  {
    const cSpellMaster* pSpell = Titan::Get()->GetDatabaseSys()->GetSpellMaster(mSpellID);

    if (pSpell)
    {
      float randDist = Titan::Get()->RandomFloat()*mRadius;
      float randHeading = Titan::Get()->RandomFloat()*SY_PI*2.0f;

      SyVect3 targetPos(mLocation);
      targetPos.X += SY_SIN(randHeading) * randDist;
      targetPos.Z += SY_COS(randHeading) * randDist;
      cGameObject* pSource = Titan::Get()->GetRegistry()->Fetch(GetSource());
      cPhysicsProjectile::Shoot(pSpell->mProjectileTypeID, pSource, targetPos, NULL, NULL, &mLocation);
      DEBUGOVERLAY_DRAWSPHERE(0, "AreaEffect", targetPos, 0.5f, cDebugOverlay::GREEN);
    }

    mTimer = 0.0f;
    mRandAdditionalTime = Titan::Get()->RandomFloat() * mTimeBetweenShots * 0.5f;
  }

  cAreaEffect_Radius::Update(time);
}


//------------------------------------------------------------ cAreaEffectSys  

cAreaEffectSys* cAreaEffectSys::smpThis = NULL;

cAreaEffectSys::cAreaEffectSys() :
  m_TotalTime(0),
  m_curID(1)
{
  InitPropObject( mCLASSID );

  SyAssert(smpThis == NULL);
  smpThis = this;
}

cAreaEffectSys::~cAreaEffectSys()
{
  Clear();

  SyAssert(smpThis == this);
  smpThis = NULL;
}

int
cAreaEffectSys::InitPropClass()
{
  AddClass( mCLASSID, 
            "cAreaEffectSys", 
            Creator,     // CRO: abstract base class has no creator
            mCLASSID, 
            0 ); 

  AddSubObjectPtrVectorProperty<cAreaEffect>(mCLASSID, PropId_AreaEffects, SyMemberOffset(cAreaEffectSys,mAreaEffects), "mAreaEffects");
  AddFloat32Property(mCLASSID,PropId_TotalTime,SyMemberOffset(cAreaEffectSys,m_TotalTime),"m_TotalTime");
  AddInt32Property(mCLASSID,PropId_CurID,SyMemberOffset(cAreaEffectSys,m_curID),"m_curID");
 
  cAreaEffect::InitPropClass();
  cAreaEffect_SingleTarget::InitPropClass();
  cAreaEffect_Radius::InitPropClass();
  cAreaEffect_PowerUp::InitPropClass();
  cAreaEffect_Burst::InitPropClass();
  cAreaEffect_3DBBox::InitPropClass();
  cAreaEffect_Trail::InitPropClass();
  cAreaEffect_WaveAngle::InitPropClass();
  cAreaEffect_Arc::InitPropClass();
  cAreaEffect_Chain::InitPropClass();
  cAreaEffect_2DOrientedBox::InitPropClass();
  cAreaEffect_Beam::InitPropClass();
  cAreaEffect_WaveLine::InitPropClass();
  cAreaEffect_Cone::InitPropClass();
  cAreaEffect_MeteorShower::InitPropClass();

  cGameEffect::InitPropClass();
  cGameEffect_Damage::InitPropClass();
  cGameEffect_Heal::InitPropClass();
  cGameEffect_ManaCost::InitPropClass();
  cGameEffect_Kill::InitPropClass();
  cGameEffect_Attack::InitPropClass();
  cGameEffect_Knockback::InitPropClass();
  cGameEffect_ScriptCall::InitPropClass();
  cGameEffect_AddCondition::InitPropClass();
  cGameEffect_Vortex::InitPropClass();
  cGameEffect_Nudge::InitPropClass();
  cGameEffect_Resurrect::InitPropClass();
  cGameEffect_Levitate::InitPropClass();
  cGameEffect_CastSpell::InitPropClass();
  cGameEffect_AddEssence::InitPropClass();

  return 0;
}

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

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

  return(pObject);
}

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

tEffectID 
cAreaEffectSys::Add(cAreaEffect *effect)
{  
  tEffectID id = m_curID++;
  effect->SetID(id);
  mAreaEffects.Add(effect);
  mXMinList.InsertTail(effect);
  mXMaxList.InsertTail(effect);
  effect->UpdateExtents();
  return id;
}

void 
cAreaEffectSys::Update(float time)
{
  cGameObjectRegistry *reg = Titan::Get()->GetRegistry();
  reg->ClearOpen();
  ClearOpen();

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

  // now go through and see who's included
  cGameObject * cur_min_obj = reg->BeginMin();
  cGameObject * cur_max_obj = reg->BeginMax();
  cAreaEffect * cur_min_eff = BeginMin();
  cAreaEffect * cur_max_eff = BeginMax();

  while (cur_max_eff != NULL)
  {
    if (cur_min_eff != NULL && cur_min_eff->GetMinX() < cur_max_eff->GetMaxX())
    {
      if (cur_min_obj != NULL && cur_min_obj->GetMinX() < cur_min_eff->GetMinX())
      {
        if (cur_max_obj != NULL && cur_max_obj->GetMaxX() < cur_min_obj->GetMinX())
        {
          // process cur_max_obj
          reg->RemoveOpen(cur_max_obj);
          cur_max_obj = reg->NextMax(cur_max_obj);
        }
        else
        {
          // process cur_min_obj
          reg->AddOpen(cur_min_obj);
          // for all open effects, add the object
          cAreaEffect *cur = BeginOpen();
          while (cur != NULL)
          {
            cur->AddObject(cur_min_obj);
            cur = NextOpen(cur);
          }
          cur_min_obj = reg->NextMin(cur_min_obj);
        }
      }
      else
      {
        if (cur_max_obj != NULL && cur_max_obj->GetMaxX() < cur_min_eff->GetMinX())
        {
          // process cur_max_obj
          reg->RemoveOpen(cur_max_obj);
          cur_max_obj = reg->NextMax(cur_max_obj);
        }
        else
        {
          // process cur_min_eff
          cur_min_eff->AddAllOpenObjects();
          AddOpen(cur_min_eff);  
          cur_min_eff = NextMin(cur_min_eff);
        }
      }
    }
    else
    {
      if (cur_min_obj != NULL && cur_min_obj->GetMinX() < cur_max_eff->GetMaxX())
      {
        if (cur_max_obj != NULL && cur_max_obj->GetMaxX() < cur_min_obj->GetMinX())
        {
          // process cur_max_obj
          reg->RemoveOpen(cur_max_obj);
          cur_max_obj = reg->NextMax(cur_max_obj);
        }
        else
        {
          // process cur_min_obj
          reg->AddOpen(cur_min_obj);
          // for all open effects, add the object
          cAreaEffect *cur = BeginOpen();
          while (cur != NULL)
          {
            cur->AddObject(cur_min_obj);
            cur = NextOpen(cur);
          }
          cur_min_obj = reg->NextMin(cur_min_obj);
        }
      }
      else
      {
        if (cur_max_obj != NULL && cur_max_obj->GetMaxX() < cur_max_eff->GetMaxX())
        {
          // process cur_max_obj
          reg->RemoveOpen(cur_max_obj);
          cur_max_obj = reg->NextMax(cur_max_obj);
        }
        else
        {
          // process cur_max_eff
          RemoveOpen(cur_max_eff);
          cur_max_eff->UpdateIncluded(time);
          cur_max_eff = NextMax(cur_max_eff);
        }
      }
    }
  }
  reg->ClearOpen();

  for (int ii=mAreaEffects.Begin();ii!=mAreaEffects.End();)
  {
    if (mAreaEffects(ii)->IsExpired() || mAreaEffects(ii)->IsMarkedForDelete())
    {
      cAreaEffect *cur = mAreaEffects(ii);
      cur->ExitAll();
      mXMinList.Remove(cur);
      mXMaxList.Remove(cur);
      delete cur;
      ii =  mAreaEffects.ReplaceLast(ii);
    }
    else
    {
      ii= mAreaEffects.Next(ii);
    }
  }
  m_TotalTime += time;
}

void 
cAreaEffectSys::ProcessPacket(cRulePacket* packet)
{
  for (int ii=mAreaEffects.Begin();ii!=mAreaEffects.End();ii=mAreaEffects.Next(ii))
  {
    mAreaEffects(ii)->ProcessPacket(packet);
  }
}

void 
cAreaEffectSys::Remove(tGameID spellID, tGameObjectID sourceID)
{
  for (int ii=mAreaEffects.Begin();ii!=mAreaEffects.End(); ii = mAreaEffects.Next(ii))
  {
    if ((ID_NONE == sourceID || mAreaEffects(ii)->GetSource() == sourceID) &&
        (ID_NONE == spellID || mAreaEffects(ii)->GetSpellID() == spellID))
    {
      mAreaEffects(ii)->MarkForDelete();
    }
  }
}

void 
cAreaEffectSys::Remove(tEffectID id)
{
  for (int ii=mAreaEffects.Begin();ii!=mAreaEffects.End(); ii = mAreaEffects.Next(ii))
  {
    if (mAreaEffects(ii)->GetID() == id)
    {
      mAreaEffects(ii)->MarkForDelete();
      return;
    }
  }
}

bool 
cAreaEffectSys::IsLineOfSightBlocked(const SyVect3& start, const SyVect3& end, SyVect3& hit)
{
  bool bHitArea = false;
  SyVect3 areaHitPos;

  for (int ii=mAreaEffects.Begin();ii != mAreaEffects.End(); ii = mAreaEffects.Next(ii))
  {
    if (mAreaEffects(ii)->IsLineOfSightBlocked(start, end, areaHitPos))
    {
      if (!bHitArea || start.DistanceSquared(areaHitPos) < start.DistanceSquared(hit))
      {
        bHitArea = true;
        hit = areaHitPos;
      }
    }
  }

  return bHitArea;
}

cAreaEffect* cAreaEffectSys::Fetch(tEffectID id)
{
  for (int ii=mAreaEffects.Begin();ii != mAreaEffects.End(); ii = mAreaEffects.Next(ii))
  {
    if (mAreaEffects(ii)->GetID() == id)
    {
      return mAreaEffects(ii);
    }
  }

  return NULL;
}


void              
cAreaEffectSys::ReinsertAfterMin(cAreaEffect *before,cAreaEffect *after)
{
  mXMinList.Remove(after);
  mXMinList.InsertAfter(after,before);
};

void              
cAreaEffectSys::ReinsertAfterMax(cAreaEffect *before,cAreaEffect *after)
{
  mXMaxList.Remove(after);
  mXMaxList.InsertAfter(after,before);
};

// EOF
