/*  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*/

/*----------------------------------------------------------\
|                      EngineClass.h                        |
|    Contient toutes les classes nécessaires au moteur 3D   |
| et au moteur physique.                                    |
|                                                           |
\----------------------------------------------------------*/

#include "particles.h"			// Pour le moteur à particules
#include "tdf.h"				// Pour le gestionnaire de sprites

#ifndef ENGINE_CLASS		// Ne déclare pas 2 fois les classes du moteur
#define ENGINE_CLASS		// Définit les classes comme déclarées

#define PARTICLE_LIMIT		100000		// pas plus de 100000 particules
#define HMAP_RESIZE			0.04f

#define H_DIV		0.5f
float const transform=1.0f/tan(63.44f*DEG2RAD)/H_DIV;

extern float	player_color[30];

class BLOC				// Blocs composant la carte
{
public:
	int			nbindex;	// Nombre d'indices
	int			nbpoint;	// Nombre de points
	POINTF		*point;		// Points du bloc
	GLuint		*index;		// Tableau d'indices de ces points
	float		*texcoord;	// Coordonnées de texture
	GLuint		tex;		// Indice de texture OpenGl
	bool		lava;		// Indique si le bloc est de type lave

	void init()
	{
		nbindex=nbpoint=0;
		point=NULL;
		index=NULL;
		texcoord=NULL;
		tex=0;
		lava=false;
	}

	BLOC()
	{
		init();
	}

	void destroy()
	{
		if(point) delete(point);
		if(index) delete(index);
		if(texcoord) delete(texcoord);
		init();
	}
};

class SECTOR			// Structure pour regrouper les informations sur le terrain (variations d'altitude, submergé, teneur en metal, ...)
{
public:
	float	dh;					// dérivée maximale en valeur absolue de l'altitude
	bool	underwater;			// indique si le bloc est sous l'eau
	byte	metal;				// Teneur en metal du sol
	byte	view;				// Indique quels sont les joueurs voyant ce morceau de terrain
	int		stuff;				// Indique l'élément graphique présent sur ce secteur
	short	unit_idx;			// Indice de l'unité qui se trouve sur ce secteur

	inline void init()
	{
		dh=0.0f;
		underwater=false;
		metal=0;
		view=0;
		stuff=-1;
		unit_idx=-1;
	}
};

#include "pathfinding.h"		// Algorithme de pathfinding

class MAP				// Données concernant la carte
{
public:
	int			ntex;		// Indique si la texture est chargée et doit être détruite
	GLuint		*tex;		// Texture de surface
	int			nbbloc;		// Nombre de blocs
	BLOC		*bloc;		// Blocs composant le terrain
	unsigned short	**bmap;		// Tableau d'indice des blocs
	float		**h_map;	// Tableau de l'élévation du terrain
	float		**ph_map;	// Tableau du relief projeté pour le calcul inverse(projection) lors de l'affichage
	SECTOR		**map_data;	// Tableau d'informations sur le terrain
	byte		**view;		// Indique quels sont les parcelles de terrain visibles à l'écran
	byte		**path;		// Tableau pour le pathfinding
	int			map_w;		// Largeur de la carte en 16ème de bloc
	int			map_h;		// Hauteur de la carte en 16ème de bloc
	int			bloc_w;		// Largeur en blocs
	int			bloc_h;		// Hauteur en blocs
	BITMAP		*mini;		// Minimap
	GLuint		glmini;		// Texture OpenGl pour la minimap
	int			mini_w;
	int			mini_h;
	float		sealvl;		// Niveau de la mer
	POINTF		**lvl;		// Bloc de flottants pour le relief de la carte
	bool		water;		// Indique qu'il faut dessiner la mer
	bool		tnt;		// Indique si la carte est format tnt(format de total annihilation)
	float		sea_dec;	// Décalage de la mer
	int			ox1,ox2;	// Coordonnées de la dernière fenêtre de terrain dessinée
	int			oy1,oy2;

	void init()
	{
		path=NULL;
		mini_w=mini_h=252;
		ntex=0;
		tex=NULL;
		nbbloc=0;
		bloc=NULL;
		mini=NULL;
		bmap=NULL;
		h_map=NULL;
		ph_map=NULL;
		map_data=NULL;
		sealvl=0.0f;
		glmini=0;
		lvl=NULL;
		water=true;
		tnt=false;			// Laisse la possibilité de créer un autre format de cartes
		sea_dec=0.0f;
		view=NULL;
		ox1=ox2=oy1=oy2=0;
	}

	MAP()
	{
		init();
	}

	void destroy()
	{
		if(path && bloc_w && bloc_h) {
			free(path[0]);
			free(path);
			}
		if(view && bloc_w && bloc_h) {
			free(view[0]);
			free(view);
			}
		if(map_data && bloc_w && bloc_h) {
			free(map_data[0]);
			free(map_data);
			}
		if(ph_map && bloc_w && bloc_h) {
			free(ph_map[0]);		// la carte est allouée d'un seul bloc
			free(ph_map);
			}
		if(h_map && bloc_w && bloc_h) {
			free(h_map[0]);		// la carte est allouée d'un seul bloc
			free(h_map);
			}
		if(bmap && bloc_w && bloc_h) {
			free(bmap[0]);		// la carte est allouée d'un seul bloc
			free(bmap);
			}
		if(ntex>0) {
			for(int i=0;i<ntex;i++)
				glDeleteTextures(1,&(tex[i]));
			free(tex);
			}
		if(lvl) {
			for(int i=0;i<bloc_h*bloc_w;i++)
				free(lvl[i]);
			free(lvl);
			}
		if(bloc && nbbloc>0) {
			free(bloc[0].index);
			for(int i=0;i<nbbloc;i++) {
				bloc[i].point=NULL;
				bloc[i].index=NULL;
				bloc[i].destroy();
				}
			free(bloc);
			}
		if(mini) {
			glDeleteTextures(1,&glmini);
			destroy_bitmap(mini);
			}
		init();
	}

