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

#ifdef CWDEBUG
#include <libcwd/sys.h>
#include <libcwd/debug.h>
#endif
#include "stdafx.h"
#include "TA3D_NameSpace.h"
#include "matrix.h"

#include "intro.h"			// Introduction
#include "cTA3D_Engine.h"	// The Engine
#include "ta3d.h"			// Some core include
#include "menu.h"			// Game menus

#ifndef SCROLL_SPEED
	#define SCROLL_SPEED		400.0f
#endif

VECTOR cursor_on_map( CAMERA *cam,MAP *map, bool on_mini_map = false );

/*--------------------------------------------------------------------\
|                              Game Engine                            |
\--------------------------------------------------------------------*/
void play(GAME_DATA *game_data)
{
if(game_data==NULL)	{
	Console->AddEntry("error: cannot start a game!! (game_data is NULL, no game information)");
	return;
	}

gfx->SCREEN_W_TO_640 = 1.0f;				// To have mouse sensibility undependent from the resolution
gfx->SCREEN_H_TO_480 = 1.0f;
#ifdef CWDEBUG
  Debug( libcw_do.on() );
  Debug( dc::malloc.on() );
  Debug( dc::notice.on() );
#endif
expected_players=game_data->nb_players;

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

Console->AddEntry("Loading textures");
loading(0.0f,TRANSLATE("Loading textures"));

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

texture_manager.all_texture();

Console->AddEntry("Loading 3D Models");
loading(100.0f/7.0f,TRANSLATE("Loading 3D Models"));
/*-----------------------charge les modèles 3D-----------------------*/

model_manager.init();
model_manager.load_all(loading);
model_manager.optimise_all();

Console->AddEntry("Loading graphical features");
loading(200.0f/7.0f,TRANSLATE("Loading graphical features"));
/*-----------------------charge les éléments graphiques--------------*/

load_features();
feature_manager.clean();

model_manager.compute_ids();

Console->AddEntry("Loading weapons");
loading(250.0f/7.0f,TRANSLATE("Loading weapons"));
/*-----------------------charge les armes----------------------------*/

load_weapons();

weapons.init();

Console->AddEntry("Loading units");
loading(300.0f/7.0f,TRANSLATE("Loading units"));
/*-----------------------charge les unités---------------------------*/

load_all_units();

Console->AddEntry("Freeing unused memory");
loading(400.0f/7.0f,TRANSLATE("Free unused memory"));
/*-----------------------libère la mémoire inutilisée----------------*/

texture_manager.destroy();

Console->AddEntry("Initialising engine");
loading(500.0f/7.0f,TRANSLATE("Initialising engine"));
/*-----------------------initialise le moteur------------------------*/

gfx->SetDefState();
particle_engine.init();

set_palette(pal);

Console->AddEntry("Adding players");
players.init();													// Object containing data about players
for(uint16 i = 0; i<game_data->nb_players; i++)
	players.add((char*)game_data->player_names[i].c_str(),(char*)game_data->player_sides[i].c_str(),game_data->player_control[i],game_data->energy[i],game_data->metal[i],game_data->ai_level[i]);		// add a player

if( players.local_human_id >= 0 ) {
	String prefix = "";
	String intgaf = "";

	for( int i = 0 ; i < ta3d_sidedata.nb_side ; i++ )
		if( ta3d_sidedata.side_name[ i ] == game_data->player_sides[ players.local_human_id ] ) {
			prefix = ta3d_sidedata.side_pref[ i ];
			intgaf = ta3d_sidedata.side_int[ i ];
			break;
			}

	if( prefix != "" && intgaf != "" )
		unit_manager.load_panel_texture( prefix, intgaf );
	}

units.init( true );

Console->AddEntry("Loading GUI");
loading(550.0f/7.0f,TRANSLATE("Loading GUI"));
//-------------------	Code related to the GUI		--------------------------

AREA	game_area;			// Area object that will handle game GUI

game_area.load_tdf( "gui/game.area" );					// Loads the game area

game_area.load_window( ta3d_sidedata.guis_dir + ta3d_sidedata.side_pref[ players.side_view ] + "gen.gui" );			// Load the order interface
I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)(String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen.hide").c_str(), NULL, NULL );	// Hide it

game_area.load_window( ta3d_sidedata.guis_dir + ta3d_sidedata.side_pref[ players.side_view ] + "dl.gui" );			// Load the default build interface
I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)(String( ta3d_sidedata.side_pref[ players.side_view ] ) + "dl.hide").c_str(), NULL, NULL );	// Hide it

for( int i = 0 ; i < unit_manager.nb_unit ; i++ ) {		// Loads the GUI
	if( !(i & 0xF) )
		loading( (550.0f + 50.0f * i / (unit_manager.nb_unit+1) )/7.0f,TRANSLATE("Loading GUI"));
	if( Lowercase( unit_manager.unit_type[ i ].side ) == Lowercase( ta3d_sidedata.side_name[ players.side_view ] ) ) {
		int e = 1;
		while( HPIManager->Exists( ta3d_sidedata.guis_dir + unit_manager.unit_type[ i ].Unitname + format( "%d.gui", e ) ) ) {
			game_area.load_window( ta3d_sidedata.guis_dir + unit_manager.unit_type[ i ].Unitname + format( "%d.gui", e ) );			// Load the build interface
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( unit_manager.unit_type[ i ].Unitname + format( "%d.hide", e) ).c_str(), NULL, NULL );	// Hide it
			e++;
			}
		}
	}

String current_gui = "";

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

Console->AddEntry("Loading the map");
loading(600.0f/7.0f,TRANSLATE("Loading the map"));
/*-----------------------charge la carte-----------------------------*/

Console->AddEntry("Extracting '%s'", game_data->map_filename);
byte *map_file=HPIManager->PullFromHPI(game_data->map_filename);

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

Console->AddEntry("Loading details texture");
map->load_details_texture( "gfx/details.jpg" );			// Load the details texture

Console->AddEntry("Initialising Fog Of War");
map->clear_FOW( game_data->fog_of_war );

units.map = map;			// Setup some useful information

replace_extension(game_data->map_filename, game_data->map_filename, "ota", strlen(game_data->map_filename)+1);

Console->AddEntry("Extracting '%s'", game_data->map_filename);
uint32 ota_size=0;
map_file=HPIManager->PullFromHPI(game_data->map_filename,&ota_size);
if(map_file) {
	Console->AddEntry("Loading map informations");
	map->ota_data.load((char*)map_file,ota_size);

	if( map->ota_data.lavaworld ) {			// make sure we'll draw lava and not water
		gfx->destroy_texture( map->lava_map );

		BITMAP *tmp = create_bitmap_ex(32, 16, 16 );
		clear_to_color( tmp, 0xFFFFFFFF );
		map->lava_map = gfx->make_texture( tmp );
		destroy_bitmap( tmp );
		}

	free(map_file);
	}

game_data->map_filename[ strlen( game_data->map_filename ) - 4 ] = 0;		// Remove the ".ota" extension

SKY_DATA	*sky_data = choose_a_sky( game_data->map_filename, map->ota_data.planet );

if( sky_data == NULL ) {
	sky_data = new SKY_DATA;
	sky_data->texture_name = "gfx/sky/sky.jpg";
	}

bool spherical_sky = sky_data->spherical;

if(map->ota_data.maxwindspeed!=map->ota_data.minwindspeed)
	map->wind=(TA3D_RAND()%(map->ota_data.maxwindspeed-map->ota_data.minwindspeed))+map->ota_data.minwindspeed;

uint32	script_timer = 0;

GLuint	sky;
sky = gfx->load_texture( sky_data->texture_name, FILTER_TRILINEAR, NULL, NULL, false);
GLuint	glow = gfx->load_texture("gfx/glow.tga");
GLuint	freecam_on = gfx->load_texture("gfx/freecam_on.tga");
GLuint	freecam_off = gfx->load_texture("gfx/freecam_off.tga");
GLuint	arrow_texture = gfx->load_texture("gfx/arrow.tga");
GLuint	circle_texture = gfx->load_texture("gfx/circle.tga");
GLuint	water = 0;

loading(100.0f,TRANSLATE("Load finished"));

start_time = msec_timer-start_time;
printf("loading time: %f sec.\n",(float)start_time * 0.001f);

float Conv=0.001f;
float dt=0.0f;
float t=0.0f;
int count=msec_timer;
int nbfps=0;
int fpscount=msec_timer;
int fps=0;
int i;

bool	reflection_drawn_last_time = false;

float camera_zscroll =  - 0.00001f;		// The position of the camera on the virtual "rail"
float r1,r2,r3;
r1=r2=r3=0.0f;
r1 = -lp_CONFIG->camera_def_angle - 0.00001f;
CAMERA cam;
VECTOR cam_target;
int cam_target_mx = gfx->SCREEN_W_HALF;
int cam_target_my = gfx->SCREEN_H_HALF;
bool cam_has_target=false;
bool freecam=false;
int cam_def_timer = msec_timer;		// Just to see if the cam has been long enough at the default angle
int track_mode = -1;			// Tracking a unit ? negative value => no
bool last_time_activated_track_mode = false;
game_cam=&cam;

cam.RPos.x = cam.RPos.y = cam.RPos.z = 0.0f;
cam.RPos.z += 150.0f;
cam.RPos.y = lp_CONFIG->camera_def_h;
cam.zfar=500.0f;
cam.set_width_factor( gfx->width, gfx->height );

float FogD=0.3f;
float FogNear=cam.zfar*0.5f;
float FogColor[] = {0.8f,0.8f,0.8f,1.0f};

memcpy( FogColor, sky_data->FogColor, sizeof( float ) * 4 );

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

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

float speed_limit= lp_CONFIG->fps_limit;
float delay=(speed_limit==0.0f) ? 0.0f : 1.0f/speed_limit;
int nb_shoot=0;
bool shoot=false;

bool ordered_destruct = false;

bool tilde=false;
bool done=false;

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

int	exit_mode=EXIT_NONE;

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

fx_manager.load_data();

int mx,my,omb=mouse_b,omb2=mouse_b,omb3=mouse_b, amx=mouse_x, amy=mouse_y;
int	sel_x[2],sel_y[2],selecting=false;
int cur_sel = -1;
int old_gui_sel = -1;
bool old_sel = false;
bool selected = false;
int	build=-1;				// Indique si l'utilisateur veut construire quelque chose
bool build_order_given = false;
int cur_sel_index=-1;
int omz=mouse_z;
float cam_h = lp_CONFIG->camera_def_h;

bool show_script=false;			// Affichage des scripts
bool show_model=false;			// Affichage les noms des sous objets du modèle 3D de l'unité sélectionnée
bool rotate_light=false;
float light_angle=0.0f;
bool cheat_metal=false;
bool cheat_energy=false;
bool internal_name=false;
bool internal_idx=false;
bool ia_debug=false;
bool view_dbg=false;
bool show_mission_info=false;
bool speed_changed = false;
float show_timefactor = 0.0f;

float show_gamestatus = 0.0f;

float unit_info=0.0f;
int	unit_info_id=-1;

rest(100);						// Juste pour éviter un "Xlib: ..." / To avoid an "Xlib: ..." error

float wind_t=t;					// To handle wind variations
bool wind_change=false;

int video_timer=msec_timer;			// To handle video
bool video_shoot=false;

int current_order=SIGNAL_ORDER_NONE;

WATER	water_obj;
water_obj.build(map->map_w,map->map_h,1000);

SKY		sky_obj;
sky_obj.build(10,400,sky_data->full_sphere);
float	sky_angle = sky_data->rotation_offset;

SHADER	water_shader, water_shader_reflec, water_pass1, water_pass1_low, water_pass2;
GLuint transtex,reflectex,first_pass,second_pass,water_color;
GLuint water_FBO;

if(g_useProgram && g_useFBO && map->water) {
	glGenFramebuffersEXT(1,&water_FBO);

	water_pass1.load("shaders/water_pass1.frag","shaders/water_pass1.vert");
	water_pass1_low.load("shaders/water_pass1_low.frag","shaders/water_pass1.vert");
	water_pass2.load("shaders/water_pass2.frag","shaders/water_pass2.vert");
	water_shader.load("shaders/water.frag","shaders/water.vert");
	water_shader_reflec.load("shaders/water_reflec.frag","shaders/water.vert");

	allegro_gl_use_alpha_channel(true);

	allegro_gl_set_texture_format(GL_RGBA8);
	BITMAP *tmp=create_bitmap_ex(32,512,512);	// On ne peut pas utiliser screen donc on crée un BITMAP temporaire
	transtex = gfx->make_texture( tmp, FILTER_LINEAR );		// pour les effets de transparence de l'eau
	reflectex = gfx->make_texture( tmp, FILTER_LINEAR );	// pour les effets de reflection sur l'eau
	first_pass = gfx->make_texture( tmp, FILTER_LINEAR );	// pour les effets de reflection/transparence sur l'eau
	second_pass = gfx->make_texture( tmp, FILTER_LINEAR );	// pour les effets de reflection/transparence sur l'eau
	water_color = gfx->make_texture( tmp, FILTER_LINEAR );	// pour les effets de reflection/transparence sur l'eau

	for( int z = 0 ; z < 512 ; z++ )						// The wave base model
		for( int x = 0 ; x < 512 ; x++ ) {
			// equation : y = ( 1 - sqrt( 1 - (x*z)^2 ) ) * z / 3 + (1-z) * sin( x * PI / 2 ) ^ 2 where z is a parameter
			// Stores the gradient vector clamped into 0.0 - 1.0 ( 0 - 0xFF )
			float X = (x - 256.0f) / 256.0f;
			float Z = z / 512.0f;
			float DX = -X * Z * Z / ( 3.0f * sqrt( 1.0f - X * Z * X * Z ) ) + ( 1.0f - Z ) * PI * sin( X * PI / 2.0f ) * cos( X * PI / 2.0f );
			float L = sqrt( DX * DX + 1.0f );
			DX = DX / L * 127.0f + 127.0f;
			if( DX < 0.0f )	DX = 0.0f;
			else if( DX > 255.0f )	DX = 255.0f;
			tmp->line[z][(x<<2)] = (int)(DX);
			tmp->line[z][(x<<2)+1] = (int)((127.0f / L) + 127.0f);
			tmp->line[z][(x<<2)+2] = 0;
			tmp->line[z][(x<<2)+3] = 0;
			}

	allegro_gl_set_texture_format(GL_RGB8);

	water = gfx->make_texture( tmp, FILTER_TRILINEAR, false );

	destroy_bitmap(tmp);

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);

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

delay=(speed_limit==0.0f) ? 0.0f : 1.0f/speed_limit;

LUA_PROGRAM	game_script;					// Script that will rule the game
game_script.load(game_data->game_script, map);	// Load the script

//-----------------------   Code related to threads   ------------------------

unit_engine_thread_sync = 0;
weapon_engine_thread_sync = 0;
particle_engine_thread_sync = 0;

sound_manager->PlayMusic();

units.Start();			// Start the Unit Engine

particle_engine.more_memory();				// Pre-allocating memory (no lag with first particles)
particle_engine.set_data( map->ota_data.gravity, map->wind_vec );
particle_engine.Start();		// Start the particle engine

		// Start the weapon engine
weapons.set_data( map );
features.set_data( map->wind_vec );		// NB: the feature engine runs in the weapon thread to avoid having too much thread to synchronise
weapons.Start();

/*---------------------------- players management --------------------------*/

players.set_map( map );
players.Start();

/*------------------------- end of players management ----------------------*/

