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

//-------------------------------------------------------- Includes
#include "rule_condition.h"
#include "registry.h"
#include "titan.h"
#include "database.h"
#include "spell.h"
#include "graphic.h"
#include "intel.h"
#include "stats.h"
#include "SyScene.h"
#include "SyTypes.h"
#include "areaeffect.h"
#include "physics.h"
#include "gameerror.h"

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

cRule_Condition::cRule_Condition() :
  cRule(),
  mRemainingTime(-1.0f),
  mEffectID(ID_NONE)
{
}

cRule_Condition::cRule_Condition(const cRule_Condition& rule)
{
  *this = rule;
}

void            
cRule_Condition::Init(int param1, int param2) 
{
}

void cRule_Condition::SetDuration(float t)
{
  if (t > 0.0f)
  {
    mRemainingTime = t;
  }
  else
  {
    mRemainingTime = -1.0f;
  }
}

bool 
cRule_Condition::Combine(cRule *other)
{
  if (GetSpellID() != other->GetSpellID())
  {
    return false;
  }

  if (GetSourceID() != other->GetSourceID())
  {
    return false;
  }

  if (GetItemMasterID() != other->GetItemMasterID())
  {
    return false;
  }

  if (strcmp(GetName(), other->GetName()) != 0)
  {
    return false;
  }

  if (GetSpellID() == other->GetSpellID())
  {
    if (mRemainingTime > 0.0f)
    {
      mRemainingTime = SY_MAX(mRemainingTime, static_cast<cRule_Condition*>(other)->mRemainingTime);
    }

    return true;
  }

  return false;
} 

bool 
cRule_Condition::Block(cRule *other)
{
  if (strcmp(GetName(), other->GetName()) != 0)
  {
    return false;
  }

  if (GetSpellID() == other->GetSpellID())
  {
    if (GetItemMasterID() != other->GetItemMasterID())
    {
      return false;
    }

    return true;
  }
  
  int myStackingCategory = mpTitan->GetDatabaseSys()->GetSpellMaster(GetSpellID())->mStackingCategory;
  int otherStackingCategory = mpTitan->GetDatabaseSys()->GetSpellMaster(other->GetSpellID())->mStackingCategory;
  if (myStackingCategory >= 0 && myStackingCategory == otherStackingCategory)
  {
    return true;
  }

  return false;
} 

void            
cRule_Condition::Update(float time) // returns true if it's expired
{
  if (mRemainingTime >= 0.0f) // negative time value means permanent condition
  {
    if (mRemainingTime > time)
    {
      mRemainingTime -= time;
    }
    else
    {
      mRemainingTime = 0.0f;
      SetDisabled();
    }
  }
}

bool
cRule_Condition::OnAdded(tGameObjectID target)
{
  if (ID_NONE != mEffectID)
  {
    cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

    if (pObj)
    {
      cGraphicActor* pGraphic = prop_cast<cGraphicActor*>(pObj->GetGraphic());

      if (pGraphic && pGraphic->GetActorHandle() != SyActorNull)
      {
        pGraphic->AddEffect(0, mEffectID);
      }
    }
  }

  return true;
}

bool 
cRule_Condition::OnRemoved(tGameObjectID target)
{
  if (ID_NONE != mEffectID)
  {
    cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

    if (pObj)
    {
      cGraphicActor* pGraphic = prop_cast<cGraphicActor*>(pObj->GetGraphic());

      if (pGraphic && pGraphic->GetActorHandle() != SyActorNull)
      {
        pGraphic->RemoveEffect(0, mEffectID);
      }
    }
  }

  return true;
}

//------------------------------------ cRule_AlterStat

void            
cRule_AlterStat::Init(int param1, int param2) 
{
  mStrength = param1;
}


//------------------------------------ cRuleInvisible

void 
cRule_Invisible::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Invisible"))
  {
    packet->Set(!mbWasAttacking);
  }
}


bool cRule_Invisible::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE);
  mOwnerID = target;
  mbWasAttacking = false;
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(mOwnerID);

  if (pObj)
  {
    cGraphic* pGraphic = pObj->GetGraphic();
    SyAssert(pGraphic != NULL);

    if (pGraphic && pGraphic->GetActorHandle() != SyActorNull)
    {
      pGraphic->SetVisible(false);
    }
  }

  return true;
}

bool cRule_Invisible::OnRemoved(tGameObjectID target)
{
  cRule_Condition::OnRemoved(target);

  SyAssert(mOwnerID == target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj)
  {
    cGraphic* pGraphic = pObj->GetGraphic();
    SyAssert(pGraphic != NULL);

    if (pGraphic && pGraphic->GetActorHandle() != SyActorNull)
    {
      pGraphic->SetVisible(true);
    }
  }

  mOwnerID = ID_NONE;
  return true;
}

void cRule_Invisible::Update(float time)
{
  cRule_Condition::Update(time);

  cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);
  if (!pOwner)
  {
    return;
  }

  cGraphicCharacter* pCharGraphic = prop_cast<cGraphicCharacter*>(pOwner->GetGraphic());

  if (pCharGraphic)
  {
    bool bIsAttacking = pCharGraphic->GetAnimController()->IsAttacking();

    if (bIsAttacking && !mbWasAttacking)
    {
      pCharGraphic->SetVisible(true);
    }
    else if (mbWasAttacking && !bIsAttacking)
    {
      pCharGraphic->SetVisible(false);
    }

    mbWasAttacking = bIsAttacking;
  }
}


//------------------------------------ cRule_Stunned
void 
cRule_Stunned::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Stunned"))
  {
    packet->Set(true);
  }
}

