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

/*----------------------------------------------------------------------\
|                                 script.h                              |
|      contient les classes nécessaires à la gestion des scripts de     |
| controle du déroulement de la partie. Les scripts peuvent influencer  |
| considérablement le déroulement de la partie en manipulant les unités |
| les ressources mais aussi l'écran et déclenche les signaux de défaite |
| et de victoire.                                                       |
\----------------------------------------------------------------------*/

#include <string.h>
#include <stdio.h>
#include <math.h>

#ifndef CLASSE_SCRIPT

#define CLASSE_SCRIPT

typedef unsigned char byte;

#define ASM_CODE_VAR			0x0				// Désigne une variable
#define ASM_CODE_REG			0x1				// Désigne un registre
#define ASM_CODE_VAL			0x2				// Désigne une constante
#define ASM_CODE_INT			0x3				// Désigne un entier
#define ASM_CODE_FLT			0x4				// Désigne un flottant
#define ASM_CODE_STR			0x5				// Désigne une chaîne

#define ASM_MOV					0x00			// Copie une valeur
#define ASM_ADD					0x01			// Somme
#define ASM_SUB					0x02			// Différence
#define ASM_MUL					0x03			// Produit
#define ASM_DIV					0x04			// Division
#define ASM_IF					0x05			// Condition
#define ASM_JMP					0x06			// Saut
#define ASM_CALL				0x07			// Appel une fonction
#define ASM_NEW					0x08			// Crée une variable
#define ASM_INC					0x09			// Incrémente une variable
#define ASM_DEC					0x0A			// Décrémente une variable
#define ASM_PUSH				0x0B			// Empile un registre		(Pour les calculs)
#define ASM_POP					0x0C			// Dépile un registre
#define ASM_CLEAR				0x0D			// Vide la pile	(fin de traitement d'une ligne si besoin)
#define ASM_END					0x0E			// Fin du programme
#define ASM_MOD					0x0F			// Modulo
#define ASM_AND					0x10			// Et
#define ASM_OR					0x11			// Ou inclusif
#define ASM_XOR					0x12			// Ou exclusif
#define ASM_IMP					0x13			// Implication
#define ASM_EQV					0x14			// Equivalence
#define ASM_NOT					0x15			// Non
#define ASM_EQUAL				0x16			// =
#define ASM_LEQUAL				0x17			// <=
#define ASM_GEQUAL				0x18			// >=
#define ASM_LESS				0x19			// <
#define ASM_GREATER				0x1A			// >
#define ASM_NEQUAL				0x1B			// !=
#define ASM_PRINT				0x1C			// Affiche une variable
#define ASM_LOGMSG				0x1D			// Affiche du texte dans la console
#define ASM_LINE				0x1E			// Trace une ligne
#define ASM_POINT				0x1F			// Dessine un point
#define ASM_TRIANGLE			0x20			// Dessine un triangle
#define ASM_SIGNAL				0x21			// Envoie un signal à TA3D (quitter la partie,victoire,défaite,...)
#define ASM_LOCATE				0x22			// Déplace le curseur texte
#define ASM_COLOR				0x23			// Modifie la couleur du texte
#define ASM_CLS					0x24			// Efface l'écran
#define ASM_BOX					0x25			// Rectangle
#define ASM_FILLBOX				0x26			// Rectangle plein
#define ASM_CIRCLE				0x27			// Cercle
#define ASM_SLEEP				0x28			// Fait une pause
#define ASM_WAIT				0x29			// Attent une action utilisateur (souris/clavier)
#define ASM_EXP					0x2A			// Exponentielle
#define ASM_LOG					0x2B			// Logarithme
#define ASM_SQRT				0x2C			// Racine carrée
#define ASM_COS					0x2D			// Cosinus
#define ASM_SIN					0x2E			// Sinus
#define ASM_RAND				0x2F			// Générateur de nombres aléatoires
#define ASM_ABS					0x30			// Valeur absolue
#define ASM_TAN					0x31
#define ASM_COTAN				0x32
#define ASM_SH					0x33
#define ASM_CH					0x34
#define ASM_TH					0x35
#define ASM_COTH				0x36
#define ASM_ACOS				0x37
#define ASM_ASIN				0x38
#define ASM_ATAN				0x39
#define ASM_ACH					0x3A
#define ASM_ASH					0x3B
#define ASM_ATH					0x3C
#define ASM_MOUSE_X				0x3D			// Coordonnées du curseur
#define ASM_MOUSE_Y				0x3E
#define ASM_MOUSE_Z				0x3F
#define ASM_MOUSE_B				0x40			// Boutons de la souris
#define ASM_GET_KEY				0x41			// Lit la touche enfoncée
#define ASM_PUSH_VAR			0x42			// Empile une variable
#define ASM_PUSH_VAL			0x43			// Empile une constante
#define ASM_INT					0x44			// Convertit en entier
#define ASM_AFFECT				0x45			// Affectation du dessus de la pile à une variable
#define ASM_IF_NOT				0x46			// Condition inversée
#define ASM_NEXT				0x47			// Condensé de l'instruction NEXT
#define ASM_IF_LESS				0x48			// Condensé d'une partie de l'instruction FOR
#define ASM_PUSH_VARS			0x49			// Empile plusieurs variables
#define ASM_TIME				0x4A			// Renvoie le durée en secondes d'éxécution du programme
#define ASM_PUSH_VALS			0x4B			// Empile plusieurs constantes
#define ASM_PUT					0x4C			// Affiche un bitmap à l'écran
#define ASM_GET					0x4D			// Enregistre une portion de l'écran dans un bitmap
#define ASM_LOAD				0x4E			// Charge un bitmap
#define ASM_SAVE				0x4F			// Enregistre un bitmap
#define ASM_DESTROY				0x50			// Détruit un bitmap
#define ASM_CREATE				0x51			// Crée un bitmap
#define ASM_ARRAY_PUSH			0x52			// Empile une valeur d'un tableau
#define ASM_ARRAY_AFFECT		0x53			// Affectation à un élément de tableau
#define ASM_NB_PLAYER			0x54			// Renvoie le nombre de joueurs
#define ASM_GET_UNIT_NUMBER_FOR_PLAYER	0x55	// Renvoie le nombre d'unité du joueur dont l'identifiant est sur la pile
#define ASM_GET_UNIT_OWNER		0x56			// Renvoie le propriétaire de l'unité dont l'identifiant est sur la pile
#define ASM_GET_UNIT_NUMBER		0x57			// Renvoie le nombre total d'unités
#define ASM_ANNIHILATED			0x58			// Indique si le joueur dont l'identifiant est sur la pile a été annihilé
#define ASM_HAS_UNIT			0x59			// Indique si le joueur courant a encore un type d'unité dont l'identifiant ou le nom est sur la pile
#define ASM_SET_CURRENT_PLAYER	0x5A			// Modifie le joueur courant
#define ASM_CREATE_UNIT			0x5B			// Crée une unité pour le joueur courant
#define ASM_CHANGE_UNIT_OWNER	0x5C			// Change le propriétaire de l'unité dont l'identifiant est sur la pile
#define ASM_MOVE_UNIT			0x5D			// Déplace l'unité dont l'identifiant est sur la pile
#define ASM_KILL_UNIT			0x5E			// Tue l'unité dont l'identifiant est sur la pile
#define ASM_KICK_UNIT			0x5F			// Inflige des dommages à une unité (toutes les infos sont sur la pile: dégats et ID de l'unité)
#define ASM_PLAY				0x60			// Joue le fichier son dont le nom est sur la pile
#define ASM_MAP_W				0x61			// Renvoie la largeur de la carte
#define ASM_MAP_H				0x62			// Renvoie la hauteur de la carte
#define ASM_PLAYER_SIDE			0x63			// Renvoie le camp du joueur dont l'identifiant est sur la pile

