/*  TA3D, a remake of Total Annihilation
    Copyright (C) 2005  Roland BROCHARD

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA*/

#include "config.h"			// Configuration de TA3D à la compilation
#include <allegro.h>		// Allegro
#include <alleggl.h>		// AllegroGL
#include <GL/glu.h>			// GLU

#if USE_MP3
#include "../include/almp3.h"
#endif

#include "intro.h"			// Introduction

#include "console.h"		// Console
#include "ta3d.h"			// Moteur
#include "menu.h"			// Menus du jeu

FONT *aglfont;
RGB *pal;

VECTOR cursor_on_map(CAMERA *cam,MAP *map);

int main(int argc,char *argv[])
{
allegro_init();

install_timer();			// Initialise allegro
install_keyboard();
install_mouse();
install_allegro_gl();

install_sound(DIGI_AUTODETECT, MIDI_NONE, 0);

install_int_ex(Timer,precision);

	allegro_gl_clear_settings();			// Initialise AllegroGL
	allegro_gl_set (AGL_STENCIL_DEPTH, 8);
	allegro_gl_set (AGL_SAMPLE_BUFFERS, 0);
	allegro_gl_set (AGL_SAMPLES, 0);
	allegro_gl_set (AGL_COLOR_DEPTH, 32);
	allegro_gl_set (AGL_Z_DEPTH, 32);
	allegro_gl_set (AGL_FULLSCREEN, TRUE);
	allegro_gl_set (AGL_DOUBLEBUFFER, 1);
	allegro_gl_set (AGL_RENDERMETHOD, 1);
	allegro_gl_set (AGL_SUGGEST, AGL_RENDERMETHOD | AGL_COLOR_DEPTH | AGL_Z_DEPTH | AGL_DOUBLEBUFFER | AGL_FULLSCREEN | AGL_SAMPLES | AGL_SAMPLE_BUFFERS | AGL_STENCIL_DEPTH);

	allegro_gl_use_mipmapping(TRUE);

	allegro_gl_flip_texture(false);

request_refresh_rate(70);

set_gfx_mode(GFX_OPENGL_WINDOWED, 640, 480, 0, 0);		// Entre en mode graphique OpenGL

install_ext();		// Pour les fonctions OpenGl supplémentaires

if(g_useTextureCompression)			// Active la compression de texture
	allegro_gl_set_texture_format(GL_COMPRESSED_RGB_ARB);
else
	allegro_gl_set_texture_format(GL_RGB8);

set_uformat(U_ASCII);		// Juste histoire d'avoir un affichage correct des textes

set_window_title("Total Annihilation 3D");

aglfont=allegro_gl_convert_allegro_font(font,AGL_FONT_TYPE_TEXTURED,-0.9f);

start=Atimer;		// Pour l'animation du curseur

Console=new CONSOLE;				// Initialise la console

Console->StartRecording("console.log");
Console->AddEntry("TA3D Engine");
Console->AddEntry(TA3D_ENGINE_VERSION);
print_system_data(Console);

if(g_useTextureCompression)
	Console->AddEntry("GL_ARB_texture_compression disponible");
if(g_useStencilTwoSide)
	Console->AddEntry("GL_EXT_stencil_two_side disponible");

Console->AddEntry("Chargement de la palette de couleurs");

pal=LoadPal("/palettes/palette.pal");			// Charge la palette graphique
set_palette(pal);		// Active la palette chargée

Console->AddEntry("Lancement de l'introduction");

play_intro();

Console->AddEntry("Initialisation du moteur");
model_manager.init();
unit_manager.init();
feature_manager.init();
weapon_manager.init();
sound_manager.init();
fx_manager.init();

{
	byte *data=load_file("/anims/CURSORS.GAF");
	cursor.load_gaf(data);
	cursor.convert();
	CURSOR_MOVE=cursor.find_entry("cursormove");
	CURSOR_GREEN=cursor.find_entry("cursorgrn");
	CURSOR_CROSS=cursor.find_entry("cursorselect");
	CURSOR_RED=cursor.find_entry("cursorred");
	CURSOR_LOAD=cursor.find_entry("cursorload");
	CURSOR_GUARD=cursor.find_entry("cursorprotect");
	CURSOR_PATROL=cursor.find_entry("cursorpatrol");
	CURSOR_REPAIR=cursor.find_entry("cursorrepair");
	CURSOR_ATTACK=cursor.find_entry("cursorattack");
	CURSOR_BLUE=cursor.find_entry("cursornormal");
	CURSOR_AIR_LOAD=cursor.find_entry("cursorpickup");
	CURSOR_BOMB_ATTACK=cursor.find_entry("cursorairstrike");
	CURSOR_BALANCE=cursor.find_entry("cursorunload");
	CURSOR_RECLAIM=cursor.find_entry("cursorreclamate");
	CURSOR_WAIT=cursor.find_entry("cursorhourglass");
	CURSOR_CANT_ATTACK=cursor.find_entry("cursortoofar");
	CURSOR_CROSS_LINK=cursor.find_entry("pathicon");
	free(data);
}

if(argc==1) {
	Console->AddEntry("Lancement du menu");
	main_menu();
	}
else {
	for(int i=0;i<strlen(argv[1]);i++)
		if(argv[1][i]=='_')
			argv[1][i]=' ';
	char *hpi_name=find_hpi(argv[1]);
	if(hpi_name) {						// Charge l'archive contenant la carte
		Console->AddEntry("Lancement d'une partie");
		play(argv[1]);
		}
	}

cursor.destroy();

Console->AddEntry("Fin du programme");

Console->destroy();		// Détruit la console
delete Console;

allegro_exit();

return 0;
}
END_OF_MAIN();

