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

//-------------------------------------------------------- Includes
#include "database.h"
#include "stats.h"
#include "droplist.h"
#include "minimap.h"
#include "tinyxml.h"
#include "ai\behaviortypes.h"
#include "debris.h"
#include "spell.h"
#include "animdefs.h"
#include "animcontroller.h"
#include "SyESFParse.h"
#include "item.h"
#include "gameerror.h"

static const char *s_DamageTypenames[] = 
{
  "None",
  "Physical",
  "Magical",
};
static const int NUM_DAMAGE_TYPENAMES = (sizeof(s_DamageTypenames) / sizeof (s_DamageTypenames[0]));

static const char *s_ElementalTypenames[] = 
{
  "None",
  "Fire",
  "Ice",
  "Lightning",
  "Poison",
  "Shadow",
  "Earth"
};
static const int NUM_ELEMENTAL_TYPENAMES = (sizeof(s_ElementalTypenames) / sizeof (s_ElementalTypenames[0]));

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

//------------------------------------ cDatabaseSys

cDatabaseSys::cDatabaseSys(Titan *titan)
: mpExperienceTable(NULL),
  mpDifficultySettingsTable(NULL),
  mpTitan(titan)
{
}

cDatabaseSys::~cDatabaseSys()
{
  delete mpDifficultySettingsTable;
  mpDifficultySettingsTable = NULL;

  delete mpExperienceTable;
  mpExperienceTable = NULL;

  for (int index = mCharacterMasters.Begin(); index != mCharacterMasters.End();index = mCharacterMasters.Next(index))
  {
    delete mCharacterMasters(index);
  }
  mCharacterMasters.Clear();


  for (int index = mItemMasters.Begin(); index != mItemMasters.End();index = mItemMasters.Next(index))
  {
    delete mItemMasters(index);
  }
  mItemMasters.Clear();


  for (int index = mEnhancementMasters.Begin(); index != mEnhancementMasters.End();index = mEnhancementMasters.Next(index))
  {
    delete mEnhancementMasters(index);
  }
  mEnhancementMasters.Clear();

  for (int index = mMerchantSets.Begin(); index != mMerchantSets.End();index = mMerchantSets.Next(index))
  {
    delete mMerchantSets(index);
  }
  mMerchantSets.Clear();

  for (int index = mEnhancementComboModels.Begin(); index != mEnhancementComboModels.End();index = mEnhancementComboModels.Next(index))
  {
    delete mEnhancementComboModels(index);
  }
  mEnhancementComboModels.Clear();


  for (int index = mProjectileMasters.Begin(); index != mProjectileMasters.End();index = mProjectileMasters.Next(index))
  {
    delete mProjectileMasters(index);
  }
  mProjectileMasters.Clear();


  for (int index = mPropMasters.Begin(); index != mPropMasters.End();index = mPropMasters.Next(index))
  {
    delete mPropMasters(index);
  }
  mPropMasters.Clear();


  for (int index = mCharacterClasses.Begin(); index != mCharacterClasses.End();index = mCharacterClasses.Next(index))
  {
    delete mCharacterClasses(index);
  }
  mCharacterClasses.Clear();


  for (int index = mLevelMasters.Begin(); index != mLevelMasters.End();index = mLevelMasters.Next(index))
  {
    delete mLevelMasters(index);
  }
  mLevelMasters.Clear();


  for (int index = mTreasureSets.Begin(); index != mTreasureSets.End();index = mTreasureSets.Next(index))
  {
    delete mTreasureSets(index);
  }
  mTreasureSets.Clear();


  for (int index = mMiniMaps.Begin(); index != mMiniMaps.End();index = mMiniMaps.Next(index))
  {
    delete mMiniMaps(index);
  }
  mMiniMaps.Clear();


  for (int index = mDropLists.Begin(); index != mDropLists.End();index = mDropLists.Next(index))
  {
    delete mDropLists(index);
  }
  mDropLists.Clear();

  for (int index = mDebrisMasters.Begin(); index != mDebrisMasters.End();index = mDebrisMasters.Next(index))
  {
    delete mDebrisMasters(index);
  }
  mDebrisMasters.Clear();

  for (int index = mSpellMasters.Begin(); index != mSpellMasters.End();index = mSpellMasters.Next(index))
  {
    delete mSpellMasters(index);
  }
  mSpellMasters.Clear();

  for (int i = mAbilityMasters.Begin(); i != mAbilityMasters.End();i = mAbilityMasters.Next(i))
  {
    for (int j = mAbilityMasters(i).Begin(); j != mAbilityMasters(i).End();j = mAbilityMasters(i).Next(j))
    {
      delete mAbilityMasters(i)(j);
    }
    
    mAbilityMasters(i).Clear();
  }
  mAbilityMasters.Clear();  
  mUnlockAbilityList.Clear();

  for (int index = mBlockBehaviors.Begin(); index != mBlockBehaviors.End();index = mBlockBehaviors.Next(index))
  {
    delete mBlockBehaviors(index);
  }
  mBlockBehaviors.Clear();

  for (int index = mAbilitySets.Begin(); index != mAbilitySets.End();index = mAbilitySets.Next(index))
  {
    delete mAbilitySets(index);
  }
  mAbilitySets.Clear();

  for (int index = mImpactFXSets.Begin(); index != mImpactFXSets.End();index = mImpactFXSets.Next(index))
  {
    delete mImpactFXSets(index);
  }
  mImpactFXSets.Clear();
  
  for (int index = mImpactSoundSets.Begin(); index != mImpactSoundSets.End();index = mImpactSoundSets.Next(index))
  {
    delete mImpactSoundSets(index);
  }
  mImpactSoundSets.Clear();

  for (int index = mModelData.Begin(); index != mModelData.End();index = mModelData.Next(index))
  {
    delete mModelData(index);
  }
  mModelData.Clear();
}

void                         
cDatabaseSys::Init()
{
  LoadModelData();
  LoadCharacterMasters();
  LoadItemMasters();
  LoadProjectileMasters();
  LoadPropMasters();
  LoadClasses();
  LoadLevels();
  LoadDropLists();
  LoadTreasureSets();
  LoadDebris();
  LoadExperienceTable();
  LoadDifficultySettingsTable();
  LoadEnhancements(); // must come after spells
  LoadEnhancementComboModels();
  LoadMerchantSets();
  LoadAbilities();
  LoadBlockBehaviors();
  LoadAbilitySets();
  LoadTextIDs();
  LoadImpactFXSets();
  LoadImpactSoundSets();
  LoadSpells();
  LoadZones();
}

const cExperienceTable*
cDatabaseSys::GetExperienceTable()
{
  SyAssert(mpExperienceTable!=NULL);
  return mpExperienceTable;
}

const cDifficultySettingsTable*
cDatabaseSys::GetDifficultySettingsTable()
{
  SyAssert(mpDifficultySettingsTable!=NULL);
  return mpDifficultySettingsTable;
}

void                         
cDatabaseSys::LoadExperienceTable()
{
  // load up xml file for experience...
  TiXmlDocument doc( "game_assets/design/data/experience.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load experience.xml");
    return;
  }

  mpExperienceTable = SyNew cExperienceTable();

  TiXmlNode* node = 0;
  TiXmlNode* data_node = 0;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);

  node = node->FirstChild( "ExpReward" );
  SyAssert(node);

  int lvlDiff = 0, expGiven = 0;
  int lowestLvlDiff = cExperienceTable::MAX_LEVEL_DELTAS/2+1;
  int highestLvlDiff = -(cExperienceTable::MAX_LEVEL_DELTAS/2+1);
  int lowestExp = -1;
  int highestExp = -1;

  while (node != NULL)
  {
    data_node = node->FirstChild("LevelDifference");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Empty LevelDifference attribute in experience.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      lvlDiff = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ExpGiven");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Empty ExpGiven attribute for LevelDifference %d in experience.xml", lvlDiff);
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      expGiven = atoi(data_node->FirstChild()->Value());
    }

    GAME_ASSERT(ERROR_DESIGN, lvlDiff >= -(cExperienceTable::MAX_LEVEL_DELTAS/2) && lvlDiff <= (cExperienceTable::MAX_LEVEL_DELTAS/2), "Invalid level difference when reading in experience.xml %d", lvlDiff);

    if (lvlDiff >= -(cExperienceTable::MAX_LEVEL_DELTAS/2) && lvlDiff <= (cExperienceTable::MAX_LEVEL_DELTAS/2))
    {
      mpExperienceTable->mDeltaExpReward[lvlDiff+(cExperienceTable::MAX_LEVEL_DELTAS/2)] = (short)expGiven;
    }

    if (lvlDiff < lowestLvlDiff)
    {
      lowestLvlDiff = lvlDiff;
      lowestExp = expGiven;
    }

    if (lvlDiff > highestLvlDiff)
    {
      highestLvlDiff = lvlDiff;
      highestExp = expGiven;
    }

    node = node->NextSibling();
  }

  for (int i=-(cExperienceTable::MAX_LEVEL_DELTAS/2); i<lowestLvlDiff; ++i)
  {
    mpExperienceTable->mDeltaExpReward[i+(cExperienceTable::MAX_LEVEL_DELTAS/2)] = (short)lowestExp;
  }

  for (int i=(cExperienceTable::MAX_LEVEL_DELTAS/2); i>highestLvlDiff; --i)
  {
    mpExperienceTable->mDeltaExpReward[i+(cExperienceTable::MAX_LEVEL_DELTAS/2)] = (short)highestExp;
  }
}

void                         
cDatabaseSys::LoadDifficultySettingsTable()
{
  // load up xml file for experience...
  TiXmlDocument doc( "game_assets/design/data/difficulty_settings.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load difficulty_settings.xml");
    return;
  }

  mpDifficultySettingsTable = SyNew cDifficultySettingsTable();

  TiXmlNode* node = 0;
  TiXmlNode* data_node = 0;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);

  node = node->FirstChild( "DifficultySetting" );
  SyAssert(node);

  int level = 0;

  while (node != NULL)
  {
    data_node = node->FirstChild("Difficulty");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Empty Level attribute in difficulty_settings.xml");
    
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      level = atoi(data_node->FirstChild()->Value());
    }

    GAME_ASSERT(ERROR_DESIGN, level >= 1 && level <= cDifficultySettingsTable::MAX_DIFFICULTY_LEVELS, "Difficulty level in table outside of range 1-10");

    if (level < 1 || level > cDifficultySettingsTable::MAX_DIFFICULTY_LEVELS)
    {
      node = node->NextSibling();
      continue;
    }

    --level;

    data_node = node->FirstChild("DamageMultiplier");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Empty DamageMultiplier attribute for in difficulty_settings.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      mpDifficultySettingsTable->mDamageMultipliers[level] = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("HealthMultiplier");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Empty DamageMultiplier attribute for in difficulty_settings.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      mpDifficultySettingsTable->mHealthMultipliers[level] = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("BlockMeterMultiplier");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Empty DamageMultiplier attribute for in difficulty_settings.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      mpDifficultySettingsTable->mBlockMultipliers[level] = (float)atof(data_node->FirstChild()->Value());
    }

    node = node->NextSibling();
  }
}

void                         
cDatabaseSys::LoadDebrisModels(tGameID debrisMasterID)
{
  if (mpTitan == NULL)
  {
    SyAssertf(0,"Bad Titan Ptr");
    return;
  }

  const cDebrisMaster *master = GetDebrisMaster(debrisMasterID);

  if (master)
  {
    for (int shard = master->mShardMasters.Begin();
          shard != master->mShardMasters.End();
          shard = master->mShardMasters.Next(shard))
    {
      cDebrisShardMaster *shardMaster = master->mShardMasters(shard);
      SyESFParse& Parser = mpTitan->GetTitanUI()->GetESFParser();
      shardMaster->mSpriteTemplateHandle = mpTitan->LoadAssetSprite (Parser, shardMaster->mModelName);
    }
  }
}