do
{
	/*------------------------ handle GUI events -------------------------------*/

	bool IsOnGUI = false;
	bool IsOnMinimap = mouse_x < 128 && mouse_y < 128;		// Used to handle on mini map commands
	IsOnGUI = (mouse_x < 128 && ( mouse_y >= SCREEN_H - 64 || mouse_y < 128 ) ) || mouse_y < 32 || mouse_y >= SCREEN_H - 32;		// Priority given to game interface
	if( !IsOnGUI )
		IsOnGUI = game_area.check();

	IsOnGUI |= mouse_x < 128;		// Priority given to game interface

	if( IsOnMinimap ) {				// Check if we can project the cursor position on the map
		if( fabs( (mouse_x - 64) * 252.0f / 128.0f ) > map->mini_w * 0.5f )	IsOnMinimap = false;
		else if( fabs( (mouse_y - 64) * 252.0f / 128.0f ) > map->mini_h * 0.5f )	IsOnMinimap = false;
		}

	if( IsOnMinimap )
		units.pick_minimap();			// Precompute this, we'll need it

	/*------------------------ end of "handle GUI events" ----------------------*/

	bool can_be_there=false;		// To tell if the unit the player wants to build can be built where the cursor is

	if(video_shoot)
		if((msec_timer-video_timer)*Conv>=1.0f/15.0f) {
			video_timer=msec_timer;
			shoot=true;
			}

	/*------------------------ handle wind speed and dir -----------------------*/

	if(t-wind_t>=10.0f) {		// Make a change every 10 sec.
		wind_t=t;
		map->wind+=(rand_from_table()%2001)-1000;
		if(map->wind<map->ota_data.minwindspeed)
			map->wind=map->ota_data.minwindspeed;
		else if(map->wind>map->ota_data.maxwindspeed)
			map->wind=map->ota_data.maxwindspeed;
		map->wind_dir+=(rand_from_table()%901)*0.1f-45.0f;
		if(map->wind_dir<0.0f)	map->wind_dir+=360.0f;
		else if(map->wind_dir>=360.0f)	map->wind_dir-=360.0f;
		map->wind_vec.y=0.0f;
		map->wind_vec.x=0.01f*map->wind*cos(map->wind_dir*DEG2RAD);
		map->wind_vec.z=0.01f*map->wind*sin(map->wind_dir*DEG2RAD);
		units.set_wind_change();
		wind_change=true;
		}
	else
		wind_change=false;

	/*---------------------- end of wind speed and dir code --------------------*/

	/*----------------------------- sound management ---------------------------*/

	if(units.nb_attacked/(units.nb_attacked+units.nb_built+1)>=0.75f)
		sound_manager->SetMusicMode( true );
	else if(units.nb_attacked/(units.nb_attacked+units.nb_built+1)<=0.25f)
		sound_manager->SetMusicMode( false );

	sound_manager->SetListenerPos( &cam.RPos );
	sound_manager->Update3DSound();

	/*-------------------------- end of sound management -----------------------*/

	VECTOR old_cam_pos = cam.RPos;
	float old_r1=r1,old_r2=r2,old_r3=r3;
	float old_zscroll = camera_zscroll;
	if(!freecam) {
		int delta=mouse_z-omz;
		camera_zscroll += delta * 2.0f * lp_CONFIG->camera_zoom_speed;
		if( camera_zscroll < -25.0f ) camera_zscroll = -25.0f;
		else if( camera_zscroll > 20.0f ) camera_zscroll = 20.0f;

		if( (msec_timer - cam_def_timer) * Conv >= 1.0f && delta != 0
		&& ( ( camera_zscroll > 0.0f && old_zscroll <= 0.0f )
		 || ( camera_zscroll < 0.0f && old_zscroll >= 0.0f ) ) )	{		// Just to make it lock near def position
			cam_def_timer = msec_timer;
			old_zscroll = 0.0f;
			if( camera_zscroll > -lp_CONFIG->camera_def_angle )	old_zscroll += 0.00001f;
			else old_zscroll -= 0.00001f;
			}

		if( (msec_timer - cam_def_timer) * Conv < 0.5f )
			camera_zscroll = old_zscroll;

		float angle_factor = max( fabs(-lp_CONFIG->camera_def_angle+45.0f) / 20.0f, fabs(-lp_CONFIG->camera_def_angle+90.0f) / 25.0f );

		r1 = -lp_CONFIG->camera_def_angle + camera_zscroll * angle_factor;
		if( r1 > -45.0f ) 		r1 = -45.0f;
		else if( r1 < -90.0f )	r1 = -90.0f;

		cam_h = lp_CONFIG->camera_def_h + (exp(-camera_zscroll * 0.15f) - 1.0f) / (exp(3.75f) - 1.0f) * max(map->map_w,map->map_h);
		if( delta > 0 && !IsOnGUI ) {
			if( !cam_has_target || abs( mouse_x - cam_target_mx ) > 2 || abs( mouse_y - cam_target_my ) > 2 ) {
				cam_target = cursor_on_map(&cam,map);
				cam_target_mx = mouse_x;
				cam_target_my = mouse_y;
				cam_has_target=true;
				}
			}
		}
	else {
		cam.RPos=cam.RPos-0.5f*(mouse_z-omz)*cam.Dir;
		cam_has_target=false;
		}
	omz=mouse_z;

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

	if( !IsOnGUI || IsOnMinimap) {
		if(selected)
			for(i=0;i<units.index_list_size;i++) {
				uint32 e = units.idx_list[i];
				if( (units.unit[e].flags & 1) && units.unit[e].owner_id==players.local_human_id && units.unit[e].sel && unit_manager.unit_type[units.unit[e].type_id].canmove) {
					cursor_type=CURSOR_MOVE;
					break;
					}
				}
		}

	if(build>=0)
		cursor_type=CURSOR_DEFAULT;

	dt=(msec_timer-count)*Conv;			// Regulate frame rate
	rest( 0 );						// To play nice with other threads
	while(dt<delay) {
		switch(lp_CONFIG->priority_level)
		{
		case 0:			rest(1);	break;
		case 1:			rest(0);	break;
		};
		dt=(msec_timer-count)*Conv;
		}
	count=msec_timer;

	poll_mouse();

	if( spherical_sky )
		sky_angle += sky_data->rotation_speed * dt * units.apparent_timefactor; 

	unit_info-=dt;
	light_angle+=dt*units.apparent_timefactor;

	t+=dt*lp_CONFIG->timefactor;
	nbfps++;
	if(nbfps>=10 && (msec_timer-fpscount)*Conv>=0.05f) {
		fps=(int)(nbfps/((msec_timer-fpscount)*Conv));
		fpscount=msec_timer;
		nbfps=0;
		}

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

	if( players.local_human_id >= 0 && !Console->activated() ) {
		if( key[ KEY_SPACE ] ) {				// Show gamestatus window
			if( show_gamestatus == 0.0f ) {
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)"gamestatus.show", NULL, NULL );	// Show it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)"playerstats.show", NULL, NULL );	// Show it
				}

			show_gamestatus += 10.0f * dt;
			if( show_gamestatus > 1.0f )
				show_gamestatus = 1.0f;
			}
		else {									// Hide gamestatus window
			bool pre_visible = show_gamestatus > 0.0f;

			show_gamestatus -= 10.0f * dt;

			if( show_gamestatus < 0.0f && pre_visible ) {
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)"gamestatus.hide", NULL, NULL );	// Hide it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)"playerstats.hide", NULL, NULL );	// Hide it
				}
			if( show_gamestatus < 0.0f )
				show_gamestatus = 0.0f;
			}

		if( show_gamestatus > 0.0f ) {
			WND *statuswnd = game_area.get_wnd( "gamestatus" );
			if( statuswnd )
				statuswnd->y = (int)(SCREEN_H - (statuswnd->height + 32) * show_gamestatus);
			uint32 game_time = units.current_tick / TICKS_PER_SEC;
			game_area.set_caption( "gamestatus.time_label", TRANSLATE("game time") + format(" : %d:%d:%d", game_time / 3600, (game_time / 60) % 60, game_time % 60 ) );
			game_area.set_caption( "gamestatus.units_label", TRANSLATE("units") + format(" : %d/%d", players.nb_unit[ players.local_human_id ], MAX_UNIT_PER_PLAYER ) );
			game_area.set_caption( "gamestatus.speed_label", TRANSLATE("speed") + format(" : %d (%d)", (int)lp_CONFIG->timefactor,(int)units.apparent_timefactor ) );

			statuswnd = game_area.get_wnd( "playerstats" );
			if( statuswnd )
				statuswnd->x = (int)(SCREEN_W - (statuswnd->width + 10) * show_gamestatus);
			for( int i = 0 ; i < players.nb_player ; i++ ) {
				GUIOBJ *obj = game_area.get_object( format( "playerstats.p%d_box", i ) );
				if( obj )
					obj->Data = gfx->makeintcol( player_color[ 3 * player_color_map[ i ] ], player_color[ 3 * player_color_map[ i ] + 1 ], player_color[ 3 * player_color_map[ i ] + 2 ], 0.5f );
				game_area.set_caption( format( "playerstats.p%d_kills", i ), format( "%d", players.kills[ i ] ) );
				game_area.set_caption( format( "playerstats.p%d_losses", i ), format( "%d", players.losses[ i ] ) );
				}
			}
		}

	if( TA3D_CTRL_PRESSED && key[KEY_D] ) {
		if( !ordered_destruct ) {
			for(uint16 e=0;e<units.index_list_size;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
					units.unit[i].toggle_self_destruct();
				}
			ordered_destruct = true;
			}
		}
	else
		ordered_destruct = false;

	if( key[KEY_PLUS_PAD] && !Console->activated() ) {
		if( !speed_changed && lp_CONFIG->timefactor < 10.0f ) {
			lp_CONFIG->timefactor++;
			show_timefactor = 1.0f;
			}
		speed_changed = true;
		}
	else if( ( key[KEY_MINUS] || key[KEY_MINUS_PAD] ) && !Console->activated() ) {
		if( !speed_changed && lp_CONFIG->timefactor > 1.0f ) {
			lp_CONFIG->timefactor--;
			show_timefactor = 1.0f;
			}
		speed_changed = true;
		}
	else
		speed_changed = false;

	if( track_mode >= 0 && track_mode <= units.max_unit) {
		if( !(units.unit[ track_mode ].flags & 1) ) {		// Leave tracking mode
			track_mode = -1;
			cam_has_target = false;
			}
		else {
			cam_has_target = true;
			cam_target = units.unit[ track_mode ].Pos;
			cam_target_mx = gfx->SCREEN_W_HALF;
			cam_target_my = gfx->SCREEN_H_HALF;
			}
		}
	else
		track_mode = -1;

	if( track_mode >= 0 && ( cur_sel_index < 0 || cur_sel_index >= units.max_unit || track_mode != cur_sel_index ) )
		track_mode = -1;

	if(key[KEY_T] && !Console->activated() && cur_sel_index>=0 && cur_sel_index<units.max_unit ) {
		if( !last_time_activated_track_mode )
			track_mode = track_mode == cur_sel_index ? -1 : cur_sel_index;
		last_time_activated_track_mode = true;
		}
	else if( key[KEY_T] && !Console->activated() ) {
		track_mode = -1;
		cam_has_target = false;
		}
	else
		last_time_activated_track_mode = false;

	if( key[KEY_F1] && units.last_on >= 0 && units.unit[ units.last_on ].type_id >= 0 ) {
		unit_info_id = units.unit[ units.last_on ].type_id;
		unit_info = 1.0f;
		}
	else if(key[KEY_F1] && cur_sel >= 0) {
		unit_info_id = cur_sel;
		unit_info = 1.0f;
		}

	if(mouse_x<128.0f && mouse_y<128.0f && mouse_x>=0.0f && mouse_y>=0.0f && mouse_b == 2 ) {
		cam.RPos.x=(mouse_x-64)*map->map_w/128.0f*252.0f/map->mini_w;
		cam.RPos.z=(mouse_y-64)*map->map_h/128.0f*252.0f/map->mini_h;
		cam_has_target=false;
		}
	if(mouse_x<1) {
		VECTOR move_dir = cam.Side;
		move_dir.y = 0.0f;
		move_dir.Unit();
		cam.RPos = cam.RPos - (SCROLL_SPEED*dt*cam_h / 151.0f)*move_dir;
		cam_has_target=false;
		}
	else if(mouse_x>=SCREEN_W-1) {
		VECTOR move_dir = cam.Side;
		move_dir.y = 0.0f;
		move_dir.Unit();
		cam.RPos = cam.RPos + (SCROLL_SPEED*dt*cam_h / 151.0f)*move_dir;
		cam_has_target=false;
		}
	if(mouse_y<1) {
		VECTOR move_dir = cam.Up;
		if(move_dir.x==0.0f && move_dir.z==0.0f) {
			move_dir = cam.Dir;
			move_dir.y=0.0f;
			}
		else
			move_dir.y=0.0f;
		move_dir.Unit();
		cam.RPos = cam.RPos+ (SCROLL_SPEED*dt*cam_h / 151.0f) * move_dir;
		cam_has_target=false;
		}
	else if(mouse_y>=SCREEN_H-1) {
		VECTOR move_dir = cam.Up;
		if(move_dir.x==0.0f && move_dir.z==0.0f) {
			move_dir = cam.Dir;
			move_dir.y=0.0f;
			}
		else
			move_dir.y=0.0f;
		move_dir.Unit();
		cam.RPos = cam.RPos - (SCROLL_SPEED*dt*cam_h / 151.0f)*move_dir;
		cam_has_target=false;
		}

	if(freecam) {
		if(mouse_b==4) {
			get_mouse_mickeys(&mx,&my);
			if(omb==mouse_b) {
				r2-=mx;
				r1-=my;
				}
			position_mouse(gfx->SCREEN_W_HALF,gfx->SCREEN_H_HALF);
			}
		else if(omb==4)
			position_mouse(gfx->SCREEN_W_HALF,gfx->SCREEN_H_HALF);
		}
	else if(!TA3D_CTRL_PRESSED) {
		if(mouse_b==4) {
			get_mouse_mickeys(&mx,&my);
			if(omb==mouse_b) {
				cam.RPos.x+=mx * cam_h / 151.0f;
				cam.RPos.z+=my * cam_h / 151.0f;
				cam_has_target=false;
				}
			position_mouse(gfx->SCREEN_W_HALF,gfx->SCREEN_H_HALF);
			}
		else if(omb==4) {
			position_mouse(gfx->SCREEN_W_HALF,gfx->SCREEN_H_HALF);
			cam_has_target=false;
			}
		}
	omb=mouse_b;

	if(!freecam)	{
		if(key[KEY_UP]) {
			cam.RPos.z-=SCROLL_SPEED*dt*cam_h / 151.0f;
			cam_has_target=false;
			}
		if(key[KEY_DOWN]) {
			cam.RPos.z+=SCROLL_SPEED*dt*cam_h / 151.0f;
			cam_has_target=false;
			}
		if(key[KEY_RIGHT]) {
			cam.RPos.x+=SCROLL_SPEED*dt*cam_h / 151.0f;
			cam_has_target=false;
			}
		if(key[KEY_LEFT]) {
			cam.RPos.x-=SCROLL_SPEED*dt*cam_h / 151.0f;
			cam_has_target=false;
			}

		float h=map->get_unit_h(cam.RPos.x,cam.RPos.z);
		if(h<map->sealvl)
			h=map->sealvl;
		for( int i = 0 ; i < 20 ; i++ )				// Increase precision
			for( float T = 0.0f ; T < dt ; T += 0.1f )
				cam.RPos.y+=(h+cam_h-cam.RPos.y) * min( dt - T, 0.1f );
		}
	else {
		if(key[KEY_UP])
			cam.RPos=cam.RPos+100.0f*dt*cam_h / 151.0f*cam.Dir;
		if(key[KEY_DOWN])
			cam.RPos=cam.RPos-100.0f*dt*cam_h / 151.0f*cam.Dir;
		if(key[KEY_RIGHT])
			cam.RPos=cam.RPos+100.0f*dt*cam_h / 151.0f*cam.Side;
		if(key[KEY_LEFT])
			cam.RPos=cam.RPos-100.0f*dt*cam_h / 151.0f*cam.Side;
		}

	if(cam.RPos.x<-map->map_w_d) {
		cam.RPos.x=-map->map_w_d;
		cam_has_target=false;
		}
	if(cam.RPos.x>map->map_w_d)	{
		cam.RPos.x=map->map_w_d;
		cam_has_target=false;
		}
	if(cam.RPos.z<-map->map_h_d+200.0f)	{
		cam.RPos.z=-map->map_h_d+200.0f;
		cam_has_target=false;
		}
	if(cam.RPos.z>map->map_h_d && !cam_has_target)
		cam.RPos.z=map->map_h_d;
	if(cam.RPos.z > map->map_h_d+200.0f)
		cam.RPos.z = map->map_h_d+200.0f;

	MATRIX_4x4 Rotation;
	if( lp_CONFIG->camera_zoom == ZOOM_NORMAL )
		Rotation = RotateX( r1 * DEG2RAD ) * RotateY( r2 * DEG2RAD ) * RotateZ( r3 * DEG2RAD );
	else
		Rotation = RotateX( -lp_CONFIG->camera_def_angle * DEG2RAD ) * RotateY( r2 * DEG2RAD ) * RotateZ( r3 * DEG2RAD );

	cam.SetMatrix(Rotation);
	cam.update_shake( dt );				// Update the shake vector

	if(cam_has_target) {

		VECTOR cur_dir;
		cur_dir = cam.Dir+cam.width_factor*2.0f*(cam_target_mx-gfx->SCREEN_W_HALF)*gfx->SCREEN_W_INV*cam.Side-1.5f*(cam_target_my-gfx->SCREEN_H_HALF)*gfx->SCREEN_H_INV*cam.Up;
		cur_dir.Unit();		// Direction pointée par le curseur
		VECTOR moving_target = cam_target - cam.RPos;
		moving_target = moving_target - (moving_target % cur_dir) * cur_dir;
		float d = moving_target.Sq();
		moving_target.y = 0.0f;
		float D = moving_target.Sq();
		if( D == 0.0f ) {
			cam_has_target = false;
			}
		else
			moving_target = d / D * moving_target;
		cam.RPos = moving_target + cam.RPos;
		}

	if(!selected)
		current_order=SIGNAL_ORDER_NONE;

	if(current_order!=SIGNAL_ORDER_NONE)
		selecting=false;

	if(selected && ( !IsOnGUI || IsOnMinimap ) ) {
		bool builders=false;
		bool canattack=false;
		bool canreclamate=false;
		bool canresurrect=false;
		for(uint16 e=0;e<units.index_list_size;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel) {
				builders|=unit_manager.unit_type[units.unit[i].type_id].Builder;
				canattack|=unit_manager.unit_type[units.unit[i].type_id].canattack;
				canreclamate|=unit_manager.unit_type[units.unit[i].type_id].CanReclamate;
				canresurrect|=unit_manager.unit_type[units.unit[i].type_id].canresurrect;
				}
			}
		int pointing = 0;
		if( !IsOnGUI ) {
			pointing = units.pick(&cam);		// Sur quoi le curseur est-il pointé??
			if( pointing == -1 ) {				// Is the cursor on a rock, tree, ...?
				VECTOR cur_pos = cursor_on_map(&cam,map,IsOnMinimap);
				int px=((int)(cur_pos.x+map->map_w_d))>>3;
				int py=((int)(cur_pos.z+map->map_h_d))>>3;
				if(px>=0 && px<map->bloc_w_db && py>=0 && py<map->bloc_h_db && (map->view_map->line[py>>1][px>>1] & (1<<players.local_human_id))  ) {
					int idx = -map->map_data[py][px].unit_idx - 2;				// Basic check
					if( idx<0 || features.feature[idx].type<0) {
						units.last_on = -1;
						for( int dy = -7 ; dy < 8 ; dy++ )						// Look for things like metal patches
							if( py + dy >= 0 && py + dy < map->bloc_h_db )
								for( int dx = -7 ; dx < 8 ; dx++ )
									if( px + dx >= 0 && px + dx < map->bloc_w_db )
										if( map->map_data[py+dy][px+dx].stuff >= 0 ) {
											idx = map->map_data[py+dy][px+dx].stuff;
											if( features.feature[idx].type >=0
											&& feature_manager.feature[ features.feature[idx].type ].footprintx + 1 >= (abs(dx) << 1)
											&& feature_manager.feature[ features.feature[idx].type ].footprintz + 1 >= (abs(dy) << 1) ) {
												units.last_on = -idx - 2;
												dy = 800;
												break;
												}
											}
						}
					else
						units.last_on = -idx - 2;
					}
				pointing = units.last_on;
				}
			}
		else
			pointing = units.pick_minimap();

		if( pointing < -1 && canreclamate && feature_manager.feature[features.feature[ -pointing - 2 ].type].reclaimable && build == -1 )	cursor_type = CURSOR_RECLAIM;
		if( pointing < -1 && canresurrect && feature_manager.feature[features.feature[ -pointing - 2 ].type].reclaimable && build == -1 && CURSOR_REVIVE != CURSOR_RECLAIM )	cursor_type = CURSOR_REVIVE;

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

			switch(current_order)
			{
			case SIGNAL_ORDER_CAPTURE:	cursor_type=CURSOR_CAPTURE;	break;
			case SIGNAL_ORDER_MOVE:		cursor_type=CURSOR_MOVE;	break;
			case SIGNAL_ORDER_PATROL:	cursor_type=CURSOR_PATROL;	break;
			case SIGNAL_ORDER_GUARD:	cursor_type=CURSOR_GUARD;	break;
			case SIGNAL_ORDER_DGUN:		cursor_type=CURSOR_ATTACK;	break;
			case SIGNAL_ORDER_ATTACK:	cursor_type=CURSOR_ATTACK;	break;
			case SIGNAL_ORDER_RECLAM:	cursor_type=CURSOR_RECLAIM;	break;
			case SIGNAL_ORDER_LOAD:		cursor_type=CURSOR_LOAD;	break;
			case SIGNAL_ORDER_UNLOAD:	cursor_type=CURSOR_UNLOAD;	break;
			case SIGNAL_ORDER_REPAIR:	cursor_type=CURSOR_REPAIR;	break;
			};

			if(mouse_b!=1 && omb3==1) {
				if(cursor_type==CURSOR_ATTACK) {
					for(uint16 e=0;e<units.index_list_size;e++) {
						uint32 commandfire = current_order == SIGNAL_ORDER_DGUN ? MISSION_FLAG_COMMAND_FIRE : 0;
						i = units.idx_list[e];
						units.unit[ i ].Lock();
						if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel && unit_manager.unit_type[units.unit[i].type_id].canattack
						&& ( unit_manager.unit_type[units.unit[i].type_id].BMcode || !unit_manager.unit_type[units.unit[i].type_id].Builder ) ) {
							if( ( unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 0 ] && unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 0 ]->stockpile )
							|| ( unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 1 ] && unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 1 ]->stockpile )
							|| ( unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 2 ] && unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 2 ]->stockpile ) )
								commandfire = MISSION_FLAG_COMMAND_FIRE;
							if(TA3D_SHIFT_PRESSED)
								units.unit[i].add_mission(MISSION_ATTACK,&(units.unit[pointing].Pos),false,0,&(units.unit[pointing]),NULL,commandfire );
							else
								units.unit[i].set_mission(MISSION_ATTACK,&(units.unit[pointing].Pos),false,0,true,&(units.unit[pointing]),NULL,commandfire );
							}
						units.unit[ i ].UnLock();
						}
					if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
					}
				else if( cursor_type == CURSOR_CAPTURE && can_be_captured ) {
					for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
						i = units.idx_list[e];
						units.unit[ i ].Lock();
						if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel && unit_manager.unit_type[units.unit[i].type_id].CanCapture ) {
							if( TA3D_SHIFT_PRESSED )
								units.unit[i].add_mission( MISSION_CAPTURE, &(units.unit[pointing].Pos), false, 0, &(units.unit[pointing]), NULL );
							else
								units.unit[i].set_mission( MISSION_CAPTURE, &(units.unit[pointing].Pos), false, 0, true, &(units.unit[pointing]), NULL );
							}
						units.unit[ i ].UnLock();
						}
					if(!TA3D_SHIFT_PRESSED)	current_order = SIGNAL_ORDER_NONE;
					}
				else if(cursor_type==CURSOR_REPAIR) {
					for(uint16 e=0;e<units.index_list_size;e++) {
						i = units.idx_list[e];
						if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel
						&& unit_manager.unit_type[units.unit[i].type_id].Builder && unit_manager.unit_type[units.unit[i].type_id].BMcode) {
							units.EnterCS_from_outside();
							if( !TA3D_SHIFT_PRESSED )
								units.unit[ i ].play_sound( "repair" );
							if(TA3D_SHIFT_PRESSED)
								units.unit[i].add_mission(MISSION_REPAIR,&(units.unit[pointing].Pos),false,0,&(units.unit[pointing]));
							else
								units.unit[i].set_mission(MISSION_REPAIR,&(units.unit[pointing].Pos),false,0,true,&(units.unit[pointing]));
							units.LeaveCS_from_outside();
							}
						}
					if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
					}
				else if(cursor_type==CURSOR_RECLAIM) {
					for(uint16 e=0;e<units.index_list_size;e++) {
						i = units.idx_list[e];
						if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel
						&& unit_manager.unit_type[units.unit[i].type_id].CanReclamate && unit_manager.unit_type[units.unit[i].type_id].BMcode ) {
							units.EnterCS_from_outside();
							if(TA3D_SHIFT_PRESSED)
								units.unit[i].add_mission(MISSION_RECLAIM,&(units.unit[pointing].Pos),false,0,&(units.unit[pointing]));
							else
								units.unit[i].set_mission(MISSION_RECLAIM,&(units.unit[pointing].Pos),false,0,true,&(units.unit[pointing]));
							units.LeaveCS_from_outside();
							}
						}
					if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
					}
				else if(cursor_type==CURSOR_GUARD) {	// Le curseur donne un ordre
					units.give_order_guard(players.local_human_id,pointing,!TA3D_SHIFT_PRESSED);
					if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
					}
				else if(cursor_type==CURSOR_LOAD) {	// Le curseur donne un ordre
					units.give_order_load(players.local_human_id,pointing,!TA3D_SHIFT_PRESSED);
					if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
					}
				}
			}
		else {
			switch(current_order)
			{
			case SIGNAL_ORDER_CAPTURE:	cursor_type=CURSOR_CAPTURE;	break;
			case SIGNAL_ORDER_MOVE:		cursor_type=CURSOR_MOVE;	break;
			case SIGNAL_ORDER_PATROL:	cursor_type=CURSOR_PATROL;	break;
			case SIGNAL_ORDER_GUARD:	cursor_type=CURSOR_GUARD;	break;
			case SIGNAL_ORDER_DGUN:		cursor_type=CURSOR_ATTACK;	break;
			case SIGNAL_ORDER_ATTACK:	cursor_type=CURSOR_ATTACK;	break;
			case SIGNAL_ORDER_RECLAM:	cursor_type=CURSOR_RECLAIM;	break;
			case SIGNAL_ORDER_LOAD:		cursor_type=CURSOR_LOAD;	break;
			case SIGNAL_ORDER_UNLOAD:	cursor_type=CURSOR_UNLOAD;	break;
			case SIGNAL_ORDER_REPAIR:	cursor_type=CURSOR_REPAIR;	break;
			};

			if(mouse_b!=1 && omb3==1)
				if(cursor_type==CURSOR_ATTACK) {
					VECTOR cursor_pos = cursor_on_map(&cam,map,IsOnMinimap);
					for(uint16 e=0;e<units.index_list_size;e++) {
						uint32 commandfire = current_order == SIGNAL_ORDER_DGUN ? MISSION_FLAG_COMMAND_FIRE : 0;
						i = units.idx_list[e];
						units.unit[ i ].Lock();
						if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel && unit_manager.unit_type[units.unit[i].type_id].canattack
						&& ( unit_manager.unit_type[units.unit[i].type_id].BMcode || !unit_manager.unit_type[units.unit[i].type_id].Builder ) ) {
							units.EnterCS_from_outside();
							if( ( unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 0 ] && unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 0 ]->stockpile )
							|| ( unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 1 ] && unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 1 ]->stockpile )
							|| ( unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 2 ] && unit_manager.unit_type[ units.unit[i].type_id ].weapon[ 2 ]->stockpile ) )
								commandfire = MISSION_FLAG_COMMAND_FIRE;
							if(TA3D_SHIFT_PRESSED)
								units.unit[i].add_mission(MISSION_ATTACK,&(cursor_pos),false,0,NULL,NULL,commandfire );
							else
								units.unit[i].set_mission(MISSION_ATTACK,&(cursor_pos),false,0,true,NULL,NULL,commandfire );
							units.LeaveCS_from_outside();
							}
						units.unit[ i ].UnLock();
						}
					if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
					}
			}
		}

	if(cursor_type==CURSOR_REVIVE && CURSOR_REVIVE != CURSOR_RECLAIM && mouse_b!=1 && omb3 ==1 && ( !IsOnGUI || IsOnMinimap ) ) {	// The cursor orders to resurrect a wreckage
		VECTOR cur_pos=cursor_on_map(&cam,map,IsOnMinimap);
		int idx = -units.last_on - 2;
		if(idx>=0 && features.feature[ idx ].type >= 0 && feature_manager.feature[ features.feature[ idx ].type ].reclaimable )
			for(uint16 e=0;e<units.index_list_size;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel
				&& unit_manager.unit_type[units.unit[i].type_id].canresurrect && unit_manager.unit_type[units.unit[i].type_id].BMcode ) {
					units.EnterCS_from_outside();
					if(TA3D_SHIFT_PRESSED)
						units.unit[i].add_mission(MISSION_REVIVE,&cur_pos,false,idx,NULL);
					else
						units.unit[i].set_mission(MISSION_REVIVE,&cur_pos,false,idx,true,NULL);
					units.LeaveCS_from_outside();
					}
				}
		if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
		}

	if(cursor_type==CURSOR_RECLAIM && mouse_b!=1 && omb3 ==1 && ( !IsOnGUI || IsOnMinimap ) ) {	// The cursor orders to reclaim something
		VECTOR cur_pos=cursor_on_map(&cam,map,IsOnMinimap);
		int idx = -units.last_on - 2;
		if( idx >= 0 && features.feature[ idx ].type >= 0 && feature_manager.feature[ features.feature[ idx ].type ].reclaimable )
			for(uint16 e=0;e<units.index_list_size;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel
				&& unit_manager.unit_type[units.unit[i].type_id].CanReclamate && unit_manager.unit_type[units.unit[i].type_id].BMcode ) {
					units.EnterCS_from_outside();
					if(TA3D_SHIFT_PRESSED)
						units.unit[i].add_mission(MISSION_RECLAIM,&cur_pos,false,idx,NULL);
					else
						units.unit[i].set_mission(MISSION_RECLAIM,&cur_pos,false,idx,true,NULL);
					units.LeaveCS_from_outside();
					}
				}
		if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
		}

	if(cursor_type==CURSOR_UNLOAD && mouse_b!=1 && omb3 ==1 && ( !IsOnGUI || IsOnMinimap ) ) {	// The cursor orders to unload units
		units.give_order_unload(players.local_human_id,cursor_on_map(&cam,map,IsOnMinimap),!TA3D_SHIFT_PRESSED);
		if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
		}

	if(cursor_type==CURSOR_MOVE && mouse_b!=1 && omb3 ==1 && ( !IsOnGUI || IsOnMinimap ) ) {	// The cursor orders to move
		units.give_order_move(players.local_human_id,cursor_on_map(&cam,map,IsOnMinimap),!TA3D_SHIFT_PRESSED);
		if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
		}

	if(cursor_type==CURSOR_PATROL && mouse_b!=1 && omb3 ==1 && ( !IsOnGUI || IsOnMinimap ) ) {	// The cursor orders to patrol
		units.give_order_patrol(players.local_human_id,cursor_on_map(&cam,map,IsOnMinimap),!TA3D_SHIFT_PRESSED);
		if(!TA3D_SHIFT_PRESSED)	current_order=SIGNAL_ORDER_NONE;
		}

	if(build>=0 && cursor_type==CURSOR_DEFAULT && mouse_b != 1 && omb3 == 1 && !IsOnGUI ) {	// The cursor orders to build something
		VECTOR target=cursor_on_map(&cam,map);
		sel_x[1] = ((int)(target.x)+map->map_w_d)>>3;
		sel_y[1] = ((int)(target.z)+map->map_h_d)>>3;

		int d = max( abs( sel_x[1] - sel_x[0] ), abs( sel_y[1] - sel_y[0] ) );

		int ox = sel_x[0] + 0xFFFF;
		int oy = sel_y[0] + 0xFFFF;

		for( int c = 0 ; c <= d ; c++ ) {
			target.x = sel_x[0] + (sel_x[1] - sel_x[0]) * c / max( d, 1 );
			target.z = sel_y[0] + (sel_y[1] - sel_y[0]) * c / max( d, 1 );

			if( abs( ox - (int)target.x ) < unit_manager.unit_type[ build ].FootprintX
			&& abs( oy - (int)target.z ) < unit_manager.unit_type[ build ].FootprintZ )	continue;
			ox = (int)target.x;
			oy = (int)target.z;

			target.y=max(map->get_max_rect_h((int)target.x,(int)target.z, unit_manager.unit_type[ build ].FootprintX, unit_manager.unit_type[ build ].FootprintZ ),map->sealvl);
			target.x=target.x*8.0f-map->map_w_d;
			target.z=target.z*8.0f-map->map_h_d;

			can_be_there = can_be_built( target, map, build, players.local_human_id );

			if( can_be_there ) {
				units.give_order_build(players.local_human_id,build,target,!( TA3D_SHIFT_PRESSED || c != 0 ));
				build_order_given = true;
				}
			}
		if( build_order_given ) {
			if(!TA3D_SHIFT_PRESSED)
				build=-1;
			sound_manager->PlayTDFSound( "OKTOBUILD", "sound" , NULL );
			}
		else
			sound_manager->PlayTDFSound( "NOTOKTOBUILD", "sound" , NULL );
		}
	else if(build>=0 && cursor_type==CURSOR_DEFAULT && mouse_b == 1 && omb3 != 1 && !IsOnGUI ) {	// Giving the order to build a row
		VECTOR target = cursor_on_map(&cam,map);
		sel_x[ 0 ] = ((int)(target.x)+map->map_w_d)>>3;
		sel_y[ 0 ] = ((int)(target.z)+map->map_h_d)>>3;
		}

	if( !TA3D_SHIFT_PRESSED && build_order_given )
		build=-1;

	if( build == -1 )	build_order_given = false;

	if(cursor_type!=CURSOR_DEFAULT && mouse_b!=2 && omb3 ==2 && !IsOnGUI && TA3D_SHIFT_PRESSED ) {					// Remove commands from queue
		VECTOR target = cursor_on_map(&cam,map);
		target.x=((int)(target.x)+map->map_w_d)>>3;
		target.z=((int)(target.z)+map->map_h_d)>>3;
		target.x=target.x*8.0f-map->map_w_d;
		target.z=target.z*8.0f-map->map_h_d;
		target.y=max(map->get_unit_h(target.x,target.z),map->sealvl);
		units.remove_order(players.local_human_id,target);
		}

	if(mouse_b!=1 && omb3==1 && !TA3D_SHIFT_PRESSED && ( !IsOnGUI || IsOnMinimap ) )
		current_order=SIGNAL_ORDER_NONE;

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

	if( !IsOnGUI ) {
		if((mouse_b==2 && omb3!=2) || (mouse_b==1 && build == -1 && omb3==1 && selected && ( abs( amx - mouse_x ) >= 2 || abs( amy - mouse_y ) >= 2 ) ) ) {					// Deselect units
			if( mouse_b == 2 && current_order != SIGNAL_ORDER_NONE && current_order != SIGNAL_ORDER_MOVE )
				current_order = SIGNAL_ORDER_NONE;
			else {
				selecting = false;
				if( mouse_b == 1 ) {
					selecting=true;
					sel_x[0]=mouse_x;
					sel_y[0]=mouse_y;
					}
				if(build>=0) {
					if( (!TA3D_SHIFT_PRESSED && !(mouse_b==2 && omb3!=2) ) || (mouse_b==2 && omb3!=2) )
						build=-1;				// leave build mode
					}
				else {
					selected=false;
					cur_sel=-1;
					cur_sel_index=-1;
					for(uint16 e=0;e<units.index_list_size;e++) {
						i = units.idx_list[e];
						if(units.unit[i].owner_id==players.local_human_id)			// On peut désélectionner les morts, ça ne change rien :-)
							units.unit[i].sel=false;
						}
					}
				}
			}
		}

	if(build==-1 && (cursor_type==CURSOR_DEFAULT || cursor_type==CURSOR_CROSS) && (!IsOnGUI || (selecting && (mouse_y<32 || mouse_y>SCREEN_H-32) ) || IsOnMinimap ) ) {		// Si le curseur est dans la zone de jeu
		if( (mouse_b!=1 && selecting) || ( IsOnMinimap && mouse_b == 1 && omb3 != 1 ) ) {		// Récupère les unités présentes dans la sélection
			if( (sel_x[0]==sel_x[1] && sel_y[0]==sel_y[1]) || IsOnMinimap ) {
				int pointing = IsOnMinimap ? units.pick_minimap() : units.pick(&cam);		// Sélectionne une unité sur clic
				if(!TA3D_SHIFT_PRESSED)
					for(uint16 e=0;e<units.index_list_size;e++) {
						i = units.idx_list[e];
						if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id)
							units.unit[i].sel=false;
						}
				if(pointing>=0 && units.unit[pointing].port[BUILD_PERCENT_LEFT]==0.0f)		// On ne sélectionne pas les unités en construction
					units.unit[pointing].sel^=true;			// Sélectionne/Désélectionne si l'unité est déjà sélectionnée en appuyant sur SHIFT
				selected=false;
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id)
						selected|=units.unit[i].sel;
					}
				}
			else
				selected = units.select(&cam,sel_x,sel_y);		// Séléction au lasso
			if( selected )			// In order to refresh GUI
				old_sel = false;
			cur_sel=-1;
			cur_sel_index=-1;
			for(uint16 e=0;e<units.index_list_size && cur_sel!=-2;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
					cur_sel= (cur_sel==-1) ? i : -2;
				}
			if(cur_sel>=0) {
				cur_sel_index=cur_sel;
				cur_sel=units.unit[cur_sel].type_id;
						// Let's do some noise
				units.unit[ cur_sel_index ].play_sound( "select1" );
				}
			}
		selecting=false;
		if( mouse_b==1 && !IsOnMinimap ) {
			if(omb3!=1) {
				sel_x[0]=mouse_x;
				sel_y[0]=mouse_y;
				}
			sel_x[1]=mouse_x;
			sel_y[1]=mouse_y;
			selecting=true;
			}
		}
	else
		selecting=false;
	omb3=mouse_b;
	amx=mouse_x;
	amy=mouse_y;

	if( IsOnGUI && !IsOnMinimap )	cursor_type=CURSOR_DEFAULT;

	if( !IsOnGUI && ( cursor_type == CURSOR_DEFAULT || units.last_on == -1 ) )	{
		units.pick(&cam);		// Let's see what's under the cursor
		if( units.last_on == -1 ) {				// Is the cursor on a rock, tree, ...?
			VECTOR cur_pos = cursor_on_map(&cam,map,IsOnMinimap);
			int px=((int)(cur_pos.x+map->map_w_d))>>3;
			int py=((int)(cur_pos.z+map->map_h_d))>>3;
			if(px>=0 && px<map->bloc_w_db && py>=0 && py<map->bloc_h_db && (map->view_map->line[py>>1][px>>1] & (1<<players.local_human_id)) ) {
				int idx = -map->map_data[py][px].unit_idx - 2;				// Basic check
				if( idx<0 || features.feature[idx].type<0) {
					units.last_on = -1;
					for( int dy = -7 ; dy < 8 ; dy++ )						// Look for things like metal patches
						if( py + dy >= 0 && py + dy < map->bloc_h_db )
							for( int dx = -7 ; dx < 8 ; dx++ )
								if( px + dx >= 0 && px + dx < map->bloc_w_db )
									if( map->map_data[py+dy][px+dx].stuff >= 0 ) {
										idx = map->map_data[py+dy][px+dx].stuff;
										if( features.feature[idx].type >=0
										&& feature_manager.feature[ features.feature[idx].type ].footprintx + 1 >= (abs(dx) << 1)
										&& feature_manager.feature[ features.feature[idx].type ].footprintz + 1 >= (abs(dy) << 1) ) {
											units.last_on = -idx - 2;
											dy = 800;
											break;
											}
										}
					}
				else
					units.last_on = -idx - 2;
				}
			}
		}

	if(TA3D_CTRL_PRESSED && ( key[KEY_C] || key[KEY_F] || key[KEY_V] || key[KEY_B] ) ) {							// Select CTRL_* category units
		String check_cat = "CTRL_";
		if( key[KEY_C] )	check_cat += "C";
		else if( key[KEY_F] )	check_cat += "F";
		else if( key[KEY_V] )	check_cat += "V";
		else if( key[KEY_B] )	check_cat += "B";
		for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].build_percent_left == 0.0f ) {
				if( unit_manager.unit_type[units.unit[i].type_id].checkCategory( check_cat.c_str() ) )
					units.unit[i].sel=true;
				else if(!TA3D_SHIFT_PRESSED)
					units.unit[i].sel=false;
				}
			}
		cur_sel=-1;
		cur_sel_index=-1;
		build=-1;
		for(uint16 e=0;e<units.index_list_size && cur_sel!=-2;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel)
				cur_sel= (cur_sel==-1) ? i : -2;
			}
		selected=(cur_sel!=-1);
		if(cur_sel>=0) {
			cur_sel_index=cur_sel;
			cur_sel=units.unit[cur_sel].type_id;
			}
		}
	else if(TA3D_CTRL_PRESSED && key[KEY_Z]) {		// Séletionne toutes les unités dont le type est déjà sélectionné / Select units of the same type
		bool *sel_type = new bool[unit_manager.nb_unit];
		for(i=0;i<unit_manager.nb_unit;i++)
			sel_type[i]=false;
		for(uint16 e=0;e<units.index_list_size;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
				sel_type[units.unit[i].type_id]=true;
			}
		for(uint16 e=0;e<units.index_list_size;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].build_percent_left == 0.0f && units.unit[i].owner_id==players.local_human_id && sel_type[units.unit[i].type_id])
				units.unit[i].sel=true;
			}
		cur_sel=-1;
		cur_sel_index=-1;
		for(uint16 e=0;e<units.index_list_size && cur_sel!=-2;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
				cur_sel= (cur_sel==-1) ? i : -2;
			}
		selected=(cur_sel!=-1);
		build=-1;
		if(cur_sel>=0) {
			cur_sel_index=cur_sel;
			cur_sel=units.unit[cur_sel].type_id;
			}
		delete[] sel_type;
		}
	else if(TA3D_CTRL_PRESSED && key[KEY_A]) {		// Select all the player's units
		for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].port[BUILD_PERCENT_LEFT] == 0.0f && units.unit[i].owner_id == players.local_human_id )
				units.unit[i].sel=true;
			}
		cur_sel=-1;
		cur_sel_index=-1;
		for( uint16 e = 0; e < units.index_list_size && cur_sel != -2 ; e++ ) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel )
				cur_sel= (cur_sel==-1) ? i : -2;
			}
		selected=(cur_sel!=-1);
		build=-1;
		if(cur_sel>=0) {
			cur_sel_index=cur_sel;
			cur_sel=units.unit[cur_sel].type_id;
			}
		}
	else if(TA3D_CTRL_PRESSED) {			// Formation de groupes d'unités
		int grpe=-1;
		if(key[KEY_0])	grpe=0;
		if(key[KEY_1])	grpe=1;
		if(key[KEY_2])	grpe=2;
		if(key[KEY_3])	grpe=3;
		if(key[KEY_4])	grpe=4;
		if(key[KEY_5])	grpe=5;
		if(key[KEY_6])	grpe=6;
		if(key[KEY_7])	grpe=7;
		if(key[KEY_8])	grpe=8;
		if(key[KEY_9])	grpe=9;

		if(grpe>=0) {
			grpe=1<<grpe;
			for(uint16 e=0;e<units.index_list_size;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id) {
					if(units.unit[i].sel)
						units.unit[i].groupe|=grpe;
					else if(!TA3D_SHIFT_PRESSED)
						units.unit[i].groupe&=~grpe;
					}
				}
			}
		}
	else if(key[KEY_ALT]) {			// Restauration de groupes d'unités
		int grpe=-1;
		if(key[KEY_0])	grpe=0;
		if(key[KEY_1])	grpe=1;
		if(key[KEY_2])	grpe=2;
		if(key[KEY_3])	grpe=3;
		if(key[KEY_4])	grpe=4;
		if(key[KEY_5])	grpe=5;
		if(key[KEY_6])	grpe=6;
		if(key[KEY_7])	grpe=7;
		if(key[KEY_8])	grpe=8;
		if(key[KEY_9])	grpe=9;

		if(grpe>=0) {
			build=-1;
			grpe=1<<grpe;
			for(uint16 e=0;e<units.index_list_size;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id) {
					if(units.unit[i].groupe&grpe)
						units.unit[i].sel=true;
					else if(!TA3D_SHIFT_PRESSED)
						units.unit[i].sel=false;
					}
				}
			}

		cur_sel=-1;
		cur_sel_index=-1;
		for(uint16 e=0;e<units.index_list_size && cur_sel!=-2;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
				cur_sel= (cur_sel==-1) ? i : -2;
			}
		selected=(cur_sel!=-1);
		if(cur_sel>=0) {
			cur_sel_index=cur_sel;
			cur_sel=units.unit[cur_sel].type_id;
			}
		}

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

			// That code was rewritten multithreaded
	float timetosimulate=dt*units.apparent_timefactor;							// Physics calculations take place here
	wind_change=false;						// Don't try to run following code in separate thread
	features.move(timetosimulate, map);					// Animate objects
	fx_manager.move(timetosimulate);

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

	cam_h = cam.RPos.y - map->get_unit_h(cam.RPos.x,cam.RPos.z);

	cam.zfar = 600.0f + max( (cam_h-150.0f)*2.0f, 0.0f );

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

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

		memcpy( FogColor, sky_data->FogColor, sizeof( float ) * 4 );
		}

	gfx->SetDefState();
	glClearColor(FogColor[0],FogColor[1],FogColor[2],FogColor[3]);

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

	if(g_useProgram && g_useFBO && lp_CONFIG->water_quality>=2 && map->water && !map->ota_data.lavaworld && !reflection_drawn_last_time) {		// Dessine les reflets sur l'eau

		reflection_drawn_last_time = true;

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Efface l'écran

		glViewport(0, 0, 512, 512);

		gfx->ReInitAllTex();
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		glDisable(GL_BLEND);

		double eqn[4]= { 0.0f, 1.0f, 0.0f, -map->sealvl };

		CAMERA refcam = cam;
		refcam.zfar *= 2.0f;
		refcam.mirror = true;
		refcam.mirror_pos = -2.0f*map->sealvl;

		refcam.SetView();
		glClipPlane(GL_CLIP_PLANE1, eqn);
		glEnable(GL_CLIP_PLANE1);

		sun.Set(refcam);
		sun.Enable();

		refcam.zfar*=100.0f;
		refcam.SetView();
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D,sky);
		glDisable(GL_LIGHTING);
		glFogi (GL_FOG_MODE, FogMode);
		glFogfv (GL_FOG_COLOR, FogColor);
		glFogf (GL_FOG_DENSITY, FogD);
		glHint (GL_FOG_HINT, GL_NICEST);
		glFogf (GL_FOG_START, FogNear);
		glFogf (GL_FOG_END, refcam.zfar);
		glDepthMask(GL_FALSE);
		if( spherical_sky ) {
			glCullFace(GL_FRONT);
			glTranslatef(cam.RPos.x,-map->sealvl,cam.RPos.z);
			glRotatef( sky_angle, 0.0f, 1.0f, 0.0f );
			float scale_factor = 15.0f * ( cam.RPos.y + cam.shake_vec.y + sky_obj.w ) / sky_obj.w;
			glScalef( scale_factor, scale_factor, scale_factor );
			sky_obj.draw();
			}
		else {
			glBegin(GL_QUADS);
				glTexCoord2f(0.0f,map->map_h*0.002f);				glVertex3f(-2.0f*map->map_w,300.0f,2.0f*map->map_h);
				glTexCoord2f(map->map_w*0.002f,map->map_h*0.002f);	glVertex3f(2.0f*map->map_w,300.0f,2.0f*map->map_h);
				glTexCoord2f(map->map_w*0.002f,0.0f);				glVertex3f(2.0f*map->map_w,300.0f,-2.0f*map->map_h);
				glTexCoord2f(0.0f,0.0f);							glVertex3f(-2.0f*map->map_w,300.0f,-2.0f*map->map_h);
			glEnd();
			}
		glDepthMask(GL_TRUE);
		glFogi (GL_FOG_MODE, FogMode);
		glFogfv (GL_FOG_COLOR, FogColor);
		glFogf (GL_FOG_DENSITY, FogD);
		glHint (GL_FOG_HINT, GL_NICEST);
		glFogf (GL_FOG_START, FogNear);
		glFogf (GL_FOG_END, refcam.zfar);
		glEnable(GL_CULL_FACE);
		glEnable(GL_LIGHTING);
		glEnable(GL_FOG);
		glCullFace(GL_FRONT);
		refcam.zfar=(500.0f+(cam_h-150.0f)*2.0f)*2.0f;
		refcam.SetView();

		if(cam.RPos.y<=gfx->low_def_limit && lp_CONFIG->water_quality==4) {

			if(lp_CONFIG->wireframe)
				glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);

			map->draw(&refcam,1<<players.local_human_id,false,0.0f,t,dt*lp_CONFIG->timefactor,false,false,false);

			if(lp_CONFIG->wireframe)
				glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);

			features.draw(&refcam);		// Dessine les éléments "2D" / "sprites"

			units.draw(&refcam,map,false,true,false,lp_CONFIG->height_line);			// Dessine les unités / draw units

			glDisable(GL_CULL_FACE);
			weapons.draw(&refcam,map);			// Dessine les objets produits par les armes / draw weapons

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

			fx_manager.draw(&refcam,map,map->sealvl);		// Effets spéciaux en surface / fx above water

			}

		glDisable(GL_CLIP_PLANE1);

		gfx->ReInitAllTex( true );

		glColor4f(1.0f,1.0f,1.0f,1.0f);
		glDisable(GL_BLEND);

		glBindTexture(GL_TEXTURE_2D,reflectex);								// Store what's on screen for reflection effect
		glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 512, 512, 0);

		glViewport(0, 0, SCREEN_W, SCREEN_H);

		gfx->SetDefState();

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

	gfx->SetDefState();
	glClearColor(FogColor[0],FogColor[1],FogColor[2],FogColor[3]);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear screen

	cam.SetView();

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

	cam.SetView();

	glDisable(GL_FOG);
	glColor3f( 0.0f, 0.0f, 0.0f );				// Black background
	glDisable( GL_TEXTURE_2D );
	glDepthMask(GL_FALSE);
	glBegin( GL_QUADS );
		glVertex3i( -map->map_w, 0, map->map_h );
		glVertex3i( map->map_w, 0, map->map_h );
		glVertex3i( map->map_w, 0, -map->map_h );
		glVertex3i( -map->map_w, 0, -map->map_h );
	glEnd();
	glDepthMask(GL_TRUE);
	glEnable(GL_FOG);

	cam.zfar*=100.0f;
	cam.SetView();
	glColor4f(1.0f,1.0f,1.0f,1.0f);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,sky);
	glDisable(GL_LIGHTING);
	glDepthMask(GL_FALSE);
	if( spherical_sky ) {
		glTranslatef( cam.RPos.x, cam.RPos.y+cam.shake_vec.y, cam.RPos.z );
		glRotatef( sky_angle, 0.0f, 1.0f, 0.0f );
		sky_obj.draw();
		if( !sky_obj.full ) {
			glScalef( 1.0f, -1.0f, 1.0f );
			glCullFace( GL_FRONT );
			sky_obj.draw();
			glCullFace( GL_BACK );
			}
		}
	else {
		glBegin(GL_QUADS);
			glTexCoord2f(0.0f,0.0f);							glVertex3f(-2.0f*map->map_w,300.0f,-2.0f*map->map_h);
			glTexCoord2f(map->map_w*0.002f,0.0f);				glVertex3f(2.0f*map->map_w,300.0f,-2.0f*map->map_h);
			glTexCoord2f(map->map_w*0.002f,map->map_h*0.002f);	glVertex3f(2.0f*map->map_w,300.0f,2.0f*map->map_h);
			glTexCoord2f(0.0f,map->map_h*0.002f);				glVertex3f(-2.0f*map->map_w,300.0f,2.0f*map->map_h);
		glEnd();
		}
	glDepthMask(GL_TRUE);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHTING);
	glEnable(GL_FOG);
	cam.zfar=500.0f+(cam_h-150.0f)*2.0f;
	cam.SetView();

	if(lp_CONFIG->wireframe)
		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);

	map->draw(&cam,1<<players.local_human_id,false,0.0f,t,dt*lp_CONFIG->timefactor);

	if(lp_CONFIG->wireframe)
		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);

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

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

	units.draw(&cam,map,true,false,true,lp_CONFIG->height_line);			// Dessine les unités sous l'eau / Draw units which are under water

	weapons.draw(&cam,map,true);			// Dessine les objets produits par les armes sous l'eau / Draw weapons which are under water

	if(map->water)
		fx_manager.draw(&cam,map,map->sealvl,true);		// Effets spéciaux sous-marins / Draw fx which are under water

	if(map->water) {
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		gfx->ReInitAllTex( true );

		if(!g_useProgram || !g_useFBO || lp_CONFIG->water_quality<2) {
			gfx->set_alpha_blending();
			if(lp_CONFIG->water_quality==1) {					// lp_CONFIG->water_quality=1
				glColor4f(1.0f,1.0f,1.0f,0.5f);

				glActiveTextureARB(GL_TEXTURE0_ARB);
				glEnable(GL_TEXTURE_2D);
				glClientActiveTextureARB(GL_TEXTURE0_ARB);

				map->draw(&cam,1,true,map->sealvl,t,dt*lp_CONFIG->timefactor);
				}
			else {									// lp_CONFIG->water_quality=0
				glColor4f(1.0f,1.0f,1.0f,0.5f);
				glDisable(GL_LIGHTING);

				glActiveTextureARB(GL_TEXTURE0_ARB);
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D,map->low_tex);

				cam.SetView();
				glTranslatef( 0.0f, map->sealvl, map->sea_dec );
				water_obj.draw(t,cam.RPos.x,cam.RPos.z,false);
				glColor4f(1.0f,1.0f,1.0f,0.75f);

				glEnable(GL_LIGHTING);
				glActiveTextureARB(GL_TEXTURE0_ARB);
				gfx->ReInitTexSys();
				glEnable(GL_TEXTURE_2D);
				}
			gfx->unset_alpha_blending();
			}
		else {
			glColor4f(1.0f,1.0f,1.0f,1.0f);
			glDisable(GL_LIGHTING);

			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,water_FBO);					// First pass of water rendering, store reflection vector
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D,first_pass,0);