/*--------------------------------------------------------------------\
|                            Moteur du jeu                            |
\--------------------------------------------------------------------*/
void play(char *filename)
{
al_ffblk search;

int start_time=Atimer;		// Pour la mesure du temps de chargement

loading(0.0f,"Chargement en cours des textures");

/*-----------------------charge les textures-------------------------*/

texture_manager.all_texture();

loading(100.0f/7.0f,"Chargement en cours des modeles 3D");
/*-----------------------charge les modèles 3D-----------------------*/

model_manager.init();
model_manager.load_all();

loading(200.0f/7.0f,"Chargement en cours des elements graphiques");
/*-----------------------charge les éléments graphiques--------------*/

load_features();
feature_manager.clean();

loading(250.0f/7.0f,"Chargement en cours des armes");
/*-----------------------charge les armes----------------------------*/

load_weapons();

weapons.init();

loading(300.0f/7.0f,"Chargement en cours des unites");
/*-----------------------charge les unités---------------------------*/

unit_manager.init();
int nb_inconnu=0;
if(al_findfirst("*.hpi",&search,FA_RDONLY | FA_ARCH)==0)
do
{
	nb_inconnu+=unit_manager.all_units_in_hpi(search.name);
}while(al_findnext(&search)==0);
al_findclose(&search);
loading(300.0f/7.0f+100.0f/21.0f,"Chargement en cours des unites");
if(al_findfirst("*.ufo",&search,FA_RDONLY | FA_ARCH)==0)
do
{
	nb_inconnu+=unit_manager.all_units_in_hpi(search.name);
}while(al_findnext(&search)==0);
al_findclose(&search);

loading(300.0f/7.0f+200.0f/21.0f,"Chargement en cours des unites");

nb_inconnu+=unit_manager.all_units_in_hpi("rev31.gp3");

printf("%d mots clées inconnus\n",nb_inconnu);

unit_manager.Identify();

for(int i=0;i<arch_manager.nb_arch;i++)
	unit_manager.load_sound_file(&(arch_manager.arch[i]));

loading(400.0f/7.0f,"Libere la memoire inutilisee");
/*-----------------------libère la mémoire inutilisée----------------*/

texture_manager.destroy();

loading(500.0f/7.0f,"Initialise le moteur");
/*-----------------------initialise le moteur------------------------*/

SetDefOpenGlState();
particle_engine.init();

set_palette(pal);

players.init();						// Objet contenant les données sur les joueurs
players.add("joueur0","arm");		// Ajoute un joueur
players.add("joueur1","core");		// Ajoute un joueur

units.init();

loading(600.0f/7.0f,"Charge la carte");
/*-----------------------charge la carte-----------------------------*/

byte *map_file=load_file(filename);

if(!map_file)	return;
MAP *map=load_tnt_map(map_file);
free(map_file);

ASM_PROGRAM	game_script;		// Script qui va gérer la partie
game_script.compile("scripts/default.c");	// Compile le script

GLuint	sky=LoadTex("sky.tga");
GLuint	glow=LoadTex("glow.tga");

loading(100.0f,"Chargement termine");

start_time=Atimer-start_time;
printf("temps de chargement: %f secondes\n",(float)start_time/SECS_TO_TIMER(1));

#if USE_MP3		// Si on compile avec AllegroMP3
byte *data=(byte*) malloc(32768);			// tampon de 32ko
PACKFILE *mp3_file=NULL;
ALMP3_MP3STREAM *mp3=NULL;
int nb_mp3=0;
if(al_findfirst("music/*.mp3",&search,FA_RDONLY | FA_ARCH)==0) {	// Compte le nombre de fichiers jouables
	nb_mp3++;
	while(al_findnext(&search)==0) nb_mp3++;
	}
al_findclose(&search);
char **play_list=NULL;
if(nb_mp3>0) {
	play_list=(char**) malloc(sizeof(char*)*nb_mp3);
	for(int i=0;i<nb_mp3;i++)
		play_list[i]=NULL;
	}
if(al_findfirst("music/*.mp3",&search,FA_RDONLY | FA_ARCH)==0) {
	char name[1000];
	name[0]=0;
	strcat(name,"music/");
	strcat(name,search.name);
	play_list[0]=strdup(name);
	for(int i=1;i<nb_mp3;i++) {			// Crée une liste de tout les fichiers mp3 jouables
		al_findnext(&search);
		name[0]=0;
		strcat(name,"music/");
		strcat(name,search.name);
		play_list[i]=strdup(name);
		}

	int cur=rand()%nb_mp3;
	name[0]=0;
	strcat(name,"playing ");
	strcat(name,play_list[cur]);
	Console->AddEntry(name);

	name[0]=0;
	strcat(name,play_list[cur]);

	mp3_file=pack_fopen(name,F_READ);			// Ouvre le fichier

	int len=pack_fread(data,32768,mp3_file);

	mp3=almp3_create_mp3stream(data,len,len<32768);
	if(mp3)
		almp3_play_ex_mp3stream(mp3,65536,100,50,1000);
	}
al_findclose(&search);
#endif

float Conv=1.0f/SECS_TO_TIMER(1);
float dt=0.0f;
float t=0.0f;
int count=Atimer;
int nbfps=0;
int fpscount=Atimer;
int fps=0;
int i;

float r1,r2,r3,or1;
r1=r2=r3=0.0f;
r1=-45.0f;
CAMERA cam;
bool freecam=false;
game_cam=&cam;

cam.RPos.x=cam.RPos.y=cam.RPos.z=0.0f;
cam.RPos.z+=150.0f;
cam.RPos.y=150.0f;
cam.zfar=500.0f;

float FogD=0.3f;
float FogNear=cam.zfar*0.5f;
float FogColor[]={0.8f,0.8f,0.8f,1.0f};
GLuint FogMode=GL_LINEAR;
	glFogi (GL_FOG_MODE, FogMode);
	glFogfv (GL_FOG_COLOR, FogColor);
	glFogf (GL_FOG_DENSITY, FogD);
	glHint (GL_FOG_HINT, GL_NICEST);
	glFogf (GL_FOG_START, FogNear);
	glFogf (GL_FOG_END, cam.zfar);
	
glEnable(GL_FOG);

HWLIGHT	sun;
sun.Att=0.0f;
sun.Dir.x=-1.0f;
sun.Dir.y=1.0f;
sun.Dir.z=1.0f;
sun.Dir.Unit();
sun.LightAmbient[0]=0.25f;
sun.LightAmbient[1]=0.25f;
sun.LightAmbient[2]=0.25f;
sun.LightAmbient[3]=0.25f;
sun.LightDiffuse[0]=1.0f;
sun.LightDiffuse[1]=1.0f;
sun.LightDiffuse[2]=1.0f;
sun.LightDiffuse[3]=1.0f;
sun.LightSpecular[0]=0.0f;
sun.LightSpecular[1]=0.0f;
sun.LightSpecular[2]=0.0f;
sun.LightSpecular[3]=0.0f;
sun.Directionnal=true;

float speed_limit=-1.0f;
float delay=1.0f/speed_limit;
int nb_shoot=0;
bool shoot=false;

bool tilde=false;
bool showfps=false;
bool done=false;

#define EXIT_NONE		0x0
#define EXIT_VICTORY	0x1
#define EXIT_DEFEAT		0x2

int	exit_mode=EXIT_NONE;

fire=particle_engine.addtex("fire.tga","firea.tga");
build_part=particle_engine.addtex("part.tga",NULL);

int mx,my,omb=mouse_b,omb2=mouse_b,omb3=mouse_b;
int	sel_x[2],sel_y[2],selecting=false;
int cur_sel=-1;
bool selected=false;
int	build=-1;				// Indique si l'utilisateur veut construire quelque chose
int cur_sel_index=-1;
int omz=mouse_z;
float cam_h=150.0f;
float time_factor=1.0f;

bool wireframe=false;		// Mode fil de fer pour le terrain
bool shadow=false;			// Active les ombres
bool particle=true;
bool trees=true;
bool show_script=false;			// Affichage des scripts
bool show_model=false;			// Affichage les noms des sous objets du modèle 3D de l'unité sélectionnée
bool rotate_light=false;
float light_angle=0.0f;
int shadow_quality=1;
float shadow_r=0.02f;
int priority_level=0;
bool cheat_metal=false;
bool cheat_energy=false;

do
{

	/*------------------------------ gestion du son ----------------------------*/

#if USE_MP3
	if(mp3) {
		if(almp3_poll_mp3stream(mp3)==ALMP3_POLL_PLAYJUSTFINISHED) {
			almp3_destroy_mp3stream(mp3);
			int cur=rand()%nb_mp3;
			char name[1000];
			name[0]=0;
			strcat(name,"playing ");
			strcat(name,play_list[cur]);
			Console->AddEntry(name);
			pack_fclose(mp3_file);
			mp3_file=pack_fopen(play_list[cur],F_READ);
			int len=pack_fread(data,32768,mp3_file);
			mp3=almp3_create_mp3stream(data,len,len<32768);
			if(mp3)
				almp3_play_ex_mp3stream(mp3,65536,100,50,1000);
			}
		else {
			byte *buf=(byte*) almp3_get_mp3stream_buffer(mp3);
			if(buf) {
				int len=pack_fread(buf,32768,mp3_file);
				almp3_free_mp3stream_buffer(mp3,len<32768 ? len : -1);
				}
			}
		}
#endif

	sound_manager.clean(cam.RPos,cam.Side);

	/*------------------------- fin de la gestion du son -----------------------*/

	if(!freecam) {
		r1+=(mouse_z-omz)*0.5f;
		if(r1>-45.0f) r1=-45.0f;
		else if(r1<-75.0f) r1=-75.0f;
		else
			cam_h-=(mouse_z-omz);
		}
	else
		cam.RPos=cam.RPos-0.5f*(mouse_z-omz)*cam.Dir;
	omz=mouse_z;

	cursor_type=CURSOR_DEFAULT;			// Revient au curseur par défaut

	if(mouse_x>128 && mouse_y>32 && mouse_y<SCREEN_H-32) {
		if(selected)
			for(i=0;i<=units.last_index;i++)
				if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel && unit_manager.unit_type[units.unit[i].type_id].canmove) {
					cursor_type=CURSOR_MOVE;
					break;
					}
		}

	if(build>=0)
		cursor_type=CURSOR_DEFAULT;

	dt=(Atimer-count)*Conv;			// Régulateur de vitesse d'affichage
	while(dt<delay) {
		switch(priority_level)
		{
		case 0:			rest(1);	break;
		case 1:			rest(0);	break;
		};
		dt=(Atimer-count)*Conv;
		}

	light_angle+=dt*time_factor;

	t=Atimer*Conv;
	nbfps++;
	if(nbfps>=10 && (Atimer-fpscount)*Conv>=0.05f) {
		fps=(int)(nbfps/((Atimer-fpscount)*Conv));
		fpscount=Atimer;
		nbfps=0;
		}
	count=Atimer;

	if(freecam && cam.RPos.y<map->sealvl) {
		FogD=0.03f;
		FogNear=0.0f;
		FogMode=GL_EXP;

		FogColor[0]=0.0f;
		FogColor[1]=0.0f;
		FogColor[2]=0.3f;
		FogColor[3]=1.0f;
		}
	else {
		FogD=0.3f;
		FogNear=cam.zfar*0.5f;
		FogMode=GL_LINEAR;

		FogColor[0]=0.8f;
		FogColor[1]=0.8f;
		FogColor[2]=0.8f;
		FogColor[3]=1.0f;
		}

	SetDefOpenGlState();
	glClearColor(FogColor[0],FogColor[1],FogColor[2],FogColor[3]);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Efface l'écran

	glFogi (GL_FOG_MODE, FogMode);
	glFogfv (GL_FOG_COLOR, FogColor);
	glFogf (GL_FOG_DENSITY, FogD);
	glHint (GL_FOG_HINT, GL_NICEST);
	glFogf (GL_FOG_START, FogNear);
	glFogf (GL_FOG_END, cam.zfar);

/*------------bloc regroupant ce qui est relatif aux commandes----------------*/

	if(mouse_x<128.0f && mouse_y<128.0f && mouse_x>=0.0f && mouse_y>=0.0f && mouse_b==1) {
		cam.RPos.x=(mouse_x-64)*map->map_w/128.0f*252.0f/map->mini_w;
		cam.RPos.z=(mouse_y-64)*map->map_h/128.0f*252.0f/map->mini_h;
		}
	if(mouse_x<10)
		cam.RPos.x-=100.0f*dt;
	if(mouse_x>SCREEN_W-10)
		cam.RPos.x+=100.0f*dt;
	if(mouse_y<10)
		cam.RPos.z-=100.0f*dt;
	if(mouse_y>SCREEN_H-10)
		cam.RPos.z+=100.0f*dt;

	if(freecam) {
		if(mouse_b==4) {
			get_mouse_mickeys(&mx,&my);
			if(omb==mouse_b) {
				r2-=mx;
				r1-=my;
				}
			position_mouse(SCREEN_W>>1,SCREEN_H>>1);
			}
		else if(omb==4)
			position_mouse(SCREEN_W>>1,SCREEN_H>>1);
		if(key[KEY_SPACE] && !Console->show)
			cam.RPos=cam.RPos+100.0f*dt*cam.Dir;
		}
	else {
		if(mouse_b==4) {
			get_mouse_mickeys(&mx,&my);
			if(omb==mouse_b) {
				cam.RPos.x+=mx;
				cam.RPos.z+=my;
				}
			position_mouse(SCREEN_W>>1,SCREEN_H>>1);
			}
		else if(omb==4)
			position_mouse(SCREEN_W>>1,SCREEN_H>>1);
		}
	omb=mouse_b;

	if(key[KEY_UP])
		cam.RPos.z-=100.0f*dt;
	if(key[KEY_DOWN])
		cam.RPos.z+=100.0f*dt;
	if(key[KEY_RIGHT])
		cam.RPos.x+=100.0f*dt;
	if(key[KEY_LEFT])
		cam.RPos.x-=100.0f*dt;

	if(cam.RPos.x<-0.5f*map->map_w)	cam.RPos.x=-0.5f*map->map_w;
	if(cam.RPos.x>0.5f*map->map_w)	cam.RPos.x=0.5f*map->map_w;
	if(cam.RPos.z<-0.5f*map->map_h+200.0f)	cam.RPos.z=-0.5f*map->map_h+200.0f;
	if(cam.RPos.z>0.5f*map->map_h)	cam.RPos.z=0.5f*map->map_h;

	if(!freecam)
	{
		float h=map->get_unit_h(cam.RPos.x,cam.RPos.z);
		if(h<map->sealvl)
			h=map->sealvl;
		cam.RPos.y+=(h+cam_h-cam.RPos.y)*dt;
	}

	MATRIX_4x4 Rotation;
	r1+=cam.Rotat.x*dt;
	r2+=cam.Rotat.y*dt;
	r3+=cam.Rotat.z*dt;
	Rotation=RotateX(r1*PI/180.0f)*RotateY(r2*PI/180.0f)*RotateZ(r3*PI/180.0f);

	cam.SetMatrix(Rotation,dt);

	if(selected) {
		bool builders=false;
		bool canattack=false;
		bool canreclamate=false;
		for(i=0;i<=units.last_index;i++)
			if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel) {
				builders|=unit_manager.unit_type[units.unit[i].type_id].Builder;
				canattack|=unit_manager.unit_type[units.unit[i].type_id].canattack;
				canreclamate|=unit_manager.unit_type[units.unit[i].type_id].CanReclamate;
				}
		int pointing=units.pick(&cam);		// Sur quoi le curseur est-il pointé??

		if(pointing>=0) {	// S'il y a quelque chose sous le curseur
			cursor_type=CURSOR_CROSS;
			if(units.unit[pointing].owner_id!=0) {
				if(canattack)
					cursor_type=CURSOR_ATTACK;
				else if(canreclamate)
					cursor_type=CURSOR_RECLAIM;
				else
					cursor_type=CURSOR_CANT_ATTACK;
				}
			else if(units.unit[pointing].port[BUILD_PERCENT_LEFT]>0.0f && builders)	cursor_type=CURSOR_REPAIR;

			if(mouse_b==1 && omb3!=1) {
				if(cursor_type==CURSOR_ATTACK) {
					for(i=0;i<=units.last_index;i++)
						if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel) {
							if(key[KEY_LSHIFT])
								units.unit[i].add_mission(MISSION_ATTACK,&(units.unit[pointing].Pos),false,0,&(units.unit[pointing]));
							else
								units.unit[i].set_mission(MISSION_ATTACK,&(units.unit[pointing].Pos),false,0,true,&(units.unit[pointing]));
							}
					}
				else if(cursor_type==CURSOR_REPAIR) {
					for(i=0;i<=units.last_index;i++)
						if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel) {
							sound_manager.play(unit_manager.unit_type[units.unit[i].type_id].repair,units.unit[i].Pos);
							if(key[KEY_LSHIFT])
								units.unit[i].add_mission(MISSION_REPAIR,&(units.unit[pointing].Pos),false,0,&(units.unit[pointing]));
							else
								units.unit[i].set_mission(MISSION_REPAIR,&(units.unit[pointing].Pos),false,0,true,&(units.unit[pointing]));
							}
					}
				}
			}
		}

	if(cursor_type==CURSOR_MOVE && mouse_b==1 && omb3 !=1 && mouse_x>128.0f && mouse_y>32.0f && mouse_y<SCREEN_H-32) {	// Le curseur donne un ordre
		units.give_order_move(0,cursor_on_map(&cam,map),!key[KEY_LSHIFT],map);
		}

	if(build>=0 && cursor_type==CURSOR_DEFAULT && mouse_b==1 && omb3 !=1 && mouse_x>128.0f && mouse_y>32.0f && mouse_y<SCREEN_H-32) {	// Le curseur donne un ordre
		units.give_order_build(0,build,cursor_on_map(&cam,map),!key[KEY_LSHIFT]);
		if(!key[KEY_LSHIFT])
			build=-1;
		}

