#include "Titan.h"
#include "TitanInputHandler.h"
#include "TitanPeeringNetwork.h"
#include "TitanFXScriptDriver.h"
#include "SyAssert.h"
#include "SyDebug.h"
#include "SyESFParse.h"       
#include "SyCollSprite.h"       
#include "registry.h"
#include "cameracontroller.h"
#include "stats.h"
#include "intel.h"
#include "minimap.h"

#include "animcontroller.h"
#include "SyCamera.h"
#include "SyScene.h"
#include "SyPerformanceStats.h"
#include "rule.h"
#include "rule_global.h"
#include "rule_inventory.h"
#include "rule_condition.h"
#include "inventory.h"
#include "tuning.h"
#include "graphic.h"
#include "script_pawn.h"
#include "database.h"
#include "graphic.h"
#include "SyDamageNumMgr.h"
#include "spell.h"
#include "SyParameterValidator.h"

#include "ai.h"
#include "ai/lostable.h"
#include "ai/ailodmanager.h"
#include "debugoverlay.h"
#include "gameerror.h"
#include "script_func.h"
#include "debris.h"
#include "levelobj.h"
#include "SyRand.h"
#include "t4utf.h"
#include "TitanT4File.h"
#include "TitanT4OS.h"
#include "SyHavok.h"
#include "service.h"
#include "motioncontroller_service.h"
#include "soundmanager.h"
#include "../titanui/ticharacterdata.h"
#include "waterinit.h"

#ifdef SNTUNER
#include "libsntuner.h"
#endif

#ifdef WIN32
    #include <crtdbg.h>
#endif



/*static*/ TitanI* TitanI::Create( TitanUII* pTitanUI )
{
  return new Titan( pTitanUI );
}

Titan* Titan::smpThis = NULL;