void
cRule_Stunned::CalcAttackSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "Calc Attack Speed", GetName());
}

void
cRule_Stunned::CalcMovementSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "Calc Movement Speed", GetName());
}

//------------------------------------ cRule_Sleeping

void 
cRule_Sleeping::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Sleeping"))
  {
    packet->Set(true);
  }
  else if (packet->IsFlag("Stunned"))
  {
    packet->Set(true);
  }
};

void cRule_Sleeping::OnApplyDamage(cDamagePacket *packet)
{
  if (packet->GetTotal() > 0)
  {
    // don't wake up if damage is from same spell and same person
    if (packet->mAttackerID != GetSourceID() ||
        packet->mSpellID != GetSpellID())
    {
      SetDisabled();
    }
  }
}

void
cRule_Sleeping::CalcAttackSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "Calc Attack Speed", GetName());
}

void
cRule_Sleeping::CalcMovementSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "Calc Movement Speed", GetName());
}

//------------------------------------ cRule_Frozen
void cRule_Frozen::OnApplyDamage(cDamagePacket *packet)
{
  if (packet->GetTotal() > 0)
  {
    cGameObject* pObj = mpTitan->GetRegistry()->Fetch(packet->mDefenderID);

    if (pObj &&
        (pObj->GetType() == cGameObject::OBJ_NPC ||
         pObj->GetType() == cGameObject::OBJ_PLAYER))
    {
      SyAssert(prop_cast<cStatsCharacter*>(pObj->GetStats())!=NULL);
      static_cast<cStatsCharacter*>(pObj->GetStats())->Die(packet->mAttackerID);
    }

    SetDisabled();
  }
}

void
cRule_Frozen::CalcAttackSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "Calc Attack Speed", GetName());
}

void
cRule_Frozen::CalcMovementSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "Calc Movement Speed", GetName());
}

void 
cRule_Frozen::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Frozen"))
  {
    packet->Set(true);
  }
  else if (packet->IsFlag("Stunned"))
  {
    packet->Set(true);
  }
}

bool
cRule_Frozen::Combine(cRule *other)
{
  if (cRule_Condition::Combine(other))
  {
    return true;
  }
  
  if (strcmp(other->GetName(), "On Fire") == 0)
  {
    SetDisabled();
    return true;
  }

  return false;
}


//------------------------------------ cRule_Silence

void 
cRule_Silence::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Silenced"))
  {
    packet->Set(true);
  }
}

//------------------------------------ cRule_Bind

void 
cRule_Bind::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Bound"))
  {
    packet->Set(true);
  }
}

void
cRule_Bind::CalcAttackSpeed(cCalcPacket *packet)
{
  packet->SetTotal(0, "CalcAttackSpeed", GetName());
}


//------------------------------------ cRule_Fear
bool cRule_Fear::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj && pObj->GetType() == cGameObject::OBJ_NPC)
  {
    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pObj->GetIntel());
    SyAssert(pIntel != NULL);

    if (pIntel)
    {
      pIntel->GetAi()->Flee(100, mSourceID, mRemainingTime, (float)GetStrength());
    }
  }

  return true;
}


//------------------------------------ cRule_Immunity

void cRule_Immunity::Init(int param1, int param2)
{
  mRuleNameID = param1;
}

bool cRule_Immunity::Combine(cRule *other)
{
  float oldTime = mRemainingTime;

  if (cRule_Condition::Combine(other))
  {
    SyAssert(strcmp(GetName(), other->GetName()) == 0);
    if (mRuleNameID == static_cast<cRule_Immunity*>(other)->mRuleNameID)
    {
      return true;
    }
    else
    {
      mRemainingTime = oldTime;
      return false;
    }
  }

  if (((tGameID)SyHashResourceID(other->GetName())) == mRuleNameID)
  {
    return true;
  }

  return false;
}

bool cRule_Immunity::Block(cRule *other)
{
  if (cRule_Condition::Block(other))
  {
    SyAssert(strcmp(GetName(), other->GetName()) == 0);
    if (mRuleNameID == static_cast<cRule_Immunity*>(other)->mRuleNameID)
    {
      return true;
    }
    else
    {
      return false;
    }
  }

//  if (((tGameID)SyHashResourceID(other->GetName())) == mRuleNameID)
//  {
//    return true;
//  }

  return false;
}

//------------------------------------ cRule_KnockdownImmunity

void 
cRule_KnockdownImmunity::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Knockdown Immunity"))
  {
    packet->Set(true);
  }
}

//------------------------------------ cRule_KnockbackImmunity

void 
cRule_KnockbackImmunity::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Knockback Immunity"))
  {
    packet->Set(true);
  }
}

//------------------------------------ cRule_VortexImmunity

void 
cRule_VortexImmunity::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Vortex Immunity"))
  {
    packet->Set(true);
  }
}

//------------------------------------ cRule_KillImmunity

void 
cRule_KillImmunity::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Kill Immunity"))
  {
    packet->Set(true);
  }
}

//------------------------------------ cRule_Illusion
bool cRule_Illusion::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj)
  {
    cStatsCharacter* pStats = prop_cast<cStatsCharacter*>(pObj->GetStats());
    cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

    if (pStats && pGraphic)
    {
      mOldMasterID = pStats->GetMaster()->mID.GetID();
  
      const cSpellMaster* pSpell = mpTitan->GetDatabaseSys()->GetSpellMaster(mSpellID);

      if (pSpell)
      {
        for (int i=pSpell->mEffects.Begin(); i!=pSpell->mEffects.End(); i=pSpell->mEffects.Next(i))
        {
          if (cSpellEffectMaster::SPELLEFFECT_ILLUSION == pSpell->mEffects(i)->mType)
          {
            GAME_ASSERT(ERROR_DESIGN, pSpell->mEffects(i)->mObjectNameID.GetName()!=NULL, "Illusion spell effect has bad object/model name");
            pStats->SetMaster(pSpell->mEffects(i)->mObjectNameID.GetName());
            pGraphic->Release();
            pGraphic->Init();
            break;
          }
        }
      }
    }
  }

  return true;
}

