/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2005.
-------------------------------------------------------------------------
$Id$
$DateTime$
Description: Directs the 'Film' asking the CFilmCamera for shots, and CFilmAction for choreography.
-------------------------------------------------------------------------
History:
- 30:6:2005: Created by Nick Hesketh

*************************************************************************/

#include "StdAfx.h"

#include ".\FilmDirector.h"

#include "FilmCamera.h"
#include "FilmAction.h"
#include "FilmWorld.h"

#include "Player.h"
#include "G4Player.h"
#include "Game.h"
#include "Game04.h"
#include "GameUtils.h"

#include <map>
//#include <ViewSystem.h>

#define DURATION_SCALE 1.0f

//////////////////////////////////////////////////////////////////////
/*! convert a length unit vector to camera's angles:
x camera axis is looking up/down
y is roll
z is left/right	
*/
inline Vec3 ConvertUnitVectorToCameraAngles(const Vec3& vec)	
{
  Vec3 v=vec;

  float	fForward;
  float	fYaw,fPitch;

  if (v.y==0 && v.x==0) 
  {
    //looking up/down
    fYaw=0;
    if (v.z>0) 			
      fPitch=90;			
    else 			
      fPitch=270;	
  } 
  else 
  {
    if (v.x!=0) 
    {
      fYaw=(float)(cry_atan2f((float)(v.y),(float)(v.x))*180.0f/gf_PI);
    }
    else 
      //looking left/right	
      if (v.y>0) 							
        fYaw=90;			
      else 			
        fYaw=270;			

    if (fYaw<0) 			
      fYaw+=360;			

    fForward=(float)cry_sqrtf(v.x*v.x+v.y*v.y);
    fPitch=(float)(cry_atan2f(v.z,fForward)*180.0f/gf_PI);
    if (fPitch<0) 
      fPitch+=360;			
  }

  //y = -fPitch;
  //x = fYaw;
  //z = 0;
  v.x=-fPitch;
  v.y=0; 
  v.z=fYaw+90;

  //clamp again
  if (v.x>360)	
    v.x-=360;
  else
    if (v.x<-360) 
      v.x+=360;

  if (v.z>360)	
    v.z-=360;
  else
    if (v.z<-360) 
      v.z+=360;

  return v;
}

//////////////////////////////////////////////////////////////////////
/*! convert a vector to camera's angles:
x camera axis is looking up/down
y is roll
z is left/right	
*/
inline Vec3 ConvertVectorToCameraAngles(const Vec3& v) 
{
  return ConvertUnitVectorToCameraAngles( v.normalized() );
}

struct SFilmEventNameToId
{
  const char *psName;
  eFilmEvents eId;
};

// to become data driven
static SFilmEventNameToId aEventToId[]=
{
    {"eNull",eNull},
  // Game hints
    {"eApproachingAction",eApproachingAction},   // Near an action bubble - Probably leads to director invoking tension via foreshadowing
    {"eCombat",eCombat},              // We're in combat
    {"eMelee",eMelee},				        // We're in hand-to-hand
    {"eCarRace",eCarRace},
    {"eCarFight",eCarFight},
    {"eCarCollision",eCarCollision},
    {"eEnemyDeath",eEnemyDeath},			    // Enemy about to die
    {"eFriendDeath",eFriendDeath},			    // Friend about to die
    {"eExplosion",eExplosion},			      // Explosion - if it's nearby with interest objects nearby, may be worth a slow-mo
    {"eNewTarget",eNewTarget},           // Auto Combat has picked a new target
    // Game requests
    {"eCutAway",eCutAway},             // Request for a cut-away
    {"eEndCutAway",eEndCutAway},
    {"eEstablish",eEstablish},           // Request for an establishing shot
    {"eEndEstablish",eEndEstablish},
    {"eDeathCut",eDeathCut},            // Request for a specific death cut
    {"eEndDeathCut",eEndDeathCut},

    //--- Terminate the table with a 0 psName
    {0,eNull}
};

eFilmEvents CFilmEvent::NameToType(const char *psName)
{
  eFilmEvents eRet=eNull;
  SFilmEventNameToId *pI;

  for(pI=aEventToId ; pI->psName ; ++pI)
  {
    if(0==strcmp(pI->psName,psName))
    {
      eRet=pI->eId;
      break;
    }
  }

  return eRet;
}