// Initializes an instance of the Titan class.
Titan::Titan( TitanUII* pTitanUI ) :
mpTitanUI( pTitanUI ),
mpRegistry(0),
mpCameraController(0),
mpDatabaseSys(0),
mpRuleSys(0),
mpAnimControllerSys(0),
mpGlobalCharacterRules(0),
mpGlobalPropRules(0),
mpGlobalMarkerRules(0),
mpInventoryRules(0),
mpTitanPeeringNetwork(0),
mpScriptSys(0),
mpDebrisSys(0),
mpDmgNumMgr(0),
mpRand(NULL),
mpServiceScheduler(NULL),
mpSoundMgr(NULL),
mCurTime(0),
mTimeScale(1.0f),
mBloomEnabled(true),
mMultiPassEnabled(true),
mDrawMode(DrawModeNormal),
mpTitanFXScriptDriver(0),
mbUnfocused(false),
mbCheckArt(false),
mbShowDmgFlash(true),
mbTransitionLevelSet(false),
mbInTransition(false),
mbUnlockAllAbilities(false),
mbSuperSpeedMode( false ),
mpT4(0),
mpDebugOverlay(NULL),
mpGameErrorLog(NULL),
mNumLocalPlayers(1),
mDifficultySetting(cDifficultySettingsTable::MAX_DIFFICULTY_LEVELS/2),
mItemEssenceConversionRate(0.25f),
mGlobalAIAttackDelay(0.5f),
mGlobalAIAttackTimer(0.0f),
mNPCQueryID(ID_NONE),
mMerchantSetID(ID_NONE),
mbAllowCutSceneControllerMotion(true),
mSubtitleTime(0.0f),
mLogFile(NULL),
mLogList(NULL),
mLoggingFlag(false),
mLoadingLevel(false),
mbStartLevelCutscene(true),
mUseWadFile(false),
mbBlankScreen(false),
mbPvPEnabled(false),
mbNetworkGame(false),
mbReloadTest(false)
{  
  // don't do any file I/O in this constructor
  // file system may not have been mounted yet 

  //_CrtSetBreakAlloc(129);  // break for debugging leaked memory
  RegPropClasses_Registry();
  RegPropClasses_ScriptSys();

  mpRegistry = new cGameObjectRegistry();
  mpCameraController = new cCameraController( this);
  mpDatabaseSys = new cDatabaseSys(this);
  mpRuleSys = new cRuleSys();
  mpAnimControllerSys = new cAnimControllerSys();
  mpGlobalCharacterRules = new cRuleContainer();
  mpGlobalPropRules = new cRuleContainer();
  mpGlobalMarkerRules = new cRuleContainer();
  mpInventoryRules = new cInventoryRuleContainer();
  mpInventoryRules->SetTitan(this);
  mpTitanPeeringNetwork = new TitanPeeringNetwork(this);
  mpTitanFXScriptDriver = new TitanFXScriptDriver(this);
  mpScriptSys = new cScriptSys(this);
  mpDebrisSys = new cDebrisSys(this);
  mpDmgNumMgr = new SyDamageNumMgr(this);
  mpRand = new SyRand();
  mpServiceScheduler = new cServiceScheduler();
  mpSoundMgr = new cSoundManager();

  for (int ii=0; ii<MAX_NUM_LOCAL_PLAYERS; ++ii)
  {
    mpInputHandler[ii] = SyNew TitanInputHandler(mpTitanUI);
    mPlayerCharacterMasterNames[ii] = "Brute";
  }

  m_iLanguage = -1;
  mpT4OS = 0;
  mpT4File= 0;
  mpT4 = 0;

#ifdef _DRAWDEBUGOVERLAY
  mpDebugOverlay = new cDebugOverlay();
  mpDebugOverlay->Enable(true);
#else
  mpDebugOverlay = NULL;
#endif

#ifdef _GAME_ERROR_REPORTING
  mpGameErrorLog = new cGameErrorLog();
#else
  mpGameErrorLog = NULL;
#endif

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

// Destroys an instance of the Titan class.
Titan::~Titan(void)
{
  delete mpSoundMgr;
  delete mpDebrisSys;
  delete mpTitanFXScriptDriver;
  delete mpInventoryRules;
  delete mpRegistry;
  delete mpTitanPeeringNetwork;
  delete mpCameraController;
  delete mpDatabaseSys;
  delete mpRuleSys;
  delete mpAnimControllerSys;
  delete mpGlobalCharacterRules;
  delete mpGlobalPropRules;
  delete mpGlobalMarkerRules;
  delete mpScriptSys;
  delete mpDmgNumMgr;
  delete mpRand;
  delete mpServiceScheduler;
  delete mpT4;
  delete mpT4File;
  delete mpT4OS;

  for (int ii=0;ii<MAX_NUM_LOCAL_PLAYERS;++ii)
  {
    delete mpInputHandler[ii];
    mpInputHandler[ii] = NULL;
  }

#ifdef _DRAWDEBUGOVERLAY
  delete mpDebugOverlay;
#endif

#ifdef _GAME_ERROR_REPORTING
  delete mpGameErrorLog;
#endif

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

#define OPTION_ART_CHECK          "art_check"
#define OPTION_BLOOM              "bloom"
#define OPTION_PAUSED             "paused"
#define OPTION_HAVOKSTABILIZE     "havok_stabilize"
#define OPTION_HAVOKNORAGDOLL     "havok_no_ragdoll"
#define OPTION_NO_WATER_RAY_CASTS "no_water_ray_casts"
#define OPTION_NO_PLUNK           "no_plunk"
#define OPTION_LOG                "log"
#define OPTION_MULTIPASS          "multipass"
#define OPTION_PAWN_DEBUG         "pawn_debug"
#define OPTION_PLAYER_MODEL       "player_model"
#define OPTION_CHARACTER          "character"
#define OPTION_TWO_PLAYER         "two_player"
#define OPTION_DIFFICULTY         "difficulty"
#define OPTION_ERRORS             "errors"
#define OPTION_NO_WAD             "no_wad"
#define OPTION_RELOAD_TEST        "reload_test"

void Titan::InitParameterValidator( SyParameterValidator* parameterValidator ) const
{
  parameterValidator->AddSwitch(        OPTION_ART_CHECK,           "",   "Check source art for missing animations" );
  parameterValidator->AddBooleanOption( OPTION_BLOOM,               "b",  "enable/disable bloom post processing effect" );
  parameterValidator->AddSwitch(        OPTION_PAUSED,              "",   "start game paused" );
  parameterValidator->AddSwitch(        OPTION_HAVOKSTABILIZE,      "",   "stabilize havok for level and then exit" );
  parameterValidator->AddSwitch(        OPTION_HAVOKNORAGDOLL,      "",   "disable ragdoll" );
  parameterValidator->AddSwitch(        OPTION_NO_WATER_RAY_CASTS,  "",   "disable water ray casts" );
  parameterValidator->AddSwitch(        OPTION_NO_PLUNK,            "",   "disable water plunk" );
  parameterValidator->AddSwitch(        OPTION_LOG,                 "",   "log combat results to log.txt" );
  parameterValidator->AddBooleanOption( OPTION_MULTIPASS,           "m",  "enable/disable multipass rendering" );
  parameterValidator->AddSwitch(        OPTION_PAWN_DEBUG,          "",   "start game with source level script debugging" );
  parameterValidator->AddStringOption(  OPTION_PLAYER_MODEL,        "",   "name of .esf to use for the player" );
  parameterValidator->AddStringOption(  OPTION_CHARACTER,           "",   "name of the character master from the database to use for the player" );
  parameterValidator->AddSwitch(        OPTION_TWO_PLAYER,          "",   "starts the game in two player mode" );
  parameterValidator->AddIntegerOption( OPTION_DIFFICULTY,          "",   "difficulty setting (1-10)" );
  parameterValidator->AddStringOption(  OPTION_ERRORS,              "",   "channel of game errors to display as asserts" );
  parameterValidator->AddSwitch(        OPTION_NO_WAD,              "",   "disable use of wad file" );
  parameterValidator->AddSwitch(        OPTION_RELOAD_TEST,         "",   "Reloads level 100 times to test for memory fragmentation" );
}


void Titan::InitT4WithLanguage(int32 iLanguage)
{
  delete mpT4;

  m_iLanguage = iLanguage;

  if(mpT4OS == NULL)
    mpT4OS = SyNew TitanT4OS();
  T4utf::useOs(mpT4OS);
  
  //"game_assets/design/locale/config.dat","rb"
  if(mpT4File == NULL)
    mpT4File = SyNew TitanT4File();
  T4utf::useFile(mpT4File);
  
  //mpT4 = NULL;
  T4utf::setDataPath("game_assets/design/locale/");
  mpT4 = SyNew T4utf(1000, 1000, (T4::Locale)iLanguage); //T4::EN_US);
}

// Allocates resources required by the Titan object.
bool Titan::Init( uint64 time, const SyParameterValidator& parameterValidator )
{
  // T4 cannot be created in the constructor because it does file I/O and
  // file system may not have been mounted yet 
  InitT4WithLanguage(T4::EN_US);

  bool bloom = mBloomEnabled;
  parameterValidator.Found(OPTION_BLOOM,&bloom);
  SetBloom( bloom );

  if ( parameterValidator.Found(OPTION_LOG) )
  {
    SetLogMode(true);
  }

  if ( parameterValidator.Found(OPTION_PAUSED) )
  {
    mpTitanUI->SetPaused( 1 );
  }

  if ( parameterValidator.Found(OPTION_HAVOKNORAGDOLL) )
  {
    mpTitanUI->SetHavokRagdoll( 0 );
  }
  
  if ( parameterValidator.Found(OPTION_HAVOKSTABILIZE) )
  {
    mpTitanUI->SetHavokStabilizeAndExit( 1 );
  }

  bool multiPass = mMultiPassEnabled;
  parameterValidator.Found(OPTION_MULTIPASS,&multiPass);
  SetMultiPass(multiPass);

  SetNoWaterRayCasts( parameterValidator.Found(OPTION_NO_WATER_RAY_CASTS) );

  SetNoPlunk( parameterValidator.Found(OPTION_NO_PLUNK) );

  if ( parameterValidator.Found(OPTION_PAWN_DEBUG) )
  {
    ScriptEnableDebugging();
  }

  if ( parameterValidator.Found(OPTION_TWO_PLAYER) )
  {
    SetNumLocalPlayers(2);
  }

  int difficultySetting = 1;
  if ( parameterValidator.Found(OPTION_DIFFICULTY, &difficultySetting) )
  {
    SetDifficultySetting(difficultySetting);
  }

  parameterValidator.Found(OPTION_PLAYER_MODEL, &mPlayerModelOverride);

  SyString playerCharacterMasterOverride;
  if (parameterValidator.Found(OPTION_CHARACTER, &playerCharacterMasterOverride))
  {
    mPlayerCharacterMasterNames[0] = playerCharacterMasterOverride;
  }

#ifdef _GAME_ERROR_REPORTING
  SyString errorChannelName;

  if (parameterValidator.Found(OPTION_ERRORS, &errorChannelName))
  {
    if (SyStr::Stricmp(errorChannelName.AsChar(), "DESIGN") == 0)
    {
      GAME_ERROR_PROMOTE_TO_ASSERT(ERROR_DESIGN);
//      GAME_ERROR_DEMOTE_TO_WARNING(ERROR_ART);
    }
    else if (SyStr::Stricmp(errorChannelName.AsChar(), "ART") == 0)
    {
      GAME_ERROR_PROMOTE_TO_ASSERT(ERROR_ART);
//      GAME_ERROR_DEMOTE_TO_WARNING(ERROR_DESIGN);
    }
    else if (SyStr::Stricmp(errorChannelName.AsChar(), "ALL") == 0)
    {
      GAME_ERROR_PROMOTE_TO_ASSERT(ERROR_DESIGN);
      GAME_ERROR_PROMOTE_TO_ASSERT(ERROR_ART);
      GAME_ERROR_PROMOTE_TO_ASSERT(ERROR_CODE);
    }
    else if (SyStr::Stricmp(errorChannelName.AsChar(), "NONE") == 0)
    {
      GAME_ERROR_DEMOTE_TO_WARNING(ERROR_DESIGN);
      GAME_ERROR_DEMOTE_TO_WARNING(ERROR_ART);
      GAME_ERROR_DEMOTE_TO_WARNING(ERROR_CODE);
    }
    else if (SyStr::Stricmp(errorChannelName.AsChar(), "DEFAULT") == 0)
    {
      // keep standard assert levels
    }
  }
  else 
  {
    GAME_ERROR_DEMOTE_TO_WARNING(ERROR_DESIGN);
    GAME_ERROR_DEMOTE_TO_WARNING(ERROR_ART);
    GAME_ERROR_DEMOTE_TO_WARNING(ERROR_CODE);
  }
#endif


  if ( parameterValidator.Found(OPTION_NO_WAD) )
  {
    SetUseWadFile (false);
  }
  else
  {
    SetUseWadFile (true);
  }


  // time management
  mbPaused = false;
  SyScene *scene = GetScene();
  mGameTime = 0;
  mFrameTime = 0;
  mRealFrameTime = 0;
  scene->SetTime(mGameTime);
  mCurTime = time;
  mpRand->Seed((uint32)time);

#ifdef _GAME_ERROR_REPORTING
  cGameErrorLog::Get()->Init(mpTitanUI->GetInputConsole(), scene->GetRasterDev(), scene);
  cGameErrorLog::Get()->SetChannelBackgroundColor(ERROR_DESIGN, SyColor32F(0.0f, 0.0f, 0.7f, 1.0f));
  cGameErrorLog::Get()->SetChannelBackgroundColor(ERROR_ART, SyColor32F(0.0f, 0.7f, 0.0f, 1.0f));
  cGameErrorLog::Get()->SetChannelBackgroundColor(ERROR_CODE, SyColor32F(0.0f, 0.0f, 0.0f, 1.0f));
#endif

  gTuningSys.Init();

  mpDatabaseSys->Init();
  mpRuleSys->Init();
  mpRegistry->Init(this);
  mpAnimControllerSys->Init();
  mpGlobalCharacterRules->SetTitan(this);
  mpGlobalPropRules->SetTitan(this);
  mpGlobalMarkerRules->SetTitan(this);
  AddGlobalCharacterRules(mpGlobalCharacterRules);
  AddGlobalPropRules(mpGlobalPropRules);
  AddGlobalMarkerRules(mpGlobalMarkerRules);
  mpCameraController->OnInit();
  cAiInterface::RegisterTuningVariables();
  scriptFuncs_RegisterTuningVars();
  Stats_RegisterTuningVariables();
  GlobalRules_RegisterTuningVariables();
  Debris_RegisterTuningVariables();
  Physics_RegisterTuningVariables();
  Script_RegisterTuningVariables();
  Intel_RegisterTuningVariables();
  WaterInit_LoadFluidProfilesXML();
  gTuningSys.AddFloat(&mItemEssenceConversionRate, "ItemEssenceConversionRate");
  gTuningSys.AddFloat(&mGlobalAIAttackDelay, "AI_MeleeCombat_Delay");
  gTuningSys.LoadValues("tuning");  // load tuning variables

  mpServiceScheduler->Init(this);
  mpServiceScheduler->AddService(new cMotionController_Service);

  mpSoundMgr->Init(this);

  if (parameterValidator.Found(OPTION_RELOAD_TEST))
  {
    mbReloadTest = true;
  }

  return true;
}


// Releases resources acquired by the titan. object.
void Titan::Close()
{ 
  mpDmgNumMgr->Close();
}

void Titan::PauseGame()
{
  mbPaused = true;

  for (int i=0; i<MAX_NUM_LOCAL_PLAYERS; ++i)
  {
    if (mpInputHandler[i] && mpInputHandler[i]->GetGameObject())
    {
      mpInputHandler[i]->OnPauseGame();
    }
  }
}

void Titan::UnpauseGame()
{
  mbPaused = false;

  for (int i=0; i<MAX_NUM_LOCAL_PLAYERS; ++i)
  {
    if (mpInputHandler[i] && mpInputHandler[i]->GetGameObject())
    {
      mpInputHandler[i]->OnUnPauseGame();
    }
  }
}

bool Titan::IsPaused()
{
  return mbPaused;
}

void Titan::IncreaseTimeScale()
{
  mTimeScale *= 2.0f;
  if (mTimeScale > 1.0f)
  {
    mTimeScale = 1.0f;
  }
}

void Titan::DecreaseTimeScale()
{
  mTimeScale *= 0.5f;
}

void Titan::SetTimeScale(float scale)
{
  if (scale >= 0.0f)
  {
    mTimeScale = scale;
  }
}

float Titan::GetTimeScale()
{
  return mTimeScale;
}

int Titan::GetDifficultySetting()
{
  SyAssert(mDifficultySetting >= 1 && mDifficultySetting <= cDifficultySettingsTable::MAX_DIFFICULTY_LEVELS);
  return mDifficultySetting;
}

void Titan::SetDifficultySetting(int ds)
{
  if (ds < 1)
  {
    ds = 1;
  }
  
  if (ds > cDifficultySettingsTable::MAX_DIFFICULTY_LEVELS)
  {
    ds = cDifficultySettingsTable::MAX_DIFFICULTY_LEVELS;
  }
  
  mDifficultySetting = ds;
}

// for use with havok stabilization 
void Titan::UpdateJustProps( uint64 time )
{
  if (mbUnfocused) // happens when the game is stopped due to an open modal window
  {
    mCurTime = time;
    return;
  }

  uint64 delta_time_msec = time - mCurTime;
  if (mbPaused)
  {
    delta_time_msec = 0;
  }

  // clamp delta time to facilitate stepping through code in debugger
  if (delta_time_msec > 200) delta_time_msec = 200;
  delta_time_msec = (int)((float)delta_time_msec * mTimeScale);
  float delta_time = ((float)delta_time_msec)/1000.0f;

  mpRegistry->UpdateJustProps( delta_time );

  mCurTime = time;
}

// Updates the game state to the specified time.
void Titan::AdvanceTime( uint64 time )
{
  if (mbUnfocused) // sgc happens when the game is stopped due to an open modal window
  {
    mCurTime = time;
    return;
  }

  mRealFrameTime = mFrameTime = time - mCurTime;
  if (mbPaused)
  {
    mFrameTime = 0;
  }

  // clamp delta time to facilitate stepping through code in debugger
  if (mFrameTime > 200)
  {
    mFrameTime = 200;
  }

  mFrameTime = (int)((float)mFrameTime * mTimeScale);
  mGameTime += mFrameTime;

  SyScene *scene = GetScene();
  scene->SetTime(mGameTime);

  mCurTime = time;
}

void Titan::Update( uint64 time )
{
  if (mbUnfocused) // sgc happens when the game is stopped due to an open modal window
  {
    return;
  }

  if (mbReloadTest)
  {
    for (int i=0; i<100; ++i)
    {
      Restart();
    }

    mbReloadTest = false;
  }

  float delta_time = ((float)mFrameTime)/1000.0f;
  float delta_time_real = ((float)mRealFrameTime)/1000.0f;

  if (mGlobalAIAttackTimer > delta_time)
  {
    mGlobalAIAttackTimer -= delta_time;
  }
  else
  {
    mGlobalAIAttackTimer = 0.0f;
  }

  if (mSubtitleTime > 0.0f)
  {
    mSubtitleTime -= delta_time;
  }

#ifdef SNTUNER
  snStartMarker(20, "mpScriptSys->Update(delta_time)");
#endif
  mpScriptSys->Update(delta_time);
#ifdef SNTUNER
  snStopMarker(20);
#endif
  mpRegistry->Update(delta_time);

#ifdef SNTUNER
  snStartMarker(22, "mpCameraController->Update(delta_time)");
#endif
  mpCameraController->Update(delta_time_real);
#ifdef SNTUNER
  snStopMarker(22);
#endif

#ifdef SNTUNER
  snStartMarker(23, "cLOSTable::Update(...)");
#endif
  cLOSTable::Update(GetRegistry(), GetScene());  // update our max line of sights per frame
#ifdef SNTUNER
  snStopMarker(23);
#endif

#ifdef SNTUNER
  snStartMarker(24, "cAILODManager::Update(...)");
#endif
  cAILODManager::Update(this, delta_time);       // timeslice ai update based on dist to cam
#ifdef SNTUNER
  snStopMarker(24);
#endif

#ifdef SNTUNER
  snStartMarker(25, "mpDebrisSys->Update(delta_time)");
#endif
  mpDebrisSys->Update(delta_time);
#ifdef SNTUNER
  snStopMarker(25);
#endif

#ifdef SNTUNER
  snStartMarker(26, "mpServiceScheduler->Perform(delta_time)");
#endif
  mpServiceScheduler->Perform(delta_time);
#ifdef SNTUNER
  snStopMarker(26);
#endif

  SyScene *scene = GetScene();
#ifdef SNTUNER
  snStartMarker(27, "scene->Update()");
#endif
  scene->Update();
#ifdef SNTUNER
  snStopMarker(27);
#endif

  mpSoundMgr->Update(delta_time);

  mbStartLevelCutscene = false;

  mpTitanPeeringNetwork->GiveTime();     // give processing time to the network layer

  TransitionLevel(); // if necessary...
  if (mbInTransition) // we've transitioned levels, we need to wait until we're ready to go.
  {
    // wait for a level object to come in over the pipe
    while (mpRegistry->GetLevelObject() == NULL || 
           strcmp(mpRegistry->GetLevelObject()->GetLevelName(), mCurFilename.AsChar()) != 0 ||
           mpRegistry->Fetch(mMarkername.AsChar())==NULL)
    {
      //todo: timeout?
      GetPeeringNetwork()->GiveTime();
    }

    TransitionComplete();
    mbInTransition=false;
  }

#ifdef SNTUNER
  snStartMarker(28, "mpInputHandler[i]->Update(deltatime)");
#endif
  for (int i=0; i<MAX_NUM_LOCAL_PLAYERS; ++i)
  {
    if (mpInputHandler[i] && mpInputHandler[i]->GetGameObject())
    {
      mpInputHandler[i]->Update(delta_time);
    }
  }
#ifdef SNTUNER
  snStopMarker(28);
#endif
}

void Titan::RenderHUD()
{
  if (!mpTitanUI) return;
  SyScene *scene = mpTitanUI->GetScene();
  if (!scene) return;

  scene->RenderHUD();
}

// Renders the scene.
// Scene::BeginScene must be called before this method is invoked.
// Scene::EndScene must be called after this method is invoked.
void Titan::Render()
{
  if (mbUnfocused) // sgc happens when the game is stopped due to an open modal window
  {
    return;
  }

  if (mbBlankScreen)
  {
    return;
  }

#ifdef SNTUNER
  snStartMarker(30, "mpRegistry->Prerender()");
#endif
  SyAssert( mpRegistry );
  mpRegistry->Prerender();
#ifdef SNTUNER
  snStopMarker(30);
#endif

  SyAssert( mpTitanUI );
  SyScene *scene = mpTitanUI->GetScene();

  SyAssert( mpCameraController );
  SyCamera *camera = mpCameraController->GetCamera();

  SyAssert( scene );
  SyAssert( camera );
  if ( mBloomEnabled )
  {
    scene->RenderWithPostEffect( *camera, mpTitanUI->GetBloomEffectHandle() );
  }
  else
  {
    scene->Render( *camera );
  }

  mpDmgNumMgr->Render(*camera);

#ifdef _DRAWDEBUGOVERLAY
  cDebugOverlay::Get()->Render(scene->GetRasterDev(), camera);
#endif


  if (GetNumLocalPlayers() > 1)
  {
    cGameObject *player = mpRegistry->BeginType(cGameObject::OBJ_PLAYER);
    while (player != NULL)
    {
      cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(player->GetGraphic());

      if (graphic != NULL) graphic->DrawRing(*camera);

      player = mpRegistry->NextType(player);
    }
  }
}

TitanInputHandlerI* Titan::GetInputHandler( int controllerId )
{
  SyAssert( controllerId < MAX_NUM_LOCAL_PLAYERS ); // only support one controller right now
  SyAssert( controllerId >= 0 ); // only support one controller right now

  return mpInputHandler[controllerId];
}

int 
Titan::Random(int min,int max)
{
  SyAssert(mpRand != NULL);

  if (min==max) return min;

  return mpRand->Rand(min, max+1); // Possibly return 2 on a call of random(1,2)
}

float 
Titan::RandomFloat()
{
  SyAssert(mpRand != NULL);

  return mpRand->fRand();
}

/**********************************************************
 * LoadLevel - Can be done with or without the use of a
 * Wad file. The LoadAsset* () functions need to know if
 * we're using a wad.  Also, this function is used by the
 * WadTool to help generate the list of .esf files that
 * will get included in the wad file.
 **********************************************************/
void Titan::LoadLevel(const char *filename)
{
  /*
   * Let the LoadAsset* funcs know to skip, if we're using wad.
   */
  mLoadingLevel = true;
  mbStartLevelCutscene = true;

  mCurFilename = filename;

  SyPathname path(filename);

  if (strcmp(path.Extension().AsChar(),"xml")==0)
  {
    mpRegistry->LoadLevelXML(filename);
  }
  else
  {
    mpRegistry->LoadLevelBinary(filename);
  }

  /*
   * Initialize minimap and load its resources
   */
  bool      deleteMinimap = false;
  cMinimap* minimap = mpDatabaseSys->GetMinimap(mpRegistry->GetLevelObject()->GetLevelNameHashID());
  if (minimap == 0)
  {
    // We shouldn't ever get here in the final game since a default minimap info
    // entry is created for every map listed in zones.xml.  This is here to prevent
    // crashes and unnecessary assertions when running test levels.
    minimap       = new cMinimap;
    deleteMinimap = true;
  }
  mpTitanUI->SetMinimap(minimap->mTextureName,
                        minimap->mWorldPoint1,
                        minimap->mWorldPoint2,
                        minimap->mMapPoint1,
                        minimap->mMapPoint2,
                        GetScene()->GetRasterDev(),
                        GetScene()->GetDictionary());
  if (deleteMinimap)
  {
    // Delete temp minimap info entry if one was created.
    // Once again, we shouldn't ever really get here in the final game.
    delete minimap;
    minimap = 0;
  }

  LoadGlobalResources();

  /*
   * Ok, back to normal asset reading.
   */
  mLoadingLevel = false;

  /*
   * Only WadTool does Logging. CreatePlayers was causing
   * some kind of WadTool crash, probably related to the
   * fact that its command-line, and not a full game app.
   */
  if (IsLogging() == NULL)
  {
    CreatePlayers();
  }

  EnterGame();
}

//-----------------------------------------------------------------------
const char* Titan::GetLevelName() const
{
	if(( mpRegistry != NULL ) && ( mpRegistry->GetLevelObject() != NULL ))
	{
		return mpRegistry->GetLevelObject()->GetLevelName();
	}
	return NULL;
}

void Titan::NudgeProps()
{
  mpRegistry->NudgeProps();
}

void Titan::SaveLevel(const char8* filePrefix)
{
  if (filePrefix == NULL)
  {
    mpRegistry->SaveLevelBinary(mCurFilename.AsChar());
  }
  else
  {
    char strBuf [256];
    sprintf(strBuf, "%s%s", filePrefix, mCurFilename.AsChar());
    mpRegistry->SaveLevelBinary(strBuf);
  }
}

void Titan::SetTransitionLevel(const char *filename,const char *markerName)
{
  mCurFilename = filename;
  mMarkername = markerName;

  mbTransitionLevelSet = true;
  if (!mpRegistry->GetLevelObject()->IsRemote())
  {
    // send packet to everyone to load up new level
    cNetTransitionLevelPacket packet;
    packet.Init(mCurFilename.AsChar(), mMarkername.AsChar());

    char buf[8192];
    int len = packet.PackBuffer(buf, sizeof(buf));
    GetPeeringNetwork()->ObjectBroadcast(cLevelObject::LEVEL_OBJECT_GAME_ID, buf, len);
  };
}

void Titan::SetFluidLevel( const char * FluidName, float value, float fTimeSeconds )
{
  GetScene()->GetWaterSystem()->SetFluidLevel( FluidName, value, fTimeSeconds );
}


void Titan::TransitionLevel()
{
  if (!mbTransitionLevelSet)
  {
    return;
  }
  mbTransitionLevelSet = false;

  mpTitanPeeringNetwork->Flush();

  mpDmgNumMgr->Close();
  mpDebrisSys->Clear(); 
  mpRegistry->TransitionLevel(mCurFilename.AsChar());
  mBloomEffectHandle=-1; // registry clears scene, have to recreate effect handle.
  LoadGlobalResources();
}

void Titan::TransitionComplete()
{
  cGameObject *player=NULL;
  cGameObject *playerMarker=NULL;
  SyString name;

  mbStartLevelCutscene = true;

  for (int ii=0;ii<mNumLocalPlayers;++ii)
  {
    GetLocalPlayerName(ii,&name);
    player = mpRegistry->Fetch(name.AsChar());

    SyAssertf(player != NULL,"Unable to find player after level transition?");
    playerMarker = mpRegistry->Fetch(mMarkername.AsChar());
    SyAssertf(playerMarker != NULL,"Unable to Find Player Spawn Marker");
    
    if (playerMarker == NULL)
    {
      playerMarker = mpRegistry->Fetch("playerSpawn");
    }

    if (playerMarker != NULL)
    {
      player->SetSafeLocation(playerMarker->GetLocation()); // find ground pos
      player->SetHPR(playerMarker->GetHPR());
      SyAssert(prop_cast<cIntelEntity*>(player->GetIntel())!=NULL);
      static_cast<cIntelEntity*>(player->GetIntel())->SetStartLocation(playerMarker->GetLocation()); // set respawn point
      static_cast<cIntelEntity*>(player->GetIntel())->SetStartHeading(playerMarker->GetHeading()); // set respawn point
    }

    ((cIntelPlayer*)player->GetIntel())->SetControllerID(ii);
  }
  // set up camera
  mpCameraController->SetHeading(mpInputHandler[0]->GetGameObject()->GetHeading());

  // update armor and items because assets were reloaded
  cGameObject* pPlayer = mpRegistry->BeginType(cGameObject::OBJ_PLAYER);
  while (pPlayer)
  {
    static_cast<cGraphicCharacter*>(pPlayer->GetGraphic())->SetDirty();
    pPlayer = mpRegistry->NextType(pPlayer);
  }
}

void
Titan::LoadGlobalResources()
{
  mpDmgNumMgr->Init();
}

void Titan::ScriptEnableDebugging()
{
  mpScriptSys->EnableDebugging();
}

static int bShowAnimsForPlayers = 0;
static void ToggleShowPlayerAnims()
{
  bShowAnimsForPlayers = !bShowAnimsForPlayers;
}

static int bShowWaterInfo = 0;
static void ToggleShowWaterInfo()
{
  bShowWaterInfo = !bShowWaterInfo;
}

int Titan::GetShowWaterInfo()
{
  return bShowWaterInfo;
}



int Titan::GetShowPlayerAnims()
{
  return bShowAnimsForPlayers;
}

void Titan::CheatKey(KeyCode key)
{
  switch (key)
  {
  case KEYCODE_R: 
    {
      static bool sCollEnabled = false;
      sCollEnabled = !sCollEnabled;
      SyCollSprite::SetClassVisible( sCollEnabled );
    }
    break;
  
  case KEYCODE_U: 
    mbUnlockAllAbilities = !mbUnlockAllAbilities;
    for (int ii=0; ii<mNumLocalPlayers; ++ii)
    {
      if (mpInputHandler[ii] && mpInputHandler[ii]->GetGameObject())
      {
        mpInputHandler[ii]->ToggleAbilityUnlock();
      }
    }
    break;


  case KEYCODE_KEYPAD_ADD:
    IncreaseTimeScale();
    break;

  case KEYCODE_KEYPAD_SUBTRACT:
    DecreaseTimeScale();
    break;

  case KEYCODE_A:
    ToggleShowPlayerAnims();
    break;

  case KEYCODE_F:
    ToggleShowWaterInfo();
    break;

  case KEYCODE_F8: 
    GetDatabaseSys()->LoadSpells();
    GetDatabaseSys()->LoadBlockBehaviors();
    GetDatabaseSys()->LoadAbilitySets();
    GetAnimControllerSys()->Reload();
    break;

  case KEYCODE_F9:
    gTuningSys.LoadValues("tuning");
    break;

  case KEYCODE_F11:
    Restart();
    break;

  case KEYCODE_G:
    {
      SyScene *scene = GetScene();
      if (scene)
      {
        scene->TogglePerformanceHUDGrid();
      }
    }
    break;
  case KEYCODE_F10:
    {
      int value = (SyWaterSystem::GetPlunkEnabled() | (SyWaterSystem::GetRayCastsEnabled() << 1)) + 1;
      if (value & 1)
      {
        SyWaterSystem::EnablePlunk();
        printf( "SyWaterSystem:: water interactivity ON\n" );
      }
      else 
      {
        SyWaterSystem::DisablePlunk();
        printf( "SyWaterSystem:: water interactivity OFF\n" );
      }
      if (value & 2)
      {
        SyWaterSystem::EnableRayCasts();
        printf( "SyWaterSystem:: ray casts ON\n" );
      }
      else 
      {
        SyWaterSystem::DisableRayCasts();
        printf( "SyWaterSystem:: ray casts OFF\n" );
      }
      fflush(stdout);
    }
    break;
  case KEYCODE_F3:
    {
      SyScene *scene = GetScene();
      if (scene)
      {
        scene->TogglePerformanceHUDGraph();
      }
    }
    break;

  case KEYCODE_P:
    mpScriptSys->Recompile();
    break;

  case KEYCODE_I:
    {
      static bool sbDebuggingEnabled = false;
      if (sbDebuggingEnabled)
      {
        mpScriptSys->DisableDebugging();
      }
      else
      {
        mpScriptSys->EnableDebugging();
      }

      sbDebuggingEnabled = !sbDebuggingEnabled;
    }
    break;

  case KEYCODE_N:
    {
      static bool sbAIDisabled = false;
      sbAIDisabled = !sbAIDisabled;
      cAILODManager::DisableAll(sbAIDisabled);
    }
    break;

#ifdef _DRAWDEBUGOVERLAY
  case KEYCODE_L:
    if (cDebugOverlay::Get()->IsChannelEnabled("Anim"))
    {
      cDebugOverlay::Get()->EnableChannel("Anim", false);
    }
    else
    {
      cDebugOverlay::Get()->EnableChannel("Anim", true);
    }
    break;

  case KEYCODE_K:
    if (cDebugOverlay::Get()->IsChannelEnabled("NamedNode"))
    {
      cDebugOverlay::Get()->EnableChannel("NamedNode", false);
    }
    else
    {
      cDebugOverlay::Get()->EnableChannel("NamedNode", true);
    }
    break;

  case KEYCODE_M:
    if (cDebugOverlay::Get()->IsChannelEnabled("AI"))
    {
      cDebugOverlay::Get()->EnableChannel("AI", false);
    }
    else
    {
      cDebugOverlay::Get()->SelectAllObjects();
      cDebugOverlay::Get()->EnableChannel("AI", true);
    }
    break;

  case KEYCODE_SEMICOLON:
    if (cDebugOverlay::Get()->IsChannelEnabled("Physics"))
    {
      cDebugOverlay::Get()->EnableChannel("Physics", false);
    }
    else
    {
      cDebugOverlay::Get()->SelectAllObjects();
      cDebugOverlay::Get()->EnableChannel("Physics", true);
    }
    break;

  case KEYCODE_T:
    WaterInit_ReloadShaders( *(GetScene()->GetRasterDev()) );
    WaterInit_LoadFluidProfilesXML();
    break;

  case KEYCODE_COMMA:
    cDebugOverlay::Get()->SelectPrevObject(GetRegistry(), GetCameraController()->GetCamera());
    break;

  case KEYCODE_PERIOD:
    cDebugOverlay::Get()->SelectNextObject(GetRegistry(), GetCameraController()->GetCamera());
    break;

  case KEYCODE_B: 
  case KEYCODE_F7: 
    if (cDebugOverlay::Get()->IsChannelEnabled("AreaEffect"))
    {
      cDebugOverlay::Get()->EnableChannel("AreaEffect", false);
    }
    else
    {
      cDebugOverlay::Get()->EnableChannel("AreaEffect", true);
    }
    break;
#endif

  case KEYCODE_F6:
    SaveGameLoadLast();
    break;
  case KEYCODE_F5:
    SaveGameOverwriteLast();
    break;

  case KEYCODE_F12: 
#ifdef HAVOK_ENABLED
    {
      cGameObject *player = mpRegistry->BeginType(cGameObject::OBJ_PLAYER);
      while (player != NULL)
      {
        cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(player->GetGraphic());
        if (graphic != NULL) 
        {
          SySprite* pSprite = GetScene()->GetActorSpritePtr(graphic->GetActorHandle());
          if (pSprite && pSprite->GetHavokRagdollInstance())
          {
            SyHavok::toggleKeyframeMode(pSprite->GetHavokRagdollInstance());
          }
        }
        player = mpRegistry->NextType(player);
      }
    }
#endif
    break;

  case KEYCODE_UP:
    static_cast<cStatsCharacter*>(mpInputHandler[0]->GetGameObject()->GetStats())->AwardExperience(GetPlayerNextLevelExp(0)-GetPlayerCurExp(0)+1);
    break;

  case KEYCODE_DOWN:
    static_cast<cStatsCharacter*>(mpInputHandler[0]->GetGameObject()->GetStats())->SetLevel(GetPlayerLevel(0)-1);
    break;

  case KEYCODE_LEFT_BRACKET:
    SetDifficultySetting(mDifficultySetting-1);
    DEBUGOVERLAY_ENABLECHANNEL("TestUI", true);
    DEBUGOVERLAY_DRAWSCREENTEXT(0, "TestUI", 300, 200, ("Difficulty Setting: %d", mDifficultySetting), cDebugOverlay::WHITE);
    break;

  case KEYCODE_RIGHT_BRACKET:
    SetDifficultySetting(mDifficultySetting+1);
    DEBUGOVERLAY_ENABLECHANNEL("TestUI", true);
    DEBUGOVERLAY_DRAWSCREENTEXT(0, "TestUI", 300, 200, ("Difficulty Setting: %d", mDifficultySetting), cDebugOverlay::WHITE);
    break;

  case KEYCODE_C:
    if (mpCameraController->GetScriptSetting()==CAMERA_FREE)
    {
      mpCameraController->DisableFreeCam(); 
    }
    else
    {
      mpCameraController->EnableFreeCam(); 
    }
    break;

  case KEYCODE_V:
    SetSuperSpeedMode( !GetSuperSpeedMode() );
    break;

  default:
    break;
  }
}

void Titan::CreatePlayers() // after new game
{
  for (int ii=0;ii<mNumLocalPlayers;++ii)
  {
    tGameObjectID  playerID = mpRegistry->Create(cGameObject::OBJ_PLAYER);
    cGameObject *player = mpRegistry->Fetch(playerID);
    SyString name;
    GetLocalPlayerName(ii,&name);
    player->SetName(name.AsChar());
    cGameObject *playerMarker = mpRegistry->Fetch("playerSpawn");
    GAME_ASSERT(ERROR_CODE, playerMarker != NULL, "Unable to Find Player Spawn Marker");
    GAME_ASSERT(ERROR_CODE, mPlayerCharacterMasterNames[ii].Length() > 0, "Empty player character master name");
    player->GetStats()->SetMaster(mPlayerCharacterMasterNames[ii].AsChar());

    ((cIntelPlayer*)player->GetIntel())->SetControllerID(ii);
    SyAssert(mpInputHandler[ii]!=NULL);
    mpInputHandler[ii]->SetGameObject(player, ii);

    mpRegistry->InitObject(player->GetID(),true);

    player->SetSafeLocation(playerMarker->GetLocation());
    player->SetHPR(playerMarker->GetHPR());
    SyAssert(prop_cast<cIntelEntity*>(player->GetIntel())!=NULL);
    static_cast<cIntelEntity*>(player->GetIntel())->SetStartLocation(playerMarker->GetLocation()); // set respawn point
    static_cast<cIntelEntity*>(player->GetIntel())->SetStartHeading(playerMarker->GetHeading()); // set respawn point
  }
}

void Titan::EnterGame()
{
  SyAssert(mpInputHandler[0]!=NULL && mpInputHandler[0]->GetGameObject()!=NULL); // must have a player
  mpCameraController->SetTarget(mpInputHandler[0]->GetGameObject()->GetID());
  mpCameraController->SetHeading(mpInputHandler[0]->GetGameObject()->GetHeading());

  if (mNumLocalPlayers > 1)
  {
    SyAssert(mpInputHandler[1]!=NULL && mpInputHandler[1]->GetGameObject()!=NULL);
    mpCameraController->SetTargetTwoPlayer(mpInputHandler[0]->GetGameObject()->GetID(),
                                           mpInputHandler[1]->GetGameObject()->GetID());
  }

  static_cast<cStatsCharacter*>(mpInputHandler[0]->GetGameObject()->GetStats())->UpdateNPCLevels();

  CheckPlayerModelOverride();

  // update armor and items because assets were reloaded
  cGameObject* pPlayer = mpRegistry->BeginType(cGameObject::OBJ_PLAYER);
  while (pPlayer)
  {
    static_cast<cGraphicCharacter*>(pPlayer->GetGraphic())->SetDirty();
    pPlayer = mpRegistry->NextType(pPlayer);
  }
}

const cStatsItemMaster* Titan::GetItemMaster( int player, int item )
{
  cInventoryCharacter* ic = (cInventoryCharacter*)mpInputHandler[player]->GetGameObject()->GetInventory();
  SyAssert( item < ic->GetNumItems() );
  const cStatsItemMaster* pItem = ic->GetItem(item)->GetMaster();
  return( pItem );
}

int Titan::SelectItemEquipSlot( cItem* pItem )
{
  eItemType type = pItem->GetMaster()->mType;
  switch( type )
  {
  case IT_MELEE:
    return( EQUIP_MELEE );
  case IT_RANGED:
    return( EQUIP_RANGED );
  case IT_HEAD:
    return( EQUIP_HEAD );
  case IT_CHEST:
    return( EQUIP_CHEST );
  case IT_LEGS:
    return( EQUIP_LEGS );
  case IT_FEET:
    return( EQUIP_FEET );
  case IT_RING:
    return( EQUIP_L_RING );
  case IT_BELT:
    return( EQUIP_BELT );
  case IT_NECKLACE:
    return( EQUIP_NECKLACE );
  case IT_SHOULDERS:
    return( EQUIP_SHOULDERS );
  default:
    return( EQUIP_UNEQUIPPED );
  }
}

void Titan::Restart()
{
  // clear out level...
  Reset();
  LoadLevel(mCurFilename.AsChar());
}

void Titan::Reset()
{
  // clear out current level
  mpDmgNumMgr->Close();
  mpDebrisSys->Clear();
  mpRegistry->Clear();
  mpServiceScheduler->Reset();

  mbNetworkGame = false;
}

void Titan::NetworkGameJoin(/*gift*/ PeeringNetwork::PeeringServerApi *api, int gameId)
{
  mpTitanPeeringNetwork->SetPeeringNetwork(api);
  mpTitanPeeringNetwork->GameJoin(gameId);
  mbNetworkGame = true;

  while (mpRegistry->GetLevelObject() == NULL || 
         mpRegistry->GetNumGameObjects() < mpRegistry->GetLevelObject()->GetNumGameObjects())
  {
    // wait for the level object and all the existing game objects
    mpTitanPeeringNetwork->GiveTime();
  }

  // todo: make sure we're under the max number of players: who does this?
  for (int ii=0;ii<mNumLocalPlayers;++ii)
  {
    tGameObjectID  playerID = mpRegistry->Create(cGameObject::OBJ_PLAYER);
    cGameObject *player = mpRegistry->Fetch(playerID);

    SyString name;
    GetLocalPlayerName(ii,&name);
    player->SetName(name.AsChar());
    cGameObject *playerMarker = mpRegistry->Fetch("playerSpawn");

    GAME_ASSERT(ERROR_CODE, mPlayerCharacterMasterNames[ii].Length() > 0, "Empty player character master name");
    player->GetStats()->SetMaster(mPlayerCharacterMasterNames[ii].AsChar());

    ((cIntelPlayer*)player->GetIntel())->SetControllerID(ii);
    SyAssert(mpInputHandler[ii] != NULL);
    mpInputHandler[ii]->SetGameObject(player, ii);

    // set up camera
    mpRegistry->InitObject(player->GetID(),true);
    player->SetSafeLocation(playerMarker->GetLocation());
    SyAssert(prop_cast<cIntelEntity*>(player->GetIntel())!=NULL);
    static_cast<cIntelEntity*>(player->GetIntel())->SetStartLocation(playerMarker->GetLocation()); // set respawn point
    static_cast<cIntelEntity*>(player->GetIntel())->SetStartHeading(playerMarker->GetHeading()); // set respawn point
  }

  LoadGlobalResources();

  EnterGame();
}

void Titan::NetworkGameCreate(/*gift*/ PeeringNetwork::PeeringServerApi *api, const char *gameName)
{
  mpTitanPeeringNetwork->SetPeeringNetwork(api);
  mpTitanPeeringNetwork->GameCreate(gameName);
  mbNetworkGame = true;
}

void Titan::HackStartupNetwork(const char *playerName, const char *gameName, const char *xmlFilename, const char *recordFilename, const char *playbackFilename)
{
  mpTitanPeeringNetwork->HackStartupNetwork(playerName, gameName, xmlFilename, recordFilename, playbackFilename);
  CheckPlayerModelOverride();
}

void Titan::CheckPlayerModelOverride()
{
  if ( mPlayerModelOverride.Length() )
  {
    OverloadPlayerModel( mPlayerModelOverride.AsChar() );
  }
}

//============================================================================
// Design Settings
//============================================================================

void          
Titan::SetLogMode(bool on)
{
  LogEnable(on);
}

bool 
Titan::GetLogMode()
{
  return IsLogEnabled();
}


void          
Titan::SetShowDamageFlash(bool on)
{
  mbShowDmgFlash = on;
}

bool 
Titan::GetShowDamageFlash()
{
  return mbShowDmgFlash;
}

void          
Titan::SetUnlockAllAbilities(bool on)
{
  mbUnlockAllAbilities = on;
}

bool 
Titan::GetUnlockAllAbilities()
{
  return mbUnlockAllAbilities;
}

// Sets whether player is invulnerable or not
void 
Titan::SetGodMode(bool on)
{
  if (mpInputHandler[0] && mpInputHandler[0]->GetGameObject())
  {
    if (on)
    {
      mpInputHandler[0]->GetGameObject()->GetStats()->AddCondition("Invulnerable", 0, 0, false, -1.0f, ID_NONE);
    }
    else
    {
      mpInputHandler[0]->GetGameObject()->GetStats()->RemoveCondition("Invulnerable", 0, 0, 0);
    }
  }
}

bool 
Titan::GetGodMode()
{
  if (mpInputHandler[0] && mpInputHandler[0]->GetGameObject())
  {
    return mpInputHandler[0]->GetGameObject()->GetStats()->HasCondition("Invulnerable");
  }

  return false;
}

// Sets whether to log combat events and calculations to a file.
void 
Titan::SetInvisibleMode(bool on)
{
  if (mpInputHandler[0] && mpInputHandler[0]->GetGameObject())
  {
    if (on)
    {
      mpInputHandler[0]->GetGameObject()->GetStats()->AddCondition("Invisible", 0, 0, 0, -1.0f, ID_NONE);
    }
    else
    {
      mpInputHandler[0]->GetGameObject()->GetStats()->RemoveCondition("Invisible", 0, 0, 0);
    }
  }
}

bool 
Titan::GetInvisibleMode()
{
  if (mpInputHandler[0] && mpInputHandler[0]->GetGameObject())
  {
    return mpInputHandler[0]->GetGameObject()->GetStats()->HasCondition("Invisible");
  }

  return false;
}

void Titan::SetSuperSpeedMode( bool on )
{
  if ( on != mbSuperSpeedMode )
  {
    mbSuperSpeedMode = on;
    if ( mpInputHandler[0] && mpInputHandler[0]->GetGameObject() )
    {
      if ( on )
      {
        mpInputHandler[0]->GetGameObject()->GetStats()->AddCondition( "Altered Movement Speed", 0, 0, 0, -1, 0, 100 );
      }
      else
      {
        mpInputHandler[0]->GetGameObject()->GetStats()->RemoveCondition( "Altered Movement Speed", 0, 0, 0 );
      }
    }
  }
}

bool Titan::GetSuperSpeedMode() const
{
  return mbSuperSpeedMode;
}

void 
Titan::OverloadPlayerModel(const char *modelname)
{
  SyAssertf(mpRegistry!=NULL,"Registry not initialized");
  cGameObject *obj = mpRegistry->Fetch("Player0_0");
  SyAssertf(obj!=NULL,"Can't Find Player");
  if (obj != NULL)
  {
    const cStatsCharacterMaster *c_master = prop_cast<cStatsCharacter*>(obj->GetStats())->GetMaster();

    const cStatsModelData* pModelData = GetDatabaseSys()->GetModelData(SyHashResourceID(modelname));

    if (pModelData)
    {
      // here we go, breaking encapsulation...i feel dirty...
      cStatsCharacterMaster *master = const_cast<cStatsCharacterMaster *>(c_master);

      master->mModelDataID = pModelData->mID.GetID();

      cGraphicActor *actor = prop_cast<cGraphicActor*>(obj->GetGraphic());
      actor->Release();  // release old actor      
      actor->Init();     // init new one with changed model
    }
    else
    {
      GAME_ASSERT(ERROR_DESIGN, false, "Could not find model data for '%s' to use with --player_model option", modelname);
    }
  }
}

void Titan::ReverseCamera(bool bReverse) const
{
  mpCameraController->ReverseCameraPan(bReverse);
}

void                  
Titan::GetLocalPlayerName(int num,SyString *name)
{
  char buffer[256];
  int peerID = mpTitanPeeringNetwork->GetPeerID();

  sprintf(buffer,"Player%d_%d",peerID,num);
  name->Init(buffer);
}

//============================================================================
// Player Inventory
//============================================================================

int Titan::InventoryGetItemCount(int player)
{
  cInventoryCharacter* ic = (cInventoryCharacter*)mpInputHandler[player]->GetGameObject()->GetInventory();
  return ic->GetNumItems();
}

bool Titan::InventoryGetItemInfo(int player, int item, TiCharacterItemInfo* pInfo)
{
  cGameObject *obj = mpInputHandler[player]->GetGameObject();
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(obj->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );
  const cStatsItemMaster* pMaster = pItem->GetMaster();

  pInfo->m_iHandle = item;

  static const int bufflen = 256;
  static char buffer[bufflen+1];

  pItem->T4_SetContext_Name(obj,'t');
  static const int name = SyHashResourceID("INV_ITEM_NAME");
  T4Expand( buffer, bufflen, name );
  pInfo->m_strName = buffer;

  pItem->GetPropertyString(obj, pInfo->m_strDescription);

  pInfo->m_strIcon = (pMaster->mIcon == 0) ? ("") : pMaster->mIcon;
  pInfo->m_idIcon = SyHashResourceID(pInfo->m_strIcon.AsChar());
  pInfo->m_eType = (EItemType)pMaster->mType;
  pInfo->m_eSlot = (EEquipSlot)pItem->GetEquipSlot();
  pInfo->m_nDamageBonus = pMaster->mBaseDamageBonus;
  pInfo->m_nPhysicalDefense = pMaster->mPhysicalDefense;
  pInfo->m_nMagicalDefense = pMaster->mMagicalDefense;
  pInfo->m_nAttackPower = pMaster->mAttackPower;
  pInfo->m_nSpellPower = pMaster->mSpellPower;
  return true;
}


int Titan::InventoryGetEquipSlot( int player, int item )
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item <= ic->GetNumItems() );
  if( item < ic->GetNumItems() )
  {
    cItem* pItem = ic->GetItem( item );
    return( pItem->GetEquipSlot() );
  }
  else
  {
    return( EQUIP_UNEQUIPPED );
  }
}