#define WORD_TYPE_NONE			0x0			// Inconnu
#define WORD_TYPE_OP			0x1			// Opérateur
#define WORD_TYPE_VAL			0x2			// Valeur
#define WORD_TYPE_FUNCTION		0x3			// Fonction
#define WORD_TYPE_COMMAND		0x4			// Commande préimplémentée
#define WORD_TYPE_VAR			0x5			// Nom de variable
#define WORD_TYPE_PARENTHESE	0x6			// Parenthèse ( ou )
#define WORD_TYPE_VIRGULE		0x7			// ,

#define P_ID_1					0x0			// (
#define P_ID_2					0x1			// )

extern int		nb_op;
extern char		*op[];

#define OP_ADD					0x00
#define OP_SUBS					0x01
#define OP_MUL					0x02
#define OP_DIV					0x03
#define OP_MOD					0x04
#define OP_AND					0x05
#define OP_OR					0x06
#define OP_XOR					0x07
#define OP_NOT					0x08
#define OP_IMP					0x09
#define OP_EQV					0x0A
#define OP_EQUAL				0x0B
#define OP_LESS					0x0C
#define OP_GREATER				0x0D
#define OP_LEQUAL				0x0E
#define OP_GEQUAL				0x0F
#define OP_NEQUAL				0x10
#define OP_EXP					0x11
#define OP_LOG					0x12
#define OP_SQRT					0x13
#define OP_COS					0x14
#define OP_SIN					0x15
#define OP_RAND					0x16
#define OP_ABS					0x17
#define OP_TAN					0x18
#define OP_COTAN				0x19
#define OP_SH					0x1A
#define OP_CH					0x1B
#define OP_TH					0x1C
#define OP_COTH					0x1D
#define OP_ACOS					0x1E
#define OP_ASIN					0x1F
#define OP_ATAN					0x20
#define OP_ACH					0x21
#define OP_ASH					0x22
#define OP_ATH					0x23
#define OP_MOUSE_X				0x24
#define OP_MOUSE_Y				0x25
#define OP_MOUSE_Z				0x26
#define OP_MOUSE_B				0x27
#define OP_GET_KEY				0x28
#define OP_INT					0x29
#define OP_TIME					0x2A
#define OP_LOAD					0x2B
#define OP_NB_PLAYER			0x2C
#define OP_GET_UNIT_NUMBER_FOR_PLAYER	0x2D
#define OP_GET_UNIT_OWNER		0x2E
#define OP_GET_UNIT_NUMBER		0x2F
#define OP_ANNIHILATED			0x30
#define OP_HAS_UNIT				0x31
#define OP_CREATE_UNIT			0x32
#define OP_MAP_W				0x33
#define OP_MAP_H				0x34
#define OP_PLAYER_SIDE			0x35