//---------------------------------	Code de sélection d'unités

	if(mouse_x>128 && mouse_y>32 && mouse_y<SCREEN_H-32) {
		if(mouse_b==2 && omb3!=2) {					// Déselectionne les unités
			if(build>=0)
				build=-1;				// Sort du mode construction
			else {
				selected=false;
				cur_sel=-1;
				cur_sel_index=-1;
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].owner_id==0)			// On peut désélectionner les morts, ça ne change rien :-)
						units.unit[i].sel=false;
				}
			}
		}

	if(build==-1 && (cursor_type==CURSOR_DEFAULT || cursor_type==CURSOR_CROSS) && mouse_x>128 && (mouse_y>32 || selecting)
	   && (mouse_y<SCREEN_H-32 || selecting)) {		// Si le curseur est dans la zone de jeu
		if(mouse_b!=1 && selecting) {		// Récupère les unités présentent dans la sélection
			if(sel_x[0]==sel_x[1] && sel_y[0]==sel_y[1]) {
				int pointing=units.pick(&cam);		// Sélectionne une unité sur clic
				if(!key[KEY_LSHIFT])
					for(i=0;i<=units.last_index;i++)
						if(units.unit[i].flags!=0 && units.unit[i].owner_id==0)
							units.unit[i].sel=false;
				if(pointing>=0 && units.unit[pointing].port[BUILD_PERCENT_LEFT]==0.0f)		// On ne sélectionne pas les unités en construction
					units.unit[pointing].sel^=true;			// Sélectionne/Désélectionne si l'unité est déjà sélectionnée en appuyant sur SHIFT
				selected=false;
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].flags!=0 && units.unit[i].owner_id==0)
						selected|=units.unit[i].sel;
				}
			else
				selected=units.select(&cam,sel_x,sel_y);		// Séléction au lasso
			cur_sel=-1;
			cur_sel_index=-1;
			for(i=0;i<=units.last_index && cur_sel!=-2;i++)
				if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel)
					cur_sel= (cur_sel==-1) ? i : -2;
			if(cur_sel>=0) {
				cur_sel_index=cur_sel;
				cur_sel=units.unit[cur_sel].type_id;
						// Joue un son
				sound_manager.play(unit_manager.unit_type[cur_sel].select1,units.unit[i].Pos);
				}
			}
		selecting=false;
		if(mouse_b==1) {
			if(omb3!=1) {
				sel_x[0]=mouse_x;
				sel_y[0]=mouse_y;
				}
			sel_x[1]=mouse_x;
			sel_y[1]=mouse_y;
			selecting=true;
			}
		}
	else
		selecting=false;
	omb3=mouse_b;

	if(mouse_x<=128 || mouse_y<=32 || mouse_y>=SCREEN_H-32)	cursor_type=CURSOR_DEFAULT;