void                         
cDatabaseSys::LoadModelData()
{
  // load up xml file for characters...
  TiXmlDocument doc( "game_assets/design/data/model_data.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load model_data.xml");
    return;
  }

  for (int index = mModelData.Begin(); index != mModelData.End();index = mModelData.Next(index))
  {
    delete mModelData(index);
  }
  mModelData.Clear();


  const char *l_ModelTypeNames[] = 
  {
    "Character",
    "Prop",
    "Item",
  };

  static const int NUM_MODEL_TYPENAMES = (sizeof(l_ModelTypeNames) / sizeof (l_ModelTypeNames[0]));
  GAME_ASSERT(ERROR_CODE, NUM_MODEL_TYPENAMES == cStatsModelData::MODELDATA_MAXTYPES, "Forgot to add ModelData typename?");

  const char *l_SoundSurfaceTypeNames[] = 
  {
    "Flesh",
    "Leather",
    "Metal",
    "Stone",
    "Magic",
  };

  static const int NUM_SOUNDSURFACE_TYPENAMES = (sizeof(l_SoundSurfaceTypeNames) / sizeof (l_SoundSurfaceTypeNames[0]));
  GAME_ASSERT(ERROR_CODE, NUM_SOUNDSURFACE_TYPENAMES == CHARACTER_SOUND_SURFACE_MAX_TYPES, "Forgot to add Character sound surface type name?");

  TiXmlNode* node = 0;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "Model" );
  SyAssert( node );

  while (node != NULL)
  {
    TiXmlNode* data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Model with no Name attribute in model_data.xml");

    if (!data_node || !data_node->FirstChild() || !data_node->FirstChild()->Value())
    {
      node = node->NextSibling();
      continue;
    }

    cStatsModelData *newmaster = SyNew cStatsModelData; 
    const char *name = data_node->FirstChild()->Value();
    newmaster->mID.SetName(name);

    data_node = node->FirstChild("Type");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      int enumVal = 0;
      bool bResult = ChooseEnum(data_node->FirstChild()->Value(), l_ModelTypeNames, NUM_MODEL_TYPENAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown ModelData type name in record '%s' in model_data.xml",newmaster->mID.GetName());
      newmaster->mType = (cStatsModelData::Type)enumVal;
    }

    newmaster->mModelName = SyNew char[strlen(name)+1]; 
    strcpy(newmaster->mModelName, name);

    if (newmaster->mType == cStatsModelData::MODELDATA_PROP)
    {
      static const char *path = "game_assets/art/props/";
      static const char *extension = ".esf";
      newmaster->mFileName = SyNew char[strlen(name)+strlen(path)+strlen(extension)+ 1]; 
      sprintf(newmaster->mFileName,"%s%s%s",path,name,extension);
    }
    else if (newmaster->mType == cStatsModelData::MODELDATA_ITEM)
    {
      static const char *path = "game_assets/art/items/";
      static const char *extension = ".esf";
      newmaster->mFileName = SyNew char[strlen(name)+strlen(path)+strlen(extension)+ 1]; 
      sprintf(newmaster->mFileName,"%s%s%s",path,name,extension);
    }
    else
    {
      static const char *path = "game_assets/art/characters/";
      static const char *extension = ".esf";
      newmaster->mFileName = SyNew char[strlen(name)+strlen(path)+strlen(extension)+ 1]; 
      sprintf(newmaster->mFileName,"%s%s%s",path,name,extension);
    }

    if (newmaster->mType == cStatsModelData::MODELDATA_CHARACTER)
    {
      data_node = node->FirstChild("Ragdoll");
      if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
      {
        const char *RagdollName = data_node->FirstChild()->Value();
        newmaster->mRagdollName = SyNew char[strlen(RagdollName)+1]; 
        strcpy(newmaster->mRagdollName, RagdollName);
      }
    }

    data_node = node->FirstChild("Debris");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDebrisID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DefaultAnimSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDefaultAnimSetID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("SoundSurfaceType");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      int enumVal = 0;
      bool bResult = ChooseEnum(data_node->FirstChild()->Value(), l_SoundSurfaceTypeNames, NUM_SOUNDSURFACE_TYPENAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult, "ModelData record '%s' has unknown SoundSurfaceType '%s' name in model_data.xml", newmaster->mID.GetName(), data_node->FirstChild()->Value());
      newmaster->mType = (cStatsModelData::Type)enumVal;

      newmaster->mDefaultAnimSetID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DeathFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDeathFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("SummonFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSummonFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DeathSound");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      static const char *path = "game_assets/sound/";
      static const char *extension = ".esf";
      newmaster->mSoundName = SyNew char[strlen(data_node->FirstChild()->Value())+strlen(path)+strlen(extension)+ 1]; 
      sprintf(newmaster->mSoundName,"%s%s%s",path,data_node->FirstChild()->Value(),extension);
    }

    mModelData.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}

void                         
cDatabaseSys::LoadCharacterMasters()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/character.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load character.xml");
    return;
	}
  
  const char *l_AttachPointNames[] = 
  {
    "Melee",      
    "Ranged",   
    "Head",     
    "Body",     
    "Feet",     
    "Accessory",
    "Shield",
  };

  static const int NUM_ATTACH_POINT_NAMES = (sizeof(l_AttachPointNames) / sizeof (l_AttachPointNames[0]));
  GAME_ASSERT(ERROR_CODE, NUM_ATTACH_POINT_NAMES == CHAR_ATTACH_MAX_SLOTS-1, "Forgot to add character attach point name?");
	TiXmlNode* node = 0;

  int enumVal;
  bool bResult;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Character" );
	SyAssert( node );

  while (node != NULL)
  {
    TiXmlNode* data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Character with no Name attribute in character.xml");
    if (!data_node || !data_node->FirstChild() || !data_node->FirstChild()->Value())
    {
      node = node->NextSibling();
      continue;
    }

    cStatsCharacterMaster *newmaster = SyNew cStatsCharacterMaster; 
    const char *name = data_node->FirstChild()->Value();
    newmaster->mID.SetName(name);

    data_node = node->FirstChild("Model");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'Model' in character database XML file");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mModelDataID = SyHashResourceID(data_node->FirstChild()->Value());

      const cStatsModelData* pModelData = GetModelData(newmaster->mModelDataID);
      GAME_ASSERT(ERROR_DESIGN, pModelData != NULL, "Could not find model data '%s' for character master '%s' character database XML file", data_node->FirstChild()->Value(), newmaster->mID.GetName());
      if (pModelData)
      {
        newmaster->mAnimSet = pModelData->mDefaultAnimSetID;
      }
    }

    data_node = node->FirstChild("Lvl");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mLevel = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("LvlDiff");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mLevelDiff = atoi(data_node->FirstChild()->Value());
    }

    GAME_ASSERT(ERROR_DESIGN, newmaster->mLevel <= 0 || newmaster->mLevelDiff == 0, "Must have Lvl = 0 if using LvlDiff field for '%s' in character.xml", newmaster->mID.GetName());

    data_node = node->FirstChild("Class");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'Class' in character database XML file");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mClass = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AnimSet");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'AnimSet' in character database XML file");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAnimSet = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("NameString");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNameString = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("MovementSpeed");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mMovementSpeed = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("CloseDistance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCloseDistance = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("MeleeRange");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mMeleeRange = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("CollisionRadius");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCollisionRadius =(float) (atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("ActivationRadiusOverride");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mActivationRadius =(float) (atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("TreasureSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mTreasureSet = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("NaturalMelee");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNaturalMelee = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("NaturalRanged");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNaturalRanged = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("NaturalSpell");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNaturalSpell = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("BehaviorType");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      bResult = ChooseEnum(data_node->FirstChild()->Value(),NPC_BEHAVIOR_TYPE_NAMES,NPCBEH_MAXTYPES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult,"Unknown BehaviorType type in record '%s' in character.xml",name);
      if (bResult)
      {
        newmaster->mNPCBehaviorType = (char) enumVal;
      }
    }
    else
    {
      newmaster->mNPCBehaviorType = NPCBEH_CLASSDEFAULT;
    }

    data_node = node->FirstChild("BlockBehavior");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mBlockBehavior = SyHashResourceID(data_node->FirstChild()->Value());
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "Character '%s' does not have a BlockBehavior", name);
    }

    data_node = node->FirstChild("AbilitySet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAbilitySet = SyHashResourceID(data_node->FirstChild()->Value());
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "Character '%s' does not have an AbilitySet", name);
    }

    data_node = node->FirstChild("Race");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mRace = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Scale");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mScale = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Weight");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mWeight = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Attachment");
    while (data_node)
    {
      if (data_node->FirstChild() && data_node->FirstChild()->Value())
      {
        TiXmlNode* attachDataNode = data_node->FirstChild("AttachmentPoint");
        if (attachDataNode && attachDataNode->FirstChild() && attachDataNode->FirstChild()->Value())
        {
          enumVal = 0;
          bResult = ChooseEnum(attachDataNode->FirstChild()->Value(),l_AttachPointNames, NUM_ATTACH_POINT_NAMES, enumVal);
          GAME_ASSERT(ERROR_DESIGN, bResult,"Unknown AttachmentPoint type in record '%s' in character.xml", name);
          if (bResult && enumVal < cStatsCharacterMaster::MAX_ATTACHMENTS)
          {
            attachDataNode = data_node->FirstChild("Model");
            if (attachDataNode && attachDataNode->FirstChild() && attachDataNode->FirstChild()->Value())
            {
              GAME_ASSERT(ERROR_DESIGN, newmaster->mAttachmentModelNames[enumVal].Length() == 0, "Trying to attach multiple models to character '%s' in character.xml", newmaster->mID.GetName());
              newmaster->mAttachmentModelNames[enumVal] = attachDataNode->FirstChild()->Value();
            }
          }
        }
      }
      data_node = data_node->NextSibling("Attachment");
    }

    static const int NUM_NPCFACTION_NAMES = (sizeof(NPC_FACTION_NAMES) / sizeof (NPC_FACTION_NAMES[0]));

    data_node = node->FirstChild("Faction");
    if (data_node != NULL)
    {
      int enumVal = 0;
      const char *type = data_node->FirstChild()->Value();
      SyAssertf(NUM_NPCFACTION_NAMES == NPCFACTION_MAXTYPES,"Forgot to add NPC Faction type name.");
      bool result = ChooseEnum(type,NPC_FACTION_NAMES,NUM_NPCFACTION_NAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result,"Unknown Faction type in record '%s' in character.xml",name);
      newmaster->mNPCFaction = (char)enumVal;
    }
    else
    {
      newmaster->mNPCFaction = NPCFACTION_CLASSDEFAULT;
    }

    data_node = node->FirstChild("Script");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      const char *text = data_node->FirstChild()->Value();
      newmaster->mScriptName = SyNew char[strlen(text)+1];
      sprintf(newmaster->mScriptName,"%s",text);
    }

    mCharacterMasters.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}

bool 
ChooseEnum(const char *str,const char **name_list,int name_list_size, int& retVal)
{
  for (int ii=0;ii<name_list_size;++ii)
  {
    if (SyStr::Stricmp(name_list[ii],str)==0)
    {
      retVal = ii;
      return true;
    }
  }

  return false;
}