	~MAP()
	{
		destroy();
	}

/*	void build(BITMAP *hmap,BITMAP *bmptex=NULL,bool lmap=true)
	{
		destroy();
		if(lmap) {				// Crée la texture de luminosité
			BITMAP *lmap=create_bitmap_ex(8,hmap->w,hmap->h);
			VECTOR light;				// Vecteur lumière
			light.x=1.0f;	light.y=-1.0f;	light.z=1.0f;	light.Unit();
			VECTOR AB,AC,N;				// Vecteur pour le calcul des normales
			for(int y=0;y<hmap->h;y++)
				for(int x=0;x<hmap->w;x++) {
					AB.z=0.0f;
					AB.x=1.0f;
					if(x<hmap->w-1)
						AB.y=(hmap->line[y][x]-hmap->line[y][x+1])*HMAP_RESIZE;
					else
						AB.y=(hmap->line[y][x-1]-hmap->line[y][x])*HMAP_RESIZE;
					AC.z=1.0f;
					AC.x=0.0f;
					if(y<hmap->h-1)
						AC.y=(hmap->line[y][x]-hmap->line[y+1][x])*HMAP_RESIZE;
					else
						AC.y=(hmap->line[y-1][x]-hmap->line[y][x])*HMAP_RESIZE;
					N=AB*AC;
					N.Unit();
					int l=255.0f*(N%light);
					if(l<0) l=0;
					lmap->line[y][x]=l;
					}
			dltex=true;
			allegro_gl_set_texture_format(GL_LUMINANCE8);
			lightmap=allegro_gl_make_texture(lmap);
			glBindTexture(GL_TEXTURE_2D,lightmap);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
			destroy_bitmap(lmap);
			}
		allegro_gl_set_texture_format(-1);		// Format de texture par defaut
		if(bmptex) {			// Crée la texture de surface
			ntex=1;
			tex=(GLuint*) malloc(sizeof(GLuint));
			tex[0]=allegro_gl_make_texture(bmptex);
			glBindTexture(GL_TEXTURE_2D,tex[0]);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
			}
		int x,y,i;
		map_w=hmap->w;			// Dimensions de la carte
		map_h=hmap->h;
		bloc_w=(map_w-1>>4)+1;
		bloc_h=(map_h-1>>4)+1;
		nbbloc=bloc_w*bloc_h;
		bloc=(BLOC*) malloc(sizeof(BLOC)*nbbloc);
		bmap=(short**) malloc(sizeof(short*)*bloc_h);
		bmap[0]=(short*) malloc(sizeof(short)*bloc_w*bloc_h);
		for(i=1;i<bloc_h;i++)
			bmap[i]=&(bmap[0][bloc_w*i]);
		h_map=(byte**) malloc(sizeof(byte*)*bloc_h<<1);
		h_map[0]=(byte*) malloc(sizeof(byte)*bloc_w*bloc_h<<1);
		for(i=1;i<bloc_h;i++)
			h_map[i]=&(h_map[0][bloc_w*i<<1]);

		for(y=0;y<bloc_h;y++)			// Crée les blocs
			for(x=0;x<bloc_w;x++) {
				i=y*bloc_w+x;
				bmap[y<<1][x<<1]=i;
				h_map[(y<<1)][(x<<1)]=0;
				h_map[(y<<1)][(x<<1)+1]=0;
				h_map[(y<<1)+1][(x<<1)]=0;
				h_map[(y<<1)+1][(x<<1)+1]=0;
				bloc[i].init();
				if(ntex>0)
					bloc[i].tex=tex[0];
				int mx,my;
				mx=map_w-x*16;	my=map_h-y*16;
				if(mx>16) mx=16;
				if(my>16) my=16;
				bloc[i].nbpoint=(mx+1)*(my+1);
				bloc[i].nbindex=((mx+1)*2-1)*my+1;
				bloc[i].point=new POINTF[bloc[i].nbpoint];
				bloc[i].index=new GLuint[bloc[i].nbindex];
				if(bmptex)
					bloc[i].texcoord=new float[bloc[i].nbpoint<<1];
				int pos=0,X,Y;
				for(Y=0;Y<=my;Y++)										// Crée le tableau des coordonnées
					for(X=0;X<=mx;X++) {
						bloc[i].point[pos].x=X;
						bloc[i].point[pos].z=Y;
//						bloc[i].point[pos].x=x*16+X-0.5f*hmap->w;
//						bloc[i].point[pos].z=y*16+Y-0.5f*hmap->h;
						if(X+16*x<map_w && Y+16*y<map_h)
							bloc[i].point[pos].y=hmap->line[y*16+Y][x*16+X]*HMAP_RESIZE;	// Utilise la heightmap
						else
							bloc[i].point[pos].y=bloc[i].point[pos-1].y;			// Utilise le point d'avant
						bloc[i].texcoord[pos<<1]=(x*16.0f+X)/hmap->w;
						bloc[i].texcoord[(pos<<1)+1]=(y*16.0f+Y)/hmap->h;
						pos++;
						}
				pos=0;
				bloc[i].index[pos++]=0;									// Crée le tableau d'indices
				for(Y=0;Y<my;Y++)
					for(X=0;X<=mx;X++) {
						int MX=X;
						if(Y&1) MX=mx-X;
						if(X!=0)
							bloc[i].index[pos++]=Y*(mx+1)+MX;
						bloc[i].index[pos++]=(Y+1)*(mx+1)+MX;
						}
				}
	}*/

	void draw_mini(int x1=0,int y1=0,int w=252,int h=252,CAMERA *cam=NULL);			// Dessine la mini-carte

	inline float get_unit_h(float x,float y)
	{
		x=(x+0.5f*map_w)*0.125f;		// Convertit les coordonnées
		y=(y+0.5f*map_h)*0.125f;
		if(x<0.0f) x=0.0f;
		if(y<0.0f) y=0.0f;
		int lx=(bloc_w<<1)-1;
		int ly=(bloc_h<<1)-1;
		if(x>=lx) x=lx-1;
		if(y>=ly) y=ly-1;
		float h[4];
		int X=(int)x,Y=(int)y;
		float dx=x-X;
		float dy=y-Y;
		h[0]=h_map[Y][X];
		if(X+1<lx)
			h[1]=h_map[Y][X+1];
		else
			h[1]=h[0];
		if(Y+1<ly) {
			h[2]=h_map[Y+1][X];
			if(X+1<lx)
				h[3]=h_map[Y+1][X+1];
			else
				h[3]=h[2];
			}
		else {
			h[2]=h[0];
			h[3]=h[1];
			}
		h[0]=h[0]+(h[1]-h[0])*dx;
		h[2]=h[2]+(h[3]-h[2])*dx;
		return h[0]+(h[2]-h[0])*dy;
	}

