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

//-------------------------------------------------------- Includes
#include "droplist.h"
#include "tinyxml.h"
#include "database.h"
#include "item.h"
#include "registry.h"
#include "physics.h"
#include "stats.h"
#include "areaeffect.h"
#include "tuning.h"
#include "graphic.h"
#include "gameerror.h"

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

cDropListElement::cDropListElement() :
  mType(DROP_ITEM),
  mWeight(1),
  mMinAmount(1),
  mMaxAmount(1)
{
}

cDropListElement::~cDropListElement()
{
};

bool                        
cDropListElement::Drop(cGameObject* pOwner,SyVect3 location)
{
  Titan* titan = pOwner->GetTitan();

  if (mType == DROP_ITEM)
  {
    tGameID id = mDropID;
    int quantity= titan->Random(mMinAmount,mMaxAmount);
    if (id == (tGameID)SyHashResourceID("Gold"))
    {
      static const int MED_GOLD_AMOUNT = 33;
      static const int LARGE_GOLD_AMOUNT = 66;
      if (quantity > LARGE_GOLD_AMOUNT)
      {
        id = SyHashResourceID("GoldLarge");
      }
      else if (quantity > MED_GOLD_AMOUNT)
      {
        id = SyHashResourceID("GoldMedium");
      }
    }

    const cStatsItemMaster * itemmaster = titan->GetDatabaseSys()->GetItemMaster(id);
    if (itemmaster != NULL)
    {
      cGameObjectRegistry *reg= titan->GetRegistry();
      tGameObjectID  item_id = reg->Create(cGameObject::OBJ_ITEM);
      cItem *pItem = new cItem;
      // hack for treasure


      pItem->SetMaster(itemmaster);
      pItem->SetQuantity(quantity);
      cGameObject *item_obj = reg->Fetch(item_id);
      item_obj->SetLocation(location);
      prop_cast<cStatsItem*>(item_obj->GetStats())->SetItem(pItem);
    
      float angle = SY_DEG_TO_RAD(titan->Random(-180,180));

      static const float VEL_XZ = 2.0f;
      static const float VEL_Y = 2.0f;
      SyVect3 vel;
      vel.X = SY_SIN(angle) * VEL_XZ;
      vel.Y = VEL_Y;
      vel.Z = SY_COS(angle) * VEL_XZ;

      item_obj->SetHeading(SY_DEG_TO_RAD(titan->Random(-180,180)));
      prop_cast<cPhysicsDynamic *>(item_obj->GetPhysics())->SetVelocity(vel);
      reg->InitObject(item_obj->GetID(),false);

      prop_cast<cGraphicActor *>(item_obj->GetGraphic())->AddEffect(0, SyHashResourceID("fx_item_pickup"));
      return true;
    }
  }
  else if (mType == DROP_LIST)
  {
    cDropList * droplist = titan->GetDatabaseSys()->GetDropList(mDropID);
    if (droplist != NULL)
    {
      droplist->Drop(pOwner, location);
      return true;
    }
  }
  else if (mType == DROP_HEALTH ||
           mType == DROP_MANA ||
           mType == DROP_ESSENCE)
  {
    int amt = 0;    
    if (mMinAmount != -1 && mMaxAmount != -1)
    {
      amt = titan->Random(mMinAmount, mMaxAmount);
    }

    if (mType == DROP_HEALTH)
    {
      cDropList::DropHealth(pOwner, location, amt, mDropID);
    }
    else if (mType == DROP_MANA)
    {
      cDropList::DropMana(pOwner, location, amt, mDropID);
    }
    else
    {
      cDropList::DropEssence(pOwner, location, amt, mDropID);
    }
    
    return true;
  }
  else
  {
    SyAssertf(false, "Bad drop type");
  }

  return false;
}


//--------------------------------------------------  cDropList

cDropList::cDropList()
{
}

cDropList::~cDropList()
{
}

void                        
cDropList::Drop(cGameObject* pOwner,
                SyVect3 location)
{
  Titan* titan = pOwner->GetTitan();
  int total = 0;

  for (int ii=0;ii<mElements.Size();++ii)
  {
    total += mElements(ii).mWeight;
  }

  if (total <= 0)
  {
    SyAssert(0);
    return;
  }
  int index = titan->Random(0,total);

  for (int ii=0;ii<mElements.Size();++ii)
  {
    index -= mElements(ii).mWeight;
    if (index <= 0)
    {
      if (!mElements(ii).Drop(pOwner, location))
      {
        GAME_ASSERT(ERROR_DESIGN, false, "Unknown item in droplist : %s",mName.GetName()); 
      }
      return;
    }
  }
};


