/***************************************************************************
    copyright            : (C) 2003 by Michael Speck
    email                : http://lgames.sf.net
 ***************************************************************************/

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

#include "defs.h"
#include "shots.h"
#include "units.h"
#include "bfield.h"

extern SDL_Surface *img_trees, *background, *img_ground, *img_small_crater, *img_crater, *img_gun;
extern void vec2xy( Vector *v, int *sx, int *sy ); /* from shots.c */
extern SDL_Sound *wav_expl1, *wav_expl2, *wav_cannon1, *wav_cannon2;
extern SDL_Cursor *cr_reload, *cr_hourglass, *cr_crosshair;
extern int player_ammo, player_score;
extern int strip_blocked[STRIP_COUNT];
extern int audio_on;
extern int cursor_x_offset, cursor_w;
extern int gun_w, gun_h;

BField bfield;

extern void vec2xy( Vector *v, int *sx, int *sy );

/* fire positions relative to the gun base point for each gun sprite */
Vector fire_offsets[9] = {
	{2, -6, 28},
	{5, -5, 30},
	{7, -3, 30},
	{10,-3, 34},
	{11, 0, 36},
	{10, 1, 34},
	{8,  2, 36},
	{7,  3, 34},
	{3,  4, 34}
};

static void set_tree( Tree *tree, int x, int y, int dead )
{
	tree->img = img_trees;
	tree->img_x_offset = 0;
	tree->img_y_offset = dead*23;
	
	tree->w = 38; tree->h = 23;
	tree->x = x; tree->y = y;
	tree->hx = 17; tree->hy = 8; /* hotspot: center for impacts */
	tree->burn_time = 0; 
	tree->dead = dead;
}

void bfield_init( int type, Vector *gun, Vector *gun_img_offset )
{
	int tree_strips[] = { 1, 7, 11, 17, -1 };
	int i;
	
	memset( &bfield, 0, sizeof( bfield ) );
	bfield.gun = *gun; bfield.gun_img_offset = *gun_img_offset;
	bfield.img_gun_x_offset = 4 * gun_w;
	bfield.demo = (type==BFIELD_DEMO);
	memset( strip_blocked, 0, sizeof( strip_blocked ) );
	
	/* basic ground */
	SDL_BlitSurface( img_ground, 0, background, 0 );
	
	/* add burned trees to some strips and mark them unuseable */
	i = 0;
	while ( tree_strips[i] != -1 ) {
		strip_blocked[tree_strips[i]] = 1;
		set_tree( &bfield.trees[bfield.tree_count++], 
			STRIP_OFFSET + 
			tree_strips[i]*STRIP_SIZE + 
			RAND(0,STRIP_SIZE-17) - 10 /* shadow must be in strip */, 
			rand() % 230, 1 );
		set_tree( &bfield.trees[bfield.tree_count++], 
			STRIP_OFFSET + 
			tree_strips[i]*STRIP_SIZE + 
			RAND(0,STRIP_SIZE-17) - 10 /*shadow must be in strip */, 
			230 + rand() % 230, 1 );
		i++;
	}
	/* add normal trees to gun strip */
	for ( i = 0; i < 4; i++ )
		set_tree( &bfield.trees[bfield.tree_count++], 
			RAND(0, STRIP_OFFSET-38), RAND(i*50, (i+1)*50-20), 0 );
	for ( i = 0; i < 4; i++ )
		set_tree( &bfield.trees[bfield.tree_count++], 
			RAND(0, STRIP_OFFSET-38), RAND(280+i*50, 280+(i+1)*50-20), 0 );

	/* set the ranges */
	SET_RANGE( bfield.delay_ranges[UT_SOLDIER], 2000, 4000 );
	SET_RANGE( bfield.delay_ranges[UT_RECON],   2000, 4000 );
	SET_RANGE( bfield.delay_ranges[UT_TANK],    2000, 5000 );
	SET_RANGE( bfield.group_sizes[UT_SOLDIER], 2, 4 );
	SET_RANGE( bfield.group_sizes[UT_TANK],    1, 1 );
	SET_RANGE( bfield.group_sizes[UT_RECON],   1, 1 );

	/* use bfield_impact_handler for exploding projectiles */
	shots_set_impact_handler( bfield_handle_impact );
}