//			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Efface la texture tampon
			glClear(GL_DEPTH_BUFFER_BIT);		// Efface la texture tampon

			glViewport(0,0,512,512);

			glActiveTextureARB(GL_TEXTURE0_ARB);
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D,map->lava_map);
			glClientActiveTextureARB(GL_TEXTURE0_ARB);

			glActiveTextureARB(GL_TEXTURE1_ARB);
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D,water);
			glClientActiveTextureARB(GL_TEXTURE1_ARB);

			if(lp_CONFIG->water_quality==2) {
				water_pass1_low.on();
				water_pass1_low.setvar1i("lava",0);
				water_pass1_low.setvar1i("map",1);
				water_pass1_low.setvar1f("t",t);
				water_pass1_low.setvar2f("factor",water_obj.w/map->map_w,water_obj.w/map->map_h);
				}
			else {
				water_pass1.on();
				water_pass1.setvar1i("lava",0);
				water_pass1.setvar1i("map",1);
				water_pass1.setvar1f("t",t);
				water_pass1.setvar2f("factor",water_obj.w/map->map_w,water_obj.w/map->map_h);
				}

			cam.SetView();
			glTranslatef(0.0f,map->sealvl,0.0f);
			water_obj.draw(t,cam.RPos.x,cam.RPos.z,true);

			if(lp_CONFIG->water_quality==2)
				water_pass1_low.off();
			else
				water_pass1.off();

			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D,second_pass,0);					// Second pass of water rendering, store viewing vector