	inline float get_h(int x,int y)
	{
		if(x<0) x=0;
		if(y<0) y=0;
		if(x>=(bloc_w<<1)-1) x=(bloc_w<<1)-2;
		if(y>=(bloc_h<<1)-1) y=(bloc_h<<1)-2;
		return h_map[y][x];
	}

	inline float get_zdec(int x,int y)
	{
		if(x<0) x=0;
		if(y<0) y=0;
		if(x>=(bloc_w<<1)-1) x=(bloc_w<<1)-2;
		if(y>=(bloc_h<<1)-1) y=(bloc_h<<1)-2;
		return ph_map[y][x]*transform*H_DIV;//*0.5f;
	}

	inline float get_nh(int x,int y)
	{
		if(x<0) x=0;
		if(y<0) y=0;
		if(x>=(bloc_w<<1)-1) x=(bloc_w<<1)-2;
		if(y>=(bloc_h<<1)-1) y=(bloc_h<<1)-2;
		return ph_map[y][x];
	}

	inline void rect(int x1,int y1,int w,int h,short c,char *yardmap=NULL,bool open=false)
	{
		if(yardmap==NULL) {
			int y2=y1+h;
			int x2=x1+w;
			if(y1<0)	y1=0;
			if(y2>(bloc_h<<1)-1)	y2=(bloc_h<<1)-1;
			if(x1<0)	x1=0;
			if(x2>(bloc_w<<1)-1)	x2=(bloc_w<<1)-1;
			for(int y=y1;y<y2;y++)
				for(int x=x1;x<x2;x++)
					map_data[y][x].unit_idx=c;
			}
		else {
			int i=0;
			int y2=y1+h;
			int x2=x1+w;
			if(y1<0)	{	i-=y1*w;	y1=0;	}
			if(y2>(bloc_h<<1)-1)	y2=(bloc_h<<1)-1;
			if(x1<0)	x1=0;
			if(x2>(bloc_w<<1)-1)	x2=(bloc_w<<1)-1;
			int dw=w-(x2-x1);
			for(int y=y1;y<y2;y++) {
				for(int x=x1;x<x2;x++) {
					if(yardmap[i]=='G' || yardmap[i]=='o' || yardmap[i]=='w' || yardmap[i]=='f' || (yardmap[i]=='c' && !open) || (yardmap[i]=='C' && !open) || (yardmap[i]=='O' && open))
						map_data[y][x].unit_idx=c;
					i++;
					}
				i+=dw;
				}
/*				if(y>=0 && y<(bloc_h<<1)-1) {
					for(int x=x1;x<x2;x++) {
						if(x>=0 && x<(bloc_w<<1)-1 &&
							(yardmap[i]=='G' || yardmap[i]=='o' || yardmap[i]=='w' || yardmap[i]=='f' || (yardmap[i]=='c' && !open) || (yardmap[i]=='C' && !open) || (yardmap[i]=='O' && open)))
							map_data[y][x].unit_idx=c;
						i++;
						}
					}
				else
					i+=w;*/
			}
	}

	inline bool check_rect(int x1,int y1,int w,int h,short c)
	{
		int y2=y1+h;
		int x2=x1+w;
		if(y1<0)	y1=0;
		if(y2>(bloc_h<<1)-1)	y2=(bloc_h<<1)-1;
		if(x1<0)	x1=0;
		if(x2>(bloc_w<<1)-1)	x2=(bloc_w<<1)-1;
		for(int y=y1;y<y2;y++)
			for(int x=x1;x<x2;x++)
				if(map_data[y][x].unit_idx!=c && map_data[y][x].unit_idx!=-1)
					return false;
		return true;
	}

	void draw(CAMERA *cam,bool FLAT=false,float niv=0.0f,float t=0.0f,float dt=1.0f);

	VECTOR hit(VECTOR Pos,VECTOR Dir,bool water=true);			// Calcule l'intersection d'un rayon avec la carte(le rayon partant du dessus de la carte)
};

extern int NB_PLAYERS;

class PLAYERS			// Classe pour gérer les joueurs et leurs statistiques de partie
{
public:
	int		nb_player;		// Nombre de joueurs (maximum 10 joueurs)
	char	*nom[10];		// Noms des joueurs
	char	*side[10];		// Camp des joueurs
	float	energy[10];		// Energie des joueurs
	float	metal[10];		// Metal des joueurs
	float	metal_u[10];	// Metal utilisé
	float	energy_u[10];	// Energie utilisée
	float	metal_t[10];	// Metal extrait
	float	energy_t[10];	// Energie produite
	int		kills[10];		// Victimes
	int		losses[10];		// Pertes
	int		energy_s[10];	// Capacités de stockage d'énergie
	int		metal_s[10];	// Capacités de stockage de metal
	int		com_metal;		// Stockage fournit par le commandeur
	int		com_energy;
	bool	commander[10];	// Indique s'il y a un commandeur
	bool	annihilated[10];	// Le joueur a perdu la partie??

	inline void clear()		// Remet à 0 la taille des stocks
	{
		for(int i=0;i<nb_player;i++) {
			energy_s[i]=metal_s[i]=0;			// Stocks
			metal_t[i]=energy_t[i]=0.0f;		// Production
			metal_u[i]=energy_u[i]=0.0f;		// Consommation
			commander[i]=false;
			annihilated[i]=true;
			}
	}

	inline int add(char *NOM,char *SIDE)
	{
		if(nb_player>=10)	return -1;		// Trop de joueurs déjà
		nom[nb_player]=strdup(NOM);
		side[nb_player++]=strdup(SIDE);
		NB_PLAYERS++;
	}