bool cRule_Illusion::OnRemoved(tGameObjectID target)
{
  cRule_Condition::OnRemoved(target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj)
  {
    cStatsCharacter* pStats = prop_cast<cStatsCharacter*>(pObj->GetStats());
    cGraphicCharacter* pGraphic = prop_cast<cGraphicCharacter*>(pObj->GetGraphic());

    if (pStats && pGraphic)
    {
      const cStatsCharacterMaster* pMaster = mpTitan->GetDatabaseSys()->GetCharacterMaster(mOldMasterID);

      if (pMaster)
      {
        pStats->SetMaster(pMaster->mID.GetName());
        pGraphic->Release();
        pGraphic->Init();
      }
    }
  }

  return true;
}

//------------------------------------ cRule_Confused

void 
cRule_Confused::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Confused"))
  {
    packet->Set(true);
  }
}

bool cRule_Confused::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE);
  mOwnerID = target;
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj && !pObj->IsRemote() && pObj->GetType() == cGameObject::OBJ_NPC)
  {
    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pObj->GetIntel());
    SyAssert(pIntel != NULL);

    if (pIntel)
    {
      // reset attack target to allow confused target logic to kick in
      pIntel->GetAi()->SetAttackTarget(ID_NONE);
      pIntel->GetAi()->SetAttackTarget(PickRandomTarget());
    }
  }

  return true;
}

bool cRule_Confused::OnRemoved(tGameObjectID target)
{
  cRule_Condition::OnRemoved(target);

  SyAssert(mOwnerID == target);
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj && !pObj->IsRemote() && pObj->GetType() == cGameObject::OBJ_NPC)
  {
    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pObj->GetIntel());
    SyAssert(pIntel != NULL);

    if (pIntel)
    {
      // reset attack target to allow confused target logic to kick in
      pIntel->GetAi()->SetAttackTarget(ID_NONE);
    }
  }

  mOwnerID = ID_NONE;

  return true;
}

void cRule_Confused::Update(float time)
{
  SyAssert(mOwnerID != ID_NONE);
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(mOwnerID);

  if (pObj && !pObj->IsRemote())
  {
    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pObj->GetIntel());
    SyAssert(pIntel != NULL);

    if (pIntel && !pIntel->GetAi()->GetAttackTarget())
    {
      pIntel->GetAi()->SetAttackTarget(PickRandomTarget());
    }
  }

  cRule_Condition::Update(time);
}

tGameObjectID cRule_Confused::PickRandomTarget()
{
  SyAssert(mOwnerID != ID_NONE);
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(mOwnerID);

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

  cGameObjectRegistry *pRegistry = pObj->GetRegistry();
  float distSqr;
  float maxDistSqr = 25.0f*25.0f;
  float closestDistSqr = 0.0f;
  cGameObject *closest = NULL;

  SyVect3 myLoc(pObj->GetLocation());

  // could also search each object type one at a time
  cGameObject* pTarget = pRegistry->Begin();
  for(;pTarget != NULL;pTarget= pRegistry->Next(pTarget))
  {
    if (!pTarget || pTarget == pObj || pTarget->GetStats()->IsDead())
    {
      continue;
    }

    if (pTarget->GetType() != cGameObject::OBJ_NPC && pTarget->GetType() != cGameObject::OBJ_PLAYER)
    {
      continue;
    }

    distSqr = myLoc.DistanceSquared(pTarget->GetLocation());

    if (distSqr > maxDistSqr)
    {
      continue;
    }

    if (NULL == closest || distSqr < closestDistSqr)
    {
      closest = pTarget;
      closestDistSqr = distSqr;
    }
  }

  if (closest)
  {
    return closest->GetID();
  }

  return ID_NONE;
}

//------------------------------------ cRule_Charmed

void 
cRule_Charmed::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Charmed"))
  {
    packet->Set(true);
  }
}

bool cRule_Charmed::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj && pObj->GetType() == cGameObject::OBJ_NPC)
  {
    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pObj->GetIntel());
    SyAssert(pIntel != NULL);

    if (pIntel)
    {
      // reset attack target to allow confused target logic to kick in
      pIntel->GetAi()->SetAttackTarget(ID_NONE);
    }
  }

  return true;
}

bool cRule_Charmed::OnRemoved(tGameObjectID target)
{
  cRule_Condition::OnRemoved(target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj && pObj->GetType() == cGameObject::OBJ_NPC)
  {
    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pObj->GetIntel());
    SyAssert(pIntel != NULL);

    if (pIntel)
    {
      // reset attack target to allow uncharmed target logic to kick in
      pIntel->GetAi()->SetAttackTarget(ID_NONE);
    }
  }

  return true;
}

//------------------------------------ cRule_HealthDrain
void cRule_HealthDrain::Update(float time)
{
  cRule_Condition::Update(time);

  if (mDrain > 0)
  {
    cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);

    if (pOwner &&
        !pOwner->GetStats()->IsDead() &&
        !pOwner->IsRemote())
    {
      cCalcPacket packet;
      packet.SetTotal(mDrain, "Add Health Drained", GetName());
      pOwner->GetStats()->ApplyHealing(&packet);
    }

    mDrain = 0;
  }
}