/*--------------bloc regroupant ce qui est relatif au temps-------------------*/

	float timetosimulate=dt*time_factor;								// Moteur physique
	for(float dt_sim=0.0f;dt_sim<timetosimulate;dt_sim+=TIME_UNIT) {
		float dt_r=min(timetosimulate-dt_sim,TIME_UNIT);
		if(particle)
			particle_engine.move(dt_r);					// Anime les particules
		units.move(dt_r,map);							// Anime les unités
		weapons.move(dt_r,map);							// Anime les armes
		}
	if(trees)
		features.move(timetosimulate);					// Anime les objets
	fx_manager.move(timetosimulate);

/*----------------------------------------------------------------------------*/

	cam.SetView();

	sun.Set(cam);
	sun.Enable();

	cam.SetView();
	glRotatef(Atimer*Conv*RAD2DEG,0.0f,1.0f,0.0f);
	glTranslatef(0.0f,60.0f,0.0f);

	cam.zfar*=100.0f;
	cam.SetView();
	glColor4f(1.0f,1.0f,1.0f,1.0f);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,sky);
	glDisable(GL_CULL_FACE);
	glDisable(GL_LIGHTING);
//	glDisable(GL_FOG);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f,0.0f);		glVertex3f(-2.0f*map->map_w,300.0f,-2.0f*map->map_h);
		glTexCoord2f(100.0f,0.0f);		glVertex3f(2.0f*map->map_w,300.0f,-2.0f*map->map_h);
		glTexCoord2f(100.0f,100.0f);	glVertex3f(2.0f*map->map_w,300.0f,2.0f*map->map_h);
		glTexCoord2f(0.0f,100.0f);		glVertex3f(-2.0f*map->map_w,300.0f,2.0f*map->map_h);
	glEnd();
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHTING);
	glEnable(GL_FOG);
	cam.zfar=500.0f;
	cam.SetView();

	if(build>=0 && mouse_x>128 && mouse_y>32 && mouse_y<SCREEN_H-32) {			// Affiche le bâtiment à construire
		VECTOR target=cursor_on_map(&cam,map);
		cam.SetView();
		glTranslatef(target.x,target.y,target.z);
		glScalef(unit_manager.unit_type[build].Scale,unit_manager.unit_type[build].Scale,unit_manager.unit_type[build].Scale);
		if(unit_manager.unit_type[build].model)
			unit_manager.unit_type[build].model->draw();
		}

	if(wireframe)
		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);

	map->draw(&cam,false,0.0f,t,dt*time_factor);

	if(wireframe)
		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);

	units.draw(&cam,map);			// Dessine les unités

	weapons.draw(&cam);			// Dessine les objets produits par les armes

	if(map->water) {
		glColor4f(1.0f,1.0f,1.0f,0.75f);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		map->draw(&cam,true,map->sealvl,t,dt*time_factor);
		glDisable(GL_BLEND);
		}

	if(selected && key[KEY_LSHIFT]) {
		cam.SetView();
		for(i=0;i<=units.last_index;i++)
			if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel)
				units.unit[i].show_orders();					// Dessine les ordres reçus par l'unité
		}

	if(trees)
		features.draw(&cam);		// Dessine les éléments "2D"
	fx_manager.draw(&cam);

	if(particle)
		particle_engine.draw(&cam,map->map_w,map->map_h,map->bloc_w,map->bloc_h,map->view);	// Dessine les particules

	if(shadow)
		if(shadow_quality<=1) {
			if(rotate_light) {
				sun.Dir.x=-1.0f;
				sun.Dir.y=1.0f;
				sun.Dir.z=1.0f;
				sun.Dir.Unit();
				VECTOR Dir=-sun.Dir;
				Dir.x=cos(light_angle);
				Dir.z=sin(light_angle);
				Dir.Unit();
				sun.Dir=-Dir;
				units.draw_shadow(&cam,Dir,map);
				}
			else {
				sun.Dir.x=-1.0f;
				sun.Dir.y=1.0f;
				sun.Dir.z=1.0f;
				sun.Dir.Unit();
				units.draw_shadow(&cam,-sun.Dir,map);
				}
			}
		else {
			float alpha=1.0f-exp((1.0f/shadow_quality)*log(0.5f));
			VECTOR Dir;
			if(rotate_light) {
				sun.Dir.x=-1.0f;
				sun.Dir.y=1.0f;
				sun.Dir.z=1.0f;
				sun.Dir.Unit();
				Dir=-sun.Dir;
				Dir.x=cos(light_angle);
				Dir.z=sin(light_angle);
				Dir.Unit();
				sun.Dir=-Dir;
				}
			else {
				sun.Dir.x=-1.0f;
				sun.Dir.y=1.0f;
				sun.Dir.z=1.0f;
				sun.Dir.Unit();
				Dir=-sun.Dir;
				}
			for(int i=0;i<shadow_quality;i++) {
				VECTOR RDir;
				RDir=Dir;
				RDir.x+=cos(i*PI*2.0f/shadow_quality)*shadow_r;
				RDir.z+=sin(i*PI*2.0f/shadow_quality)*shadow_r;
				RDir.Unit();
				units.draw_shadow(&cam,RDir,map,alpha);
				}
			}

	if(key[KEY_ESC]) done=true;
	if(key[KEY_TILDE]) {
		if(!tilde)
			Console->show^=true;
		tilde=true;
		}
	else
		tilde=false;

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	for(i=7;i>=0;i--) {
		glActiveTextureARB(GL_TEXTURE0_ARB+i);
		ReInitTexSys();
		glDisable(GL_TEXTURE_2D);
		}
	allegro_gl_set_allegro_mode();		// Affiche console, infos,...

	int signal=game_script.run(map,timetosimulate);

	switch(signal)
	{
	case 0:				// Rien de spécial
		break;
	case -1:			// Fin du script
		game_script.stop();
		break;
	case -2:			// Pause
		break;
	case -3:			// Attente d'un évènement
		break;
	case 1:				// Fin de partie (match nul)
		done=true;
		exit_mode=EXIT_NONE;
		break;
	case 2:				// Fin de partie (victoire)
		done=true;
		exit_mode=EXIT_VICTORY;
		break;
	case 3:				// Fin de partie (défaite)
		done=true;
		exit_mode=EXIT_DEFEAT;
		break;
	};

	if(selecting) {						// Affiche le rectangle de selection
		glDisable(GL_TEXTURE_2D);
		glBegin(GL_LINE_LOOP);
			glColor4f(0.0f,0.0f,0.0f,1.0f);
			glVertex2i(sel_x[0]+1,sel_y[0]+1);
			glVertex2i(sel_x[1]+1,sel_y[0]+1);
			glVertex2i(sel_x[1]+1,sel_y[1]+1);
			glVertex2i(sel_x[0]+1,sel_y[1]+1);
		glEnd();
		glBegin(GL_LINE_LOOP);
			glColor4f(1.0f,1.0f,1.0f,1.0f);
			glVertex2i(sel_x[0],sel_y[0]);
			glVertex2i(sel_x[1],sel_y[0]);
			glVertex2i(sel_x[1],sel_y[1]);
			glVertex2i(sel_x[0],sel_y[1]);
		glEnd();
		}

	if(cur_sel_index>=0 && cur_sel_index<=units.last_index && units.unit[cur_sel_index].flags==0) {
		cur_sel=-1;
		cur_sel_index=-1;
		}

	int n=cur_sel;
	if(n>=0 && units.unit[cur_sel_index].port[BUILD_PERCENT_LEFT]>0.0f)		// Unité non terminée
		n=-1;
	int sel=unit_manager.unit_build_menu(n,omb2);		// Menu correspondant à l'unité
	units.complete_menu(cur_sel_index);
	units.menu_order(omb2);
	players.show_resources();

	if(sel>=0 && mouse_b==2 && omb2!=2) {
		MISSION *cur=units.unit[cur_sel_index].mission;
		MISSION **old=&(units.unit[cur_sel_index].mission);
		int nb=1;
		if(key[KEY_LSHIFT]) nb=5;
		if(cur)
			while(cur->next) {
				if(cur->mission==MISSION_BUILD && cur->data==sel) {			// Efface un ordre
					*old=cur->next;
					free(cur);
					nb--;
					if(nb==0)
						break;
					}
				old=&(cur->next);
				cur=cur->next;
				}
		}
	if(sel>=0 && mouse_b==1 && omb2!=1) {
		build=sel;
		if(unit_manager.unit_type[cur_sel].TEDclass==CLASS_PLANT) {			// S'il s'agit d'un bâtiment
			if(key[KEY_LSHIFT])
				for(i=0;i<5;i++)
					units.give_order_build(0,build,units.unit[cur_sel_index].Pos,false);
			else
				units.give_order_build(0,build,units.unit[cur_sel_index].Pos,false);
			build=-1;
			}
		}

	omb2=mouse_b;

	map->draw_mini(0,0,128,128,&cam);	// Mini-carte
	units.draw_mini(map->map_w,map->map_h,map->mini_w,map->mini_h);
	weapons.draw_mini(map->map_w,map->map_h,map->mini_w,map->mini_h);

	glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
	glEnable(GL_BLEND);
	if(show_script)
	if(cur_sel>=0 && unit_manager.unit_type[cur_sel].script) {
		float Y=32.0f;
		allegro_gl_printf(aglfont,128.0f,Y,0.0f,0xFFFFFF,"%d scripts",unit_manager.unit_type[cur_sel].script->nb_script);	Y+=9.0f;
		for(i=0;i<unit_manager.unit_type[cur_sel].script->nb_script;i++) {
			allegro_gl_printf(aglfont,128.0f,Y,0.0f,0xFFFFFF,"%d %s",i,unit_manager.unit_type[cur_sel].script->name[i]);	Y+=9.0f;
			}
		}

	if(show_model && cur_sel>=0 && unit_manager.unit_type[cur_sel].model)
		unit_manager.unit_type[cur_sel].model->print_struct(32.0f,128.0f,aglfont);

