/******************************************************************************
*******************************************************************************

	Space Impakto DS
	relminator
	http://rel.betterwebber.com


	Cturret class


*******************************************************************************
******************************************************************************/


#include <nds.h>

#include "cearn_atan.h"
#include "Cturret.h"


/*============================================================================


Cturret Special functions to be used with function pointers within the 
Cturret class.


=============================================================================*/

/******************************************************************************


******************************************************************************/
void rotate_1(Cturret *t)
{

	t->angle = (t->angle + t->angle_speed * t->angle_dir) & 32767;
    
}

/******************************************************************************


******************************************************************************/
void rotate_2(Cturret *t)
{

	
	if (t->angle > t->max_angle)         
        t->angle_dir = -t->angle_dir;         
    
	if (t->angle < t->min_angle)         
        t->angle_dir = -t->angle_dir;         
	
	t->angle = (t->angle + t->angle_speed * t->angle_dir);
}

/******************************************************************************


******************************************************************************/
void rotate_3(Cturret *t)
{

	if (t->angle > t->max_angle)         
        t->angle -= t->max_angle;         
    
	if (t->angle < t->min_angle)         
        t->angle += t->max_angle;  
		
	t->angle = (t->angle + t->angle_speed * t->angle_dir);       
	
}

/******************************************************************************


******************************************************************************/
void single_shot(Cturret *t)
{

	int cos_a = cosLerp(t->angle);
	int sin_a = sinLerp(t->angle);
	

	for (int i = 0; i < MAX_BULLETS; i++)
	{
		if (!t->p_bullets[i].active)
		{
			t->p_bullets[i].x = t->x + t->x_radius * cos_a; // 12
			t->p_bullets[i].y = t->y + t->y_radius * sin_a; // 12
			t->p_bullets[i].dx = (cos_a * t->bp.speed) >> 12;
			t->p_bullets[i].dy = (sin_a * t->bp.speed) >> 12;
			t->p_bullets[i].x_accel = t->p_bullets[i].dx >> 1; 
			t->p_bullets[i].y_accel = t->p_bullets[i].dy >> 1;
			t->p_bullets[i].speed = t->bp.speed;
			t->p_bullets[i].gx = t->bp.gx;
			t->p_bullets[i].gy = t->bp.gy;
			t->p_bullets[i].delay = t->bp.delay;
			t->p_bullets[i].counter = 0;
			t->p_bullets[i].smart = t->bp.smart;
			t->p_bullets[i].width = t->bp.width;
			t->p_bullets[i].height = t->bp.height;
			t->p_bullets[i].frame = 0;
			t->p_bullets[i].start_frame = t->bp.start_frame;
			t->p_bullets[i].num_frames = t->bp.num_frames;
			t->p_bullets[i].anim_delay = t->bp.anim_delay;
			t->p_bullets[i].set_state(t->bp.state);
			t->p_bullets[i].counter = 0;
			t->p_bullets[i].active = true;
			t->p_bullets[i].target = t->target;
			break;
		}
		
	}

}

