/* Autor: Leandro Bernsmüller
Funcoes para controle da partida: loop principal, controle de eventos,
cenario e objetos.
Arquivos complementares: apl.cpp, exibe.cpp  */



#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <d3drmwin.h>
#include <mmsystem.h>
#include <dsound.h>
#include "defs.h"
#include "..\extra\ddutil.h"
#include "..\extra\ddmm.h"
#include "..\extra\arqs.h"
#include "..\cenario\gr.h"
#include "..\cenario\cenario.h"
#include "..\cenario\cenfis.h"
#include "..\som\sons.h"
#include "..\ia\acoes\acao.h"
#include "..\ia\dados\dados.h"
#include "..\ia\agente.h"
#include "..\ia\agencia.h"
#include "..\interf\janela.h"
#include "..\interf\jancen.h"
#include "..\interf\dlgconpa.h"
#include "..\interf\dlgconf.h"
#include "..\interf\dlgmodo.h"
#include "..\interf\dlgsobre.h"
#include "..\interf\dlgconco.h"
#include "..\interf\dlgesper.h"
#include "..\comunic\sockcomu.h"
#define IDIRECTPLAY2_OR_GREATER
#include <dplay.h>
#include "..\comunic\dplayinf.h"
#include "..\comunic\dpcomu.h"
#include "apl.h"



// pre-declaracoes 
extern CApl oJOGO;



/* Executa interacoes
**********************************************************/
VOID CALLBACK TimerProc (
                         HWND  hwnd,     // handle of window for timer messages 
                         UINT  uMsg,     // WM_TIMER message
                         UINT  idEvent,  // timer identifier
                         DWORD  dwTime ) // current system time
{
   oJOGO.mExecEventosFase ( 1 );
}



/* Espera um tempo antes do inicio efetivo da partida
**********************************************************/
/*VOID CALLBACK TimerProc2 (
                         HWND  hwnd,     // handle of window for timer messages 
                         UINT  uMsg,     // WM_TIMER message
                         UINT  idEvent,  // timer identifier
                         DWORD  dwTime ) // current system time
{
   oJOGO.m_yEstadoExecucao = 1;
   KillTimer ( oJOGO.m_oJanPrincipal.m_hWnd, 3 );
}*/



/* Define a quantidade de ferramentas que um personagem possui.
dwPers - o personagem
padwNumFerramentas - seu conteudo eh alterado, devolve numero
de ferramentas
**********************************************************/
void CApl::mDefineNumFerramentas ( DWORD dwPers, DWORD* padwNumFerramentas )
{
   switch ( dwPers ) {
      
      case ID_PERS_MAE_NATUREZA:
         padwNumFerramentas[ 0 ] = (DWORD) afNUM_FERRAMENTAS_MAE_NATUREZA [ 0 ];
         padwNumFerramentas[ 1 ] = (DWORD) afNUM_FERRAMENTAS_MAE_NATUREZA [ 1 ];
         padwNumFerramentas[ 2 ] = (DWORD) afNUM_FERRAMENTAS_MAE_NATUREZA [ 2 ];
         padwNumFerramentas[ 3 ] = (DWORD) afNUM_FERRAMENTAS_MAE_NATUREZA [ 3 ];
         break;
      
      case ID_PERS_PESCADOR:
         padwNumFerramentas[ 0 ] = (DWORD) afNUM_FERRAMENTAS_PESCADOR [ 0 ];
         padwNumFerramentas[ 1 ] = (DWORD) afNUM_FERRAMENTAS_PESCADOR [ 1 ];
         padwNumFerramentas[ 2 ] = (DWORD) afNUM_FERRAMENTAS_PESCADOR [ 2 ];
         padwNumFerramentas[ 3 ] = (DWORD) afNUM_FERRAMENTAS_PESCADOR [ 3 ];
         break;
      
      case ID_PERS_PREFEITO:
         padwNumFerramentas[ 0 ] = (DWORD) afNUM_FERRAMENTAS_PREFEITO [ 0 ];
         padwNumFerramentas[ 1 ] = (DWORD) afNUM_FERRAMENTAS_PREFEITO [ 1 ];
         padwNumFerramentas[ 2 ] = (DWORD) afNUM_FERRAMENTAS_PREFEITO [ 2 ];
         padwNumFerramentas[ 3 ] = (DWORD) afNUM_FERRAMENTAS_PREFEITO [ 3 ];
         break;
      
      case ID_PERS_TURISTA:
         padwNumFerramentas[ 0 ] = (DWORD) afNUM_FERRAMENTAS_TURISTA [ 0 ];
         padwNumFerramentas[ 1 ] = (DWORD) afNUM_FERRAMENTAS_TURISTA [ 1 ];
         padwNumFerramentas[ 2 ] = (DWORD) afNUM_FERRAMENTAS_TURISTA [ 2 ];
         padwNumFerramentas[ 3 ] = (DWORD) afNUM_FERRAMENTAS_TURISTA [ 3 ];
         break;
   }
}