void                         
cDatabaseSys::LoadItemMasters()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/item.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    SyAssert(0);
    return;
	}
  
	TiXmlNode* node = 0;
  const char* val;

  const char *l_ItemTypenames[] = 
  {
    "Melee", //IT_MELEE,
    "Ranged",//IT_RANGED,
    "Head",//IT_HEAD,
    "Chest",//IT_CHEST,
    "Shoulders",//IT_SHOULDERS,
    "Legs",//IT_LEGS,
    "Feet",//IT_FEET,
    "Belt",//IT_BELT,
    "Necklace",//IT_NECKLACE
    "Ring",//IT_Ring
    "Enhancement",//IT_ENHANCEMENT,
    "Gold",//IT_GOLD,
    "Trinket",//IT_TRINKET,
    "Potion",//IT_POTION,
    
  };

  static const int NUM_ITEM_TYPENAMES = (sizeof(l_ItemTypenames) / sizeof (l_ItemTypenames[0]));
  const char *l_SkinNames[] = 
  {
    "None",
    "Body",
    "Base_Helmet",
    "Base_Shoulders",
    "Base_Chest",
    "Base_Legs",
    "Base_Boots",
    "Base_Hands",

    "Light_Helmet",
    "Light_Shoulders",
    "Light_Chest",
    "Light_Legs",
    "Light_Boots",

    "Medium_Helmet",
    "Medium_Shoulders",
    "Medium_Chest",
    "Medium_Legs",
    "Medium_Boots",

    "Heavy_Helmet",
    "Heavy_Shoulders",
    "Heavy_Chest",
    "Heavy_Legs",
    "Heavy_Boots",

    "Hands_1",
    "Hands_2",
    "Hands_3",
    "Hands_4",
    "Hands_5",
    "Hands_6",
    "Hands_7",
    "Hands_8",
    "Hands_9",
    "Hands_10",
  };
  
  static const int NUM_ITEM_SKINNAMES = (sizeof(l_SkinNames) / sizeof (l_SkinNames[0]));

  const char *l_ItemClassnames[] = 
  {
    "Scout", 
    "Mage",
    "Brute",
    "Any",
  };
  static const int NUM_ITEM_CLASSNAMES = (sizeof(l_ItemClassnames) / sizeof (l_ItemClassnames[0]));

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Item" );
	SyAssert( node );

  while (node != NULL)
  {
    TiXmlNode* data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Item with no Name attribute in item.xml");
    if (!data_node || !data_node->FirstChild() || !data_node->FirstChild()->Value())
    {
      node = node->NextSibling();
      continue;
    }

    cStatsItemMaster *newmaster = SyNew cStatsItemMaster; 
    const char *name = data_node->FirstChild()->Value();
    newmaster->mID.SetName(name);

    int enumVal = 0;
    data_node = node->FirstChild("Type");
    GAME_FATAL(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Item '%s' has no Type attribute in item.xml", newmaster->mID.GetName());
    const char *type = data_node->FirstChild()->Value();

    SyAssertf(NUM_ITEM_TYPENAMES == NUM_ITEM_TYPES,"Forgot to add item typename?");
    bool result = ChooseEnum(type,l_ItemTypenames,NUM_ITEM_TYPENAMES, enumVal);

    GAME_ASSERT(ERROR_DESIGN, result == true,"Unknown item type in record '%s' in item.xml",name);
    newmaster->mType = (eItemType) enumVal;

    data_node = node->FirstChild("Class");
    if (data_node != NULL)
    {
      int enumVal = 0;
      type = data_node->FirstChild()->Value();
      SyAssertf(NUM_ITEM_CLASSNAMES == NUM_ITEM_CLASSES,"Forgot to add item typename?");
      result = ChooseEnum(type,l_ItemClassnames,NUM_ITEM_CLASSNAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result == true,"Unknown item class in record '%s' in item.xml",name);
      newmaster->mClass = (eItemClass)enumVal;
    }


    data_node = node->FirstChild("WorldModel");
    if (data_node != NULL)
    {
      const char *model = data_node->FirstChild()->Value();
      if (model[0] != '\0')
      {
        static const char *path = "game_assets/art/items/";
        static const char *extension = ".esf";
        newmaster->mFileName = SyNew char[strlen(model)+strlen(path)+strlen(extension)+ 1]; 
        newmaster->mWorldModel = SyNew char[strlen(model)+ 1]; 
        sprintf(newmaster->mFileName,"%s%s%s",path,model,extension);
        sprintf(newmaster->mWorldModel,"%s",model);
      }
    }

    data_node = node->FirstChild("Icon");
    if (data_node != NULL)
    {
      const char *model = data_node->FirstChild()->Value();
      newmaster->mIcon = SyNew char[strlen(model)+1]; 
      strcpy(newmaster->mIcon,model);
    }
    
    data_node = node->FirstChild("AttachModel");
    if (data_node != NULL)
    {
      const char *model = data_node->FirstChild()->Value();
      if (model[0] != '\0')
      {
        static const char *path = "game_assets/art/items/";
        static const char *extension = ".esf";
        newmaster->mAttachModel = SyNew char[strlen(model)+strlen(path)+strlen(extension)+ 1]; 
        sprintf(newmaster->mAttachModel,"%s%s%s",path,model,extension);

        newmaster->mEmitterModel = SyNew char[strlen(model)+strlen(path)+strlen(extension)+8+ 1]; 
        sprintf(newmaster->mEmitterModel,"%s%s_emitter%s",path,model,extension);
      }
    }

    data_node = node->FirstChild("AttachSkin");
    if (data_node != NULL)
    {
      const char *skin = data_node->FirstChild()->Value();
      SyAssertf(NUM_ITEM_SKINNAMES == NUM_SKINS,"Forgot to add item typename?");
      result = ChooseEnum(skin,l_SkinNames,NUM_ITEM_SKINNAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result == true,"Unknown attach skin in record '%s' in item.xml",name);
      newmaster->mAttachSkin = (eEquipSkin)enumVal;
    }

    data_node = node->FirstChild("NameString");
    if (data_node != NULL)
    {
      newmaster->mNameString = SyHashResourceID(data_node->FirstChild()->Value());
    }


    // todo: description id - i18n?
    //data_node = node->FirstChild("Description");
    //SyAssertf(data_node!=NULL,"Missing Data Field in character database XML file");
    //newmaster->mPower[COMBO_SSS] = (float) atof(data_node->FirstChild()->Value());
 
    data_node = node->FirstChild("Projectile");
    if (data_node != NULL)
    {
      newmaster->mProjectile = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Value");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing 'Value' data field for '%s' in item database XML file", name);
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mValue =  atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("BaseDamageType");
    if (data_node != NULL)
    {
      int enumVal = 0;
      type = data_node->FirstChild()->Value();
      SyAssertf(NUM_DAMAGE_TYPENAMES == NUM_DAMAGE_TYPES,"Forgot to add damage typename?");
      result = ChooseEnum(type,s_DamageTypenames,NUM_DAMAGE_TYPES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result == true,"Unknown damage type in record '%s' in item.xml",name);
      newmaster->mBaseDamageType = (eDamageType) enumVal;
    }

    data_node = node->FirstChild("BaseDamageBonus");
    if (data_node != NULL)
    {
      newmaster->mBaseDamageBonus =  atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("HP");
    if (data_node != NULL)
    {
      newmaster->mMaxHP =  atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Mana");
    if (data_node != NULL)
    {
      newmaster->mMaxMana =  atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AttackPower");
    if (data_node != NULL)
    {
      newmaster->mAttackPower = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("SpellPower");
    if (data_node != NULL)
    {
      newmaster->mSpellPower = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("PhysicalDefense");
    if (data_node != NULL)
    {
      newmaster->mPhysicalDefense = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("MagicalDefense");
    if (data_node != NULL)
    {
      newmaster->mMagicalDefense =  atoi(data_node->FirstChild()->Value());
    }

    for (int i=0; i<cStatsItemMaster::MAX_NUM_ENHANCEMENTS; ++i)
    {
      char attrName[64];
      sprintf(attrName, "Enhancement%d", i+1);
      data_node = node->FirstChild(attrName);
      if (data_node != NULL)
      {
        val = data_node->FirstChild()->Value();
        if (val != NULL && val[0] != 0)
        {
          newmaster->mEnhancements[i] = SyHashResourceID(val);
        }
      }
    }

    mItemMasters.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}                                            

void                         
cDatabaseSys::LoadEnhancements()
{
  // load up xml file for enhancements...
	TiXmlDocument doc( "game_assets/design/data/enhancements.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load enhancements.xml");
    return;
  }

  TiXmlNode* node = 0;
  TiXmlNode* data_node;
  const char* val;
  bool result;
  int enumVal;

  const char *l_DeliveryTypenames[] = 
  {
    "Equip",
    "Attack",
    "Defend",
  };
  static const int NUM_DELIVERY_TYPENAMES = (sizeof(l_DeliveryTypenames) / sizeof (l_DeliveryTypenames[0]));

  const char *l_SlotTypenames[] = 
  {
    "Universal",
    "Statistical",
    "Proc",
    "Utility"
  };
  static const int NUM_SLOT_TYPENAMES = (sizeof(l_SlotTypenames) / sizeof (l_SlotTypenames[0]));

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "Enhancement" );
  SyAssert( node );

  while (node != NULL)
  {
    cStatsEnhancementMaster *newmaster = SyNew cStatsEnhancementMaster; 
    data_node = node->FirstChild("Name");
    GAME_FATAL(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Enhancement with no Name attribute in item.xml");
    val = data_node->FirstChild()->Value();
    newmaster->mID.SetName(val);

    data_node = node->FirstChild("Delivery");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Enhancement '%s' has no Delivery attribute in item.xml", newmaster->mID.GetName());
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      val = data_node->FirstChild()->Value();
      SyAssertf(NUM_DELIVERY_TYPENAMES == cStatsEnhancementMaster::ENH_DELIVERY_NUM_TYPES,"Forgot to add enhancement delivery typename?");
      result = ChooseEnum(val,l_DeliveryTypenames,NUM_DELIVERY_TYPENAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result,"Unknown item type in record '%s' in item.xml",newmaster->mID.GetName());
      newmaster->mDeliveryType = (cStatsEnhancementMaster::DeliveryType) enumVal;
    }

    data_node = node->FirstChild("Slot");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      val = data_node->FirstChild()->Value();
      SyAssertf(NUM_SLOT_TYPENAMES == cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES,"Forgot to add enhancement slot typename?");
      result = ChooseEnum(val,l_SlotTypenames,NUM_SLOT_TYPENAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result,"Unknown item type in record '%s' in item.xml",newmaster->mID.GetName());
      newmaster->mSlotType = (cStatsEnhancementMaster::SlotType) enumVal;
    }

    data_node = node->FirstChild("AttachCost");
    if (data_node != NULL)
    {
      newmaster->mSlotUsageCost = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Spell");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mSpellID = SyHashResourceID(data_node->FirstChild()->Value()); 
    }

    data_node = node->FirstChild("SpellChance");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mSpellChance = atoi(data_node->FirstChild()->Value()); 
      GAME_ASSERT(ERROR_DESIGN, newmaster->mSpellChance > 0 && newmaster->mSpellChance <= 100, "Invalid spell chance for enhancement '%s'", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("AttachFX");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mAttachFXID = SyHashResourceID(data_node->FirstChild()->Value()); 
    }

    data_node = node->FirstChild("EquipMelee");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mEquipSlots[EQUIP_MELEE] = atoi(data_node->FirstChild()->Value()) != 0; 
    }

    data_node = node->FirstChild("EquipRanged");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mEquipSlots[EQUIP_RANGED] = atoi(data_node->FirstChild()->Value()) != 0; 
    }

    data_node = node->FirstChild("EquipHead");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mEquipSlots[EQUIP_HEAD] = atoi(data_node->FirstChild()->Value()) != 0; 
    }

    data_node = node->FirstChild("EquipChest");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mEquipSlots[EQUIP_CHEST] = atoi(data_node->FirstChild()->Value()) != 0; 
    }

    data_node = node->FirstChild("EquipShoulders");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mEquipSlots[EQUIP_SHOULDERS] = atoi(data_node->FirstChild()->Value()) != 0; 
    }

    data_node = node->FirstChild("EquipLegs");
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      newmaster->mEquipSlots[EQUIP_LEGS] = atoi(data_node->FirstChild()->Value()) != 0; 
    }

    mEnhancementMasters.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}                                            