/******************************************************************************


******************************************************************************/
void wave_spray_normal(Cturret *t)
{
	int angle = t->angle;
	int factor;
	int cos_a;
	int sin_a;
	
	for (int j = 0; j < t->bullets_per_shot; j++)
	{
		
		angle += (t->spread_angle * t->angle_dir);		// 12
		factor = t->bp.speed + ( (t->bp.speed_delta * (j+1)) / t->bullets_per_shot);  // 12
		cos_a = cosLerp(angle);
		sin_a = sinLerp(angle);
		for (int i = 0; i < MAX_BULLETS; i++)
		{
			if (!t->p_bullets[i].active)
			{
				t->p_bullets[i].x = t->x + t->x_radius * cos_a; // 12
				t->p_bullets[i].y = t->y + t->y_radius * sin_a; // 12
				t->p_bullets[i].dx = (cos_a * factor) >> 12;
				t->p_bullets[i].dy = (sin_a * factor) >> 12;
				t->p_bullets[i].x_accel = t->p_bullets[i].dx >> 1; 
				t->p_bullets[i].y_accel = t->p_bullets[i].dy >> 1;
				t->p_bullets[i].speed = t->bp.speed;
				t->p_bullets[i].gx = t->bp.gx;
				t->p_bullets[i].gy = t->bp.gy;
				t->p_bullets[i].delay = t->bp.delay;
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].smart = t->bp.smart;
				t->p_bullets[i].width = t->bp.width;
				t->p_bullets[i].height = t->bp.height;
				t->p_bullets[i].frame = 0;
				t->p_bullets[i].start_frame = t->bp.start_frame;
				t->p_bullets[i].num_frames = t->bp.num_frames;
				t->p_bullets[i].anim_delay = t->bp.anim_delay;
				t->p_bullets[i].set_state(t->bp.state);
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].active = true;
				t->p_bullets[i].target = t->target;
				break;
			}
			
		}
		
		
	}
	
}


/******************************************************************************


******************************************************************************/
void wave_spray_centered(Cturret *t)
{
	int angle_fix = 1 * (t->bullets_per_shot & 1);
	int bps_div_2 = floattof32(t->bullets_per_shot/2);
	int factor;
	int centerfactor;
	int cos_a;
	int sin_a;
	int bullet_offset = 1 << 12;
	if (t->wave_counter & 1) bullet_offset = t->bp.offset; 
	
	int spread_rot = (t->spread_angle * bullet_offset) >> 12;
	int angle = t->angle - (((t->bullets_per_shot + angle_fix)/2) * (spread_rot * t->angle_dir));
	
	for (int j = 0; j < t->bullets_per_shot; j++)
	{
		angle += (t->spread_angle * t->angle_dir);		// 12
		centerfactor = ( (abs(floattof32(j) - bps_div_2) << 12) / bps_div_2 ); //12
		factor = t->bp.speed +  ( (t->bp.speed_delta * centerfactor) >> 12);  // 12
		cos_a = cosLerp(angle);
		sin_a = sinLerp(angle);
		for (int i = 0; i < MAX_BULLETS; i++)
		{
			if (!t->p_bullets[i].active)
			{
				t->p_bullets[i].x = t->x + t->x_radius * cos_a; // 12
				t->p_bullets[i].y = t->y + t->y_radius * sin_a; // 12
				t->p_bullets[i].dx = (cos_a * factor) >> 12;
				t->p_bullets[i].dy = (sin_a * factor) >> 12;
				t->p_bullets[i].x_accel = t->p_bullets[i].dx >> 1; 
				t->p_bullets[i].y_accel = t->p_bullets[i].dy >> 1;
				t->p_bullets[i].speed = t->bp.speed;
				t->p_bullets[i].gx = t->bp.gx;
				t->p_bullets[i].gy = t->bp.gy;
				t->p_bullets[i].delay = t->bp.delay;
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].smart = t->bp.smart;
				t->p_bullets[i].width = t->bp.width;
				t->p_bullets[i].height = t->bp.height;
				t->p_bullets[i].frame = 0;
				t->p_bullets[i].start_frame = t->bp.start_frame;
				t->p_bullets[i].num_frames = t->bp.num_frames;
				t->p_bullets[i].anim_delay = t->bp.anim_delay;
				t->p_bullets[i].set_state(t->bp.state);
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].active = true;
				t->p_bullets[i].target = t->target;
				break;
			}
			
		}
		
		
	}
	
}