bool Titan::InventoryIsEquipped( int player, int item )
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  if( item < ic->GetNumItems() )
  {
    cItem* pItem = ic->GetItem( item );
    return( pItem->GetEquipSlot() != EQUIP_UNEQUIPPED );
  }
  else
  {
    return( false );
  }
}

bool Titan::InventoryEquip( int player, int item )
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );
  eEquipSlot equipSlot = (eEquipSlot)SelectItemEquipSlot( pItem );

  if( equipSlot == EQUIP_UNEQUIPPED )
  {
    return( false );
  }
  else
  {
    bool bResult = ic->Equip( pItem, equipSlot );  
    static_cast<cGraphicCharacter*>(mpInputHandler[player]->GetGameObject()->GetGraphic())->RecalculateWeaponAttachments();
    return bResult;
  }
}

void Titan::InventoryGetItemSpriteAndSlot (int player, int item, int& spriteHandle, int& slotId)
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );
  eEquipSlot equipSlot = (eEquipSlot)SelectItemEquipSlot( pItem );
  slotId = (int) equipSlot;
  SyESFParse& Parser = GetTitanUI()->GetESFParser();

  SyString path;
  pItem->GetAttachModelFilename(mpInputHandler[player]->GetGameObject(), &path);

  if (path.Length() <= 0)
  {
    spriteHandle = -1;
    return;
  }

  spriteHandle = LoadAssetSprite(Parser, path.AsChar());
}