void                         
cDatabaseSys::LoadEnhancementComboModels()
{
  // load up xml file for enhancements...
  TiXmlDocument doc( "game_assets/design/data/player_weapon_models.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load player_weapon_models.xml");
    return;
  }

  TiXmlNode* node = 0;
  TiXmlNode* data_node;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "EnhancementModel" );

  while (node != NULL)
  {
    cStatsEnhancementComboModel *newmaster = SyNew cStatsEnhancementComboModel; 

    data_node = node->FirstChild("Universal");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value() && strlen(data_node->FirstChild()->Value()) > 0)
    {
      newmaster->mSlots[cStatsEnhancementMaster::ENH_SLOT_UNIVERSAL] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Statistical");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value() && strlen(data_node->FirstChild()->Value()) > 0)
    {
      newmaster->mSlots[cStatsEnhancementMaster::ENH_SLOT_STATISTICAL] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Proc");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value() && strlen(data_node->FirstChild()->Value()) > 0)
    {
      newmaster->mSlots[cStatsEnhancementMaster::ENH_SLOT_PROC] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Utility");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value() && strlen(data_node->FirstChild()->Value()) > 0)
    {
      newmaster->mSlots[cStatsEnhancementMaster::ENH_SLOT_UTILITY] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("BruteModel");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Player weapon model missing BruteModel entry in player_weapon_models.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value() && strlen(data_node->FirstChild()->Value()) > 0)
    {
      newmaster->mModelDataIDs[0] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("MageModel");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Player weapon model missing BruteModel entry in player_weapon_models.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mModelDataIDs[1] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ScoutRightModel");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Player weapon model missing BruteModel entry in player_weapon_models.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mModelDataIDs[2] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ScoutLeftModel");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Player weapon model missing BruteModel entry in player_weapon_models.xml");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mModelDataIDs[3] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    mEnhancementComboModels.Add(newmaster);
    node = node->NextSibling();
  }

  // ensure default entry
  cStatsEnhancementComboModel *newmaster = SyNew cStatsEnhancementComboModel; 
  newmaster->mModelDataIDs[0] = SyHashResourceID("brute_weapon_skull");
  newmaster->mModelDataIDs[1] = SyHashResourceID("brute_weapon_skull");
  newmaster->mModelDataIDs[2] = SyHashResourceID("scout_weapon_standard");
  newmaster->mModelDataIDs[3] = SyHashResourceID("scout_weapon_standard");
  mEnhancementComboModels.Add(newmaster);
}

void                         
cDatabaseSys::LoadMerchantSets()
{
  // load up xml file for merchants...
  TiXmlDocument doc( "game_assets/design/data/merchants.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load merchants.xml");
    return;
  }

  TiXmlNode* node = 0;
  TiXmlNode* data_node;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "MerchantSet" );
  SyAssert( node );

  while (node != NULL)
  {
    data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Enhancement with no Name attribute in item.xml");
    if (!data_node || !data_node->FirstChild() || !data_node->FirstChild()->Value())
    {
      node = node->NextSibling();
      continue;
    }

    cStatsMerchantSet *newmaster = NULL; 
    int index = mMerchantSets.Find(SyHashResourceID(data_node->FirstChild()->Value()));
    if (index == mMerchantSets.End())
    {
      newmaster = SyNew cStatsMerchantSet;
      newmaster->mID.SetName(data_node->FirstChild()->Value());
      mMerchantSets.Insert(newmaster->mID.GetID(), newmaster);
    }
    else
    {
      newmaster = mMerchantSets(index);
    }

    data_node = node->FirstChild("Item");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Merchant Set '%s' has empty Item field in merchants.xml", newmaster->mID.GetName());
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      const cStatsItemMaster* pItemMaster = GetItemMaster(SyHashResourceID(data_node->FirstChild()->Value()));

      if (pItemMaster)
      {
        newmaster->mItems.Add(pItemMaster->mID.GetID());
      }
    }

    node = node->NextSibling();
  }
}                                            