/* Inicia uma partida - usado se eh servidor ou jogo sem rede.
retorna TRUE - certo / FALSE - erro
**********************************************************/
BOOL CApl::mIniciaPartida ()
{
   // somente pode iniciar partida se estah na tela inicial ou terminou uma partida
   if ( m_yEstadoExecucao == 0 || m_yEstadoExecucao == 3 ) {

      // dados iniciais sobre a partida
      if ( ! FazDialogo_ConfiguraPartida ( m_oJanPrincipal.m_hWnd, m_dwQuemJogador1, m_szNomeArqFase ) )
         // cancelou inicio da partida
         return FALSE;

      // pede dados iniciais sobre a partida (do colega), se nao estah
      // jogando em rede
      if ( ! bUSANDO_REDEDP ) {
         //MSGSTR ( STR_ESCOLHER_PERSONAGEM_COLEGA );
         if ( ! FazDialogo_ConfiguraPartidaColega ( m_oJanPrincipal.m_hWnd, m_dwQuemJogador1, m_dwQuemJogador2 ) )
            // cancelou
            return FALSE;
      }

      // atualiza bmps dos personagens
      mAlteraBmpsPersonagens ( m_dwQuemJogador1, m_dwQuemJogador2 );
   }
   else {
      // nao pode iniciar partida agora
      MSGSTR ( STR_ERRO_INICIO_PARTIDA_DURANTE );
      return FALSE;
   }

   //m_oSons.mPara ( 0 );

   // inicia gerador de numeros aleatorios
   DWORD
      dwTickIni = ::GetTickCount ();
   srand ( dwTickIni );

   // le arquivo de fase - se falhou exibe abertura
   if ( ! CarregaFase ( m_szNomeArqFase, m_astEventosFase ) ) {
      mExibeAbertura ();
      return FALSE;
   }

   // servidor: envia mensagem com dados da fase para cliente
   if ( bUSANDO_REDEDP && DPInfo.bIsHost ) {

      // aloca:
      // 1o. DWORD - semente
      // 2o. DWORD - ID personagem
      // resto     - eventos da fase
      void
         *pvMsg;
      DWORD
         dwTamanho = sizeof ( DWORD ) +   // semente
                     sizeof ( DWORD ) +   // ID do personagem do servidor
                     MAX_EVENTOS_FASE * sizeof ( tagEventoTempo ); // eventos da fase
      pvMsg = GlobalAllocPtr ( GHND, dwTamanho );

      // semente
      * ( (DWORD*) pvMsg ) = dwTickIni;

      // ID personagem
      ( (DWORD*) pvMsg ) [ 1 ] = m_dwQuemJogador1;

      // copia eventos da fase
      for ( DWORD dwEv = 0; dwEv < MAX_EVENTOS_FASE; dwEv ++ ) {
         ( (DWORD*) pvMsg ) [ 2 + dwEv * 2 + 0 ] = m_astEventosFase [ dwEv ].dwTempo;
         ( (DWORD*) pvMsg ) [ 2 + dwEv * 2 + 1 ] = m_astEventosFase [ dwEv ].dwEvento;
      }

      // manda
      SendGeneralMessage ( APPMSG_DADOS_SERVIDOR, dwTamanho, pvMsg );
      
      // libera
      GlobalFreePtr ( pvMsg );

      // aguarda cliente escolher o seu personagem
      bCLIENTE_ESCOLHEU_PERSONAGEM = FALSE;
      if ( ! FazDialogo_Espera ( m_hInstancia, STR_ESPERA_PERSONAGEM_CLIENTE, &bCLIENTE_ESCOLHEU_PERSONAGEM ) ) {
         SendGeneralMessage ( APPMSG_SERVIDOR_DESISTIU_ESPERA_PERSONAGEM, 0, NULL );
         return FALSE;
      }
   }

   return mIniciaDadosPartida ();
}



/* Suspende/dessuspende temporariamente a partida
retorna TRUE - certo / FALSE - erro
**********************************************************/
BOOL CApl::mSuspendePartida ()
{
   // se estah bloqueado ou em andamento
   if ( m_yEstadoExecucao == 1 || m_yEstadoExecucao == 2 ) {

      // muda para bloqueado/desbloqueado
      if ( m_yEstadoExecucao != 2 )
         m_yEstadoExecucao = 2;
      else
         m_yEstadoExecucao = 1;

      // marca/desmarca item do menu
      HMENU
         hMenu = ::GetMenu ( m_oJanPrincipal.m_hWnd );
      ::CheckMenuItem ( hMenu, ID_ARQUIVO_PAUSA, ( m_yEstadoExecucao == 2 ) ? MF_CHECKED : MF_UNCHECKED );
   }

   return TRUE;
}