void Titan::InventoryUnequip( int player, int item )
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );

  // E3 hack to prevent unequipping weapons b/c brute has no non-weapon animations
  if (pItem->GetEquipSlot() != EQUIP_MELEE)
  {
    ic->Unequip( pItem->GetEquipSlot() );
    static_cast<cGraphicCharacter*>(mpInputHandler[player]->GetGameObject()->GetGraphic())->RecalculateWeaponAttachments();
  }
}

bool Titan::InventoryDrop( int player, int item )
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );

  // E3 hack to prevent dropping weapons b/c brute has no non-weapon animations
  bool bResult = false;
  if (pItem->GetEquipSlot() != EQUIP_MELEE)
  {
    bResult = ic->Drop( pItem );
    static_cast<cGraphicCharacter*>(mpInputHandler[player]->GetGameObject()->GetGraphic())->RecalculateWeaponAttachments();
  }

  return( bResult );
}

bool Titan::InventoryGetIsEnhancementValidInSlot( int player, int item, int slot )
{
	// get item's slot (invalid slot returned for non-enhancements and other invalid items)
	int nItemSlot = InventoryGetEnhancementSlotType( player, item );
	if( nItemSlot != cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES )
	{
		// everything valid in UNIVERSAL slot 
		// (not sure if there are "universal" items, but easy enough to allow them)
		if(( slot == cStatsEnhancementMaster::ENH_SLOT_UNIVERSAL ) ||
		   ( nItemSlot == cStatsEnhancementMaster::ENH_SLOT_UNIVERSAL ))
		{
			return true;
		}

		// otherwise, only valid if slots match
		return ( slot == nItemSlot );
	}
	return false;
}