void                         
cDatabaseSys::LoadProjectileMasters()
{
  // load up xml file for projectiles...
	TiXmlDocument doc( "game_assets/design/data/projectile.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load projectile.xml");
    return;
	}
  
	TiXmlNode* node = 0;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Projectile" );
	SyAssert( node );

  while (node != NULL)
  {
    cStatsProjectileMaster *newmaster = SyNew cStatsProjectileMaster; 
    TiXmlNode* data_node = node->FirstChild("Name");
    GAME_FATAL(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Projectile with no Name attribute in projectile.xml");
    const char *name = data_node->FirstChild()->Value();
    newmaster->mID.SetName(name);

    data_node = node->FirstChild("Model");
    if (data_node != NULL)
    {
      newmaster->mModelDataID = SyHashResourceID(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, GetModelData(newmaster->mModelDataID) != NULL, "Could not find model data '%s' for projectile master '%s' in projectiles.xml", data_node->FirstChild()->Value(), newmaster->mID.GetName());
    }

    data_node = node->FirstChild("Speed");
    if (data_node != NULL)
    {
      newmaster->mSpeed = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Seeking");
    if (data_node != NULL)
    {
      newmaster->mSeeking = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("HomingRange");
    if (data_node != NULL)
    {
      newmaster->mHomingRange = (float)atof(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mSeeking <= 0.0001f || (newmaster->mHomingRange > 0.0f && newmaster->mHomingRange <= 100.0f), "Projectile '%s' with Seeking > 0 must have a HomingRange > 0 and <= 100", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("Gravity");
    if (data_node != NULL)
    {
      newmaster->mGravity =(float) (atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("Spins");
    if (data_node != NULL)
    {
      newmaster->mSpins = (atoi(data_node->FirstChild()->Value())==1);
    }

    data_node = node->FirstChild("Penetrates");
    if (data_node != NULL)
    {
      newmaster->mPenetrates = (atoi(data_node->FirstChild()->Value())==1);
    }

    data_node = node->FirstChild("PinCushion");
    if (data_node != NULL)
    {
      newmaster->mPinCushion = (atoi(data_node->FirstChild()->Value())==1);
    }

    data_node = node->FirstChild("CollisionRadius");
    if (data_node != NULL)
    {
      newmaster->mCollisionRadius =(float) (atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("Num");
    if (data_node != NULL)
    {
      newmaster->mNum = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mNum > 0, "Projectile Num attribute must be > 0 for '%s'", name);
      if (newmaster->mNum <= 0)
      {
        newmaster->mNum = 1;
      }
    }

    data_node = node->FirstChild("MaxArc");
    if (data_node != NULL)
    {
      newmaster->mMaxArc = (float)atof(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mMaxArc >= 0.0f, "MaxArc attribute for '%s' must be 0 or more degrees", name);
      if (newmaster->mMaxArc < 0.0)
      {
        newmaster->mMaxArc = 10.0f;
      }
    }

    data_node = node->FirstChild("Acceleration");
    if (data_node != NULL)
    {
      newmaster->mAcceleration = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("OrbitRadius");
    if (data_node != NULL)
    {
      newmaster->mOrbiting = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Meteor");
    if (data_node != NULL)
    {
      newmaster->mMeteor = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("VerticalWave");
    if (data_node != NULL)
    {
      newmaster->mVerticalWave = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DamageMultiplier");
    if (data_node != NULL)
    {
      newmaster->mDamageMultiplier = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AttachFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAttachFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("CreationFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCreationFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ImpactFXSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mImpactFXSetID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ImpactSoundSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mImpactSoundSetID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    mProjectileMasters.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}                                            

void                         
cDatabaseSys::LoadPropMasters()
{
  // load up xml file for props...
	TiXmlDocument doc( "game_assets/design/data/prop.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load prop.xml");
    return;
  }

  const char *l_MobilityTypenames[] = 
  {
    "Immobile",
    "Pushable",
    "Liftable",
    "Simulatable",
  };

  static const int NUM_MOBILITY_TYPENAMES = (sizeof(l_MobilityTypenames) / sizeof (l_MobilityTypenames[0]));
  SyAssertf(cStatsPropMaster::PROP_NUM_MOBILITY_TYPES == NUM_MOBILITY_TYPENAMES, "Forgot to add Prop Mobility Type Name?");

  TiXmlNode* node = 0;
  const char *text;
  int enumVal;
  bool bResult;

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "Prop" );
  SyAssert( node );

  while (node != NULL)
  {
    TiXmlNode* data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Prop with no Name attribute in prop.xml");
    if (!data_node || !data_node->FirstChild() || !data_node->FirstChild()->Value())
    {
      node = node->NextSibling();
      continue;
    }

    cStatsPropMaster *newmaster = SyNew cStatsPropMaster; 
    text = data_node->FirstChild()->Value();
    newmaster->mID.SetName(text);

    // read anim set
    data_node = node->FirstChild("AnimSet");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'AnimSet' for prop %s in database XML file", newmaster->mID.GetName());
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAnimSet = SyHashResourceID(data_node->FirstChild()->Value());
    }

    // read model name
    data_node = node->FirstChild("Model");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'Model' for prop %s in database XML file", newmaster->mID.GetName());
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mModelDataID = SyHashResourceID(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, GetModelData(newmaster->mModelDataID) != NULL, "Could not find model data '%s' for prop master '%s' in props. XML file", data_node->FirstChild()->Value(), newmaster->mID.GetName());
    }

    // read Maxhealth amt
    data_node = node->FirstChild("MaxHealth");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mMaxHealth = atoi(data_node->FirstChild()->Value());
    }

    // read container flag
    data_node = node->FirstChild("TreasureSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      if (strcmp(data_node->FirstChild()->Value(), "0") != 0)
      {
        newmaster->mTreasureSet = SyHashResourceID(data_node->FirstChild()->Value());
      }
    }

    data_node = node->FirstChild("Mobility");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      text = data_node->FirstChild()->Value();
      bResult = ChooseEnum(text, l_MobilityTypenames, cStatsPropMaster::PROP_NUM_MOBILITY_TYPES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown prop mobility type in record '%s' in prop.xml",newmaster->mID.GetName());
      newmaster->mMobility = (cStatsPropMaster::MobilityType) enumVal;
    }

    data_node = node->FirstChild("Weight");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mWeight = static_cast< float >(atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("ThrownDamageMultiplier");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mThrownDamageMultiplier = static_cast< float >(atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("NumThrows");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNumThrows = atoi(data_node->FirstChild()->Value());
      GAME_WARN(ERROR_DESIGN, newmaster->mNumThrows <= 0 || newmaster->mNumThrows >= newmaster->mMaxHealth, "Prop master '%s' cannot have NumThrows >1 but less than MaxHealth (must be at MaxHealth) in props.xml", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("ThrowSpell");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mThrowSpellID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("SpeedThreshold");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSpeedThreshold = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Script");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      text = data_node->FirstChild()->Value();
      newmaster->mScriptName = SyNew char[strlen(text)+1];
      sprintf(newmaster->mScriptName,"%s",text);
    }

    data_node = node->FirstChild("NameString");
    if (data_node != NULL)
    {
      newmaster->mT4_NameID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Scale");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mScale = static_cast< float >(atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("ActivateRadius");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mActivateRadius = static_cast< float >(atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("PhysicsMass");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      // TODO - make sure we have reasonable values!
      //GAME_ASSERT(ERROR_DESIGN, false, "Invalid value for physics mass!");
      newmaster->mPhysicsMass = static_cast< float >(atof( data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("PhysicsFriction");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      // TODO - make sure we have reasonable values!
      //GAME_ASSERT(ERROR_DESIGN, false, "Invalid value for physics friction!");
      newmaster->mPhysicsFriction = static_cast< float >(atof( data_node->FirstChild()->Value()));
    }

    static const int NUM_FACTION_NAMES = (sizeof(NPC_FACTION_NAMES) / sizeof (NPC_FACTION_NAMES[0]));

    data_node = node->FirstChild("Faction");
    if (data_node != NULL)
    {
      int enumVal = 0;
      const char *type = data_node->FirstChild()->Value();
      SyAssertf(NUM_FACTION_NAMES == NPCFACTION_MAXTYPES,"Forgot to add NPC Faction type name.");
      bool result = ChooseEnum(type,NPC_FACTION_NAMES,NUM_FACTION_NAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, result,"Unknown Faction type in record '%s' in prop.xml",newmaster->mID.GetName());
      newmaster->mFaction = (char)enumVal;
    }
    else
    {
      newmaster->mFaction = NPCFACTION_PROP;
    }

    mPropMasters.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}                                            



void                         
cDatabaseSys::LoadClasses()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/class_stats.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    SyAssert(0);
    return;
	}
  
	TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Class_Stats" );
	SyAssert( node );

  while (node != NULL)
  {
    cCharacterClass *newClass = ParseClass(node);
    if (newClass != NULL)
    {
      mCharacterClasses.Insert(newClass->mName.GetID(),newClass);
    }
    node = node->NextSibling();
  }
}

void                         
cDatabaseSys::LoadLevels()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/level_stats.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    SyAssert(0);
    return;
	}
  
	TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Level_Stats" );
	SyAssert( node );

  while (node != NULL)
  {
    cCharacterLevel *newLevel = ParseLevel(node);
    if (newLevel != NULL)
    {
      mLevelMasters.Insert(newLevel->mLevel,newLevel);
    }
    node = node->NextSibling();
  }
}


void                         
cDatabaseSys::LoadDropLists()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/drop_list.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    SyAssert(0);
    return;
	}
  
	TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Drop_List" );
	SyAssert( node );

  while (node != NULL)
  {
    cDropList *newDropList = ParseDropList(this,node);
    if (newDropList != NULL)
    {
      mDropLists.Insert(newDropList->mName.GetID(),newDropList);
    }
    node = node->NextSibling();
  }
}


void                         
cDatabaseSys::LoadTreasureSets()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/treasure_set.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    SyAssert(0);
    return;
	}
  
	TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "Treasure_Set" );
	SyAssert( node );

  while (node != NULL)
  {
    cTreasureSet *newTreasureSet = ParseTreasureSet(this,node);
    if (newTreasureSet != NULL)
    {
      mTreasureSets.Insert(newTreasureSet->mName.GetID(),newTreasureSet);
    }
    node = node->NextSibling();
  }
}

void
cDatabaseSys::LoadDebris()
{
  // load up debris group xml... 
  const char *filename ="game_assets/design/data/debris_groups.xml" ;
  TiXmlDocument doc(filename);
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    GAME_ASSERT(ERROR_DESIGN, 0, "Unable to load %s",filename);
    return;
	}
  
	TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "DebrisGroup" );
	SyAssert( node );

  while (node != NULL)
  {
    TiXmlNode* data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Debris group with no Name attribute in debris_groups.xml");
    if (!data_node || !data_node->FirstChild() || !data_node->FirstChild()->Value())
    {
      node = node->NextSibling();
      continue;
    }

    cDebrisMaster *newmaster = SyNew cDebrisMaster;
    const char *text = data_node->FirstChild()->Value();
    newmaster->mID.SetName(text);

    data_node = node->FirstChild("Radius");
    if (data_node != NULL)
    {
      newmaster->mRadius = (float)atof(data_node->FirstChild()->Value());
    }

    if (newmaster != NULL)
    {
      mDebrisMasters.Insert(newmaster->mID.GetID(),newmaster);
    }
    node = node->NextSibling();
  }

	TiXmlDocument shard_doc( "game_assets/design/data/debris_shards.xml" );
	loadOkay = shard_doc.LoadFile();

	if ( !loadOkay )
	{
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load debris_shards.xml");
    return;
	}
  
	node = 0;
  node = shard_doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "DebrisShard" );
	SyAssert( node );

  while (node != NULL)
  {
    cDebrisShardMaster *newmaster = SyNew cDebrisShardMaster;
    TiXmlNode* data_node = node->FirstChild("Model");
    GAME_ASSERT(ERROR_DESIGN, data_node!=NULL, "Debris Shard does not have model name in database");
    const char *text = data_node->FirstChild()->Value();


    // parse model

    if (text!=NULL && text[0] != '\0')
    {

      // todo: wait to load debris models?

      static const char *path = "game_assets/art/props/debris/";
      static const char *extension = ".esf";
      newmaster->mModelName = SyNew char[strlen(path)+strlen(text)+strlen(extension)+ 1]; 
      sprintf(newmaster->mModelName,"%s%s%s",path,text,extension);

    }
    data_node = node->FirstChild("Quantity");
    if (data_node != NULL)
    {
      newmaster->mQuantity = atoi(data_node->FirstChild()->Value()) ;
    }

    data_node = node->FirstChild("Radius");
    if (data_node != NULL)
    {
      newmaster->mRadius =(float)atof(data_node->FirstChild()->Value()) ;
    }

    data_node = node->FirstChild("Scale");
    if (data_node != NULL)
    {
      newmaster->mScale = (float)atof(data_node->FirstChild()->Value()) ;
    }

    data_node = node->FirstChild("Height");
    if (data_node != NULL)
    {
      newmaster->mHeight = (float) atof(data_node->FirstChild()->Value()) ;
    }

    data_node = node->FirstChild("Node");
    if (data_node != NULL)
    {
      newmaster->mNodeID = SyHashResourceID(data_node->FirstChild()->Value()) ;
    }

    data_node = node->FirstChild("GroupName");

    tGameID id =SyHashResourceID(data_node->FirstChild()->Value());
    cDebrisMaster  *group = const_cast<cDebrisMaster*>(GetDebrisMaster(id));

    if (group == NULL)
    {
      GAME_ASSERT(ERROR_DESIGN, false,"Bad group '%s' in debris shard xml file.",data_node->FirstChild()->Value());
      delete newmaster;
    }
    else
    {
      group->mShardMasters.Add(newmaster);
    }

    node = node->NextSibling();
  }
}

extern const char *l_AnimStatenames[];

void
cDatabaseSys::LoadSpells()
{
  // load up main spell table xml... 
  TiXmlDocument spellDoc( "game_assets/design/data/spells.xml" );
  bool loadOkay = spellDoc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Unable to load spells.xml");
    return;
  }

  // clear out old spells to in case we're reloading
  for (int index = mSpellMasters.Begin(); index != mSpellMasters.End();index = mSpellMasters.Next(index))
  {
    delete mSpellMasters(index);
  }
  mSpellMasters.Clear();

  TiXmlNode* data_node;
  TiXmlNode* node = 0;
  const char* val;
  int enumVal;
  bool bResult;

  SyAssert(mpTitan!=NULL && mpTitan->GetScene()!=NULL && mpTitan->GetScene()->GetDictionary()!=NULL);
  //SyDictionary *pDictionary = mpTitan->GetScene()->GetDictionary();

  node = spellDoc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "Spell" );
  SyAssert( node );

  const char *originMechanismNames[] = 
  {
    "Caster",
    "Target",
    "Caster Location",
    "Target Location",
    "Random Location",
    "Ward",
  };

  const int NUM_ORIGIN_TYPENAMES = (sizeof(originMechanismNames) / sizeof (originMechanismNames[0]));
  SyAssertf(NUM_ORIGIN_TYPENAMES == cSpellMaster::SPELL_ORIGIN_NUM_TYPES,"Forgot to add spell origin typename?");

  const char *deliveryMechanismNames[] = 
  {
    "Single",
    "Radius",
    "Nova",
    "Chain",
    "Wave Angle",
    "Wave Line",
    "Beam",
    "Arc",
    "Wall",
    "Trail",
    "Cone",
    "Meteor Shower"
  };

  const int NUM_DELIVERY_TYPENAMES = (sizeof(deliveryMechanismNames) / sizeof (deliveryMechanismNames[0]));

  const char *targetingNames[] = 
  {
    "Any",
    "Allies",
    "Enemies",
    "Enemies And Props",
  };

  const int NUM_TARGETING_TYPENAMES = (sizeof(targetingNames) / sizeof (targetingNames[0]));
  SyAssertf(NUM_DELIVERY_TYPENAMES == cSpellMaster::SPELL_DELIVERY_NUM_TYPES,"Forgot to add spell delivery typename?");

  const char *effectNames[] = 
  {
    "Damage",
    "Damage Over Time",
    "Heal",
    "Decrease Mana",
    "Decrease Mana Over Time",
    "Increase Mana",
    "Health Drain",
    "Mana Drain",
    "Health Regen",
    "Mana Regen",
    "Attack Speed",
    "Movement Speed",
    "Attack Power",
    "Spell Power",
    "Block Power",
    "Critical Chance Melee",
    "Critical Chance Ranged",
    "Dodge Chance Melee",
    "Dodge Chance Ranged",
    "Defense",
    "Damage Done",
    "Damage Taken",
    "Size",
    "Stun",
    "Stun Immunity",
    "Sleep",
    "Sleep Immunity",
    "Confuse",
    "Confuse Immunity",
    "Charm",
    "Charm Immunity",
    "Invisibility",
    "See Invisibility",
    "KnockBack",
    "KnockDown",
    "KnockBack Immunity",
    "KnockDown Immunity",
    "Vortex",
    "Vortex Immunity",
    "Freeze",
    "Freeze Immunity",
    "Spread Fire",
    "Kill",
    "Kill Immunity",
    "Bind",
    "Bind Immunity",
    "Silence",
    "Silence Immunity",
    "Fear",
    "Fear Immunity",
    "Force Field",
    "Damage Field",
    "Teleport",
    "Illusion",
    "Resurrect",
    "Dummy Target",
    "Summon Pet",
    "Trigger Spell",
    "Trigger Target Spell",
    "Trigger Attack Spell",
    "Trigger Defend Spell",
    "Trigger Death Spell",
    "Show Map Enemies",
    "Show Map Treasure",
    "Health Absorb",
    "Mana Absorb",
    "Essence Absorb",
    "Health Drop Chance",
    "Mana Drop Chance",
    "Essence Drop Chance",
    "Item Drop Chance",
    "Experience Gain",
    "Spawn Treasure"
  };

  const int NUM_EFFECT_TYPENAMES = (sizeof(effectNames) / sizeof (effectNames[0]));
  SyAssertf(NUM_TARGETING_TYPENAMES == cSpellMaster::SPELL_TARGETING_NUM_TYPES,"Forgot to add spell targeting typename?");

  while (node != NULL)
  {
    cSpellMaster *newmaster = SyNew cSpellMaster;
    data_node = node->FirstChild("Name");
    GAME_FATAL(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Spell with no Name found in spells.xml");
    val = data_node->FirstChild()->Value();
    newmaster->mID.SetName(val);

    data_node = node->FirstChild("Origin");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Spell '%s' has no PointOfOrigin found in spells.xml", newmaster->mID.GetName());
    enumVal = 0;
    val = data_node->FirstChild()->Value();
    bResult = ChooseEnum(val,originMechanismNames,cSpellMaster::SPELL_ORIGIN_NUM_TYPES, enumVal);
    GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown spell point of origin type in record '%s' in spell.xml",newmaster->mID.GetName());
    newmaster->mOrigin = (cSpellMaster::OriginMechanism) enumVal;

    data_node = node->FirstChild("DeliveryType");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Spell '%s' has no Delivery Mechanism found in spells.xml", newmaster->mID.GetName());
    enumVal = 0;
    val = data_node->FirstChild()->Value();
    bResult = ChooseEnum(val,deliveryMechanismNames,cSpellMaster::SPELL_DELIVERY_NUM_TYPES, enumVal);
    GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown spell delivery type in record '%s' in spell.xml",newmaster->mID.GetName());
    newmaster->mDelivery = (cSpellMaster::DeliveryMechanism) enumVal;

    data_node = node->FirstChild("Targeting");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Spell '%s' has no Targeting type found in spells.xml", newmaster->mID.GetName());
    enumVal = 0;
    val = data_node->FirstChild()->Value();
    bResult = ChooseEnum(val,targetingNames,cSpellMaster::SPELL_DELIVERY_NUM_TYPES, enumVal);
    GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown spell targeting type in record '%s' in spell.xml",newmaster->mID.GetName());
    newmaster->mTargeting = (cSpellMaster::TargetingMechanism) enumVal;

    data_node = node->FirstChild("Offset");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mOffset = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Range");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mRange = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Width");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mWidth = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Duration");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDeliveryDuration = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("RefreshTime");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mRefreshTime = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AngleStart");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAngleStart = SY_DEG_TO_RAD((float)atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("AngleEnd");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAngleEnd = SY_DEG_TO_RAD((float)atof(data_node->FirstChild()->Value()));
    }

    data_node = node->FirstChild("Blockable");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mbBlockable = atoi(data_node->FirstChild()->Value()) > 0;
    }

    data_node = node->FirstChild("Jumpable");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mbJumpable = atoi(data_node->FirstChild()->Value()) > 0;
    }

    data_node = node->FirstChild("Projectile");
    if (data_node && data_node->FirstChild())
    {
      val = data_node->FirstChild()->Value();
      if (val && val[0] != 0)
      {
        newmaster->mProjectileTypeID = SyHashResourceID(data_node->FirstChild()->Value());
        
        GAME_ASSERT(ERROR_DESIGN, GetProjectileMaster(newmaster->mProjectileTypeID)!=NULL, "Could not find projectile type %s for spell %s", val, newmaster->mID.GetName());
      }
    }

    data_node = node->FirstChild("MaxTargets");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mMaxTargets = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ManaCost");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mManaCost = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("StackingCategory");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mStackingCategory = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("WardProp");
    if (data_node && data_node->FirstChild())
    {
      val = data_node->FirstChild()->Value();
      if (val && val[0] != 0)
      {
        newmaster->mWardPropID = SyHashResourceID(data_node->FirstChild()->Value());

        GAME_ASSERT(ERROR_DESIGN, GetPropMaster(newmaster->mWardPropID)!=NULL, "Could not find ward prop type %s for spell %s", val, newmaster->mID.GetName());
      }
    }

    data_node = node->FirstChild("CastAnim");
    if (data_node && data_node->FirstChild())
    {
      val = data_node->FirstChild()->Value();
      if (val && val[0] != 0)
      {
        enumVal = 0;
        bResult = ChooseEnum(val,l_AnimStatenames, NUM_ANIM_STATES, enumVal);
        GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown cast anim %s for spell %s in spell.xml", val, newmaster->mID.GetName());
        newmaster->mCastAnimID = (eAnimState)enumVal;
      }
    }

    data_node = node->FirstChild("CastTime");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNPCCastTime = (float)atof(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mNPCCastTime < 0.000001f || newmaster->mCastAnimID != 0, "Spell %s has NPC cast time but no cast animation", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("CastFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCastFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("CastStartFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCastStartFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AreaFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mAreaFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("CastLoopFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCastLoopFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ImpactFXSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mImpactFXSetID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ImpactSoundSet");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mImpactSoundSetID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DamageType");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      val = data_node->FirstChild()->Value();
      SyAssertf(NUM_DAMAGE_TYPENAMES == NUM_DAMAGE_TYPES,"Forgot to add normal damage typename?");
      bResult = ChooseEnum(val,s_DamageTypenames,NUM_DAMAGE_TYPES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown damage type in spells.xml (spell='%s')", newmaster->mID.GetName());
      newmaster->mDamageType = (eDamageType) enumVal;
    }

    data_node = node->FirstChild("ElementalType");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      val = data_node->FirstChild()->Value();
      SyAssertf(NUM_ELEMENTAL_TYPENAMES == NUM_ELEMENTAL_TYPES,"Forgot to add normal damage typename?");
      bResult = ChooseEnum(val,s_ElementalTypenames,NUM_ELEMENTAL_TYPES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown elemental type in spells.xml (spell='%s')", newmaster->mID.GetName());
      newmaster->mElementalType = (eElementalType) enumVal;
    }

    mSpellMasters.Insert(newmaster->mID.GetID(), newmaster);
    node = node->NextSibling();
  }

  // load up spell effects xml... 
  TiXmlDocument spellEffectDoc( "game_assets/design/data/spell_effects.xml" );
  loadOkay = spellEffectDoc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Unable to load spell_effects.xml");
    return;
  }

  cSpellMaster* pSpell;

  node = spellEffectDoc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "SpellEffect" );
  SyAssert( node );

  while (node != NULL)
  {
    cSpellEffectMaster *newmaster = SyNew cSpellEffectMaster;

    data_node = node->FirstChild("Spell");
    GAME_ASSERT(ERROR_DESIGN, data_node!=NULL && data_node->FirstChild()!=NULL, "No Spell attribute for effect in spell_effects.xml");
    val = data_node->FirstChild()->Value();
    GAME_ASSERT(ERROR_DESIGN, val!=NULL && val[0] != 0,"No Spell attribute for effect in spell_effects.xml");
    newmaster->mSpellOwnerID = SyHashResourceID(val);
    pSpell = const_cast<cSpellMaster*>(GetSpellMaster(newmaster->mSpellOwnerID));
    GAME_ASSERT(ERROR_DESIGN, pSpell!=NULL, "Bad spell '%s' for effect in spell_effects xml file.",val);

    if (!pSpell)
    {
      delete newmaster;
      node = node->NextSibling();
      continue;
    }

    data_node = node->FirstChild("Type");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Spell effect with no Type in spell_effects.xml (spell='%s')", pSpell->mID.GetName());
    
    enumVal = 0;
    val = data_node->FirstChild()->Value();
    SyAssertf(NUM_EFFECT_TYPENAMES == cSpellEffectMaster::SPELLEFFECT_NUM_TYPES,"Forgot to add spell effect typename?");
    bResult = ChooseEnum(val,effectNames,cSpellEffectMaster::SPELLEFFECT_NUM_TYPES, enumVal);
    GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown spell effect type in spell_effects.xml (spell='%s')", pSpell->mID.GetName());
    newmaster->mType = (cSpellEffectMaster::EffectType) enumVal;

    data_node = node->FirstChild("BaseAmount");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mBaseAmount = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("MultAmount");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mMultAmount = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Duration");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDuration = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("PercentChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mPercentChance = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mPercentChance > 0 && newmaster->mPercentChance <= 100, "Spell effect has percent chance outside of 0-100 in spell_effects.xml (spell='%s')", pSpell->mID.GetName());
    }

    data_node = node->FirstChild("TargetFX");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mTargetFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("TargetRace");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mTargetRace = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ObjectName");
    if (data_node && data_node->FirstChild())
    {
      val = data_node->FirstChild()->Value();
      if (val && val[0] != 0)
      {
        newmaster->mObjectNameID.SetName(val);
      }
    }

    if (pSpell == NULL)
    {
      delete newmaster;
    }
    else
    {
      pSpell->mEffects.Add(newmaster);
    }

    node = node->NextSibling();
  }

  // link triggered spells to their parents
  cSpellMaster* pTriggerSpell;
  int triggerSpellIndex;

  for (int iSpell = mSpellMasters.Begin(); iSpell != mSpellMasters.End(); iSpell = mSpellMasters.Next(iSpell))
  {
    pSpell = mSpellMasters(iSpell);

    for (int iEffect = pSpell->mEffects.Begin(); iEffect != pSpell->mEffects.End(); iEffect = pSpell->mEffects.Next(iEffect))
    {
      if (pSpell->mEffects(iEffect)->mType == cSpellEffectMaster::SPELLEFFECT_TRIGGER_SPELL ||
          pSpell->mEffects(iEffect)->mType == cSpellEffectMaster::SPELLEFFECT_TRIGGER_TARGET_SPELL ||
          pSpell->mEffects(iEffect)->mType == cSpellEffectMaster::SPELLEFFECT_TRIGGER_SPELL_ON_ATTACK ||
          pSpell->mEffects(iEffect)->mType == cSpellEffectMaster::SPELLEFFECT_TRIGGER_SPELL_ON_DEFEND)
      {
        triggerSpellIndex = mSpellMasters.Find(pSpell->mEffects(iEffect)->mObjectNameID.GetID());
        GAME_ASSERT(ERROR_DESIGN, triggerSpellIndex != mSpellMasters.End(), "Trigger spell '%s' not found for spell '%s'", pSpell->mEffects(iEffect)->mObjectNameID.GetName(), pSpell->mID.GetName());

        if (triggerSpellIndex != mSpellMasters.End())
        {
          pTriggerSpell = mSpellMasters(triggerSpellIndex);
          GAME_ASSERT(ERROR_DESIGN, pTriggerSpell!=NULL, "Trigger spell '%s' not found for spell '%s'", pSpell->mEffects(iEffect)->mObjectNameID.GetName(), pSpell->mID.GetName());

          if (pTriggerSpell)
          {
            pTriggerSpell->mParentSpellID = pSpell->mID.GetID();
          }
        }
      }
    }
  }
}