/* Termina a partida
**********************************************************/
BOOL CApl::mTerminaPartida ()
{
   // se fase jah havia terminado
   if ( m_yEstadoExecucao == 3 )
      return TRUE;

   // se estava suspensa, remove suspensao
   if ( m_yEstadoExecucao == 2 )
      mSuspendePartida ();

   // exibe objeto: termina partida
   m_nNdxObjetoMensagem = m_oCen.CCenario::mCriaObjeto (
                        2, NDX_MODELO_PARTIDA_TERMINOU, 0, 0, 0,
                                                        0, 0, 0,
                                                        0, 0, 0, 0 );

   // indica termino
   m_yEstadoExecucao = 3;

   m_oSons.mToca ( 2, 0 );

   // servidor: envia mensagem avisando que terminou partida para cliente
   if ( bUSANDO_REDEDP && DPInfo.bIsHost )
      SendGeneralMessage ( APPMSG_SERVIDOR_TERMINOU_PARTIDA, 0, NULL );

   return TRUE;
}



/* Chamado quando um cliente recebe aviso do inicio de uma
fase pelo servidor.
dwSemente - semente do random
dwIDPersonagemServ - personagem do servidor
pstEventos - eventos da fase
**********************************************************/
void CApl::mIniciaPartidaCliente ( DWORD dwSemente, DWORD dwIDPersonagemServ, tagEventoTempo* pstEventos )
{
   //m_oSons.mPara ( 0 );

   // semente
   srand ( dwSemente );
   
   // ID personagem servidor
   m_dwQuemJogador2 = dwIDPersonagemServ;

   // copia eventos da fase
   for ( DWORD dwEv = 0; dwEv < MAX_EVENTOS_FASE; dwEv ++ ) {
      m_astEventosFase [ dwEv ].dwTempo  = pstEventos [ dwEv ].dwTempo;
      m_astEventosFase [ dwEv ].dwEvento = pstEventos [ dwEv ].dwEvento;
   }

   // pede dados iniciais sobre a partida
   FazDialogo_ConfiguraPartidaColega ( m_oJanPrincipal.m_hWnd, m_dwQuemJogador2, m_dwQuemJogador1 );

   // verifica se servidor desistiu de esperar o personagem do cliente
   // REVER - se servidor pressionasse cancel o flag deveria ser setado e parar aqui inicio
   if ( bSERVIDOR_DESISTIU_DE_ESPERAR_PERSONAGEM ) {

      bSERVIDOR_DESISTIU_DE_ESPERAR_PERSONAGEM = FALSE;
      mExibeAbertura ();
      return;
   }

   // envia mensagem para servidor informando qual o personagem do cliente

   // prepara dados para transferencia

   // aloca
   void
      *pvMsg;
   DWORD
      dwTamanho = sizeof ( DWORD );
   pvMsg = GlobalAllocPtr ( GHND, dwTamanho );

   // ID personagem
   * ( (DWORD*) pvMsg ) = m_dwQuemJogador1;

   // manda
   SendGeneralMessage ( APPMSG_DADOS_CLIENTE, dwTamanho, pvMsg );
   
   // libera
   GlobalFreePtr ( pvMsg );

   // inicia dados internos
   mIniciaDadosPartida ();
}



/* Inicia dados da partida - usado sem e com rede,
pelo cliente e pelo servidor.
return T - certo / F - erro
**********************************************************/
BOOL CApl::mIniciaDadosPartida ()
{
   // atualiza bmps dos personagens
   mAlteraBmpsPersonagens ( m_dwQuemJogador1, m_dwQuemJogador2 );

   // tenta ler pesos das ferramentas, poluidores e numero
   // inicial de seres
   if ( ! mCarregaValores () )
      return FALSE;

   // tenta iniciar agentes
   if ( ! mIniciaAgentes () )
      return FALSE;

   mRemoveAbertura ();

   // se havia mensagem de "terminou partida", retira
   if ( m_nNdxObjetoMensagem != -1 ) {
      m_oCen.mDestroiObjeto ( 2, m_nNdxObjetoMensagem );
      m_nNdxObjetoMensagem = -1;
   }

   // se estava suspensa, remove suspensao
   if ( m_yEstadoExecucao == 2 )
      mSuspendePartida ();

   // programa timers
   // controle dos eventos especiais
   //m_yEstadoExecucao = 2;       // partida suspenca, comeca logo
   //KillTimer ( m_oJanPrincipal.m_hWnd, 3 );
   //SetTimer ( m_oJanPrincipal.m_hWnd, 3, 7000, (TIMERPROC) TimerProc2 );

   // define estado de execusao da partida
   // se for servidor ou jogo monousuario
   if ( ! bUSANDO_REDEDP || DPInfo.bIsHost )
      m_yEstadoExecucao = 1;
   else
      // se for cliente - nao executa agentes, apenas move conforme
      // mensagens recebidas do servidor
      m_yEstadoExecucao = 2;

   // inicia eventos da fase e timer
   mExecEventosFase ( 0 );

   // numero de ferramentas do personagem
   mDefineNumFerramentas ( m_dwQuemJogador1, m_adwNumFerramentas1 );
   mDefineNumFerramentas ( m_dwQuemJogador2, m_adwNumFerramentas2 );

   // exibe os controles
   mExibeControles ();

   m_dwNumInteracoes = 0;

   m_oSons.mToca ( 1, 0 );

   return TRUE;
}