int Titan::InventoryGetEnhancementSlotType (int player, int item)
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );

  if (pItem->GetMaster()->mType == IT_ENHANCEMENT)
  {
    const cStatsEnhancementMaster* pEnh = mpDatabaseSys->GetEnhancementMaster(pItem->GetMaster()->mID.GetID());

    if (pEnh)
    {
      return pEnh->mSlotType;
    }
  }

  return cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES;
}

int Titan::InventoryGetWeaponEnhancementItem (int player, int slotType)
{
  cInventoryCharacter* pInv = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());

  cItem* pItem = pInv->GetEquippedItem( EQUIP_MELEE );

  if (pItem &&
      slotType >= cStatsEnhancementMaster::ENH_SLOT_UNIVERSAL &&
      slotType < cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES)
  {
    tGameID enhID = pItem->GetEnhancementInSlot((cStatsEnhancementMaster::SlotType)slotType);
  
    for (int iItem=0; iItem<pInv->GetNumItems(); ++iItem)
    {
      if (pInv->GetItem(iItem) && pInv->GetItem(iItem)->GetMasterID() == enhID)
      {
        return iItem;
      }
    }
  }
  
  return -1;
}

void Titan::InventoryUseEnhancement(int player, int item, int slotType, bool bApplyCost)
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < ic->GetNumItems() );
  cItem* pItem = ic->GetItem( item );

  if (pItem->GetMaster()->mType == IT_ENHANCEMENT)
  {
    const cStatsEnhancementMaster* pEnh = mpDatabaseSys->GetEnhancementMaster(pItem->GetMaster()->mID.GetID());

    if (pEnh)
    {
      cItem* pWeapon = ic->GetEquippedItem(EQUIP_MELEE);

      if (pWeapon)
      {
        pWeapon->SetEnhancementInSlot((cStatsEnhancementMaster::SlotType)slotType, pEnh->mID.GetID());
        static_cast<cGraphicCharacter*>(mpInputHandler[player]->GetGameObject()->GetGraphic())->SetDirty();

        if (bApplyCost)
        {
          cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());

          cCalcPacket packet;
          packet.SetTotal(-pEnh->mSlotUsageCost, "Enhancement slot", "Essence");
          pStats->AddEssence(&packet);
        }
      }
    }
  }
}

//-----------------------------------------------------------------------
void Titan::InventoryClearEnhancement( int player, int slotType )
{
	cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
	cItem* pWeapon = ic->GetEquippedItem(EQUIP_MELEE);

	if (pWeapon)
	{
		pWeapon->SetEnhancementInSlot( (cStatsEnhancementMaster::SlotType)slotType, ID_NONE );
    static_cast<cGraphicCharacter*>(mpInputHandler[player]->GetGameObject()->GetGraphic())->SetDirty();
	}
}

void Titan::InventoryConvertItemToEssence(int player, int item)
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
  cInventoryCharacter* pInv = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
  SyAssert( item < pInv->GetNumItems() );
  cItem* pItem = pInv->GetItem( item );

  int essence = SY_ROUND(((float)pItem->GetMaster()->mValue*mItemEssenceConversionRate));

  pInv->Remove(pItem);
  delete pItem;

  cCalcPacket packet;
  packet.SetTotal(essence, "Item Conversion", "Essence");
  pStats->AddEssence(&packet);
}

//============================================================================
// Merchant functionality
//============================================================================

int Titan::MerchantGetItemCount()
{
  const cStatsMerchantSet* pMerch = mpDatabaseSys->GetMerchantSet(mMerchantSetID);

  if (pMerch)
  {
    return pMerch->mItems.Size();
  }

  return 0;
}

bool Titan::MerchantGetItemInfo(int item, TiCharacterItemInfo* pInfo)
{
  const cStatsMerchantSet* pMerch = mpDatabaseSys->GetMerchantSet(mMerchantSetID);

  if (!pMerch)
  {
    return false;
  }

  SyAssert( item < pMerch->mItems.Size() );
  const cStatsItemMaster* pMaster = mpDatabaseSys->GetItemMaster(pMerch->mItems(item));

  pInfo->m_iHandle = item;

  pInfo->m_strIcon = (pMaster->mIcon == 0) ? ("") : pMaster->mIcon;
  pInfo->m_idIcon = SyHashResourceID(pInfo->m_strIcon.AsChar());
  pInfo->m_eType = (EItemType)pMaster->mType;
  pInfo->m_eSlot = (EEquipSlot)EQUIP_UNEQUIPPED;
  pInfo->m_nDamageBonus = pMaster->mBaseDamageBonus;
  pInfo->m_nPhysicalDefense = pMaster->mPhysicalDefense;
  pInfo->m_nMagicalDefense = pMaster->mMagicalDefense;
  pInfo->m_nAttackPower = pMaster->mAttackPower;
  pInfo->m_nSpellPower = pMaster->mSpellPower;
  return true;
}

void Titan::MerchantBuyItem(int player, int item)
{
  const cStatsMerchantSet* pMerch = mpDatabaseSys->GetMerchantSet(mMerchantSetID);

  if (!pMerch)
  {
    return;
  }

  SyAssert( item < pMerch->mItems.Size() );
  const cStatsItemMaster* pMaster = mpDatabaseSys->GetItemMaster(pMerch->mItems(item));

  int essence = GetPlayerEssence(player);

  if (essence >= pMaster->mValue)
  {
    cInventoryCharacter* pInv = static_cast<cInventoryCharacter*>(mpInputHandler[player]->GetGameObject()->GetInventory());
    cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());

    cCalcPacket packet;
    packet.SetTotal(-pMaster->mValue, "Buy Item", "Essence Cost");
    pInv->Add(pMerch->mItems(item));
    pStats->AddEssence(&packet);
  }
}

//============================================================================
// Save/Load Games
//============================================================================
int Titan::SaveGameGetCount()
{
  if (mpRegistry)
  {
    return mpRegistry->SaveGameGetCount();
  }

  return 0;
}

bool Titan::SaveGameGetInfo(int index, TitanSave* pInfo)
{
  if (mpRegistry)
  {
    return mpRegistry->SaveGameGetInfo(index, pInfo);
  }

  return false;
}

bool Titan::SaveGameLoad(int index)
{
  if (mpRegistry)
  {
    mpDmgNumMgr->Close();
    mpDebrisSys->Clear(); 
    mpRegistry->SaveGameLoad(index);
    LoadGlobalResources();
    mpServiceScheduler->Reset();

    // hook up camera / controller
    SyString name;
    int ii;
    for (ii=0; ii<MAX_NUM_LOCAL_PLAYERS; ++ii)
    {
      GetLocalPlayerName(ii,&name);
      cGameObject *obj = mpRegistry->Fetch(name.AsChar());

      if (obj == NULL)
      {
        SyAssert(ii!=0); // make sure we've got at least one local player
        break;
      }

      ((cIntelPlayer*)obj->GetIntel())->SetControllerID(ii);
      SyAssert(mpInputHandler[ii]!=NULL);
      mpInputHandler[ii]->SetGameObject(obj, ii);
    }

    mNumLocalPlayers = ii;

    EnterGame();
  }

  return true;
}

bool Titan::SaveGameOverwrite(int index, const SyString& desc)
{
  if (mpRegistry)
  {
    return mpRegistry->SaveGameOverwrite(index, desc);
  }

  return false;
}

bool Titan::SaveGameCreate(const SyString& desc)
{
  if (mpRegistry)
  {
    return mpRegistry->SaveGameCreate(desc);
  }

  return false;
}

bool Titan::SaveGameOverwriteLast()
{
  if (mpRegistry)
  {
    return mpRegistry->SaveGameOverwriteLast();
  }

  return false;
}

bool Titan::SaveGameLoadLast()
{
  if (mpRegistry)
  {
    mpDmgNumMgr->Close();
    mpDebrisSys->Clear(); 
    mpRegistry->SaveGameLoadLast();
    LoadGlobalResources();
    mpServiceScheduler->Reset();

    // hook up camera / controller
    SyString name;
    int ii;
    for ( ii=0;ii<MAX_NUM_LOCAL_PLAYERS;++ii)
    {
      GetLocalPlayerName(ii,&name);
      cGameObject *obj = mpRegistry->Fetch(name.AsChar());

      if (obj == NULL)
      {
        SyAssert(ii!=0); // make sure we've got at least one local player
        break;
      }

      ((cIntelPlayer*)obj->GetIntel())->SetControllerID(ii);
      SyAssert(mpInputHandler[ii] != NULL);
      mpInputHandler[ii]->SetGameObject(obj, ii);
    }

    mNumLocalPlayers = ii;

    EnterGame();
  }

  return false;
}

//============================================================================
// Debug Methods (Jon Wiesman)
//============================================================================

int Titan::Debug_AddItemToPlayerInventory( int32 nPlayerId, const char8 *pszItem )
{
  cInventoryCharacter* ic = static_cast<cInventoryCharacter*>(mpInputHandler[nPlayerId]->GetGameObject()->GetInventory());

  ic->Add(SyHashResourceID(pszItem));

  return 0;
}



//============================================================================
// Player Spells/Abilities
//============================================================================
int Titan::AbilityGetNumPoints( int player )
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());

  return pStats->GetAbilityPoints();
}

int Titan::AbilityMappedGetIndex( int player, int button )
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
  TitanButton tb = ButtonMax;

  switch (button)
  {
    case 0: tb = ButtonAttackL; break;
    case 1: tb = ButtonAttackS; break;
    case 2: tb = ButtonAction; break;
    case 3: tb = ButtonJump; break;
    default: break;
  }

  if (tb != ButtonMax)
  {
    const cAbilityMaster* pAbility = mpInputHandler[player]->GetMappedAbility(tb);

    if (pAbility)
    {
      for (int i = pStats->GetAbilityIDs().Begin(); i != pStats->GetAbilityIDs().End(); i=pStats->GetAbilityIDs().Next(i))
      {
        if ((tGameID)pStats->GetAbilityIDs()(i) == pAbility->mID.GetID())
        {
          return i;
        }
      }
    }
  }

  return -1;
}

int Titan::AbilityCurrentGetCount( int player )
{
  if (mbUnlockAllAbilities)
  {
    return GetDatabaseSys()->GetCompleteAbilityList().Size();
  }
  else
  {
    SyAssert(player >= 0);
    cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
    SyAssert(pStats->GetAbilityIDs().Size() == pStats->GetAbilityRanks().Size());
    return pStats->GetAbilityIDs().Size();
  }
}

bool Titan::AbilityCurrentGetName( int player, int index, SyString& name)
{
  const cAbilityMaster* pAbility = NULL;
  if (mbUnlockAllAbilities)
  {
    pAbility = GetDatabaseSys()->GetCompleteAbilityList()(index);
  }
  else
  {
    SyAssert(player >= 0);

    cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
    SyAssert(index < pStats->GetAbilityIDs().Size());

    pAbility = GetDatabaseSys()->GetAbilityMaster(pStats->GetAbilityIDs()(index), pStats->GetAbilityRanks()(index));
    SyAssert(pAbility != NULL);
  }

  if (pAbility)
  {
    int buffLen = 512;
    char buffer[512]; 
    buffer[0]='\0';

    if (!mbUnlockAllAbilities && pAbility->mNameString != 0)
    {
      T4Expand(buffer,buffLen,pAbility->mNameString);
    }
    else
    {
      sprintf(buffer, "%s %d", pAbility->mID.GetName(), pAbility->mRank);
    } 

    name = buffer;
    return true;
  }

  return false;
}