bool cRule_HealthDrain::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE && target != ID_NONE);
  mOwnerID = target;
  return true;
}

void cRule_HealthDrain::OnHit(cHitPacket *packet)
{
  // should be called when attacker hits target, on attacker
  SyAssert(mOwnerID == packet->mAttackerID);
  mDrain += packet->GetTotal();
}

//------------------------------------ cRule_ManaDrain
void cRule_ManaDrain::Update(float time)
{
  cRule_Condition::Update(time);

  if (mDrain > 0)
  {
    cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);

    if (pOwner &&
        !pOwner->GetStats()->IsDead() &&
        pOwner->IsRemote())
    {
      cCalcPacket packet;
      packet.mPacketType = cRulePacket::EVENT_APPLY_MANA_COST;
      packet.SetTotal(-mDrain, "Add Mana Drained", GetName());
      pOwner->GetStats()->ApplyManaCost(&packet);
    }

    mDrain = 0;
  }
}

bool cRule_ManaDrain::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE && target != ID_NONE);
  mOwnerID = target;
  return true;
}

void cRule_ManaDrain::OnHit(cHitPacket *packet)
{
  // should be called when attacker hits target, on attacker
  mDrain += packet->GetTotal();
}

//---------------------------------------------- class cRule_DamageShieldPercent
void
cRule_DamageShieldPercent::Init(int param1, int param2)
{
  mPercent = param1;
}

void
cRule_DamageShieldPercent::OnApplyDamage(cDamagePacket *packet)
{
  if (packet->mbBlocked)
  {
    return;
  }

  cGameObject* pAttacker = mpTitan->GetRegistry()->Fetch(packet->mAttackerID);

  if (!pAttacker)
  {
    return;
  }

  int damage = SY_ROUND(((float)packet->GetTotal()) * ((float)mPercent*0.01f));

  cDamagePacket reflectedDamage;
  reflectedDamage.SetTotal(damage, "Damage reflected from shield", GetName());
  reflectedDamage.mAttackerID = packet->mDefenderID;
  reflectedDamage.mDefenderID = packet->mAttackerID;
  reflectedDamage.mSpellID = GetSpellID();
  pAttacker->GetStats()->ApplyDamage(&reflectedDamage);
}

//---------------------------------------------- class cRule_ManaShield
void
cRule_ManaShieldPercent::Init(int param1, int param2)
{
  mPercent = param1;
}

void
cRule_ManaShieldPercent::OnApplyDamage(cDamagePacket *packet)
{
  if (packet->mbBlocked)
  {
    return;
  }

  cGameObject* pDefender = mpTitan->GetRegistry()->Fetch(packet->mDefenderID);

  if (pDefender->GetType() == cGameObject::OBJ_NPC || 
      pDefender->GetType() == cGameObject::OBJ_PLAYER)
  {
    cStatsCharacter* pDefenderStats = static_cast<cStatsCharacter*>(pDefender->GetStats());

    int mana = pDefenderStats->GetMana();
    int convert = mPercent==100?packet->GetTotal():(SY_ROUND(((float)packet->GetTotal())*((float)mPercent)*0.01f));
    int manaCost = convert;
    if (convert > mana)
    {
      manaCost = mana;
      convert -= mana;     
    }

    packet->SetTotal(packet->GetTotal()-convert, "Converted to Mana", GetName());

    cCalcPacket manaCalc;
    manaCalc.SetTotal(manaCost, "Mana Converted Damage", GetName());
    pDefenderStats->ApplyManaCost(&manaCalc);
  }
}

//---------------------------------------------- class cRule_TargetMe
void cRule_TargetMe::QueryFlag(cFlagPacket* packet)
{
  if (packet->IsFlag("Target Me"))
  {
    packet->Set(true);
  }
}

//---------------------------------------------- class cRule_RandomTeleport
void
cRule_RandomTeleport::Init(int param1, int param2)
{
  mDist = ((float)param1)*0.01f; // assumes centimeters
  mHeading = mpTitan->RandomFloat()*SY_PI*2.0f;
  mNumTests = 0;
}

void
cRule_RandomTeleport::Update(float time)
{
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(mOwnerID);

  static const int MAX_TESTS = 40;

  if (!pObj || pObj->IsRemote() || mNumTests > MAX_TESTS)
  {
    SetDisabled();
    return;
  }

  bool bFoundTeleport = false;
  SyVect3 testPos(mStart);
  testPos.X += SY_SIN(mHeading) * mDist;
  testPos.Z += SY_COS(mHeading) * mDist;

  SyCollRay ray;
  SySceneFilter filter;
  ray.Init(mStart+SyVect3(0.0f, 1.0f, 0.0f), testPos+SyVect3(0.0f, 1.0f, 0.0f));
  filter.Init();
  if (!mpTitan->GetScene()->Collide(ray, filter))
  {
    ray.Init(testPos+SyVect3(0.0f, 1.0f, 0.0f), testPos+SyVect3(0.0f, -5.0f, 0.0f));
    filter.Init();
    if (mpTitan->GetScene()->Collide(ray, filter) > 0 && ray.GetHitPoint().Y < 0.8f)
    {
      bFoundTeleport = true;
      testPos.Y += 0.1f;
    }
  }

  if (bFoundTeleport)
  {
    pObj->SetLocation(testPos);
    SetDisabled();
  }
  else
  {
    ++mNumTests;
    mHeading += SY_DEG_TO_RAD(11.0f); 
  }
}