extern int		nb_cmd;
extern char		*cmd[];

#define CMD_IF					0x00
#define CMD_THEN				0x01
#define CMD_ELSE				0x02
#define CMD_FOR					0x03
#define CMD_TO					0x04
#define CMD_WHILE				0x05
#define CMD_CLS					0x06
#define CMD_DO					0x07
#define CMD_UNTIL				0x08
#define CMD_END					0x09
#define CMD_PRINT				0x0A
#define CMD_LOGMSG				0x0B
#define CMD_LINE				0x0C
#define CMD_POINT				0x0D
#define CMD_TRIANGLE			0x0E
#define CMD_SIGNAL				0x0F
#define CMD_NEXT				0x10
#define CMD_LOOP				0x11
#define CMD_LOCATE				0x12
#define CMD_COLOR				0x13
#define CMD_BOX					0x14
#define CMD_FILLBOX				0x15
#define CMD_WEND				0x16
#define CMD_CIRCLE				0x17
#define CMD_SLEEP				0x18
#define CMD_WAIT				0x19
#define CMD_NEW					0x1A
#define CMD_INTEGER				0x1B
#define CMD_FLOAT				0x1C
#define CMD_BITMAP				0x1D
#define CMD_PUT					0x1E
#define CMD_GET					0x1F
#define CMD_SAVE				0x20
#define CMD_DESTROY				0x21
#define CMD_CREATE				0x22
#define CMD_SET_CURRENT_PLAYER	0x23
#define CMD_CHANGE_UNIT_OWNER	0x24
#define CMD_MOVE_UNIT			0x25
#define CMD_KILL_UNIT			0x26
#define CMD_KICK_UNIT			0x27
#define CMD_PLAY				0x28

#define VAR_NONE				0x0		// Type non défini
#define VAR_INTEGER				0x1		// Entier
#define VAR_FLOATING			0x2		// Flottant
#define VAR_STRING				0x3		// Chaîne
#define VAR_ARRAY_INTEGER		0x4		// Tableau d'entiers
#define VAR_ARRAY_FLOATING		0x5		// Tableau de flottants
#define VAR_BITMAP				0x6		// Image bitmap
#define VAR_ARRAY_BITMAP		0x7		// Tableau de bitmaps