	inline void show_resources()
	{
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
		allegro_gl_printf(aglfont,130.0f,8.0f,0.0f,0xFFFFFF,"METAL");
		allegro_gl_printf(aglfont,130+(SCREEN_W-130>>1),8.0f,0.0f,0xFFFFFF,"ENERGY");
		allegro_gl_printf(aglfont,65+(SCREEN_W-130>>1),8.0f,0.0f,0xFFFFFF,"%d",(int)metal[0]);
		allegro_gl_printf(aglfont,65+(SCREEN_W-130>>1),16.0f,0.0f,0xFFFFFF,"+%f",metal_t[0]);
		allegro_gl_printf(aglfont,65+(SCREEN_W-130>>1),24.0f,0.0f,0xFFFFFF,"-%f",metal_u[0]);
		allegro_gl_printf(aglfont,SCREEN_W-65,8.0f,0.0f,0xFFFFFF,"%d",(int)energy[0]);
		allegro_gl_printf(aglfont,SCREEN_W-65,16.0f,0.0f,0xFFFFFF,"+%f",energy_t[0]);
		allegro_gl_printf(aglfont,SCREEN_W-65,24.0f,0.0f,0xFFFFFF,"-%f",energy_u[0]);

		glDisable(GL_TEXTURE_2D);

		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glBegin(GL_QUADS);			// Dessine les barres de metal et d'énergie
			glColor4f(0.7f,0.7f,0.7f,0.5f);

			glVertex2f(180.0f,8.0f);					// Fond de la barre de metal
			glVertex2f(60+(SCREEN_W-130>>1),8.0f);
			glVertex2f(60+(SCREEN_W-130>>1),16.0f);
			glVertex2f(180.0f,16.0f);

			glVertex2f(186+(SCREEN_W-130>>1),8.0f);		// Fond de la barre d'énergie
			glVertex2f(SCREEN_W-70,8.0f);
			glVertex2f(SCREEN_W-70,16.0f);
			glVertex2f(186+(SCREEN_W-130>>1),16.0f);
		glEnd();
		glDisable(GL_BLEND);
		glBegin(GL_QUADS);			// Dessine les barres de metal et d'énergie
			glColor3f(1.0f,1.0f,0.0f);

			if(metal_s[0]) {
				glVertex2f(180.0f,8.0f);					// Barre de metal
				glVertex2f(180+((SCREEN_W-130>>1)-120)*metal[0]/metal_s[0],8.0f);
				glVertex2f(180+((SCREEN_W-130>>1)-120)*metal[0]/metal_s[0],16.0f);
				glVertex2f(180.0f,16.0f);
				}

			if(energy_s[0]) {
				glVertex2f(186+(SCREEN_W-130>>1),8.0f);		// Barre d'énergie
				glVertex2f(186+(SCREEN_W-130>>1)+(SCREEN_W-256-(SCREEN_W-130>>1))*energy[0]/energy_s[0],8.0f);
				glVertex2f(186+(SCREEN_W-130>>1)+(SCREEN_W-256-(SCREEN_W-130>>1))*energy[0]/energy_s[0],16.0f);
				glVertex2f(186+(SCREEN_W-130>>1),16.0f);
				}
		glEnd();
		glColor4f(1.0f,1.0f,1.0f,1.0f);
	}

	inline void init(int E=10000,int M=10000)		// Initialise les données des joueurs
	{
		com_metal=M;
		com_energy=E;
		nb_player=0;
		NB_PLAYERS=0;
		for(int i=0;i<10;i++) {
			nom[i]=NULL;
			side[i]=NULL;
			energy[i]=E;
			metal[i]=M;
			metal_u[i]=0;
			energy_u[i]=0;
			metal_t[i]=0;
			energy_t[i]=0;
			kills[i]=0;
			losses[i]=0;
			energy_s[i]=E;
			metal_s[i]=M;
			}
	}

	inline PLAYERS()
	{
		init();
	}

	inline void destroy()
	{
		for(int i=0;i<10;i++) {
			if(nom[i])	free(nom[i]);
			if(side[i])	free(side[i]);
			}
		init();
	}

	inline ~PLAYERS()
	{
		destroy();
	}
};

#include "fbi.h"
#include "weapons.h"

extern PLAYERS	players;		// Objet contenant les données sur les joueurs

void *create_unit(int type_id,int owner,VECTOR pos);

struct MISSION			// Structure pour stocker les ordres
{
	int			mission;
	PATH_NODE	*path;		// Chemin emprunté par l'unité si besoin pour la mission
	VECTOR		target;
	MISSION 	*next;		// Mission suivante
	bool		step;		// Etape d'une mission
	float		time;		// Temps écoulé depuis la création de l'ordre
	int			data;		// Données de l'ordre
	void		*p;
};

class SCRIPT_ENV_STACK			// Pile pour la gestion des scripts
{
public:
	int					var[15];
	int					signal_mask;
	int					cur;
	SCRIPT_ENV_STACK	*next;

	inline void init()
	{
		for(int i=0;i<15;i++)
			var[i]=0;
		cur=0;
		signal_mask=0;
		next=NULL;
	}

	inline SCRIPT_ENV_STACK()
	{
		init();
	}
};

class SCRIPT_ENV			// Classe pour la gestion de l'environnement des scripts
{
public:
	SCRIPT_STACK		*stack;			// Pile utilisée par les scripts
	SCRIPT_ENV_STACK	*env;			// Pile d'éxecution des scripts
	float				wait;
	bool				running;

	inline void init()
	{
		stack=NULL;
		env=NULL;
		wait=0.0f;
		running=false;
	}

	SCRIPT_ENV()
	{
		init();
	}

	inline void destroy()
	{
		while(stack) {
			SCRIPT_STACK *next=stack->next;
			delete stack;
			stack=next;
			}
		while(env) {
			SCRIPT_ENV_STACK *next=env->next;
			delete env;
			env=next;
			}
		init();
	}

	inline ~SCRIPT_ENV()
	{
		destroy();
	}

	inline void push(int v)
	{
		SCRIPT_STACK *new_stack=new SCRIPT_STACK;
		new_stack->next=stack;
		stack=new_stack;
		stack->val=v;
	}

	inline int pop()
	{
		if(stack==NULL) {			// Si la pile est vide, renvoie 0 et un message pour le débuggage
#ifdef DEBUG_MODE
			Console->AddEntry("interpréteur de scripts: pile vide\n");
#endif
			return 0;
			}
		int v=stack->val;
		SCRIPT_STACK *old=stack;
		stack=stack->next;
		delete old;
		return v;
	}
};