void bfield_finalize()
{
	units_delete();
	shots_delete();
	particles_clear();
	
}

static void bfield_add_units( int ms )
{
	int num, x, y, dir, i, strip;

	/* decrease delays */
	for ( i = 0; i < UT_LAST; i++ ) 
		if ( (bfield.delays[i] -= ms) < 0 ) 
			bfield.delays[i] = 0;
	
	/* check wether new units appear. as soldiers may interfere
	 * they use only even id'd strips to prevent them from blocking
	 * all strips. */
	if ( bfield.delays[UT_SOLDIER] == 0 ) {
		bfield.delays[UT_SOLDIER] = RAND_IN_RANGE( bfield.delay_ranges[UT_SOLDIER] );
		strip = units_get_rand_strip( UT_SOLDIER ); /* works always */
		dir = rand() % 2;
		num = RAND_IN_RANGE( bfield.group_sizes[UT_SOLDIER] );
		for ( i = 0, y=0; i < num; i++ ) {
			x = RAND(0,STRIP_SIZE-12);
			units_add_soldier( strip, x, ((dir)?480:-50)+y, dir );
			y += RAND(10,20);
		}
	}
	if ( bfield.delays[UT_TANK] == 0 ) {
		strip = units_get_rand_strip( UT_TANK );
		if ( strip != -1 ) {
			bfield.delays[UT_TANK] = RAND_IN_RANGE( bfield.delay_ranges[UT_TANK] );
			x = RAND(0,STRIP_SIZE-13);
			y = RAND(480,530);
			units_add_dummy_tank( strip, x, y, RAND(30,40) );
		}
	}
	if ( bfield.delays[UT_RECON] == 0 ) {
		strip = units_get_rand_strip( UT_RECON );
		if ( strip != -1 ) {
			bfield.delays[UT_RECON] = RAND_IN_RANGE( bfield.delay_ranges[UT_RECON] );
			x = RAND(0,STRIP_SIZE-12);
			y = RAND(-50,-20);
			units_add_dummy_recon( strip, x, y, RAND(40,50) );
		}
	}
}

/* let gun look towards mouse position */
static void bfield_draw_gun( SDL_Surface *dest, int camera_x, int camera_y )
{
	SDL_Rect drect, srect;

	srect.w = drect.w = gun_w; srect.h = drect.h = gun_h;
	srect.x = bfield.img_gun_x_offset; srect.y = 0;
	drect.x = bfield.gun.x + bfield.gun_img_offset.x; 
	drect.y = bfield.gun.y + bfield.gun_img_offset.y;
	SDL_BlitSurface( img_gun, &srect, dest, &drect );
}

void bfield_draw( SDL_Surface *dest, int camera_x, int camera_y )
{
	Tree *tree;
	int i;
	SDL_Rect drect, srect;
	
	SDL_BlitSurface( background, 0, dest, 0 );
	bfield_draw_gun( dest, camera_x, camera_y );
	
	units_draw( dest, 0, 0 );

	bfield_draw_gun( dest, camera_x, camera_y );
	
	for ( i = 0; i < bfield.tree_count; i++ ) {
		tree = &bfield.trees[i];
		drect.x = tree->x; drect.y = tree->y;
		srect.w = drect.w = tree->w; 
		srect.h = drect.h = tree->h;
		srect.x = tree->img_x_offset;
		srect.y = tree->img_y_offset;
		SDL_BlitSurface( tree->img, &srect, dest, &drect );
	}
	
	particles_draw( dest, 0, 0 );
	shots_draw( dest, 0, 0 );
}
		