class VAR			// Type variable
{
public:
	char	*name;	// Nom de la variable si applicable
	byte	type;	// Type de variable
	int		val;	// Valeur numérique entière ou longueur de la chaîne/taille du tableau
	float	f;		// Valeur flottante
	byte	*s;		// Pointeur vers données de type tableau (chaîne de caractères, ...)

	inline void init()
	{
		name=NULL;
		type=VAR_NONE;
		val=0;
		f=0.0f;
		s=NULL;
	}

	inline void destroy()
	{
		if(name)	free(name);
		if(type==VAR_BITMAP && s!=NULL) {
			destroy_bitmap((BITMAP*)s);
			type=VAR_NONE;
			s=NULL;
			}
		if(s)		free(s);
		init();
	}

	inline VAR()
	{
		name=NULL;
		type=VAR_NONE;
		val=0;
		f=0.0f;
		s=NULL;
	}

	inline ~VAR()
	{
		if(name)	free(name);
		if(type==VAR_BITMAP && s!=NULL) {
			destroy_bitmap((BITMAP*)s);
			type=VAR_NONE;
			s=NULL;
			}
		if(s)		free(s);
		s=NULL;
	}
};

struct A_WORD
{
	byte	type;			// Nature du mot
	char	*name;			// Nom du mot
	int		val;			// Valeur entière
	float	f;				// Valeur flottante
	int		id;				// Identifiant s'il s'agit d'une commande préimplémentée
};

class ANALYSE
{
public:
	int		nb;				// Nombre de mots
	int		max;
	A_WORD	*word;			// Tableau des mots

	void init()
	{
		max=0;
		nb=0;
		word=NULL;
	}

	void destroy()
	{
	 	if(nb>0 && word) {
			for(int i=0;i<nb;i++)
				if(word[i].name)	free(word[i].name);
			free(word);
			}
		init();
	}

	ANALYSE()
	{
		init();
	}

	~ANALYSE()
	{
		destroy();
	}

	void add_word(A_WORD w)			// Ajoute un mot
	{
		nb++;
		if(nb>max) {
			max+=1000;
			A_WORD *n_word=(A_WORD*) malloc(sizeof(A_WORD)*max);
			for(int i=0;i<nb-1;i++)
				n_word[i]=word[i];
			if(word)	free(word);
			word=n_word;
			}
		word[nb-1]=w;
	}

	void analyse(char *text);
};

class EVAL_TREE			// Arbre d'évaluation d'une expression
{
public:
	int			id;			// Identifiant à évaluer
	int			pr;			// Priorité
	bool		unary;		// Opérateur unaire
	EVAL_TREE	*g,*d;		// Fils droits et gauches

	inline void init()
	{
		unary=false;
		pr=0;
		g=NULL;
		d=NULL;
	}

	inline void destroy()
	{
		if(g)	{	g->destroy();		delete g;	}
		if(d)	{	d->destroy();		delete d;	}
		init();
	}

	EVAL_TREE()
	{
		init();
	}

	~EVAL_TREE()
	{
		destroy();
	}

	bool fix_priority()				// Corrige l'ordre pour respecter les priorités
	{
		if(d==NULL && pr<100000)	return	false;
		if(pr==100000 && d!=NULL) {						// Parenthèse avec quelque chose à droite, on rééquilibre
			int	s_id=id;
			EVAL_TREE *s_g=g,*s_d=d;
			pr=d->pr;
			id=d->id;
			unary=d->unary;
			d=d->d;
			g=s_d;
			g->d=NULL;
			g->g=s_g;
			g->pr=100000;
			g->id=s_id;
			g->unary=false;
			return true;
			}
		if(pr<100000 && pr>d->pr && !d->unary) {		// Si la priorité n'est pas respectée (dans le cas où on n'a pas de parenthèses)
			int			s_id=id;
			int			s_pr=pr;
			bool		s_unary=unary;
			EVAL_TREE	*s_g=g,*s_d=d;
			pr=d->pr;
			id=d->id;
			unary=d->unary;
			d=d->d;
			g=s_d;
			g->d=g->g;
			g->g=s_g;
			g->id=s_id;
			g->pr=s_pr;
			g->unary=s_unary;
			bool refix=d->fix_priority();
			if(g)
				refix|=g->fix_priority();
			if(refix)
				return fix_priority();
			return true;
			}
		bool refix=false;
		if(d)
			refix|=d->fix_priority();
		if(g)
			refix|=g->fix_priority();
		if(refix)
			return fix_priority();
		return false;
	}