//			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Efface la texture tampon
			glClear(GL_DEPTH_BUFFER_BIT);		// Efface la texture tampon

			glActiveTextureARB(GL_TEXTURE0_ARB);
			glDisable(GL_TEXTURE_2D);

			glActiveTextureARB(GL_TEXTURE1_ARB);
			glDisable(GL_TEXTURE_2D);

			water_pass2.on();

			cam.SetView();
			glTranslatef(0.0f,map->sealvl,0.0f);
			water_obj.draw(t,cam.RPos.x,cam.RPos.z,true);

			water_pass2.off();

			if(lp_CONFIG->water_quality > 2) {
				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D,water_color,0);					// Third pass of water rendering, store water color

				glClear(GL_DEPTH_BUFFER_BIT);		// Efface la texture tampon

				glActiveTextureARB(GL_TEXTURE0_ARB);
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D,map->low_tex);

				cam.SetView();
				glTranslatef( 0.0f, map->sealvl, map->sea_dec );
				water_obj.draw(t,cam.RPos.x,cam.RPos.z,false);
				}

			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);

			glViewport(0, 0, SCREEN_W, SCREEN_H);

			float logw=log((float)SCREEN_W)/log(2.0f);
			float logh=log((float)SCREEN_H)/log(2.0f);
			int wx=logw>(int)logw ? (int)logw+1 : (int)logw;
			int wy=logh>(int)logh ? (int)logh+1 : (int)logh;
			wx=1<<wx;
			wy=1<<wy;
			glBindTexture(GL_TEXTURE_2D,transtex);								// Store what's on screen for transparency effect
			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, wx, wy, 0);

			glEnable(GL_STENCIL_TEST);											// Draw basic water in order to have correct texture mapping
			glClear(GL_STENCIL_BUFFER_BIT);
			glStencilFunc(GL_ALWAYS,128, 0xffffffff);
			glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);

			glActiveTextureARB(GL_TEXTURE0_ARB);
			glDisable(GL_TEXTURE_2D);
			glClientActiveTextureARB(GL_TEXTURE0_ARB);

			cam.SetView();
			glTranslatef(0.0f,map->sealvl,0.0f);
			water_obj.draw(t,cam.RPos.x,cam.RPos.z,true);

			glDisable(GL_STENCIL_TEST);

			glMatrixMode(GL_TEXTURE);
				glLoadIdentity();
			glMatrixMode(GL_MODELVIEW);

			glEnable(GL_LIGHTING);

			glActiveTextureARB(GL_TEXTURE0_ARB);
			if(map->ota_data.lavaworld)
				glBindTexture(GL_TEXTURE_2D,sky);
			else
				glBindTexture(GL_TEXTURE_2D,reflectex);
			glEnable(GL_TEXTURE_2D);

			glActiveTextureARB(GL_TEXTURE1_ARB);
			glBindTexture(GL_TEXTURE_2D,transtex);
			glEnable(GL_TEXTURE_2D);

			glActiveTextureARB(GL_TEXTURE2_ARB);
			glBindTexture(GL_TEXTURE_2D,first_pass);
			glEnable(GL_TEXTURE_2D);

			glActiveTextureARB(GL_TEXTURE3_ARB);
			glBindTexture(GL_TEXTURE_2D,second_pass);
			glEnable(GL_TEXTURE_2D);

			if(lp_CONFIG->water_quality==2) {
				water_shader.on();
				water_shader.setvar1i("sky",0);
				water_shader.setvar1i("rtex",1);
				water_shader.setvar1i("bump",2);
				water_shader.setvar1i("view",3);
				water_shader.setvar2f("coef",(float)SCREEN_W/wx,(float)SCREEN_H/wy);
				}
			else {
				glActiveTextureARB(GL_TEXTURE4_ARB);
				glBindTexture(GL_TEXTURE_2D,water_color);
				glEnable(GL_TEXTURE_2D);

				water_shader_reflec.on();
				water_shader_reflec.setvar1i("sky",0);
				water_shader_reflec.setvar1i("rtex",1);
				water_shader_reflec.setvar1i("bump",2);
				water_shader_reflec.setvar1i("view",3);
				water_shader_reflec.setvar1i("water_color",4);
				water_shader_reflec.setvar2f("coef",(float)SCREEN_W/wx,(float)SCREEN_H/wy);
				}

			glColor4f(1.0f,1.0f,1.0f,1.0f);
			glDisable(GL_DEPTH_TEST);
			cam.SetView();
			glEnable(GL_STENCIL_TEST);
			glStencilFunc(GL_NOTEQUAL,0, 0xffffffff);
			glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
			VECTOR cam_pos = cam.RPos + cam.shake_vec;
			glBegin(GL_QUADS);
				VECTOR P = cam_pos + 1.1f*(cam.Dir+0.75f*cam.Up-cam.width_factor*cam.Side);
				glTexCoord2f(0.0f,1.0f);	glVertex3f(P.x,P.y,P.z);

				P = cam_pos + 1.1f*(cam.Dir+0.75f*cam.Up+cam.width_factor*cam.Side);
				glTexCoord2f(1.0f,1.0f);	glVertex3f(P.x,P.y,P.z);

				P = cam_pos + 1.1f*(cam.Dir-0.75f*cam.Up+cam.width_factor*cam.Side);
				glTexCoord2f(1.0f,0.0f);	glVertex3f(P.x,P.y,P.z);

				P = cam_pos + 1.1f*(cam.Dir-0.75f*cam.Up-cam.width_factor*cam.Side);
				glTexCoord2f(0.0f,0.0f);	glVertex3f(P.x,P.y,P.z);
			glEnd();
			glDisable(GL_STENCIL_TEST);
			glEnable(GL_DEPTH_TEST);

			if(lp_CONFIG->water_quality==2)
				water_shader.off();
			else
				water_shader_reflec.off();
			}
		gfx->ReInitAllTex( true );
		}

	if(build>=0 && !IsOnGUI ) {			// Display the building we want to build (with nice selection quads)
		VECTOR target=cursor_on_map(&cam,map);
		sel_x[1] = ((int)(target.x)+map->map_w_d)>>3;
		sel_y[1] = ((int)(target.z)+map->map_h_d)>>3;

		if( mouse_b != 1 && omb3 != 1 ) {
			sel_x[0] = sel_x[1];
			sel_y[0] = sel_y[1];
			}
		
		int d = max( abs( sel_x[1] - sel_x[0] ), abs( sel_y[1] - sel_y[0] ) );

		int ox = sel_x[0] + 0xFFFF;
		int oy = sel_y[0] + 0xFFFF;

		for( int c = 0 ; c <= d ; c++ ) {
			target.x = sel_x[0] + (sel_x[1] - sel_x[0]) * c / max( d, 1 );
			target.z = sel_y[0] + (sel_y[1] - sel_y[0]) * c / max( d, 1 );

			if( abs( ox - (int)target.x ) < unit_manager.unit_type[ build ].FootprintX
			&& abs( oy - (int)target.z ) < unit_manager.unit_type[ build ].FootprintZ )	continue;
			ox = (int)target.x;
			oy = (int)target.z;

			target.y=max(map->get_max_rect_h((int)target.x,(int)target.z, unit_manager.unit_type[ build ].FootprintX, unit_manager.unit_type[ build ].FootprintZ ),map->sealvl);
			target.x=target.x*8.0f-map->map_w_d;
			target.z=target.z*8.0f-map->map_h_d;

			can_be_there = can_be_built( target, map, build, players.local_human_id );

			cam.SetView();
			glTranslatef(target.x,target.y,target.z);
			glScalef(unit_manager.unit_type[build].Scale,unit_manager.unit_type[build].Scale,unit_manager.unit_type[build].Scale);
			if(unit_manager.unit_type[build].model) {
				gfx->ReInitAllTex( true );
				if(can_be_there)
					glColor4f(1.0f,1.0f,1.0f,1.0f);
				else
					glColor4f(1.0f,0.0f,0.0f,0.0f);
				unit_manager.unit_type[build].model->draw(0.0f,NULL,false,false,false,0,NULL,NULL,NULL,0.0f,NULL,false,players.local_human_id,false);
				glColor4f(1.0f,1.0f,1.0f,1.0f);
				}
			cam.SetView();
			glTranslatef(target.x,target.y,target.z);
			float DX = (unit_manager.unit_type[build].FootprintX<<2);
			float DZ = (unit_manager.unit_type[build].FootprintZ<<2);
			float red=1.0f, green=0.0f;
			if(can_be_there) {
				green=1.0f;
				red=0.0f;
				}
			glDisable(GL_CULL_FACE);
			glDisable(GL_TEXTURE_2D);
			glDisable(GL_LIGHTING);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
			glBegin(GL_QUADS);
				glColor4f(red,green,0.0f,1.0f);
				glVertex3f(-DX,0.0f,-DZ);			// First quad
				glVertex3f(DX,0.0f,-DZ);
				glColor4f(red,green,0.0f,0.0f);
				glVertex3f(DX+2.0f,5.0f,-DZ-2.0f);
				glVertex3f(-DX-2.0f,5.0f,-DZ-2.0f);

				glColor4f(red,green,0.0f,1.0f);
				glVertex3f(-DX,0.0f,-DZ);			// Second quad
				glVertex3f(-DX,0.0f,DZ);
				glColor4f(red,green,0.0f,0.0f);
				glVertex3f(-DX-2.0f,5.0f,DZ+2.0f);
				glVertex3f(-DX-2.0f,5.0f,-DZ-2.0f);

				glColor4f(red,green,0.0f,1.0f);
				glVertex3f(DX,0.0f,-DZ);			// Third quad
				glVertex3f(DX,0.0f,DZ);
				glColor4f(red,green,0.0f,0.0f);
				glVertex3f(DX+2.0f,5.0f,DZ+2.0f);
				glVertex3f(DX+2.0f,5.0f,-DZ-2.0f);

				glColor4f(red,green,0.0f,1.0f);
				glVertex3f(-DX,0.0f,DZ);			// Fourth quad
				glVertex3f(DX,0.0f,DZ);
				glColor4f(red,green,0.0f,0.0f);
				glVertex3f(DX+2.0f,5.0f,DZ+2.0f);
				glVertex3f(-DX-2.0f,5.0f,DZ+2.0f);
			glEnd();
			glDisable(GL_BLEND);
			glEnable(GL_LIGHTING);
			glEnable(GL_CULL_FACE);
			}
		}

	units.draw(&cam,map,false,false,true,lp_CONFIG->height_line);			// Dessine les unités non encore dessinées / Draw units which have not been drawn

	weapons.draw(&cam,map,false);			// Dessine les objets produits par les armes n'ayant pas été dessinés / Draw weapons which have not been drawn

	if(selected && TA3D_SHIFT_PRESSED) {
		cam.SetView();
		bool builders = false;
		for(uint16 e=0;e<units.index_list_size;e++) {
			i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel) {
				builders |= unit_manager.unit_type[units.unit[i].type_id].Builder;
				units.unit[i].show_orders();					// Dessine les ordres reçus par l'unité
				}
			}

		if(builders)
			for(uint16 e=0;e<units.index_list_size;e++) {
				i = units.idx_list[e];
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && !units.unit[i].sel
				&& unit_manager.unit_type[units.unit[i].type_id].Builder && unit_manager.unit_type[units.unit[i].type_id].BMcode)
					units.unit[i].show_orders(true);					// Dessine les ordres reçus par l'unité
				}
		}

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

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

	if(!map->water)
		fx_manager.draw(&cam,map,map->sealvl,true);		// Effets spéciaux en surface
	fx_manager.draw(&cam,map,map->sealvl);		// Effets spéciaux en surface

	if(key[KEY_ESC])
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (char*)"esc_menu.show", NULL, NULL );
	if(game_area.get_state("esc_menu.b_exit"))
		done=true;
	if(key[KEY_TILDE]) {
		if(!tilde)
			Console->ToggleShow();
		tilde=true;
		}
	else
		tilde=false;

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	gfx->ReInitAllTex( true );

	gfx->set_2D_mode();		// Affiche console, infos,...

	old_cam_pos=cam.RPos;
	int signal = game_script.run(map,((float)(units.current_tick - script_timer)) / TICKS_PER_SEC,players.local_human_id);
	script_timer = units.current_tick;

	switch(signal)
	{
	case 0:				// Rien de spécial
		break;
	case -1:			// Fin du script
		game_script.stop();
		break;
	case -2:			// Pause
		break;
	case -3:			// Attente d'un évènement
		break;
	case 1:				// Fin de partie (match nul)
		done=true;
		exit_mode=EXIT_NONE;
		break;
	case 2:				// Fin de partie (victoire)
		done=true;
		exit_mode=EXIT_VICTORY;
		break;
	case 3:				// Fin de partie (défaite)
		done=true;
		exit_mode=EXIT_DEFEAT;
		break;
	case 4:				// Caméra en mode normal
		if(freecam) {
			freecam=false;
			r2=0.0f;
			}
		break;
	case 5:				// Caméra libre
		if(!freecam)
			freecam=true;
		break;
	};

	if(selecting) {						// Affiche le rectangle de selection
		glDisable(GL_TEXTURE_2D);
		glColor4f(0.0f,0.0f,0.0f,1.0f);
		gfx->rect(sel_x[0]+1,sel_y[0]+1,sel_x[1]+1,sel_y[1]+1);
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		gfx->rect(sel_x[0],sel_y[0],sel_x[1],sel_y[1]);
		}

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

	int n = cur_sel;
	if( n == -1 )
		n = -2;
	if(n>=0 && units.unit[cur_sel_index].port[BUILD_PERCENT_LEFT]>0.0f)		// Unité non terminée
		n = -1;
	int sel = -1;

	/*------------------- Draw GUI components -------------------------------------------------------*/

	if( current_gui != String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen" )
		unit_manager.unit_build_menu(n,omb2,dt, 0, 0, true );	// Draw GUI background
	else
		unit_manager.unit_build_menu(-1,omb2,dt, 0, 0, true);	// Draw GUI background

	game_area.draw();

	/*------------------- End of GUI drawings -------------------------------------------------------*/

	WND *current_wnd = game_area.get_wnd( current_gui );
	if( current_gui != String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen" )
		sel = unit_manager.unit_build_menu(n,omb2,dt, current_wnd ? current_wnd->build_pic_x : 0, current_wnd ? current_wnd->build_pic_y : 0 );	// Menu correspondant à l'unité / Unit's menu
	else
		sel = unit_manager.unit_build_menu(-1,omb2,dt);	// Menu correspondant à l'unité / Unit's menu
	if(sel==-2) {										// Crée des armes / build weapons
		if(mouse_b==1 && omb2!=1) {
			if(TA3D_SHIFT_PRESSED)
				units.unit[cur_sel_index].planned_weapons+=5.0f;
			else
				units.unit[cur_sel_index].planned_weapons+=1.0f;
			}
		else if(mouse_b==2 && omb2!=2) {
			if(TA3D_SHIFT_PRESSED)
				units.unit[cur_sel_index].planned_weapons-=5.0f;
			else
				units.unit[cur_sel_index].planned_weapons-=1.0f;
			if(units.unit[cur_sel_index].planned_weapons<0.0f)
				units.unit[cur_sel_index].planned_weapons=0.0f;
			}
		sel=-1;
		}
	bool	refresh_gui = false;
	if( !selected && current_gui != "" ) {
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".hide" ).c_str(), NULL, NULL );	// Hide it
		current_gui = "";
		old_sel = false;
		}
	if( (old_gui_sel >= 0 && old_gui_sel != n) || ( !old_sel && !selected ) ) {			// Update GUI
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)(current_gui + ".hide").c_str(), NULL, NULL );	// Hide it
		current_gui = "";
		old_sel = false;
		}
	if( n >= 0 && n != old_gui_sel ) {
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".hide" ).c_str(), NULL, NULL );	// Hide it
		current_gui = String( unit_manager.unit_type[ n ].Unitname ) + "1";
		if( game_area.get_wnd( current_gui ) == NULL ) {
			if( unit_manager.unit_type[ n ].nb_unit > 0 )				// The default build page
				current_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "dl";
			else
				current_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen";
			}
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".show" ).c_str(), NULL, NULL );	// Show it
		refresh_gui = true;
		}
	if( n < 0 && ( selected && !old_sel ) ) {
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".hide" ).c_str(), NULL, NULL );	// Hide it
		old_sel = true;
		current_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen";
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".show" ).c_str(), NULL, NULL );	// Show it
		refresh_gui = true;
		}
	old_gui_sel = n;

	if( refresh_gui ) {
	/*------------------- GUI update ----------------------------------------------------------------*/

		bool onoffable=false;
		bool canstop=false;
		bool canpatrol=false;
		bool canmove=false;
		bool canguard=false;
		bool canattack=false;
		bool canreclam=false;
		bool builders=false;			// For repair purposes only
		bool canload=false;
		bool cancapture=false;
		bool cancloak=false;
		bool candgun=false;
		int onoff_state = 0;
		byte sforder = 0;
		byte smorder = 0;

		units.EnterCS_from_outside();

		for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
			uint32 i = units.idx_list[e];
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel) {
				onoffable |= unit_manager.unit_type[ units.unit[i].type_id ].onoffable;
				canstop |= unit_manager.unit_type[ units.unit[i].type_id ].canstop;
				canmove |= unit_manager.unit_type[ units.unit[i].type_id ].canmove;
				canpatrol |= unit_manager.unit_type[ units.unit[i].type_id ].canpatrol;
				canguard |= unit_manager.unit_type[ units.unit[i].type_id ].canguard;
				canattack |= unit_manager.unit_type[ units.unit[i].type_id ].canattack;
				canreclam |= unit_manager.unit_type[ units.unit[i].type_id ].CanReclamate;
				builders |= unit_manager.unit_type[ units.unit[i].type_id ].Builder;
				canload |= unit_manager.unit_type[ units.unit[i].type_id ].canload;
				cancapture |= unit_manager.unit_type[ units.unit[i].type_id ].CanCapture;
				cancloak |= unit_manager.unit_type[ units.unit[i].type_id ].CloakCost > 0;
				candgun |= unit_manager.unit_type[ units.unit[i].type_id ].candgun;
				if( unit_manager.unit_type[ units.unit[i].type_id ].canattack )
					sforder |= units.unit[i].port[ STANDINGFIREORDERS ];
				if( unit_manager.unit_type[ units.unit[i].type_id ].canmove )
					smorder |= units.unit[i].port[ STANDINGMOVEORDERS ];
				if( unit_manager.unit_type[ units.unit[i].type_id ].onoffable )
					onoff_state |= units.unit[ i ].port[ ACTIVATION ] ? 2 : 1;
				}
			}

		if( onoff_state == 0 )	onoff_state = 3;

		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "STOP", canstop );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVE", canmove );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "PATROL", canpatrol );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "DEFEND", canguard );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ATTACK", canattack );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "RECLAIM", canreclam );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD", canload );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "UNLOAD", canload );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "REPAIR", builders );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ONOFF", onoffable );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVEORD", canmove );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "FIREORD", canattack );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CAPTURE", cancapture );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CLOAK", cancloak );
		game_area.set_enable_flag( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST", candgun );

		game_area.set_enable_flag( current_gui + ".ARMSTOP", canstop );			// Alternate version to support mods
		game_area.set_enable_flag( current_gui + ".ARMMOVE", canmove );
		game_area.set_enable_flag( current_gui + ".ARMPATROL", canpatrol );
		game_area.set_enable_flag( current_gui + ".ARMDEFEND", canguard );
		game_area.set_enable_flag( current_gui + ".ARMATTACK", canattack );
		game_area.set_enable_flag( current_gui + ".ARMRECLAIM", canreclam );
		game_area.set_enable_flag( current_gui + ".ARMLOAD", canload );
		game_area.set_enable_flag( current_gui + ".ARMUNLOAD", canload );
		game_area.set_enable_flag( current_gui + ".ARMREPAIR", builders );
		game_area.set_enable_flag( current_gui + ".ARMONOFF", onoffable );
		game_area.set_enable_flag( current_gui + ".ARMMOVEORD", canmove );
		game_area.set_enable_flag( current_gui + ".ARMFIREORD", canattack );
		game_area.set_enable_flag( current_gui + ".ARMCAPTURE", cancapture );
		game_area.set_enable_flag( current_gui + ".ARMCLOAK", cancloak );
		game_area.set_enable_flag( current_gui + ".ARMBLAST", candgun );

		GUIOBJ *onoff_gui = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ONOFF");
		if( onoff_gui == NULL )
			onoff_gui = game_area.get_object( current_gui + ".ARMONOFF");

		if( onoff_gui )
			onoff_gui->current_state = onoff_state - 1;

		GUIOBJ *sorder_gui = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "FIREORD");
		if( sorder_gui == NULL )
			sorder_gui = game_area.get_object( current_gui + ".ARMFIREORD");

		if( sorder_gui )
			sorder_gui->current_state = sforder;

		sorder_gui = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVEORD");
		if( sorder_gui == NULL )
			sorder_gui = game_area.get_object( current_gui + ".ARMMOVEORD");

		if( sorder_gui )
			sorder_gui->current_state = smorder;

		if( canload ) {
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD.show" ).c_str(), NULL, NULL );	// Show it
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST.hide" ).c_str(), NULL, NULL );	// Hide it
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".ARMLOAD.show" ).c_str(), NULL, NULL );	// Show it
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".ARMBLAST.hide" ).c_str(), NULL, NULL );	// Hide it
			}
		else {
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD.hide" ).c_str(), NULL, NULL );	// Hide it
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST.show" ).c_str(), NULL, NULL );	// Show it
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".ARMLOAD.hide" ).c_str(), NULL, NULL );	// Hide it
			I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".ARMBLAST.show" ).c_str(), NULL, NULL );	// Show it
			}

		if( current_gui != String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen" ) {
			String gen_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen";

			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "STOP", canstop );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVE", canmove );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "PATROL", canpatrol );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "DEFEND", canguard );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ATTACK", canattack );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "RECLAIM", canreclam );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD", canload );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "UNLOAD", canload );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "REPAIR", builders );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ONOFF", onoffable );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVEORD", canmove );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "FIREORD", canattack );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CAPTURE", cancapture );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CLOAK", cancloak );
			game_area.set_enable_flag( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST", candgun );

			game_area.set_enable_flag( gen_gui + ".ARMSTOP", canstop );
			game_area.set_enable_flag( gen_gui + ".ARMMOVE", canmove );
			game_area.set_enable_flag( gen_gui + ".ARMPATROL", canpatrol );
			game_area.set_enable_flag( gen_gui + ".ARMDEFEND", canguard );
			game_area.set_enable_flag( gen_gui + ".ARMATTACK", canattack );
			game_area.set_enable_flag( gen_gui + ".ARMRECLAIM", canreclam );
			game_area.set_enable_flag( gen_gui + ".ARMLOAD", canload );
			game_area.set_enable_flag( gen_gui + ".ARMUNLOAD", canload );
			game_area.set_enable_flag( gen_gui + ".ARMREPAIR", builders );
			game_area.set_enable_flag( gen_gui + ".ARMONOFF", onoffable );
			game_area.set_enable_flag( gen_gui + ".ARMMOVEORD", canmove );
			game_area.set_enable_flag( gen_gui + ".ARMFIREORD", canattack );
			game_area.set_enable_flag( gen_gui + ".ARMCAPTURE", cancapture );
			game_area.set_enable_flag( gen_gui + ".ARMCLOAK", cancloak );
			game_area.set_enable_flag( gen_gui + ".ARMBLAST", candgun );

			onoff_gui = game_area.get_object( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ONOFF");
			if( onoff_gui == NULL )
				onoff_gui = game_area.get_object( gen_gui + ".ARMONOFF");

			if( onoff_gui )
				onoff_gui->current_state = onoff_state - 1;

			sorder_gui = game_area.get_object( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "FIREORD");
			if( sorder_gui == NULL )
				sorder_gui = game_area.get_object( gen_gui + ".ARMFIREORD");

			if( sorder_gui )
				sorder_gui->current_state = sforder;

			sorder_gui = game_area.get_object( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVEORD");
			if( sorder_gui == NULL )
				sorder_gui = game_area.get_object( gen_gui + ".ARMMOVEORD");

			if( sorder_gui )
				sorder_gui->current_state = smorder;

			if( canload ) {
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD.show" ).c_str(), NULL, NULL );	// Show it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST.hide" ).c_str(), NULL, NULL );	// Hide it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + ".ARMLOAD.show" ).c_str(), NULL, NULL );	// Show it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + ".ARMBLAST.hide" ).c_str(), NULL, NULL );	// Hide it
				}
			else {
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD.hide" ).c_str(), NULL, NULL );	// Hide it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST.show" ).c_str(), NULL, NULL );	// Show it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + ".ARMLOAD.hide" ).c_str(), NULL, NULL );	// Hide it
				I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( gen_gui + ".ARMBLAST.show" ).c_str(), NULL, NULL );	// Show it
				}
			}

		units.LeaveCS_from_outside();

	/*------------------- End of GUI update ---------------------------------------------------------*/
		}
		
	if( current_gui != String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen" && current_gui != "" )		// Show information about units
		units.complete_menu(cur_sel_index, sel != -1 || units.last_on <= -2, false, current_wnd ? current_wnd->build_pic_x : 0, current_wnd ? current_wnd->build_pic_y : 0 );
	else
		units.complete_menu(cur_sel_index, sel != -1 || units.last_on <= -2, true );

	int signal_order = 0;
	features.display_info( -units.last_on - 2 );
	players.show_resources();

	/*------------------- GUI reacting code ---------------------------------------------------------*/

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ORDERS" )
	 || game_area.get_state( current_gui + ".ARMORDERS" ) ) {		// Go to the order menu
		game_area.set_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ORDERS", false );
		game_area.set_state( current_gui + ".ARMORDERS", false );				// Because of mod support
		sound_manager->PlayTDFSound( "ORDERSBUTTON", "sound" , NULL );
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)(current_gui + ".hide").c_str(), NULL, NULL );	// Hide it
		current_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen";
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".show" ).c_str(), NULL, NULL );	// Show it
		}

	if( ( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BUILD" ) ||
	    game_area.get_state( current_gui + ".ARMBUILD" ) ) && old_gui_sel >= 0 )	{	// Back to the build menu
		game_area.set_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BUILD", false );
		game_area.set_state( current_gui + ".ARMBUILD", false );
		sound_manager->PlayTDFSound( "BUILDBUTTON", "sound" , NULL );
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)(current_gui + ".hide").c_str(), NULL, NULL );	// Hide it
		current_gui = String( unit_manager.unit_type[ old_gui_sel ].Unitname ) + "1";
		if( game_area.get_wnd( current_gui ) == NULL ) {
			if( unit_manager.unit_type[ old_gui_sel ].nb_unit > 0 )				// The default build page
				current_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "dl";
			else
				current_gui = String( ta3d_sidedata.side_pref[ players.side_view ] ) + "gen";
			}
		I_Msg( TA3D::TA3D_IM_GUI_MSG, (void*)( current_gui + ".show" ).c_str(), NULL, NULL );	// Show it
		}

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "PREV" )
	|| game_area.get_state( current_gui + ".ARMPREV" ) ) {
		sound_manager->PlayTDFSound( "NEXTBUILDMENU", "sound" , NULL );
		unit_manager.unit_type[ old_gui_sel ].page = (unit_manager.unit_type[ old_gui_sel ].page + ((unit_manager.unit_type[ old_gui_sel ].nb_unit+5)/6)-1)%((unit_manager.unit_type[old_gui_sel].nb_unit+5)/6);
		}
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "NEXT" )
	|| game_area.get_state( current_gui + ".ARMNEXT" ) ) {
		sound_manager->PlayTDFSound( "NEXTBUILDMENU", "sound" , NULL );
		unit_manager.unit_type[ old_gui_sel ].page= (unit_manager.unit_type[ old_gui_sel ].page+1)%((unit_manager.unit_type[ old_gui_sel ].nb_unit+5)/6);
		}

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ONOFF" )
	|| game_area.get_state( current_gui + ".ARMONOFF" ) ) {		// Toggle the on/off value
		signal_order = SIGNAL_ORDER_ONOFF;
		GUIOBJ *onoff_obj = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ONOFF" );
		if( onoff_obj == NULL )
			onoff_obj = game_area.get_object( current_gui + ".ARMONOFF" );
		if( onoff_obj != NULL ) {
			units.EnterCS_from_outside();
			for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
				uint16 i = units.idx_list[e];
				units.LeaveCS_from_outside();
				units.unit[i].Lock();
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel && unit_manager.unit_type[ units.unit[i].type_id ].onoffable ) {
					if( onoff_obj->nb_stages > 1 ) {
						onoff_obj->current_state &= 1;
						if( onoff_obj->current_state == 0 )
							units.unit[i].deactivate();
						else
							units.unit[i].activate();
						}
					else {
						if( units.unit[i].port[ ACTIVATION ] )
							units.unit[i].deactivate();
						else
							units.unit[i].activate();
						}
					}
				units.unit[i].UnLock();
				units.EnterCS_from_outside();
				}
			units.LeaveCS_from_outside();
			}
		}

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CLOAK" )
	|| game_area.get_state( current_gui + ".ARMCLOAK" ) ) {		// Toggle the cloak value
		GUIOBJ *cloak_obj = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CLOAK" );
		if( cloak_obj == NULL )
			cloak_obj = game_area.get_object( current_gui + ".ARMCLOAK" );
		if( cloak_obj != NULL ) {
			units.EnterCS_from_outside();
			for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
				uint16 i = units.idx_list[e];
				units.LeaveCS_from_outside();
				units.unit[i].Lock();
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel && unit_manager.unit_type[ units.unit[i].type_id ].CloakCost > 0 ) {
					if( cloak_obj->nb_stages > 1 ) {
						cloak_obj->current_state &= 1;
						units.unit[i].cloaking = cloak_obj->current_state != 0;
						}
					else
						units.unit[i].cloaking ^= true;
					}
				units.unit[i].UnLock();
				units.EnterCS_from_outside();
				}
			units.LeaveCS_from_outside();
			}
		}

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "FIREORD" )
	|| game_area.get_state( current_gui + ".ARMFIREORD" ) ) {		// Toggle the fireorder value
		sound_manager->PlayTDFSound( "SETFIREORDERS", "sound" , NULL );
		GUIOBJ *sorder_obj = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "FIREORD" );
		if( sorder_obj == NULL )
			sorder_obj = game_area.get_object( current_gui + ".ARMFIREORD" );
		if( sorder_obj != NULL ) {
			sorder_obj->current_state %= 3;
			units.EnterCS_from_outside();
			for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
				uint16 i = units.idx_list[e];
				units.LeaveCS_from_outside();
				units.unit[i].Lock();
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel )
					units.unit[i].port[ STANDINGFIREORDERS ] = sorder_obj->current_state;
				units.unit[i].UnLock();
				units.EnterCS_from_outside();
				}
			units.LeaveCS_from_outside();
			}
		}

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVEORD" )
	|| game_area.get_state( current_gui + ".ARMMOVEORD" ) ) {		// Toggle the moveorder value
		sound_manager->PlayTDFSound( "SETMOVEORDERS", "sound" , NULL );
		GUIOBJ *sorder_obj = game_area.get_object( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVEORD" );
		if( sorder_obj == NULL )
			sorder_obj = game_area.get_object( current_gui + ".ARMMOVEORD" );
		if( sorder_obj != NULL ) {
			sorder_obj->current_state %= 3;
			units.EnterCS_from_outside();
			for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
				uint16 i = units.idx_list[e];
				units.LeaveCS_from_outside();
				units.unit[i].Lock();
				if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel )
					units.unit[i].port[ STANDINGMOVEORDERS ] = sorder_obj->current_state;
				units.unit[i].UnLock();
				units.EnterCS_from_outside();
				}
			units.LeaveCS_from_outside();
			}
		}

	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "MOVE" )
	|| game_area.get_state( current_gui + ".ARMMOVE" ) )														signal_order = SIGNAL_ORDER_MOVE;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "PATROL" )
	|| game_area.get_state( current_gui + ".ARMPATROL" ) )														signal_order = SIGNAL_ORDER_PATROL;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "STOP" )
	|| game_area.get_state( current_gui + ".ARMSTOP" ) )														signal_order = SIGNAL_ORDER_STOP;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "DEFEND" )
	|| game_area.get_state( current_gui + ".ARMDEFEND" ) )														signal_order = SIGNAL_ORDER_GUARD;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "ATTACK" )
	|| game_area.get_state( current_gui + ".ARMATTACK" ) )														signal_order = SIGNAL_ORDER_ATTACK;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "RECLAIM" )
	|| game_area.get_state( current_gui + ".ARMRECLAIM" ) )														signal_order = SIGNAL_ORDER_RECLAM;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "LOAD" )
	|| game_area.get_state( current_gui + ".ARMLOAD" ) )														signal_order = SIGNAL_ORDER_LOAD;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "UNLOAD" )
	|| game_area.get_state( current_gui + ".ARMUNLOAD" ) )														signal_order = SIGNAL_ORDER_UNLOAD;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "REPAIR" )
	|| game_area.get_state( current_gui + ".ARMREPAIR" ) )														signal_order = SIGNAL_ORDER_REPAIR;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "CAPTURE" )
	|| game_area.get_state( current_gui + ".ARMCAPTURE" ) )														signal_order = SIGNAL_ORDER_CAPTURE;
	if( game_area.get_state( current_gui + "." + ta3d_sidedata.side_pref[ players.side_view ] + "BLAST" )
	|| game_area.get_state( current_gui + ".ARMBLAST" ) )														signal_order = SIGNAL_ORDER_DGUN;

	/*------------------- End of GUI reacting code --------------------------------------------------*/

	switch(signal_order)
	{
	case SIGNAL_ORDER_CAPTURE:
	case SIGNAL_ORDER_DGUN:
	case SIGNAL_ORDER_MOVE:
	case SIGNAL_ORDER_PATROL:
	case SIGNAL_ORDER_GUARD:
	case SIGNAL_ORDER_ATTACK:
	case SIGNAL_ORDER_RECLAM:
	case SIGNAL_ORDER_LOAD:
	case SIGNAL_ORDER_UNLOAD:
	case SIGNAL_ORDER_REPAIR:
		sound_manager->PlayTDFSound( "IMMEDIATEORDERS", "sound" , NULL );
		current_order = signal_order;
		break;
	case SIGNAL_ORDER_STOP:
		sound_manager->PlayTDFSound( "IMMEDIATEORDERS", "sound" , NULL );
		units.EnterCS_from_outside();
		for( uint16 e = 0 ; e < units.index_list_size ; e++ ) {
			uint16 i = units.idx_list[e];
			units.LeaveCS_from_outside();
			units.unit[i].Lock();
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id == players.local_human_id && units.unit[i].sel && unit_manager.unit_type[ units.unit[i].type_id ].canstop )
				units.unit[i].set_mission(MISSION_STOP);
			units.unit[i].UnLock();
			units.EnterCS_from_outside();
			}
		units.LeaveCS_from_outside();
	case SIGNAL_ORDER_ONOFF:
		current_order=SIGNAL_ORDER_NONE;
		break;
	};
	if(sel>=0 || (mouse_b&2))
		current_order=SIGNAL_ORDER_NONE;

	if(sel>=0 && mouse_b==2 && omb2!=2) {
		units.unit[cur_sel_index].Lock();
		MISSION *cur=units.unit[cur_sel_index].mission;
		MISSION **old=&(units.unit[cur_sel_index].mission);
		int nb=1;
		if(TA3D_SHIFT_PRESSED) nb=5;
		if(cur)	{	old=&(cur->next);	cur=cur->next; }
		if(cur)
			while(cur) {
				if((cur->mission==MISSION_BUILD && cur->data==sel) || cur->mission==MISSION_STOP) {			// Efface un ordre
					if(cur->mission==MISSION_BUILD)
						nb--;
					*old=cur->next;
					free(cur);
					cur=*old;
					if(nb==0)
						break;
					continue;
					}
				old=&(cur->next);
				cur=cur->next;
				}
		cur=units.unit[cur_sel_index].mission;
		if(nb>0 && cur!=NULL && cur->mission==MISSION_BUILD_2 && cur->data==sel) {
			sint32 prev = -1;
			for(i = units.nb_unit-1; i>=0 ; i--)
				if(units.idx_list[i] == ((UNIT*)(units.unit[cur_sel_index].mission->p))->idx) {
					prev = i;
					break;
					}
			if( prev >= 0 )
				units.kill(((UNIT*)(units.unit[cur_sel_index].mission->p))->idx,map,prev);
			units.unit[cur_sel_index].next_mission();
			}
		units.unit[cur_sel_index].UnLock();
		}
	if(sel>=0 && mouse_b==1 && omb2!=1) {
		build=sel;
		sound_manager->PlayTDFSound( "ADDBUILD", "sound" , NULL );
		if(!unit_manager.unit_type[cur_sel].BMcode) {			// S'il s'agit d'un bâtiment
			if(TA3D_SHIFT_PRESSED)
				for(i=0;i<5;i++)
					units.give_order_build(players.local_human_id,build,units.unit[cur_sel_index].Pos,false);
			else
				units.give_order_build(players.local_human_id,build,units.unit[cur_sel_index].Pos,false);
			build=-1;
			}
		}

	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	gfx->drawtexture(freecam ? freecam_on : freecam_off,32,SCREEN_H-64,95,SCREEN_H);
	glDisable(GL_BLEND);

	if(mouse_x>=32 && mouse_x<=95 && mouse_y>=SCREEN_H-64 && omb2==0) {
		if(mouse_b==1) {
			freecam^=true;
			if(!freecam)	r2=0.0f;
			}
		else if(mouse_b==2 && !freecam) {				// Reset default view
			camera_zscroll = -0.00001f;
			r1 = -lp_CONFIG->camera_def_angle - 0.00001f;
			}
		}

	omb2=mouse_b;

	int last_on = units.last_on;

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

	if( !freecam && mouse_b == 4 ) {			// Moving the cam around
		gfx->set_alpha_blending();
		gfx->set_color( 0xFFFFFFFF );
		gfx->drawtexture( circle_texture, gfx->width * 0.45f, gfx->height * 0.45f, gfx->width * 0.55f, gfx->height * 0.55f );
		gfx->drawtexture( arrow_texture, gfx->width * 0.4f, 0.0f, gfx->width * 0.6f, gfx->height * 0.05f );
		gfx->drawtexture( arrow_texture, gfx->width * 0.4f, gfx->height, gfx->width * 0.6f, gfx->height * 0.95f );
		gfx->drawtexture_flip( arrow_texture, 0.0f, gfx->height * 0.4f, gfx->width * 0.05f, gfx->height * 0.6f );
		gfx->drawtexture_flip( arrow_texture, gfx->width, gfx->height * 0.4f, gfx->width * 0.95f, gfx->height * 0.6f );
		gfx->unset_alpha_blending();
		}

	if(view_dbg) {
		glDisable(GL_TEXTURE_2D);
		glColor3f(1.0f,1.0f,1.0f);
		glBegin(GL_POINTS);
		float rw=128.0f*map->mini_w/252/map->bloc_w;
		float rh=128.0f*map->mini_h/252/map->bloc_h;
		float dw=64.0f-0.5f*map->bloc_w*rw;
		float dh=64.0f-0.5f*map->bloc_h*rh;
		for(int y=0;y<map->bloc_h;y++)
			for(int x=0;x<map->bloc_w;x++)
				if(map->view[y][x])
					glVertex2f(x*rw+dw,y*rh+dh);
		glEnd();
		glEnable(GL_TEXTURE_2D);
		}

	glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
	glEnable(GL_BLEND);
	if(show_script)
	if(cur_sel>=0 && unit_manager.unit_type[cur_sel].script) {
		float Y=32.0f;
		gfx->print(gfx->normal_font,128.0f,Y,0.0f,0xFFFFFFFF,format("%d scripts",unit_manager.unit_type[cur_sel].script->nb_script));	Y+=9.0f;
		for(i=0;i<unit_manager.unit_type[cur_sel].script->nb_script;i++) {
			if(units.unit[cur_sel_index].is_running(i))
				gfx->print(gfx->normal_font,128.0f,Y,0.0f,0xFFFFFFFF,format("%d %s (on)",i,unit_manager.unit_type[cur_sel].script->name[i]));
			else
				gfx->print(gfx->normal_font,128.0f,Y,0.0f,0xFFFFFFFF,format("%d %s (off)",i,unit_manager.unit_type[cur_sel].script->name[i]));
			Y+=9.0f;
			}
		}

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

	if(internal_name && last_on >= 0 && unit_manager.unit_type[ units.unit[ last_on ].type_id ].Unitname!=NULL)
		gfx->print(gfx->normal_font,128.0f,32.0f,0.0f,0xFFFFFFFF,format("internal name %s",unit_manager.unit_type[ units.unit[ last_on ].type_id ].Unitname));
	else if(internal_name && cur_sel >= 0 && unit_manager.unit_type[cur_sel].Unitname!=NULL)
		gfx->print(gfx->normal_font,128.0f,32.0f,0.0f,0xFFFFFFFF,format("internal name %s",unit_manager.unit_type[cur_sel].Unitname));

	if( internal_idx && last_on >= 0 )
		gfx->print(gfx->normal_font,128.0f,32.0f,0.0f,0xFFFFFFFF,format("idx = %d", last_on));
	else if( internal_idx && cur_sel_index >= 0 )
		gfx->print(gfx->normal_font,128.0f,32.0f,0.0f,0xFFFFFFFF,format("idx = %d", cur_sel_index));

	if(unit_info>0.0f && unit_info_id>=0)
		unit_manager.unit_type[unit_info_id].show_info(unit_info,gfx->TA_font);

	if(selected && cur_sel!=-1 && show_mission_info ) {					// Sur les unités sélectionnées
		char *unit_info[]={"MISSION_STANDBY","MISSION_VTOL_STANDBY","MISSION_GUARD_NOMOVE","MISSION_MOVE","MISSION_BUILD","MISSION_BUILD_2","MISSION_STOP","MISSION_REPAIR","MISSION_ATTACK",
							"MISSION_PATROL","MISSION_GUARD","MISSION_RECLAIM","MISSION_LOAD","MISSION_UNLOAD","MISSION_STANDBY_MINE"};
		float y = 32.0f;
		for( i = 0; i < units.max_unit ; i++ ) {
			units.unit[ i ].Lock();
			if( (units.unit[i].flags & 1) && units.unit[i].owner_id==0 && units.unit[i].sel) {
				if(units.unit[i].mission!=NULL && units.unit[i].mission->mission<=0x0E) {
					gfx->print(gfx->normal_font,128.0f,y,0.0f,0xFFFFFFFF,format("MISSION: %s",unit_info[units.unit[i].mission->mission]));
					String flags = "";
					if( units.unit[i].mission->flags & MISSION_FLAG_CAN_ATTACK )	flags += "CAN_ATTACK; ";
					if( units.unit[i].mission->flags & MISSION_FLAG_SEARCH_PATH )	flags += "SEARCH_PATH; ";
					if( units.unit[i].mission->flags & MISSION_FLAG_TARGET_WEAPON )	flags += "TARGET_WEAPON; ";
					if( units.unit[i].mission->flags & MISSION_FLAG_COMMAND_FIRE )	flags += "COMMAND_FIRE; ";
					if( units.unit[i].mission->flags & MISSION_FLAG_MOVE )	flags += "MOVE; ";
					if( units.unit[i].mission->flags & MISSION_FLAG_REFRESH_PATH )	flags += "REFRESH_PATH; ";
					y += gfx->normal_font.height();
					gfx->print(gfx->normal_font,128.0f,y,0.0f,0xFFFFFFFF,format("FLAGS: %s", flags.c_str()));
					}
				else
					gfx->print(gfx->normal_font,128.0f,y,0.0f,0xFFFFFFFF,format("MISSION: NONE"));
				y += gfx->normal_font.height();
				}
			units.unit[ i ].UnLock();
			}
		}

	glDisable(GL_BLEND);

	if( show_timefactor > 0.0f ) {
		String value = format("x %f", lp_CONFIG->timefactor);
		if( value.find( "." ) != -1 )
			value.resize( value.find( "." ) + 2 );
		if( show_timefactor > 0.5f )
			gfx->print( gfx->TA_font, gfx->width - (int)gfx->TA_font.length( value )>>1, SCREEN_H-80, 0.0f, 0xFFFFFFFF, value );
		else {
			uint32 c = (uint32)(511.0f * show_timefactor) * 0x01010101;
			gfx->print( gfx->TA_font, gfx->width - (int)gfx->TA_font.length( value )>>1, SCREEN_H-80, 0.0f, c, value );
			}
		show_timefactor -= dt;
		}

	char *cmd=NULL;
	if(!shoot || video_shoot)
		cmd=Console->draw(gfx->TA_font,dt,gfx->TA_font.height());			// Affiche la console

	float Y=0.0f;

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

	if(lp_CONFIG->showfps) {
		gfx->print(gfx->TA_font,0.0f,Y,0.0f,0xFFFFFFFF,format("fps: %d",fps));
		Y+=9.0f;
		}

	glDisable(GL_BLEND);

	if( mouse_b!=4 || TA3D_CTRL_PRESSED )
		draw_cursor();

	if(shoot) {
		BITMAP *shoot_bmp = create_bitmap_ex(32,SCREEN_W,SCREEN_H);
		blit(screen,shoot_bmp,0,0,0,0,SCREEN_W,SCREEN_H);
		char nom[100];
		nom[0]=0;
		strcat(nom,"shoot000000");
		nom[strlen(nom)-6]+=(nb_shoot/100000)%10;
		nom[strlen(nom)-5]+=(nb_shoot/10000)%10;
		nom[strlen(nom)-4]+=(nb_shoot/1000)%10;
		nom[strlen(nom)-3]+=(nb_shoot/100)%10;
		nom[strlen(nom)-2]+=(nb_shoot/10)%10;
		nom[strlen(nom)-1]+=nb_shoot%10;
		nb_shoot = (nb_shoot+1)%1000000;
		strcat(nom,".jpg");
		save_jpg_ex( (TA3D_OUTPUT_DIR + nom).c_str(),shoot_bmp,NULL,75,JPG_SAMPLING_411,NULL);
		destroy_bitmap(shoot_bmp);
		shoot=false;
		}

	gfx->unset_2D_mode();

	gfx->flip();

	if(cmd) {		// Analyse les commandes tapées dans la console
		Vector< String > params = ReadVectorString( cmd, " " );
		if( params[0] == "fps_on" ) lp_CONFIG->showfps=true;				// Affiche le nombre d'images/seconde
		else if( params[0] == "fps_off" ) lp_CONFIG->showfps=false;				// Cache le nombre d'images/seconde
		else if( params[0] == "zshoot" ) {									// Prend une capture d'écran(tampon de profondeur seulement)
			BITMAP *bmp=create_bitmap_ex(32,SCREEN_W,SCREEN_H);
			clear(bmp);
			glReadPixels(0,0,SCREEN_W,SCREEN_H,GL_DEPTH_COMPONENT,GL_INT,bmp->line[0]);
			save_bitmap( (TA3D_OUTPUT_DIR + "z.tga").c_str(),bmp,NULL);
			destroy_bitmap(bmp);
			}
		else if( params[0] == "video" && params[1] == "shoot" ) video_shoot ^= true;		// Capture video
		else if( params[0] == "shoot" ) shoot = true;					// Prend une capture d'écran
		else if( params[0] == "reload" && params[1] == "shaders" && g_useProgram) {
			water_shader.destroy();
			water_shader.load("shaders/water.frag","shaders/water.vert");
			water_shader_reflec.destroy();
			water_shader_reflec.load("shaders/water_reflec.frag","shaders/water.vert");
			water_pass1.destroy();
			water_pass1.load("shaders/water_pass1.frag","shaders/water_pass1.vert");
			water_pass1_low.destroy();
			water_pass1_low.load("shaders/water_pass1_low.frag","shaders/water_pass1.vert");
			water_pass2.destroy();
			water_pass2.load("shaders/water_pass2.frag","shaders/water_pass2.vert");
			map->detail_shader.destroy();
			map->detail_shader.load( "shaders/details.frag", "shaders/details.vert" );
			}
		else if( params[0] == "show" && params[1] == "mission" && params[2] == "info" )	show_mission_info ^= true;
		else if( params[0] == "view" && params[1] == "debug" )	view_dbg^=true;
		else if( params[0] == "ia" && params[1] == "debug" )	ia_debug^=true;
		else if( params[0] == "internal" && params[1] == "name" ) internal_name^=true;		// Affiche/Cache le nom interne de l'unité
		else if( params[0] == "internal" && params[1] == "idx" ) internal_idx^=true;		// Show/Hide unit indexes in unit array
		else if( params[0] == "exit" ) done=true;					// Quitte le programme
		else if( params[0] == "wireframe" ) 	lp_CONFIG->wireframe^=true;
		else if( params[0] == "priority" && params.size() == 2 ) lp_CONFIG->priority_level = atoi( params[1].c_str() );
		else if( params[0] == "water" && params[1] == "quality" &&  params.size() == 3 ) lp_CONFIG->water_quality = atoi( params[2].c_str() )%5;
		else if( params[0] == "shadow" && params.size() == 3) {
			if( params[1] == "quality" ) lp_CONFIG->shadow_quality = atoi( params[2].c_str() );
			else if( params[1] == "ray" ) lp_CONFIG->shadow_r = atof( params[2].c_str() );
			}
		else if( params[0] == "shadow" && params.size() == 1 ) lp_CONFIG->shadow^=true;
		else if( params[0] == "details" )	lp_CONFIG->detail_tex ^= true;
		else if( params[0] == "particle" )	lp_CONFIG->particle^=true;
		else if( params[0] == "waves" ) lp_CONFIG->waves^=true;
		else if( params[0] == "show" && params[1] == "script" ) show_script^=true;
		else if( params[0] == "show" && params[1] == "model" ) show_model^=true;
		else if( params[0] == "rotate" && params[1] == "light" ) rotate_light^=true;
		else if( params[0] == "shake" )
			cam.set_shake( 1.0f, 32.0f );
		else if( params[0] == "freecam" ) {
			freecam^=true;
			if(!freecam)	r2=0.0f;
			}
		else if( params[0] == "fps_limit" && params.size() == 2 ) {
			speed_limit = atoi( params[1].c_str() );
			if(speed_limit==0.0f) speed_limit=-1.0f;
			delay=1.0f/speed_limit;
			}
		else if( params[0] == "spawn" && params.size() >= 2 ) {
			int unit_type = -1;
			int player_id = players.local_human_id;
			int nb_to_spawn=1;
			unit_type = unit_manager.get_unit_index( params[1].c_str() );
			if( params.size() >= 3 ) {
				player_id = atoi( params[2].c_str() );
				if( params.size() >= 4 )
					nb_to_spawn = atoi( params[3].c_str() );
				}
			ThreadSynchroniser->EnterSync();		// Make sure we won't destroy something we mustn't
			units.EnterCS_from_outside();
			for(i=0;i<nb_to_spawn;i++) {
				int id=0;
				if( unit_type < 0 || unit_type >= unit_manager.nb_unit )
					id = units.create( abs( TA3D_RAND() ) % unit_manager.nb_unit,player_id);
				else
					id = units.create(unit_type,player_id);
				if( id == -1 )	break;		// Ho ho no more space to store that unit, limit reached
				units.unit[ id ].Lock();
				int e=0;

				do {
					units.unit[id].Pos.x=(TA3D_RAND()%map->map_w)-map->map_w_d;
					units.unit[id].Pos.z=(TA3D_RAND()%map->map_h)-map->map_h_d;
					e++;
				}while(e<100 && !can_be_built( units.unit[id].Pos, map, units.unit[id].type_id, player_id ));
				if(!can_be_built( units.unit[id].Pos, map, units.unit[id].type_id, player_id )) {
					units.unit[id].flags = 4;
					units.kill(id,map,units.idx_list[units.index_list_size-1]);
					}
				else {								// Compute unit's Y coordinate
					VECTOR target_pos = units.unit[id].Pos;
					target_pos.x=((int)(target_pos.x)+map->map_w_d)>>3;
					target_pos.z=((int)(target_pos.z)+map->map_h_d)>>3;
					target_pos.y=max(map->get_max_rect_h((int)target_pos.x,(int)target_pos.z, unit_manager.unit_type[ units.unit[id].type_id ].FootprintX, unit_manager.unit_type[ units.unit[id].type_id ].FootprintZ ),map->sealvl);
					units.unit[id].Pos.y = target_pos.y;
					units.unit[id].cur_px = (int)target_pos.x;
					units.unit[id].cur_py = (int)target_pos.z;
					units.unit[id].draw_on_map();

					if(unit_manager.unit_type[units.unit[id].type_id].ActivateWhenBuilt ) {		// Start activated
						units.unit[id].port[ ACTIVATION ] = 0;
						units.unit[id].activate();
						}
					if(unit_manager.unit_type[units.unit[id].type_id].init_cloaked )				// Start cloaked
						units.unit[id].cloaking = true;
					}
				units.unit[ id ].UnLock();
				}
			units.LeaveCS_from_outside();
			ThreadSynchroniser->LeaveSync();
			}
		else if( params[0] == "timefactor" && params.size() == 2)
			lp_CONFIG->timefactor = atof( params[1].c_str() );
		else if( params[0] == "addhp" && params.size() == 2) {
			if(selected) {					// Sur les unités sélectionnées
				int value = atoi( params[1].c_str() );
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel) {
						units.unit[i].hp+=value;
						if(units.unit[i].hp<0)
							units.unit[i].hp=0;
						else if(units.unit[i].hp>unit_manager.unit_type[units.unit[i].type_id].MaxDamage)
							units.unit[i].hp=unit_manager.unit_type[units.unit[i].type_id].MaxDamage;
						}
					}
				}
			}
		else if( params[0] == "deactivate" ) {
			if(selected)					// Sur les unités sélectionnées
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
						units.unit[i].deactivate();
					}
			}
		else if( params[0] == "activate" ) {
			if(selected)					// Sur les unités sélectionnées
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
						units.unit[i].activate();
					}
			}
		else if( params[0] == "reset_script" ) {							// Réinitialise les scripts
			if(selected) {					// Sur les unités sélectionnées
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
						units.unit[i].reset_script();
					}
				}
			}
		else if( params[0] == "unitinfo" ) {
			if(selected && cur_sel!=-1) {					// Sur les unités sélectionnées
				char *unit_info[]={"ACTIVATION","STANDINGMOVEORDERS","STANDINGFIREORDERS","HEALTH","INBUILDSTANCE","BUSY","PIECE_XZ","PIECE_Y","UNIT_XZ","UNIT_Y","UNIT_HEIGHT","XZ_ATAN","XZ_HYPOT","ATAN",
								   "HYPOT","GROUND_HEIGHT","BUILD_PERCENT_LEFT","YARD_OPEN","BUGGER_OFF","ARMORED"};
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel) {
						printf("flags=%d\n", units.unit[i].flags);
						for(int f=1;f<21;f++)
							printf("%s=%d\n",unit_info[f-1],units.unit[i].port[f]);
						}
					}
				}
			}
		else if( params[0] == "kill" ) {							// Détruit les unités sélectionnées
			if(selected) {					// Sur les unités sélectionnées
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
						units.kill(i,map,e);
					}
				selected=false;
				cur_sel=-1;
				}
			}
		else if( params[0] == "script" && params[1].size() == 2) {							// Force l'éxecution d'un script
			if(selected) {					// Sur les unités sélectionnées
				int id = atoi( params[1].c_str() );
				for(uint16 e=0;e<units.index_list_size;e++) {
					i = units.idx_list[e];
					if( (units.unit[i].flags & 1) && units.unit[i].owner_id==players.local_human_id && units.unit[i].sel)
						units.unit[i].launch_script(id);
					}
				}
			}
		else if( params[0] == "give" ) {							// Give resources to player_id
			bool success = false;
			if( params.size() == 4 ) {
				int player_id = atoi( params[2].c_str() );
				int amount = atoi( params[3].c_str() );
				if( player_id >= 0 && player_id < players.nb_player ) {
					if( params[1] == "metal" || params[1] == "both" ) {
						players.metal[ player_id ] = players.c_metal[ player_id ] = players.c_metal[ player_id ] + amount;					// cheat codes
						success = true;
						}
					if( params[1] == "energy" || params[1] == "both" ) {
						players.energy[ player_id ] = players.c_energy[ player_id ] = players.c_energy[ player_id ] + amount;					// cheat codes
						success = true;
						}
					}
				}
			if( !success ) {
				Console->AddEntry("command error:");
				Console->AddEntry("correct syntax is: give metal/energy/both player_id amount");
				}
			}
		else if( params[0] == "metal" ) cheat_metal^=true;				// cheat codes
		else if( params[0] == "energy" ) cheat_energy^=true;			// cheat codes
		params.clear();
		free(cmd);
		}
	if(cheat_metal)
		players.metal[players.local_human_id]=players.c_metal[players.local_human_id]=players.metal_s[players.local_human_id];					// cheat codes
	if(cheat_energy)
		players.energy[players.local_human_id]=players.c_energy[players.local_human_id]=players.energy_s[players.local_human_id];				// cheat codes
	if(key[KEY_F12])
		shoot=true;

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

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

}while(!done);