void
cDatabaseSys::LoadAbilities()
{
  // load up main spell table xml... 
  TiXmlDocument spellDoc( "game_assets/design/data/abilities.xml" );
  bool loadOkay = spellDoc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Unable to load abilities.xml");
    return;
  }

  TiXmlNode* data_node;
  TiXmlNode* node = 0;
  bool bResult;
  int enumVal;
  const char* val;
  char attrName[64];

  node = spellDoc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "Ability" );
  SyAssert( node );

  const char *l_ComboNames[] = 
  {
    "ComboL",
    "ComboH",
    "ComboLL",
    "ComboLH",                       
    "ComboHL",
    "ComboHH",
    "ComboLLL",
    "ComboLLH",
    "ComboLHL",
    "ComboLHH",
    "ComboHLL",
    "ComboHLH",
    "ComboHHL",
    "ComboHHH",
    "ComboLLLL",
    "RangedAttack",
    "JumpAttack",
    "ThrowCharacter"
  };

  static const int NUM_COMBO_NAMES = (sizeof(l_ComboNames) / sizeof (l_ComboNames[0]));
  SyAssertf(NUM_COMBO_NAMES == NUM_COMBOS, "Forgot to add combo name?");

  const char *l_TypeNames[] = 
  {
    "Spell",
    "Combo",
  };

  while (node != NULL)
  {
    cAbilityMaster *newmaster = SyNew cAbilityMaster;
    data_node = node->FirstChild("AbilityLine");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Ability with no AbilityLine attribute in abilities.xml");
    val = data_node->FirstChild()->Value();
    newmaster->mID.SetName(val);

    data_node = node->FirstChild("Rank");
    if (data_node != NULL)
    {
      newmaster->mRank = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Class");
    if (data_node != NULL && data_node->FirstChild()!=NULL && data_node->FirstChild()->Value()!=NULL && data_node->FirstChild()->Value()[0] != 0)
    {
      newmaster->mClass = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("LevelMin");
    if (data_node != NULL)
    {
      newmaster->mLevelMin = atoi(data_node->FirstChild()->Value());
    }

    for (int i=0; i<cAbilityMaster::MAX_PREREQS; ++i)
    {
      sprintf(attrName, "AbilityPrereq%d", i+1);
      data_node = node->FirstChild(attrName);
      if (data_node != NULL && data_node->FirstChild()!=NULL && data_node->FirstChild()->Value()!=NULL && data_node->FirstChild()->Value()[0] != 0)
      {
        newmaster->mAbilityPrereqs[i] = SyHashResourceID(data_node->FirstChild()->Value());
      }
    }

    data_node = node->FirstChild("Cost");
    if (data_node != NULL)
    {
      newmaster->mCost = atoi(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Type");
    GAME_ASSERT(ERROR_DESIGN, data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL, "Bad ability type for '%s' in abilities.xml", newmaster->mID.GetName());
    enumVal = 0;
    if (data_node != NULL && data_node->FirstChild() != NULL && data_node->FirstChild()->Value() != NULL)
    {
      bResult = ChooseEnum(data_node->FirstChild()->Value(),l_TypeNames, 2, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown ability type for '%s' in abilities.xml",newmaster->mID.GetName());
    }

    data_node = node->FirstChild("NameString");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mNameString = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DescString");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDescString = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Icon");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mIconID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Ability");
    if (data_node != NULL && data_node->FirstChild()!=NULL && data_node->FirstChild()->Value()!=NULL && data_node->FirstChild()->Value()[0] != 0)
    {
      if (0 == enumVal) // spells
      {
        newmaster->mSpell = SyHashResourceID(data_node->FirstChild()->Value());
      }
      else if (1 == enumVal) // spells
      {
        enumVal = 0;
        bResult = ChooseEnum(data_node->FirstChild()->Value(),l_ComboNames,NUM_COMBO_NAMES, enumVal);
        GAME_ASSERT(ERROR_DESIGN, bResult, "Unknown combo type for ability '%s' in abilities.xml",newmaster->mID.GetName());
        newmaster->mCombo = (eComboType) enumVal;
      }
      else
      {
        GAME_ASSERT(ERROR_DESIGN, false, "Unknown ability for '%s' in abilities.xml",newmaster->mID.GetName());
      }
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "Unknown ability for '%s' in abilities.xml",newmaster->mID.GetName());
    }

    GAME_ASSERT(ERROR_DESIGN, (ID_NONE == newmaster->mSpell && NUM_COMBOS != newmaster->mCombo) || 
                              (ID_NONE != newmaster->mSpell && NUM_COMBOS == newmaster->mCombo),
                "Ability %s must have a spell OR combo", newmaster->mID.GetName());


    int foundIndex = mAbilityMasters.Find(newmaster->mID.GetID());
    if (foundIndex == mAbilityMasters.End())
    {
      foundIndex = mAbilityMasters.Insert(newmaster->mID.GetID(), AbilityVector());
    }

    mAbilityMasters(foundIndex).Add(newmaster);
    node = node->NextSibling();
  }

  for (int i = mAbilityMasters.Begin(); i != mAbilityMasters.End();i = mAbilityMasters.Next(i))
  {
    for (int j = mAbilityMasters(i).Begin(); j != mAbilityMasters(i).End();j = mAbilityMasters(i).Next(j))
    {
      int k = mUnlockAbilityList.Begin();
      while (k != mUnlockAbilityList.End())
      {
        if (SyStr::stricmp(mAbilityMasters(i)(j)->mID.GetName(), mUnlockAbilityList(k)->mID.GetName()) < 0)
        {
          break;
        }

        k = mUnlockAbilityList.Next(k);
      }

      if (k == mUnlockAbilityList.End())
      {
        mUnlockAbilityList.Add(mAbilityMasters(i)(j));
      }
      else
      {
        mUnlockAbilityList.Add();

        if (mUnlockAbilityList.Size() > 1)
        {
          for (int l=mUnlockAbilityList.Prev(mUnlockAbilityList.End()); l > k; l=mUnlockAbilityList.Prev(l))
          {
            mUnlockAbilityList(l) = mUnlockAbilityList(mUnlockAbilityList.Prev(l));
          }
        }

        mUnlockAbilityList(k) = mAbilityMasters(i)(j);
      }
    }
  }
}

void                         
cDatabaseSys::LoadBlockBehaviors()
{
  // load up xml file for props...
  TiXmlDocument doc( "game_assets/design/data/block_behaviors.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load block_behaviors.xml");
    return;
  }

  // clear out old spells to in case we're reloading
  for (int index = mBlockBehaviors.Begin(); index != mBlockBehaviors.End();index = mBlockBehaviors.Next(index))
  {
    delete mBlockBehaviors(index);
  }
  mBlockBehaviors.Clear();

  TiXmlNode* node = 0;
  TiXmlNode* data_node;
  const char *text;
  int enumVal;
  bool result;

  const char *l_PBSNames[] = 
  {
    "Ignore",
    "Switch Targets",
    "Back Off",
    "Circle Around",
    "Try Different Attack",
  };

  static const int NUM_PBS_NAMES = (sizeof(l_PBSNames) / sizeof (l_PBSNames[0]));
  SyAssertf(NUM_PBS_NAMES == cStatsBlockBehavior::PBS_NUM_STRATEGIES, "Forgot to add player block strategy name?");

  const char *l_NPCBSNames[] = 
  {
    "After Hit",
    "Next Hit",
    "After Attack",
    "On Close",
    "While Close",
  };

  static const int NUM_NPCBS_NAMES = (sizeof(l_NPCBSNames) / sizeof (l_NPCBSNames[0]));
  SyAssertf(NUM_NPCBS_NAMES == cStatsBlockBehavior::NPCBS_NUM_STRATEGIES, "Forgot to add NPC block strategy name?");

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "BlockBehavior" );
  SyAssert( node );

  while (node != NULL)
  {
    cStatsBlockBehavior *newmaster = SyNew cStatsBlockBehavior; 
    data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "Block Behavior with no Name attribute in block_behaviors.xml");
    text = data_node->FirstChild()->Value();
    newmaster->mID.SetName(text);

    data_node = node->FirstChild("BlockLightChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] >= 0 && newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] <= 100, "BlockBehavior %s BlockChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("BlockHeavyChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] >= 0 && newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] <= 100, "BlockBehavior %s BlockChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("BlockRangedChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_RANGED] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_RANGED] >= 0 && newmaster->mBlockPercentChance[cStatsBlockBehavior::BLOCK_ATTACK_RANGED] <= 100, "BlockBehavior %s BlockChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("DodgeLightChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] >= 0 && newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] <= 100, "BlockBehavior %s DodgeChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("DodgeHeavyChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] >= 0 && newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] <= 100, "BlockBehavior %s DodgeChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("DodgeRangedChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_RANGED] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_RANGED] >= 0 && newmaster->mDodgePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_RANGED] <= 100, "BlockBehavior %s DodgeChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("RiposteLightChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mRipostePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mRipostePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] >= 0 && newmaster->mRipostePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_LIGHT] <= 100, "BlockBehavior %s RiposteLightChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("RiposteHeavyChance");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mRipostePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] = atoi(data_node->FirstChild()->Value());
      GAME_ASSERT(ERROR_DESIGN, newmaster->mRipostePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] >= 0 && newmaster->mRipostePercentChance[cStatsBlockBehavior::BLOCK_ATTACK_HEAVY] <= 100, "BlockBehavior %s RiposteHeavyChance must be between 0 and 100 in block_behaviors.XML file", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("NumHitsBeforeBlock");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'NumHitsBeforeBlock' for BlockBehavior %s in block_behaviors.XML file", newmaster->mID.GetName());
    newmaster->mNumHitsBeforeBlock = atoi(data_node->FirstChild()->Value());

    data_node = node->FirstChild("PlayerBlockStrategy");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'PlayerBlockStrategy' for BlockBehavior %s in block_behaviors.XML file", newmaster->mID.GetName());    
    enumVal = 0;
    text = data_node->FirstChild()->Value();
    result = ChooseEnum(text, l_PBSNames, cStatsBlockBehavior::PBS_NUM_STRATEGIES, enumVal);
    GAME_ASSERT(ERROR_DESIGN, result,"Unknown PlayerBlockStrategy type in record '%s' in block_behaviors.xml", newmaster->mID.GetName());
    newmaster->mPlayerBlockStrategy = (cStatsBlockBehavior::PlayerBlockStrategyType) enumVal;

    data_node = node->FirstChild("NPCBlockStrategy");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'NPCBlockStrategy' for BlockBehavior %s in block_behaviors.XML file", newmaster->mID.GetName());    
    enumVal = 0;
    text = data_node->FirstChild()->Value();
    result = ChooseEnum(text, l_NPCBSNames, cStatsBlockBehavior::NPCBS_NUM_STRATEGIES, enumVal);
    GAME_ASSERT(ERROR_DESIGN, result,"Unknown NPCBlockStrategy type in record '%s' in block_behaviors.xml", newmaster->mID.GetName());
    newmaster->mNPCBlockStrategy = (cStatsBlockBehavior::NPCBlockStrategyType) enumVal;

    data_node = node->FirstChild("BlockHoldTime");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'BlockHoldTime' for BlockBehavior %s in block_behaviors.XML file", newmaster->mID.GetName());
    newmaster->mBlockHoldTime = (float)atof(data_node->FirstChild()->Value());

    data_node = node->FirstChild("BlockReactTime");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'BlockReactTime' for BlockBehavior %s in block_behaviors.XML file", newmaster->mID.GetName());
    newmaster->mBlockReactTime = (float)atof(data_node->FirstChild()->Value());

    mBlockBehaviors.Insert(newmaster->mID.GetID(),newmaster);
    node = node->NextSibling();
  }
}                                            

void                         
cDatabaseSys::LoadAbilitySets()
{
  // load up xml file for props...
  TiXmlDocument doc( "game_assets/design/data/ability_sets.xml" );
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load ability_sets.xml");
    return;
  }

  // clear out old spells to in case we're reloading
  for (int index = mAbilitySets.Begin(); index != mAbilitySets.End();index = mAbilitySets.Next(index))
  {
    delete mAbilitySets(index);
  }
  mAbilitySets.Clear();

  TiXmlNode* node = 0;
  TiXmlNode* data_node;
  int enumVal;
  bool bResult;

  const char *l_ConditionNames[] = 
  {
    "Any",
    "Melee Attack",
    "Ranged Attack",
    "Block Break",
    "Low Health",
    "Ally Wounded",
    "Ally Dead",
    "Buff Ally In Combat"
  };

  static const int NUM_CONDITION_NAMES = (sizeof(l_ConditionNames) / sizeof (l_ConditionNames[0]));
  GAME_ASSERT(ERROR_CODE, NUM_CONDITION_NAMES == cStatsAbilitySet::ABILITY_CONDITION_MAX_TYPES, "Forgot to add ability set condition name?");

  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "AbilitySet" );
  SyAssert( node );
  
  cStatsAbilitySet *newmaster = NULL;; 
  int foundIndex;

  while (node != NULL)
  {
    data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "AbilitySet with no Name attribute in ability_sets.xml");
    foundIndex = mAbilitySets.Find(SyHashResourceID(data_node->FirstChild()->Value()));
    if (foundIndex == mAbilitySets.End())
    {
      newmaster = SyNew cStatsAbilitySet;
      newmaster->mID.SetName(data_node->FirstChild()->Value());
      mAbilitySets.Insert(newmaster->mID.GetID(),newmaster);
    }
    else
    {
      newmaster = mAbilitySets(foundIndex);
    }
    SyAssert(newmaster != NULL);

    cStatsAbilitySet::Entry entry;

    data_node = node->FirstChild("Condition");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      enumVal = 0;
      bResult = ChooseEnum(data_node->FirstChild()->Value(), l_ConditionNames, NUM_CONDITION_NAMES, enumVal);
      GAME_ASSERT(ERROR_DESIGN, bResult,"Unknown AbilitySet condition type in record '%s' in ability_sets.xml",newmaster->mID.GetName());
      entry.mCondition = (cStatsAbilitySet::UsageCondition) enumVal;
    }

    data_node = node->FirstChild("Weight");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(),"Missing Data Field 'Weight' for AbilitySet %s in ability_sets.XML file", newmaster->mID.GetName());
    entry.mWeight = atoi(data_node->FirstChild()->Value());

    data_node = node->FirstChild("Delay");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      entry.mDelay = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Ability1");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      entry.mAbilityID1 = SyHashResourceID(data_node->FirstChild()->Value());
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "AbilitySet %s must have at least one ability", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("Ability2");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      entry.mAbilityID2 = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Ability3");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      entry.mAbilityID3 = SyHashResourceID(data_node->FirstChild()->Value());
    }
  
    newmaster->mEntries.Add(entry);
    node = node->NextSibling();
  }
}                                            