/******************************************************************************


******************************************************************************/
void wave_spread_offset(Cturret *t)
{

	int cos_a;
	int sin_a;

    int angle_fix = 1 * (t->bullets_per_shot & 1);

	
	

	int bullet_offset = 1 << 12; 
    int b_delta;
	
	if (t->wave_counter & 1) bullet_offset = t->bp.offset;
	
	int spread_rot = (t->spread_angle * bullet_offset) >> 12;
	int angle = t->angle - (((t->bullets_per_shot + angle_fix)/2) * (spread_rot * t->angle_dir));
		
	if (t->wave_counter == 0)
        b_delta = (t->bp.speed + (t->bp.speed_delta * (t->max_wave)));
    else
        b_delta = (t->bp.speed + (t->bp.speed_delta * (t->wave_counter-1)));
    
	
	for (int j = 0; j < t->bullets_per_shot; j++)
	{
		angle += (spread_rot * t->angle_dir);		// 12
		cos_a = cosLerp(angle);
		sin_a = sinLerp(angle);
		for (int i = 0; i < MAX_BULLETS; i++)
		{
			if (!t->p_bullets[i].active)
			{
				t->p_bullets[i].x = t->x + t->x_radius * cos_a; // 12
				t->p_bullets[i].y = t->y + t->y_radius * sin_a; // 12
				t->p_bullets[i].dx = (cos_a * b_delta) >> 12;
				t->p_bullets[i].dy = (sin_a * b_delta) >> 12;
				t->p_bullets[i].x_accel = t->p_bullets[i].dx >> 1; 
				t->p_bullets[i].y_accel = t->p_bullets[i].dy >> 1;
				t->p_bullets[i].speed = t->bp.speed;
				t->p_bullets[i].gx = t->bp.gx;
				t->p_bullets[i].gy = t->bp.gy;
				t->p_bullets[i].delay = t->bp.delay;
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].smart = t->bp.smart;
				t->p_bullets[i].width = t->bp.width;
				t->p_bullets[i].height = t->bp.height;
				t->p_bullets[i].frame = 0;
				t->p_bullets[i].start_frame = t->bp.start_frame;
				t->p_bullets[i].num_frames = t->bp.num_frames;
				t->p_bullets[i].anim_delay = t->bp.anim_delay;
				t->p_bullets[i].set_state(t->bp.state);
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].active = true;
				t->p_bullets[i].target = t->target;
				break;
			}
			
		}
		
		
	}
	
}


/******************************************************************************


******************************************************************************/
void wave_spray_target(Cturret *t)
{
	int angle_fix = 1 * (t->bullets_per_shot & 1);
	int bps_div_2 = floattof32(t->bullets_per_shot/2);
	int factor;
	int centerfactor;
	int cos_a;
	int sin_a;
	int cos_r;
	int sin_r;
	int bullet_offset = 1 << 12;
	if (t->wave_counter & 1) bullet_offset = t->bp.offset; 
	
	int spread_rot = (t->spread_angle * bullet_offset) >> 12;
	int angle = t->angle - (((t->bullets_per_shot + angle_fix)/2) * (spread_rot * t->angle_dir));
	
	cos_r = cosLerp(t->angle);
	sin_r = sinLerp(t->angle);

	
	for (int j = 0; j < t->bullets_per_shot; j++)
	{
		angle += (t->spread_angle * t->angle_dir);		// 12
		centerfactor = ( (abs(floattof32(j) - bps_div_2) << 12) / bps_div_2 ); //12
		factor = t->bp.speed +  ( (t->bp.speed_delta * centerfactor) >> 12);  // 12
		cos_a = cosLerp(angle);
		sin_a = sinLerp(angle);
		for (int i = 0; i < MAX_BULLETS; i++)
		{
			if (!t->p_bullets[i].active)
			{
				t->p_bullets[i].x = t->x + t->x_radius * cos_a; // 12
				t->p_bullets[i].y = t->y + t->y_radius * sin_a; // 12
				t->p_bullets[i].dx = (cos_r * factor) >> 12;
				t->p_bullets[i].dy = (sin_r * factor) >> 12;
				t->p_bullets[i].x_accel = t->p_bullets[i].dx >> 1; 
				t->p_bullets[i].y_accel = t->p_bullets[i].dy >> 1;	
				t->p_bullets[i].speed = t->bp.speed;
				t->p_bullets[i].gx = t->bp.gx;
				t->p_bullets[i].gy = t->bp.gy;
				t->p_bullets[i].delay = t->bp.delay;
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].smart = t->bp.smart;
				t->p_bullets[i].width = t->bp.width;
				t->p_bullets[i].height = t->bp.height;
				t->p_bullets[i].frame = 0;
				t->p_bullets[i].start_frame = t->bp.start_frame;
				t->p_bullets[i].num_frames = t->bp.num_frames;
				t->p_bullets[i].anim_delay = t->bp.anim_delay;
				t->p_bullets[i].set_state(t->bp.state);
				t->p_bullets[i].counter = 0;
				t->p_bullets[i].active = true;
				t->p_bullets[i].target = t->target;
				break;
			}
			
		}
		
		
	}
	
}