const char * CFilmEvent::TypeToName(eFilmEvents eType)
{
  const char *psRet="Unknown";
  SFilmEventNameToId *pI;

  for(pI=aEventToId ; pI->psName ; ++pI)
  {
    if(pI->eId==eType)
    {
      psRet=pI->psName;
      break;
    }
  }

  return psRet;
}


CFilmEvent::CFilmEvent() : type(eNull),priority(1.0f),vPos(ZERO)
{
  // init the actor ids to 0.
  int iAct;

  for(iAct=0; iAct < (sizeof(aIdActors)/sizeof(aIdActors[0])) ; ++iAct  )
    aIdActors[iAct]=0;
}


CFilmDirector::CFilmDirector(CGame *pGame)
{
  m_pGame=pGame;

  InitEvents();

  m_pFilmCamera=new CFilmCamera(this,pGame);
  m_pFilmAction=new CFilmAction(this,pGame);
  m_pFilmWorld=new CFilmWorld(this,pGame);

  int iMood;
  for(iMood=0;iMood<eLastMood;++iMood)
  {
    m_direction.mood.aMood[iMood]=0;
  }
  m_direction.mood.aMood[eAdrenaline]=0.01f;

  m_direction.visualIntensity=0.01f;

  m_bActiveCamCut=false;
  m_bChosenShot=false;
  m_progress=0.0f;
  m_dProgress=0.0f;
  m_pChosenSeq=0;
  m_iChosenSeqShot=-1;
  m_bUpdatedSequence=false;
  m_desiredSFXVolume=0.5f;
  m_desiredMusicVolume=0.5f;
  m_shotCurrentRealDuration=0.0f;
}

CFilmDirector::~CFilmDirector()
{
  DeInitEvents();

  delete m_pFilmAction;
  delete m_pFilmCamera;
}

void CFilmDirector::InitEvents()
{
  m_nEvents=0;
}

void CFilmDirector::DeInitEvents()
{
  FlushEvents();
}

void CFilmDirector::FlushEvents()
{
  m_events.erase(m_events.begin(),m_events.end());
  m_nEvents=0;
}

bool CFilmDirector::PeekEvent(CFilmEvent & rEvent)
{
  bool bRet=false;
  if(m_nEvents)
  {
    rEvent=m_events.front();
    bRet=true;
  }
  return bRet;
}

bool CFilmDirector::PopEvent(CFilmEvent & rEvent)
{
  bool bRet=false;
  if(m_nEvents)
  {
    rEvent=m_events.front();
    m_events.pop_front();
    --m_nEvents;
    bRet=true;

    //CryLogAlways(" -pop Event: '%s'",CFilmEvent::TypeToName(rEvent.type));
  }
  return bRet;
}


void CFilmDirector::PushEvent(CFilmEvent & rEvent)
{
  ++m_nEvents;
  m_events.push_back(rEvent);
  //CryLogAlways(" +push Event: '%s'",CFilmEvent::TypeToName(rEvent.type));
}

void CFilmDirector::PushEventFront(CFilmEvent & rEvent)
{
  //CryLogAlways(" +push front Event: '%s'",CFilmEvent::TypeToName(rEvent.type));
  ++m_nEvents;
  m_events.push_front(rEvent);
}

//--- Notifications of game events.
// TODO: remove switch, pass data onto world
void CFilmDirector::HandleNotifyEvents(CFilmEvent & rEv,float frameTime)
{
  switch (rEv.type)
  {
  default: // fallthrough
  case eNull:
    break;
  case eApproachingAction:   // Near an action bubble - Probably leads to director invoking tension via foreshadowing
    {
    } break;
  case eCombat:              // We're in combat
    {
    } break;
  case eMelee:				// We're in hand-to-hand
    {
    } break;
  case eCarRace:
    {
    } break;
  case eCarFight:
    {
    } break;
  case eCarCollision:
    {
    } break;
  case eEnemyDeath:			// Enemy about to die
    {
    } break;
  case eFriendDeath:			// Friend about to die
    {
    } break;
  case eExplosion:			// Explosion - if it's nearby with interest objects nearby, may be worth a slow-mo
    {
    } break;
  case eNewTarget:
    {
      if(!m_pChosenSeq)
      {

      m_evChosenShot=rEv;
      m_bChosenShot=true;
      m_evEndChosen.type=eEndDeathCut;
      m_evEndChosen.aIdActors[0]=rEv.aIdActors[0];
      m_evEndChosen.aIdActors[1]=rEv.aIdActors[1];
      m_evEndChosen.aIdActors[2]=rEv.aIdActors[2];
      m_evEndChosen.aIdActors[3]=rEv.aIdActors[3];
      m_evEndChosen.vPos=rEv.vPos;
      m_chosenDuration=2.00f;

      }

    } break;
  }
}