//----------------------------------------------  cTresureSetItem
cTreasureSetElement::cTreasureSetElement()
: mDropID(ID_NONE),
  mChance(1.0f)
{
}

cTreasureSetElement::~cTreasureSetElement()
{
}

void                        
cTreasureSetElement::Drop(cGameObject* pOwner,
                          SyVect3 location)
{
  cDropList * droplist = pOwner->GetTitan()->GetDatabaseSys()->GetDropList(mDropID);
  if (droplist != NULL)
  {
    droplist->Drop(pOwner, location);
  }
};


//----------------------------------------------  cTresureSet

 cTreasureSet::cTreasureSet()
 {
 }

 cTreasureSet::~cTreasureSet()
 {
 }

 void                        
 cTreasureSet::Drop(cGameObject* pOwner,
                    SyVect3 location, 
                    cGameObject* pActivater)
{
  cCalcPacket bonusChancePacket;
  bonusChancePacket.mPacketType = cRulePacket::CALC_ITEM_DROP_CHANCE;
  bonusChancePacket.SetTotal(0, "Bonus Item Drop Chance", "Treasure Set");

  float bonusChance = 0.0f;
  if (pActivater)
  {
    pActivater->GetStats()->ProcessRulePacket(&bonusChancePacket);
    bonusChance = ((float)bonusChancePacket.GetTotal())*.01f;
  }

  for (int ii=0;ii < mElements.Size();++ii)
  {
    if ((pOwner->GetTitan()->RandomFloat()+bonusChance) <= mElements(ii).mChance)
    {
      mElements(ii).Drop(pOwner,location);
    }
  }
}


//------------------------------------------- Function Declarations
 float cDropList::smDropLingerTime = 10.0f;

cDropList *
ParseDropList(cDatabaseSys *db,TiXmlNode* node)
{
  TiXmlNode* data_node;
  data_node = node->FirstChild("Name");
  SyAssert(data_node);
  const char *name = data_node->FirstChild()->Value();

  cDropList *retval = NULL;
  cDropList *master = db->GetDropList(SyHashResourceID(name));


  if (master == NULL)
  {
    master = new cDropList;
    master->mName.SetName(name);
    retval = master;
  }

  cDropListElement element;

  const char *l_DropTypeNames[] = 
  {
    "Item",
    "DropList",
    "Health",
    "Mana",
    "Essence",
  };

  static const int NUM_DROP_TYPENAMES = sizeof(l_DropTypeNames)/sizeof(l_DropTypeNames[0]);

  data_node = node->FirstChild("Type");
  GAME_ASSERT(ERROR_DESIGN, data_node != NULL,"Can't find type attribute in drop list %s",master->mName.GetName());
  if (data_node != NULL)
  {
    int enumVal = 0;
    const char *type = data_node->FirstChild()->Value();
    ChooseEnum(type,l_DropTypeNames,NUM_DROP_TYPENAMES, enumVal);

    element.mType = (cDropListElement::eDropType)enumVal;
  }

  data_node = node->FirstChild("DropName");
  if (data_node == NULL)
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Can't parse drop list record %s",master->mName.GetName());
    return retval;
  }

  element.mDropID = SyHashResourceID(data_node->FirstChild()->Value());

  if (element.mType == cDropListElement::DROP_ITEM)
  {
    // check to see if item exists...
    GAME_ASSERT(ERROR_DESIGN, db->GetItemMaster(element.mDropID)!=NULL,"Bad Item Drop %s in %s", data_node->FirstChild()->Value(),master->mName.GetName());
  }

  data_node = node->FirstChild("Weight");
  if (data_node != NULL)
  {
    element.mWeight = atoi(data_node->FirstChild()->Value());
  }
  else
  {
    GAME_ASSERT(ERROR_DESIGN, element.mType == cDropListElement::DROP_HEALTH ||
                element.mType == cDropListElement::DROP_MANA ||
                element.mType == cDropListElement::DROP_ESSENCE,
                "%s needs weight in drop list table", master->mName.GetName());
  }

  data_node = node->FirstChild("MinAmount");
  if (data_node != NULL)
  {
    element.mMinAmount = atoi(data_node->FirstChild()->Value());
  }
  else if (element.mType == cDropListElement::DROP_HEALTH ||
           element.mType == cDropListElement::DROP_MANA ||
           element.mType == cDropListElement::DROP_ESSENCE)
  {
    element.mMinAmount = -1;
  }

  data_node = node->FirstChild("MaxAmount");
  if (data_node != NULL)
  {
    element.mMaxAmount = atoi(data_node->FirstChild()->Value());
    GAME_ASSERT(ERROR_DESIGN, element.mMinAmount != -1, "Drop list %s has max but no min", master->mName.GetName());
  }
  else if (element.mType == cDropListElement::DROP_HEALTH ||
           element.mType == cDropListElement::DROP_MANA ||
           element.mType == cDropListElement::DROP_ESSENCE)
  {
    GAME_ASSERT(ERROR_DESIGN, element.mMinAmount == -1, "Drop list %s has min but no max", master->mName.GetName());
    element.mMaxAmount = -1;
  }

  master->mElements.Add(element);

  return retval;
};

