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

/*-----------------------------------------------------------------------------------\
|                                       weapons.h                                    |
|   Ce module contient les structures, classes et fonctions nécessaires à la lecture |
| des fichiers tdf du jeu totalannihilation concernant les armes utilisées par les   |
| unités du jeu.                                                                     |
|                                                                                    |
\-----------------------------------------------------------------------------------*/

#include "gaf.h"

#ifndef __WEAPON_CLASSES
#define __WEAPON_CLASSES

#define RENDER_TYPE_LASER		0x0
#define RENDER_TYPE_MISSILE		0x1
#define RENDER_TYPE_GUN			0x2
#define RENDER_TYPE_DGUN		0x3
#define RENDER_TYPE_BITMAP		0x4
#define RENDER_TYPE_PARTICLES	0x5
#define RENDER_TYPE_BOMB		0x6
#define RENDER_TYPE_LIGHTNING	0x7

class WEAPON_DEF				// Classe définissant un type d'arme
{
public:
	int		weapon_id;			// Numéro identifiant l'arme
	char	*internal_name;		// Nom interne de l'arme
	char	*name;				// Nom de l'arme
	int		rendertype;
	bool	ballistic;
	bool	dropped;
	bool	turret;
	int		range;				// portée
	float	reloadtime;			// temps de rechargement
	int		weaponvelocity;
	int		areaofeffect;		// zone d'effet
	bool	startsmoke;
	bool	endsmoke;
	int		damage;				// Dégats causés par l'arme
	int		firestarter;
	int		accuracy;
	int		aimrate;
	int		tolerance;
	int		holdtime;
	int		energypershot;
	int		metalpershot;
	int		minbarrelangle;
	bool	unitsonly;
	float	edgeeffectiveness;
	bool	lineofsight;
	int		color[2];
	float	burstrate;
	float	duration;
	bool	beamweapon;
	int		startvelocity;			// Pour les missiles
	float	weapontimer;			// Pour les missiles
	int		weaponacceleration;		// Pour les missiles
	int		turnrate;				// Pour les missiles
	MODEL	*model;					// Modèle 3D
	float	smokedelay;
	bool	guidance;				// Guidage
	bool	tracks;
	bool	selfprop;
	bool	smoketrail;				// Laisse de la fumée en passant
	bool	noautorange;
	int		flighttime;
	bool	vlaunch;
	bool	stockpile;
	bool	targetable;				// On peut viser
	bool	commandfire;			// ne tire pas seul
	bool	cruise;
	bool	propeller;
	bool	twophase;
	int		shakemagnitude;
	float	shakeduration;
	char	*soundstart;			// Son à jouer lorsque l'arme tire
	char	*soundhit;				// Son à jouer lorsque l'arme explose (si l'arme explose)
	char	*soundwater;			// Son à jouer lorsque l'arme touche l'eau
	char	*soundtrigger;			// Son à jouer lorsque l'arme tire en mode rapide
	char	*explosiongaf;
	char	*explosionart;
	char	*waterexplosiongaf;
	char	*waterexplosionart;
	char	*lavaexplosiongaf;
	char	*lavaexplosionart;
	int		nb_id;
	bool	waterweapon;

	inline void init()
	{
		waterweapon=false;
		explosiongaf=NULL;
		explosionart=NULL;
		waterexplosiongaf=NULL;
		waterexplosionart=NULL;
		lavaexplosiongaf=NULL;
		lavaexplosionart=NULL;
		dropped=false;
		nb_id=0;
		soundstart=NULL;
		soundhit=NULL;
		soundwater=NULL;
		soundtrigger=NULL;
		weapon_id=0;			// Numéro identifiant l'arme
		internal_name=NULL;		// Nom interne de l'arme
		name=NULL;				// Nom de l'arme
		rendertype=0;
		ballistic=false;
		turret=false;
		range=100;				// portée
		reloadtime=0.0f;		// temps de rechargement
		weaponvelocity=1;
		areaofeffect=10;		// zone d'effet
		startsmoke=false;
		endsmoke=false;
		damage=100;				// Dégats causés par l'arme
		firestarter=0;
		accuracy=0;
		aimrate=0;
		tolerance=0;
		holdtime=0;
		energypershot=0;
		metalpershot=0;
		minbarrelangle=0;
		unitsonly=false;
		edgeeffectiveness=1.0f;
		lineofsight=false;
		color[0]=0;
		color[1]=0xFFFFFF;
		burstrate=1.0f;
		duration=-1.0f;
		beamweapon=false;
		startvelocity=0;			// Pour les missiles
		weapontimer=100.0f;			// Pour les missiles
		weaponacceleration=1;		// Pour les missiles
		turnrate=1;					// Pour les missiles
		model=NULL;					// Modèle 3D
		smokedelay=0.1f;
		guidance=false;				// Guidage
		tracks=false;
		selfprop=false;
		smoketrail=false;			// Laisse de la fumée en passant
		noautorange=false;
		flighttime=10;
		vlaunch=false;
		stockpile=false;
		targetable=false;			// On peut viser
		commandfire=false;			// ne tire pas seul
		cruise=false;
		propeller=false;
		twophase=false;
		shakemagnitude=0;
		shakeduration=0.1f;
	}