void                         
cDatabaseSys::LoadTextIDs()
{
  // load up xml file for characters...
	TiXmlDocument doc( "game_assets/design/data/string_names.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    GAME_ASSERT(ERROR_DESIGN, false, "Could not load string_names.xml");
    return;
	}
  
	TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
	node = node->FirstChild( "String_Name" );
	SyAssert( node );

  while (node != NULL)
  {

    TiXmlNode* data_node = 0;
    data_node = node->FirstChild("ID");
    if (data_node != NULL)
    {
      uint32 index = atoi(data_node->FirstChild()->Value());
      data_node = node->FirstChild("Name");
      if (data_node != NULL)
      {
        tGameID nameID = SyHashResourceID(data_node->FirstChild()->Value());
        mTextIDs.Insert(nameID,index);
      }
      node = node->NextSibling();
    }
  }
}

void
cDatabaseSys::LoadImpactFXSets()
{
  // load up debris group xml... 
  const char *filename ="game_assets/design/data/impact_fx.xml" ;
  TiXmlDocument doc(filename);
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false,"Unable to load %s",filename);
    return;
  }

  TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "ImpactFXSet" );
  SyAssert( node );

  cImpactFXSet *newmaster;
  TiXmlNode* data_node;
  const char* text;

  while (node != NULL)
  {
    newmaster = SyNew cImpactFXSet;
    data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "ImpactFXSet with no Name attribute in impact_fx.xml");
    text = data_node->FirstChild()->Value();
    newmaster->mID.SetName(text);

    data_node = node->FirstChild("Character");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCharacterFXName.Concat(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("CharacterBlock");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mCharacterBlockFXName.Concat(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Prop");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mPropFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Debris");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mDebrisFXID = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DefaultSurface");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_DEFAULT] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Earth");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_EARTH] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Wood");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_WOOD] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Rock");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_ROCK] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Metal");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_METAL] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Sand");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_SAND] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Snow");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_SNOW] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Ice");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_ICE] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("DungeonWater");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_DUNGEON_WATER] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("SewerWater");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_SEWER_WATER] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("PondWater");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_POND_WATER] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Mud");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_MUD] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Lava");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      newmaster->mSurfaceFXIDs[GAME_SURFACE_LAVA] = SyHashResourceID(data_node->FirstChild()->Value());
    }

    mImpactFXSets.Insert(newmaster->mID.GetID(), newmaster);
    node = node->NextSibling();
  }
}