/*	if(n>=0 && unit_manager.unit_type[cur_sel].weapon[0]!=NULL && unit_manager.unit_type[cur_sel].weapon[0]->name!=NULL)
		allegro_gl_printf(aglfont,128.0f,32.0f,0.0f,0xFFFFFF,"arme ok");
	else if(n>=0 && unit_manager.unit_type[cur_sel].weapon[0]!=NULL)
		allegro_gl_printf(aglfont,128.0f,32.0f,0.0f,0xFFFFFF,"arme sans nom");
	else
		allegro_gl_printf(aglfont,128.0f,32.0f,0.0f,0xFFFFFF,"pas d'arme");*/

/*	if(selected && cur_sel!=-1) {					// Sur les unités sélectionnées
		char *unit_info[]={"MISSION_STANDBY","MISSION_VTOL_STANDBY","MISSION_GUARD_NOMOVE","MISSION_MOVE","MISSION_BUILD","MISSION_BUILD_2","MISSION_STOP","MISSION_REPAIR","MISSION_ATTACK",
							"MISSION_AIM","MISSION_SHOOT"};
		for(i=0;i<=units.last_index;i++)
			if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel) {
				if(units.unit[i].mission!=NULL && units.unit[i].mission->mission>=0 && units.unit[i].mission->mission<=10)
					printf("MISSION: %s\n",unit_info[units.unit[i].mission->mission]);
				else
					printf("MISSION: NONE\n");
				}
		}*/

	glDisable(GL_BLEND);

	char *cmd=NULL;
	if(!shoot)
		cmd=Console->draw(aglfont,dt,text_height(font));			// Affiche la console

	float Y=0.0f;

	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR);

	if(showfps) {
		allegro_gl_printf(aglfont,0.0f,Y,0.0f,0xFFFFFF,"fps: %d",fps);
		Y+=9.0f;
		}

	glDisable(GL_BLEND);

	if(mouse_b!=4)
		draw_cursor();

	allegro_gl_unset_allegro_mode();

	allegro_gl_flip();

	if(shoot) {
		BITMAP *shoot_bmp=create_bitmap(SCREEN_W,SCREEN_H);
		blit(screen,shoot_bmp,0,0,0,0,SCREEN_W,SCREEN_H);
		char nom[100];
		nom[0]=0;
		strcat(nom,"shoot00");
		nom[strlen(nom)-2]+=nb_shoot/10;
		nom[strlen(nom)-1]+=nb_shoot%10;
		nb_shoot=++nb_shoot%100;
		strcat(nom,".pcx");
		save_bitmap(nom,shoot_bmp,NULL);
		destroy_bitmap(shoot_bmp);
		shoot=false;
		}

	if(cmd) {		// Analyse les commandes tapées dans la console
		if(strstr(cmd,"fps_on")) showfps=true;				// Affiche le nombre d'images/seconde
		if(strstr(cmd,"fps_off")) showfps=false;				// Cache le nombre d'images/seconde
		if(strstr(cmd,"shoot")) shoot=true;					// Prend une capture d'écran
		if(strstr(cmd,"exit")) done=true;					// Quitte le programme
		if(strstr(cmd,"wireframe")) wireframe^=true;
		if(strstr(cmd,"priority")) priority_level=atoi(strstr(cmd,"priority")+9);
		if(strstr(cmd,"shadow quality")) shadow_quality=atoi(strstr(cmd,"shadow quality")+15);
		else if(strstr(cmd,"shadow ray")) shadow_r=atof(strstr(cmd,"shadow ray")+11);
		else if(strstr(cmd,"shadow")) shadow^=true;
		if(strstr(cmd,"particle")) particle^=true;
		if(strstr(cmd,"trees")) trees^=true;
		if(strstr(cmd,"show script")) show_script^=true;
		if(strstr(cmd,"show model")) show_model^=true;
		if(strstr(cmd,"rotate light")) rotate_light^=true;
		if(strstr(cmd,"freecam")) {
			freecam^=true;
			if(!freecam)	r2=0.0f;
			if(freecam)		or1=r1;
			else
				r1=or1;
			}
		if(strstr(cmd,"fps_limit ")) {
			speed_limit=atoi(strstr(cmd,"fps_limit ")+10);
			if(speed_limit==0.0f) speed_limit=-1.0f;
			delay=1.0f/speed_limit;
			}
		if(strstr(cmd,"spawn ")) {
			int nb_to_spawn=atoi(strstr(cmd,"spawn ")+6);
			for(i=0;i<nb_to_spawn;i++) {
				int id=units.create(rand()%unit_manager.nb_unit,0);
				units.unit[id].Pos.x=(rand()%map->map_w)-0.5f*map->map_w;
				units.unit[id].Pos.z=(rand()%map->map_h)-0.5f*map->map_h;
				}
			}
		if(strstr(cmd,"spawn_enemy ")) {
			int nb_to_spawn=atoi(strstr(cmd,"spawn_enemy ")+12);
			for(i=0;i<nb_to_spawn;i++) {
				int id=units.create(rand()%unit_manager.nb_unit,1);
				units.unit[id].Pos.x=(rand()%map->map_w)-0.5f*map->map_w;
				units.unit[id].Pos.z=(rand()%map->map_h)-0.5f*map->map_h;
				}
			}
		if(strstr(cmd,"timefactor "))
			time_factor=atof(strstr(cmd,"timefactor ")+11);
		if(strstr(cmd,"addhp "))
			if(selected) {					// Sur les unités sélectionnées
				int value=atoi(strstr(cmd,"addhp ")+6);
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel) {
						units.unit[i].hp+=value;
						if(units.unit[i].hp<0)
							units.unit[i].hp=0;
						else if(units.unit[i].hp>unit_manager.unit_type[units.unit[i].type_id].MaxDamage)
							units.unit[i].hp=unit_manager.unit_type[units.unit[i].type_id].MaxDamage;
						}
				}
		if(strstr(cmd,"reset_script"))							// Réinitialise les scripts
			if(selected) {					// Sur les unités sélectionnées
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel)
						units.unit[i].reset_script();
				}
		if(strstr(cmd,"unitinfo"))
			if(selected && cur_sel!=-1) {					// Sur les unités sélectionnées
				char *unit_info[]={"ACTIVATION","STANDINGMOVEORDERS","STANDINGFIREORDERS","HEALTH","INBUILDSTANCE","BUSY","PIECE_XZ","PIECE_Y","UNIT_XZ","UNIT_Y","UNIT_HEIGHT","XZ_ATAN","XZ_HYPOT","ATAN",
								   "HYPOT","GROUND_HEIGHT","BUILD_PERCENT_LEFT","YARD_OPEN","BUGGER_OFF","ARMORED"};
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel)
						for(int e=1;e<21;e++)
							printf("%s=%f\n",unit_info[e-1],units.unit[i].port[e]);
				}
		if(strstr(cmd,"kill"))							// Détruit les unités sélectionnées
			if(selected) {					// Sur les unités sélectionnées
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel)
						units.kill(i,map);
				selected=false;
				cur_sel=-1;
				}
		if(strstr(cmd,"script "))							// Force l'éxecution d'un script
			if(selected) {					// Sur les unités sélectionnées
				int id=atoi(strstr(cmd,"script ")+7);
				for(i=0;i<=units.last_index;i++)
					if(units.unit[i].flags!=0 && units.unit[i].owner_id==0 && units.unit[i].sel)
						units.unit[i].launch_script(id);
				}

		if(strstr(cmd,"metal")) cheat_metal^=true;				// cheat codes
		if(strstr(cmd,"energy")) cheat_energy^=true;			// cheat codes
		}
	if(cheat_metal) players.metal[0]=players.metal_s[0];				// cheat codes
	if(cheat_energy) players.energy[0]=players.energy_s[0];				// cheat codes
	if(key[KEY_F12])
		shoot=true;