class UNIT				// Classe pour la gestion des unités
{
public:
	SCRIPT			*script;		// Scripts concernant l'unité
	int				s_var[25];		// Tableau de variables pour les scripts
	MODEL			*model;			// Modèle représentant l'objet
	byte			owner_id;		// Numéro du propriétaire de l'unité
	int				type_id;		// Type d'unité
	float			hp;				// Points de vide restant à l'unité
	VECTOR			Pos;			// Vecteur position
	VECTOR			V;				// Vitesse de l'unité
	VECTOR			Angle;			// Orientation dans l'espace
	VECTOR			V_Angle;		// Variation de l'orientation dans l'espace
	bool			sel;			// Unité sélectionnée?
	SCRIPT_DATA		data;			// Données pour l'animation de l'unité par le script
	float			port[21];		// Ports
	MISSION			*mission;		// Ordres de l'unité
	int				nb_running;		// Nombre de scripts lancés en même temps
	SCRIPT_ENV		script_env[25];	// Environnements des scripts
	byte			flags;			// Pour indiquer entre autres au gestionnaire d'unités si l'unité existe
	float			c_time;			// Compteur de temps entre 2 émissions de particules par une unité de construction
	bool			compute_coord;	// Indique s'il est nécessaire de recalculer les coordonnées du modèle 3d
	int				script_val[25];	// Tableau de valeurs retournées par les scripts
	int				idx;			// Indice dans le tableau d'unité
	VECTOR			aim_dir;		// Vecteur de visée

	inline void start_mission_script(int mission_type)
	{
		if(script==NULL)	return;
		switch(mission_type)
		{
		case MISSION_MOVE:
			launch_script(get_script_index("MotionControl"));
			launch_script(get_script_index("startmoving"));
			break;
		case MISSION_BUILD_2:
			launch_script(get_script_index("startbuilding"));
			break;
		};
	}

	inline void next_mission()
	{
		if(mission==NULL) {
			set_mission(unit_manager.unit_type[type_id].DefaultMissionType,false,0,false);
			return;
			}
		switch(mission->mission)		// Commandes de fin de mission
		{
		case MISSION_BUILD_2:
//			deactivate();
			break;
		};
		MISSION *old=mission;
		mission=mission->next;
		if(old->path)				// Détruit le chemin si nécessaire
			destroy_path(old->path);
		free(old);
		if(mission==NULL)
			set_mission(unit_manager.unit_type[type_id].DefaultMissionType);
		start_mission_script(mission->mission);
		c_time=0.0f;
	}

	inline void clear_mission()
	{
		if(mission==NULL)	return;
		MISSION *old=mission;
		mission=mission->next;
		if(old->path)				// Détruit le chemin si nécessaire
			destroy_path(old->path);
		free(old);
	}

	void add_mission(int mission_type,VECTOR *target=NULL,bool step=false,int dat=0,void *pointer=NULL,PATH_NODE *path=NULL);

	void set_mission(int mission_type,VECTOR *target=NULL,bool step=false,int dat=0,bool stopit=true,void *pointer=NULL,PATH_NODE *path=NULL);

	inline void compute_model_coord()
	{
		if(!compute_coord)	return;
		if(model==NULL)	return;		// S'il n'y a pas de modèle associé, on quitte la fonction
		compute_coord=false;
		MATRIX_4x4 M;
		float scale=unit_manager.unit_type[type_id].Scale;
		M=RotateZ(Angle.z*DEG2RAD)*RotateY(Angle.y*DEG2RAD)*RotateX(Angle.x*DEG2RAD)*Scale(scale);			// Matrice pour le calcul des positions des éléments du modèle de l'unité
		model->compute_coord(&data,&M);
	}

	inline void raise_signal(int signal)		// Tue les processus associés
	{
		SCRIPT_ENV_STACK *tmp;
		for(int i=0;i<nb_running;i++) {
			tmp=script_env[i].env;
			while(tmp) {
				if(tmp->signal_mask&signal) {
					tmp=script_env[i].env;
					while(tmp->signal_mask!=signal) {
						script_env[i].env=tmp->next;
						delete tmp;
						tmp=script_env[i].env;
						}
					script_env[i].env=tmp->next;
					delete tmp;
					tmp=script_env[i].env;
					}
				if(tmp)
					tmp=tmp->next;
				}
			if(script_env[i].env==NULL)
				script_env[i].running=false;
			}
	}

	inline void init(int unit_type=-1,int owner=-1)
	{
		compute_coord=true;
		c_time=0.0f;
		flags=1;
		sel=false;
		script=NULL;
		model=NULL;
		owner_id=owner;
		type_id=-1;
		hp=0.0f;
		V.x=V.y=V.z=0.0f;
		Pos=V;
		data.init();
		Angle.x=Angle.y=Angle.z=0.0f;
		V_Angle=Angle;
		nb_running=0;
		int i;
		for(i=0;i<25;i++) {
			script_env[i].init();
			script_env[i].running=false;
			}
		for(i=0;i<21;i++)
			port[i]=0;
		for(i=0;i<25;i++)
			s_var[i]=0;
		if(unit_type<0 || unit_type>=unit_manager.nb_unit)
			unit_type=-1;
		port[ACTIVATION]=0;
		mission=NULL;
		port[BUILD_PERCENT_LEFT]=0.0f;
		if(unit_type!=-1) {
			set_mission(MISSION_STANDBY);
			type_id=unit_type;
			model=unit_manager.unit_type[type_id].model;
			hp=unit_manager.unit_type[type_id].MaxDamage;
			script=unit_manager.unit_type[type_id].script;
			port[STANDINGMOVEORDERS]=unit_manager.unit_type[type_id].StandingMoveOrder;
			port[STANDINGFIREORDERS]=unit_manager.unit_type[type_id].StandingFireOrder;
			set_mission(unit_manager.unit_type[type_id].DefaultMissionType);
			if(script) {
				data.load(script->nb_piece);
				launch_script(get_script_index("create"));
				}
			}
	}

	inline UNIT()
	{
		init();
	}

	inline void destroy()
	{
		for(int i=0;i<nb_running;i++)
			script_env[i].destroy();
		while(mission) clear_mission();
		init();
		flags=0;
	}

	inline ~UNIT()
	{
		destroy();
	}

	void draw(CAMERA *cam,MAP *map);

	void draw_shadow(CAMERA *cam,VECTOR Dir,MAP *map);

	inline int get_script_index(char *script_name)			// Cherche l'indice du script dont on fournit le nom
	{
		if(script)
			for(int i=0;i<script->nb_script;i++)
				if(strcasecmp(script->name[i],script_name)==0)
					return i;
		return -1;
	}

	inline int launch_script(int id,int nb_param=0,int *param=NULL)
	{
		if(script==NULL)	return -2;
		if(id<0 || id>=script->nb_script)
			return -2;
		if(is_running(id)) return -1;			// Le script tourne déjà
		script_env[nb_running].init();
		script_env[nb_running].env=new SCRIPT_ENV_STACK;
		script_env[nb_running].env->init();
		script_env[nb_running].env->cur=id;
		script_env[nb_running].running=true;
		if(nb_param>0 && param!=NULL)
			for(int i=0;i<nb_param;i++)
				script_env[nb_running].env->var[i]=param[i];
		return nb_running++;
	}