bool Titan::AbilityCurrentGetIDRank( int player, int index, int* pID, int* pRank )
{
  const cAbilityMaster* pAbility = NULL;
  if (mbUnlockAllAbilities)
  {
    pAbility = GetDatabaseSys()->GetCompleteAbilityList()(index);
  }
  else
  {
    SyAssert(player >= 0);

    cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
    SyAssert(index < pStats->GetAbilityIDs().Size());

    pAbility = GetDatabaseSys()->GetAbilityMaster(pStats->GetAbilityIDs()(index), pStats->GetAbilityRanks()(index));
    SyAssert(pAbility != NULL);
  }

  if (pAbility)
  {
    SyAssert(pID!=NULL && pRank !=NULL);
    *pID = pAbility->mID.GetID();
    *pRank = pAbility->mRank;

    return true;
  }

  return false;
}

bool Titan::AbilityCurrentGetDescription( int player, int index, SyString& desc )
{
  return false;
}

bool Titan::AbilityCurrentIsActive( int player, int index )
{
  const cAbilityMaster* pAbility = NULL;
  if (mbUnlockAllAbilities)
  {
    pAbility = GetDatabaseSys()->GetCompleteAbilityList()(index);
  }
  else
  {
    SyAssert(player >= 0);

    cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
    SyAssert(index < pStats->GetAbilityIDs().Size());

    pAbility = GetDatabaseSys()->GetAbilityMaster(pStats->GetAbilityIDs()(index), pStats->GetAbilityRanks()(index));
    SyAssert(pAbility != NULL);
  }

  if (pAbility)
  {
    return pAbility->mCombo == NUM_COMBOS; 
  }

  return false;
}

void Titan::AbilityCurrentMap( int player, int index, int button )
{
  TitanButton tb = ButtonMax;
  switch (button)
  {
    case 0: tb = ButtonAttackL; break;
    case 1: tb = ButtonAttackS; break;
    case 2: tb = ButtonAction; break;
    case 3: tb = ButtonJump; break;
    default: break;
  }

  if (tb != ButtonMax)
  {
    const cAbilityMaster* pAbility = NULL;
    if (mbUnlockAllAbilities)
    {
      pAbility = GetDatabaseSys()->GetCompleteAbilityList()(index);
    }
    else
    {
      cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
      SyAssert(index < pStats->GetAbilityIDs().Size());

      pAbility = GetDatabaseSys()->GetAbilityMaster(pStats->GetAbilityIDs()(index), pStats->GetAbilityRanks()(index));
      SyAssert(pAbility != NULL);
    }

    if (pAbility)
    {
      mpInputHandler[player]->MapAbility(tb, pAbility);
    }
  }
}

int Titan::AbilityAvailableGetCount( int player )
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());

  return pStats->GetNumAvailableAbilities();
}

bool Titan::AbilityAvailableGetName( int player, int index, SyString& name )
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
  tGameID id;
  int rank;
  pStats->GetAvailableAbility(index, id, rank);

  const cAbilityMaster* pAbility = GetDatabaseSys()->GetAbilityMaster(id, rank);

  SyAssert(pAbility != NULL);

  if (pAbility)
  {
    int buffLen = 512;
    char buffer[512]; 
    buffer[0]='\0';

    if (pAbility->mNameString != 0)
    {
      T4Expand(buffer,buffLen,pAbility->mNameString);
    }
    else
    {
      sprintf(buffer, "%s %d", pAbility->mID.GetName(), pAbility->mRank);
    } 

    name = buffer;
    return true;
  }

  return false;
}

int Titan::AbilityAvailableGetCost( int player, int index )
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
  tGameID id;
  int rank;
  pStats->GetAvailableAbility(index, id, rank);

  const cAbilityMaster* pAbility = GetDatabaseSys()->GetAbilityMaster(id, rank);

  SyAssert(pAbility != NULL);

  if (pAbility)
  {
    return pAbility->mCost;
  }

  return -1;
}

bool Titan::AbilityAvailableGetDescription( int player, int index, SyString& desc )
{
  return false;
}

bool Titan::AbilityAvailableIsBuyable( int player, int index )
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
  tGameID id;
  int rank;

  pStats->GetAvailableAbility(index, id, rank);
  const cAbilityMaster* pAbility = GetDatabaseSys()->GetAbilityMaster(id, rank);

  return pStats->GetAbilityPoints() >= pAbility->mCost;
}

void Titan::AbilityAvailableBuy( int player, int index)
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[player]->GetGameObject()->GetStats());
  tGameID id;
  int rank;

  pStats->GetAvailableAbility(index, id, rank);
  pStats->BuyAbility(id, rank);

  if (rank > 1)
  {
    for (int i=0; i < ButtonMax; ++i)
    {
      const cAbilityMaster* pMappedAbility = mpInputHandler[player]->GetMappedAbility((TitanButton)i);

      if (pMappedAbility && pMappedAbility->mID.GetID() == id && pMappedAbility->mRank < rank)
      {
        const cAbilityMaster* pNewAbility = GetDatabaseSys()->GetAbilityMaster(id, rank);
        mpInputHandler[player]->MapAbility((TitanButton)i, pNewAbility);
        break;
      }
    }
  }                                   
}

//============================================================================
// Wiesman's Ability functions
//============================================================================

int32 Titan::AbilityGetRank(int32 iPlayer, SyResourceID idAbility)
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[iPlayer]->GetGameObject()->GetStats());

  const SyVector<uint32> &arIDs = pStats->GetAbilityIDs();
  const SyVector<uint32> &arRanks = pStats->GetAbilityRanks();

  for(int i = 0; i < arIDs.Size(); i++)
  {
    if(arIDs(i) == idAbility)
      return arRanks(i);
  }
  return 0;
}

bool Titan::AbilityGetName(int32 iPlayer, SyResourceID idAbility, SyString &strName)
{
  bool bInactive = false;
  int nRank = AbilityGetRank(iPlayer, idAbility);
  if(nRank <= 0)
  {
    bInactive = true;
    nRank = 1;
  }

  const cAbilityMaster* pAbility = GetDatabaseSys()->GetAbilityMaster(idAbility, nRank);

  if (pAbility)
  {
    int buffLen = 512;
    char buffer[512]; 
    buffer[0]='\0';

    if (pAbility->mNameString != 0)
    {
      T4Expand(buffer,buffLen,pAbility->mNameString);
    }
    else
    {
      if(bInactive)
        sprintf(buffer, "%s", pAbility->mID.GetName());
      else
        sprintf(buffer, "%s %d", pAbility->mID.GetName(), pAbility->mRank);
    } 

    strName = buffer;
    return true;
  }

  return false;
}

bool Titan::AbilityGetDescription(int32 iPlayer, SyResourceID idAbility, SyString &strDescr)
{
  int nRank = AbilityGetRank(iPlayer, idAbility);
  if(nRank < 0)
    nRank = 0;

  const cAbilityMaster* pAbility = GetDatabaseSys()->GetAbilityMaster(idAbility, nRank);

  if (pAbility)
  {
    int buffLen = 512;
    char buffer[512]; 
    buffer[0]='\0';

    if ( false )
    {
      T4Expand(buffer,buffLen,pAbility->mNameString);
    }
    else
    {
      sprintf(buffer, "A description is needed for this ability.");
    } 

    strDescr = buffer;
    return true;
  }

  return false;
}

SyResourceID Titan::AbilityGetIcon(int32 iPlayer, SyResourceID idAbility)
{
  int nRank = AbilityGetRank(iPlayer, idAbility);
  if(nRank < 0)
    nRank = 0;

  const cAbilityMaster* pAbility = GetDatabaseSys()->GetAbilityMaster(idAbility, nRank);

  if (pAbility)
  {
    // the icon field has not been added
    return 0;
  }

  return 0;
}

void Titan::AbilityMapButton(int32 iPlayer, SyResourceID idAbility, int32 iButton)
{
  TitanButton tb = ButtonMax;
  switch (iButton)
  {
  case 0: tb = ButtonAttackL; break;
  case 1: tb = ButtonAttackS; break;
  case 2: tb = ButtonAction; break;
  case 3: tb = ButtonJump; break;
  default: break;
  }

  if (tb != ButtonMax)
  {
    const cAbilityMaster* pAbility = NULL;

    int32 nRank = AbilityGetRank(iPlayer, idAbility);

    pAbility = GetDatabaseSys()->GetAbilityMaster(idAbility, nRank);

    if (pAbility)
    {
      mpInputHandler[iPlayer]->MapAbility(tb, pAbility);
    }
  }
}

int32 Titan::AbilityGetButtonForAbility(int32 iPlayer, SyResourceID idAbility)
{
  TitanButton arButtons[] = {
    ButtonAttackL,
    ButtonAttackS,
    ButtonAction,
    ButtonJump,
  };

  for(int32 i = 0; i < 4; i++)
  {
    const cAbilityMaster* pAbility = mpInputHandler[iPlayer]->GetMappedAbility(arButtons[i]);
    if(pAbility != NULL && (SyResourceID)pAbility->mID.GetID() == idAbility)
      return i;
  }

  return -1;
}

SyResourceID Titan::AbilityGetAbilityForButton(int32 iPlayer, int32 iButton)
{
  TitanButton arButtons[] = {
    ButtonAttackL,
    ButtonAttackS,
    ButtonAction,
    ButtonJump,
  };

  const cAbilityMaster* pAbility = mpInputHandler[iPlayer]->GetMappedAbility(arButtons[iButton]);
  if(pAbility != NULL)
    return pAbility->mID.GetID();
  return 0;
}

void Titan::AbilityBuy(int iPlayer, SyResourceID idAbility)
{
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(mpInputHandler[iPlayer]->GetGameObject()->GetStats());

  int nRank = AbilityGetRank(iPlayer, idAbility);

  nRank++;
  if (pStats->BuyAbility(idAbility, nRank) && nRank > 1)
  {
    for (int i=0; i < ButtonMax; ++i)
    {
      const cAbilityMaster* pMappedAbility = mpInputHandler[iPlayer]->GetMappedAbility((TitanButton)i);

      if (pMappedAbility && (SyResourceID)pMappedAbility->mID.GetID() == idAbility && pMappedAbility->mRank < nRank)
      {
        const cAbilityMaster* pNewAbility = GetDatabaseSys()->GetAbilityMaster(idAbility, nRank);
        SyAssert(pNewAbility);
        if (pNewAbility)
        {
          mpInputHandler[iPlayer]->MapAbility((TitanButton)i, pNewAbility);
        }
        break;
      }
    }
  }                                   
}

//============================================================================
// Scene options
//============================================================================

// Gets or sets a value that indicates whether the bloom post processing effect is enabled
bool Titan::GetBloom()
{
  return mBloomEnabled;
}
void Titan::SetBloom( bool value )
{
  if ( mBloomEnabled == value )
  {
    // no change
    return;
  }
  mBloomEnabled = value;
}

bool Titan::GetMultiPass()
{
  return mMultiPassEnabled;
}
void Titan::SetMultiPass( bool value )
{
  if ( mMultiPassEnabled == value )
  {
    // no change
    return;
  }
  mMultiPassEnabled = value;
  SyScene* scene = mpTitanUI->GetScene();
  scene->SetMultiPass(mMultiPassEnabled);
}

void Titan::SetNoWaterRayCasts( bool value )
{
  if (value)
  {
    SyWaterSystem::DisableRayCasts();
  }
  else
  {
    SyWaterSystem::EnableRayCasts();
  }
}

void Titan::SetNoPlunk( bool value )
{
  if (value)
  {
    SyWaterSystem::DisablePlunk();
  }
  else
  {
    SyWaterSystem::EnablePlunk();
  }
}

TitanI::DrawMode Titan::GetDrawMode()
{
  return mDrawMode;
}

void Titan::SetDrawMode( DrawMode value )
{
  if ( mDrawMode == value )
  {
    // no change
    return;
  }
  mDrawMode = value;
  SyScene* scene = mpTitanUI->GetScene();
  switch ( mDrawMode )
  {
  case DrawModeNormal:
    scene->SetDrawMode( SYSCENEDRAWMODE_NORMAL );
    break;
  case DrawModeWire:
    scene->SetDrawMode( SYSCENEDRAWMODE_WIRE );
    break;
  case DrawModeSolid:
    scene->SetDrawMode( SYSCENEDRAWMODE_SOLIDFILL );
    break;
  }
}

void 
Titan::SetNumLocalPlayers(int numplayers)
{
  mNumLocalPlayers = numplayers;
};

int  
Titan::GetNumLocalPlayers()
{
  return mNumLocalPlayers;
};

//============================================================================
// Performance Stats
//============================================================================

int Titan::GetFPS()
{
  return mpTitanUI->GetPerformanceStats()->GetFPS();
}
int Titan::GetBatches()
{
  return mpTitanUI->GetPerformanceStats()->GetBatchesPerFrame();
}
int Titan::GetVertexes()
{
  return mpTitanUI->GetPerformanceStats()->GetVertsPerFrame();
}
int Titan::GetTriangles()
{
  return mpTitanUI->GetPerformanceStats()->GetTrisPerFrame();
}
uint32 Titan::GetPixels()
{
  return mpTitanUI->GetPerformanceStats()->GetPixelsPerFrame();
}
int Titan::GetDegenerateTriangles()
{
  return mpTitanUI->GetPerformanceStats()->GetDegenerateTrisPerFrame();
}
int Titan::GetAverageFPS()
{
  return mpTitanUI->GetPerformanceStats()->GetAvgFPS();
}
int Titan::GetAverageBatches()
{
  return mpTitanUI->GetPerformanceStats()->GetAvgBatchesPerFrame();
}
int Titan::GetAverageVertexes()
{
  return mpTitanUI->GetPerformanceStats()->GetAvgVertsPerFrame();
}
int Titan::GetAverageTriangles()
{
  return mpTitanUI->GetPerformanceStats()->GetAvgTrisPerFrame();
}
int Titan::GetAverageDegenerateTriangles()
{
  return mpTitanUI->GetPerformanceStats()->GetAvgDegenerateTrisPerFrame();
}