float CFilmDirector::GetIntensity()
{
  float intensity=0.0f;

  CPlayerG4 *pPlayer;
  pPlayer=g_pGame04->GetClientPlayer();

  float progressFactor=min(1.0f,m_progress*0.40f + min(m_dProgress,0.5f));
  float healthFactor=0.0f;

  int health=pPlayer->GetHealth();

  if(health<49)
  {
    healthFactor=(7-sqrtf((float)health))/7.0f;
  }

  intensity=min(1.0f,(progressFactor+healthFactor)*0.75f);

  return intensity;
}

//--- Game requests for shot coverage.
// TODO: remove switch, pass data onto world. remove embedded state.
void CFilmDirector::HandleRequestEvents(CFilmEvent & rEv,float frameTime)
{
  CPlayerG4 *pPlayer;
  pPlayer=g_pGame04->GetClientPlayer();

  if(pPlayer)
  {
    switch (rEv.type)
    {
    default: // fallthrough
    case eNull:
      break;
    case eCutAway:             // Request for a cut-away
      {
        if(!m_pChosenSeq)
        {
        m_evChosenShot=rEv;
        m_bChosenShot=true;
        m_evEndChosen.type=eEndCutAway;
        m_evEndChosen.aIdActors[0]=rEv.aIdActors[0];
        m_evEndChosen.aIdActors[1]=rEv.aIdActors[1];
        m_evEndChosen.aIdActors[2]=rEv.aIdActors[2];
        m_evEndChosen.aIdActors[3]=rEv.aIdActors[3];
        m_evEndChosen.vPos=rEv.vPos;
        m_chosenDuration=5.00f;
        }
      } break;
    case eEndCutAway:
      {
        // End shots should happen immediately.
        //pPlayer->EndPlayerCameraCut();
        m_pFilmCamera->NotifyEndShot();
        m_bActiveCamCut=false;
      } break;
    case eEstablish:           // Request for an establishing shot
      {
        if(!m_pChosenSeq)
        {
        m_evChosenShot=rEv;
        m_bChosenShot=true;
        m_evEndChosen.type=eEndEstablish;
        m_evEndChosen.aIdActors[0]=rEv.aIdActors[0];
        m_evEndChosen.aIdActors[1]=rEv.aIdActors[1];
        m_evEndChosen.aIdActors[2]=rEv.aIdActors[2];
        m_evEndChosen.aIdActors[3]=rEv.aIdActors[3];
        m_evEndChosen.vPos=rEv.vPos;
        m_chosenDuration=4.00f;
        }
      } break;
    case eEndEstablish:
      {
        // End shots should happen immediately.
        //pPlayer->EndPlayerCameraCut();
        m_pFilmCamera->NotifyEndShot();
        m_bActiveCamCut=false;
      } break;
    case eDeathCut:           // Request for a specific death cut
      {
        const SFilmShotSeq *pSeq;
        if(!m_pChosenSeq)
        {
          pSeq=m_pFilmCamera->GetShotSeqForEvent(rEv);
          SetSequence(pSeq);
        
          m_evChosenShot=rEv;
          m_bChosenShot=true;
          m_evEndChosen.type=eEndDeathCut;
          m_evEndChosen.aIdActors[0]=rEv.aIdActors[0];
          m_evEndChosen.aIdActors[1]=rEv.aIdActors[1];
          m_evEndChosen.aIdActors[2]=rEv.aIdActors[2];
          m_evEndChosen.aIdActors[3]=rEv.aIdActors[3];
          m_evEndChosen.vPos=rEv.vPos;
          m_chosenDuration=2.00f; 
        }
      } break;
    case eEndDeathCut:
      {
        // End shots should happen immediately.
        //pPlayer->EndPlayerCameraCut();
        m_pFilmCamera->NotifyEndShot();
        m_bActiveCamCut=false;
      } break;
    }
    m_chosenDuration*=DURATION_SCALE;
  }
}


