/******************************************************************
  
  Module:  spell.cpp
  
  Author: Borut Pfeifer
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "spell.h"
#include "Titan.h"
#include "areaeffect.h"
#include "registry.h"
#include "gameobj.h"
#include "intel.h"
#include "graphic.h"
#include "stats.h"
#include "rule.h"
#include "database.h"
#include "tuning.h"
#include "physics.h"
#include "netpacket.h"
#include "ai/behaviortypes.h"
#include "SyDamageNumMgr.h"
#include "TitanPeeringNetwork.h"
#include "TitanFXScriptDriver.h"
#include "SyScene.h"
#include "droplist.h"
#include "gameerror.h"
#include "script_pawn.h"

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


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

//------------------------------------------------------------- cSpellEffectMaster
cSpellEffectMaster::cSpellEffectMaster()
: mSpellOwnerID(0),
  mBaseAmount(0),
  mMultAmount(0.0f),
  mDuration(0.0f),
  mPercentChance(100),
  mTargetFXID(ID_NONE),
  mObjectNameID(0),
  mTargetRace(ID_NONE)
{
}

cSpellEffectMaster::~cSpellEffectMaster()
{
}
  
cGameEffect* cSpellEffectMaster::CreateGameEffect(cGameObject* pCaster,
                                                  const SyVect3& loc,
                                                  eDamageType damageType,
                                                  bool bArea,
                                                  tGameID itemMasterID)
{
  Titan* pTitan = pCaster->GetTitan();
  cGameEffect* pGameEffect = NULL;

  int amt = mBaseAmount;
  cCalcPacket packet;
  packet.mPacketType = cRulePacket::CALC_SPELL_POWER;
  packet.mObjectID = pCaster->GetID();
  packet.SetTotal(0, "Calc Spell Power", "Initial Spell Cast");
  pCaster->GetStats()->ProcessRulePacket(&packet);
  packet.MultiplyTotal(mMultAmount, "Spell Effect Multiply", "Initial Spell Cast");
  packet.AddTotal(mBaseAmount, "Spell Effect Add Base", "Initial Spell Cast");
  amt = packet.GetTotal();

  float damageMult = 1.0f;
  if (pCaster->GetType() == cGameObject::OBJ_PLAYER)
  {
    damageMult = static_cast<cStatsCharacter*>(pCaster->GetStats())->GetLevel()->mPlayerDamage*static_cast<cStatsCharacter*>(pCaster->GetStats())->GetClass()->mDamage;
  }
  else if (pCaster->GetType() == cGameObject::OBJ_NPC)
  {
    damageMult = static_cast<cStatsCharacter*>(pCaster->GetStats())->GetLevel()->mNPCDamage*static_cast<cStatsCharacter*>(pCaster->GetStats())->GetClass()->mDamage;
  }

  // effects with durations do not last based on area delivery mechanism
  bool bAreaOnlyConditions = bArea && mDuration <= 0.0f;

  switch (mType)
  {
    case SPELLEFFECT_DAMAGE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Damage spell effect needs an BaseAmount+MultAmount > 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      packet.MultiplyTotal(damageMult, "Spell Effect Base Damage Mult", "Initial Spell Cast");
      amt = packet.GetTotal();
      pGameEffect = SyNew cGameEffect_Damage();
      static_cast<cGameEffect_Damage*>(pGameEffect)->SetDamage(amt, damageType);
      break;

    case SPELLEFFECT_DAMAGE_OVER_TIME:
      packet.MultiplyTotal(damageMult, "Spell Effect Base Damage Mult", "Initial Spell Cast");
      amt = packet.GetTotal();
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Damage over time spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Damage Over Time", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, (int)damageType);
      break;

    case SPELLEFFECT_HEAL:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Healing spell effect needs an BaseAmount+MultAmount > 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_Heal();
      static_cast<cGameEffect_Heal*>(pGameEffect)->SetHealing(amt);
      break;

    case SPELLEFFECT_DECREASE_MANA:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Mana Cost spell effect needs an BaseAmount+MultAmount not equal to 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_ManaCost();
      static_cast<cGameEffect_ManaCost*>(pGameEffect)->SetCost(amt);
      break;

    case SPELLEFFECT_DECREASE_MANA_OVER_TIME:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Decrease mana over time spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Mana Cost Over Time", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_INCREASE_MANA:                       
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Increase mana spell effect needs an BaseAmount+MultAmount > 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_ManaCost();
      static_cast<cGameEffect_ManaCost*>(pGameEffect)->SetCost(-amt);
      break;

    case SPELLEFFECT_HEALTH_DRAIN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Health Drain spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Health Drain", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_MANA_DRAIN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Mana Drain spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Mana Drain", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_HEALTH_REGEN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Health Regen spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Health Regen", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_MANA_REGEN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Mana Regen spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Mana Regen", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_ATTACK_SPEED:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Attack Speed spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Attack Speed", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_MOVEMENT_SPEED:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Movement Speed spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Movement Speed", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_ATTACK_POWER:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Attack Power spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Attack Power", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_SPELL_POWER:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Spell Power spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Spell Power", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_BLOCK_POWER:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Block Power spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Block Power", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_MELEE_CRITICAL_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Melee Critical Chance spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Critical Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, 0);
      break;

    case SPELLEFFECT_RANGED_CRITICAL_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Ranged Critical Chance spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Critical Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, 1);
      break;

    case SPELLEFFECT_MELEE_DODGE_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Melee Dodge Chance spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Dodge Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, 0);
      break;

    case SPELLEFFECT_RANGED_DODGE_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Ranged Dodge Chance spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Dodge Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, 1);
      break;

    case SPELLEFFECT_DEFENSE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Defense spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Defense", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, (int)damageType);
      break;

    case SPELLEFFECT_DAMAGE_DONE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Defense spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Damage Done", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, (int)damageType);
      break;

    case SPELLEFFECT_DAMAGE_TAKEN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Defense spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Damage Taken", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt, (int)damageType);
      break;

    case SPELLEFFECT_SIZE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount != 0, "Size spell effect needs a nonzero BaseAmount+MultAmount");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Size", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_STUN:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Stunned", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_STUN_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Stunned"));
      break;

    case SPELLEFFECT_SLEEP:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Sleeping", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_SLEEP_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Sleeping"));
      break;

    case SPELLEFFECT_CONFUSE:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Confused", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_CONFUSE_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Confused"));
      break;

    case SPELLEFFECT_CHARM:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Charmed", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_CHARM_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Charmed"));
      break;

    case SPELLEFFECT_INVISIBILITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Invisible", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_SEE_INVISIBILITY:
      break;

    case SPELLEFFECT_KNOCKBACK:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "KnockBack spell effect needs an BaseAmount+MultAmount > 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_Knockback();
      static_cast<cGameEffect_Knockback*>(pGameEffect)->SetAmount((float)amt, 0.0f);
// use source loc instead     static_cast<cGameEffect_Knockback*>(pGameEffect)->SetLocation(loc);
      static_cast<cGameEffect_Knockback*>(pGameEffect)->SetKnockdown(false, 0.0f);
      break;

    case SPELLEFFECT_KNOCKDOWN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount >= 0, "KnockDown spell effect needs an BaseAmount+MultAmount >= 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_Knockback();
      static_cast<cGameEffect_Knockback*>(pGameEffect)->SetAmount(0.0f, (float)amt);
// use source loc instead     static_cast<cGameEffect_Knockback*>(pGameEffect)->SetLocation(loc);
      static_cast<cGameEffect_Knockback*>(pGameEffect)->SetKnockdown(true, mDuration);
      break;

    case SPELLEFFECT_KNOCKBACK_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Knockback Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_KNOCKDOWN_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Knockdown Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_VORTEX:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Vortex spell effect needs an BaseAmount+MultAmount > 0");
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Vortex spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_Vortex();
      static_cast<cGameEffect_Vortex*>(pGameEffect)->SetSpeed((float)amt);
      static_cast<cGameEffect_Vortex*>(pGameEffect)->SetLocation(loc);
      break;

    case SPELLEFFECT_VORTEX_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Vortex Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_FREEZE:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Frozen", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_FREEZE_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Frozen"));
      break;

    case SPELLEFFECT_SPREAD_FIRE:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("On Fire", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_KILL:
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_Kill();
      break;

    case SPELLEFFECT_KILL_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Kill Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_BIND:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Bound", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_BIND_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Bound"));
      break;

    case SPELLEFFECT_SILENCE:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Silenced", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_SILENCE_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Silenced"));
      break;

    case SPELLEFFECT_FEAR:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Fear spell effect needs an BaseAmount+MultAmount (distance to run) > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Fear", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_FEAR_IMMUNITY:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Immunity", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, SyHashResourceID("Fear"));
      break;

    case SPELLEFFECT_FORCE_FIELD:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Force Field spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Mana Shield Percent", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_DAMAGE_FIELD:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Damage Field spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Damage Shield Percent", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_TELEPORT:
      {
        GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");

        pGameEffect = SyNew cGameEffect_AddCondition();
        const cSpellMaster* pSpell = pTitan->GetDatabaseSys()->GetSpellMaster(mSpellOwnerID);

        if (pSpell && cSpellMaster::SPELL_ORIGIN_RANDOM_LOCATION == pSpell->mOrigin)
        {
          int distCM = SY_ROUND((pCaster->GetDistance(loc)*100.0f));
          int headingDeg = SY_ROUND((SY_RAD_TO_DEG(pCaster->GetHeadingTowards(loc))));
          static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Directional Teleport", mSpellOwnerID, false, ID_NONE, 0.0f, mTargetFXID, distCM, headingDeg);
        }
        else
        {
          static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Random Teleport", mSpellOwnerID, false, ID_NONE, 0.0f, mTargetFXID, amt*100);
        }
      }

      break;

    case SPELLEFFECT_ILLUSION:
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Illusion", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_RESURRECT:
      GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
      pGameEffect = SyNew cGameEffect_Resurrect();
      break;

    case SPELLEFFECT_SUMMON_PET:
    case SPELLEFFECT_DUMMY_TARGET:
      if (!pCaster->IsRemote())
      {
        GAME_ASSERT(ERROR_DESIGN, ID_NONE == mTargetFXID, "Instantaneous spell effect has ongoing target fx specified in database");
        GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for summon pet or dummy target spell effect");
        tGameObjectID petID = pTitan->GetRegistry()->Create(cGameObject::OBJ_NPC);
        cGameObject* pPet = pTitan->GetRegistry()->Fetch(petID);
        SyAssertf(pPet != NULL, "Could not create pet");

        if (pPet)
        {
          SyVect3 dir;
          dir.HPR(pCaster->GetHeading(), 0.0f, 0.0f);
          dir *= 2.0f;
          GAME_ASSERT(ERROR_DESIGN, pTitan->GetDatabaseSys()->GetCharacterMaster(mObjectNameID.GetID()) != NULL, "Could not find character entry in database for %s in summon pet/attack target spell effect", mObjectNameID.GetName());
          static_cast<cStatsCharacter*>(pPet->GetStats())->SetMaster(mObjectNameID.GetName());
          if (!pPet->SetSafeLocation(loc+dir))
          {
            pPet->SetLocation(loc+dir);
            GAME_ASSERT(ERROR_DESIGN, false, "Could not find safe location for NPC spawned via summon spell at %.2f, %.2f, %.2f", loc.X+dir.X, loc.Y+dir.Y, loc.Z+dir.Z);
          }

          pPet->SetHeading(pCaster->GetHeading());
          pTitan->GetRegistry()->InitObject(petID, false);

          if (SPELLEFFECT_DUMMY_TARGET == mType)
          {
            pPet->GetStats()->AddCondition("Target Me", mSpellOwnerID, pCaster->GetID(), ID_NONE, -1.0f, ID_NONE);
          }
          else
          {
            SyAssert(SPELLEFFECT_SUMMON_PET == mType);
            SyAssert(prop_cast<cIntelNPC*>(pPet->GetIntel())!=NULL);
            static_cast<cIntelNPC*>(pPet->GetIntel())->GetAi()->Follow(1, pCaster->GetID());
            SyAssert(prop_cast<cGraphicCharacter*>(pPet->GetGraphic())!=NULL);

            pPet->GetStats()->Summon();
          }

          if (mDuration > 0.0f)
          {
            pPet->GetStats()->AddCondition("Delayed Death", mSpellOwnerID, pCaster->GetID(), ID_NONE, mDuration, ID_NONE);
          }
        }
      }
      break;


    case SPELLEFFECT_TRIGGER_SPELL:
      GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for trigger spell effect");
      pGameEffect = NULL;
      pCaster->GetStats()->AddCondition("Delayed Spell", mSpellOwnerID, pCaster->GetID(), itemMasterID, mDuration, mTargetFXID, mObjectNameID.GetID());
      break;

    case SPELLEFFECT_TRIGGER_TARGET_SPELL:
      GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for trigger spell effect");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Delayed Spell", mSpellOwnerID, itemMasterID, false, mDuration, mTargetFXID, mObjectNameID.GetID());
      break;

    case SPELLEFFECT_TRIGGER_SPELL_ON_ATTACK:
      GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for trigger spell effect");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Delayed Attack Spell", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, mObjectNameID.GetID());
      break;

    case SPELLEFFECT_TRIGGER_SPELL_ON_DEFEND:
      GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for trigger spell effect");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Delayed Defend Spell", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, mObjectNameID.GetID());
      break;

    case SPELLEFFECT_TRIGGER_SPELL_ON_DEATH:
      GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for trigger spell effect");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Delayed Death Spell", mSpellOwnerID, itemMasterID, false, mDuration, mTargetFXID, mObjectNameID.GetID());
      break;

    case SPELLEFFECT_SHOW_MAP_ENEMIES:
    case SPELLEFFECT_SHOW_MAP_TREASURE:
      GAME_ASSERT(ERROR_DESIGN, false, "Show map spell effects not implemented");
      break;

    case SPELLEFFECT_HEALTH_ABSORB:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Health absorb effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Health Absorb", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_MANA_ABSORB:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Mana absorb effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Mana Absorb", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_ESSENCE_ABSORB:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Essence absorb effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Essence Absorb", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;


    case SPELLEFFECT_HEALTH_DROP_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Health drop chance effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Health Drop Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_MANA_DROP_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Mana drop chance effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Mana Drop Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_ESSENCE_DROP_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Essence drop chance effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Essence Drop Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_ITEM_DROP_CHANCE:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Item drop chance effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Item Drop Chance", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_EXPERIENCE_GAIN:
      GAME_ASSERT(ERROR_DESIGN, mBaseAmount+mMultAmount > 0, "Exp Gain spell effect needs an BaseAmount+MultAmount > 0");
      pGameEffect = SyNew cGameEffect_AddCondition();
      static_cast<cGameEffect_AddCondition*>(pGameEffect)->SetCondition("Altered Experience Gain", mSpellOwnerID, itemMasterID, bAreaOnlyConditions, mDuration, mTargetFXID, amt);
      break;

    case SPELLEFFECT_SPAWN_TREASURE:
      {
        GAME_ASSERT(ERROR_DESIGN, mObjectNameID.GetID() != 0, "Bad object name for spawn treasure spell effect");
        cTreasureSet* pTreasure = pTitan->GetDatabaseSys()->GetTreasureSet(mObjectNameID.GetID());
        GAME_ASSERT(ERROR_DESIGN, pTreasure != NULL, "Could not find treasure set '%s' for spell effect", mObjectNameID.GetName());
        if (pTreasure)
        {
          pTreasure->Drop(pCaster, loc, NULL);
        }
      }
      break;

    case SPELLEFFECT_NUM_TYPES:
    default:
      GAME_ASSERT(ERROR_DESIGN, false, "Bad spell effect type"); 
      break;
  }

  if (pGameEffect)
  {
    pGameEffect->SetPercentChance(mPercentChance);
    pGameEffect->SetTargetRace(mTargetRace);
  }

  return pGameEffect;
}


static const uint32 l_DamageNames[NUM_DAMAGE_TYPES] =
{
  SyHashResourceID("DAMAGE_TYPE_NONE"),
  SyHashResourceID("DAMAGE_TYPE_PHYSICAL"),
  SyHashResourceID("DAMAGE_TYPE_MAGIC")
};

void 
cSpellEffectMaster::AppendPropertyString(cGameObject *pObj, SyString &str, eDamageType damageType) const
{
  // T4 system isn't finished yet; use literal strings for now, later switch to strings from db 
  int amt = mBaseAmount;
  cCalcPacket packet;
  packet.mPacketType = cRulePacket::CALC_SPELL_POWER;
  packet.mObjectID = pObj->GetID();
  packet.SetTotal(0, "Calc Spell Power", "Initial Spell Cast");
  pObj->GetStats()->ProcessRulePacket(&packet);
  amt += SY_ROUND(((float)(packet.GetTotal()*mMultAmount))*0.01f);

  Titan *pTitan = pObj->GetTitan();
  char buffer[512]; 
  buffer[0]='\0';
  int buffLen = 512;

  switch (mType)
  {
    case SPELLEFFECT_DAMAGE:
    {
      int id = l_DamageNames[damageType];//SyHashResourceID(l_DamageNames[mDamageType]);

      //pTitan->T4SetFinal(1,pDamageName,0,amt);

      pTitan->T4SetContext(1,id,0,amt);   

      id = SyHashResourceID("SPELL_EFFECT_DAMAGE");
      pTitan->T4Expand(buffer,buffLen,id);
    }
    break;

#if 0  // todo
    case SPELLEFFECT_DAMAGE_OVER_TIME:
      break;

    case SPELLEFFECT_HEAL:
      break;

    case SPELLEFFECT_DECREASE_MANA:
      break;

    case SPELLEFFECT_DECREASE_MANA_OVER_TIME:
      break;

    case SPELLEFFECT_INCREASE_MANA:                       
      break;

    case SPELLEFFECT_HEALTH_DRAIN:
      break;

    case SPELLEFFECT_MANA_DRAIN:
      break;

    case SPELLEFFECT_HEALTH_REGEN:
      break;

    case SPELLEFFECT_MANA_REGEN:
      break;

    case SPELLEFFECT_ATTACK_SPEED:
      break;

    case SPELLEFFECT_MOVEMENT_SPEED:
      break;

    case SPELLEFFECT_ATTACK_POWER:
      break;

    case SPELLEFFECT_SPELL_POWER:
      break;

    case SPELLEFFECT_BLOCK_POWER:
      break;

    case SPELLEFFECT_CRITICAL_CHANCE:
      break;

    case SPELLEFFECT_NORMAL_RESIST:
      break;

    case SPELLEFFECT_MAGIC_RESIST:
      break;

    case SPELLEFFECT_SIZE:
      break;

    case SPELLEFFECT_STUN:
      break;

    case SPELLEFFECT_SLEEP:
      break;

    case SPELLEFFECT_CONFUSE:
      break;

    case SPELLEFFECT_INVISIBILITY:
      break;

    case SPELLEFFECT_SEE_INVISIBILITY:
      break;

    case SPELLEFFECT_KNOCKBACK:
      break;

    case SPELLEFFECT_KNOCKDOWN:
      break;

    case SPELLEFFECT_VORTEX:
      break;

    case SPELLEFFECT_FREEZE:
      break;

    case SPELLEFFECT_SPREAD_FIRE:
      break;

    case SPELLEFFECT_KILL:
      break;

    case SPELLEFFECT_BIND:
      break;

    case SPELLEFFECT_SILENCE:
      break;

    case SPELLEFFECT_FEAR:
      break;

    case SPELLEFFECT_FORCE_FIELD:
      break;

    case SPELLEFFECT_DAMAGE_FIELD:
      break;

    case SPELLEFFECT_TELEPORT:
      break;

    case SPELLEFFECT_ILLUSION:
      break;

    case SPELLEFFECT_RESURRECT:
      break;

    case SPELLEFFECT_SUMMON_PET:
    case SPELLEFFECT_DUMMY_TARGET:
      break;


    case SPELLEFFECT_TRIGGER_SPELL:
      break;

    case SPELLEFFECT_TRIGGER_SPELL_ON_ATTACK:
      break;

    case SPELLEFFECT_TRIGGER_SPELL_ON_DEFEND:
      break;

    case SPELLEFFECT_NUM_TYPES:
 #endif
    default:
      //SyAssertf(false, "Bad spell effect type"); 
      break;
  }

  if (buffer[0]!='\0')
  {
    str+='\n';
    str+=buffer;
  }

}

//----------------------------------------------------------- cSpellMaster

cSpellMaster::cSpellMaster()
: mID(0),
  mOrigin(SPELL_ORIGIN_CASTER),
  mDelivery(SPELL_DELIVERY_SINGLE),
  mTargeting(SPELL_TARGETING_ANY),
  mProjectileTypeID(0),
  mOffset(0.0f),
  mRange(0.0f),
  mWidth(0.0f),
  mAngleStart(0.0f),
  mAngleEnd(0.0f),
  mMaxTargets(0),
  mManaCost(0),
  mWardPropID(0),
  mCastAnimID(0),
  mCastFXID(ID_NONE),
  mCastStartFXID(ID_NONE),
  mAreaFXID(ID_NONE),
  mCastLoopFXID(ID_NONE),
  mParentSpellID(ID_NONE),
  mStackingCategory(-1),
  mDeliveryDuration(0.0f),
  mRefreshTime(0.0f),
  mNPCCastTime(0.0f),
  mbBlockable(false),
  mbJumpable(false),
  mImpactFXSetID(ID_NONE),
  mImpactSoundSetID(ID_NONE),
  mDamageType(DT_MAGICAL),
  mElementalType(ET_NONE)
{
}

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

void cSpellMaster::CastSpell(cGameObject* pCaster,
                             cGameObject* pTarget,
                             SyVect3* pTargetPos,
                             eComboType combo,
                             tGameID itemMasterID) const
{
  SyAssert(pCaster != NULL);

  if (!pCaster->IsRemote())
  {    
    if (pCaster->GetStats()->QueryFlag("Silenced"))
    {
      return;
    }

    if (pCaster->GetType() == cGameObject::OBJ_PLAYER &&
        static_cast<cStatsCharacter*>(pCaster->GetStats())->GetMana() < mManaCost)
    {
      return; // not enough mana to cast spell
    }

    if (!pTarget && !pTargetPos)
    {
      static const float MAX_SPELL_ANGLE = SY_PI/2.0f;

      if (SPELL_ORIGIN_TARGET == mOrigin || SPELL_ORIGIN_TARGET_LOCATION == mOrigin)
      {
        float heading = pCaster->GetHeading();
        cIntelPlayer* pPlayerIntel = prop_cast<cIntelPlayer*>(pCaster->GetIntel());

        if (pPlayerIntel && pPlayerIntel->GetControllerMagnitude() > 0.5f)
        {
          heading = pPlayerIntel->GetControllerHeading(0.0f);
        }

        pTarget = pCaster->GetRegistry()->Fetch(pCaster->GetIntel()->PickAttackTarget(heading, mRange, MAX_SPELL_ANGLE));
      }
    }

    // spells cast for combos are handled in anim system, don't need to send a msg
    if (NUM_COMBOS == combo)
    {
      cNetCastSpellPacket netPacket;
      netPacket.mTargetID = pTarget ? pTarget->GetID() : ID_NONE;
      netPacket.mSpellID = mID.GetID();
      netPacket.mCombo = combo;

      if (pTargetPos)
      {
        netPacket.mTargetLoc = *pTargetPos;
      }

      char buf[1024];
      int len = netPacket.PackBuffer(buf, sizeof(buf));
      pCaster->GetTitan()->GetPeeringNetwork()->ObjectBroadcast(pCaster->GetID(), buf, len);
    }
  }

  if (mCastAnimID != ID_NONE)
  {
    SyAssertf(combo == NUM_COMBOS, "Trying to cast %s with a casting animation via a combo", mID.GetName());

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

    if (pGraphic)
    {
      cAnimCharControllerInput* pInput = pGraphic->GetAnimController()->GetInput();
      pInput->mCastAnimSettingOverride = (eAnimState)mCastAnimID;
      pInput->mCastSpellID = mID.GetID();
      pInput->mTarget = pTarget!=NULL? pTarget->GetID() : ID_NONE;      
      pInput->mTargetPitch = 0.0f;

      if (pTargetPos)
      {
        pInput->mTargetHeading = pCaster->GetHeadingTowards(*pTargetPos);
        pInput->mTargetRange = pCaster->GetDistance(*pTargetPos);
      }
      else
      {
        pInput->mTargetHeading = pCaster->GetHeading();
        pInput->mTargetRange = mOffset;
      }

      return;
    }
  }

  // play the cast effect here since there's no anim
  if (mCastFXID != 0 && NUM_COMBOS == combo)
  {
    SyDictionary *pDictionary = pCaster->GetTitan()->GetScene()->GetDictionary();
    int32         scriptHandle;
    SyActorHandle actorHandle = pCaster->GetGraphic()->GetActorHandle();

    if(pDictionary->FindTyped( mCastFXID, SYRESOURCETYPE_FXSCRIPT, scriptHandle))
    {
      pCaster->GetTitan()->GetScene()->GetFXScriptSystem()->PlayScript(scriptHandle, 1, &actorHandle);
    }
  }

  CreateSpellDelivery(pCaster, pTarget, pTargetPos, combo, itemMasterID);
}

void cSpellMaster::CreateSpellDelivery(cGameObject* pCaster,
                                       cGameObject* pTarget,
                                       SyVect3* pTargetPos,
                                       eComboType combo,
                                       tGameID itemMasterID) const
{
  SyAssert(pCaster != NULL);

  // spell cast script callback
  static const int32 ID_LastSpellCast = SyHashResourceID("LAST_SPELL_CAST");
  pCaster->GetStats()->Value_Set(ID_LastSpellCast, mID.GetID());

  pCaster->GetTitan()->GetScriptSys()->ScriptEvent( PET_SPELL_CAST, pCaster->GetID(), pTarget ? pTarget->GetID() : 0 );

  if (pCaster->GetType() == cGameObject::OBJ_PLAYER)
  {
    // Apply mana cost once' we're sure we're creating the spell effects
    cCalcPacket packet;
    packet.SetTotal(mManaCost, "Cast Spell", "");
    static_cast<cStatsCharacter*>(pCaster->GetStats())->ApplyManaCost(&packet);
  }

  if (ID_NONE != mCastFXID)
  {
    SyDictionary *pDictionary = pCaster->GetTitan()->GetScene()->GetDictionary();
    int32         scriptHandle;
    SyActorHandle actorHandle = pCaster->GetGraphic()->GetActorHandle();

    if(pDictionary->FindTyped( mCastFXID, SYRESOURCETYPE_FXSCRIPT, scriptHandle))
    {
      pCaster->GetTitan()->GetScene()->GetFXScriptSystem()->PlayScript(scriptHandle, 1, &actorHandle);
    }
  }

  if (mProjectileTypeID != ID_NONE && 
      SPELL_DELIVERY_METEOR_SHOWER != mDelivery)
  {
    if (!pCaster->IsRemote())
    {  
      SyVect3 targetLoc;

      if (pTargetPos)
      {
        targetLoc = *pTargetPos;
      }
      else if (pTarget)
      {
        targetLoc = pTarget->GetLocation();
      }
      else
      {
        SyVect3 dir;
        dir.HPR(pCaster->GetHeading(), 0.0f, 0.0f);
        dir *= SY_MAX(mRange, 5.0f);
        targetLoc = pCaster->GetLocation() + dir;
      }

      cPhysicsProjectile::Shoot(mProjectileTypeID, pCaster, targetLoc, pTarget, this);
    }
  }
  else
  {
    CreateSpellEffects(pCaster, pTarget, pTargetPos, combo, itemMasterID);
  }
}

void cSpellMaster::CreateSpellEffects(cGameObject* pCaster,
                                      cGameObject* pTarget,
                                      SyVect3* pTargetPos,
                                      eComboType combo,
                                      tGameID itemMasterID) const
{
  if (!pCaster) // if caster died by this point in the spell, just quit
  {
    return;
  }

  Titan* pTitan = pCaster->GetTitan();
  cGameObject* pOrigin = NULL;
  cAreaEffect *pDeliverer = NULL;
  
  SyVect3 spellLocation(pCaster->GetLocation());
  float spellHeading = pCaster->GetHeading();

  float speed = 0.0f;
  SyVect3 dir;

  bool bAreaBasedConditions = true;

  float duration = mDeliveryDuration;

  if (0.0f == duration)
  {
    duration = 0.00001f; // kludge to ensure area effect gets run one frame
  }

  if (NUM_COMBOS != combo)
  {
    SyAssert(prop_cast<cGraphicCharacter*>(pCaster->GetGraphic())!=NULL);
    duration *= static_cast<cGraphicCharacter*>(pCaster->GetGraphic())->GetAnimController()->GetAnimSpeed();
  }

  //
  // Select spell location
  //
  switch (mOrigin)
  {
    case SPELL_ORIGIN_CASTER:
      pOrigin = pCaster;
      spellHeading = pCaster->GetHeading();
      dir.HPR(spellHeading, 0.0f, 0.0f);
      dir.Normalize();
      spellLocation = pCaster->GetLocation();
      spellLocation.MulAdd(dir, mOffset);
      break;

    case SPELL_ORIGIN_TARGET:
      pOrigin = pTarget;

      if (pTarget)
      {
        spellHeading = pTarget->GetHeading();
        dir.HPR(spellHeading, 0.0f, 0.0f);
        dir.Normalize();
        spellLocation = pTarget->GetLocation();
        spellLocation.MulAdd(dir, mOffset);
      }
      break;

    case SPELL_ORIGIN_CASTER_LOCATION:
      pOrigin = NULL;
      spellHeading = pCaster->GetHeading();

      if (pTargetPos)
      {
        spellLocation = *pTargetPos;
      }
      else
      {
        dir.HPR(spellHeading, 0.0f, 0.0f);
        dir.Normalize();
        spellLocation = pCaster->GetLocation();
        spellLocation.MulAdd(dir, mOffset);
      }
      break;

    case SPELL_ORIGIN_TARGET_LOCATION:
      pOrigin = NULL;

      if (pTarget)
      {
        spellHeading = pTarget->GetHeading();
        dir.HPR(spellHeading, 0.0f, 0.0f);
        dir.Normalize();
        spellLocation = pTarget->GetLocation();
        spellLocation.MulAdd(dir, mOffset);
      }
      break;

    case SPELL_ORIGIN_RANDOM_LOCATION:
      pOrigin = pCaster;
      spellHeading = pCaster->GetHeading();

      if (pTargetPos)
      {
        spellLocation = *pTargetPos;
      }
      else
      {
        spellLocation = pCaster->GetLocation();
      }
      break;

    case SPELL_ORIGIN_WARD:
    {
      GAME_ASSERT(ERROR_DESIGN, SPELL_DELIVERY_SINGLE!=mDelivery, "Cannot have Single delivery type for ward spell %s (any other area type allowed)", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mWardPropID != 0, "Bad Ward Prop ID for spell %s", mID.GetName());
      tGameObjectID wardID = pTitan->GetRegistry()->Create(cGameObject::OBJ_PROP);
      cGameObject* pWard = pTitan->GetRegistry()->Fetch(wardID);
      SyAssertf(pWard != NULL, "Could not create ward object for spell %s", mID.GetName());

      if (pWard)
      {
        spellHeading = pCaster->GetHeading();
        dir.HPR(spellHeading, 0.0f, 0.0f);
        dir.Normalize();
        spellLocation = pCaster->GetLocation();
        spellLocation.MulAdd(dir, mOffset);
        const cStatsPropMaster* pMaster = pTitan->GetDatabaseSys()->GetPropMaster(mWardPropID);
        GAME_ASSERT(ERROR_DESIGN, pMaster != NULL, "Could not find ward prop master");
        static_cast<cStatsProp*>(pWard->GetStats())->SetMaster(pMaster->mID.GetName());
        pTitan->GetRegistry()->InitObject(wardID, false);
        if (!pWard->SetSafeLocation(spellLocation))
        {
          pWard->SetLocation(spellLocation);
          GAME_ASSERT(ERROR_DESIGN, false, "Could not find safe location for ward created for spell at %.2f, %.2f, %.2f", spellLocation.X, spellLocation.Y, spellLocation.Z);
        }
        pWard->SetHeading(spellHeading);
        pOrigin = pWard;

        if (mDeliveryDuration > 0.0f)
        {
          pWard->GetStats()->AddCondition("Delayed Death", mID.GetID(), pCaster->GetID(), ID_NONE, mDeliveryDuration, ID_NONE);
        }
      }

      break;
    }

    case SPELL_ORIGIN_NUM_TYPES:
    default:
      GAME_ASSERT(ERROR_DESIGN, false, "Bad origin type for spell %s", mID.GetName());
      break;
  }

  // 
  // Create area effect object
  //
  switch (mDelivery)
  {
    case SPELL_DELIVERY_SINGLE:
      if (pOrigin)
      {
        bAreaBasedConditions = false;
        pDeliverer = SyNew cAreaEffect_SingleTarget();
        static_cast<cAreaEffect_SingleTarget*>(pDeliverer)->SetDuration(duration);
        static_cast<cAreaEffect_SingleTarget*>(pDeliverer)->SetTarget(pOrigin->GetID());
      }
      break;

    case SPELL_DELIVERY_RADIUS:
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Radius spell %s needs width", mID.GetName());
      pDeliverer = SyNew cAreaEffect_Radius();
      static_cast<cAreaEffect_Radius*>(pDeliverer)->SetRadius(mWidth*0.5f);
      static_cast<cAreaEffect_Radius*>(pDeliverer)->SetLocation(spellLocation);
      static_cast<cAreaEffect_Radius*>(pDeliverer)->SetDuration(duration);
  
      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }
      break;

    case SPELL_DELIVERY_NOVA:
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Nova spell %s needs width", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, duration > 0.0f, "Nova spell %s needs a delivery duration > 0", mID.GetName());
      speed = duration > 0.0f ? (mWidth*0.5f)/duration : 1.0f;
      pDeliverer = SyNew cAreaEffect_Burst();
      static_cast<cAreaEffect_Burst*>(pDeliverer)->SetRadius(0.0f);
      static_cast<cAreaEffect_Burst*>(pDeliverer)->SetMaxRadius(mWidth*0.5f);
      static_cast<cAreaEffect_Burst*>(pDeliverer)->SetLocation(spellLocation);
      static_cast<cAreaEffect_Burst*>(pDeliverer)->SetSpeed(speed);

      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }
      break;

    case SPELL_DELIVERY_CHAIN:
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Chain spell %s needs range (max distance)", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mMaxTargets > 0, "Chain spell %s needs MaxTargets > 0", mID.GetName());
      pDeliverer = SyNew cAreaEffect_Chain();
      static_cast<cAreaEffect_Chain*>(pDeliverer)->SetDuration(duration);
      static_cast<cAreaEffect_Chain*>(pDeliverer)->SetTargetDelay(0.25f);
      static_cast<cAreaEffect_Chain*>(pDeliverer)->SetMaxDistance(mRange);
      static_cast<cAreaEffect_Chain*>(pDeliverer)->SetMaxTargets(mMaxTargets);
      break;

    case SPELL_DELIVERY_WAVE_ANGLE:
      {
        GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Wave Angle spell %s needs Width (thickness of wave)", mID.GetName());
        GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Wave Angle spell %s needs Range", mID.GetName());
        float minAngle = mAngleStart;
        float maxAngle = mAngleEnd;
        if (mAngleStart > mAngleEnd)
        {
          minAngle = mAngleEnd;
          maxAngle = mAngleStart;
        }
        GAME_ASSERT(ERROR_DESIGN, maxAngle-minAngle > 0.0f, "Wave Angle spell %s must have a angle range > 0", mID.GetName());
        dir.HPR(spellHeading + (maxAngle+minAngle)*0.5f, 0.0f, 0.0f);
        GAME_ASSERT(ERROR_DESIGN, duration > 0.0f, "Wave Angle spell %s needs a delivery duration > 0", mID.GetName());
        pDeliverer = SyNew cAreaEffect_WaveAngle();
        speed = duration > 0.0f ? mRange/duration : 1.0f;
        static_cast<cAreaEffect_WaveAngle*>(pDeliverer)->SetWave(spellLocation, dir, speed, mRange, maxAngle-minAngle, mWidth);

        if (pOrigin)
        {
          static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
        }
      }
      break;

    case SPELL_DELIVERY_WAVE_LINE:
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Wave Line spell %s needs Width", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Wave Line spell %s needs Range", mID.GetName());
      dir.HPR(spellHeading, 0.0f, 0.0f);
      GAME_ASSERT(ERROR_DESIGN, duration > 0.0f, "Wave Line spell %s needs a delivery duration > 0", mID.GetName());
      pDeliverer = SyNew cAreaEffect_WaveLine();
      speed = duration > 0.0f ? mRange/duration : 1.0f;
      static_cast<cAreaEffect_WaveLine*>(pDeliverer)->SetWave(spellLocation, dir, speed, mRange, mWidth);

      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }
      break;

    case SPELL_DELIVERY_BEAM:
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Beam spell %s needs Width", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Beam spell %s needs Range", mID.GetName());
      dir.HPR(spellHeading, 0.0f, 0.0f);
      GAME_ASSERT(ERROR_DESIGN, duration > 0.0f, "Beam spell %s needs a delivery duration > 0", mID.GetName());
      pDeliverer = SyNew cAreaEffect_Beam();
      speed = duration > 0.0f ? mRange/duration : 1.0f;
      static_cast<cAreaEffect_Beam*>(pDeliverer)->SetBeam(spellLocation, dir, speed, mRange, mWidth);

      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }
      break;

    case SPELL_DELIVERY_ARC:
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Arc spell %s needs a Range (radius) > 0", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, duration > 0.0f, "Arc spell %s needs a delivery duration > 0", mID.GetName());
      pDeliverer = SyNew cAreaEffect_Arc();
      speed = duration > 0.0f ? SY_FABS(mAngleEnd-mAngleStart)/duration : 1.0f;
      static_cast<cAreaEffect_Arc*>(pDeliverer)->SetArc(spellLocation, speed, mRange, spellHeading+mAngleStart, spellHeading+mAngleEnd, SY_PI*0.5f);

      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }

      break;

    case SPELL_DELIVERY_WALL:
    { 
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Wall spell %s needs a range (length in front of caster) specified", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Wall spell %s needs a Width", mID.GetName());

      SyVect3 center, halfLength, halfWidth;

      halfLength.HPR(spellHeading, 0.0f, 0.0f);
      halfLength *= mRange * 0.5f;
      halfWidth.Cross(halfLength, SyVect3(0.0f, 1.0f, 0.0f));
      halfWidth.Normalize();
      halfWidth *= mWidth * 0.5f;
      center = spellLocation + halfLength;

      pDeliverer = SyNew cAreaEffect_2DOrientedBox();
      static_cast<cAreaEffect_2DOrientedBox*>(pDeliverer)->SetCenter(center);
      static_cast<cAreaEffect_2DOrientedBox*>(pDeliverer)->SetHalfLength(halfLength);
      static_cast<cAreaEffect_2DOrientedBox*>(pDeliverer)->SetHalfWidth(halfWidth);
      static_cast<cAreaEffect_2DOrientedBox*>(pDeliverer)->SetDuration(duration);

      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }
      break;
    }

    case SPELL_DELIVERY_TRAIL:
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Trail spell %s needs Range (length behind caster)", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Trail spell %s needs Width", mID.GetName());
      pDeliverer = SyNew cAreaEffect_Trail();
      static_cast<cAreaEffect_Trail*>(pDeliverer)->SetDuration(duration);
      static_cast<cAreaEffect_Trail*>(pDeliverer)->SetLength(mRange);
      static_cast<cAreaEffect_Trail*>(pDeliverer)->SetWidth(mWidth);
      break;

    case SPELL_DELIVERY_CONE:
    {
      GAME_ASSERT(ERROR_DESIGN, mRange > 0.0f, "Cone spell %s needs Range", mID.GetName());
      SyVect3 dir;
      float minAngle = mAngleStart;
      float maxAngle = mAngleEnd;
      if (mAngleStart > mAngleEnd)
      {
        minAngle = mAngleEnd;
        maxAngle = mAngleStart;
      }
      GAME_ASSERT(ERROR_DESIGN, maxAngle-minAngle > 0.0f, "Cone spell %s must have a angle start-end range > 0", mID.GetName());
      dir.HPR(spellHeading + (maxAngle+minAngle)*0.5f, 0.0f, 0.0f);
      dir.Normalize();
      pDeliverer = SyNew cAreaEffect_Cone();
      pDeliverer->SetDuration(duration);
      static_cast<cAreaEffect_Cone*>(pDeliverer)->SetCone(spellLocation, dir, mRange, maxAngle-minAngle);
      static_cast<cAreaEffect_Cone*>(pDeliverer)->SetMaxTargets(mMaxTargets);

      if (pOrigin)
      {
        static_cast<cAreaEffect_Radius*>(pDeliverer)->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }
      break;
    }

    case SPELL_DELIVERY_METEOR_SHOWER:
    {
      GAME_ASSERT(ERROR_DESIGN, mWidth > 0.0f, "Meteor Shower spell %s needs width", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mDeliveryDuration > 0.0f, "Meteor Shower spell %s needs delivery duration > 0.0f", mID.GetName());
      GAME_ASSERT(ERROR_DESIGN, mMaxTargets > 0, "Meteor Shower spell %s needs max targets > 0", mID.GetName());
      cAreaEffect_MeteorShower* pShower = SyNew cAreaEffect_MeteorShower();
      pShower->SetRadius(mWidth*0.5f);
      pShower->SetLocation(spellLocation);
      pShower->SetDuration(duration);
      pShower->SetSpell(mID.GetID());
      pShower->SetSource(pCaster->GetID());
      pShower->SetSpellID(mID.GetID());

      if (pOrigin)
      {
        pShower->SetMovesWithObject(pOrigin->GetID(), mOffset);
      }

      cAreaEffectSys::Get()->Add(pShower);
      break;
    }

    case SPELL_DELIVERY_NUM_TYPES:
    default:
      GAME_ASSERT(ERROR_DESIGN, false, "Bad spell delivery type for %s", mID.GetName());
      break;
  }

  if (!pDeliverer)
  {
    // fail silently, most likely because our target died mid-casting
    return; // no targeted area of effect
  }

  // 
  // Create spell effects
  //

  cGameEffect* pEffect;

  if (NUM_COMBOS != combo)
  {
    // if we have no spell effects in the db we just use a combo attack
    GAME_ASSERT(ERROR_DESIGN, pDeliverer != NULL, "Spell that creates game effects needs a delivery type other than none");
    if (pDeliverer)
    {
      pEffect = SyNew cGameEffect_Attack();
      static_cast<cGameEffect_Attack*>(pEffect)->SetAttackIndex(combo);
      pEffect->SetSource(pCaster->GetID()); 
      pDeliverer->AddGameEffect(pEffect);
    }
  }

  for (int i=mEffects.Begin(); i!=mEffects.End(); i=mEffects.Next(i))
  {
    pEffect = mEffects(i)->CreateGameEffect(pCaster,
                                            spellLocation,
                                            mDamageType,
                                            bAreaBasedConditions,
                                            itemMasterID);

    if (pEffect)
    {
      pEffect->SetSource(pCaster->GetID()); 
      GAME_ASSERT(ERROR_DESIGN, pDeliverer != NULL, "Spell that creates game effects needs a delivery type other than none");
      if (pDeliverer)
      {
        pDeliverer->AddGameEffect(pEffect);
      }
      else
      {
        delete pEffect; // clean up because area of effect won't
      }
    }
  }

  if (pDeliverer)
  {
    pDeliverer->SetSource(pCaster->GetID());
    pDeliverer->SetSpellID(mID.GetID());
    pDeliverer->SetIgnoreSource(SPELL_TARGETING_ENEMIES == mTargeting || SPELL_TARGETING_ENEMIES_AND_PROPS == mTargeting);

    //
    // Set allowable targets
    //

    NPCFactionType faction = NPCFACTION_MONSTER;
    bool bSetDefaultPropTargets = false;

    if (pCaster->GetType() == cGameObject::OBJ_PROP)
    {
      faction = (NPCFactionType)static_cast<cStatsProp*>(pCaster->GetStats())->GetMaster()->mFaction;

      if (NPCFACTION_CLASSDEFAULT == faction || NPCFACTION_PROP == faction)
      {
        bSetDefaultPropTargets = true;

        switch (mTargeting)
        {
          case SPELL_TARGETING_ANY:
          case SPELL_TARGETING_ENEMIES_AND_PROPS:
            pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_ANY);
            break;

          case SPELL_TARGETING_ALLIES:
          case SPELL_TARGETING_ENEMIES:
            pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_CHARACTERS);
            break;

          default:
            GAME_ASSERT(ERROR_CODE, false, "Bad spell targeting type");
        }
      }
    }
    
    if (!bSetDefaultPropTargets)
    {
      if (SPELL_TARGETING_ANY == mTargeting)
      {
        pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_ANY);
      }
      else
      {
        if (pCaster->GetType() == cGameObject::OBJ_NPC)
        {
          faction = (NPCFactionType)static_cast<cStatsCharacter*>(pCaster->GetStats())->GetFaction();

          // if we're charmed switch factions
          if (pCaster->GetStats()->HasCondition("Charmed"))
          {
            if (NPCFACTION_ALLY == faction)
            {
              faction = NPCFACTION_MONSTER;
            }
            else if (NPCFACTION_MONSTER == faction)
            {
              faction = NPCFACTION_ALLY;
            }
          }
        }
        else if (pCaster->GetType() == cGameObject::OBJ_PLAYER)
        {
          faction = NPCFACTION_ALLY;
        }

        if ((SPELL_TARGETING_ALLIES == mTargeting && NPCFACTION_ALLY == faction) ||
            (SPELL_TARGETING_ENEMIES == mTargeting && NPCFACTION_MONSTER == faction))
        {
          pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_ALLIES);
        }
        else if (SPELL_TARGETING_ENEMIES_AND_PROPS == mTargeting && NPCFACTION_MONSTER == faction)
        {
          pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_ALLIES_AND_PROPS);
        }
        else if ((SPELL_TARGETING_ALLIES == mTargeting && NPCFACTION_MONSTER == faction) ||
                (SPELL_TARGETING_ENEMIES == mTargeting && NPCFACTION_ALLY == faction))
        {
          pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_MONSTERS);

          if (mDeliveryDuration > 1.0f)
          {
            pDeliverer->SetAINegativeAreaEffect(true);
          }
        }
        else if (SPELL_TARGETING_ENEMIES_AND_PROPS == mTargeting && NPCFACTION_ALLY == faction)
        {
          pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_MONSTERS_AND_PROPS);

          if (mDeliveryDuration > 1.0f)
          {
            pDeliverer->SetAINegativeAreaEffect(true);
          }
        }
        else
        {
          pDeliverer->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_ANY);
        }
      }
    }

    cAreaEffectSys::Get()->Add(pDeliverer);
  }
}

// if spell is ongoing (eg based on button press), this is called when it ends
void cSpellMaster::EndSpellEffects(cGameObject* pCaster,
                                   cGameObject* pTarget,
                                   tGameID itemMasterID) const
{
  SyAssert(pCaster != NULL);

  if (!pCaster)
  {
    return;
  }

  if (pTarget)
  {
    pTarget->GetStats()->RemoveConditions(mID.GetID(), pCaster->GetID(), itemMasterID);
  }

  pCaster->GetStats()->RemoveConditions(mID.GetID(), pCaster->GetID(), itemMasterID);
  cAreaEffectSys::Get()->Remove(mID.GetID(), pCaster->GetID());
}

void 
cSpellMaster::AppendPropertyString(cGameObject *obj,
                                   SyString &str) const
{
  for (int i = mEffects.Begin(); i != mEffects.End(); i = mEffects.Next(i))
  {
    mEffects(i)->AppendPropertyString(obj, str, mDamageType);
  }
}

void cSpellMaster::PlayImpact(cGameObject* pObj) const
{
  if (!pObj)
  {
    return;
  }

  Titan* pTitan = pObj->GetTitan();

  if (ID_NONE != mImpactFXSetID)
  {
    const cImpactFXSet* pImpactFXSet = pTitan->GetDatabaseSys()->GetImpactFXSet(mImpactFXSetID);
    GAME_ASSERT(ERROR_DESIGN, pImpactFXSet!=NULL, "Could not find impact fx set for spell %s", mID.GetName());

    if (pImpactFXSet)
    {
      pImpactFXSet->PlayImpact(pObj, true);
    }
  }

  if (ID_NONE != mImpactSoundSetID)
  {
    const cImpactSoundSet* pImpactSoundSet = pTitan->GetDatabaseSys()->GetImpactSoundSet(mImpactSoundSetID);
    GAME_ASSERT(ERROR_DESIGN, pImpactSoundSet!=NULL, "Could not find impact sound set for spell %s", mID.GetName());

    if (pImpactSoundSet)
    {
      pImpactSoundSet->PlayHitSound(pObj);
    }
  }
}

void cSpellMaster::PlayImpact(Titan* pTitan, SyCollRay& ray, SySceneFilter& filter, int waterType) const
{
  if (ID_NONE != mImpactFXSetID)
  {
    const cImpactFXSet* pImpactFXSet = pTitan->GetDatabaseSys()->GetImpactFXSet(mImpactFXSetID);

    GAME_ASSERT(ERROR_DESIGN, pImpactFXSet!=NULL, "Could not find impact efxffect set for spell %s", mID.GetName());

    if (pImpactFXSet)
    {
      pImpactFXSet->PlayImpact(pTitan, ray, filter, waterType);
    }
  }

  if (ID_NONE != mImpactSoundSetID)
  {
    const cImpactSoundSet* pImpactSoundSet = pTitan->GetDatabaseSys()->GetImpactSoundSet(mImpactSoundSetID);
    GAME_ASSERT(ERROR_DESIGN, pImpactSoundSet!=NULL, "Could not find impact sound set for spell %s", mID.GetName());

    if (pImpactSoundSet)
    {
      pImpactSoundSet->PlayHitSound(pTitan, ray, filter, waterType);
    }
  }
}

//--------------------------------------------------------------cAbilityMaster
cAbilityMaster::cAbilityMaster()
: mID(0),
  mRank(0),
  mClass(0),
  mLevelMin(0),
  mSpell(0),
  mCombo(NUM_COMBOS),
  mCost(0),
  mNameString(0),
  mDescString(0),
  mIconID(ID_NONE)
{
  for (int i=0; i<MAX_PREREQS; ++i)
  {
    mAbilityPrereqs[MAX_PREREQS] = 0;
  }
}

// EOF