//============================================================================
// PlayScriptUsingNamedActors - This is the simplest-to-use version of cutscene
// playbacks. This assumes that the names of the Actors in Maya (name of
// root node on skel joint heirarchy) is the same name as the in-game name of
// the character. So no SyFXScriptActorPairs are needed to related the two.
//============================================================================
int32 Titan::PlayScriptUsingNamedActors (int32 ScriptHandle)
{
  SyScene *scene = mpTitanUI->GetScene();
  //SyDictionary * sceneDictionary = scene->GetDictionary();
  //int32 cutSceneScriptResourceHandle = -1;
  int32 numCutSceneActors = 0;

  // get the fxScriptSystem for playing back cutscenes
  SyFXScriptSystem* fxScriptSystem = scene->GetFXScriptSystem();

  // Assemble SyFXScriptActorPair array, correlating in-game Scene actor names to "Maya"/art tool actor names.

  SyFXScript    *pScript = fxScriptSystem->Script(ScriptHandle);
  int scriptArg = pScript->GetFirstArg();
  SyFXScriptActorPair  actorPair[200];

  while (scriptArg != -1)
  {
    const char8* actorName = pScript->GetArgActorName (scriptArg);
    cGameObject * gob = GetRegistry()->Fetch(actorName);
    SyAssertf(gob!=NULL, "Error: Can't find player (%s) in game registry. Maya name may be wrong.\n\
                         The name (%s) was what was found in the cutscene .esf file, but not found in the game registry.", actorName, actorName);
    SyActorHandle gameActor = gob->GetGraphic()->GetActorHandle();

    // add it to our SyFXScriptActor pair argument structure.
    actorPair[numCutSceneActors].mActorHandle = gameActor;
    actorPair[numCutSceneActors].mpArgName = SyStr::AllocateString(actorName); // this is the name of it in the FXScript (Maya)
    numCutSceneActors++;

    scriptArg = pScript->GetNextArg (scriptArg);
  }

  if (numCutSceneActors == 0)
  {
    SyAssertf (0, "No actors were found in cut scene");
    return -1;
  }

  // fire off the script
  mpTitanFXScriptDriver->BeginCutScene();
  fxScriptSystem->PlayScript (ScriptHandle, numCutSceneActors,  (const SyFXScriptActorPair *) actorPair);

  return 0;
}


//============================================================================
// PlayScript - The more verbose / explicit way of running a cut scene. Using
// SyFXScriptActorPairs, specify which Actor handle you want to run using which
// character name in Maya (root node of skel heirarchy name).
//============================================================================
int32 Titan::PlayScript (int32 ScriptHandle, int32 NumActorPairs, const SyFXScriptActorPair *pPairs )
{
  SyScene *scene = mpTitanUI->GetScene();
  //SyDictionary * sceneDictionary = scene->GetDictionary();
  //int32 cutSceneScriptResourceHandle = -1;

  SyAssertf(NumActorPairs!=0, "oops: NumActorPairs is 0. Can't run a cut scene with no actors.\n\
                              Did you mean to use PlayScriptUsingNamedActors() instead?\n");

  // get the fxScriptSystem for playing back cutscenes
  SyFXScriptSystem* fxScriptSystem = scene->GetFXScriptSystem();

  // fire off the script
  mpTitanFXScriptDriver->BeginCutScene();
  fxScriptSystem->PlayScript (ScriptHandle, NumActorPairs,  pPairs);

  return 0;
}



//============================================================================
// isCutScenePlaying
//============================================================================
bool Titan::isCutScenePlaying()
{
  bool result = mpTitanFXScriptDriver->isCutScenePlaying();
  return result;
}

//============================================================================
// StartCutScene
//============================================================================

void Titan::StartCutScene(bool bAllowControllerMotion)
{ 
  mpRegistry->GetLevelObject()->SetEnableInput(bAllowControllerMotion);

  // clear all input to everyone, except their headings

  float heading;

  cGameObject *player = mpRegistry->BeginType(cGameObject::OBJ_PLAYER);
  while (player != NULL)
  {
    cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(player->GetGraphic());
    if (graphic != NULL)
    {
      heading = player->GetHeading();
      graphic->GetAnimController()->GetInput()->Clear();
      graphic->GetAnimController()->GetInput()->mHeadingRequest = heading;
    }

    player = mpRegistry->NextType(player);
  }
  
  cGameObject *npc = mpRegistry->BeginType(cGameObject::OBJ_NPC);
  while (npc != NULL)
  {
    cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(npc->GetGraphic());
    if (graphic != NULL)
    {
      heading = npc->GetHeading();
      graphic->GetAnimController()->GetInput()->Clear();
      graphic->GetAnimController()->GetInput()->mHeadingRequest = heading;
    }

    npc = mpRegistry->NextType(npc);
  }

  if (!bAllowControllerMotion)
  {
    mpTitanUI->BeginLetterbox(mbStartLevelCutscene);
  }
  else
  {
    mpTitanUI->EndLetterbox();
  }

  if (mbAllowCutSceneControllerMotion != bAllowControllerMotion)
  {
    mbAllowCutSceneControllerMotion = bAllowControllerMotion;

    for (int iLocal=0; iLocal<mNumLocalPlayers; ++iLocal)
    {
      if (mpInputHandler[iLocal] && mpInputHandler[iLocal]->GetGameObject())
      {
        if (mbAllowCutSceneControllerMotion)
        {
          mpInputHandler[iLocal]->OnUnPauseGame();
        }
        else
        {
          mpInputHandler[iLocal]->OnPauseGame();
        }
      }
    }
  }
}

void Titan::EndCutScene()
{
  mbAllowCutSceneControllerMotion = !mpTitanUI->IsExiting();

  mpTitanUI->EndLetterbox();

  cGameObject *player = mpRegistry->BeginType(cGameObject::OBJ_PLAYER);
  while (player != NULL)
  {
    cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(player->GetGraphic());
    if (graphic != NULL)
    {
      graphic->GetAnimController()->GetInput()->mHeadingRequest = player->GetHeading();
    }

    player = mpRegistry->NextType(player);
  }

  cGameObject *npc = mpRegistry->BeginType(cGameObject::OBJ_NPC);
  while (npc != NULL)
  {
    cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(npc->GetGraphic());
    if (graphic != NULL)
    {
      graphic->GetAnimController()->GetInput()->mHeadingRequest = npc->GetHeading();
    }

    npc = mpRegistry->NextType(npc);
  }
}

//============================================================================
// Player Stats
//============================================================================

int Titan::GetPlayerName( int32 PlayerId, char8* pBuffer, int32 BuffLen ) const
{
  if ( pBuffer && BuffLen > 0 )
  {
    pBuffer[0] = 0;
  }

  if( PlayerId >= MAX_NUM_LOCAL_PLAYERS )
  {
    return -1;
  }

  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }

  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  if( pObj == NULL )
  {
    return -1;
  }
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  const SyString& name = pStats->GetProperName();
  SyStr::strncpy( pBuffer, name.AsChar(), BuffLen ); 
  return 0;
}

void Titan::SetPlayerName( int32 PlayerId, const char8* pName )
{
  if( PlayerId >= MAX_NUM_LOCAL_PLAYERS )
  {
    return;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  if( pObj == NULL )
  {
    return;
  }
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  pStats->SetProperName( pName );
}

int Titan::GetPlayerHealthMax( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->CalcMaxHealth() );
}

int Titan::GetPlayerHealth( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->GetHealth() );
}

int Titan::GetPlayerBlockMax( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->CalcMaxBlock() );
}

int Titan::GetPlayerBlock( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->GetBlock() );
}

int Titan::GetPlayerManaMax( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->CalcMaxMana() );
}

int Titan::GetPlayerMana( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->GetMana() );
}

int Titan::GetPlayerEssence( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->GetEssence() );
}

int Titan::GetPlayerLevel( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->GetLevel()->mLevel );
}

int Titan::GetPlayerCurExp( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  return( pStats->GetExperience() );
}

int Titan::GetPlayerNextLevelExp( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  const cCharacterLevel* pLevel = GetDatabaseSys()->GetCharacterLevel(pStats->GetLevel()->mLevel+1);

  if (pLevel)
  {
    return pLevel->mXPRequired;
  }

  return -1;
}

int Titan::GetPlayerPrevLevelExp( int32 PlayerId )
{
  SyAssert( PlayerId >= 0 && PlayerId < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[PlayerId] )
  {
    return 0;
  }
  cGameObject* pObj = mpInputHandler[PlayerId]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());
  const cCharacterLevel* pLevel = GetDatabaseSys()->GetCharacterLevel(pStats->GetLevel()->mLevel);

  if (pLevel)
  {
    return pLevel->mXPRequired;
  }

  return -1;
}

int32 Titan::GetPlayerLocation(int32 iPlayer, SyVect3 &vecLocation)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return 0;
  }
  cGameObject *pObject = mpInputHandler[iPlayer]->GetGameObject();

  vecLocation = pObject->GetLocation();

  return 0;
}

float Titan::GetPlayerHeading( int32 iPlayer ) const 
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  float heading = 0.0f;

  if ( mpInputHandler[iPlayer] )
  {
    cGameObject *pObject = mpInputHandler[iPlayer]->GetGameObject();
    heading = pObject->GetHeading();
  }

  return heading; 
}

void Titan::SetPlayerCharacter(int iPlayer, const char* characterMasterName)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  mPlayerCharacterMasterNames[iPlayer] = characterMasterName;
}

float Titan::GetCameraHeading()
{
  float heading = 0.0f;
  float pitch   = 0.0f;
  float roll    = 0.0f;

  mpCameraController->GetCamera()->GetDir().HPR(&heading, &pitch, &roll);

  return heading;
}

int32 Titan::GetPlayerAttackPower(int32 iPlayer) const
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return 0;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  return pStats->CalcAttackPower();
}

int32 Titan::GetPlayerMagicPower(int32 iPlayer) const
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return 0;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  return pStats->CalcMagicPower();
}

int32 Titan::GetPlayerMeleeDefense(int32 iPlayer) const
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return 0;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  return pStats->CalcMeleeDefense();
}

int32 Titan::GetPlayerSpellDefense(int32 iPlayer) const
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return 0;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  return pStats->CalcSpellDefense();
}

int32 Titan::GetPlayerBonusPoints(int32 iPlayer) const
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return 0;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  return pStats->GetBonusPoints();
}

void Titan::AddHealthPoints(int32 iPlayer, int32 nPoints)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  pStats->AddHealth(nPoints);
}

void Titan::AddManaPoints(int32 iPlayer, int32 nPoints)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  pStats->AddMana(nPoints);
}

void Titan::AddAttackPowerPoints(int32 iPlayer, int32 nPoints)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  pStats->AddAttackPower(nPoints);
}

void Titan::AddMagicPowerPoints(int32 iPlayer, int32 nPoints)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  pStats->AddMagicPower(nPoints);
}

void Titan::AddMeleeDefensePoints(int32 iPlayer, int32 nPoints)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  pStats->AddMeleeDefense(nPoints);
}

void Titan::AddSpellDefensePoints(int32 iPlayer, int32 nPoints)
{
  SyAssert( iPlayer >= 0 && iPlayer < MAX_NUM_LOCAL_PLAYERS );
  if ( !mpInputHandler[iPlayer] )
  {
    return;
  }

  cGameObject* pObj = mpInputHandler[iPlayer]->GetGameObject();
  cStatsCharacter* pStats = static_cast<cStatsCharacter*>(pObj->GetStats());

  pStats->AddSpellDefense(nPoints);
}




//============================================================================
// NPC Stats
//============================================================================

static const float32 NPC_INFO_DIST_MAX = 5.0f;

bool Titan::GetFirstNPCInfo(int player, int& health, int& healthmax, int& block, int& blockmax, int& x, int& y, bool& offscreen, bool& isBlocking)
{
  offscreen = true; // default
  isBlocking = true; // default;

  if (!mpInputHandler[player])
  {
    return false;
  }

  cGameObject* pNPC = mpRegistry->BeginType(cGameObject::OBJ_NPC);

  if (!pNPC)
  {
    mNPCQueryID = ID_NONE;
    return false;
  }
  mNPCQueryID = pNPC->GetID();

  return( GetNPCInfo( pNPC, player, health, healthmax, block, blockmax, x, y, offscreen, isBlocking ) );
}

bool Titan::GetNextNPCInfo(int player, int& health, int& healthmax, int& block, int& blockmax, int& x, int& y, bool& offscreen, bool& isBlocking)
{
  offscreen = true; // default
  isBlocking = true; // default;

  if (!mpInputHandler[player])
  {
    return false;
  }

  SyAssertf(mNPCQueryID != ID_NONE, "Didn't init NPC query properly");
  
  cGameObject* pNPC = mpRegistry->Fetch(mNPCQueryID);
  if (!pNPC)
  {
    mNPCQueryID = ID_NONE;
    return false;
  }

  pNPC = mpRegistry->NextType(pNPC);
  if (!pNPC)
  {
    mNPCQueryID = ID_NONE;
    return false;
  }
  mNPCQueryID = pNPC->GetID();

  return( GetNPCInfo( pNPC, player, health, healthmax, block, blockmax, x, y, offscreen, isBlocking ) );
}