	inline bool is_running(int script_index)
	{
		if(script==NULL)	return false;
		if(script_index<0 || script_index>=script->nb_script)	return false;
		for(int i=0;i<nb_running;i++)
			if(script_env[i].running && script_env[i].env!=NULL) {
				SCRIPT_ENV_STACK *current=script_env[i].env;
				while(current) {
					if((current->cur&0xFF)==script_index)
						return true;
					current=current->next;
					}
				}
		return false;
	}

	inline void reset_script()
	{
		for(int i=0;i<nb_running;i++)
			script_env[i].destroy();
		nb_running=0;
	}

	void run_script(float dt,int id,MAP *map);			// Interprète les scripts liés à l'unité

	int move(float dt,MAP *map=NULL);

	void show_orders();				// Dessine les ordres reçus

	inline void activate()
	{
		if(port[ACTIVATION]==0.0f) {
			sound_manager.play(unit_manager.unit_type[type_id].activate,Pos);
			launch_script(get_script_index("Activate"));
			port[ACTIVATION]=1.0f;
			}
	}

	inline void deactivate()
	{
		if(port[ACTIVATION]!=0.0f) {
			sound_manager.play(unit_manager.unit_type[type_id].deactivate,Pos);
			launch_script(get_script_index("Deactivate"));
			port[ACTIVATION]=0.0f;
			}
	}

	void shoot(int target,VECTOR startpos,VECTOR Dir,int w_id=0);

	bool hit(VECTOR P,VECTOR Dir,VECTOR *hit_vec);
};

class INGAME_UNITS		// Classe pour la gestion massive des unités pendant la partie
{
public:
	int		nb_unit;		// Nombre d'unités
	int		max_unit;		// Nombre maximum d'unités stockables dans le tableau
	UNIT	*unit;			// Tableau contenant les références aux unités
	int		last_index;		// Dernier indice contenant une unité

	inline void init()
	{
		nb_unit=0;
		unit=NULL;
		max_unit=0;
		last_index=-1;
	}

	inline INGAME_UNITS()
	{
		init();
	}

	inline void destroy()
	{
		if(max_unit>0 && unit)			// Détruit une à une les unités
			for(int i=0;i<max_unit;i++)
				unit[i].destroy();
		if(unit)
			free(unit);
		init();
	}

	inline ~INGAME_UNITS()
	{
		destroy();
	}

	inline void kill(int index,MAP *map)			// Détruit une unité
	{
		if(index<0 || index>last_index)	return;		// On ne peut pas détruire une unité qui n'existe pas
		if(unit[index].flags) {
			map->rect((((int)(unit[index].Pos.x+map->map_w*0.5f))>>3)-(unit_manager.unit_type[unit[index].type_id].FootprintX>>1),(((int)(unit[index].Pos.z+map->map_h*0.5f))>>3)-(unit_manager.unit_type[unit[index].type_id].FootprintZ>>1),unit_manager.unit_type[unit[index].type_id].FootprintX,unit_manager.unit_type[unit[index].type_id].FootprintZ,-1);
			unit[index].destroy();		// Détruit l'unité
			}
		if(index==last_index)			// Redétermine le dernier indice
			while(last_index>=0 && unit[last_index].flags==0)	last_index--;
		nb_unit--;		// Unité détruite
	}

	inline void draw(CAMERA *cam,MAP *map)					// Dessine les unités visibles
	{
		if(nb_unit<=0 || unit==NULL)	return;		// Pas d'unités à dessiner

		glEnable(GL_LIGHTING);
		glDisable(GL_BLEND);
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		for(int i=0;i<=last_index;i++)
			if(unit[i].flags) {				// Si il y a une unité
				unit[i].draw(cam,map);
				players.annihilated[unit[i].owner_id]=false;
				}
	}

	inline void draw_shadow(CAMERA *cam,VECTOR Dir,MAP *map,float alpha=0.5f)					// Dessine les ombres des unités visibles
	{
		if(nb_unit<=0 || unit==NULL)	return;		// Pas d'unités à dessiner

		if(g_useStencilTwoSide) {					// Si l'extension GL_EXT_stencil_two_side est disponible
			glEnable(GL_STENCIL_TEST);
			glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
			glDisable(GL_CULL_FACE);
			glClear(GL_STENCIL_BUFFER_BIT);
			glDepthMask(GL_FALSE);
			glColorMask(0,0,0,0);
			glStencilFunc(GL_ALWAYS,128, 0xffffffff);

			glActiveStencilFaceEXT(GL_FRONT);
			glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
			glActiveStencilFaceEXT(GL_BACK);
			glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);

			for(int i=0;i<=last_index;i++)
				if(unit[i].flags)				// Si il y a une unité
					unit[i].draw_shadow(cam,Dir,map);
			}
		else {										// Si elle ne l'est pas
			glDepthMask(GL_FALSE);
			glColorMask(0,0,0,0);

			glClear(GL_STENCIL_BUFFER_BIT);
			glEnable(GL_STENCIL_TEST);
			glStencilFunc(GL_ALWAYS,128, 0xffffffff);
			glEnable(GL_CULL_FACE);
			GLuint list;

			for(int i=0;i<=last_index;i++)
				if(unit[i].flags) {				// Si il y a une unité
					glFrontFace(GL_CW);
					glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
					list=glGenLists(1);
					glNewList(list,GL_COMPILE_AND_EXECUTE);
					unit[i].draw_shadow(cam,Dir,map);
					glEndList();

					glFrontFace(GL_CCW);  
					glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
					glCallList(list);
					glDeleteLists(list,1);
					}
			}
		features.draw_shadow(cam,Dir);