	inline void destroy()
	{
		if(internal_name)	free(internal_name);
		if(name)	free(name);
		init();
	}

	WEAPON_DEF()
	{
		init();
	}

	~WEAPON_DEF()
	{
		destroy();
	}
};

class WEAPON_MANAGER			// Gestionnaire de types d'armes
{
public:
	int			nb_weapons;
	WEAPON_DEF	*weapon;
	ANIM		cannonshell;	// Animation pour les tirs de cannons

	void init()
	{
		nb_weapons=0;
		weapon=NULL;
		cannonshell.init();
	}

	WEAPON_MANAGER()
	{
		init();
	}

	void destroy()
	{
		cannonshell.destroy();
		if(nb_weapons>0 && weapon)			// Détruit les éléments
			for(int i=0;i<nb_weapons;i++)
				weapon[i].destroy();
		if(weapon)
			free(weapon);
		init();
	}

	~WEAPON_MANAGER()
	{
		destroy();
	}

	int add_weapon(char *name)			// Ajoute un élément
	{
		nb_weapons++;
		WEAPON_DEF *n_weapon=(WEAPON_DEF*) malloc(sizeof(WEAPON_DEF)*nb_weapons);
		if(weapon && nb_weapons>1)
			for(int i=0;i<nb_weapons-1;i++)
				n_weapon[i]=weapon[i];
		if(weapon)	free(weapon);
		weapon=n_weapon;
		weapon[nb_weapons-1].init();
		weapon[nb_weapons-1].internal_name=strdup(name);
		weapon[nb_weapons-1].nb_id=nb_weapons-1;
		return nb_weapons-1;
	}

private:
	inline char *get_line(char *data)
	{
		int pos=0;
		while(data[pos]!=0 && data[pos]!=13 && data[pos]!=10)	pos++;
		char *d=new char[pos+1];
		memcpy(d,data,pos);
		d[pos]=0;
		return d;
	}
public:

	void load_tdf(char *data,int size=99999999);					// Charge un fichier tdf

	int get_weapon_index(char *name)
	{
		if(nb_weapons<=0)	return -1;
		for(int i=0;i<nb_weapons;i++)
			if(strcasecmp(name,weapon[i].internal_name)==0)
				return i;
		return -1;
	}

	int get_weapon_index(int weapon_id)
	{
		if(nb_weapons<=0)	return -1;
		for(int i=0;i<nb_weapons;i++)
			if(weapon[i].weapon_id==weapon_id)
				return i;
		return -1;
	}
};

extern WEAPON_MANAGER		weapon_manager;

void load_all_weapons_in_hpi(char *filename);

void load_weapons();				// Charge tout les éléments

class WEAPON						// Objet arme utilisé pendant le jeu
{
public:
	int			weapon_id;			// Identifiant de l'arme
	VECTOR		Pos;				// Position
	VECTOR		V;					// Vitesse
	VECTOR		Ac;					// Accélération
	VECTOR		target_pos;			// Position ciblée
	int			target;				// Unité ciblée (dans le tableau des unités)
	float		stime;				// Temps écoulé depuis le lancement
	float		killtime;			// Temps écoulé depuis la destruction de l'arme
	bool		dying;
	float		smoke_time;			// Temps écoulé depuis la dernière émission de fumée
	float		f_time;				// Temps de vol
	float		a_time;				// Temps d'activité
	int			anim_sprite;		// Position dans l'animation
	int			shooter_idx;			// Unité qui a tiré l'arme (ne peut pas se tirer dessus)
	int			phase;