/* Retarda a execucao para controlar o ritmo do jogo
return T - pode executar / F - nao executa (passou muito pouco tempo)
**********************************************************/
void CApl::mCongelaTempo ( DWORD dwTempo )
{
   static DWORD
      dwUltimoTempo = 0;

   // se nao passou tempo suficiente -> espera
   while ( ( dwTempo - dwUltimoTempo) <= m_dwVeloc );
   
   // atualiza
   dwUltimoTempo = dwTempo;
}


  
/* Executa agentes e desenha cenario
**********************************************************/
void CApl::mExecInteracao ()
{
   // verifica se estah exibindo abertura
   if ( m_yEstadoExecucao == 0 )
      return;

   // conta tempo, qual o tempo atual em milisegundos
   //DWORD
      //dwTempo = ::GetTickCount ();

   // roda agentes
   if ( m_yEstadoExecucao == 1 ) {

      // executa comunicacao entre agentes
      m_oAgencia.mDisparaEvento ( NDX_EVENTO_ATIVIDADE_1, 0, 0 );
      m_oAgencia.mDisparaEvento ( NDX_EVENTO_ATIVIDADE_2, 0, 0 );
      m_oAgencia.mDisparaEvento ( NDX_EVENTO_ATIVIDADE_3, 0, 0 );

      // verifica se ocorreu colisao entre objetos ou
      // entre objetos e bordas do cenario
      m_oCen.mDetectaColisaoComBordas ( 1 ); // os interventores
      m_oCen.mDetectaColisaoComBordas ( 0 ); // peixes, plantas, microorganismos
      m_oCen.mDetectaColisaoEntreObjetos ( 0, FALSE );

      m_dwNumInteracoes ++; 

      mOndulaLuzes ();
   }

   mExibeEcometro ();
      
   // executa atualizacao dos objetos do cenario
   m_oJanPrincipal.mProcessaCenario ( m_yEstadoExecucao == 1 );

   // envia a posicao dos objetos para o cliente
   if ( bUSANDO_REDEDP && DPInfo.bIsHost ) {   // se eh servidor
      EnviaObjetosGrupo ( 0 );
      EnviaObjetosGrupo ( 1 );
      EnviaObjetosGrupo ( 2 );
   }

   // Se a conexao via socket esta aberta, verifica se recebeu alguma
   // mensagem.
   if ( bUSANDO_SOCKETS )
      mTutorRecebeuMensagemSocket ();
 
   // testa se transcorreu pouco tempo, se sim espera
   //mCongelaTempo ( dwTempo );
}



/* Faz ondulacao das luzes - simula refracao da agua
**********************************************************/
void CApl::mOndulaLuzes ()
{
   static int
      nI = 1,
      nC = 0;

   // faz movimento pendular dos raios de luz (vai e volta)
   nC += nI;
   if ( nC == -2 || nC == 8 )
      nI *= -1;

   // rotaciona o frame 3 do grupo 2 (esse frame tem uma fonte
   // de luz direcional criada no inicio do jogo). Eh rotacionada
   // conforme o eixo Z (z=1); o ultimo parametro indica por quantos
   // radianos.
   m_oCen.mRotacionaObjeto ( 2, 3,   0, 0, 1,   0.2 * (float) nI );
}