/*============================================================================


Cturret Class
Implementation


=============================================================================*/

/******************************************************************************


******************************************************************************/
Cturret::Cturret()
{

	index = 0;
	active = false;
	pos = &default_coord;
	target = &default_coord;
	do_wave = single_shot;
	rotate = rotate_1;
	
}

/******************************************************************************


******************************************************************************/
Cturret::~Cturret()
{

	parameters.clear();
	index = 0;
	
	// null pointers so that we don't have probs with
	// "dangling pointers"
	
	pos = NULL;
	target = NULL;
	do_wave = NULL;
	rotate = NULL;
}

/******************************************************************************


******************************************************************************/
void Cturret::update(Csound *sound)
{
	x = pos->x;
	y = pos->y;

	// dec activetime until < 0
	// then increment index circularly
	// and load new parameters
	active_time--;
	if (active_time < 0)
	{
		index++;
		if (index >= parameters.size())  // rotate index 
		{
			index = 0;
		}
		
		// reload new turret params
		reset(parameters[index]);
		
		
	}
	
	// do ai
	shoot(sound);
	
}

/******************************************************************************


******************************************************************************/
void Cturret::update()
{
	x = pos->x;
	y = pos->y;

	// dec activetime until < 0
	// then increment index circularly
	// and load new parameters
	active_time--;
	if (active_time < 0)
	{
		index++;
		if (index >= parameters.size())  // rotate index 
		{
			index = 0;
		}
		
		// reload new turret params
		reset(parameters[index]);
		
		
	}
	
	// do ai
	shoot();
	
}

/******************************************************************************


******************************************************************************/
void Cturret::init(Tturret_params &_tp, Cbullet *bullets)
{
	
	p_bullets = bullets;
	
	copy_params(_tp);

	set_mode();
	set_rotation();
	
	index = 0;
	
}

/******************************************************************************


******************************************************************************/
void Cturret::reset(Tturret_params &_tp)
{
	
	copy_params(_tp);

	set_mode();
	set_rotation();
	
	
}

/******************************************************************************

called at enemy kill()

******************************************************************************/
void Cturret::kill()
{

	parameters.clear();
	
	active = false;
	
	index = 0;
}


/******************************************************************************


******************************************************************************/
void Cturret::destroy()
{


	parameters.clear();
	
	active = false;
	
	index = 0;
	
	
	// null pointers so that we don't have probs with
	// "dangling pointers"
	
	pos = NULL;
	target = NULL;
	do_wave = NULL;
	rotate = NULL;
	
}

/******************************************************************************


******************************************************************************/