	inline void init()
	{
		phase=1;
		a_time=0.0f;
		f_time=0.0f;
		shooter_idx=-1;
		anim_sprite=0;
		weapon_id=-1;		// Non défini
		Pos.x=Pos.y=Pos.z=0.0f;
		Ac=V=Pos;
		target_pos=Pos;
		target=-1;			// Pas de cible
		stime=0.0f;
		killtime=0.0f;
		dying=false;
		smoke_time=0.0f;
	}

	WEAPON()
	{
		init();
	}

	void move(float dt,MAP *map);

	void draw(CAMERA *cam=NULL);
};

class INGAME_WEAPONS				// Gestionnaire d'armes
{
public:
	int			nb_weapon;			// Nombre d'armes
	int			max_weapon;			// Nombre maximum d'armes stockables dans le tableau
	WEAPON		*weapon;			// Tableau regroupant les armes

	void init()
	{
		nb_weapon=0;
		max_weapon=0;
		weapon=NULL;
	}

	void destroy()
	{
		if(max_weapon>0 && weapon)
			free(weapon);
		init();
	}

	INGAME_WEAPONS()
	{
		init();
	}

	~INGAME_WEAPONS()
	{
		destroy();
	}

	int add_weapon(int weapon_id,int shooter)
	{
		if(nb_weapon<max_weapon) {		// S'il y a encore de la place
			for(int i=0;i<max_weapon;i++)
				if(weapon[i].weapon_id==-1) {
					nb_weapon++;
					weapon[i].init();
					weapon[i].weapon_id=weapon_id;
					weapon[i].shooter_idx=shooter;
					weapon[i].f_time=weapon_manager.weapon[weapon_id].flighttime;
					return i;
					}
			}
		else {
			max_weapon+=100;			// Augmente la taille du tableau
			WEAPON *new_weapon=(WEAPON*) malloc(sizeof(WEAPON)*max_weapon);
			for(int i=0;i<max_weapon;i++)
				new_weapon[i].weapon_id=-1;
			if(weapon && nb_weapon>0)
				for(int i=0;i<nb_weapon;i++)
					new_weapon[i]=weapon[i];
			if(weapon)
				free(weapon);
			weapon=new_weapon;
			int index=nb_weapon;
			nb_weapon++;
			weapon[index].init();
			weapon[index].weapon_id=weapon_id;
			weapon[index].shooter_idx=shooter;
			weapon[index].f_time=weapon_manager.weapon[weapon_id].flighttime;
			return index;
			}
	}

	void move(float dt,MAP *map)
	{
		if(nb_weapon<=0 || max_weapon<=0) return;
		for(int i=0;i<max_weapon;i++)
			if(weapon[i].weapon_id>=0)
				weapon[i].move(dt,map);
	}

	void draw(CAMERA *cam=NULL)
	{
		if(nb_weapon<=0 || max_weapon<=0) return;
		for(int i=0;i<max_weapon;i++)
			if(weapon[i].weapon_id>=0)
				weapon[i].draw(cam);
	}

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

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

		glColor4f(1.0f,1.0f,1.0f,1.0f);
		glDisable(GL_TEXTURE_2D);
		glBegin(GL_POINTS);
		int cur_id=0;
		for(int i=0;i<max_weapon;i++)
			if(weapon[i].weapon_id>=0)
				glVertex2f(weapon[i].Pos.x/map_w*rw+64.0f,weapon[i].Pos.z/map_h*rh+64.0f);
		glEnd();
		glEnable(GL_TEXTURE_2D);
	}
};

extern INGAME_WEAPONS weapons;

class FX
{
public:
	float		time;		// Durée d'éxistence de l'effet
	bool		playing;	// Si l'effet est joué
	VECTOR		Pos;		// Position
	float		size;		// Taille (proportion de l'image d'origine)
	ANIM		anm;		// Animation

public:
	inline void init()
	{
		time=0.0f;
		playing=false;
		Pos.x=Pos.y=Pos.z=0.0f;
		size=1.0f;
		anm.init();
	}