bool
cRule_RandomTeleport::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE);
  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj)
  {
    mOwnerID = target;
    mStart = pObj->GetLocation();
  }

  return true;
}


void cRule_RandomTeleport::QueryFlag(cFlagPacket* packet)
{
  if (packet->IsFlag("Teleporting"))
  {
    packet->Set(true);
  }
}

//---------------------------------------------- class cRule_DirectionalTeleport
void
cRule_DirectionalTeleport::Init(int param1, int param2)
{
  mDist = ((float)param1)*0.01f; // assumes meters
  mHeading = SY_DEG_TO_RAD((float)param2);
  mNumTests = 0;
}


//---------------------------------------------- class cRule_Resurrecting

bool
cRule_Resurrecting::OnRemoved(tGameObjectID target)
{
  cRule_Condition::OnRemoved(target);

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(target);

  if (pObj && !pObj->IsRemote())
  {
    SyVect3 loc(pObj->GetLocation());
    SyVect3 hpr(pObj->GetHPR());
    pObj->Reset();
    pObj->SetLocation(loc);
    pObj->SetHPR(hpr);
    pObj->GetGraphic()->SetVisible(true);    
  }

  return true;
}


//---------------------------------------------- class cRule_DelayedSpell
void
cRule_DelayedSpell::Init(int param1, int param2)
{
  mDelayedSpellID = param1;
}

void
cRule_DelayedSpell::Update(float time)
{
  cRule_Condition::Update(time);

  if (mRemainingTime <= 0.0f)
  {
    cGameObject* pTarget = mpTitan->GetRegistry()->Fetch(mOwnerID);
    cGameObject* pSource = mpTitan->GetRegistry()->Fetch(mSourceID);

    if (pTarget &&
        !pTarget->GetStats()->IsDead() &&
        !pTarget->IsRemote() &&
        pSource)
    {
      const cSpellMaster* pSpell = mpTitan->GetDatabaseSys()->GetSpellMaster(mDelayedSpellID);
      SyAssert(pSpell);

      if (pSpell)
      {
        if (cSpellMaster::SPELL_ORIGIN_CASTER_LOCATION == pSpell->mOrigin)
        {
          pSpell->CastSpell(pSource, pTarget, &mSpellTargetPos, NUM_COMBOS, GetItemMasterID());
        }
        else
        {
          pSpell->CastSpell(pSource, pTarget, NULL, NUM_COMBOS, GetItemMasterID());
        }
      }
    }

    SetDisabled();
  }
}

bool
cRule_DelayedSpell::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE);
  mOwnerID = target;

  const cSpellMaster* pSpell = mpTitan->GetDatabaseSys()->GetSpellMaster(mDelayedSpellID);
  SyAssert(pSpell);

  if (pSpell && cSpellMaster::SPELL_ORIGIN_CASTER_LOCATION == pSpell->mOrigin)
  {
    cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);
    mSpellTargetPos.HPR(pOwner->GetHeading(), 0.0f, 0.0f);
    mSpellTargetPos.Normalize();
    mSpellTargetPos *= pSpell->mOffset;
    mSpellTargetPos += pOwner->GetLocation();
  }

  return true;
}

bool cRule_DelayedSpell::Combine(cRule *other)
{
  float oldTime = mRemainingTime;

  if (cRule_Condition::Combine(other))
  {
    SyAssert(strcmp(GetName(), other->GetName()) == 0);
    if (mDelayedSpellID == static_cast<cRule_DelayedSpell*>(other)->mDelayedSpellID)
    {
      return true;
    }
    else
    {
      mRemainingTime = oldTime;
      return false;
    }
  }

  return false;
}

bool cRule_DelayedSpell::Block(cRule *other)
{
  if (cRule_Condition::Block(other))
  {
    SyAssert(strcmp(GetName(), other->GetName()) == 0);
    if (mDelayedSpellID == static_cast<cRule_DelayedSpell*>(other)->mDelayedSpellID)
    {
      return true;
    }
    else
    {
      return false;
    }
  }

  return false;
}

//---------------------------------------------- class cRule_DelayedAttackSpell

void
cRule_DelayedAttackSpell::Update(float time)
{
  cRule_Condition::Update(time);
}

void cRule_DelayedAttackSpell::OnHit(cHitPacket *packet)
{
  if (packet->mAttackerID != mOwnerID)
  {
    return;
  }

  cGameObject* pTarget = mpTitan->GetRegistry()->Fetch(packet->mDefenderID);
  cGameObject* pSource = mpTitan->GetRegistry()->Fetch(packet->mAttackerID);

  if (pTarget && pSource && !pSource->IsRemote())
  {
    const cSpellMaster* pSpell = mpTitan->GetDatabaseSys()->GetSpellMaster(mDelayedSpellID);
    SyAssert(pSpell);

    if (pSpell)
    {
      pSpell->CastSpell(pSource, pTarget, NULL, NUM_COMBOS, GetItemMasterID());
    }
  }
}

//---------------------------------------------- class cRule_DelayedDefendSpell

void
cRule_DelayedDefendSpell::Update(float time)
{
  cRule_Condition::Update(time);
}

void cRule_DelayedDefendSpell::OnApplyDamage(cDamagePacket *packet)
{
  if (packet->mbBlocked || packet->mDefenderID != mOwnerID)
  {
    return;
  }

  cGameObject* pTarget = mpTitan->GetRegistry()->Fetch(packet->mAttackerID);
  cGameObject* pSource = mpTitan->GetRegistry()->Fetch(packet->mDefenderID);

  if (pTarget && 
      !pTarget->IsRemote() &&
      pSource)
  {
    const cSpellMaster* pSpell = mpTitan->GetDatabaseSys()->GetSpellMaster(mDelayedSpellID);
    SyAssert(pSpell);

    if (pSpell)
    {
      pSpell->CastSpell(pSource, pTarget, NULL, NUM_COMBOS, GetItemMasterID());
    }
  }
}