void bfield_update( int ms )
{
	int i;
	Vector dest;
	Unit *unit;
	
	/* add units */
	bfield_add_units( ms );

	/* move things */
	units_update(ms);
	shots_update(ms);
	particles_update(ms);

	/* check wether gun may fire */
	if ( bfield.gun_delay > 0 )
	if ( (bfield.gun_delay-=ms) <= 0 ) {
		bfield.gun_delay = 0;
		if ( !bfield.demo )
			SET_CURSOR( CURSOR_CROSSHAIR );
	}
	
	/* fire cannon automatically in demo mode */
	if ( bfield.demo && bfield.gun_delay == 0 && (bfield.demo_delay-=ms) <= 0 ) {
		/* soldiers or tanks? */
		if ( bfield.demo_hunt_soldiers == 0 )
		if ( (rand() % 3) == 0  )
			bfield.demo_hunt_soldiers = 4;
		/* select target */
		if ( bfield.demo_hunt_soldiers > 0 ) {
			unit = units_get_first_soldier();
			if ( unit ) {
				bfield.demo_hunt_soldiers--;
				if ( bfield.demo_hunt_soldiers == 0 )
					bfield.demo_delay = 2500; /* take a short break */
			}
		}
		else
			unit = units_get_first_vehicle();
		/* fire! */
		if ( unit ) {
			if ( unit->type != UT_SOLDIER )
				bfield.demo_delay = 2500;
			dest.x = unit->pos.x + unit->hx + RAND(-10,10);
			dest.y = unit->pos.y + unit->hy + RAND(-20,20);
			if ( unit->type != UT_SOLDIER )
				dest.y += ((unit->dir.y<0)?-30:40); 
			dest.z = 0;
			bfield_update_gun_dir( (int)dest.x, (int)dest.y );
			bfield_fire_gun( (unit->type==UT_SOLDIER)?ST_GRENADE:ST_BOMB, &dest, 60, 0 );
		}
	}

	/* check burning trees */
	for ( i = 0; i < bfield.tree_count; i++ )
		if ( bfield.trees[i].burn_time > 0 )
		if ( (bfield.trees[i].burn_time-=ms) <=0 ) {
			bfield.trees[i].burn_time = 0;
			bfield.trees[i].dead = 1;
			bfield.trees[i].img_y_offset += bfield.trees[i].h;
		}
}

int bfield_gun_is_ready()
{
	return (bfield.gun_delay==0);
}

void bfield_fire_gun( int type /* ST_... */, Vector *dest, double alpha, double power )
{
	int sx, sy;
	Vector pos;

	/* must not be too near to gun */
	if ( dest->x < STRIP_OFFSET )
		return;
	
	pos = bfield.gun;
	pos.x += fire_offsets[bfield.gun_dir].x;
	pos.y += fire_offsets[bfield.gun_dir].y;
	pos.z += fire_offsets[bfield.gun_dir].z;
	switch ( type ) {
		case ST_GRENADE:
			if ( player_ammo >= AMMO_GRENADE || bfield.demo ) {
				SET_DELAY(bfield.gun_delay,GRENADE_DELAY);
				shots_fire_grenade( &pos, dest, alpha, power );
				SDL_PlaySound( wav_cannon1 );
				if ( !bfield.demo )
					player_ammo -= AMMO_GRENADE;
				vec2xy( &pos, &sx, &sy );
				particles_set_muzzle_fire( sx, sy, 10 );
			}
			else
				if ( !bfield.demo )
					SET_CURSOR( CURSOR_RELOAD );
			break;
		case ST_BOMB:
			if ( player_ammo >= AMMO_BOMB || bfield.demo ) {
				SET_DELAY(bfield.gun_delay,BOMB_DELAY);
				shots_fire_bomb( &pos, dest, alpha, power );
				SDL_PlaySound( wav_cannon2 );
				if ( !bfield.demo ) {
					player_ammo -= AMMO_BOMB;
					SET_CURSOR( CURSOR_WAIT );
				}
				vec2xy( &pos, &sx, &sy );
				particles_set_muzzle_fire( sx, sy, 30 );
			}
			else
				if ( !bfield.demo )
					SET_CURSOR( CURSOR_RELOAD );
			break;
		case ST_CLUSTER:
			if ( player_ammo >= AMMO_CLUSTER || bfield.demo ) {
				SET_DELAY(bfield.gun_delay,CLUSTER_DELAY);
				dest->z = 50; /* detonate above target */
				SDL_PlaySound( wav_cannon2 );
				shots_fire_cluster( &pos, dest, 70, 0 );
				if ( !bfield.demo ) {
					player_ammo -= AMMO_CLUSTER;
					SET_CURSOR( CURSOR_WAIT );
				}
			}
			else
				if ( !bfield.demo )
					SET_CURSOR( CURSOR_RELOAD );

			break;
		case ST_NAPALM:
			if ( player_ammo >= AMMO_NAPALM || bfield.demo ) {
				SET_DELAY(bfield.gun_delay,NAPALM_DELAY);
				SDL_PlaySound( wav_cannon2 );
				shots_fire_napalm( &pos, dest, 70, 0 );
				if ( !bfield.demo ) {
					player_ammo -= AMMO_BOMB;
					SET_CURSOR( CURSOR_WAIT );
				}
			}
			else
				if ( !bfield.demo )
					SET_CURSOR( CURSOR_RELOAD );
			break;
	}
}