	void fix_pr()
	{
		while(fix_priority()) {};
	}

	void print(A_WORD *word)
	{
		if(pr==100000) {
			printf(" ( ");
			if(g)	g->print(word);
			printf(" ) ");
			if(d)	d->print(word);
			}
		else {
			if(g)	g->print(word);
			if(d)	d->print(word);
			if(word[id].name)	printf(" %s ",word[id].name);
			}
	}

	int postfix(int *index,int pos)
	{
		if(g)	pos=g->postfix(index,pos);
		if(d)	pos=d->postfix(index,pos);
		index[pos++]=id;
		return pos;
	}
};

#define DRAW_TYPE_NONE		0x0
#define DRAW_TYPE_POINT		0x1
#define DRAW_TYPE_LINE		0x2
#define DRAW_TYPE_CIRCLE	0x3
#define DRAW_TYPE_TRIANGLE	0x4
#define DRAW_TYPE_BOX		0x5
#define DRAW_TYPE_FILLBOX	0x6
#define DRAW_TYPE_TEXT		0x7
#define DRAW_TYPE_BITMAP	0x8

struct DRAW_OBJECT					// Pour mémoriser le traçage des primitives
{
	byte	type;
	int		x[4];
	int		y[4];
	float	r[2],g[2],b[2];
	char	*text;
	GLuint	tex;
};

class DRAW_LIST
{
public:
	DRAW_OBJECT		prim;
	DRAW_LIST		*next;

	void init()
	{
		prim.type=DRAW_TYPE_NONE;
		prim.text=NULL;
		next=NULL;
	}

	void destroy()
	{
		if(prim.type==DRAW_TYPE_BITMAP)		glDeleteTextures(1,&prim.tex);
		if(prim.type==DRAW_TYPE_TEXT && prim.text!=NULL)
			free(prim.text);
		if(next) {
			next->destroy();
			delete next;
			}
		init();
	}

	DRAW_LIST()
	{
		init();
	}

	void add(DRAW_OBJECT obj)
	{
		if(next==NULL) {
			next=new DRAW_LIST;
			next->prim=obj;
			for(int i=0;i<4;i++) {
				next->prim.x[i]=(next->prim.x[i]*640+(SCREEN_W>>1))/SCREEN_W;
				next->prim.y[i]=(next->prim.y[i]*480+(SCREEN_H>>1))/SCREEN_H;
				}
			next->next=NULL;
			}
		else next->add(obj);
	}