reset_mouse();

delete sky_data;

players.DestroyThread();				// Shut down the Players thread

players.stop_threads();

weapons.DestroyThread();				// Shut down the Weapon Engine

units.DestroyThread();					// Shut down the Unit Engine

particle_engine.DestroyThread();		// Shut down the Particle Engine

water_obj.destroy();
sky_obj.destroy();

game_cam = NULL;

if(g_useProgram && g_useFBO && map->water) {
	glDeleteFramebuffersEXT(1,&water_FBO);
	water_pass1.destroy();
	water_pass1_low.destroy();
	water_pass2.destroy();
	water_shader.destroy();
	water_shader_reflec.destroy();
	gfx->destroy_texture( water_color );
	gfx->destroy_texture( first_pass );
	gfx->destroy_texture( second_pass );
	gfx->destroy_texture( reflectex );
	gfx->destroy_texture( transtex );
	}

gfx->destroy_texture( freecam_on );
gfx->destroy_texture( freecam_off );
gfx->destroy_texture( sky );
gfx->destroy_texture( water );
gfx->destroy_texture( glow );
gfx->destroy_texture( arrow_texture );
gfx->destroy_texture( circle_texture );

printf("nombre de models chargés: %d \n",model_manager.nb_models);
printf("nombre d'unités chargées: %d \n",unit_manager.nb_unit);
printf("nombre de textures chargées: %d \n",texture_manager.nbtex);

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