/*------------ Code de gestion du déroulement de la partie -----------------------------------*/

	if(signal==-1) {			// Si le script est terminé, on reprend les règles standard
		bool win=true;
		for(i=1;i<players.nb_player;i++)
			if(!players.annihilated[i])	{
				win=false;
				break;
				}
		if(win) {						// Victoire
			done=true;
			exit_mode=EXIT_VICTORY;
			}
		if(players.annihilated[0]) {	// Défaite
			done=true;
			exit_mode=EXIT_DEFEAT;
			}
		}

}while(!done);

#if USE_MP3
//almp3_destroy_mp3(mp3);
almp3_destroy_mp3stream(mp3);
if(mp3_file)
	pack_fclose(mp3_file);
if(data)
	free(data);
if(play_list) {
	for(i=0;i<nb_mp3;i++)
		free(play_list[i]);
	free(play_list);
	}
#endif

glDeleteTextures(1,&sky);
glDeleteTextures(1,&glow);

printf("nombre de models chargés: %d \n",model_manager.nb_models);
printf("nombre d'unités chargées: %d \n",unit_manager.nb_unit);
model_manager.destroy();
unit_manager.destroy();
particle_engine.destroy();
units.destroy();
weapons.destroy();
sound_manager.destroy();
fx_manager.destroy();