/* Executa algum evento da fase, definido no arquivo de fase.
nExecutar - 0 - reinicia o processamento de eventos (como do
inicio), 1 - executa interacao; 2 - executa um evento especifico
da fase
**********************************************************/
void CApl::mExecEventosFase ( int nExecutar )
{
   // inicia cursor que define qual evento executar
   static DWORD
      dwNumEventoExec = 0;

   // qual posicao no array de agentes usar na alocacao de um elemento
   // interventor
   static int
      nNumInterventor = 0;

   // quantas interacoes de jogo jah se passaram desdo inicio
   static DWORD
      dwInteracoesJogo = 0;

   // se eh apenas para iniciar variaveis
   if ( nExecutar == 0 ) {

      dwNumEventoExec = 0;
      nNumInterventor = 0;
      dwInteracoesJogo = 0;

      // remove timers
      KillTimer ( m_oJanPrincipal.m_hWnd, 1 );
      KillTimer ( m_oJanPrincipal.m_hWnd, 2 );

      // programa timers
      // controle dos eventos especiais
      SetTimer ( m_oJanPrincipal.m_hWnd, 1, m_dwVeloc, (TIMERPROC) TimerProc );
      // controle das interacoes do jogo
      SetTimer ( m_oJanPrincipal.m_hWnd, 2, (DWORD) ( ( (float) m_dwVeloc / VELOCDEFAULT ) * 1000 * m_astEventosFase [ dwNumEventoExec ].dwTempo ), NULL );

      return;
   }

   // executa uma interacao
   if ( nExecutar == 1 ) {

      mExecInteracao ();
      dwInteracoesJogo ++;

      return;
   }

   // verifica se partida esta bloqueada ou terminada, nao executa mais
   if ( m_yEstadoExecucao == 2 ||
        m_yEstadoExecucao == 3 )

      return;

   // executa evento da fase (criar agente interventor,...)

   // define qual agente interventor criar (se criar, jah tem valor)
   DWORD
      dwQualCriar = rand () % 9;
   // se eh para criar um interventor especifico
   if ( m_astEventosFase [ dwNumEventoExec ].dwEvento >= NUM_EVENTOS_TMP ) {
      // qual tipo
      dwQualCriar = m_astEventosFase [ dwNumEventoExec ].dwEvento - NUM_EVENTOS_TMP;
      // especifica que deve criar
      m_astEventosFase [ dwNumEventoExec ].dwEvento = EVENTO_TMP_CRIA_INTERVENTOR;
   }

   // executa procedimento indicado nos eventos da fase
   switch ( m_astEventosFase [ dwNumEventoExec ].dwEvento ) {

      case EVENTO_TMP_NULO:
         break;

      case EVENTO_TMP_CRIA_INTERVENTOR:  // cria agente interventor
         {
            m_oSons.mToca ( 7, 0 );

            // dados para criacao do agente
            DWORD
               *padwAgente = NULL,
               adwDrag [] = CONFIG_AGENTE_DRAGA,
               adwAgro [] = CONFIG_AGENTE_AGROTOXICO,
               adwRede [] = CONFIG_AGENTE_REDE,
               adwLixo [] = CONFIG_AGENTE_LIXO,
               adwJets [] = CONFIG_AGENTE_LANCHA,
               adwPesc [] = CONFIG_AGENTE_PESCA_PREDATORIA,
               adwEplu [] = CONFIG_AGENTE_ESGOTO_RESIDENCIAL,
               adwEind [] = CONFIG_AGENTE_ESGOTO_INDUSTRIAL,
               adwGaso [] = CONFIG_AGENTE_GASOLINA;

            // coordenadas limitrofes do cenario
            float
               fSup,
               fInf,
               fDir,
               fEsq;
            m_oCen.mRetLimiteJanVis ( fSup, fInf, fDir, fEsq ); 

            // coordenadas do agente no cenario (x,y,z)
            float
               afCoord [ 3 ];
            afCoord [ 2 ] = 0;      // z eh sempre 0
            
            // inicia dados especificos para cada agente interventor
            switch ( dwQualCriar ) {

               case 0:           // cria draga
                  afCoord [ 0 ] = fEsq;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.92 );
                  padwAgente = adwDrag;
                  break;

               case 1:           // cria agrotoxico
                  afCoord [ 0 ] = fDir;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.3 );
                  padwAgente = adwAgro;
                  break;

               case 2:           // cria rede de arrastao
                  afCoord [ 0 ] = fDir;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.92 );
                  padwAgente = adwRede;
                  break;

               case 3:           // cria lixo urbano
                  {
                     float
                        fPosX = (float) ( rand () % 60 ) / 60 + 0.2;
                     afCoord [ 0 ] = fEsq - fEsq * fPosX;
                     afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.92 );
                     padwAgente = adwLixo;
                  }
                  break;

               case 4:           // cria lancha
                  afCoord [ 0 ] = fDir;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.925 );
                  padwAgente = adwJets;
                  break;

               case 5:           // cria pesca predatoria
                  afCoord [ 0 ] = fEsq;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.92 );
                  padwAgente = adwPesc;
                  break;

               case 6:           // cria esgoto residencial
                  afCoord [ 0 ] = fEsq;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.92 );
                  padwAgente = adwEplu;
                  break;

               case 7:           // cria esgoto industrial
                  afCoord [ 0 ] = fDir;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.92 );
                  padwAgente = adwEind;
                  break;

               case 8:           // cria posto de gasolina
                  afCoord [ 0 ] = fEsq * 0.89;
                  afCoord [ 1 ] = fSup - ( fSup * (float) LIMSUP * 0.98 );
                  padwAgente = adwGaso;
                  break;

               default:
                  MSGSTR ( "Interventor com esse numero nao existe. (CApl::mExecEventosFase)" );
                  break;
            }

            // qual posicao usar no array de agentes para proximo
            // interventor
            nNumInterventor ++;
            if ( nNumInterventor >= MAX_AGENTES_INTERVENTORES )
               nNumInterventor = 0;

            // adiciona agente
            int
               nNdxNovo = MAX_AGENTES - NDX_AGENTE_INTERVENTOR - nNumInterventor;
            m_oAgencia.m_aoAgentes [ nNdxNovo ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) padwAgente, (PTRVAL) afCoord );
         }
         break;

      case EVENTO_TMP_TERMINA_PARTIDA:
         mTerminaPartida ();
         break;

      default:
         MSGSTR ( "Evento não existe. (CApl::mExecEventosFase)" );
         break;
   }

   // se nao terminou partida
   if ( m_astEventosFase [ dwNumEventoExec ].dwEvento != EVENTO_TMP_TERMINA_PARTIDA ) {

      // evento jah disparado
      dwNumEventoExec ++;
   
      // reprograma timer para novo evento
      // define o tempo a mais que deve ser esperado para o proximo evento   
      DWORD
         dwTempoProx = m_astEventosFase [ dwNumEventoExec ].dwTempo - m_astEventosFase [ dwNumEventoExec - 1 ].dwTempo;
      // reprograma
      SetTimer ( m_oJanPrincipal.m_hWnd, 2, (DWORD) ( ( (float) m_dwVeloc / VELOCDEFAULT ) * 1000 * dwTempoProx ), NULL );
   }
}