Console->AddEntry("freeing memory used for the map");
map->destroy();
delete map;
Console->AddEntry("freeing memory used for 3d models");
model_manager.destroy();
Console->AddEntry("freeing memory used for particle engine");
particle_engine.destroy();
Console->AddEntry("freeing memory used for ingame units");
units.destroy();
Console->AddEntry("freeing memory used for units");
unit_manager.destroy();
Console->AddEntry("freeing memory used for weapons");
weapons.destroy();
Console->AddEntry("freeing memory used for sound");
sound_manager->StopMusic();
Console->AddEntry("freeing memory used for fx");
fx_manager.destroy();

Console->AddEntry("freeing memory used for weapon manager");
weapon_manager.destroy();

Console->AddEntry("freeing memory used for features");
feature_manager.destroy();

Console->AddEntry("freeing memory used for ingame features");
features.destroy();

Console->AddEntry("freeing memory used for textures");
texture_manager.destroy();

stats_menu();

Console->AddEntry("freeing memory used for players");
players.destroy();

delete HPIManager;
HPIManager=new cHPIHandler("");

}

int anim_cursor(int type)
{
	if(type==-1)
		return ((msec_timer-start)/100)%cursor.anm[cursor_type].nb_bmp;
	else
		return ((msec_timer-start)/100)%cursor.anm[type].nb_bmp;
}