void Cturret::copy_params(Tturret_params &_tp)
{
	active_time			= _tp.active_time;
	active				= _tp.active;					// non-fp
	smart				= _tp.smart;					// if targeting or not
	x_radius			= _tp.x_radius;					// non-fp
	y_radius			= _tp.y_radius;					// non-fp
	angle				= _tp.angle;
	min_angle			= _tp.min_angle;
	max_angle			= _tp.max_angle;
	angle_speed			= _tp.angle_speed;				// + rotation speed
	angle_dir			= _tp.angle_dir;				// -1 or +1  determines rotation direction
	spread_angle		= _tp.spread_angle;				// distance of each bullet on a spread
	bullets_per_shot	= _tp.bullets_per_shot;
	rotation_type		= _tp.rotation_type;			// non-fp, whether back anf forth or not
	spawn_delay			= _tp.spawn_delay;				// non-fp
	max_spawn_delay		= _tp.max_spawn_delay;			// non-fp
	
	wavemode			= _tp.wavemode;
	
	wave_counter		= _tp.wave_counter;				// non-fp
	max_wave			= _tp.max_wave;					// non-fp maximum bullets per wave
	max_wave_delay		= _tp.max_wave_delay;			// non-fp maximum delay for next shot in a wave
	
	// bullet params
	bp = _tp.bp;
	
}

/******************************************************************************


******************************************************************************/
void Cturret::set_mode()
{

	
	switch (wavemode)
	{
		case EWM_SINGLE:
			do_wave = single_shot;
			break;
		case EWM_SPRAY_NORMAL:
			do_wave = wave_spray_normal;
			break;
		case EWM_SPRAY_CENTERED:
			do_wave = wave_spray_centered;
			break;
		case EWM_SPREAD_OFFSET:
			do_wave = wave_spread_offset;
			break;
		case EWM_SPRAY_TARGET:
			do_wave = wave_spray_target;
			break;
		default:
			do_wave = single_shot;
	}
}


/******************************************************************************


******************************************************************************/
void Cturret::set_rotation()
{
	
	switch (rotation_type)
	{
		case 1:
			rotate = rotate_1;
			break;
		case 2:
			rotate = rotate_2;
			break;
		case 3:
			rotate = rotate_3;
			break;
		default:
			rotate = rotate_1;
	}
}

/******************************************************************************


******************************************************************************/
void Cturret::shoot(Csound *sound)
{
	spawn_delay--;
	if (smart)
	{
		if (spawn_delay < 0)
		{
			if (wave_counter == 0)
			{
				angle = atan2Lookup( target->x - (x >> 12), target->y - (y >> 12) );
			}
			
			spawn_delay = max_wave_delay;
			
			wave_counter++;
			
			if (wave_counter > max_wave)
			{
				wave_counter = 0;
				spawn_delay = max_spawn_delay;
			}
			
			sound->play_SFX(E_SFX_TURRET_SHOOT);
			do_wave(this);
		}
	
	}
	else
	{
		
		if (spawn_delay < 0)
		{
			sound->play_SFX(E_SFX_TURRET_SHOOT);
			do_wave(this);
			
			spawn_delay = max_wave_delay;
			
			wave_counter++;
			
			if (wave_counter > max_wave)
			{
				wave_counter = 0;
				spawn_delay = max_spawn_delay;
				rotate(this);
			}
			
			
		}
		
	}

	
}


/******************************************************************************


******************************************************************************/
void Cturret::shoot()
{
	spawn_delay--;
	if (smart)
	{
		if (spawn_delay < 0)
		{
			if (wave_counter == 0)
			{
				angle = atan2Lookup( target->x - (x >> 12), target->y - (y >> 12) );
			}
			
			spawn_delay = max_wave_delay;
			
			wave_counter++;
			
			if (wave_counter > max_wave)
			{
				wave_counter = 0;
				spawn_delay = max_spawn_delay;
			}
			
			do_wave(this);
		}
	
	}
	else
	{
		
		if (spawn_delay < 0)
		{
			do_wave(this);
			
			spawn_delay = max_wave_delay;
			
			wave_counter++;
			
			if (wave_counter > max_wave)
			{
				wave_counter = 0;
				spawn_delay = max_spawn_delay;
				rotate(this);
			}
			
			
		}
		
	}

	
}

/******************************************************************************


******************************************************************************/