	void draw(FONT *fnt)
	{
		switch(prim.type)
		{
		case DRAW_TYPE_POINT:
			glBegin(GL_POINTS);
				glColor3f(prim.r[0],prim.g[0],prim.b[0]);
				glVertex2i(prim.x[0],prim.y[0]);
			glEnd();
			break;
		case DRAW_TYPE_LINE:
			glBegin(GL_LINES);
				glColor3f(prim.r[0],prim.g[0],prim.b[0]);
				glVertex2i(prim.x[0],prim.y[0]);
				glVertex2i(prim.x[1],prim.y[1]);
			glEnd();
			break;
		case DRAW_TYPE_CIRCLE:
			glBegin(GL_LINE_STRIP);
				glColor3f(prim.r[0],prim.g[0],prim.b[0]);
				{
					int max = (int)(sqrt(prim.r[1])*2.0f)*2;
					if(max>0)
						for(int i=0;i<=prim.r[1]*10;i++)
							glVertex2f(prim.x[0]+prim.r[1]*cos(i*6.2831853072f/max),prim.y[0]+prim.r[1]*sin(i*6.2831853072f/max));
				}
			glEnd();
			break;
		case DRAW_TYPE_TRIANGLE:
			glBegin(GL_TRIANGLES);
				glColor3f(prim.r[0],prim.g[0],prim.b[0]);
				glVertex2i(prim.x[0],prim.y[0]);
				glVertex2i(prim.x[1],prim.y[1]);
				glVertex2i(prim.x[2],prim.y[2]);
			glEnd();
			break;
		case DRAW_TYPE_BOX:
			glBegin(GL_LINE_STRIP);
				glColor3f(prim.r[0],prim.g[0],prim.b[0]);
				glVertex2i(prim.x[0],prim.y[0]);
				glVertex2i(prim.x[1],prim.y[0]);
				glVertex2i(prim.x[1],prim.y[1]);
				glVertex2i(prim.x[0],prim.y[1]);
				glVertex2i(prim.x[0],prim.y[0]);
			glEnd();
			break;
		case DRAW_TYPE_FILLBOX:
			glBegin(GL_QUADS);
				glColor3f(prim.r[0],prim.g[0],prim.b[0]);
				glVertex2i(prim.x[0],prim.y[0]);
				glVertex2i(prim.x[1],prim.y[0]);
				glVertex2i(prim.x[1],prim.y[1]);
				glVertex2i(prim.x[0],prim.y[1]);
			glEnd();
			break;
		case DRAW_TYPE_TEXT:
			glEnable(GL_BLEND);
			glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
			allegro_gl_printf(fnt,prim.x[0],prim.y[0],0.0f,makecol((int)(prim.r[0]*255.0f),(int)(prim.g[0]*255.0f),(int)(prim.b[0]*255.0f)),"%s",prim.text);
			glDisable(GL_BLEND);
			break;
		case DRAW_TYPE_BITMAP:
			glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
			glEnable(GL_BLEND);
			PutTex(prim.tex,prim.x[0],prim.y[0],prim.x[1],prim.y[1]);
			glDisable(GL_BLEND);
			break;
		};
		if(next)
			next->draw(fnt);
	}
};

class STACK
{
	VAR			*elt[100];	// Pile
	int			nb;

public:
	inline void init()
	{
//		for(int i=0;i<100;i++)
//			elt[i]=NULL;
		nb=0;
	}

	inline void destroy()
	{
		if(nb>0) {
			for(int i=0;i<nb;i++)
				if(elt[i]) {
					if(elt[i]->type==VAR_BITMAP)
						elt[i]->s=NULL;
					delete elt[i];
					}
			nb=0;
			}
	}

	inline STACK()
	{
		init();
	}

	inline ~STACK()
	{
		destroy();
	}

	inline VAR *pop()
	{
		if(nb<=0)	return NULL;
		return elt[--nb];
	}

	inline void c_push(VAR *v)
	{
		elt[nb++]=v;
	}

	inline void push(VAR *v)
	{
		elt[nb]=new VAR;
		*elt[nb]=*v;
		elt[nb]->name=NULL;
		if(v->type!=VAR_STRING && v->type!=VAR_BITMAP && v->type!=VAR_ARRAY_INTEGER && v->type!=VAR_ARRAY_FLOATING && v->type!=VAR_ARRAY_BITMAP)
			elt[nb]->s=NULL;
		if(v->type==VAR_STRING)
			elt[nb]->s=(byte*)(strdup((char*)v->s));
		nb++;
	}
};

class ASM_PROGRAM
{
	int			size;			// Taille du code (en octets)
	int			max_data;		// Taille du tampon utilisé pour stocker le code
	byte		*code;			// Code
	int			nb_vars;		// Nombre de variables
	VAR			*var;			// Tableau de variables

	//	Pour l'éxecution du code
	int			pos;
	STACK		stack;			// Pile
	VAR			reg[10];		// Registres
	int			tx,ty;			// Position du curseur du texte
	int			tcol;			// Couleur du texte
	int			tbcol;			// Couleur d'arrière plan
	float		sleep_time;		// Temps d'attente
	bool		sleeping;		// Indique si le programme marque une pause
	bool		waiting;		// Indique si le programme attend une action utilisateur
	int			last;			// Dernière lecture du timer
	int			amx,amy,amz;	// Coordonnées du curseur
	int			amb;			// Boutons de la souris
	DRAW_LIST	draw_list;		// Liste de commandes d'affichage
	int			current_player;	// Joueur courant

public:

	inline void stop()
	{
		pos=-1;
		draw_list.destroy();
	}