void
cDropList::DropHealth(cGameObject* pOwner,
                      const SyVect3& location,
                      int amt,
                      tGameID projID)
{
  const cStatsProjectileMaster * pMaster = pOwner->GetTitan()->GetDatabaseSys()->GetProjectileMaster(projID);
  GAME_ASSERT(ERROR_DESIGN, pMaster!=NULL, "Could not find health drop projectile master");

  if (pMaster != NULL)
  {
    GAME_ASSERT(ERROR_DESIGN, pMaster->mGravity < 0.00001f, "Health drop projectile must have 0 gravity multiplier");
    cGameObjectRegistry *reg= pOwner->GetTitan()->GetRegistry();
    tGameObjectID proj_id = reg->Create(cGameObject::OBJ_PROJECTILE);
    cGameObject* pProj = reg->Fetch(proj_id);

    prop_cast<cStatsProjectile*>(pProj->GetStats())->SetMaster(projID);
    pProj->SetLocation(location+SyVect3(0.0f, 1.5f, 0.0f));
    reg->InitObject(pProj->GetID(),false);

    cPhysicsProjectile* pPhysics = prop_cast<cPhysicsProjectile*>(pProj->GetPhysics());
    pPhysics->Launch(pOwner, location);
    pPhysics->SetCollideable(false);
    pPhysics->SetLifetime(smDropLingerTime);
    pPhysics->SetVelocity(SyVect3(0.0f, 0.0f, 0.0f));

    cAreaEffect_PowerUp* pArea = new cAreaEffect_PowerUp();
    pArea->SetLocation(location);
    pArea->SetMovesWithObject(proj_id);
    pArea->SetSource(pOwner->GetID());
    pArea->SetRadius(0.25f);
    pArea->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_PLAYERS);
    pArea->SetProjectileID(proj_id);
    pArea->TestHealth(true);
    pArea->SetIgnoreSource(false);

    cGameEffect_Heal* pEffect = new cGameEffect_Heal();
    pEffect->SetHealing(amt, true);
    pArea->AddGameEffect(pEffect);

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

void
cDropList::DropMana(cGameObject* pOwner, const SyVect3& location, int amt, tGameID projID)
{
  const cStatsProjectileMaster * pMaster = Titan::Get()->GetDatabaseSys()->GetProjectileMaster(projID);
  GAME_ASSERT(ERROR_DESIGN, pMaster!=NULL, "Could not find mana drop projectile master");

  if (pMaster != NULL)
  {
    GAME_ASSERT(ERROR_DESIGN, pMaster->mGravity < 0.00001f, "Mana drop projectile must have 0 gravity multiplier");
    cGameObjectRegistry *reg= Titan::Get()->GetRegistry();
    tGameObjectID proj_id = reg->Create(cGameObject::OBJ_PROJECTILE);
    cGameObject* pProj = reg->Fetch(proj_id);

    prop_cast<cStatsProjectile*>(pProj->GetStats())->SetMaster(projID);
    pProj->SetLocation(location+SyVect3(0.0f, 1.5f, 0.0f));
    reg->InitObject(pProj->GetID(),false);

    cPhysicsProjectile* pPhysics = prop_cast<cPhysicsProjectile*>(pProj->GetPhysics());
    pPhysics->Launch(pOwner, location);
    pPhysics->SetCollideable(false);
    pPhysics->SetLifetime(smDropLingerTime);
    pPhysics->SetVelocity(SyVect3(0.0f, 0.0f, 0.0f));

    cAreaEffect_PowerUp* pArea = new cAreaEffect_PowerUp();
    pArea->SetLocation(location);
    pArea->SetMovesWithObject(proj_id);
    pArea->SetSource(pOwner->GetID());
    pArea->SetRadius(0.25f);
    pArea->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_PLAYERS);
    pArea->SetProjectileID(proj_id);
    pArea->TestMana(true);
    pArea->SetIgnoreSource(false);

    cGameEffect_ManaCost* pEffect= new cGameEffect_ManaCost();
    pEffect->SetCost(-amt, true);
    pArea->AddGameEffect(pEffect);

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

void
cDropList::DropEssence(cGameObject* pOwner, const SyVect3& location, int amt, tGameID projID)
{
  const cStatsProjectileMaster * pMaster = Titan::Get()->GetDatabaseSys()->GetProjectileMaster(projID);
  GAME_ASSERT(ERROR_DESIGN, pMaster!=NULL, "Could not find essence drop projectile master");

  if (pMaster != NULL)
  {
    GAME_ASSERT(ERROR_DESIGN, pMaster->mGravity < 0.00001f, "Essence drop projectile must have 0 gravity multiplier");
    cGameObjectRegistry *reg= Titan::Get()->GetRegistry();
    tGameObjectID proj_id = reg->Create(cGameObject::OBJ_PROJECTILE);
    cGameObject* pProj = reg->Fetch(proj_id);

    prop_cast<cStatsProjectile*>(pProj->GetStats())->SetMaster(projID);
    pProj->SetLocation(location+SyVect3(0.0f, 1.5f, 0.0f));
    reg->InitObject(pProj->GetID(),false);
    cPhysicsProjectile* pPhysics = prop_cast<cPhysicsProjectile*>(pProj->GetPhysics());
    pPhysics->Launch(pOwner, location);
    pPhysics->SetCollideable(false);
    pPhysics->SetLifetime(smDropLingerTime);
    pPhysics->SetVelocity(SyVect3(0.0f, 0.0f, 0.0f));

    cAreaEffect_PowerUp* pArea = new cAreaEffect_PowerUp();
    pArea->SetLocation(location);
    pArea->SetMovesWithObject(proj_id);
    pArea->SetSource(pOwner->GetID());
    pArea->SetRadius(0.25f);
    pArea->SetEffectTargetFaction(cAreaEffect::EFFECT_FACTION_PLAYERS);
    pArea->SetProjectileID(proj_id);
    pArea->TestEssence(true);
    pArea->SetIgnoreSource(false);

    cGameEffect_AddEssence* pEffect = new cGameEffect_AddEssence();
    pEffect->SetAmount(amt);
    pArea->AddGameEffect(pEffect);

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

void cDropList::RegisterTuningVariables()
{
  gTuningSys.AddFloat(&smDropLingerTime, "DropLingerTime");
}

cTreasureSet *
ParseTreasureSet(cDatabaseSys *db, TiXmlNode* node)
{
  TiXmlNode* data_node;
  data_node = node->FirstChild("Name");
  SyAssert(data_node);
  const char *name = data_node->FirstChild()->Value();

  cTreasureSet *retval = NULL;
  cTreasureSet *master = db->GetTreasureSet(SyHashResourceID(name));


  if (master == NULL)
  {
    master = new cTreasureSet;
    master->mName.SetName(name);
    retval = master;
  }

  cTreasureSetElement element;

  data_node = node->FirstChild("DropName");
  if (data_node == NULL)
  {
    GAME_ASSERT(ERROR_DESIGN, false,"Can't parse treasure set record %s",master->mName.GetName());
    return retval;
  }

  element.mDropID = SyHashResourceID(data_node->FirstChild()->Value());

    // check to see if item exists...
  GAME_ASSERT(ERROR_DESIGN, db->GetDropList(element.mDropID)!=NULL,"Bad Item Drop %s in %s",
              data_node->FirstChild()->Value(),master->mName.GetName());

  data_node = node->FirstChild("Chance");
  GAME_ASSERT(ERROR_DESIGN, data_node!=NULL,"Missing Data Field 'Chance' in drop list database XML file");
  if (data_node != NULL)
  {
    element.mChance = (float) atof(data_node->FirstChild()->Value());
    element.mChance /= 100; // from percent
  }
  master->mElements.Add(element);
  return retval;
};



// EOF