void CFilmDirector::OnEditorSetGameMode(bool bGameMode)
{
  if(!bGameMode)
  {
    //if(m_bActiveCamCut)
    {
      m_pFilmCamera->NotifyEndShot(); // turn off shot effects..
    }
    m_chosenDuration=m_activeDuration=0.0f;
    m_pChosenSeq=0;
    m_iChosenSeqShot=-1;
    m_pFilmCamera->RequestShot(0);
    m_shotCurrentRealDuration=0.0f;
    SlowMoEnd();

    m_pFilmCamera->EnableEffects(false);
    m_pFilmCamera->OnEditorSetGameMode(bGameMode);
  }
  else
  {
    m_pFilmCamera->EnableEffects(true);
  }
}

//--- Do the chosen shot.
// remove switch. evaluate from world data
void CFilmDirector::StartChosenShot()
{
  CPlayerG4 *pPlayer;
  CFilmEvent & rEv=m_evChosenShot;
  pPlayer=g_pGame04->GetClientPlayer();

  m_activeCamCut.rand1=rand();
  m_activeCamCut.rand2=rand();
  m_activeCamCut.rand3=rand();
  m_activeCamCut.progress=m_progress;

//  CFilmShotCast cast;
  //m_pFilmCamera->SetCast()

  if(pPlayer && m_bChosenShot)
  {
    switch (rEv.type)
    {
    default: // fallthrough
    case eNull:
      break;
    case eApproachingAction:   // Near an action bubble - Probably leads to director invoking tension via foreshadowing
      {
      } break;
    case eCombat:              // We're in combat
      {
      } break;
    case eMelee:				// We're in hand-to-hand
      {
      } break;
    case eCarRace:
      {
      } break;
    case eCarFight:
      {
      } break;
    case eCarCollision:
      {
      } break;
    case eEnemyDeath:			// Enemy about to die
      {
      } break;
    case eFriendDeath:			// Friend about to die
      {
      } break;
    case eExplosion:			// Explosion - if it's nearby with interest objects nearby, may be worth a slow-mo
      {
      } break;
    case eNewTarget:
      {
        //pPlayer->BeginPlayerCameraCut();
        m_pFilmCamera->NotifyBeginShot();
        float timeStep=1.0f;
        float durationScale=1.0f;

        pPlayer->m_bCameraCut=true;
        m_bActiveCamCut=true;
        m_activeDuration=m_chosenDuration*durationScale;
        m_evEndActive=m_evEndChosen;


        //m_pPlayer->GetGame()->cl_ThirdPersonRange->Set(-1.0f);
        //GetISystem()->GetIConsole()->GetCVar("cl_tpvDist")->Set(8.0f);  //-1.0f



        Vec3 vPos2=rEv.vPos;
        //float theta=DEG2RAD(rand()%360);
        //float phi=DEG2RAD((rand()%21)-10);
        //float radius=(float)((rand()&15)/13.0f);
        //float radius=(float)((rand()&31)/16.0f+1.0f); // dist of 1..3 metres

        //vPos2.x+=(float)(cos(thirdPersonYaw));
        //vPos2.y+=(float)(sin(thirdPersonYaw));
        //vPos2.z+=(float)(sin(thirdPersonPitch));

        //Vec3 diff=(rEv.vPos-vPos2);

        //GetISystem()->GetIConsole()->GetCVar("cl_tpvDist")->Set(8.0f);  //-1.0f

        //vPos2-=(diff.normalized())*thirdPersonDistance;//2.0f;

        m_activeCamCut.vPos=vPos2;
        //m_activeCamCut.vAng=ConvertVectorToCameraAngles(diff);
        m_activeCamCut.vAng=Ang3(0,0,0);

        //--- Views here...
        // GetActiveView:
        //IView *pView=m_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
        //----
        //IEntityCamera *pEC=m_pPlayer->GetEntity()->GetCamera();
        //
        //pEC->SetPos(vPos2);
        //Vec3 vNewAng=ConvertVectorToCameraAngles(diff);			
        //pEC->SetAngles(vNewAng);	
        //
        //m_pPlayer->GetGame()->GetMyPlayer()->SetAngles(vNewAng);
      } break;      
    //--- Requested shots
    case eCutAway:             // Request for a cut-away
      {
      } break;
    case eEndCutAway:
      break;
    case eEstablish:           // Request for an establishing shot
      {
      } break;
    case eEndEstablish:
      break;
    case eDeathCut:
      {
        //pPlayer->BeginPlayerCameraCut();
        m_pFilmCamera->NotifyBeginShot();
        float timeStep=1.0f;
        float durationScale=1.0f;
        Vec3 vPlayer=g_pGame04->GetClientPlayer()->GetEntity()->GetWorldPos();

        pPlayer->m_bCameraCut=true;
        m_bActiveCamCut=true;
        m_activeDuration=m_chosenDuration*durationScale;
        m_evEndActive=m_evEndChosen;

        //m_pPlayer->GetGame()->cl_ThirdPersonRange->Set(-1.0f);
        GetISystem()->GetIConsole()->GetCVar("cl_tpvDist")->Set(8.0f);  //-1.0f

        Vec3 vPos2=rEv.vPos;
        float theta=DEG2RAD(rand()%360);
        float phi=DEG2RAD((rand()%21)-10);
        //float radius=(float)((rand()&15)/13.0f);
        float radius=(float)((rand()&31)/16.0f+1.0f); // dist of 1..3 metres
        
        vPos2.x+=(float)(cos(theta))*radius;
        vPos2.y+=(float)(sin(theta))*radius;
        vPos2.z+=(float)(sin(phi))*radius;

        Vec3 diff=(rEv.vPos-vPos2);

        diff=vPlayer-rEv.vPos;
        //GetISystem()->GetIConsole()->GetCVar("cl_tpvDist")->Set(8.0f);  //-1.0f

        vPos2-=(diff.normalized())*radius;//2.0f;

        //m_activeCamCut.vPos=vPos2;
        m_activeCamCut.vPos=rEv.vPos;
        if(diff.len2()>0.01f)
          m_activeCamCut.vAng=ConvertVectorToCameraAngles(diff);
        else
          m_activeCamCut.vAng=Ang3(0,0,180);

        CFilmShotCast cast;
        memset(&cast,0,sizeof(cast));
        int iCast,nCast;

        nCast=min( (sizeof(cast.aIdActors)/sizeof(cast.aIdActors[0])) , (sizeof(rEv.aIdActors)/sizeof(rEv.aIdActors[0])) );

        for(iCast=0 ; iCast<nCast ; ++iCast)
          cast.aIdActors[0]=rEv.aIdActors[0];
        m_pFilmCamera->SetCast(cast);

        //--- Views here...
        // GetActiveView:
        //IView *pView=m_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
        //----
        //IEntityCamera *pEC=m_pPlayer->GetEntity()->GetCamera();
        //
        //pEC->SetPos(vPos2);
        //Vec3 vNewAng=ConvertVectorToCameraAngles(diff);			
        //pEC->SetAngles(vNewAng);	
        //
        //m_pPlayer->GetGame()->GetMyPlayer()->SetAngles(vNewAng);
      } break;
    case eEndDeathCut:
      break;
    }

    m_shotCurrentRealDuration=0.0f;
  }
  m_bChosenShot=false;
}

