/*  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.cpp                      |
|    Contient toutes les classes nécessaires au moteur 3D   |
| et au moteur physique.                                    |
|                                                           |
\----------------------------------------------------------*/

#include <allegro.h>
#include <alleggl.h>
#include <GL/glu.h>
#include "matrix.h"
#include "ta3dbase.h"
#include "3do.h"					// Pour la lecture des fichiers 3D
#include "cob.h"					// Pour la lecture et l'éxecution des scripts
#include "tdf.h"					// Pour la gestion des éléments du jeu
#include "EngineClass.h"

float	player_color[30]={	0.0f,	0.0f,	1.0f,
							1.0f,	0.0f,	0.0f,
							0.0f,	1.0f,	0.0f,
							1.0f,	1.0f,	0.0f,
							1.0f,	0.0f,	1.0f,
							0.5f,	0.5f,	0.5f,
							0.5f,	0.5f,	1.0f,
							1.0f,	1.0f,	1.0f,
							0.0f,	0.0f,	0.0f,
							0.0f,	1.0f,	1.0f };
INGAME_UNITS units;
PLAYERS	players;		// Objet contenant les données sur les joueurs

int NB_PLAYERS;

	void MAP::draw_mini(int x1,int y1,int w,int h,CAMERA *cam)			// Dessine la mini-carte
	{
		if(!mini) return;		// Vérifie si la mini-carte existe
		int rw=w*mini_w/252;
		int rh=h*mini_h/252;
		x1+=w-rw>>1;
		y1+=h-rh>>1;
		float lw=mini_w/252.0f;
		float lh=mini_h/252.0f;
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D,glmini);
		glBegin(GL_QUADS);
			glTexCoord2f(0.0f,0.0f);	glVertex2f(x1,y1);
			glTexCoord2f(lw,0.0f);		glVertex2f(x1+rw,y1);
			glTexCoord2f(lw,lh);		glVertex2f(x1+rw,y1+rh);
			glTexCoord2f(0.0f,lh);		glVertex2f(x1,y1+rh);
		glEnd();

		if(!cam) return;

		VECTOR A,B,C,D,P;
		A=cam->Dir+1.0f*(-cam->Side-0.75f*cam->Up);
		B=cam->Dir+1.0f*(cam->Side-0.75f*cam->Up);
		C=cam->Dir+1.0f*(cam->Side+0.75f*cam->Up);
		D=cam->Dir+1.0f*(-cam->Side+0.75f*cam->Up);
		const int nmax=64;
		float cx[4*nmax+4],cy[4*nmax+4];
		if(A.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(A.y)*A;	cx[0]=P.x;	cy[0]=P.z; }
		else {
			P=cam->Pos+10000.0f*A;	cx[0]=P.x;	cy[0]=P.z; }
		if(B.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(B.y)*B;	cx[1]=P.x;	cy[1]=P.z; }
		else {
			P=cam->Pos+10000.0f*B;	cx[1]=P.x;	cy[1]=P.z; }
		if(C.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(C.y)*C;	cx[2]=P.x;	cy[2]=P.z; }
		else {
			P=cam->Pos+10000.0f*C;	cx[2]=P.x;	cy[2]=P.z; }
		if(D.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(D.y)*D;	cx[3]=P.x;	cy[3]=P.z; }
		else {
			P=cam->Pos+10000.0f*D;	cx[3]=P.x;	cy[3]=P.z; }

		int i;
		for(i=0;i<4;i++) {
			cx[i]=(cx[i]+0.5f*map_w)*rw/map_w;
			cy[i]=(cy[i]+0.5f*map_h)*rh/map_h;
			}
		for(i=0;i<4;i++) {
			for(int e=0;e<nmax;e++) {
				cx[i*nmax+e+4]=(cx[i]*(nmax-e)+cx[(i+1)%4]*(e+1))/(nmax+1);
				cy[i*nmax+e+4]=(cy[i]*(nmax-e)+cy[(i+1)%4]*(e+1))/(nmax+1);
				}
			}
		for(i=0;i<4+(nmax<<2);i++) {
			if(cx[i]<0.0f) cx[i]=0.0f;
			else if(cx[i]>rw) cx[i]=rw;
			if(cy[i]<0.0f) cy[i]=0.0f;
			else if(cy[i]>rh) cy[i]=rh;
			}

		glDisable(GL_TEXTURE_2D);
		glColor3f(1.0f,1.0f,1.0f);
		glBegin(GL_LINE_STRIP);
			for(i=0;i<4;i++) {
				glVertex2f(cx[i]+x1,cy[i]+y1);
				for(int e=0;e<nmax;e++)
					glVertex2f(x1+cx[i*nmax+e+4],y1+cy[i*nmax+e+4]);
				}
			glVertex2f(cx[0]+x1,cy[0]+y1);
		glEnd();
		glEnable(GL_TEXTURE_2D);
	}

	void MAP::draw(CAMERA *cam,bool FLAT,float niv,float t,float dt)
	{
		cam->SetView();
		if(FLAT && !water)	return;
		if(FLAT)
			glTranslatef(0.0f,0.0f,sea_dec);
		int i,x,y;
		glDisable(GL_CULL_FACE);
		glDisable(GL_LIGHTING);
		if(ntex>0) {
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			for(i=0;i<8;i++) {
				glActiveTextureARB(GL_TEXTURE0_ARB+i);
				ReInitTexSys();
				glDisable(GL_TEXTURE_2D);
				}
			}
		else
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		VECTOR A,B,C,D,P;
		A=cam->Dir+1.0f*(-cam->Side-0.75f*cam->Up);
		B=cam->Dir+1.0f*(cam->Side-0.75f*cam->Up);
		C=cam->Dir+1.0f*(cam->Side+0.75f*cam->Up);
		D=cam->Dir+1.0f*(-cam->Side+0.75f*cam->Up);
		float cx[4],cy[4];
		if(A.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(A.y)*A;	cx[0]=P.x;	cy[0]=P.z; }
		else {
			P=cam->Pos+10000.0f*A;	cx[0]=P.x;	cy[0]=P.z; }
		if(B.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(B.y)*B;	cx[1]=P.x;	cy[1]=P.z; }
		else {
			P=cam->Pos+10000.0f*B;	cx[1]=P.x;	cy[1]=P.z; }
		if(C.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(C.y)*C;	cx[2]=P.x;	cy[2]=P.z; }
		else {
			P=cam->Pos+10000.0f*C;	cx[2]=P.x;	cy[2]=P.z; }
		if(D.y<0.0f) {
			P=cam->Pos+cam->Pos.y/fabs(D.y)*D;	cx[3]=P.x;	cy[3]=P.z; }
		else {
			P=cam->Pos+10000.0f*D;	cx[3]=P.x;	cy[3]=P.z; }

		int minx=(int)cx[0],maxx=(int)cx[0];
		int miny=(int)cy[0],maxy=(int)cy[0];
		for(i=0;i<4;i++) {
			cx[i]=(cx[i]+0.5f*map_w)/16.0f;
			cy[i]=(cy[i]+0.5f*map_h)/16.0f;
			if(cx[i]<minx) minx=(int)cx[i];
			if(cx[i]>maxx) maxx=(int)cx[i];
			if(cy[i]<miny) miny=(int)cy[i];
			if(cy[i]>maxy) maxy=(int)cy[i];
			}
		int y1=bloc_h,y2=0,x1=bloc_w,x2=0,mx,my;
		float limit=cam->zfar*sqrt(2.0f);
		x1=(int)((cam->Pos.x+0.5f*map_w-limit)/16.0f);
		y1=(int)((cam->Pos.z+0.5f*map_h-limit)/16.0f);
		x2=(int)((cam->Pos.x+0.5f*map_w+limit)/16.0f);
		y2=(int)((cam->Pos.z+0.5f*map_h+limit)/16.0f);
		mx=(int)((cam->Pos.x+0.5f*map_w)/16.0f);
		my=(int)((cam->Pos.z+0.5f*map_h)/16.0f);
		if(x1<minx)
			x1=minx;
		if(x2>maxx)
			x2=maxx;
		if(y1<miny)
			y1=miny;
		if(y2>maxy)
			y2=maxy;
		if(x1>mx) x1=mx;
		if(y1>my) y1=my;
		if(x2<mx) x2=mx;
		if(y2<my) y2=my;
		if(y1<0) y1=0;
		if(y2>=bloc_h) y2=bloc_h-1;
		if(x1<0) x1=0;
		if(x2>=bloc_w) x2=bloc_w-1;

		A=(cam->Dir+1.0f*(0.75f*cam->Up-cam->Side));	A.Unit();
		float ref=0.95f*(A%cam->Dir);
		float dhm=0.5f*map_h;
		float dwm=0.5f*map_w;

		if(!FLAT)
			glColor4f(1.0f,1.0f,1.0f,1.0f);

		POINTF flat[9];
		if(FLAT) {
			flat[0].x=0.0f;		flat[0].y=niv+cos(t)*0.5f;					flat[0].z=0.0f;
			flat[1].x=8.0f;		flat[1].y=niv+cos(t+1.0f)*0.5f;				flat[1].z=0.0f;
			flat[2].x=16.0f;	flat[2].y=flat[0].y;						flat[2].z=0.0f;
			flat[3].x=0.0f;		flat[3].y=niv+cos(t+1.5f)*0.5f;				flat[3].z=8.0f;
			flat[4].x=8.0f;		flat[4].y=niv+cos(t+2.5f)*0.5f;				flat[4].z=8.0f;
			flat[5].x=16.0f;	flat[5].y=flat[3].y;						flat[5].z=8.0f;
			flat[6].x=0.0f;		flat[6].y=flat[0].y;						flat[6].z=16.0f;
			flat[7].x=8.0f;		flat[7].y=flat[1].y;						flat[7].z=16.0f;
			flat[8].x=16.0f;	flat[8].y=flat[0].y;						flat[8].z=16.0f;
			}

		if(ntex>0) {
			glActiveTextureARB(GL_TEXTURE0_ARB);
			glEnable(GL_TEXTURE_2D);
			glClientActiveTextureARB(GL_TEXTURE0_ARB);
			}
		glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets

		if(FLAT) {
			glTranslatef(cos(t),0.0f,sin(t));
			glVertexPointer( 3, GL_FLOAT, 0, flat);
			}
		int old_i=-1;
		GLuint old_tex=bloc[0].tex;
		glBindTexture(GL_TEXTURE_2D,old_tex);
		if(!FLAT) {
			for(y=oy1;y<=oy2;y++)
				memset(view[y]+ox1,0,ox2-ox1+1);
			features.min_idx=features.nb_features-1;
			features.max_idx=0;
			ox1=x1;	ox2=x2;
			oy1=y1;	oy2=y2;
			}
		int lavaprob=(int)(1000*dt);
		for(y=y1;y<=y2;y++) {				// Balaye les blocs susceptibles d'être visibles pour dessiner ceux qui le sont
			int pre_y=y<<4;
			int Y=y<<1;
			int pre_y2=y*bloc_w;
			glPushMatrix();
			glTranslatef((x1<<4)-dwm,0.0f,pre_y-dhm);
			for(x=x1;x<=x2;x++) {
				if(!FLAT) {
					VECTOR	V;
					V.x=(x<<4)-dwm;
					V.y=h_map[Y+1][(x<<1)+1];
					V.z=pre_y-dhm;
					V=V-cam->Pos;
					if(fabs(V%cam->Dir)>cam->zfar) {
						view[y][x]=0;
						glTranslatef(16.0f,0.0f,0.0f);
						continue;
						}
					float d=V.Sq();
					if(d>16384.0f)
						if((V%cam->Dir)/sqrt(d)<ref) {
							view[y][x]=0;
							glTranslatef(16.0f,0.0f,0.0f);
							continue;
							}
					view[y][x]=1;
					if(map_data[y<<1][x<<1].stuff>=0 && map_data[y<<1][x<<1].stuff<features.nb_features) {		// Indique comme affichables les objets présents sur le bloc
						features.feature[map_data[y<<1][x<<1].stuff].draw=true;
						if(map_data[y<<1][x<<1].stuff<features.min_idx)	features.min_idx=map_data[y<<1][x<<1].stuff;
						if(map_data[y<<1][x<<1].stuff>features.max_idx)	features.max_idx=map_data[y<<1][x<<1].stuff;
						}
					if(map_data[y<<1][(x<<1)+1].stuff>=0 && map_data[y<<1][(x<<1)+1].stuff<features.nb_features) {
						features.feature[map_data[y<<1][(x<<1)+1].stuff].draw=true;
						if(map_data[y<<1][(x<<1)+1].stuff<features.min_idx)	features.min_idx=map_data[y<<1][(x<<1)+1].stuff;
						if(map_data[y<<1][(x<<1)+1].stuff>features.max_idx)	features.max_idx=map_data[y<<1][(x<<1)+1].stuff;
						}
					if(map_data[(y<<1)+1][x<<1].stuff>=0 && map_data[(y<<1)+1][x<<1].stuff<features.nb_features) {
						features.feature[map_data[(y<<1)+1][x<<1].stuff].draw=true;
						if(map_data[(y<<1)+1][x<<1].stuff<features.min_idx)	features.min_idx=map_data[(y<<1)+1][x<<1].stuff;
						if(map_data[(y<<1)+1][x<<1].stuff>features.max_idx)	features.max_idx=map_data[(y<<1)+1][x<<1].stuff;
						}
					if(map_data[(y<<1)+1][(x<<1)+1].stuff>=0 && map_data[(y<<1)+1][(x<<1)+1].stuff<features.nb_features) {
						features.feature[map_data[(y<<1)+1][(x<<1)+1].stuff].draw=true;
						if(map_data[(y<<1)+1][(x<<1)+1].stuff<features.min_idx)	features.min_idx=map_data[(y<<1)+1][(x<<1)+1].stuff;
						if(map_data[(y<<1)+1][(x<<1)+1].stuff>features.max_idx)	features.max_idx=map_data[(y<<1)+1][(x<<1)+1].stuff;
						}
					}
				else
					if(!view[y][x]) {
						glTranslatef(16.0f,0.0f,0.0f);
						continue;
						}
				i=bmap[y][x];
				int X=x<<1;
				if(FLAT) {
					bloc[i].point=lvl[pre_y2+x];
					if(bloc[i].point==NULL || bloc[i].point[0].y<niv || bloc[i].point[1].y<niv || bloc[i].point[2].y<niv ||
						bloc[i].point[3].y<niv || bloc[i].point[4].y<niv || bloc[i].point[5].y<niv ||
						bloc[i].point[6].y<niv || bloc[i].point[7].y<niv || bloc[i].point[8].y<niv)
						bloc[i].point=flat;
					else {
						glTranslatef(16.0f,0.0f,0.0f);
						continue;
						}
					if(bloc[i].lava && (rand()%1000000)<=lavaprob) {
						POINTF POS;
						VECTOR V;
						POS.x=(x<<4)-dwm+8.0f;
						POS.z=pre_y-dhm+8.0f;
						POS.y=get_unit_h(POS.x,POS.y)-10.0f;
						V.x=((rand()%201)-100);
						V.y=((rand()%51)+50);
						V.z=((rand()%201)-100);
						V.Unit();
						particle_engine.emit_lava(POS,V,1,100,(rand()%1000)*0.01f+30.0f);
						}
					}
				else {
					bloc[i].point=lvl[pre_y2+x];
					if(bloc[i].point==NULL) {
						lvl[pre_y2+x]=bloc[i].point=(POINTF*) malloc(sizeof(POINTF)*9);
						if(tnt) {
							bloc[i].point[0].x=0.0f;	bloc[i].point[0].z=get_zdec(X,Y);
							bloc[i].point[1].x=8.0f;	bloc[i].point[1].z=get_zdec(X+1,Y);
							bloc[i].point[2].x=16.0f;	bloc[i].point[2].z=get_zdec(X+2,Y);
							bloc[i].point[3].x=0.0f;	bloc[i].point[3].z=8.0f+get_zdec(X,Y+1);
							bloc[i].point[4].x=8.0f;	bloc[i].point[4].z=8.0f+get_zdec(X+1,Y+1);
							bloc[i].point[5].x=16.0f;	bloc[i].point[5].z=8.0f+get_zdec(X+2,Y+1);
							bloc[i].point[6].x=0.0f;	bloc[i].point[6].z=16.0f+get_zdec(X,Y+2);
							bloc[i].point[7].x=8.0f;	bloc[i].point[7].z=16.0f+get_zdec(X+1,Y+2);
							bloc[i].point[8].x=16.0f;	bloc[i].point[8].z=16.0f+get_zdec(X+2,Y+2);
							bloc[i].point[0].y=get_nh(X,Y);
							bloc[i].point[1].y=get_nh(X+1,Y);
							bloc[i].point[2].y=get_nh(X+2,Y);
							bloc[i].point[3].y=get_nh(X,Y+1);
							bloc[i].point[4].y=get_nh(X+1,Y+1);
							bloc[i].point[5].y=get_nh(X+2,Y+1);
							bloc[i].point[6].y=get_nh(X,Y+2);
							bloc[i].point[7].y=get_nh(X+1,Y+2);
							bloc[i].point[8].y=get_nh(X+2,Y+2);
							}
						else {
							bloc[i].point[0].x=0.0f;	bloc[i].point[0].z=0.0f;
							bloc[i].point[1].x=8.0f;	bloc[i].point[1].z=0.0f;
							bloc[i].point[2].x=16.0f;	bloc[i].point[2].z=0.0f;
							bloc[i].point[3].x=0.0f;	bloc[i].point[3].z=8.0f;
							bloc[i].point[4].x=8.0f;	bloc[i].point[4].z=8.0f;
							bloc[i].point[5].x=16.0f;	bloc[i].point[5].z=8.0f;
							bloc[i].point[6].x=0.0f;	bloc[i].point[6].z=16.0f;
							bloc[i].point[7].x=8.0f;	bloc[i].point[7].z=16.0f;
							bloc[i].point[8].x=16.0f;	bloc[i].point[8].z=16.0f;
							bloc[i].point[0].y=get_h(X,Y);
							bloc[i].point[1].y=get_h(X+1,Y);
							bloc[i].point[2].y=get_h(X+2,Y);
							bloc[i].point[3].y=get_h(X,Y+1);
							bloc[i].point[4].y=get_h(X+1,Y+1);
							bloc[i].point[5].y=get_h(X+2,Y+1);
							bloc[i].point[6].y=get_h(X,Y+2);
							bloc[i].point[7].y=get_h(X+1,Y+2);
							bloc[i].point[8].y=get_h(X+2,Y+2);
							}
						}
					}
				if(!FLAT)
					glVertexPointer( 3, GL_FLOAT, 0, bloc[i].point);

/*				if(map_data[y<<1][x<<1].unit_idx!=-1 || map_data[(y<<1)+1][x<<1].unit_idx!=-1 || map_data[y<<1][(x<<1)+1].unit_idx!=-1 || map_data[(y<<1)+1][(x<<1)+1].unit_idx!=-1)
					glColor4f(1.0f,0.0f,0.0f,0.0f);
				else
					glColor4f(1.0f,1.0f,1.0f,1.0f);*/

				if(i!=old_i && ntex>0) {
					if(bloc[i].tex!=old_tex) {
						glBindTexture(GL_TEXTURE_2D,bloc[i].tex);
						old_tex=bloc[i].tex;
						}
					glTexCoordPointer(2, GL_FLOAT, 0, bloc[i].texcoord);
					old_i=i;
					}

				glDrawElements(GL_TRIANGLE_STRIP, bloc[i].nbindex,GL_UNSIGNED_INT,bloc[i].index);		// dessine le tout
				glTranslatef(16.0f,0.0f,0.0f);
				}
			glPopMatrix();
			}
	}

	VECTOR MAP::hit(VECTOR Pos,VECTOR Dir,bool water)			// Calcule l'intersection d'un rayon avec la carte(le rayon partant du dessus de la carte)
	{
		if(Dir.x==0.0f && Dir.z==0.0f)			// Solution triviale
			return Pos;
		if(get_unit_h(Pos.x,Pos.z)>Pos.y)		// Cas non traité
			return Pos;
		float step=1.0f;
		if(Dir.x!=0.0f && Dir.z!=0.0f) {
			if(fabs(Dir.x)<fabs(Dir.z))
				step=1.0f/fabs(Dir.x);
			else
				step=1.0f/fabs(Dir.z);
			}
		int nb=0;
		float dwm=map_w*0.5f;
		float dhm=map_h*0.5f;
		while(((sealvl<Pos.y && water) || !water) && get_unit_h(Pos.x,Pos.z)<Pos.y) {
			if(nb>=1000)
				return Pos;
			nb++;
			Pos=Pos+step*Dir;
			if(fabs(Pos.x)>dwm || fabs(Pos.z)>dhm)		// Pas de résultat
				return Pos;
			}
		Pos=Pos-step*Dir;		// On recommence la dernière opération mais avec plus de précision
		step*=0.1f;
		nb=0;
		while(((sealvl<Pos.y && water) || !water) && get_unit_h(Pos.x,Pos.z)<Pos.y) {
			if(nb>=10)
				break;
			nb++;
			Pos=Pos+step*Dir;
			}
		return Pos;		// Meilleure solution approximative trouvée
	}

	void UNIT::add_mission(int mission_type,VECTOR *target,bool step,int dat,void *pointer,PATH_NODE *path)
	{
		MISSION *new_mission=(MISSION*) malloc(sizeof(MISSION));
		new_mission->next=NULL;
		new_mission->mission=mission_type;
		new_mission->step=step;
		new_mission->time=0.0f;
		new_mission->data=dat;
		new_mission->p=pointer;
		new_mission->path=path;

		if(target)
			new_mission->target=*target;

		MISSION *stop=(MISSION*) malloc(sizeof(MISSION));
		stop->mission=MISSION_STOP;
		stop->step=true;
		stop->time=0.0f;
		stop->p=NULL;
		stop->data=0;
		stop->path=NULL;
		if(step) {
			stop->next=NULL;
			new_mission->next=stop;
			}
		else {
			stop->next=new_mission;
			new_mission=stop;
			}

		if(step && mission) {
//			new_mission->next=mission;
			stop->next=mission;
			mission=new_mission;
			start_mission_script(mission->mission);
			}
		else {
			if(mission) {				// Ajoute l'ordre aux autres
				MISSION *cur=mission;
				while(cur->next!=NULL)	cur=cur->next;
				cur->next=new_mission;
				}
			else {
				mission=new_mission;
				start_mission_script(mission->mission);
				}
			}
	}

	void UNIT::set_mission(int mission_type,VECTOR *target,bool step,int dat,bool stopit,void *pointer,PATH_NODE *path)
	{
		while(mission!=NULL) 	clear_mission();			// Efface les ordres précédents

		mission=(MISSION*) malloc(sizeof(MISSION));
		mission->next=NULL;
		mission->mission=mission_type;
		mission->step=step;
		mission->time=0.0f;
		mission->data=dat;
		mission->p=pointer;
		mission->path=path;
		if(target)
			mission->target=*target;

		if(stopit) {
			MISSION *stop=(MISSION*) malloc(sizeof(MISSION));
			stop->next=mission;
			stop->mission=MISSION_STOP;
			stop->step=true;
			stop->time=0.0f;
			stop->p=NULL;
			stop->data=0;
			stop->path=NULL;
			mission=stop;
			}
		else
			start_mission_script(mission->mission);
		c_time=0.0f;
	}

	void UNIT::draw(CAMERA *cam,MAP *map)
	{
		if(model==NULL) return;		// S'il n'y a pas de modèle associé, on quitte la fonction

		int px=((int)(Pos.z+map->map_h*0.5f))>>4;
		int py=((int)(Pos.x+map->map_w*0.5f))>>4;
		if(px<0 || py<0 || px>=(map->bloc_w<<1) || py>=(map->bloc_h<<1))	return;	// Unité hors de la carte
		if(map->view[px][py]==0) return;	// Unité non visible

		VECTOR D;
		D=Pos-cam->Pos;		// Vecteur "viseur unité" partant de la caméra vers l'unité

		float dist=D.Sq();
		if(dist>=16384.0f && (D%cam->Dir)<=0.0f)		return;
		if((D%cam->Dir)>cam->zfar2)	return;		// Si l'objet est hors champ on ne le dessine pas

		MATRIX_4x4 M;
		cam->SetView();					// Dessine le modèle
		glTranslatef(Pos.x,Pos.y,Pos.z);
		glRotatef(Angle.x,1.0f,0.0f,0.0f);
		glRotatef(Angle.z,0.0f,0.0f,1.0f);
		glRotatef(Angle.y,0.0f,1.0f,0.0f);
		float scale=unit_manager.unit_type[type_id].Scale;
		glScalef(scale,scale,scale);

		M=RotateY(Angle.y*DEG2RAD)*RotateZ(Angle.z*DEG2RAD)*RotateX(Angle.x*DEG2RAD)*Scale(scale);			// Matrice pour le calcul des positions des éléments du modèle de l'unité

		if(port[BUILD_PERCENT_LEFT]>66.0f)
			glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
		else
			glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
		VECTOR *target=NULL,*center=NULL;
		POINTF upos;
		bool c_part=false;
		float size=0.0f;
		if(mission!=NULL && (mission->mission==MISSION_REPAIR || mission->mission==MISSION_BUILD_2 || mission->mission==MISSION_BUILD))
			if(c_time>=0.25f) {
				c_time=0.0f;
				c_part=true;
				target=&mission->target;
				upos.x=upos.y=upos.z=0.0f;
				upos=upos+Pos;
				if(mission->p && ((UNIT*)mission->p)->flags && ((UNIT*)mission->p)->model!=NULL) {
					size=sqrt(((UNIT*)mission->p)->model->size*0.5f);
					center=&((UNIT*)mission->p)->model->center;
					}
				else
					c_part=false;
				}
		model->draw(&data,owner_id==0 && sel,port[BUILD_PERCENT_LEFT]>0.0f,c_part,build_part,target,&upos,&M,size,center);
		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	}

	void UNIT::draw_shadow(CAMERA *cam,VECTOR Dir,MAP *map)
	{
		if(model==NULL) return;		// S'il n'y a pas de modèle associé, on quitte la fonction

		if(map->view[((int)(Pos.z+map->map_h*0.5f))>>4][((int)(Pos.x+map->map_w*0.5f))>>4]==0) return;	// Unité non visible

		VECTOR D;
		D=Pos-cam->Pos;		// Vecteur "viseur unité" partant de la caméra vers l'unité

		float dist=D.Sq();
		if(dist>=16384.0f && (D%cam->Dir)<=0.0f)		return;
		if((D%cam->Dir)>cam->zfar2)	return;		// Si l'objet est hors champ on ne le dessine pas

		cam->SetView();					// Dessine le modèle
		glTranslatef(Pos.x,Pos.y,Pos.z);
		glRotatef(Angle.x,1.0f,0.0f,0.0f);
		glRotatef(Angle.z,0.0f,0.0f,1.0f);
		glRotatef(Angle.y,0.0f,1.0f,0.0f);
		float scale=unit_manager.unit_type[type_id].Scale;
		glScalef(scale,scale,scale);
		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
		float scale_dir=sqrt(model->size)*2.0f;
		scale_dir+=Pos.y;
		model->draw_shadow(scale_dir*Dir*RotateY(-Angle.y*DEG2RAD)*RotateZ(-Angle.z*DEG2RAD)*RotateX(-Angle.x*DEG2RAD),&data);
	}

	void UNIT::run_script(float dt,int id,MAP *map)			// Interprète les scripts liés à l'unité
	{
		if(flags==0) return;
		if(!script_env[id].running)	return;
		if(script_env[id].wait>0.0f) {
			script_env[id].wait-=dt;
			return;
			}
		if(script==NULL || script_env[id].env==NULL) {
			script_env[id].running=false;
			return;	// S'il n'y a pas de script associé on quitte la fonction
			}
		int script_id=(script_env[id].env->cur&0xFF);			// Récupère l'identifiant du script en cours d'éxecution et la position d'éxecution
		int pos=(script_env[id].env->cur>>8);

		if(script_id<0 || script_id>=script->nb_script)	{
			script_env[id].running=false;
			return;		// Erreur, ce n'est pas un script repertorié
			}

		float divisor=i2pwr16;
		float div=0.5f*divisor;
		POINTF O;
		O.x=O.y=O.z=0.0f;
		bool done=false;
		int nb_code=0;

//#define PRINT_CODE			// Affiche le code éxécuté dans la console

		do
		{
			int code=script->script_code[script_id][pos];			// Lit un code
			pos++;
			nb_code++;
			if(nb_code>=1) done=true;			// Pour éviter que le programme ne fige à cause d'un script
			switch(code)			// Code de l'interpréteur
			{
			case SCRIPT_MOVE_OBJECT:
#ifdef PRINT_CODE
				printf("MOVE_OBJECT\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					if(axis==0)
						v1=-v1;
					data.axe[axis][obj].move_distance=v1*div;
					data.axe[axis][obj].move_distance-=data.axe[axis][obj].pos;
					if(data.axe[axis][obj].move_distance<0.0f)
						data.axe[axis][obj].move_speed=-fabs(v2*div*0.1f);
					else
						data.axe[axis][obj].move_speed=fabs(v2*div*0.1f);
				}
				break;
			case SCRIPT_WAIT_FOR_TURN:
#ifdef PRINT_CODE
				printf("WAIT_FOR_TURN\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					float a=data.axe[axis][obj].rot_angle;
					if((data.axe[axis][obj].rot_speed!=0.0f || data.axe[axis][obj].rot_accel!=0.0f) && (a!=0.0f && data.axe[axis][obj].rot_limit))
						pos-=3;
					else if(data.axe[axis][obj].rot_speed!=data.axe[axis][obj].rot_target_speed && data.axe[axis][obj].rot_speed_limit)
						pos-=3;
				}
				done=true;
				break;
			case SCRIPT_RANDOM_NUMBER:
#ifdef PRINT_CODE
				printf("RANDOM_NUMBER\n");
#endif
				{
					int high=script_env[id].pop();
					int low=script_env[id].pop();
					script_env[id].push((rand()%(high-low+1))+low);
				}
				break;
			case SCRIPT_GREATER_EQUAL:
#ifdef PRINT_CODE
				printf("GREATER_EQUAL\n");
#endif
				{
					int v2=script_env[id].pop();
					int v1=script_env[id].pop();
					script_env[id].push(v1>=v2 ? 1 : 0);
				}
				break;
			case SCRIPT_GREATER:
#ifdef PRINT_CODE
				printf("GREATER\n");
#endif
				{
					int v2=script_env[id].pop();
					int v1=script_env[id].pop();
					script_env[id].push(v1>v2 ? 1 : 0);
				}
				break;
			case SCRIPT_LESS:
#ifdef PRINT_CODE
				printf("LESS\n");
#endif
				{
					int v2=script_env[id].pop();
					int v1=script_env[id].pop();
					script_env[id].push(v1<v2 ? 1 : 0);
				}
				break;
			case SCRIPT_EXPLODE:
#ifdef PRINT_CODE
				printf("EXPLODE\n");
#endif
				particle_engine.make_fire(O+Pos,1,(int)log(1+script_env[id].pop())<<2,45.0f);
				pos++;
				break;
			case SCRIPT_TURN_OBJECT:
#ifdef PRINT_CODE
				printf("TURN_OBJECT\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					if(axis!=2) {
						v1=-v1;
						v2=-v2;
						}
					data.axe[axis][obj].rot_angle=-v1*divisor*360.0f;
					data.axe[axis][obj].rot_accel=0.0f;
					data.axe[axis][obj].rot_angle-=data.axe[axis][obj].angle;
					if(data.axe[axis][obj].rot_angle>0.0f)
						data.axe[axis][obj].rot_speed=fabs(v2*divisor*36.0f);
					else
						data.axe[axis][obj].rot_speed=-fabs(v2*divisor*36.0f);
					data.axe[axis][obj].rot_limit=true;
					data.axe[axis][obj].rot_speed_limit=false;
				}
				break;
			case SCRIPT_WAIT_FOR_MOVE:
#ifdef PRINT_CODE
				printf("WAIT_FOR_MOVE\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					float a=data.axe[axis][obj].rot_angle;
					if(data.axe[axis][obj].move_distance!=0.0f)
						pos-=3;
				}
				done=true;
				break;
			case SCRIPT_CREATE_LOCAL_VARIABLE:
#ifdef PRINT_CODE
				printf("CREATE_LOCAL_VARIABLE\n");
#endif
				break;
			case SCRIPT_SUBTRACT:
#ifdef PRINT_CODE
				printf("SUBSTRACT\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1-v2);
				}
				break;
			case SCRIPT_GET_VALUE_FROM_PORT:
#ifdef PRINT_CODE
				printf("GET_VALUE_FROM_PORT: ");
#endif
				{
					int value=script_env[id].pop();
#ifdef PRINT_CODE
					printf("%d\n",value);
#endif
					switch(value)
					{
					case ATAN:
						{
							int v1=script_env[id].pop();
							int v2=script_env[id].pop();
							port[value]=atan((float)v1/v2);
						}
						break;
					case HYPOT:
						{
							int v1=script_env[id].pop();
							int v2=script_env[id].pop();
							port[value]=sqrt(v1*v1+v2*v2);
						}
						break;
					case BUGGER_OFF:
						port[value]=map->check_rect((((int)(Pos.x+map->map_w*0.5f))>>3)-(unit_manager.unit_type[type_id].FootprintX>>1),(((int)(Pos.z+map->map_h*0.5f))>>3)-(unit_manager.unit_type[type_id].FootprintZ>>1),unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx) ? 0.0f : 1.0f;
						break;
					case YARD_OPEN:
//						port[value]=0;
						break;
					case HEALTH:
					case BUILD_PERCENT_LEFT:
						break;
					default:
						{
							char *op[]={"INCONNU","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"};
							if(value>20)
								value=0;
							printf("opération non gérée : %s\n",op[value]);
						}
						break;
					};
					script_env[id].push((int)port[value]);
				}
				break;
			case SCRIPT_LESS_EQUAL:
#ifdef PRINT_CODE
				printf("LESS_EQUAL\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1<=v2 ? 1 : 0);
				}
				break;
			case SCRIPT_SPIN_OBJECT:
#ifdef PRINT_CODE
				printf("SPIN_OBJECT\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					if(axis!=2) {
						v1=-v1;
						v2=-v2;
						}
					data.axe[axis][obj].rot_limit=false;
					data.axe[axis][obj].rot_speed_limit=true;
					data.axe[axis][obj].rot_target_speed=v1;
					if(v1) {
						if(data.axe[axis][obj].rot_target_speed>data.axe[axis][obj].rot_speed)
							data.axe[axis][obj].rot_accel=abs(v2);
						else
							data.axe[axis][obj].rot_accel=-abs(v2);
						}
					else
						data.axe[axis][obj].rot_accel=v2;
				}
				break;
			case SCRIPT_SLEEP:
#ifdef PRINT_CODE
				printf("SLEEP\n");
#endif
				script_env[id].wait=script_env[id].pop()*0.001f;
				done=true;
				break;
			case SCRIPT_MULTIPLY:
#ifdef PRINT_CODE
				printf("MULTIPLY\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1*v2);
				}
				break;
			case SCRIPT_CALL_SCRIPT:
#ifdef PRINT_CODE
				printf("CALL_SCRIPT\n");
#endif
				{
				int function_id=script->script_code[script_id][pos];			// Lit un code
				pos++;
				int num_param=script->script_code[script_id][pos];			// Lit un code
				pos++;
				script_env[id].env->cur=script_id+(pos<<8);
				SCRIPT_ENV_STACK *old=script_env[id].env;
				script_env[id].env=new SCRIPT_ENV_STACK;
				script_env[id].env->init();
				script_env[id].env->next=old;
				script_env[id].env->cur=function_id;
				for(int i=0;i<num_param;i++)		// Lit les paramètres
					script_env[id].env->var[i]=script_env[id].pop();
				done=true;
				pos=0;
				script_id=function_id;
				}
				break;
			case SCRIPT_SHOW_OBJECT:
#ifdef PRINT_CODE
				printf("SHOW_OBJECT\n");
#endif
				data.flag[script->script_code[script_id][pos++]]&=(~FLAG_HIDE);
				break;
			case SCRIPT_EQUAL:
#ifdef PRINT_CODE
				printf("EQUAL\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1==v2 ? 1 : 0);
				}
				break;
			case SCRIPT_NOT_EQUAL:
#ifdef PRINT_CODE
				printf("NOT_EQUAL\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1!=v2 ? 1 : 0);
				}
				break;
			case SCRIPT_IF:
#ifdef PRINT_CODE
				printf("IF\n");
#endif
				if(script_env[id].pop()!=0)
					pos++;
				else {
					int target_offset=script->script_code[script_id][pos];			// Lit un code
					pos=target_offset-script->dec_offset[script_id];								// Déplace l'éxecution
					}
				break;
			case SCRIPT_HIDE_OBJECT:
#ifdef PRINT_CODE
				printf("HIDE_OBJECT\n");
#endif
				data.flag[script->script_code[script_id][pos++]]|=FLAG_HIDE;
				break;
			case SCRIPT_SIGNAL:
#ifdef PRINT_CODE
				printf("SIGNAL\n");
#endif
				script_env[id].env->cur=script_id+(pos<<8);			// Sauvegarde la position
				raise_signal(script_env[id].pop());					// Tue tout les processus utilisant ce signal
				return;												// Quitte la fonction d'émulation de scripts
			case SCRIPT_DONT_CACHE:
#ifdef PRINT_CODE
				printf("DONT_CACHE\n");
#endif
				pos++;
				break;
			case SCRIPT_SET_SIGNAL_MASK:
#ifdef PRINT_CODE
				printf("SET_SIGNAL_MASK\n");
#endif
				script_env[id].env->signal_mask=script_env[id].pop();
				break;
			case SCRIPT_NOT:
#ifdef PRINT_CODE
				printf("NOT\n");
#endif
				script_env[id].push(~script_env[id].pop());
				break;
			case SCRIPT_DONT_SHADE:
#ifdef PRINT_CODE
				printf("DONT_SHADE\n");
#endif
				pos++;
				break;
			case SCRIPT_EMIT_SFX:
#ifdef PRINT_CODE
				printf("EMIT_SFX: ");
#endif
				{
					int smoke_type=script_env[id].pop();
					int from_piece=script->script_code[script_id][pos++];
#ifdef PRINT_CODE
					printf("smoke_type %d from %d\n",smoke_type,from_piece);
#endif
					compute_model_coord();
					if(data.dir[from_piece].x!=0.0f || data.dir[from_piece].y!=0.0f || data.dir[from_piece].z!=0.0f)
						particle_engine.emit_part(O+Pos+data.pos[from_piece],data.dir[from_piece],0,1,10.0f);
					else
						particle_engine.make_smoke(O+Pos+data.pos[from_piece],0,1,10.0f);
				}
				break;
			case SCRIPT_PUSH_CONST:
#ifdef PRINT_CODE
				printf("PUSH_CONST\n");
#endif
				script_env[id].push(script->script_code[script_id][pos]);
				pos++;
				break;
			case SCRIPT_PUSH_VAR:
#ifdef PRINT_CODE
				printf("PUSH_VAR\n");
#endif
				script_env[id].push(script_env[id].env->var[script->script_code[script_id][pos]]);
				pos++;
				break;
			case SCRIPT_SET_VAR:
#ifdef PRINT_CODE
				printf("SET_VAR\n");
#endif
				script_env[id].env->var[script->script_code[script_id][pos]]=script_env[id].pop();
				pos++;
				break;
			case SCRIPT_PUSH_STATIC_VAR:
#ifdef PRINT_CODE
				printf("PUSH_STATIC_VAR\n");
#endif
				script_env[id].push(s_var[script->script_code[script_id][pos]]);
				pos++;
				break;
			case SCRIPT_SET_STATIC_VAR:
#ifdef PRINT_CODE
				printf("SET_STATIC_VAR\n");
#endif
				s_var[script->script_code[script_id][pos]]=script_env[id].pop();
				pos++;
				break;
			case SCRIPT_OR:
#ifdef PRINT_CODE
				printf("OR\n");
#endif
				{
					int v1=script_env[id].pop(),v2=script_env[id].pop();
					script_env[id].push(v1|v2);
				}
				break;
			case SCRIPT_START_SCRIPT:				// Transfère l'éxecution à un autre script
#ifdef PRINT_CODE
				printf("START_SCRIPT\n");
#endif
				{
				int function_id=script->script_code[script_id][pos];			// Lit un code
				pos++;
				int num_param=script->script_code[script_id][pos];			// Lit un code
				pos++;
				int s_id=launch_script(function_id);
				if(s_id>=0) {
					for(int i=0;i<num_param;i++)		// Lit les paramètres
						script_env[s_id].env->var[i]=script_env[id].pop();
					script_env[s_id].env->signal_mask=script_env[id].env->signal_mask;
					}
				else
					for(int i=0;i<num_param;i++)		// Enlève les paramètres de la pile
						script_env[id].pop();
				done=true;
				}
				break;
			case SCRIPT_RETURN:		// Retourne au script précédent
#ifdef PRINT_CODE
				printf("RETURN\n");
#endif
				{
					script_val[script_id]=script_env[id].env->var[0];
					SCRIPT_ENV_STACK *old=script_env[id].env;
					script_env[id].env=script_env[id].env->next;
					delete old;
					script_env[id].pop();		// Enlève la valeur retournée
				}
				if(script_env[id].env) {
					script_id=(script_env[id].env->cur&0xFF);			// Récupère l'identifiant du script en cours d'éxecution et la position d'éxecution
					pos=(script_env[id].env->cur>>8);
					}
				done=true;
				break;
			case SCRIPT_JUMP:						// Commande de saut
#ifdef PRINT_CODE
				printf("JUMP\n");
#endif
				{
					int target_offset=script->script_code[script_id][pos];			// Lit un code
					pos=target_offset-script->dec_offset[script_id];								// Déplace l'éxecution
				}
				break;
			case SCRIPT_ADD:
#ifdef PRINT_CODE
				printf("ADD\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1+v2);
				}
				break;	//added
			case SCRIPT_STOP_SPIN:
#ifdef PRINT_CODE
				printf("STOP_SPIN\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					int v=script_env[id].pop();
					if(axis!=2)
						v=-v;
					data.axe[axis][obj].rot_limit=false;
					data.axe[axis][obj].rot_speed_limit=true;
					data.axe[axis][obj].rot_target_speed=0.0f;
					if(data.axe[axis][obj].rot_speed>0.0f)
						data.axe[axis][obj].rot_accel=-abs(v);
					else
						data.axe[axis][obj].rot_accel=abs(v);
				}
				break;	//added
			case SCRIPT_DIVIDE:
#ifdef PRINT_CODE
				printf("DIVIDE\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v2 / v1);
				}
				break;	//added
			case SCRIPT_MOVE_PIECE_NOW:
#ifdef PRINT_CODE
				printf("MOVE_PIECE_NOW\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					if(axis==0)
						data.axe[axis][obj].pos=-script_env[id].pop()*div;
					else
						data.axe[axis][obj].pos=script_env[id].pop()*div;
				}
				break;	//added
			case SCRIPT_TURN_PIECE_NOW:
#ifdef PRINT_CODE
				printf("TURN_PIECE_NOW\n");
#endif
				{
					int obj=script->script_code[script_id][pos++];
					int axis=script->script_code[script_id][pos++];
					int v=script_env[id].pop();
					if(axis!=2)
						v=-v;
					data.axe[axis][obj].angle=-v*divisor*360.0f;
				}
				break;	//added
			case SCRIPT_CACHE:
#ifdef PRINT_CODE
				printf("CACHE\n");
#endif
				pos++;
				break;	//added
			case SCRIPT_COMPARE_AND:
#ifdef PRINT_CODE
				printf("COMPARE_AND\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1 && v2);
				}
				break;	//added
			case SCRIPT_COMPARE_OR:
#ifdef PRINT_CODE
				printf("COMPARE_OR\n");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
					script_env[id].push(v1 || v2);
				}
				break;	//added
			case SCRIPT_CALL_FUNCTION:
#ifdef PRINT_CODE
				printf("CALL_FUNCTION\n");
#endif
				{
				int function_id=script->script_code[script_id][pos];			// Lit un code
				pos++;
				int num_param=script->script_code[script_id][pos];			// Lit un code
				pos++;
				script_env[id].env->cur=script_id+(pos<<8);
				SCRIPT_ENV_STACK *old=script_env[id].env;
				script_env[id].env=new SCRIPT_ENV_STACK;
				script_env[id].env->init();
				script_env[id].env->next=old;
				script_env[id].env->cur=function_id;
				for(int i=0;i<num_param;i++)		// Lit les paramètres
					script_env[id].env->var[i]=script_env[id].pop();
				done=true;
				pos=0;
				script_id=function_id;
				}
				break;	//added
			case SCRIPT_GET:
				printf("GET *\n");
				{
					int v4=script_env[id].pop();
					int v3=script_env[id].pop();
					int v2=script_env[id].pop();
					int v1=script_env[id].pop();
					int val=script_env[id].pop();
					switch(val)
					{
					case ACTIVATION:
					case STANDINGMOVEORDERS:
					case STANDINGFIREORDERS:
					case HEALTH:
					case INBUILDSTANCE:
					case BUSY:
					case BUILD_PERCENT_LEFT:
					case YARD_OPEN:
					case BUGGER_OFF:
					case ARMORED:
						script_env[id].push((int)port[val]);
						break;
					case PIECE_XZ:
						compute_model_coord();
						script_env[id].push(((int)data.pos[v1].x<<16) | ((int)data.pos[v1].z));
						break;
					case PIECE_Y:
						compute_model_coord();
						script_env[id].push((int)data.pos[v1].y);
						break;
					case UNIT_XZ:
						if (v1 == 0)	
							script_env[id].push(((int)Pos.x<<16) | ((int)Pos.z));
						else
							script_env[id].push(0);
						break;
					case UNIT_Y:
						script_env[id].push((int)Pos.y);
						break;
					case UNIT_HEIGHT:
						script_env[id].push((int)sqrt(model->size*0.5f));
						break;
					case XZ_ATAN:
						{
							int x=v1>>16;
							int z=v1&0xFFFF;
							script_env[id].push((int)atan((float)x/z));
						}
						break;
					case XZ_HYPOT:
						{
							int x=v1>>16;
							int z=v1&0xFFFF;
							script_env[id].push((int)sqrt(x*x+z*z));
						}
						break;
					case ATAN:
						script_env[id].push((int)atan((float)v1/v2));
						break;
					case HYPOT:
						script_env[id].push((int)sqrt(v1*v1+v2*v2));
						break;
					case GROUND_HEIGHT:
						script_env[id].push((int)map->get_unit_h(Pos.x,Pos.z));
						break;
					default:
						printf("GET constante inconnue %d", val);
					};
				}
				break;	//added
			case SCRIPT_SET_VALUE:
#ifdef PRINT_CODE
				printf("SET_VALUE *: ");
#endif
				{
					int v1=script_env[id].pop();
					int v2=script_env[id].pop();
#ifdef PRINT_CODE
					printf("%d %d\n",v1,v2);
#endif
					port[v2]=v1;
					switch(v2)
					{
					case BUGGER_OFF:
						{
							int px=((int)(Pos.x+map->map_w*0.5f))>>3;
							int py=((int)(Pos.z+map->map_h*0.5f))>>3;
							for(int y=py-(unit_manager.unit_type[type_id].FootprintZ>>1);y<=py+(unit_manager.unit_type[type_id].FootprintZ>>1);y++)
								if(y>=0 && y<(map->bloc_h<<1)-1)
									for(int x=px-(unit_manager.unit_type[type_id].FootprintX>>1);x<=px+(unit_manager.unit_type[type_id].FootprintX>>1);x++)
										if(x>=0 && x<(map->bloc_w<<1)-1)
											if(map->map_data[y][x].unit_idx!=-1 && map->map_data[y][x].unit_idx!=idx) {
												int cur_idx=map->map_data[y][x].unit_idx;
												if(units.unit[cur_idx].owner_id!=owner_id && units.unit[cur_idx].port[BUILD_PERCENT_LEFT]==0.0f && (units.unit[cur_idx].mission==NULL || units.unit[cur_idx].mission->mission!=MISSION_MOVE)) {
													VECTOR target=units.unit[cur_idx].Pos;
													target.z+=100.0f;
													PATH_NODE *path=NULL;
													if(!unit_manager.unit_type[units.unit[cur_idx].type_id].canfly) {
														float dh_max=unit_manager.unit_type[units.unit[cur_idx].type_id].MaxSlope*H_DIV;
														float h_min=map->sealvl-unit_manager.unit_type[units.unit[cur_idx].type_id].MaxWaterDepth*H_DIV;
														if(unit_manager.unit_type[units.unit[cur_idx].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,units.unit[cur_idx].Pos,target,unit_manager.unit_type[units.unit[cur_idx].type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[units.unit[cur_idx].type_id].FootprintX,unit_manager.unit_type[units.unit[cur_idx].type_id].FootprintZ,cur_idx);
														}
													else
														path=direct_path(target);
													sound_manager.play(unit_manager.unit_type[units.unit[cur_idx].type_id].ok1,units.unit[cur_idx].Pos);
													units.unit[cur_idx].set_mission(MISSION_MOVE,&target,false,0,true,NULL,path);
													}
												}
						}
						break;
					};
				}
				break;	//added
			case SCRIPT_ATTACH_UNIT:
				printf("ATTACH_UNIT *\n");
				pos++;
				break;	//added
			case SCRIPT_DROP_UNIT:
				printf("DROP_UNIT *\n");
				pos++;
				break;	//added
			default:
				printf("UNKNOWN %d, STOPPING SCRIPT\n",code);
				{
					script_val[script_id]=script_env[id].env->var[0];
					SCRIPT_ENV_STACK *old=script_env[id].env;
					script_env[id].env=script_env[id].env->next;
					delete old;
				}
				if(script_env[id].env) {
					script_id=(script_env[id].env->cur&0xFF);			// Récupère l'identifiant du script en cours d'éxecution et la position d'éxecution
					pos=(script_env[id].env->cur>>8);
					}
				else
					script_env[id].running=false;
				done=true;
			};
		}while(!done);
		if(script_env[id].env)
			script_env[id].env->cur=script_id+(pos<<8);
	}

	inline float ballistic_angle(float v,float g,float d,float y_s,float y_e)			// Calculs de ballistique pour l'angle de tir
	{
		float v2=v*v;
		float a=4*v2*v2/(g*g*d*d)-4-8*v2*(y_e-y_s)/(g*d*d);
		if(a<0.0f)				// Pas de solution
			return 360.0f;
		return RAD2DEG*atan(v2/(g*d)-0.5f*sqrt(a));
	}

	int UNIT::move(float dt,MAP *map)
	{
		map->rect((((int)(Pos.x+map->map_w*0.5f))>>3)-(unit_manager.unit_type[type_id].FootprintX>>1),(((int)(Pos.z+map->map_h*0.5f))>>3)-(unit_manager.unit_type[type_id].FootprintZ>>1),unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,-1);
		compute_coord=true;
		if(port[BUILD_PERCENT_LEFT]>0.0f)	goto script_exec;		// L'unité n'est pas finie

		if(hp<=0.0f) {			// L'unité est détruite
			switch(flags&0xFD)
			{
			case 1:				// Début de la mort de l'unité	(Lance le script)
				flags=4;
				{
					int param[]={ (int)-hp };
					launch_script(get_script_index("killed"),1,param);
				}
				break;
			case 4:				// Vérifie si le script est terminé
				if(!is_running(get_script_index("killed")))
					return -1;
				break;
			};
			goto script_exec;
			}

		if(data.nb_piece>0)
			data.move(dt);

		V_Angle.x=V_Angle.y=V_Angle.z=0.0f;
		c_time+=dt;
		if(mission) {
				mission->time+=dt;
				switch(mission->mission)						// Commandes générales
				{
				case MISSION_STANDBY:
				case MISSION_VTOL_STANDBY:
					next_mission();
					break;
				case MISSION_ATTACK:										// Attaque une unité
					{
						UNIT *target_unit=(UNIT*) mission->p;
						if(target_unit!=NULL && target_unit->flags) {
							VECTOR Dir=target_unit->Pos-Pos;
							Dir.y=0.0f;
							mission->target=target_unit->Pos;
							float dist=Dir.Sq();
							int maxdist=0;
							if(unit_manager.unit_type[type_id].weapon[0])
								maxdist=(int)(unit_manager.unit_type[type_id].weapon[0]->range*0.5f);
							else if(unit_manager.unit_type[type_id].weapon[1])
								maxdist=(int)(unit_manager.unit_type[type_id].weapon[1]->range*0.5f);
							else if(unit_manager.unit_type[type_id].weapon[2])
								maxdist=(int)(unit_manager.unit_type[type_id].weapon[2]->range*0.5f);
							if(dist>maxdist*maxdist) {	// Si l'unité est trop loin de sa cible
								VECTOR target;
								dist=sqrt(dist);
								target=Pos+(dist-maxdist*0.75f)/dist*Dir;
								PATH_NODE *path;
								if(unit_manager.unit_type[type_id].canfly)
									path=direct_path(target);
								else {
									float dh_max=unit_manager.unit_type[type_id].MaxSlope*H_DIV;
									float h_min=map->sealvl-unit_manager.unit_type[type_id].MaxWaterDepth*H_DIV;
									if(unit_manager.unit_type[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,Pos,target,unit_manager.unit_type[type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx);
									}
								c_time=0.0f;
								add_mission(MISSION_MOVE,&target,true,0,NULL,path);		// On lui demande de se rapprocher
								}
							else {
								launch_script(get_script_index("QueryPrimary"));
								mission->mission=MISSION_AIM;							// sinon on lui demande de viser
								}
						}
						else {
							launch_script(get_script_index("TargetCleared"));
							next_mission();
							}
					}
					break;
				case MISSION_AIM:											// Vise une unité
					if(((UNIT*)(mission->p))->flags) {
						int query_id=get_script_index("QueryPrimary");
						if(!is_running(query_id)) {
							if(mission->time>=unit_manager.unit_type[type_id].weapon[0]->reloadtime) {
								if(unit_manager.unit_type[type_id].weapon[0]->turret) {			// Si l'unité doit viser, on la fait viser
									UNIT *target_unit=(UNIT*) mission->p;
									int start_piece=script_val[query_id];
									if(start_piece<0 || start_piece>=data.nb_piece)
										start_piece=0;
									VECTOR target=target_unit->Pos+target_unit->model->center-(Pos+data.pos[start_piece]);
									float dist=target.Norm();
									target=(1.0f/dist)*target;
									VECTOR I,J,IJ,RT;
									I.x=0.0f;	I.z=1.0f;	I.y=0.0f;
									J.x=1.0f;	J.z=0.0f;	J.y=0.0f;
									IJ.x=0.0f;	IJ.z=0.0f;	IJ.y=1.0f;
									RT=target;
									RT.y=0.0f;
									RT.Unit();
									float angle=acos(I%RT)*RAD2DEG;
									if(J%RT<0.0f) angle=-angle;
									angle-=Angle.y;
									if(angle<-180.0f)	angle+=360.0f;
									else if(angle>180.0f)	angle-=360.0f;
//									int aiming[]={ (int)(angle*65536.0f/360.0f), -4096 };
									int aiming[]={ (int)(angle*DEG2TA), -4096 };
									if(unit_manager.unit_type[type_id].weapon[0]->ballistic) {		// Calculs de ballistique
										VECTOR D=(Pos+data.pos[start_piece]-target_unit->Pos-target_unit->model->center);
										D.y=0.0f;
										float v;
										if(unit_manager.unit_type[type_id].weapon[0]->startvelocity==0.0f)
											v=0.5f*unit_manager.unit_type[type_id].weapon[0]->weaponvelocity;
										else
											v=0.5f*unit_manager.unit_type[type_id].weapon[0]->startvelocity;
//										aiming[1]=(int)(ballistic_angle(v,9.8,D.Norm(),(Pos+data.pos[start_piece]).y,target_unit->Pos.y+target_unit->model->center.y)/360.0f*65536.0f);
										aiming[1]=(int)(ballistic_angle(v,9.8,D.Norm(),(Pos+data.pos[start_piece]).y,target_unit->Pos.y+target_unit->model->center.y)*DEG2TA);
										}
									else {
										VECTOR K=target;
										K.y=0.0f;
										K.Unit();
										angle=acos(K%target)*RAD2DEG;
										J.x=0.0f;	J.y=1.0f;
										if(target%J>0.0f)
											angle=-angle;
										while(angle>180.0f)	angle-=360.0f;
										while(angle<-180.0f)	angle+=360.0f;
//										aiming[1]=(int)(angle/360.0f*65536.0f);
										aiming[1]=(int)(angle*DEG2TA);
										}
//									aim_dir=cos(aiming[1]*2.0f*PI/65536.0f)*(cos(aiming[0]*2.0f*PI/65536.0f)*I+sin(aiming[0]*2.0f*PI/65536.0f)*J)+sin(aiming[1]*2.0f*PI/65536.0f)*IJ;
									aim_dir=cos(aiming[1]*TA2RAD)*(cos(aiming[0]*TA2RAD+Angle.y*DEG2RAD)*I+sin(aiming[0]*TA2RAD+Angle.y*DEG2RAD)*J)+sin(aiming[1]*TA2RAD)*IJ;
									launch_script(get_script_index("AimPrimary"),2,aiming);
									}
									mission->mission=MISSION_SHOOT;									// (puis) on lui demande de tirer
								}
							}
						}
					else {
						launch_script(get_script_index("TargetCleared"));
						next_mission();
						}
					break;
				case MISSION_SHOOT:											// Tire sur une unité
					if(((UNIT*)(mission->p))->flags) {
						int query_id=get_script_index("QueryPrimary");
						if(!is_running(get_script_index("AimPrimary"))) {
							launch_script(get_script_index("FirePrimary"));
							sound_manager.play(unit_manager.unit_type[type_id].weapon[0]->soundstart,Pos);
							int start_piece=script_val[query_id];
							if(start_piece>=0 && start_piece<data.nb_piece) {
								compute_model_coord();
								VECTOR Dir=data.dir[start_piece];
								if(Dir.x==0.0f && Dir.y==0.0f && Dir.z==0.0f) {
									Dir=aim_dir;
									if(unit_manager.unit_type[type_id].weapon[0]->lineofsight)
										Dir=((UNIT*)(mission->p))->Pos-(Pos+data.pos[start_piece]);
									Dir.Unit();
									}
								shoot(((UNIT*)(mission->p))->idx,Pos+data.pos[start_piece],Dir,unit_manager.unit_type[type_id].weapon[0]!=NULL ? 0 : unit_manager.unit_type[type_id].weapon[1]!=NULL ? 1 : 2);
								}
							if(((UNIT*)(mission->p))->hp>0) {				// La cible est-elle détruite ??
								mission->mission=MISSION_ATTACK;
								mission->time=0.0f;
								}
							else {
								launch_script(get_script_index("TargetCleared"));
								next_mission();
								}
							}
						}
					else {
						launch_script(get_script_index("TargetCleared"));
						next_mission();
						}
					break;
				case MISSION_STOP:											// Arrête tout ce qui était en cours
					if(mission->data!=0) {
						next_mission();
						if(mission!=NULL && mission->mission==MISSION_STOP)		// Mode attente
							mission->data=1;
						}
					else {
						launch_script(get_script_index("StopMoving"));
						mission->data++;
						}
					break;
				case MISSION_REPAIR:
					{
						UNIT *target_unit=(UNIT*) mission->p;
						if(target_unit!=NULL && target_unit->flags && target_unit->port[BUILD_PERCENT_LEFT]==0.0f) {
							if(target_unit->hp>=unit_manager.unit_type[target_unit->type_id].MaxDamage || unit_manager.unit_type[type_id].TEDclass==CLASS_PLANT) {
								if(unit_manager.unit_type[type_id].TEDclass!=CLASS_PLANT)
									target_unit->hp=unit_manager.unit_type[target_unit->type_id].MaxDamage;
								next_mission();
								}
							else {
								VECTOR Dir=target_unit->Pos-Pos;
								Dir.y=0.0f;
								mission->target=target_unit->Pos;
								float dist=Dir.Sq();
								int maxdist=(int)(unit_manager.unit_type[type_id].BuildDistance);
								if(dist>maxdist*maxdist && unit_manager.unit_type[type_id].TEDclass!=CLASS_PLANT) {	// Si l'unité est trop loin du chantier
									VECTOR target;
									dist=sqrt(dist);
									target=Pos+(dist-maxdist*0.75f)/dist*Dir;
									PATH_NODE *path;
									if(unit_manager.unit_type[type_id].canfly)
										path=direct_path(target);
									else {
										float dh_max=unit_manager.unit_type[type_id].MaxSlope*H_DIV;
										float h_min=map->sealvl-unit_manager.unit_type[type_id].MaxWaterDepth*H_DIV;
										if(unit_manager.unit_type[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,Pos,target,unit_manager.unit_type[type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx);
										}
									c_time=0.0f;
									add_mission(MISSION_MOVE,&target,true,0,NULL,path);		// On lui demande de se rapprocher
									}
								else {
									if(dist!=0.0f)
										Dir=1.0f/sqrt(dist)*Dir;
									float dest_angle=acos(Dir.z)*RAD2DEG;
									if(Dir.x<0.0f) dest_angle=-dest_angle;
									while(fabs(dest_angle-Angle.y)>180.0f) {
										if(dest_angle<Angle.y)
											Angle.y-=360.0f;
										else
											Angle.y+=360.0f;
										}
									if(dist>0.0f && fabs(dest_angle-Angle.y)>=unit_manager.unit_type[type_id].BuildAngle) {
										float aspeed=unit_manager.unit_type[type_id].TurnRate;
										if(dest_angle<Angle.y)
											aspeed=-aspeed;
										float a=dest_angle-Angle.y;
										V_Angle.y=aspeed*0.1f;
										float b=dest_angle-(Angle.y+dt*V_Angle.y);
										if((a<0.0f && b>0.0f) || (a>0.0f && b<0.0f)) {
											V_Angle.y=0.0f;
											Angle.y=dest_angle;
											}
										}
									else {
										float conso_metal=dt*((float)(unit_manager.unit_type[type_id].WorkerTime*unit_manager.unit_type[target_unit->type_id].BuildCostMetal))/unit_manager.unit_type[target_unit->type_id].HealTime;
										float conso_energy=dt*((float)(unit_manager.unit_type[type_id].WorkerTime*unit_manager.unit_type[target_unit->type_id].BuildCostEnergy))/unit_manager.unit_type[target_unit->type_id].HealTime;
										if(players.metal[owner_id]>=conso_metal && players.energy[owner_id]>=conso_energy) {
											players.metal_u[owner_id]+=conso_metal;
											players.energy_u[owner_id]+=conso_energy;
											target_unit->hp+=dt*unit_manager.unit_type[type_id].WorkerTime*unit_manager.unit_type[target_unit->type_id].MaxDamage/unit_manager.unit_type[target_unit->type_id].HealTime;
											}
										}
									}
								}
							}
						else if(target_unit!=NULL && target_unit->flags) {
							VECTOR Dir=target_unit->Pos-Pos;
							Dir.y=0.0f;
							mission->target=target_unit->Pos;
							float dist=Dir.Sq();
							int maxdist=(int)(unit_manager.unit_type[type_id].BuildDistance);
							if(dist>maxdist*maxdist && unit_manager.unit_type[type_id].TEDclass!=CLASS_PLANT) {	// Si l'unité est trop loin du chantier
								VECTOR target;
								dist=sqrt(dist);
								target=Pos+(dist-maxdist*0.75f)/dist*Dir;
								PATH_NODE *path;
								if(unit_manager.unit_type[type_id].canfly)
									path=direct_path(target);
								else {
									float dh_max=unit_manager.unit_type[type_id].MaxSlope*H_DIV;
									float h_min=map->sealvl-unit_manager.unit_type[type_id].MaxWaterDepth*H_DIV;
									if(unit_manager.unit_type[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,Pos,target,unit_manager.unit_type[type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx);
									}
								c_time=0.0f;
								add_mission(MISSION_MOVE,&target,true,0,NULL,path);		// On lui demande de se rapprocher
								}
							else {
								if(dist!=0.0f)
									Dir=1.0f/sqrt(dist)*Dir;
								float dest_angle=acos(Dir.z)*RAD2DEG;
								if(Dir.x<0.0f) dest_angle=-dest_angle;
								while(fabs(dest_angle-Angle.y)>180.0f) {
									if(dest_angle<Angle.y)
										Angle.y-=360.0f;
									else
										Angle.y+=360.0f;
									}
								if(dist>0.0f && fabs(dest_angle-Angle.y)>=unit_manager.unit_type[type_id].BuildAngle && unit_manager.unit_type[type_id].TEDclass!=CLASS_PLANT) {
									float aspeed=unit_manager.unit_type[type_id].TurnRate;
									if(dest_angle<Angle.y)
										aspeed=-aspeed;
									float a=dest_angle-Angle.y;
									V_Angle.y=aspeed*0.1f;
									float b=dest_angle-(Angle.y+dt*V_Angle.y);
									if((a<0.0f && b>0.0f) || (a>0.0f && b<0.0f)) {
										V_Angle.y=0.0f;
										Angle.y=dest_angle;
										}
									}
								else
									mission->mission=MISSION_BUILD_2;		// Change de type de mission
								}
							}
					}
					break;
				case MISSION_BUILD_2:
					{
						UNIT *target_unit=(UNIT*) mission->p;
						if(target_unit->flags) {
							if(target_unit->port[BUILD_PERCENT_LEFT]<=0.0f) {
								target_unit->port[BUILD_PERCENT_LEFT]=0.0f;
								target_unit->launch_script(target_unit->get_script_index("create"));		// Script post création
								if(unit_manager.unit_type[target_unit->type_id].ActivateWhenBuilt)		// Activation automatique
									target_unit->activate();
								if(unit_manager.unit_type[type_id].TEDclass==CLASS_PLANT) {		// Ordre de se déplacer
									VECTOR target=Pos;
									target.z+=128.0f;
									PATH_NODE *path;
									if(unit_manager.unit_type[target_unit->type_id].canfly)
										path=direct_path(target);
									else {
										float dh_max=unit_manager.unit_type[target_unit->type_id].MaxSlope*H_DIV;
										float h_min=map->sealvl-unit_manager.unit_type[target_unit->type_id].MaxWaterDepth*H_DIV;
										if(unit_manager.unit_type[target_unit->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,target_unit->Pos,target,unit_manager.unit_type[target_unit->type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx);
										}
									target_unit->set_mission(MISSION_MOVE,&target,false,0,true,NULL,path);		// Fait sortir l'unité du bâtiment
									}
								mission->p=NULL;
								next_mission();
								deactivate();
								}
							else {
								float conso_metal=((float)(unit_manager.unit_type[type_id].WorkerTime*unit_manager.unit_type[target_unit->type_id].BuildCostMetal))/unit_manager.unit_type[target_unit->type_id].BuildTime;
								float conso_energy=((float)(unit_manager.unit_type[type_id].WorkerTime*unit_manager.unit_type[target_unit->type_id].BuildCostEnergy))/unit_manager.unit_type[target_unit->type_id].BuildTime;
								if(players.metal[owner_id]>=conso_metal*dt && players.energy[owner_id]>=conso_energy*dt) {
									players.metal_u[owner_id]+=conso_metal;
									players.energy_u[owner_id]+=conso_energy;
									target_unit->port[BUILD_PERCENT_LEFT]-=dt*unit_manager.unit_type[type_id].WorkerTime*100.0f/unit_manager.unit_type[target_unit->type_id].BuildTime;
									target_unit->hp=(1.0f-0.01f*target_unit->port[BUILD_PERCENT_LEFT])*unit_manager.unit_type[target_unit->type_id].MaxDamage;
									}
								}
							}
						else {
							deactivate();
							next_mission();
							}
						}
					break;
				case MISSION_BUILD:
					if(mission->p) {
						if(!is_running(get_script_index("Activate")))
							mission->mission=MISSION_BUILD_2;		// Change les objectifs
						}
					else {
						VECTOR Dir=mission->target-Pos;
						Dir.y=0.0f;
						float dist=Dir.Sq();
						int maxdist=(int)(unit_manager.unit_type[type_id].BuildDistance);
						if(dist>maxdist*maxdist && unit_manager.unit_type[type_id].TEDclass!=CLASS_PLANT) {	// Si l'unité est trop loin du chantier
							VECTOR target;
							dist=sqrt(dist);
							target=Pos+(dist-maxdist*0.75f)/dist*Dir;
							PATH_NODE *path;
							if(unit_manager.unit_type[type_id].canfly)
								path=direct_path(target);
							else {
								float dh_max=unit_manager.unit_type[type_id].MaxSlope*H_DIV;
								float h_min=map->sealvl-unit_manager.unit_type[type_id].MaxWaterDepth*H_DIV;
								if(unit_manager.unit_type[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,Pos,target,unit_manager.unit_type[type_id].TEDclass==CLASS_SHIP,unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx);
								}
							c_time=0.0f;
							add_mission(MISSION_MOVE,&target,true,0,NULL,path);		// On lui demande de se rapprocher
							}
						else {
							if(dist!=0.0f)
								Dir=(1.0f/sqrt(dist))*Dir;
							float dest_angle=acos(Dir.z)*RAD2DEG;
							if(Dir.x<0.0f) dest_angle=-dest_angle;
							while(fabs(dest_angle-Angle.y)>180.0f) {
								if(dest_angle<Angle.y)
									Angle.y-=360.0f;
								else
									Angle.y+=360.0f;
								}
							if(dist>0.0f && fabs(dest_angle-Angle.y)>=unit_manager.unit_type[type_id].BuildAngle && unit_manager.unit_type[type_id].TEDclass!=CLASS_PLANT) {
								float aspeed=unit_manager.unit_type[type_id].TurnRate;
								if(dest_angle<Angle.y)
									aspeed=-aspeed;
								float a=dest_angle-Angle.y;
								V_Angle.y=aspeed*0.1f;
								float b=dest_angle-(Angle.y+dt*V_Angle.y);
								if((a<0.0f && b>0.0f) || (a>0.0f && b<0.0f)) {
									V_Angle.y=0.0f;
									Angle.y=dest_angle;
									}
								}
							else {
								if(map->check_rect((((int)(mission->target.x+map->map_w*0.5f))>>3)-(unit_manager.unit_type[mission->data].FootprintX>>1),(((int)(mission->target.z+map->map_h*0.5f))>>3)-(unit_manager.unit_type[mission->data].FootprintZ>>1),unit_manager.unit_type[mission->data].FootprintX,unit_manager.unit_type[mission->data].FootprintZ,-1)) {				// Vérifie s'il y a la place de construire l'unité
									mission->p=create_unit(mission->data,owner_id,mission->target);
									activate();
									}
								else
									next_mission();
								}
							}
						}
					break;
				};

			switch(unit_manager.unit_type[type_id].TEDclass)			// Commandes particulières
			{
			case CLASS_SHIP:
				switch(mission->mission)
				{
				case MISSION_STANDBY:
					next_mission();
					V=exp(-dt)*V;			// Frottements
					break;
				case MISSION_MOVE:
					{
						VECTOR J,I;
						VECTOR Target=mission->target;
						if(mission->path)
							Target=mission->path->Pos;
						J=Target-Pos;
						J.y=0.0f;
						float dist=J.Norm();
						if(dist>0.0f)
							J=1.0f/dist*J;
						float dest_angle=acos(J.z)*RAD2DEG;
						if(J.x<0.0f) dest_angle=-dest_angle;
						while(fabs(dest_angle-Angle.y)>180.0f) {
							if(dest_angle<Angle.y)
								Angle.y-=360.0f;
							else
								Angle.y+=360.0f;
							}
						if(dist>0.0f && fabs(dest_angle-Angle.y)>=1.0f) {
							float aspeed=unit_manager.unit_type[type_id].TurnRate;
							if(dest_angle<Angle.y)
								aspeed=-aspeed;
							float a=dest_angle-Angle.y;
							V_Angle.y=aspeed*0.1f;
							float b=dest_angle-(Angle.y+dt*V_Angle.y);
							if((a<0.0f && b>0.0f) || (a>0.0f && b<0.0f)) {
								V_Angle.y=0.0f;
								Angle.y=dest_angle;
								}
							}

						J.z=cos(Angle.y*DEG2RAD);
						J.x=sin(Angle.y*DEG2RAD);
						J.y=0.0f;
						I.z=-J.x;
						I.x=J.z;
						I.y=0.0f;
						V=(V%J)*J+exp(-dt)*(V%I)*I;
						if(!(dist<128.0f && fabs(Angle.y-dest_angle)>=1)) {
							float speed=V.Norm();
							float time_to_stop=speed/unit_manager.unit_type[type_id].BrakeRate;
							float min_dist=time_to_stop*(speed-unit_manager.unit_type[type_id].BrakeRate*0.5f*time_to_stop);
							if(min_dist>=dist)			// Freine si nécessaire
								V=V-unit_manager.unit_type[type_id].BrakeRate*dt*J;
							else if(speed<unit_manager.unit_type[type_id].MaxVelocity)
								V=V+unit_manager.unit_type[type_id].Acceleration*dt*J;
							if(dist<10.0f || mission->path==NULL) {
								if(mission->path)
									mission->path=next_node(mission->path);
								if(mission->path==NULL) {
									next_mission();
									sound_manager.play(unit_manager.unit_type[type_id].arrived1,Pos);
									launch_script(get_script_index("stopmoving"));
									}
								}
							}
						else
							V=exp(-dt)*V;			// Frottements
					}
					break;
				default:
					V=exp(-dt)*V;			// Frottements
				};
				break;
			case CLASS_PLANT:
				switch(mission->mission)
				{
				case MISSION_STANDBY:
				case MISSION_BUILD:
				case MISSION_BUILD_2:
				case MISSION_REPAIR:
					break;
				default:
					next_mission();
				};
				break;
			case CLASS_WATER:
			case CLASS_VTOL:
			case CLASS_KBOT:
			case CLASS_COMMANDER:
			case CLASS_TANK:
			case CLASS_CNSTR:
				{
				VECTOR K,PV;
				K.x=0.0f;
				K.y=1.0f;
				K.z=0.0f;
				if(mission->mission!=MISSION_MOVE) {
					PV=(V%K)*K;
					V=exp(-2.0f*dt)*(V-PV)+PV;		// N'altère pas la déscente des unités
					}
				switch(mission->mission)
				{
				case MISSION_REPAIR:
				case MISSION_BUILD:
				case MISSION_BUILD_2:
					if(unit_manager.unit_type[type_id].canfly)
						activate();
					break;
				case MISSION_MOVE:
					{
						if(unit_manager.unit_type[type_id].canfly)
							activate();
						VECTOR J,I;
						VECTOR Target=mission->target;
						if(mission->path)
							Target=mission->path->Pos;
						J=Target-Pos;
						J.y=0.0f;
						float dist=J.Norm();
						if(dist>0.0f)
							J=1.0f/dist*J;
						float dest_angle=acos(J.z)*RAD2DEG;
						if(J.x<0.0f) dest_angle=-dest_angle;
						while(fabs(dest_angle-Angle.y)>180.0f) {
							if(dest_angle<Angle.y)
								Angle.y-=360.0f;
							else
								Angle.y+=360.0f;
							}
						if(dist>0.0f && fabs(dest_angle-Angle.y)>=1.0f) {
							float aspeed=unit_manager.unit_type[type_id].TurnRate;
							if(dest_angle<Angle.y)
								aspeed=-aspeed;
							float a=dest_angle-Angle.y;
							V_Angle.y=aspeed*0.1f;
							float b=dest_angle-(Angle.y+dt*V_Angle.y);
							if((a<0.0f && b>0.0f) || (a>0.0f && b<0.0f)) {
								V_Angle.y=0.0f;
								Angle.y=dest_angle;
								}
							}

						J.z=cos(Angle.y*DEG2RAD);
						J.x=sin(Angle.y*DEG2RAD);
						J.y=0.0f;
						I.z=-J.x;
						I.x=J.z;
						I.y=0.0f;
						V=(V%K)*K+(V%J)*J+exp(-4.0f*dt)*(V%I)*I;
						if(!(dist<128.0f && fabs(Angle.y-dest_angle)>=1)) {
							float speed=V.Norm();
							float time_to_stop=speed/unit_manager.unit_type[type_id].BrakeRate;
							float min_dist=time_to_stop*(speed-unit_manager.unit_type[type_id].BrakeRate*0.5f*time_to_stop);
							if(min_dist>=dist)			// Freine si nécessaire
								V=V-unit_manager.unit_type[type_id].BrakeRate*dt*J;
							else if(speed<unit_manager.unit_type[type_id].MaxVelocity)
								V=V+unit_manager.unit_type[type_id].Acceleration*dt*J;
							if(dist<5.0f || mission->path==NULL) {
								if(mission->path)
									mission->path=next_node(mission->path);
								if(mission->path==NULL) {		// Si l'on est arrivé au bout du chemin
									next_mission();
									sound_manager.play(unit_manager.unit_type[type_id].arrived1,Pos);
									launch_script(get_script_index("stopmoving"));
									}
								}
							}
					}
					break;
				default:
					if(unit_manager.unit_type[type_id].canfly)
						deactivate();
					};
				}
				break;
			case CLASS_UNDEF:
			case CLASS_METAL:
			case CLASS_ENERGY:
			case CLASS_SPECIAL:
			case CLASS_FORT:
				break;
			default:
				printf("type inconnu %d\n",unit_manager.unit_type[type_id].TEDclass);
			};
			switch(mission->mission)		// Pour le code post déplacement
			{
			case MISSION_MOVE:			// Oriente l'unité si elle est au sol
				if(!unit_manager.unit_type[type_id].canfly && !unit_manager.unit_type[type_id].Upright && unit_manager.unit_type[type_id].TEDclass!=CLASS_SHIP && unit_manager.unit_type[type_id].TEDclass!=CLASS_WATER) {
					VECTOR I,J,K,A,B,C;
					MATRIX_4x4 M=RotateY((Angle.y+90.0f)*DEG2RAD);
					I.x=I.y=I.z=0.0f;
					J=K=I;
					I.x=1.0f;
					J.z=1.0f;
					K.y=1.0f;
					A=Pos-unit_manager.unit_type[type_id].FootprintZ*4.0f*I*M;
					B=Pos+(unit_manager.unit_type[type_id].FootprintX*4.0f*I-unit_manager.unit_type[type_id].FootprintZ*4.0f*J)*M;
					C=Pos+(unit_manager.unit_type[type_id].FootprintX*4.0f*I+unit_manager.unit_type[type_id].FootprintZ*4.0f*J)*M;
					A.y=map->get_unit_h(A.x,A.z);	// Projete le triangle
					B.y=map->get_unit_h(B.x,B.z);
					C.y=map->get_unit_h(C.x,C.z);
					VECTOR D=(B-A)*(B-C);
					if(D.y>=0.0f) {					// On ne met pas une unité à l'envers!!
						D.Unit();
						float angle_1=(D.z!=0.0f || D.y!=0.0f) ? acos(D.y/sqrt(D.y*D.y+D.z*D.z))*RAD2DEG : 0.0f;
						if(D.z<0.0f)	angle_1=-angle_1;
						D=D*RotateX(-angle_1*DEG2RAD);
						float angle_2=VAngle(D,K)*RAD2DEG;
						if(D.x>0.0f)	angle_2=-angle_2;
						Angle.x=angle_1;
						Angle.z=angle_2;
						}
					}
				break;
			case MISSION_BUILD:
			case MISSION_BUILD_2:
			case MISSION_REPAIR:
				break;
			case MISSION_STOP:
				break;
			case MISSION_ATTACK:
			case MISSION_AIM:
			case MISSION_SHOOT:
				break;
			default:								// Si l'unité peut attaquer d'elle même les unités enemies proches, elle le fait
				if(unit_manager.unit_type[type_id].AutoFire && unit_manager.unit_type[type_id].canattack) {
					int dx=unit_manager.unit_type[type_id].SightDistance>>3;
					int px=((int)(Pos.x+map->map_w*0.5f))>>3;
					int py=((int)(Pos.z+map->map_h*0.5f))>>3;
					int enemy_idx=-1;
					for(int y=py-dx;y<=py+dx;y++) {
						if(y>=0 && y<(map->bloc_h<<1)-1)
							for(int x=px-dx;x<=px+dx;x++)
								if(x>=0 && x<(map->bloc_w<<1)-1 && map->map_data[y][x].unit_idx!=-1 && map->map_data[y][x].unit_idx!=idx && units.unit[map->map_data[y][x].unit_idx].owner_id!=owner_id) {
									int cur_idx=map->map_data[y][x].unit_idx;
									if(unit_manager.unit_type[units.unit[cur_idx].type_id].ShootMe) {
										enemy_idx=map->map_data[y][x].unit_idx;
										break;
										}
									}
						if(enemy_idx>=0)	break;
						}
					if(enemy_idx>=0)			// Si on a trouvé une unité, on l'attaque
						set_mission(MISSION_ATTACK,&(units.unit[enemy_idx].Pos),false,0,true,&(units.unit[enemy_idx]));
					}
				break;
			};
			}

		if(port[BUILD_PERCENT_LEFT]==0.0f) {
			Angle=Angle+dt*V_Angle;
			V.y-=dt*9.81;			// L'unité subit la force de gravitation
			Pos=Pos+dt*V;			// Déplace l'unité
			}
		script_exec:
		if(map) {
			float min_h=map->get_unit_h(Pos.x,Pos.z);
			if(!unit_manager.unit_type[type_id].norestrict && Pos.y<map->sealvl-unit_manager.unit_type[type_id].MaxWaterDepth*H_DIV) {
				Pos.y=map->sealvl-unit_manager.unit_type[type_id].MaxWaterDepth*H_DIV;
				if(V.y<0.0f)
					V.y=0.0f;
				}
			if((unit_manager.unit_type[type_id].Floater || (unit_manager.unit_type[type_id].TEDclass&CLASS_SHIP))
				 && Pos.y<map->sealvl+(unit_manager.unit_type[type_id].AltFromSeaLevel)*H_DIV) {
				Pos.y=map->sealvl+unit_manager.unit_type[type_id].AltFromSeaLevel*H_DIV;
				if(V.y<0.0f)
					V.y=0.0f;
				}
			else if((unit_manager.unit_type[type_id].TEDclass&CLASS_WATER)
				 && Pos.y<map->sealvl+(unit_manager.unit_type[type_id].AltFromSeaLevel-unit_manager.unit_type[type_id].MinWaterDepth)*H_DIV) {
				Pos.y=map->sealvl+(unit_manager.unit_type[type_id].AltFromSeaLevel-unit_manager.unit_type[type_id].MinWaterDepth)*H_DIV;
				if(V.y<0.0f)
					V.y=0.0f;
				}
			if(min_h>Pos.y) {
				Pos.y=min_h;
				if(V.y<0.0f)
					V.y=0.0f;
				}
			if(unit_manager.unit_type[type_id].canfly && port[BUILD_PERCENT_LEFT]==0.0f) {
				if(mission && (mission->mission==MISSION_MOVE || mission->mission==MISSION_BUILD || mission->mission==MISSION_BUILD_2 || mission->mission==MISSION_REPAIR)) {
					float ideal_h=max(min_h,map->sealvl)+unit_manager.unit_type[type_id].CruiseAlt*H_DIV;
					V.y=(ideal_h-Pos.y)*0.3f;
					}
				else {
					float ideal_h=max(min_h,map->sealvl);
					V.y=(ideal_h-Pos.y)*0.1f;
					}
				}
			port[GROUND_HEIGHT]=Pos.y-min_h;
			}
		port[HEALTH]=hp*100/unit_manager.unit_type[type_id].MaxDamage;
		if(nb_running>0) {
			for(int i=0;i<nb_running;i++)
				run_script(dt,i,map);
			int e=0;
			for(int i=0;i+e<nb_running;) {				// Efface les scripts qui se sont arrêtés
				if(script_env[i+e].running) {
					script_env[i]=script_env[i+e];
					i++;
					}
				else {
					script_env[i+e].destroy();
					e++;
					}
				}
			nb_running-=e;
			}
		map->rect((((int)(Pos.x+map->map_w*0.5f))>>3)-(unit_manager.unit_type[type_id].FootprintX>>1),(((int)(Pos.z+map->map_h*0.5f))>>3)-(unit_manager.unit_type[type_id].FootprintZ>>1),unit_manager.unit_type[type_id].FootprintX,unit_manager.unit_type[type_id].FootprintZ,idx,unit_manager.unit_type[type_id].yardmap,port[YARD_OPEN]!=0.0f);
		return 0;
	}

	bool UNIT::hit(VECTOR P,VECTOR Dir,VECTOR *hit_vec)
	{
		if(flags==0)	return false;
		if(model) {
			VECTOR c_dir=model->center+Pos-P;
			if((c_dir*Dir).Sq()<(model->size*0.5f)) {
				float scale=unit_manager.unit_type[type_id].Scale;
//				MATRIX_4x4 M=RotateZ(Angle.z*DEG2RAD)*RotateY(Angle.y*DEG2RAD)*RotateX(Angle.x*DEG2RAD)*Scale(scale);
				MATRIX_4x4 M=RotateY(Angle.y*DEG2RAD)*RotateZ(Angle.z*DEG2RAD)*RotateX(Angle.x*DEG2RAD)*Scale(scale);
				VECTOR RP=(P-Pos);
				bool is_hit=model->hit(RP,Dir,&data,hit_vec,M);
				if(is_hit)
					*hit_vec=(*hit_vec)+Pos;
				return is_hit;
				}
			}
		return false;
	}

	void UNIT::show_orders()				// Dessine les ordres reçus
	{
		MISSION *cur=mission;
		glEnable(GL_BLEND);
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		VECTOR p_target=Pos;
		float rab=(Atimer%1000000)*0.000001f;
		while(cur) {
			if(cur->step)				// S'il s'agit d'une étape on ne la montre pas
				cur=cur->next;
			{
			int curseur=anim_cursor(CURSOR_CROSS_LINK);
			float dx=0.5f*cursor.anm[CURSOR_CROSS_LINK].ofs_x[curseur];
			float dz=0.5f*cursor.anm[CURSOR_CROSS_LINK].ofs_y[curseur];
			float sx=0.5f*(cursor.anm[CURSOR_CROSS_LINK].bmp[curseur]->w-1);
			float sy=0.5f*(cursor.anm[CURSOR_CROSS_LINK].bmp[curseur]->h-1);
			float x,y,z;
			float dist=(cur->target-p_target).Norm();
			int rec=(int)(dist/30.0f);
			switch(cur->mission)
			{
			case MISSION_MOVE:
			case MISSION_BUILD:
			case MISSION_BUILD_2:
			case MISSION_REPAIR:
			case MISSION_ATTACK:
			case MISSION_AIM:
			case MISSION_SHOOT:
				if(rec>0) {
					glBindTexture(GL_TEXTURE_2D,cursor.anm[CURSOR_CROSS_LINK].glbmp[curseur]);
					glBegin(GL_QUADS);
						for(int i=0;i<rec;i++) {
							x=p_target.x+(cur->target.x-p_target.x)*(i+rab)/rec;
							y=p_target.y+(cur->target.y-p_target.y)*(i+rab)/rec;
							z=p_target.z+(cur->target.z-p_target.z)*(i+rab)/rec;
							x-=dx;
							y+=0.75f;
							z-=dz;
							glTexCoord2f(0.0f,0.0f);		glVertex3f(x,y,z);
							glTexCoord2f(1.0f,0.0f);		glVertex3f(x+sx,y,z);
							glTexCoord2f(1.0f,1.0f);		glVertex3f(x+sx,y,z+sy);
							glTexCoord2f(0.0f,1.0f);		glVertex3f(x,y,z+sy);
							}
					glEnd();
					}
				p_target=cur->target;
			};
			}
			switch(cur->mission)
			{
			case MISSION_MOVE:
				{
				int curseur=anim_cursor(CURSOR_MOVE);
				float x=cur->target.x-0.5f*cursor.anm[CURSOR_MOVE].ofs_x[curseur];
				float y=cur->target.y+1.0f;
				float z=cur->target.z-0.5f*cursor.anm[CURSOR_MOVE].ofs_y[curseur];
				float sx=0.5f*(cursor.anm[CURSOR_MOVE].bmp[curseur]->w-1);
				float sy=0.5f*(cursor.anm[CURSOR_MOVE].bmp[curseur]->h-1);
				glBindTexture(GL_TEXTURE_2D,cursor.anm[CURSOR_MOVE].glbmp[curseur]);
				glBegin(GL_QUADS);
					glTexCoord2f(0.0f,0.0f);		glVertex3f(x,y,z);
					glTexCoord2f(1.0f,0.0f);		glVertex3f(x+sx,y,z);
					glTexCoord2f(1.0f,1.0f);		glVertex3f(x+sx,y,z+sy);
					glTexCoord2f(0.0f,1.0f);		glVertex3f(x,y,z+sy);
				glEnd();
				}
				break;
			case MISSION_BUILD:
				if(cur->p!=NULL)
					cur->target=((UNIT*)(cur->p))->Pos;
				if(cur->data>=0 && cur->data<unit_manager.nb_unit)
					if(unit_manager.unit_type[cur->data].model!=NULL) {
						glEnable(GL_LIGHTING);
						glEnable(GL_CULL_FACE);
						glPushMatrix();
						glTranslatef(cur->target.x,cur->target.y,cur->target.z);
						glColor4f(0.0f,0.0f,1.0f,0.5f);
						unit_manager.unit_type[cur->data].model->obj.draw(NULL,false,false,false);
						glColor4f(1.0f,1.0f,1.0f,1.0f);
						glPopMatrix();
						glDisable(GL_LIGHTING);
						glDisable(GL_CULL_FACE);
						}
				break;
			case MISSION_BUILD_2:
			case MISSION_REPAIR:
				{
				if(cur->p!=NULL)
					cur->target=((UNIT*)(cur->p))->Pos;
				int curseur=anim_cursor(CURSOR_REPAIR);
				float x=cur->target.x-0.5f*cursor.anm[CURSOR_REPAIR].ofs_x[curseur];
				float y=cur->target.y+1.0f;
				float z=cur->target.z-0.5f*cursor.anm[CURSOR_REPAIR].ofs_y[curseur];
				float sx=0.5f*(cursor.anm[CURSOR_REPAIR].bmp[curseur]->w-1);
				float sy=0.5f*(cursor.anm[CURSOR_REPAIR].bmp[curseur]->h-1);
				glBindTexture(GL_TEXTURE_2D,cursor.anm[CURSOR_REPAIR].glbmp[curseur]);
				glBegin(GL_QUADS);
					glTexCoord2f(0.0f,0.0f);		glVertex3f(x,y,z);
					glTexCoord2f(1.0f,0.0f);		glVertex3f(x+sx,y,z);
					glTexCoord2f(1.0f,1.0f);		glVertex3f(x+sx,y,z+sy);
					glTexCoord2f(0.0f,1.0f);		glVertex3f(x,y,z+sy);
				glEnd();
				}
				break;
			case MISSION_ATTACK:
			case MISSION_AIM:
			case MISSION_SHOOT:
				{
				if(cur->p!=NULL)
					cur->target=((UNIT*)(cur->p))->Pos;
				int curseur=anim_cursor(CURSOR_ATTACK);
				float x=cur->target.x-0.5f*cursor.anm[CURSOR_ATTACK].ofs_x[curseur];
				float y=cur->target.y+1.0f;
				float z=cur->target.z-0.5f*cursor.anm[CURSOR_ATTACK].ofs_y[curseur];
				float sx=0.5f*(cursor.anm[CURSOR_ATTACK].bmp[curseur]->w-1);
				float sy=0.5f*(cursor.anm[CURSOR_ATTACK].bmp[curseur]->h-1);
				glBindTexture(GL_TEXTURE_2D,cursor.anm[CURSOR_ATTACK].glbmp[curseur]);
				glBegin(GL_QUADS);
					glTexCoord2f(0.0f,0.0f);		glVertex3f(x,y,z);
					glTexCoord2f(1.0f,0.0f);		glVertex3f(x+sx,y,z);
					glTexCoord2f(1.0f,1.0f);		glVertex3f(x+sx,y,z+sy);
					glTexCoord2f(0.0f,1.0f);		glVertex3f(x,y,z+sy);
				glEnd();
				}
				break;
			};
			cur=cur->next;
			}
		glDisable(GL_BLEND);
	}

	bool INGAME_UNITS::select(CAMERA *cam,int sel_x[],int sel_y[])
	{
		bool selected=false;
		cam->SetView();
		MATRIX_4x4 ModelView,Project,T;
		int	viewportCoords[4] = {0, 0, 0, 0};
		glGetIntegerv(GL_VIEWPORT, viewportCoords);
		glGetFloatv(GL_MODELVIEW_MATRIX, (float*)ModelView.E);
		glGetFloatv(GL_PROJECTION_MATRIX, (float*)Project.E);
		ModelView=Transpose(ModelView);
		Project=Transpose(Project);
		float VW=(viewportCoords[2]-viewportCoords[0])*0.5f;
		float VH=-(viewportCoords[3]-viewportCoords[1])*0.5f;
		T=ModelView*Project;		// Matrice de transformation

		POINTF UPos,O;
		O.x=O.y=O.z=0.0f;
		int X1,Y1,X2,Y2;
		X1=min(sel_x[0],sel_x[1]);
		Y1=min(sel_y[0],sel_y[1]);
		X2=max(sel_x[0],sel_x[1]);
		Y2=max(sel_y[0],sel_y[1]);

		for(int i=0;i<=last_index;i++)
			if(unit[i].flags!=0 && unit[i].owner_id==0 && unit[i].port[BUILD_PERCENT_LEFT]==0.0f) {		// Ne sélectionne que les unités achevées
				if(key[KEY_LSHIFT] && unit[i].sel) {
					selected=true;
					continue;
					}
				if(!key[KEY_LSHIFT])
					unit[i].sel=false;

				VECTOR Vec=unit[i].Pos-cam->Pos;
				float d=Vec.Sq();
				if(d>16384.0f && (Vec%cam->Dir)<=0.0f) continue;

				UPos=glNMult((O+unit[i].Pos),T);		// Transforme la position de l'unité
				UPos.x=UPos.x*VW+VW;
				UPos.y=UPos.y*VH-VH;

				if(X1<=UPos.x && X2>=UPos.x && Y1<=UPos.y && Y2>=UPos.y)
					selected=unit[i].sel=true;
				}
		return selected;
	}

	int INGAME_UNITS::pick(CAMERA *cam,int sensibility)
	{
		int index=-1;

		if(nb_unit<=0)
			return index;

		VECTOR Dir;
		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;
		Dir.Unit();		// Direction pointée par le curseur

		bool detectable=false;
		int i;

		for(i=0;i<=last_index;i++) {
			if(unit[i].flags==0) continue;		// Si l'unité n'existe pas on la zappe
			unit[i].flags&=0xFD;	// Enlève l'indicateur de possibilité d'intersection
			VECTOR center=unit[i].model->center+unit[i].Pos-cam->Pos;
			float size=unit[i].model->size*unit_manager.unit_type[unit[i].type_id].Scale*unit_manager.unit_type[unit[i].type_id].Scale;
			center=Dir*center;
			float dist=center.Sq();
			if(dist<size) {
				detectable=true;
				unit[i].flags|=0x2;		// Unité détectable
				}
			}

		if(!detectable)			// Si aucune unité n'est à proximité du curseur, on zappe la méthode OpenGl
			return index;

		glDisable(GL_LIGHTING);
		glDisable(GL_FOG);

		int BUFSIZE=max_unit<<2;

		GLuint selectBuf[BUFSIZE];
		GLint hits;

		glSelectBuffer (BUFSIZE, selectBuf);
		glRenderMode (GL_SELECT);

		glInitNames();
		glPushName(0);

		int	viewport[4] = {0, 0, 0, 0};
		glGetIntegerv(GL_VIEWPORT, viewport);

		if(cam->Ref!=NULL)
			cam->Pos=cam->RPos+(*(cam->Ref));
		else
			cam->Pos=cam->RPos;

		VECTOR	FP;
		FP=cam->Pos+cam->Dir;

		cam->zfar2=cam->zfar*cam->zfar;
		glMatrixMode (GL_PROJECTION);			// Matrice de projection
		glLoadIdentity ();

		gluPickMatrix(mouse_x, SCREEN_H-mouse_y, sensibility, sensibility, viewport);

		glFrustum (-1.0f*cam->znear, 1.0f*cam->znear, -0.75f*cam->znear, 0.75f*cam->znear, cam->znear, cam->zfar);

		glMatrixMode (GL_MODELVIEW);

		glLoadIdentity();			// Charge la matrice identité

		gluLookAt(cam->Pos.x,cam->Pos.y,cam->Pos.z,FP.x,FP.y,FP.z,cam->Up.x,cam->Up.y,cam->Up.z);

		for(i=0;i<=last_index;i++)
			if((unit[i].flags&0x2)==0x2) {			// Si l'unité existe et est sélectionnable
				unit[i].flags&=0xFD;
				if(unit[i].model) {
					glLoadName(i);
					VECTOR D;
					D=unit[i].Pos-cam->Pos;		// Vecteur "viseur unité" partant de la caméra vers l'unité

					float dist=D.Sq();
					if(!((dist>=16384.0f && (D%cam->Dir)<=0.0f)||(D%cam->Dir)>cam->zfar2)) {
						glPushMatrix();

						glTranslatef(unit[i].Pos.x,unit[i].Pos.y,unit[i].Pos.z);
						glRotatef(unit[i].Angle.x,1.0f,0.0f,0.0f);
						glRotatef(unit[i].Angle.y,0.0f,1.0f,0.0f);
						glRotatef(unit[i].Angle.z,0.0f,0.0f,1.0f);
						float scale=unit_manager.unit_type[unit[i].type_id].Scale;
						glScalef(scale,scale,scale);
						unit[i].model->draw(&(unit[i].data),false,true);

						glPopMatrix();
						}
					}
				}

		glFlush ();

		hits = glRenderMode (GL_RENDER);

		glEnable(GL_LIGHTING);
		glEnable(GL_FOG);

		int j;
		GLuint names, *ptr;

		float nearest=2.0f;
		ptr = (GLuint *) selectBuf;
		for (i = 0; i < hits; i++) { /*  for each hit  */
			names = *ptr;
			ptr++;
			float z1=(float) *ptr/0x7fffffff; ptr++;
			float z2=(float) *ptr/0x7fffffff; ptr++;
			int v=*ptr; ptr++;

			if(z1<nearest) {
				nearest=z1;
				index=v;
				}
			}

		return index;
	}

	void UNIT::shoot(int target,VECTOR startpos,VECTOR Dir,int w_id)
	{
		POINTF O;
		O.x=O.y=O.z=0.0f;
		if(unit_manager.unit_type[type_id].weapon[w_id]->startsmoke)
			particle_engine.make_smoke(O+startpos,0,1,0.0f);
		int w_idx=weapons.add_weapon(unit_manager.unit_type[type_id].weapon[w_id]->nb_id,idx);
		weapons.weapon[w_idx].Pos=startpos;
		if(unit_manager.unit_type[type_id].weapon[w_id]->startvelocity==0.0f)
			weapons.weapon[w_idx].V=0.5f*unit_manager.unit_type[type_id].weapon[w_id]->weaponvelocity*Dir;
		else
			weapons.weapon[w_idx].V=0.5f*unit_manager.unit_type[type_id].weapon[w_id]->startvelocity*Dir;
		if(unit_manager.unit_type[type_id].weapon[w_id]->dropped)
			weapons.weapon[w_idx].V=weapons.weapon[w_idx].V+V;
		weapons.weapon[w_idx].target=target;
		weapons.weapon[w_idx].target_pos=units.unit[target].Pos;
		weapons.weapon[w_idx].stime=0.0f;
	}

void *create_unit(int type_id,int owner,VECTOR pos)
{
	int id=units.create(type_id,owner);
	units.unit[id].Pos=pos;
	units.unit[id].port[BUILD_PERCENT_LEFT]=100.0f;
	return &(units.unit[id]);
}