	void init()
	{
		current_player=0;

		draw_list.init();

		amx=amy=amz=0;
		amb=0;

		sleep_time=0.0f;
		sleeping=false;
		waiting=false;

		max_data=0;
		size=0;
		code=NULL;
		nb_vars=0;
		var=NULL;

		pos=0;		// Commence au début
		stack.init();
		for(int i=0;i<10;i++)
			reg[i].init();
		tx=0;
		ty=0;
		tcol=0x7F7F7F;
		tbcol=-1;
	}

	void destroy()
	{
		draw_list.destroy();
		if(code)
			free(code);
		if(nb_vars>0 && var)
			for(int i=0;i<nb_vars;i++) {
				if(var[i].type==VAR_BITMAP && var[i].s!=NULL) {
					byte *pt=var[i].s;
					var[i].destroy();
					for(int e=i+1;e<nb_vars;e++)
						if(var[e].s==pt)
							var[e].s=NULL;
					}
				else
					var[i].destroy();
				}
		if(var)
			free(var);
		stack.destroy();
		for(int i=0;i<10;i++) {
			if(reg[i].type==VAR_BITMAP) {
				reg[i].type=VAR_NONE;
				reg[i].s=NULL;
				}
			reg[i].destroy();
			}
		init();
	}

	ASM_PROGRAM()
	{
		init();
	}

	~ASM_PROGRAM()
	{
		destroy();
	}

	void save(char *filename)					// Enregistre le code
	{
		FILE *file=fopen(filename,"wb");
		if(file) {
			fwrite("ASM",3,1,file);						// Chaîne de reconnaissance
			fwrite(&size,sizeof(size),1,file);			// Taille du code
			fwrite(&nb_vars,sizeof(nb_vars),1,file);	// Nombre de variables
			fwrite(code,size,1,file);					// Ecrit le code
			fclose(file);
			}
	}

	void load(char *filename)					// Charge le code d'un fichier
	{
		destroy();			// Au cas où
		FILE *file=fopen(filename,"rb");
		if(file) {
			char tmp[3];
			fread(tmp,3,1,file);
			if(tmp[0]!='A' || tmp[1]!='S' || tmp[2]!='M') {		// Format incorrect
				fclose(file);
				return;
				}
			fread(&size,sizeof(size),1,file);			// Taille du code
			fread(&nb_vars,sizeof(nb_vars),1,file);		// Nombre de variables
			var=(VAR*) malloc(sizeof(VAR)*nb_vars);
			code=(byte*) malloc(size);					// Alloue la mémoire pour stocker le code
			max_data=size;
			fread(code,size,1,file);					// Charge le code
			for(int i=0;i<nb_vars;i++)
				var[i].init();
			fclose(file);
			}
	}

private:

	inline int get_var_id(char *name)					// Retourne l'identifiant d'une variable
	{
		for(int i=0;i<nb_vars;i++)
			if(var[i].name && strcasecmp(name,var[i].name)==0)
				return i;
		return -1;
	}

	inline int add_var(char *name)						// Ajoute une variable
	{
		nb_vars++;
		VAR *nvar=(VAR*) malloc(sizeof(VAR)*nb_vars);
		if(var && nb_vars>1)
			for(int i=0;i<nb_vars-1;i++)
				nvar[i]=var[i];
		if(var)	free(var);
		var=nvar;
		var[nb_vars-1].init();
		if(name)
			var[nb_vars-1].name=strdup(name);
		return nb_vars-1;
	}

	inline int add_code(byte asm_code)				// Ajoute une instruction
	{
		size++;
		if(size>max_data) {
			max_data+=1000;
			byte *ncode=(byte*) malloc(max_data);
			if(code) {
				memcpy(ncode,code,size-1);
				free(code);
				}
			code=ncode;
			}
		code[size-1]=asm_code;
		return size-1;
	}

	inline void add_short(short val)					// Ajoute un entier court
	{
		size+=2;
		if(size>max_data) {
			max_data+=1000;
			byte *ncode=(byte*) malloc(max_data);
			if(code) {
				memcpy(ncode,code,size-2);
				free(code);
				}
			code=ncode;
			}
		*((short*)(&(code[size-2])))=val;
	}

	inline void add_int(int val)						// Ajoute un entier codé sur 4 octets
	{
		size+=4;
		if(size>max_data) {
			max_data+=1000;
			byte *ncode=(byte*) malloc(max_data);
			if(code) {
				memcpy(ncode,code,size-4);
				free(code);
				}
			code=ncode;
			}
		*((int*)(&(code[size-4])))=val;
	}