void CFilmDirector::PlayerDeleted(CPlayerG4 *pPlayer)
{
  m_pFilmCamera->PlayerDeleted(pPlayer);
}

// This is where the IView is actually updated.
bool CFilmDirector::TickCamera(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
  bool bRet;

  bRet=m_pFilmCamera->TickCamera(pPlayer,viewParams);

  return bRet;
}

void CFilmDirector::EditKey(const char *psName,float val)
{
  m_pFilmCamera->EditKey(psName,val);
}

void CFilmDirector::StartChosenSequence()
{
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();

  m_pFilmCamera->NotifyBeginShot();
  pPlayer->m_bCameraCut=true;
  m_bActiveCamCut=true;
  m_evEndActive=m_evEndChosen;
}

void CFilmDirector::SetSequence(const SFilmShotSeq *pSeq)
{
  m_pChosenSeq=pSeq;
  m_iChosenSeqShot=-1;

  //if(pSeq)
  //  CryLogAlways("Seq('%s') START",pSeq->psName); 
}

void CFilmDirector::SlowMo(eSlowMoRate mode,eSlowMoEase ease)
{
  const char *psMode="none";
  if(eSlowMo_None == mode)
  {
    SlowMoEnd();
  }
  else
  {
    float chance=GetISystem()->GetIConsole()->GetCVar("cl_cam_slowmo_pct")->GetFVal();
    //bool bSlowMo=!m_bSlowMo && ((rand()%100)<chance);
    //m_bSlowMo=bSlowMo;
    bool bSlowMo=((rand()%100)<chance);
    m_bSlowMo=bSlowMo;

    bool bSlowMoEnabled=GetISystem()->GetIConsole()->GetCVar("cl_cam_slowmo_on")->GetIVal() ? true : false;

    float rate=1.0f;
    switch(mode)
    {
    case eSlowMo_Stretch:       // action takes slightly longer, not noticeable as slow mo
      rate=1.1f;
      psMode="Stretch";
      break;
    case eSlowMo_StretchMore:   // action takes longer still, may seem a little drawn out
      rate=1.2f;
      psMode="StretchMore";
      break;
    case eSlowMo_Med:           // definate slow mo : half speed
      rate=2;
      psMode="Med";
      break;
    case eSlowMo_High:          // quarter speed
      rate=8;
      psMode="High";
      break;
    case eSlowMo_VHigh:         // eighth
      rate=16;
      psMode="VHigh";
      break;
    case eSlowMo_Extreme:       // 20 times longer. ie a 3 sec event takes 1 min, sound would only travel 16 metres per sec!
      rate=20;
      psMode="Extreme";
      break;
    case eSlowMo_Compress:      // Things happen a little quicker, not noticeable
      rate=1/1.1f;
      psMode="Compress";
      break;
    case eSlowMo_CompressMore:  // Things happen quicker still, may seem slightly fast
      rate=1/1.2f;
      psMode="CompressMore";
      break;
    case eSlowMo_QuickTime:     // Noticeably fast
      rate=1/1.4f;
      psMode="QuickTime";
      break;
    case eSlowMo_DoubleSpeed:   // Double speed, ** cartoony **
      rate=1/2.0f;
      psMode="DoubleSpeed";
      break;
    }

    rate=1/rate;

    switch(ease)
    {
    case eSlowMoEase_Hold:                 // same slowmo rate through shot, no ease in/out
      break;
    case eSlowMoEase_SlideIn:              // ease in to the slowmo from real time
      break;
    case eSlowMoEase_SlideInFast:          // fast ease in from real time
      break;
    case eSlowMoEase_SlideOut:             // ease out from slowmo to real time
      break;
    case eSlowMoEase_SlideOutFast:         // fast ease out from slowmo to real time
      break;
    case eSlowMoEase_SlideInHoldOut:       // ease in, hold, then ease out
      break;
    case eSlowMoEase_SlideInHoldOutFast:   // fast ease in, hold, then fast ease out
      break;
    case eSlowMoEase_SlideOutHoldIn:       // ease out, hold, then ease in
      break;
    }

    if(m_bSlowMo && bSlowMoEnabled)
    {
      //float timeStep=GetISystem()->GetIConsole()->GetCVar("cl_cam_slowmo")->GetFVal();
      //if(timeStep<0.001f)
      //  timeStep=0.001f;
      GetISystem()->GetIConsole()->GetCVar("time_scale")->Set(rate);
      CryLogAlways(" SlowMo: %s (%0.4f)",psMode,rate);
    }
    else
    {
      float baseRate=GetISystem()->GetIConsole()->GetCVar("g_time_baseRate")->GetFVal();
      GetISystem()->GetIConsole()->GetCVar("time_scale")->Set(baseRate);
    }
  }
}
void CFilmDirector::SlowMoEnd()
{
  float baseRate=GetISystem()->GetIConsole()->GetCVar("g_time_baseRate")->GetFVal();
  GetISystem()->GetIConsole()->GetCVar("time_scale")->Set(baseRate);
}