		glColorMask(0xFF,0xFF,0xFF,0xFF);
		allegro_gl_set_allegro_mode();
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_NOTEQUAL,0, 0xffffffff);
		glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		glColor4f(0.0f,0.0f,0.0f,alpha);
		glBegin(GL_QUADS);
			glVertex2i(0,0);
			glVertex2i(SCREEN_W,0);
			glVertex2i(SCREEN_W,SCREEN_H);
			glVertex2i(0,SCREEN_H);
		glEnd();
		allegro_gl_unset_allegro_mode();
		glDepthMask(GL_TRUE);
		glDisable(GL_STENCIL_TEST);
		glDisable(GL_BLEND);
		glColor4f(1.0f,1.0f,1.0f,1.0f);
	}

	inline void draw_mini(float map_w,float map_h,int mini_w,int mini_h)				// Repère les unités sur la mini-carte
	{
		if(nb_unit<=0 || unit==NULL)	return;		// Pas d'unités à dessiner

		float rw=128.0f*mini_w/252;
		float rh=128.0f*mini_h/252;

		glColor4f(player_color[0],player_color[1],player_color[2],1.0f);
		glDisable(GL_TEXTURE_2D);
		glPointSize(3.0f);
		glBegin(GL_POINTS);
		int cur_id=0;
		for(int i=0;i<=last_index;i++)
			if(unit[i].flags!=0) {
				if(cur_id!=unit[i].owner_id) {
					cur_id=unit[i].owner_id;
					glColor3f(player_color[3*cur_id],player_color[3*cur_id+1],player_color[3*cur_id+2]);
					}
				glVertex2f(unit[i].Pos.x/map_w*rw+64.0f,unit[i].Pos.z/map_h*rh+64.0f);
				}
		glEnd();
		glPointSize(1.0f);
		glEnable(GL_TEXTURE_2D);
	}

	inline void move(float dt,MAP *map=NULL)
	{
		if(nb_unit<=0 || unit==NULL)	return;		// Pas d'unités à dessiner

		players.clear();		// Réinitialise le compteur de ressources

		int i;
		for(i=0;i<=last_index;i++)		// Compte les stocks de ressources et les productions
			if(unit[i].flags && unit[i].port[BUILD_PERCENT_LEFT]==0.0f) {
				players.metal_s[unit[i].owner_id]+=unit_manager.unit_type[unit[i].type_id].MetalStorage;
				players.energy_s[unit[i].owner_id]+=unit_manager.unit_type[unit[i].type_id].EnergyStorage;
				players.energy_t[unit[i].owner_id]+=unit_manager.unit_type[unit[i].type_id].EnergyMake;
				players.commander[unit[i].owner_id]|=(unit_manager.unit_type[unit[i].type_id].TEDclass==CLASS_COMMANDER);
				if((unit[i].port[ACTIVATION] || !unit_manager.unit_type[unit[i].type_id].onoffable)
				&& unit_manager.unit_type[unit[i].type_id].EnergyUse<=players.energy[unit[i].owner_id]) {
					players.metal_t[unit[i].owner_id]+=unit_manager.unit_type[unit[i].type_id].MakesMetal+unit_manager.unit_type[unit[i].type_id].MetalMake;
					if(unit_manager.unit_type[unit[i].type_id].ExtractsMetal) {	// Extracteur de métal
						int metal_base;
						int px,py;
						px=(int)((unit[i].Pos.x+map->map_w*0.5f)/map->map_w*(map->bloc_w<<1));
						py=(int)((unit[i].Pos.z+map->map_h*0.5f)/map->map_h*(map->bloc_h<<1));
						if(map->map_data[py][px].stuff>=0)
							metal_base=feature_manager.feature[features.feature[map->map_data[py][px].stuff].type].metal;
						else
							metal_base=map->map_data[py][px].metal;
						players.metal_t[unit[i].owner_id]+=10.0f*metal_base*unit_manager.unit_type[unit[i].type_id].ExtractsMetal;
						}
					if(unit_manager.unit_type[unit[i].type_id].EnergyUse<0)
						players.energy_t[unit[i].owner_id]-=unit_manager.unit_type[unit[i].type_id].EnergyUse;
					else
						players.energy_u[unit[i].owner_id]+=unit_manager.unit_type[unit[i].type_id].EnergyUse;
					}
				}
		for(i=0;i<players.nb_player;i++)
			if(players.commander[i]) {
				players.metal_s[i]+=players.com_metal;
				players.energy_s[i]+=players.com_energy;
				}

		for(i=0;i<=last_index;i++)
			if(unit[i].flags)
				if(unit[i].move(dt,map)==-1)		// Vérifie si l'unité a été détruite
					kill(i,map);

		for(i=0;i<players.nb_player;i++) {
			players.metal[i]+=dt*(players.metal_t[i]-players.metal_u[i]);
			players.energy[i]+=dt*(players.energy_t[i]-players.energy_u[i]);
			if(players.metal[i]<0.0f)
				players.metal[i]=0.0f;
			else if(players.metal[i]>players.metal_s[i])
				players.metal[i]=players.metal_s[i];
			if(players.energy[i]<0.0f)
				players.energy[i]=0.0f;
			else if(players.energy[i]>players.energy_s[i])
				players.energy[i]=players.energy_s[i];
			}
	}

	inline int create(int type_id,int owner)
	{
		if(type_id<0 || type_id>=unit_manager.nb_unit)	return -1;
		nb_unit++;
		if(nb_unit>max_unit) {
			max_unit=nb_unit+250*NB_PLAYERS;
			UNIT *n_unit=(UNIT*) malloc(sizeof(UNIT)*max_unit);
			for(int i=0;i<max_unit;i++) {
				n_unit[i].init();
				n_unit[i].flags=0;
				n_unit[i].idx=i;
				}
			if(unit) {
				memcpy(n_unit,unit,sizeof(UNIT)*(nb_unit-1));
				free(unit);
				}
			unit=n_unit;
			}
		if(unit==NULL)
			printf("erreur: allocation de mémoire échouée\n");
		int unit_index=0;
		while(unit[unit_index].flags) unit_index++;		// La nouvelle unité occupe la première place libre
		unit[unit_index].init(type_id,owner);
		unit[unit_index].Angle.y=((rand()%2001)-1000)*0.01f;	// Angle de 10° maximum
		if(unit_index>last_index)
			last_index=unit_index;
		return unit_index;
	}

	bool select(CAMERA *cam,int sel_x[],int sel_y[]);

	int pick(CAMERA *cam,int sensibility=1);

	inline void give_order_move(int player_id,VECTOR target,bool set=true,MAP *map=NULL)
	{
		for(int i=0;i<=last_index;i++)
			if(unit[i].flags!=0 && unit[i].owner_id==player_id && unit[i].sel && unit[i].port[BUILD_PERCENT_LEFT]==0.0f) {
				PATH_NODE *path=NULL;
				if(map && !unit_manager.unit_type[unit[i].type_id].canfly) {
					float dh_max=unit_manager.unit_type[unit[i].type_id].MaxSlope*H_DIV;
					float h_min=map->sealvl-unit_manager.unit_type[unit[i].type_id].MaxWaterDepth*H_DIV;
					if(unit_manager.unit_type[unit[i].type_id].norestrict)
						h_min=0.0f;
					path=compute_path(map->map_data,map->path,map->h_map,map->map_w,map->map_h,map->bloc_w<<1,map->bloc_h<<1,dh_max,h_min,unit[i].Pos,target,unit_manager.unit_type[unit[i].type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[unit[i].type_id].FootprintX,unit_manager.unit_type[unit[i].type_id].FootprintZ,i);
					}
				else
					path=direct_path(target);
				sound_manager.play(unit_manager.unit_type[unit[i].type_id].ok1,unit[i].Pos);
				if(set)
					unit[i].set_mission(MISSION_MOVE,&target,false,0,true,NULL,path);
				else
					unit[i].add_mission(MISSION_MOVE,&target,false,0,NULL,path);
				}
	}

	inline void give_order_build(int player_id,int unit_type_id,VECTOR target,bool set=true)
	{
		for(int i=0;i<=last_index;i++)
			if(unit[i].flags!=0 && unit[i].owner_id==player_id && unit[i].sel && unit[i].port[BUILD_PERCENT_LEFT]==0.0f) {
				sound_manager.play(unit_manager.unit_type[unit[i].type_id].build,unit[i].Pos);
				if(set)
					unit[i].set_mission(MISSION_BUILD,&target,false,unit_type_id);
				else
					unit[i].add_mission(MISSION_BUILD,&target,false,unit_type_id);
				}
	}

	inline void menu_order(int omb)							// Menu permettant de donner des ordres
	{
		bool onoffable=false;

		int i;
		for(i=0;i<=last_index;i++)
			if(unit[i].flags && unit[i].owner_id==0 && unit[i].sel) {
				onoffable|=unit_manager.unit_type[unit[i].type_id].onoffable;
				}

		if(onoffable) {					// Fonction On/Off
			bool on=false,off=false;

			if(mouse_b==1 && mouse_y>=340 && mouse_y<=356) {
				if(mouse_x>=5 && mouse_x<62)
					on=true;
				if(mouse_x>66 && mouse_x<=123)
					off=true;
				}

			if(on && omb!=1)
				for(i=0;i<=last_index;i++)
					if(unit[i].flags!=0 && unit[i].owner_id==0 && unit[i].sel)
						unit[i].activate();
			if(off && omb!=1)
				for(i=0;i<=last_index;i++)
					if(unit[i].flags!=0 && unit[i].owner_id==0 && unit[i].sel)
						unit[i].deactivate();

			glbutton("on",5.0f,340.0f,61.5f,356.0f,on);
			glbutton("off",66.5f,340.0f,123.0f,356.0f,off);
			}

	}

	inline void complete_menu(int index)
	{
		if(index<0 || index>last_index) return;		// On n'affiche que des données sur les unités EXISTANTES
		if(unit[index].flags==0) return;		// L'unité n'existe plus
		UNIT *target=(UNIT*) unit[index].mission->p;
		if(target && target->flags==0)
			target=NULL;

		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
		allegro_gl_printf(aglfont,150.0f,SCREEN_H-28.0f,0.0f,0xFFFFFF,unit_manager.unit_type[unit[index].type_id].name);
		if(target)
			allegro_gl_printf(aglfont,175.0f+(SCREEN_W-200>>1),SCREEN_H-28.0f,0.0f,0xFFFFFF,unit_manager.unit_type[target->type_id].name);

		glDisable(GL_TEXTURE_2D);

		glDisable(GL_BLEND);

		glBegin(GL_QUADS);			// Dessine les barres de metal et d'énergie
			glColor4f(1.0f,0.0f,0.0f,1.0f);

			glVertex2f(150.0f,SCREEN_H-20.0f);					// Fond de la barre de vie de l'unité
			glVertex2f(125+(SCREEN_W-200>>1),SCREEN_H-20.0f);
			glVertex2f(125+(SCREEN_W-200>>1),SCREEN_H-12.0f);
			glVertex2f(150.0f,SCREEN_H-12.0f);

			if(target && !unit_manager.unit_type[target->type_id].HideDamage) {			// Si l'unité a une cible
				glVertex2f(175+(SCREEN_W-200>>1),SCREEN_H-20.0f);		// Fond de la barre de vie
				glVertex2f(SCREEN_W-50,SCREEN_H-20.0f);
				glVertex2f(SCREEN_W-50,SCREEN_H-12.0f);
				glVertex2f(175+(SCREEN_W-200>>1),SCREEN_H-12.0f);
				}

			glColor3f(1.0f,1.0f,0.0f);

			glVertex2f(150.0f,SCREEN_H-20.0f);					// Barre de vie
			glVertex2f(150+((SCREEN_W-200>>1)-25)*unit[index].hp/unit_manager.unit_type[unit[index].type_id].MaxDamage,SCREEN_H-20.0f);
			glVertex2f(150+((SCREEN_W-200>>1)-25)*unit[index].hp/unit_manager.unit_type[unit[index].type_id].MaxDamage,SCREEN_H-12.0f);
			glVertex2f(150.0f,SCREEN_H-12.0f);

			if(target && !unit_manager.unit_type[target->type_id].HideDamage) {			// Si l'unité a une cible
				glVertex2f(175+(SCREEN_W-200>>1),SCREEN_H-20.0f);		// Fond de la barre d'énergie
				glVertex2f(175+(SCREEN_W-200>>1)+((SCREEN_W-200>>1)-25)*target->hp/unit_manager.unit_type[target->type_id].MaxDamage,SCREEN_H-20.0f);
				glVertex2f(175+(SCREEN_W-200>>1)+((SCREEN_W-200>>1)-25)*target->hp/unit_manager.unit_type[target->type_id].MaxDamage,SCREEN_H-12.0f);
				glVertex2f(175+(SCREEN_W-200>>1),SCREEN_H-12.0f);
				}

		glEnd();
		glColor4f(1.0f,1.0f,1.0f,1.0f);
	}
};

extern INGAME_UNITS units;

#endif						// Fin de la déclaration des classes