void draw_cursor(float size_w, float size_h)
{
	int curseur=anim_cursor();
	if(curseur<0 || curseur>=cursor.anm[cursor_type].nb_bmp) {
		curseur=0;
		start=msec_timer;
		}
	float dx=cursor.anm[cursor_type].ofs_x[curseur]*size_w;
	float dy=cursor.anm[cursor_type].ofs_y[curseur]*size_h;
	float sx=cursor.anm[cursor_type].bmp[curseur]->w*size_w;
	float sy=cursor.anm[cursor_type].bmp[curseur]->h*size_h;
	gfx->set_color( 0xFFFFFFFF );
	gfx->set_alpha_blending();
	gfx->drawtexture( cursor.anm[cursor_type].glbmp[curseur], mouse_x*gfx->SCREEN_W_TO_640-dx, mouse_y*gfx->SCREEN_H_TO_480-dy, mouse_x*gfx->SCREEN_W_TO_640-dx+sx, mouse_y*gfx->SCREEN_H_TO_480-dy+sy);
	gfx->unset_alpha_blending();
}

VECTOR cursor_on_map(CAMERA *cam,MAP *map, bool on_mini_map )
{
	if( on_mini_map ) {			// If the cursor is on the mini_map;
		VECTOR map_pos;
		map_pos.x = (mouse_x - 64) * 252.0f / 128.0f * map->map_w / map->mini_w;
		map_pos.z = (mouse_y - 64) * 252.0f / 128.0f * map->map_h / map->mini_h;
		map_pos.y = map->get_unit_h( map_pos.x, map_pos.z );
		return map_pos;
		}
	else {
		VECTOR cur_dir;
		cur_dir=cam->Dir+cam->width_factor*2.0f*(mouse_x-gfx->SCREEN_W_HALF)*gfx->SCREEN_W_INV*cam->Side-1.5f*(mouse_y-gfx->SCREEN_H_HALF)*gfx->SCREEN_H_INV*cam->Up;
		cur_dir.Unit();		// Direction pointée par le curseur
		return map->hit(cam->Pos,cur_dir,true,2000000000.0f,true);
		}
}