float CFilmDirector::GetSlowMoScale()
{
  float fRet=1.0f;

  bool bSlowMoEnabled=GetISystem()->GetIConsole()->GetCVar("cl_cam_slowmo_on")->GetIVal() ? true : false;

  if(bSlowMoEnabled)
  {
    fRet=GetISystem()->GetIConsole()->GetCVar("time_scale")->GetFVal();
  }
  else
  {
    fRet=1.0f;
  }

  return fRet;
}



void CFilmDirector::TickSequence()
{
  if(m_pChosenSeq && !m_bActiveCamCut && !m_bUpdatedSequence)
  {
    m_bChosenShot=false;
    m_bUpdatedSequence=true;
    ++m_iChosenSeqShot;
    if(m_iChosenSeqShot<8)
    {
      m_pFilmCamera->SetCast(m_evChosenShot);
      float deltaScale=((rand()%21)-10)/10.0f *0.5f;    // *0.5f  half the duration of the deltas
      if(m_pChosenSeq->aSeqShot[m_iChosenSeqShot].psShot)
      {
        m_shotCurrentRealDuration=0.0f;
        m_pFilmCamera->RequestShot(m_pChosenSeq->aSeqShot[m_iChosenSeqShot].psShot);
        CryLogAlways("Seq('%s') shot(%d) '%s' (%f,%f)",m_pChosenSeq->psName,m_iChosenSeqShot,m_pChosenSeq->aSeqShot[m_iChosenSeqShot].psShot,m_chosenDuration-m_activeDuration,m_chosenDuration);
        m_chosenDuration=m_activeDuration=m_pChosenSeq->aSeqShot[m_iChosenSeqShot].time+deltaScale*m_pChosenSeq->aSeqShot[m_iChosenSeqShot].timeDelta;

        //if(eSlowMo_None != m_pChosenSeq->aSeqShot[m_iChosenSeqShot].slowMo)
          SlowMo(m_pChosenSeq->aSeqShot[m_iChosenSeqShot].slowMoRate,m_pChosenSeq->aSeqShot[m_iChosenSeqShot].slowMoEase);
        //else
        //  SlowMoEnd();
      }
      else if(m_pChosenSeq->aSeqShot[m_iChosenSeqShot].apsShot)
      {
        const char *psShot=m_pChosenSeq->aSeqShot[m_iChosenSeqShot].apsShot[m_pChosenSeq->aSeqShot[m_iChosenSeqShot].iShot];
        m_shotCurrentRealDuration=0.0f;
        m_pFilmCamera->RequestShot(psShot);
        CryLogAlways("Seq('%s') shot(%d) '%s' (%f/%f)",m_pChosenSeq->psName,m_iChosenSeqShot,psShot,m_chosenDuration-m_activeDuration,m_chosenDuration);
        m_chosenDuration=m_activeDuration=m_pChosenSeq->aSeqShot[m_iChosenSeqShot].time+deltaScale*m_pChosenSeq->aSeqShot[m_iChosenSeqShot].timeDelta;

//        if(eSlowMo_None != m_pChosenSeq->aSeqShot[m_iChosenSeqShot].slowMo)
          //SlowMo();
          SlowMo(m_pChosenSeq->aSeqShot[m_iChosenSeqShot].slowMoRate,m_pChosenSeq->aSeqShot[m_iChosenSeqShot].slowMoEase);
//        else
//          SlowMoEnd();
      }
      else
      {
        CryLogAlways("Seq('%s') finished. (no shot:%d) (%0.3f/%0.3f)",m_pChosenSeq->psName,m_iChosenSeqShot,m_chosenDuration-m_activeDuration,m_chosenDuration);
        m_pChosenSeq=0;
        m_iChosenSeqShot=-1;
        m_pFilmCamera->RequestShot(0);
        m_chosenDuration=m_activeDuration=0.0f;
        m_shotCurrentRealDuration=0.0f;
        SlowMoEnd();
      }
      m_chosenDuration*=DURATION_SCALE;
      m_activeDuration*=DURATION_SCALE;
    }
    else
    {
      m_chosenDuration=m_activeDuration=0.0f;
      m_pChosenSeq=0;
      m_iChosenSeqShot=-1;
      m_pFilmCamera->RequestShot(0);
      m_shotCurrentRealDuration=0.0f;
      SlowMoEnd();
    }
  }
}

