/* weapon.m,
 *
 * This file contains some prebuilt weapon classes so we don't need to
 * rewrite the same code over and over.
 */

#include <math.h>
#include "common.h"
#include "projectile.h"
#include "weapon.h"


/*------------------------------------------------------------*/
/* PulseWeapon Protocol helpers. */
#define derive_setWaveDelay(wave)	(id) setWaveDelay:(int)t { wave = t; return self; }
#define derive_setShotsPerWave(num)	(id) setShotsPerWave:(int)n { num = n; return self; }
#define derive_setShotDelayWaveDelay	(id) setShotDelay:(int)t1 WaveDelay:(int)t2 { return [[self setShotDelay:t1] setWaveDelay:t2]; }
#define derive_currentShot(num)		(int) currentShot { return num; }

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

/* Weapon handles single-shot weapons that spawns projectiles at the
   centre of a unit (and at an angle). */
@implementation Weapon
- init
{
    [super init];
    fire_tics = 15;
    return self;
}

- (id) setProjectile:(Class)c { projectile = c; return self; }
- (id) setShotDelay:(int)t  { shot_delay = t; return self; }
- (id) setFireTics:(int)t { fire_tics = t; return self; }
- (int) fireTics  { return fire_tics; }
- (BOOL) fireFromX:(double)x Y:(double)y { return [self fireFromX:x Y:y angle:-M_PI_2]; }

- (BOOL) fireFromX:(double)x Y:(double)y angle:(double)theta
{
    if (fire_tics > 0) {
	fire_tics--;
	return NO;
    }
    else {
	[spawn_projectile(projectile, x, y, NO) setAngle:theta];

	fire_tics = shot_delay;
	return YES;
    }
}
@end


/* PulseWeapon handles automatic weapons that spawns projectiles at
   the centre of a unit (and at an angle). */
@implementation PulseWeapon
- init
{
    [super init];
    current_shot_num = 0;
    return self;
}

- derive_setWaveDelay(wave_delay);
- derive_setShotDelayWaveDelay;
- derive_setShotsPerWave(shots_per_wave);
- derive_currentShot(current_shot_num);

- (BOOL) fireFromX:(double)x Y:(double)y angle:(double)theta
{
    if ([super fireFromX:x Y:y angle:theta]) {

	current_shot_num++;
	if (current_shot_num >= shots_per_wave) {
	    current_shot_num = 0;
	    fire_tics = wave_delay;
	}
	return YES;
    }
    else
	return NO;
}
@end

/*------------------------------------------------------------*/
/* OffcentreWeapons handle weapons for rotatable units that fire from
   an off-centre position. */

@implementation OffcentreWeapon
- (id) setXDisplacement:(double)x YDisplacement:(double)y { xd = x; yd = y; return self; }

- (BOOL) fireFromX:(double)x Y:(double)y angle:(double)theta
{
    if (fire_tics > 0) {
	fire_tics--;
	return NO;
    }
    else {
	x += yd*sin(theta) + xd*sin(theta+M_PI_2);
	y += yd*cos(theta) + xd*cos(theta+M_PI_2);

	[spawn_projectile(projectile, x, y, NO) setAngle:theta];

	fire_tics = shot_delay;
	return YES;
    }
}
@end


@implementation OffcentrePulseWeapon
- init
{
    [super init];
    current_shot_num = 0;
    return self;
}

- derive_setWaveDelay(wave_delay);
- derive_setShotDelayWaveDelay;
- derive_setShotsPerWave(shots_per_wave);
- derive_currentShot(current_shot_num);

- (BOOL) fireFromX:(double)x Y:(double)y angle:(double)theta
{
    if ([super fireFromX:x Y:y angle:theta]) {

	current_shot_num++;
	if (current_shot_num >= shots_per_wave) {
	    current_shot_num = 0;
	    fire_tics = wave_delay;
	}
	return YES;
    }
    else
	return NO;
}
@end

/*------------------------------------------------------------*/
/* Dual weapons are for two identical offcentre weapons except for
   their y location, which is on the opposite side of the ship.

   ShotDelay > 0: Other barrel will fire in ShotDelay tics.
   ShotDelay < 0: Barrels fire simultaneously, w/ ShotDelay tics
		  between the pulses.  Hard to describe.
 */

@implementation DualWeapon
- init
{
    [super init];
    current_shot_num = 0;
    return self;
}

- derive_setWaveDelay(wave_delay);
- derive_setShotDelayWaveDelay;
- derive_setShotsPerWave(shots_per_wave);
- derive_currentShot(current_shot_num);

- (BOOL) fireFromX:(double)x Y:(double)y angle:(double)theta
{
    if ([super fireFromX:x Y:y angle:theta]) {
	yd = -yd;

	/* Fire both barrels simulaneously. */
	if (shot_delay <= 0)
	    [super fireFromX:x Y:y angle:theta];

	current_shot_num++;
	if (current_shot_num >= shots_per_wave) {
	    current_shot_num = 0;
	    fire_tics = wave_delay;
	}
	else
	    fire_tics = ABS(shot_delay);

	return YES;
    }
    return NO;
}
@end

/*------------------------------------------------------------*/
/* Ring weapons are weapons that fire in all directions at once,
   forming a ring of bullets.  This is not for sweeping type guns. */

@implementation RingWeapon
- (id) setStepSize:(double)ss { step_size = ss; return self; }
- (id) setNumSteps:(int)steps { num_steps = steps; return self; }

- (BOOL) fireFromX:(double)x Y:(double)y angle:(double)theta
{
    int steps = num_steps;

    if (fire_tics > 0) {
	fire_tics--;
	return NO;
    }

    if (steps) {
	while (steps) {
	    [spawn_projectile(projectile, x, y, NO) setAngle:theta];
	    theta += step_size;
	    steps--;
	}
    }
    else {
	/* theta = angle to begin firing from. */
	while (theta < 2*M_PI) {
	    [spawn_projectile(projectile, x, y, NO) setAngle:theta];
	    theta += step_size;
	}
    }

    fire_tics = shot_delay;
    return YES;
}
@end