void bfield_reload_gun()
{
	if ( player_ammo < MAX_AMMO ) {
		player_ammo = MAX_AMMO;
		player_score += SCORE_RELOAD;
		if ( bfield.gun_delay == 0 )
			SET_CURSOR( CURSOR_CROSSHAIR );
	}
}

void bfield_handle_impact( Shot *shot )
{
	SDL_Rect drect;
	Vector dest;
	int i;
	int sx, sy;
	
	if ( shot->is_precise )
		shot->pos = shot->dest; /* impact at exact position */
	vec2xy( &shot->pos, &sx, &sy ); /* get screen position */
	
	switch ( shot->type ) {
	    case ST_GRENADE:
		particles_explode_grenade( sx, sy );
		drect.x = sx - (img_small_crater->w>>1);
		drect.y = sy - (img_small_crater->h>>1);
		drect.w = img_small_crater->w; drect.h = img_small_crater->h;
		SDL_BlitSurface( img_small_crater, 0, background, &drect );

		SDL_PlaySound( wav_expl1 );
		
		units_check_impact( sx, sy, 20 );
		break;
	    case ST_BOMB:
		particles_explode_bomb( sx, sy );
		drect.x = sx - (img_crater->w>>1);
		drect.y = sy - (img_crater->h>>1);
		drect.w = img_crater->w; drect.h = img_crater->h;
		SDL_BlitSurface( img_crater, 0, background, &drect );
			
		SDL_PlaySound( wav_expl2 );

		units_check_impact( sx, sy, 40 );
		break;
	    case ST_CLUSTER:
		particles_explode_clusterbomb( sx, sy );
		for ( i = 0; i < 16; i++ ) {
			dest.x = shot->dest.x + RAND(-50,50);
			dest.y = shot->dest.y + RAND(-50,50);
			dest.z = 0;
			shots_fire_grenade( 
					&shot->pos, &dest, 
					RAND(-40,0), 0.06 + (double)RAND(0, 20)/100 );
		}
		break;
	    case ST_NAPALM:
		for ( i = 0; i < 10; i++ ) {
			dest.x = shot->dest.x + RAND(-50,50);
			dest.y = shot->dest.y + RAND(-50,50);
			dest.z = 0;
			shots_fire_oil( 
					&shot->pos, &dest, 
					RAND(20,60), 0.01 + (double)RAND(0, 20)/100 );
			
			units_check_impact( sx, sy, 40 );
		}
		break;
	    case ST_OIL:
		particles_add_emitter( PT_FIRE, sx, sy, 5, 0.1, 6000 );
		break;
	}
}

void bfield_update_gun_dir( int mx, int my )
{
	double alpha;

	if ( mx <= bfield.gun.x ) {
		if ( my <= bfield.gun.y )
			bfield.gun_dir = 0;
		else
			bfield.gun_dir = 8;
	}
	else {
		alpha = atan( (my - bfield.gun.y) / (mx - bfield.gun.x ) );
		alpha = alpha * 180 / 3.14;
		bfield.gun_dir = ((alpha + 10) / 20) + 4; 
	}
	
	bfield.img_gun_x_offset = gun_w * bfield.gun_dir;
}