void CFilmDirector::Update(float frameTime)
{
  CFilmEvent ev;

  m_bUpdatedSequence=false;

  m_pFilmWorld->Update(frameTime);

  float prevProgress=m_progress;
  m_progress=m_pFilmWorld->GetBubbleProgress();
  m_dProgress=(m_dProgress+((m_progress-prevProgress)/frameTime)*0.5f)-m_dProgress/(60*frameTime);

  m_pFilmCamera->Update(frameTime);

  // Update active camera cut
  if(m_bActiveCamCut)
  {
    int holdShot(GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal());
    float timeScale=GetSlowMoScale(); //GetISystem()->GetIConsole()->GetCVar("cl_cam_slowmo_duration")->GetFVal();
    float durationScale=GetISystem()->GetIConsole()->GetCVar("cl_cam_timescale")->GetFVal();
    durationScale = (durationScale < 0.1f) ? 0.1f : (durationScale > 100) ? 100.0f : durationScale;

    if(0==holdShot)
    {
      if(m_bSlowMo)
      {
        m_activeDuration-=(frameTime/timeScale)/durationScale;
        m_shotCurrentRealDuration+=frameTime/timeScale;
      }
      else
        m_activeDuration-=frameTime/durationScale;

      if(m_activeDuration<=0.0f)
      {
        m_activeDuration=0.0f;
        PushEventFront(m_evEndActive);

      }
    }
  }

  while(PopEvent(ev))
  {
    if(!m_bActiveCamCut)
    {
      HandleRequestEvents(ev,frameTime);
      TickSequence();
      HandleNotifyEvents(ev,frameTime);
    }
    else
    {
      HandleRequestEvents(ev,frameTime);
      TickSequence();
      HandleNotifyEvents(ev,frameTime);
    }
    TickSequence();
  }

  if(m_pChosenSeq && !m_bActiveCamCut)
  {
    StartChosenSequence();
  }
  else if(m_bChosenShot && !m_bActiveCamCut)
  {
    StartChosenShot();

    int iEditHold=1;
    //GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->Set(iEditHold);
    //g_pGame04->GetDirector()->EditKey("start",iEditHold);
  }

  if(m_bActiveCamCut)
  {
    //CryLogAlways( "CFilmDirector:Update: In camera cut.");
  }

  TickSound(frameTime);
}