	inline void destroy()
	{
		anm.destroy();
		init();
	}

	FX()
	{
		init();
	}

	~FX()
	{
		destroy();
	}

	inline void move(float dt)
	{
		if(!playing)	return;
		time+=dt;
		if(time*15.0f>anm.nb_bmp)
			destroy();
	}

	inline void draw(CAMERA *cam=NULL)
	{
		if(cam==NULL || !playing)	return;
		cam->SetView();
		int img=(int)(time*15.0f);
		float wsize=size*anm.w[img];
		float hsize=size*anm.h[img];
		float dx=anm.ofs_x[img];
		float dy=anm.ofs_y[img];
		VECTOR P=Pos;
		glBindTexture(GL_TEXTURE_2D,anm.glbmp[img]);
		glBegin(GL_QUADS);
			glTexCoord2f(0.0f,0.0f);	glVertex3f(P.x-hsize*cam->Up.x-wsize*cam->Side.x,P.y-hsize*cam->Up.y-wsize*cam->Side.y,P.z-hsize*cam->Up.z-wsize*cam->Side.z);
			glTexCoord2f(1.0f,0.0f);	glVertex3f(P.x-hsize*cam->Up.x+wsize*cam->Side.x,P.y-hsize*cam->Up.y+wsize*cam->Side.y,P.z-hsize*cam->Up.z+wsize*cam->Side.z);
			glTexCoord2f(1.0f,1.0f);	glVertex3f(P.x+hsize*cam->Up.x+wsize*cam->Side.x,P.y+hsize*cam->Up.y+wsize*cam->Side.y,P.z+hsize*cam->Up.z+wsize*cam->Side.z);
			glTexCoord2f(0.0f,1.0f);	glVertex3f(P.x+hsize*cam->Up.x-wsize*cam->Side.x,P.y+hsize*cam->Up.y-wsize*cam->Side.y,P.z+hsize*cam->Up.z-wsize*cam->Side.z);
		glEnd();
	}

	inline void load(char *filename,char *entry_name,VECTOR P,float s)
	{
		byte *data=load_file(filename);
		if(data) {
			destroy();

			anm.load_gaf(data,get_gaf_entry_index(data,entry_name));
			anm.convert(false,true);
			Pos=P;
			size=s*0.5f;
			time=0.0f;
			playing=true;
			
			free(data);
			}
	}
};

class FX_MANAGER
{
private:
	int			max_fx;
	int			nb_fx;
	FX			*fx;

public:
	inline void init()
	{
		max_fx=0;
		nb_fx=0;
		fx=NULL;
	}

	inline void destroy()
	{
		if(fx) {
			for(int i=0;i<max_fx;i++)
				fx[i].destroy();
			free(fx);
			}
		init();
	}

	FX_MANAGER()
	{
		init();
	}

	~FX_MANAGER()
	{
		destroy();
	}

	inline void move(float dt)
	{
		for(int i=0;i<max_fx;i++)
			fx[i].move(dt);
	}

	inline void draw(CAMERA *cam)
	{
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glEnable(GL_BLEND);
		glDepthMask(GL_FALSE);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glPolygonOffset(0.0f,16.0f);
		for(int i=0;i<max_fx;i++)
			fx[i].draw(cam);
		glPolygonOffset(0.0f,0.0f);
		glDisable(GL_BLEND);
		glDepthMask(GL_TRUE);
	}

	inline int add(char *filename,char *entry_name,VECTOR Pos,float size)
	{
		if(nb_fx+1>max_fx) {
			max_fx+=100;
			FX *n_fx=(FX*) malloc(sizeof(FX)*max_fx);
			memcpy(n_fx,fx,sizeof(FX)*(max_fx-100));
			for(int i=max_fx-100;i<max_fx;i++)
				n_fx[i].init();
			free(fx);
			fx=n_fx;
			}
		nb_fx++;
		int idx=-1;
		for(int i=0;i<max_fx;i++)
			if(!fx[i].playing) {
				idx=i;
				break;
				}
		char tmp[200];
		tmp[0]=0;
		strcat(tmp,"/anims/");
		strcat(tmp,filename);
		strcat(tmp,".gaf");
		fx[idx].load(tmp,entry_name,Pos,size);
		return idx;
	}
};

extern FX_MANAGER	fx_manager;
#endif