	inline void add_float(float val)					// Ajoute un flottant
	{
		size+=4;
		if(size>max_data) {
			max_data+=1000;
			byte *ncode=(byte*) malloc(max_data);
			if(code) {
				memcpy(ncode,code,size-4);
				free(code);
				}
			code=ncode;
			}
		*((float*)(&(code[size-4])))=val;
	}

	inline void add_string(char *string)				// Ajoute une chaîne
	{
		add_int(strlen(string));
		for(int i=0;i<strlen(string);i++)
			add_code(string[i]);
	}

	inline bool is_val(A_WORD *word)
	{
		if(word->name[0]=='\"')	return true;						// Chaîne
		if(word->name[0]>='0' && word->name[0]<='9') return true;	// Nombre
		if(word->name[0]=='-' && ((word->name[1]>='0' && word->name[1]<='9') || word->name[1]=='.'))	return true;
		if(word->name[0]=='.'
		&& word->name[1]>='0' && word->name[1]<='9') return true;	// Flottant codé sans partie entière
		return false;
	}

	inline VAR *get_val_from_word(A_WORD *word)
	{
		VAR *var=new VAR;
		if(word->name[0]=='\"') {			// Chaîne
			var->s=(byte*)strdup(word->name+1);
			if(strstr((char*)var->s,"\""))
				*(strstr((char*)var->s,"\""))=0;
			var->type=VAR_STRING;
			}
		else if(strstr(word->name,".")) {	// Flottant
			var->f=atof(word->name);
			var->type=VAR_FLOATING;
			}
		else {								// Entier
			var->val=atoi(word->name);
			var->type=VAR_INTEGER;
			}
		return var;
	}

	inline byte get_byte()
	{
		return code[pos++];
	}

	inline short get_short()
	{
		pos+=2;
		return *((short*)&(code[pos-2]));
	}

	inline int get_int()
	{
		pos+=4;
		return *((int*)(&(code[pos-4])));
	}

	inline float get_float()
	{
		pos+=4;
		return *((float*)&(code[pos-4]));
	}

	inline char *get_string(int *length=NULL)
	{
		int l=get_int();
		if(length)	*length=l;
		char *txt=(char*) malloc(l+1);
		for(int i=0;i<l;i++)
			txt[i]=(char)get_byte();
		txt[l]=0;
		return txt;
	}

public:

	void compile(char *filename);				// Compile un fichier source

	int run(MAP *map,float dt);					// Execute le script
};

#define STRUCT_NONE			0x0			// Utilisé pour indiquer que la pile est vide
#define STRUCT_FOR			0x1			// Boucle for
#define STRUCT_IF			0x2			// Instruction conditionnelle
#define STRUCT_DO			0x3			// Boucle do while/until
#define STRUCT_WHILE		0x4			// Boucle while

struct STRUCT			// Element des piles de structures
{
	byte		type;					// Type de structure
	int			start_pos;				// Position du début de la structure dans le code compilé
	int			pos;					// Position dans le code compilé, dépend de la structure
	int			limit;					// Limite
};

struct STRUCT_STACK_ELEMENT
{
	STRUCT					val;
	STRUCT_STACK_ELEMENT	*next;
};

class STRUCT_STACK
{
	STRUCT_STACK_ELEMENT	*elt;

public:

	inline STRUCT_STACK()
	{
		elt=NULL;
	}

	inline ~STRUCT_STACK()
	{
		while(elt) {
			STRUCT_STACK_ELEMENT	*next=elt->next;
			delete(elt);
			elt=next;
			}
	}

	inline void push(STRUCT struct_elt)
	{
		STRUCT_STACK_ELEMENT	*new_elt=new STRUCT_STACK_ELEMENT;
		new_elt->next=elt;
		elt=new_elt;
		elt->val=struct_elt;
	}

	inline STRUCT pop()
	{
		STRUCT struct_elt;
		if(elt) {
			struct_elt=elt->val;
			STRUCT_STACK_ELEMENT *next=elt->next;
			delete elt;
			elt=next;
			}
		else
			struct_elt.type=STRUCT_NONE;
		return struct_elt;
	}
};

#endif