void CFilmDirector::ShotFailed(SFilmCamShotNode *pNode,bool bCritical)
{
  // If we've started the shot, and it's not a reality breaking view(bCritical), then hold it for at least this amount of time.
  const float minShotTime=0.4f;
  if((m_shotCurrentRealDuration=0.0f) || (m_shotCurrentRealDuration>=minShotTime) || bCritical)
  {
    // shot not started, or already exceeded min safe shot time
    CFilmEvent ev;
    ev.type=eEndDeathCut;
    Event(ev);
  }
}

void CFilmDirector::TickSound(float dt)
{
  float maxDeltaSound=GetISystem()->GetIConsole()->GetCVar("cl_cam_soundrate")->GetFVal();
  float tScale=GetISystem()->GetIConsole()->GetCVar("time_scale")->GetFVal();
  if(tScale<0.001f) tScale=0.001f;
  float realDt=dt/tScale;
  float desiredSfx=GetISystem()->GetIConsole()->GetCVar("cl_cam_soundsfx")->GetFVal();
  float desiredMusic=GetISystem()->GetIConsole()->GetCVar("cl_cam_soundmusic")->GetFVal();

float sampleRate;
if(desiredMusic>0.001f)   // hack has nothing related to desired music volume
{
  sampleRate=44100.0f*(1+tScale)*0.5f;  // lets not reduce/increase the pitch quite as musch as the slowmo
}
else
{
  sampleRate=44100.0f;
}
  GetISystem()->GetIConsole()->GetCVar("s_SampleRate")->Set((int)sampleRate);

  if(m_pChosenSeq || m_bChosenShot || m_bActiveCamCut)  // Boost sound for cuts or cut sequences
  {
    m_desiredSFXVolume=1.0f;
    m_desiredMusicVolume=desiredMusic;
  }
  else      // Restore sound outside cuts /cut sequences
  {
    m_desiredSFXVolume=desiredSfx;
    m_desiredMusicVolume=desiredMusic;
  }
  float currentSfx=GetISystem()->GetIConsole()->GetCVar("s_SFXVolume")->GetFVal();
  //float musicScale=GetISystem()->GetIConsole()->GetCVar("cl_cam_soundmusic")->GetFVal();

  if(abs(currentSfx-m_desiredSFXVolume)>0.01f)
  {
    if(currentSfx>m_desiredSFXVolume)
    {
      currentSfx=max((currentSfx-maxDeltaSound*realDt),m_desiredSFXVolume);
    }
    else
    {
      currentSfx=min((currentSfx+maxDeltaSound*realDt),m_desiredSFXVolume);
    }
    GetISystem()->GetIConsole()->GetCVar("s_SFXVolume")->Set(currentSfx);
  }
  GetISystem()->GetIConsole()->GetCVar("s_MusicVolume")->Set(m_desiredMusicVolume);
}

void CFilmDirector::Event(CFilmEvent ev)
{
  PushEvent(ev);
}

//----------------------------------------------------------------------------------------------------------



//----------------------------------------------------------------------------------------------------------
// Select a specific shot.
void CFilmDirector::SetShot(const char *psShot)
{
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  if(pPlayer)
  {
    CryLogAlways(" CFilmDirector:SetShot: '%s'",psShot);
    //pPlayer->BeginPlayerCameraCut();
    m_pFilmCamera->NotifyBeginShot();
    m_bActiveCamCut=true;
    //pPlayer->EndPlayerCameraCut();
    //m_bActiveCamCut=false;
    m_pFilmCamera->SetShot(psShot);
  }
}

void CFilmDirector::DumpShots()
{
  m_pFilmCamera->DumpShots();
}