//---------------------------------------------- class cRule_DelayedDeathSpell

void
cRule_DelayedDeathSpell::Update(float time)
{
  cRule_Condition::Update(time);

  cGameObject* pTarget = mpTitan->GetRegistry()->Fetch(mOwnerID);
  cGameObject* pSource = mpTitan->GetRegistry()->Fetch(GetSourceID());
  SyAssertf(pSource!=NULL, "Trigger delayed death spell has no casting source");

  if (pSource && pTarget && pTarget->GetStats()->IsDead() && !pTarget->IsRemote())
  {
    const cSpellMaster* pSpell = mpTitan->GetDatabaseSys()->GetSpellMaster(mDelayedSpellID);
    SyAssert(pSpell);

    if (pSpell)
    {
      pSpell->CastSpell(pSource, pTarget, NULL, NUM_COMBOS, GetItemMasterID());
    }

    SetDisabled();
  }
}

//---------------------------------------------- class cRule_DelayedDeath
void
cRule_DelayedDeath::Update(float time)
{
  float oldRemainTime = mRemainingTime;

  cRule_Condition::Update(time);

  if (oldRemainTime > 0.0f && mRemainingTime <= 0.0f)
  {
    cGameObject* pTarget = mpTitan->GetRegistry()->Fetch(mOwnerID);
    
    if (pTarget &&
        !pTarget->GetStats()->IsDead() &&
        !pTarget->IsRemote())
    {
      pTarget->GetStats()->Die(ID_NONE);
    }
  }
}

bool
cRule_DelayedDeath::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE);
  mOwnerID = target;
  return true;
}


//------------------------------------ cRuleInvunerable
void
cRule_Invulnerable::OnApplyDamage(cDamagePacket *packet)
{
  packet->SetTotal(0,"Invulnerable",GetName());
}

void cRule_Invulnerable::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("Vortex Immunity"))
  {
    packet->Set(true);
  }
  else if (packet->IsFlag("Knockback Immunity"))
  {
    packet->Set(true);
  }
  else if (packet->IsFlag("Knockdown Immunity"))
  {
    packet->Set(true);
  }
  else if (packet->IsFlag("Kill Immunity"))
  {
    packet->Set(true);
  }
}

//------------------------------------ cRule_DamageDone
void cRule_DamageDone::Init(int param1, int param2)
{
  mDmgMult = ((float)param1)*0.01f;
}

void
cRule_DamageDone::CalcDamageRaw(cDamagePacket *packet)
{
  int original = packet->GetTotal();
  packet->MultiplyTotal(mDmgMult, "Damage Done Percentage", GetName());
  packet->AddTotal(original, "Damage Done Percentage", GetName());
}

//------------------------------------ cRule_DamageTaken
void cRule_DamageTaken::Init(int param1, int param2)
{
  mDmgMult = ((float)param1)*0.01f;
}

void
cRule_DamageTaken::CalcDamageReduced(cDamagePacket *packet)
{
  int original = packet->GetTotal();
  packet->MultiplyTotal(mDmgMult, "Damage Taken Percentage", GetName());
  packet->AddTotal(original, "Damage Taken Percentage", GetName());
}


//------------------------------------ cRule_AlterAttackSpeed
void
cRule_AlterAttackSpeed::CalcAttackSpeed(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcAttackSpeed", GetName());
}

//------------------------------------ cRule_AlterMovementSpeed
void
cRule_AlterMovementSpeed::CalcMovementSpeed(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcMovementSpeed", GetName());
}

//------------------------------------ cRule_AlterAttackPower
void
cRule_AlterAttackPower::CalcDamageInitial(cDamagePacket *packet)
{
  if (DT_NONE == packet->GetDamageType())
  {
    float delta = ((float)packet->GetTotal()) * GetStrengthNormalized();
    packet->AddTotal(SY_ROUND(delta), "CalcDamageInitial", GetName());
  }
}

//------------------------------------ cRule_AlterSpellPower
void
cRule_AlterSpellPower::CalcSpellPower(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcSpellPower", GetName());
}

//------------------------------------ cRule_AlterBlockPower
void
cRule_AlterBlockPower::CalcBlockMax(cCalcPacket *packet)
{
  float delta = ((float)packet->GetTotal()) * GetStrengthNormalized();
  packet->AddTotal(SY_ROUND(delta), "CalcBlockMax", GetName());
}

void
cRule_AlterBlockPower::CalcBlockRegen(cCalcPacket *packet)
{
  float delta = ((float)packet->GetTotal()) * GetStrengthNormalized();
  packet->AddTotal(SY_ROUND(delta), "CalcBlockRegen", GetName());
}

void
cRule_AlterBlockPower::CalcStrikethrough(cDamagePacket *packet)
{
  float delta = ((float)packet->GetTotal()) * GetStrengthNormalized();
  packet->AddTotal(SY_ROUND(delta), "CalcStrikethrough", GetName());
}

//------------------------------------ cRule_AlterCriticalChance

void 
cRule_AlterCriticalChance::Init(int param1, int param2)
{
  cRule_AlterStat::Init(param1, param2);
  mbRanged = param2 > 0;
}