void
cDatabaseSys::LoadImpactSoundSets()
{
  // load up debris group xml... 
  const char *filename ="game_assets/design/data/impact_sounds.xml" ;
  TiXmlDocument doc(filename);
  bool loadOkay = doc.LoadFile();

  if ( !loadOkay )
  {
    GAME_ASSERT(ERROR_DESIGN, false,"Unable to load %s",filename);
    return;
  }

  TiXmlNode* node = 0;
  node = doc.FirstChild( "dataroot" );
  SyAssert(node);
  node = node->FirstChild( "ImpactSoundSet" );
  SyAssert( node );

  cImpactSoundSet *newmaster;
  TiXmlNode* data_node;
  int foundIndex;

  tGameID impactTypeID, soundID;

  while (node != NULL)
  {
    data_node = node->FirstChild("Name");
    GAME_ASSERT(ERROR_DESIGN, data_node && data_node->FirstChild() && data_node->FirstChild()->Value(), "ImpactSoundSet with no Name attribute in impact_sounds.xml");

    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      foundIndex = mImpactSoundSets.Find(SyHashResourceID(data_node->FirstChild()->Value()));
      if (foundIndex == mAbilitySets.End())
      {
        newmaster = SyNew cImpactSoundSet;
        newmaster->mID.SetName(data_node->FirstChild()->Value());
        mImpactSoundSets.Insert(newmaster->mID.GetID(), newmaster);
      }
      else
      {
        newmaster = mImpactSoundSets(foundIndex);
      }
    }
    else
    {
      node = node->NextSibling();
      continue;
    }


    impactTypeID = ID_NONE;
    soundID = ID_NONE;
    
    data_node = node->FirstChild("ImpactType");
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      impactTypeID = SyHashResourceID(data_node->FirstChild()->Value());
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "ImpactSoundSet '%s' has entry with missing ImpactType field", newmaster->mID.GetName());
    }

    data_node = node->FirstChild("Sound");
    SyPathname soundFileName; 
    if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
    {
      soundFileName.Base(data_node->FirstChild()->Value());
      soundID = SyHashResourceID(data_node->FirstChild()->Value());
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "ImpactSoundSet '%s' has entry with missing Sound field", newmaster->mID.GetName());
    }

    if (soundFileName.Base().Length() > 0 &&
        ID_NONE != impactTypeID &&
        ID_NONE != soundID)
    {
      soundFileName.Path("game_assets/sound");
      soundFileName.Extension("esf");

      if (mpTitan->LoadAssetSound(mpTitan->GetTitanUI()->GetESFParser(), soundFileName.Full().AsChar()) != -1)
      {
        newmaster->mSounds.Insert(impactTypeID, soundID);
      }
    }

    node = node->NextSibling();
  }
}

void                         
cDatabaseSys::LoadZones()
{
  // load up xml file for default level treasure sets...
  TiXmlDocument doc("game_assets/design/data/zones.xml");
  if (doc.LoadFile())
  {
    TiXmlNode* node = doc.FirstChild("dataroot");
    SyAssert(node);
    node = node->FirstChild("Zone");
    SyAssert(node);

    TiXmlNode* data_node = 0;
    while (node != NULL)
    {
      data_node = node->FirstChild("Name");
      if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
      {
        tGameID levelID = SyHashResourceID(data_node->FirstChild()->Value());

        data_node = node->FirstChild("DefaultTreasureSet");
        if (data_node && data_node->FirstChild() && data_node->FirstChild()->Value())
        {
          const char* treasureSetName = data_node->FirstChild()->Value();
          tGameID treasureSetID = SyHashResourceID(treasureSetName);
          cTreasureSet* treasureSet = GetTreasureSet(treasureSetID);
          if (treasureSet)
          {
            mZoneDefaultTreasureSets.Insert(levelID, treasureSetID);
          }
          else
          {
            GAME_ASSERT(ERROR_DESIGN, false, "Undefined treasure set '%s' in zones.xml!", treasureSetName);
          }
        }

        LoadMinimapInfo(levelID, node);
      }
      else
      {
        GAME_ASSERT(ERROR_DESIGN, false, "Entry in zones.xml is missing a Name attribute!");
      }

      node = node->NextSibling();
    }
  }
  else
  {
    //SyAssert(false);
  }
}

void
cDatabaseSys::LoadMinimapInfo(tGameID levelID, TiXmlNode* node)
{
  // Locate the minimap info if it already exists
  cMinimap* minimap = GetMinimap(levelID);

  // Create the minimap if it doesn't exist
  if (minimap == 0)
  {
    minimap = new cMinimap;
    mMiniMaps.Insert(levelID, minimap);
  }

  // Is there minimap info set for this map?
  TiXmlElement* element = node->FirstChildElement("Minimap");
  if (element)
  {
    // Iterate through the attributes
    TiXmlAttribute* attribute = element->FirstAttribute();
    while (attribute)
    {
      SyString name = attribute->Name();

      if (name.CompareWithoutCase("name") == 0)
      {
        minimap->mTextureName = attribute->Value();
      }

      // Get the next attribute
      attribute = attribute->Next();
    }

    // Iterate through child nodes to get map and world points
    TiXmlElement* childElement = element->FirstChildElement();
    while (childElement)
    {
      float x = 0.0f;
      float y = 0.0f;

      // Iterate through the attributes
      TiXmlAttribute* childAttribute = childElement->FirstAttribute();
      while (childAttribute)
      {
        SyString childAttributeName = childAttribute->Name();

        if (childAttributeName.CompareWithoutCase("x") == 0)
        {
          x = (float)childAttribute->DoubleValue();
        }
        else if (childAttributeName.CompareWithoutCase("y") == 0)
        {
          y = (float)childAttribute->DoubleValue();
        }

        // Get the next attribute
        childAttribute = childAttribute->Next();
      }

      // Get the element name
      SyString childName = childElement->Value();

      // Assign the x and y values to the proper coordinate
      if (childName.CompareWithoutCase("WorldPoint1") == 0)
      {
        minimap->mWorldPoint1(0) = x;
        minimap->mWorldPoint1(1) = y;
      }
      else if (childName.CompareWithoutCase("WorldPoint2") == 0)
      {
        minimap->mWorldPoint2(0) = x;
        minimap->mWorldPoint2(1) = y;
      }
      else if (childName.CompareWithoutCase("MapPoint1") == 0)
      {
        minimap->mMapPoint1(0) = x;
        minimap->mMapPoint1(1) = y;
      }
      else if (childName.CompareWithoutCase("MapPoint2") == 0)
      {
        minimap->mMapPoint2(0) = x;
        minimap->mMapPoint2(1) = y;
      }

      // Get the next child
      childElement = childElement->NextSiblingElement();
    }
  }
}

const cStatsCharacterMaster *
cDatabaseSys::GetCharacterMaster(tGameID id)
{
  int index = mCharacterMasters.Find(id);
  if (index == mCharacterMasters.End())
  {
    return NULL;
  }
  return mCharacterMasters(index);
}

const cStatsItemMaster *
cDatabaseSys::GetItemMaster(tGameID id)
{
  int index = mItemMasters.Find(id);
  if (index == mItemMasters.End())
  {
    return NULL;
  }
  return mItemMasters(index);
}

const cStatsEnhancementMaster *
cDatabaseSys::GetEnhancementMaster(tGameID id)
{
  int index = mEnhancementMasters.Find(id);
  if (index == mEnhancementMasters.End())
  {
    return NULL;
  }
  return mEnhancementMasters(index);
}

const cStatsProjectileMaster *
cDatabaseSys::GetProjectileMaster(tGameID id)
{
  int index = mProjectileMasters.Find(id);
  if (index == mProjectileMasters.End())
  {
    return NULL;
  }
  return mProjectileMasters(index);
}

const cStatsPropMaster *
cDatabaseSys::GetPropMaster(tGameID id)
{
  int index = mPropMasters.Find(id);
  if (index == mPropMasters.End())
  {
    return NULL;
  }
  return mPropMasters(index);
}


const cCharacterClass                *
cDatabaseSys::GetCharacterClass(tGameID class_name)
{
  int index = mCharacterClasses.Find(class_name);

  if (index == mCharacterClasses.End())
  {
    return NULL;
  }
  return mCharacterClasses(index);
}

const cCharacterLevel              *
cDatabaseSys::GetCharacterLevel(int level)
{
  int index = mLevelMasters.Find(level);

  if (index == mLevelMasters.End())
  {
    return NULL;
  }
  return mLevelMasters(index);
}

cTreasureSet              *
cDatabaseSys::GetTreasureSet(tGameID id) const
{
  int index = mTreasureSets.Find(id);

  if (index == mTreasureSets.End())
  {
    return NULL;
  }
  return mTreasureSets(index);
}

cMinimap                  *
cDatabaseSys::GetMinimap(tGameID id) const
{
  int index = mMiniMaps.Find(id);

  if (index == mMiniMaps.End())
  {
    return NULL;
  }
  return mMiniMaps(index);
}

cTreasureSet*            
cDatabaseSys::GetDefaultTreasureSetForLevel(tGameID levelID) const
{
  int index = mZoneDefaultTreasureSets.Find(levelID);
  return (index == mZoneDefaultTreasureSets.End()) ? 0 : GetTreasureSet(mZoneDefaultTreasureSets(index));
}

cDropList              *
cDatabaseSys::GetDropList(tGameID id)
{
  int index = mDropLists.Find(id);

  if (index == mDropLists.End())
  {
    return NULL;
  }
  return mDropLists(index);
}

const cDebrisMaster *
cDatabaseSys::GetDebrisMaster(tGameID id)
{
  int index = mDebrisMasters.Find(id);

  if (index == mDebrisMasters.End())
  {
    return NULL;
  }
  return mDebrisMasters(index);
}

const cSpellMaster *
cDatabaseSys::GetSpellMaster(tGameID id)
{
  int index = mSpellMasters.Find(id);

  if (index == mSpellMasters.End())
  {
    return NULL;
  }
  return mSpellMasters(index);
}

const cAbilityMaster *
cDatabaseSys::GetAbilityMaster(tGameID id, int rank)
{
  int i = mAbilityMasters.Find(id);

  if (i == mAbilityMasters.End())
  {
    return NULL;
  }

  for (int j=0; j!=mAbilityMasters(i).End(); j=mAbilityMasters(i).Next(j))
  {
    if (-1 == rank || rank == mAbilityMasters(i)(j)->mRank)
    {
      return mAbilityMasters(i)(j);
    }
  }

  return NULL;
}

const cStatsBlockBehavior *
cDatabaseSys::GetBlockBehavior(tGameID id)
{
  int index = mBlockBehaviors.Find(id);
  if (index == mBlockBehaviors.End())
  {
    return NULL;
  }
  return mBlockBehaviors(index);
}

const cStatsAbilitySet* 
cDatabaseSys::GetAbilitySet(tGameID id)
{
  int index = mAbilitySets.Find(id);
  if (index == mAbilitySets.End())
  {
    return NULL;
  }
  return mAbilitySets(index);
}

uint32
cDatabaseSys::GetTextID(tGameID stringID)
{
  int index = mTextIDs.Find(stringID);

  if (index == mTextIDs.End())
  {
    return (uint32)-1;
  }
  return mTextIDs(index);
}

const cImpactFXSet *
cDatabaseSys::GetImpactFXSet(tGameID id)
{
  int index = mImpactFXSets.Find(id);
  if (index == mImpactFXSets.End())
  {
    return NULL;
  }
  return mImpactFXSets(index);
}

const cImpactSoundSet *
cDatabaseSys::GetImpactSoundSet(tGameID id)
{
  int index = mImpactSoundSets.Find(id);
  if (index == mImpactSoundSets.End())
  {
    return NULL;
  }
  return mImpactSoundSets(index);
}

const cStatsModelData*
cDatabaseSys::GetModelData(tGameID id)
{
  int index = mModelData.Find(id);
  if (index == mModelData.End())
  {
    return NULL;
  }
  return mModelData(index);
}

const cStatsMerchantSet*
cDatabaseSys::GetMerchantSet(tGameID id)
{
  int index = mMerchantSets.Find(id);
  if (index == mMerchantSets.End())
  {
    return NULL;
  }
  return mMerchantSets(index);
}

// EOF