/* Carrega os dados de alguns parametros dos agentes
retorna TRUE - sucesso / FALSE - erro
**********************************************************/
BOOL CApl::mCarregaValores ()
{
   // interventores
   
   if ( ! CarregaValores ( "CONFIG_TAXAS_DRAGA_LEITO", 18, afCONFIG_TAXAS_DRAGA_LEITO ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_AGROTOXICO", 18, afCONFIG_TAXAS_AGROTOXICO ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_REDE", 18, afCONFIG_TAXAS_REDE ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_LIXO", 18, afCONFIG_TAXAS_LIXO ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_LANCHA", 18, afCONFIG_TAXAS_LANCHA ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_PESCA_PREDATORIA", 18, afCONFIG_TAXAS_PESCA_PREDATORIA ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_ESGOTO_RESIDENCIAL", 18, afCONFIG_TAXAS_ESGOTO_RESIDENCIAL ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_ESGOTO_INDUSTRIAL", 18, afCONFIG_TAXAS_ESGOTO_INDUSTRIAL ) )
      return FALSE;

   if ( ! CarregaValores ( "CONFIG_TAXAS_GASOLINA", 18, afCONFIG_TAXAS_GASOLINA ) )
      return FALSE;

   // personagens - numero de ferramentas
   
   if ( ! CarregaValores ( "NUM_FERRAMENTAS_MAE_NATUREZA", 4, afNUM_FERRAMENTAS_MAE_NATUREZA ) )
      return FALSE;
   
   if ( ! CarregaValores ( "NUM_FERRAMENTAS_PREFEITO", 4, afNUM_FERRAMENTAS_PREFEITO ) )
      return FALSE;
   
   if ( ! CarregaValores ( "NUM_FERRAMENTAS_TURISTA", 4, afNUM_FERRAMENTAS_TURISTA ) )
      return FALSE;
   
   if ( ! CarregaValores ( "NUM_FERRAMENTAS_PESCADOR", 4, afNUM_FERRAMENTAS_PESCADOR ) )
      return FALSE;

   // numero de agentes no inicio das partidas

   if ( ! CarregaValores ( "NUM_INI_GORDO", 1, &fNUM_INI_GORDO ) )
      return FALSE;

   if ( ! CarregaValores ( "NUM_INI_LISTRADO", 1, &fNUM_INI_LISTRADO ) )
      return FALSE;

   if ( ! CarregaValores ( "NUM_INI_AZUL", 1, &fNUM_INI_AZUL ) )
      return FALSE;

   if ( ! CarregaValores ( "NUM_INI_ALGA", 1, &fNUM_INI_ALGA ) )
      return FALSE;

   if ( ! CarregaValores ( "NUM_INI_MICRO", 1, &fNUM_INI_MICRO ) )
      return FALSE;

   if ( ! CarregaValores ( "CRIAR_LAGO", 1, &fCRIAR_LAGO ) )
      return FALSE;

   if ( ! CarregaValores ( "CRIAR_AGUA", 1, &fCRIAR_AGUA ) )
      return FALSE;

   // quanto cada ferramenta repoe aos seres

   if ( ! CarregaValores ( "ALTERA_FERR_0_MAE_NATUREZA", 7, afALTERA_FERR_0_MAE_NATUREZA ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_0_PREFEITO", 7, afALTERA_FERR_0_PREFEITO ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_0_PESCADOR", 7, afALTERA_FERR_0_PESCADOR ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_0_TURISTA", 7, afALTERA_FERR_0_TURISTA ) )
      return FALSE;


   if ( ! CarregaValores ( "ALTERA_FERR_1_MAE_NATUREZA", 7, afALTERA_FERR_1_MAE_NATUREZA ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_1_PREFEITO", 7, afALTERA_FERR_1_PREFEITO ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_1_PESCADOR", 7, afALTERA_FERR_1_PESCADOR ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_1_TURISTA", 7, afALTERA_FERR_1_TURISTA ) )
      return FALSE;


   if ( ! CarregaValores ( "ALTERA_FERR_2_MAE_NATUREZA", 7, afALTERA_FERR_2_MAE_NATUREZA ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_2_PREFEITO", 7, afALTERA_FERR_2_PREFEITO ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_2_PESCADOR", 7, afALTERA_FERR_2_PESCADOR ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_2_TURISTA", 7, afALTERA_FERR_2_TURISTA ) )
      return FALSE;


   if ( ! CarregaValores ( "ALTERA_FERR_3_MAE_NATUREZA", 7, afALTERA_FERR_3_MAE_NATUREZA ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_3_PREFEITO", 7, afALTERA_FERR_3_PREFEITO ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_3_PESCADOR", 7, afALTERA_FERR_3_PESCADOR ) )
      return FALSE;

   if ( ! CarregaValores ( "ALTERA_FERR_3_TURISTA", 7, afALTERA_FERR_3_TURISTA ) )
      return FALSE;

   return TRUE;
}



/* Inicia os agentes do cenario
retorna TRUE - certo / FALSE - erro
**********************************************************/
BOOL CApl::mIniciaAgentes ()
{
   m_oAgencia.mDisparaEvento ( NDX_EVENTO_MORRE, 0, 0 );

   // destroi agentes (se existirem)
   m_oAgencia.mDestroi ();

   // tenta iniciar agentes do cenario
   if ( ! m_oAgencia.mCriaAgentes ( &m_oCen ) )
      return FALSE;

   // faz alguns deles nascerem (antes disso nao tratam outras mensagens)
   DWORD
      adwAzul [] = CONFIG_AGENTE_PEIXE_AZUL,
      adwList [] = CONFIG_AGENTE_PEIXE_LISTRADO,
      adwGord [] = CONFIG_AGENTE_PEIXE_GORDO,
      adwMicr [] = CONFIG_AGENTE_MICROORGANISMO,
      adwAlga [] = CONFIG_AGENTE_ALGA_VERDE,
      adwLago [] = CONFIG_AGENTE_FUNDO_LAGO,
      adwAgua [] = CONFIG_AGENTE_AGUA;

   // coordenadas limitrofes
   float
      fSup,
      fInf,
      fDir,
      fEsq;
   m_oCen.mRetLimiteJanVis ( fSup, fInf, fDir, fEsq ); 

   // coordenadas do agente no cenario (x,y,z)
   float
      afCoord [ 3 ];
   afCoord [ 2 ] = 0;      // z eh sempre 0

   int
      ncA = 0,    // soh contador de loops
      nInd = 0;   // indice do agente na agencia

   // fundo do lado
   /*afCoord [ 0 ] = 0;
   afCoord [ 1 ] = fInf * 1.2;
   m_oAgencia.m_aoAgentes [ nInd ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwLago, (PTRVAL) afCoord );
   nInd ++;*/

   // fundo do lado
   if ( fCRIAR_LAGO != 0 ) {
      afCoord [ 0 ] = 0;
      afCoord [ 1 ] = 0;
      m_oAgencia.m_aoAgentes [ MAX_AGENTES - NDX_AGENTE_FUNDO_LAGO ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwLago, (PTRVAL) afCoord );
   }

   // agua
   if ( fCRIAR_AGUA != 0 ) {
      afCoord [ 0 ] = 0;
      afCoord [ 1 ] = 0;
      m_oAgencia.m_aoAgentes [ MAX_AGENTES - NDX_AGENTE_AGUA ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwAgua, (PTRVAL) afCoord );
   }
   
   // cria peixe gordo
   for ( ncA = 0; ncA < (DWORD) fNUM_INI_GORDO; ncA ++ ) {
      afCoord [ 0 ] = (float) ( rand () % 200 ) - 200 / 2;
      afCoord [ 1 ] = (float) ( rand () % 70 ) - 70 / 2 - 20;
      m_oAgencia.m_aoAgentes [ nInd ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwGord, (PTRVAL) afCoord );
      nInd ++;
   }
   
   // cria peixe azul
   for ( ncA = 0; ncA < (DWORD) fNUM_INI_AZUL; ncA ++ ) {
      afCoord [ 0 ] = (float) ( rand () % 200 ) - 200 / 2;
      afCoord [ 1 ] = (float) ( rand () % 70 ) - 70 / 2 - 20;
      m_oAgencia.m_aoAgentes [ nInd ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwAzul, (PTRVAL) afCoord );
      nInd ++;
   }
   
   // cria peixe listrado
   for ( ncA = 0; ncA < (DWORD) fNUM_INI_LISTRADO; ncA ++ ) {
      afCoord [ 0 ] = (float) ( rand () % 200 ) - 200 / 2;
      afCoord [ 1 ] = (float) ( rand () % 70 ) - 70 / 2 - 20;
      m_oAgencia.m_aoAgentes [ nInd ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwList, (PTRVAL) afCoord );
      nInd ++;
   }
   
   // cria algas
   for ( ncA = 0; ncA < (DWORD) fNUM_INI_ALGA; ncA ++ ) {
      afCoord [ 0 ] = (float) ( rand () % 200 ) - 200 / 2;
      afCoord [ 1 ] = fInf + ( rand () % 4 ) + 2;
      m_oAgencia.m_aoAgentes [ nInd ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwAlga, (PTRVAL) afCoord );
      nInd ++;
   }
   
   // cria microorganismos
   for ( ncA = 0; ncA < (DWORD) fNUM_INI_MICRO; ncA ++ ) {
      afCoord [ 0 ] = (float) ( rand () % 200 ) - 200 / 2;
      afCoord [ 1 ] = (float) ( rand () % 70 ) - 70 / 2 - 20;
      m_oAgencia.m_aoAgentes [ nInd ].mTrataEvento ( NDX_EVENTO_NASCE, (PTRVAL) adwMicr, (PTRVAL) afCoord );
      nInd ++;
   }

   return TRUE;
}



/* Carrega os modelos dos objetos e cria as luzes do cenario
return TRUE - sucesso / F - erro
**********************************************************/
BOOL CApl::mCriaModelosELuzes ()
{
   // LUZES
   
   // cria luz ambiente no frame universo
   if ( ! m_oCen.mCriaLuz ( 0, NUM_GRUPOS, NUM_GRUPOS,
                            0, 0, 0, 0.4, 0.4, 0.4 ) )
      return FALSE;

   // cria sol no frame universo (usa frames do grupo 2 para alocar)
   if ( ! m_oCen.mCriaLuz ( 1, NUM_GRUPOS, 2, 
                            1.0, -1.0, 0, 0.4, 0.4, 0.4 ) )
      return FALSE;

   // cria luz frontal no universo (usa frames do grupo 2 para alocar)
   if ( ! m_oCen.mCriaLuz ( 1, NUM_GRUPOS, 2, 
                            0, 0, 1, 0.15, 0.15, 0.15 ) )
      return FALSE;

   // cria luz inferior no universo (usa frames do grupo 2 para alocar)
   if ( ! m_oCen.mCriaLuz ( 1, NUM_GRUPOS, 2, 
                            0, 1, 0.1, 0.4, 0.4, 0.4 ) )
      return FALSE;

   // cria luz refratada no grupo 0 - grupo dos objetos subaguaticos
   // (usa frames do grupo 2 para alocar)
   if ( ! m_oCen.mCriaLuz ( 1, 0, 2, 
                            0, -1, 0.1, 0.65, 0.65, 0.65 ) )
      return FALSE;
     
   // MODELOS

   // tenta carregar todos os modelos de objetos
   for ( int ncI = 0; ncI < NUM_MODELOS; ncI ++ ) {

      // terah o nome do arquivo com o modelo
      char
         szArq [ MAX_STR ];
      strcpy ( szArq, STR_INDEFINIDO );

      // conforme qual a posicao no array escolhe nome
      switch ( ncI ) {

         case NDX_MODELO_PEIXE_AZUL:
            strcpy ( szArq, STR_NOMEARQAZUL );
            break;

         case NDX_MODELO_PEIXE_LISTRADO:
            strcpy ( szArq, STR_NOMEARQLISTRADO );
            break;

         case NDX_MODELO_PEIXE_GORDO:
            strcpy ( szArq, STR_NOMEARQGORDO );
            break;

         case NDX_MODELO_ALGA_VERDE:
            strcpy ( szArq, STR_NOMEARQALGAVERDE );
            break;

         case NDX_MODELO_MICROORGANISMO:
            strcpy ( szArq, STR_NOMEARQMICROORGANISMO );
            break;

         case NDX_MODELO_DRAGA:
            strcpy ( szArq, STR_NOMEARQDRAGA );
            break;

         case NDX_MODELO_AGROTOXICO:
            strcpy ( szArq, STR_NOMEARQAGROTOXICO );
            break;

         case NDX_MODELO_REDE:
            strcpy ( szArq, STR_NOMEARQREDE );
            break;

         case NDX_MODELO_PARTIDA_TERMINOU:
            strcpy ( szArq, STR_NOMEARQPARTIDA_TERMINOU );
            break;

         case NDX_MODELO_LIXO:
            strcpy ( szArq, STR_NOMEARQLIXO );
            break;

         case NDX_MODELO_LANCHA:
            strcpy ( szArq, STR_NOMEARQLANCHA );
            break;

         case NDX_MODELO_PESCA_PREDATORIA:
            strcpy ( szArq, STR_NOMEARQPESCA_PREDATORIA );
            break;

         case NDX_MODELO_GASOLINA:
            strcpy ( szArq, STR_NOMEARQGASOLINA );
            break;

         case NDX_MODELO_DETRITO_ESGOTO_INDUSTRIAL:
            strcpy ( szArq, STR_NOMEARQDETRITO_ESGOTO_INDUSTRIAL );
            break;

         case NDX_MODELO_DETRITO_GASOLINA:
            strcpy ( szArq, STR_NOMEARQDETRITO_GASOLINA );
            break;

         case NDX_MODELO_DETRITO_ESGOTO_RESIDENCIAL:
            strcpy ( szArq, STR_NOMEARQDETRITO_ESGOTO_RESIDENCIAL );
            break;

         case NDX_MODELO_DETRITO_AGROTOXICO:
            strcpy ( szArq, STR_NOMEARQDETRITO_AGROTOXICO );
            break;

         default:
            MSGSTR ( STR_ERRO_MODELO_INDEFINIDO );
            MSGFLO ( (float) __LINE__ );
            break;
      }

      // carrega o modelo
      if ( ! m_oCen.mIncluiModelo ( (DWORD) ncI, szArq ) )
         return FALSE;
   }

   return TRUE;
}