void
cRule_AlterCriticalChance::CalcCriticalChance(cDamagePacket *packet)
{
  SyAssert(packet);
  if ((mbRanged && packet->mbRanged) ||
      (!mbRanged && !packet->mbRanged))
  {
    packet->AddTotal(GetStrength(), "Calc Critical Chance", GetName());
  }
}

//------------------------------------ cRule_AlterDodgeChance

void 
cRule_AlterDodgeChance::Init(int param1, int param2)
{
  cRule_AlterStat::Init(param1, param2);
  mbRanged = param2 > 0;
}

void
cRule_AlterDodgeChance::CalcDodgeChance(cDamagePacket *packet)
{
  SyAssert(packet);
  if ((mbRanged && packet->mbRanged) ||
      (!mbRanged && !packet->mbRanged))
  {
    packet->AddTotal(GetStrength(), "Calc Dodge Chance", GetName());
  }
}

//------------------------------------ cRule_AlterDefense

void            
cRule_AlterDefense::Init(int param1, int param2) 
{
  cRule_AlterStat::Init(param1, param2);

  mDmgType = (eDamageType)param2;
}

void cRule_AlterDefense::CalcDefense(cDamagePacket *packet)
{
  if (DT_NONE == mDmgType || mDmgType == packet->GetDamageType())
  {
    packet->AddTotal(GetStrength(), "CalcDefense", GetName());
  }
}


//------------------------------------ cRule_AlterSize
const float cRule_AlterSize::ANIMATE_SIZE_TIME = 1.5f;

cRule_AlterSize::cRule_AlterSize()
: cRule_AlterStat(),
  mOwnerID(ID_NONE),
  mTimer(0.0f),
  mbStartDisable(false)
{
}

bool cRule_AlterSize::Combine(cRule *other)
{
  if (cRule_AlterStat::Combine(other))
  {
    cRule_AlterSize* pOtherSize = static_cast<cRule_AlterSize*>(other);
    if (mbStartDisable && !pOtherSize->mbStartDisable)
    {
      mbStartDisable = false;
      mDisabled = false;
      mTimer = ANIMATE_SIZE_TIME - mTimer;
    }

    return true;
  }

  return false;
}

void cRule_AlterSize::SetDisabled()
{
  mbStartDisable = true;
  mTimer = 0.0f;
}

bool cRule_AlterSize::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(ID_NONE == mOwnerID);
  mOwnerID = target;
  mTimer = 0.0f;
  return true;
}

void cRule_AlterSize::Update(float time)
{
  if (mRemainingTime != 0.0f)
  {
    cRule_AlterStat::Update(time);
  }

  cGameObject* pObj = mpTitan->GetRegistry()->Fetch(mOwnerID);

  if (!pObj)
  {
    mDisabled = true;
    mOwnerID = ID_NONE;
    return;
  }

  if (mTimer < ANIMATE_SIZE_TIME && (mTimer + time) > ANIMATE_SIZE_TIME)
  {
    time -= mTimer - ANIMATE_SIZE_TIME;
    mTimer = ANIMATE_SIZE_TIME;
  }
  else
  {
    mTimer += time;
  }

  if (mTimer <= ANIMATE_SIZE_TIME)
  {
    SyActorHandle handle = pObj->GetGraphic()->GetActorHandle();

    if (SyActorNull != handle)
    {
      float32 scale = mpTitan->GetScene()->GetActorScale(handle);
      float32 scaleAdjust = (time/ANIMATE_SIZE_TIME)*(GetStrengthNormalized());

      if (mbStartDisable)
      {
        scaleAdjust = -scaleAdjust;
      }

      mpTitan->GetScene()->SetActorScale(handle, scale+scaleAdjust);
    }
  }
  else if (mbStartDisable)
  {
    mDisabled = true;
    mOwnerID = ID_NONE;
  }
}

//------------------------------------ cRule_AlterHealthRegen
void 
cRule_AlterHealthRegen::CalcHealthRegen(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength()*1000, "CalcHealthRegen", GetName());
}

//------------------------------------ cRule_AlterManaRegen
void 
cRule_AlterManaRegen::CalcManaRegen(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength()*1000, "CalcManaRegen", GetName());
}

//------------------------------------ cRule_AlterExperienceGain
void 
cRule_AlterExperienceGain::CalcExperienceGain(cCalcPacket *packet)
{
  int original = packet->GetTotal();
  packet->MultiplyTotal(GetStrengthNormalized(), "CalcExperienceGain", GetName());
  packet->AddTotal(original, "CalcExperienceGain", GetName());
}

//------------------------------------ cRule_AlterHealthAbsorb
void
cRule_AlterHealthAbsorb::CalcHealthAbsorb(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcHealthAbsorb", GetName());
}

//------------------------------------ cRule_AlterManaAbsorb
void
cRule_AlterManaAbsorb::CalcManaAbsorb(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcManaAbsorb", GetName());
}

//------------------------------------ cRule_AlterEssenceAbsorb
void
cRule_AlterEssenceAbsorb::CalcEssenceAbsorb(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcEssenceAbsorb", GetName());
}

//------------------------------------ cRule_AlterHealthDropChance
void
cRule_AlterHealthDropChance::CalcHealthDropChance(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcHealthDropChance", GetName());
}

//------------------------------------ cRule_AlterManaDropChance
void
cRule_AlterManaDropChance::CalcManaDropChance(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcManaDropChance", GetName());
}

//------------------------------------ cRule_AlterEssenceDropChance
void
cRule_AlterEssenceDropChance::CalcEssenceDropChance(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcEssenceDropChance", GetName());
}

//------------------------------------ cRule_AlterItemDropChance
void
cRule_AlterItemDropChance::CalcItemDropChance(cCalcPacket *packet)
{
  packet->AddTotal(GetStrength(), "CalcItemDropChance", GetName());
}