bool
Titan::GetNPCInfo( cGameObject* pNPC, int player, int& health, int& healthmax, int& block, int& blockmax, int& x, int& y, bool& offscreen, bool& isBlocking )
{
  SyVect3 npcLoc(pNPC->GetLocation());

  float32 dist = mpInputHandler[player]->GetGameObject()->GetLocation().Distance( npcLoc );
  if ( dist < 0.0f || dist > NPC_INFO_DIST_MAX )
  {
    return true;
  }

  SyActorHandle hActor = pNPC->GetGraphic()->GetActorHandle();
  if (SyActorNull == hActor)
  {
    return true;
  }

  SyAssert(pNPC->GetLocation().Y > -200.0f);

  SyAssert(GetScene()->GetActorSpritePtr(hActor)->GetType() == SYSPRITETYPE_CSPRITE);

  SyCSprite* pSprite = static_cast<SyCSprite*>(GetScene()->GetActorSpritePtr( hActor ));

  if (pSprite && pSprite->HasIdentNode(CHAR_NODE_HEAD))
  {
    SyMatrix44 mat;
    pSprite->CalcIdentNodeWorldTransform( CHAR_NODE_HEAD, hActor, *GetScene(), mat );
    npcLoc( mat(3,0), mat(3,1), mat(3,2) );
  }

  SyCamera* pCamera = GetCameraController()->GetCamera();
  SyVect4 locInClip = npcLoc * pCamera->GetViewProjection();

  float rhw = 1.0f / locInClip(3);
  locInClip(0) *= rhw;
  locInClip(1) *= rhw;
  locInClip(2) *= rhw;
  locInClip(3) = 0;

  if ( fabs( locInClip(0) ) > 1.f ||
       fabs( locInClip(1) ) > 1.f ||
       fabs( locInClip(2) ) > 1.f )
  {
    return true;
  }

  SyRaster* pRaster = GetScene()->GetRasterDev();
  cStatsCharacter* pNPCStats = static_cast<cStatsCharacter*>(pNPC->GetStats());
  cGraphicCharacter* pGraphic = static_cast<cGraphicCharacter*>(pNPC->GetGraphic());

  health      = pNPCStats->GetHealth();
  healthmax   = pNPCStats->CalcMaxHealth();
  block       = pNPCStats->GetBlock();
  blockmax    = pNPCStats->CalcMaxBlock();
  x           = (int)( (locInClip.X+1.f) * pRaster->GetScreenWidth() / 2 );
  y           = (int)( (-locInClip.Y+1.f) * pRaster->GetScreenHeight() / 2 );
  isBlocking  = pGraphic->GetAnimController()->IsBlocking();
  offscreen   = false;
  return true;
}

//============================================================================
// UI Activate String
//============================================================================

bool 
Titan::GetActivateString(int playerId,char *buffer,int buffLen)
{
  SyAssert( playerId >= 0 && playerId < MAX_NUM_LOCAL_PLAYERS );

  // make sure we're null terminated, no matter what
  if ( buffer && buffLen > 0 )
  {
    buffer[0] = 0;
  }

  // if input is shut down (because of cutscene), don't put an activate string
  if (!AllowCutSceneControllerMotion())
  {
    return false;
  }

  if ( !mpInputHandler[playerId] )
  {
    return false;
  }

  cGameObject* pObj = mpInputHandler[playerId]->GetGameObject();

  cIntelPlayer* pIntel = static_cast<cIntelPlayer *>(pObj->GetIntel());

  cGameObject* selected = mpRegistry->Fetch(pIntel->PickActionTarget());

  if (selected == NULL)
  {
    return false;
  }

  cStats *stats = selected->GetStats();

  if (stats->GetActivateType() == cStats::ACTIVATE_PICKUP &&
      selected->GetType() == cGameObject::OBJ_PROP && 
      !(static_cast<cStatsCharacter*>(pObj->GetStats())->CanLift(selected)))
  {
    return false;
  }

  SyString str;
  stats->GetActivateString(&str);
  char8 text[256];
  SyStr::snprintf( text, sizeof(text), "|t %s",str.AsChar());

  strncpy(buffer,text,buffLen);

  return true;
};

//============================================================================
// UI Subtitles
//============================================================================

bool 
Titan::GetSubtitleText(char *buffer,int buffLen)
{
  if (mSubtitleTime <= 0.0f || mSubtitle.Length() == 0)
  {
    return false;
  }

  // TODO: set speaker/ listener?

  strncpy(buffer,mSubtitle.AsChar(),buffLen);
  return true;
}

void 
Titan::SetSubtitleText(char * str, float time)
{
  mSubtitleTime = time;
  mSubtitle.Init(str);
}

//============================================================================
// Localized Text
//============================================================================

// plain string table lookup -- translated by locale, but no token expansion
// returns T4::NO_ERROR on success
int Titan::T4Lookup( char8* pBuffer, int BufLen, uint32 StringID )
{
  uint32 index = mpDatabaseSys->GetTextID(StringID);
  if( mpT4 )
  {
    return( mpT4->lookup( pBuffer, BufLen, index ) );
  }
  else
  {
    return( -1 );
  }
}

// full template expansion & translation
// returns T4::NO_ERROR on success
int Titan::T4Expand( char8* pBuffer, int BufLen, uint32 StringId, float32 Faction )
{
  uint32 index = mpDatabaseSys->GetTextID(StringId);
  pBuffer[0] = '\0';
  if( mpT4 )
  {
    return( mpT4->expand( pBuffer, BufLen, index, Faction ) );
  }
  else
  {
    return( -1 );
  }
}

int Titan::T4SetTopic( char8 Slot )
{
  if( mpT4 )
  {
    return( mpT4->setTopic( Slot ) );
  }
  else
  {
    return( -1 );
  }
}

int Titan::T4SetContext( char8 Slot,
                        uint32 stringID, 
                        int Gender, 
                        int Count, 
                        int BeingType, 
                        int RespectType,
                        int Age )
{
  uint32 index = mpDatabaseSys->GetTextID(stringID);
  if( mpT4 )
  {
    return( mpT4->setContext( Slot, index, 
                              (T4::Sex)Gender, 
                              Count, 
                              (T4::Type)BeingType, 
                              (T4::Honor)RespectType, 
                              Age ) );
  }
  else
  {
    return( -1 );
  }
}


int Titan::T4SetFinal( char8 Slot,
                      const char8* pText, 
                      int Gender, 
                      int Count, 
                      int BeingType, 
                      int RespectType,
                      int Age )
{
  if( mpT4 )
  {
    return( mpT4->setFinal( Slot, pText, (T4::Sex)Gender, Count, (T4::Type)BeingType, (T4::Honor)RespectType, Age ) );
  }
  else
  {
    return( -1 );
  }
}

bool Titan::GetDebugLabel()
{
#ifdef _DRAWDEBUGOVERLAY
  return cDebugOverlay::Get()->IsChannelEnabled("Anim");
#else
  return false;
#endif
}

void Titan::SetDebugLabel( bool value )
{
  DEBUGOVERLAY_ENABLECHANNEL("Anim", value);
}

void Titan::SetGlobalAIAttackTimer()
{
  mGlobalAIAttackTimer = mGlobalAIAttackDelay;
}

bool Titan::IsGlobalAIAttackTimerActive()
{
  return mGlobalAIAttackTimer > 0.00001f;
}

/**********************************************************
 * LOAD SPRITE ASSET
 **********************************************************/
int32 Titan::LoadAssetSprite (SyESFParse &parser, const char * assetName, bool bOptional)
{
  SyResourceType resourceType;
  int32 assetHandle = -1;
  SyScene* tScene = GetScene();

  SyDictionary * sceneDictionary = tScene->GetDictionary();  
  SyPathname pathName (assetName);

  if (mUseWadFile == true && mLoadingLevel == true)
  {
    /*
     * Look in the dictionary, it may already be there (read from wadfile)
     */
    int found = sceneDictionary->Find (pathName.Base().AsChar(), resourceType, assetHandle);
    if (found == 0 && !bOptional)
    {
      /*
       * It really shoulda been in the Wad file.
       */
      GAME_ASSERT(ERROR_ART, assetHandle != -1, "File (Sprite) not found in Wad: '%s'.", assetName);
    }
  }
  else
  {
    // first check to see if esf is already loaded
    if (!sceneDictionary->Find (pathName.Base().AsChar(), resourceType, assetHandle))
    {
      /*
       * Just load the ESF file individually
       */
      assetHandle = parser.ParseSprite (assetName, *tScene);
    }

    /*
     * Sanity check
     */
    GAME_ASSERT(ERROR_ART, bOptional || assetHandle != -1, "Titan::LoadAssetSprite - Can't Parse .ESF file '%s'.", assetName);
  }

  /*
   * Log the .esf file we're loading to a logfile (for wadfile generation)
   */
  if (IsLogging() == true)
  {
    if (assetHandle != -1)
    {
      LogEntry (assetName);
    }
  }

  return assetHandle;
}


/**********************************************************
 * LOAD WORLD ASSET
 **********************************************************/
int32 Titan::LoadAssetWorld (SyESFParse &parser, const char * assetName, bool isStreaming)
{
  int32 assetHandle = -1;
  SyScene* tScene = GetScene();

  /*
   * We don't load worlds via Wad file.
   */
  assetHandle = parser.ParseWorld (assetName, isStreaming, *tScene);

  /*
   * Sanity check
   */
  GAME_ASSERT(ERROR_ART, assetHandle != -1, "Titan::LoadAssetWorld - Can't Parse .ESF file '%s'.", assetName);

  /*
   * Log the .esf file we're loading to a logfile (for wadfile generation)
   */
  if (IsLogging() == true)
  {
    if (assetHandle != -1)
    {
      LogEntry (assetName);
    }
  }

  return assetHandle;
}

/**********************************************************
 * LOAD ASSET
 **********************************************************/
int32 Titan::LoadAsset (SyESFParse &parser, const char * assetName, bool bOptional)
{
  int assetHandle = -1;
  SyScene* tScene;
  /*
   * We can't lookup this ESF file in the dictionary because it is a 
   * ESF pack file, so there is no dictionary key for it. The client
   * doesn't want a handle back anyway, just return zero.
   */
  if (mUseWadFile == true && mLoadingLevel == true)
  {
    return 0;
  }

  /*
   * Not using wad, read it in.
   */
  tScene = GetScene();
  assetHandle = parser.Parse (assetName, *tScene);

  /*
   * Sanity check
   */
  GAME_ASSERT(ERROR_ART, bOptional || assetHandle != -1, "Titan::LoadAsset - Can't Parse .ESF file '%s'.", assetName);

  /*
   * Log the .esf file we're loading to a logfile (for wadfile generation)
   */
  if (IsLogging() == true)
  {
    if (assetHandle != -1)
    {
      LogEntry (assetName);
    }
  }

  return assetHandle;
}

/**********************************************************
 * LOAD SOUND ASSET
 **********************************************************/
int32 Titan::LoadAssetSound (SyESFParse &parser, const char * assetName)
{
  int32 assetHandle = -1;
  SyScene* tScene = GetScene();
  SyDictionary * sceneDictionary = tScene->GetDictionary();  
  SyPathname pathName (assetName);
  SyResourceType assetResourceType;

  if (mUseWadFile == true && mLoadingLevel == true)
  {
    /*
     * Look in the dictionary, it may already be there (read from wadfile)
     */
    int found = sceneDictionary->Find (pathName.Base().AsChar(), assetResourceType, assetHandle);
    if (found == 0)
    {
      /*
       * It really shoulda been in the Wad file.
       */
      GAME_ASSERT(ERROR_ART, assetHandle != -1, "File (Sound) not found in Wad: '%s'.", assetName);
    }
    if (assetResourceType != SYRESOURCETYPE_SOUND && assetResourceType != SYRESOURCETYPE_SOUNDSPRITE)
    {
        GAME_ASSERT(ERROR_ART, assetHandle != -1, "File not of type sound or soundsprite): '%s'.", assetName);
    }
  }
  else
  {
    /*
     * Just load the ESF file individually
     */
    int found = sceneDictionary->FindTyped (pathName.Base().AsChar(), SYRESOURCETYPE_SOUND, assetHandle);
    if (found == 0)
    {
      found = sceneDictionary->FindTyped (pathName.Base().AsChar(), SYRESOURCETYPE_SOUNDSPRITE, assetHandle);

      if (found == 0)
      {
        assetHandle = parser.ParseSound (assetName, *tScene);

        if (assetHandle == -1)
        {
          assetHandle = parser.ParseSprite (assetName, *tScene);
        }
      }
    }

    /*
     * Sanity check
     */
    GAME_ASSERT(ERROR_ART, assetHandle != -1, "Titan::LoadAssetSound - Can't Parse .ESF file '%s'.", assetName);
  }

  /*
   * Log the .esf file we're loading to a logfile (for wadfile generation)
   */
  if (IsLogging() == true)
  {
    if (assetHandle != -1)
    {
      LogEntry (assetName);
    }
  }

  return assetHandle;
}

/**********************************************************
 * BeginLogging - write names of .esf files loaded into a
 * file.
 **********************************************************/
void  Titan::BeginLogging (const char * logfilename,   SyVector< SyPathname > *PackList)
{
  mLoggingFlag = true;
  mUseWadFile = false;
  if (logfilename != NULL)
  {
    mLogFile = fopen (logfilename, "w");
  }
  mLogList = PackList;
}

/**********************************************************
 * LogEntry
 **********************************************************/
void Titan::LogEntry (const char * logString)
{
  /*
   * Log the .esf file we're loading to a logfile (for wadfile generation)
   */
  if (mLoggingFlag == true)
  {
    if (mLogFile != NULL)
    {
      fprintf (mLogFile, "%s\n", logString);
    }
    mLogList->Add (SyPathname (logString));
  }
}

/**********************************************************
 * EndLogging
 **********************************************************/
void  Titan::EndLogging ()
{
  if (mLogFile != NULL)
  {
    fclose (mLogFile);
  }
  mLogFile = NULL;
  mLoggingFlag = false;
}

/**********************************************************
 * GetUseWadFile
 **********************************************************/
bool  Titan::GetUseWadFile()
{
  return mUseWadFile;
}

/**********************************************************
 * SetUseWadFile
 **********************************************************/
void Titan::SetUseWadFile(bool wadFile)
{
  mUseWadFile = wadFile;
}

// EOF