players.destroy();

map->destroy();
delete(map);

printf("nombre de textures chargées: %d \n",texture_manager.nbtex);

weapon_manager.destroy();

feature_manager.destroy();

features.destroy();

texture_manager.destroy();

switch(exit_mode)
{
case EXIT_NONE:
	break;
case EXIT_VICTORY:
	break;
case EXIT_DEFEAT:
	break;
};

}

int anim_cursor(int type)
{
	if(type==-1)
		return (Atimer-start>>17)%cursor.anm[cursor_type].nb_bmp;
	else
		return (Atimer-start>>17)%cursor.anm[type].nb_bmp;
}

void draw_cursor()
{
	int curseur=anim_cursor();
	int dx=cursor.anm[cursor_type].ofs_x[curseur];
	int dy=cursor.anm[cursor_type].ofs_y[curseur];
	int sx=cursor.anm[cursor_type].bmp[curseur]->w-1;
	int sy=cursor.anm[cursor_type].bmp[curseur]->h-1;
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	PutTex(cursor.anm[cursor_type].glbmp[curseur],mouse_x-dx,mouse_y-dy,mouse_x-dx+sx,mouse_y-dy+sy);
	glDisable(GL_BLEND);
}

VECTOR cursor_on_map(CAMERA *cam,MAP *map)
{
	VECTOR cur_dir;
	cur_dir=cam->Dir+2.0f*(mouse_x-SCREEN_W*0.5f)/SCREEN_W*cam->Side-1.5f*(mouse_y-SCREEN_H*0.5f)/SCREEN_H*cam->Up;
	cur_dir.Unit();		// Direction pointée par le curseur
	return map->hit(cam->Pos,cur_dir);
}