//---------------------------------------------- class cRule_DamageOverTime

void            
cRule_DamageOverTime::Init(int param1, int param2) 
{
  mDmgPerSec = param1;
  mDmgType = (eDamageType)param2;
  mTimer = 1.1f;
  mOwnerID = ID_NONE;
}

void            
cRule_DamageOverTime::Update(float time)
{
  mTimer += time;

  if (mTimer > 1.0f)
  {
    mTimer = 0.0f;

    cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);

    if (pOwner && !pOwner->IsRemote())
    {
      cDamagePacket damage;
      damage.AddTotal(mDmgPerSec,"Condition",GetName());
      damage.mbRanged = true;
      damage.mAttackerID = GetSourceID();
      damage.mDefenderID = mOwnerID;
      damage.mPacketType = cRulePacket::CALC_DAMAGE_REDUCED;
      damage.mbMagic = true;
      damage.mSpellID = GetSpellID();
      damage.SetDamageType(mDmgType);
      damage.mRecoveryTime = 0.0f;

      pOwner->GetStats()->ProcessRulePacket(&damage);

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

  cRule_Condition::Update(time);
}

bool cRule_DamageOverTime::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE && target != ID_NONE);
  mOwnerID = target;
  return true;
}

//------------------------------------ cRule_OnFire

cRule_OnFire::cRule_OnFire()
: cRule_DamageOverTime(),
  mAreaEffectID(ID_NONE),
  mLifetime(10.0f),
  mbOwnerDied(false)
{
}

void 
cRule_OnFire::QueryFlag(cFlagPacket *packet)
{
  if (packet->IsFlag("On Fire"))
  {
    packet->Set(true);
  }
}

void 
cRule_OnFire::Init(int param1, int param2)
{
  cRule_DamageOverTime::Init(param1, param2);

  mLifetime = mRemainingTime;
}

void 
cRule_OnFire::Update(float time)
{
  cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);

  if (!pOwner)
  {
    SetDisabled();
    return;
  }
  
  float oldTimer = mTimer;
  cRule_DamageOverTime::Update(time);

  if (ID_NONE == mAreaEffectID && 
      oldTimer != 0.0f &&
      mTimer == 0.0f)
  {
    cAreaEffect_Radius* pFireArea = SyNew cAreaEffect_Radius();
    pFireArea->SetSource(mOwnerID);
    pFireArea->SetLocation(pOwner->GetLocation());
    pFireArea->SetMovesWithObject(mOwnerID);
    pFireArea->SetRadius(pOwner->GetPhysics()->GetCollisionRadius()*1.5f);
    pFireArea->SetDuration(mLifetime);

    mAreaEffectID = pFireArea->GetID();

    cGameEffect_AddCondition* pFire = SyNew cGameEffect_AddCondition();
    pFire->SetCondition("On Fire", GetSpellID(), false, false, mLifetime, mEffectID, mDmgPerSec);
    pFire->SetSource(mOwnerID);

    pFireArea->AddGameEffect(pFire);
    cAreaEffectSys::Get()->Add(pFireArea);

    cIntelNPC* pIntel = prop_cast<cIntelNPC*>(pOwner->GetIntel());
    if (pIntel)
    {
      pIntel->GetAi()->OnFire(100);
    }
  }
//  else if (ID_NONE != mAreaEffectID)
//  {
//    if (!mbOwnerDied && pOwner->GetStats()->IsDead())
//    {
//      mbOwnerDied = true;
//
//      cAreaEffect* pArea = cAreaEffectSys::Get()->Fetch(mAreaEffectID);
//
//      if (pArea && pArea->GetDuration() > FIRE_DURATION_AFTER_DEATH)
//      {
//        pArea->SetDuration(FIRE_DURATION_AFTER_DEATH);
//      }
//    }
//  }
}

bool
cRule_OnFire::Combine(cRule *other)
{
  if (cRule_DamageOverTime::Combine(other))
  {
    if (ID_NONE != mAreaEffectID)
    {
      cAreaEffect* pArea = cAreaEffectSys::Get()->Fetch(mAreaEffectID);

      if (pArea)
      {
        pArea->SetDuration(mRemainingTime);
      }
    }

    return true;
  }

  if (strcmp(other->GetName(), "Frozen") == 0)
  {
    SetDisabled();
    return true;
  }

  return false;
}

//---------------------------------------------- class cRule_ManaCostOverTime

void            
cRule_ManaCostOverTime::Init(int param1, int param2)  
{
  mCostPerSec = param1;
  mTimer = 0.0f;
  mOwnerID = ID_NONE;
}

void            
cRule_ManaCostOverTime::Update(float time)
{
  cRule_Condition::Update(time);

  mTimer += time;

  if (mTimer > 1.0f)
  {
    mTimer = 0.0f;

    cGameObject* pOwner = mpTitan->GetRegistry()->Fetch(mOwnerID);

    if (pOwner && !pOwner->IsRemote())
    {
      cCalcPacket costPacket;
      costPacket.mPacketType = cRulePacket::EVENT_APPLY_MANA_COST;
      costPacket.SetTotal(mCostPerSec, "Mana Cost Over Time", GetName());
      pOwner->GetStats()->ApplyManaCost(&costPacket); 
    }
  }
}

bool cRule_ManaCostOverTime::OnAdded(tGameObjectID target)
{
  cRule_Condition::OnAdded(target);

  SyAssert(mOwnerID == ID_NONE && target != ID_NONE);
  mOwnerID = target;
  return true;
}

// EOF
