/* powerup.m,
 *
 * Powerups are yummy collectables.  Collides only with the player
 * (and not robots) so they can't just be 0 damage projectiles.
 */

#include <allegro.h>
#include <assert.h>
#include <math.h>
#include "common.h"
#include "debris/ir-line.h"
#include "linklist.h"
#include "map.h"
#include "nuke.h"
#include "player.h"
#include "powerup.h"
#include "pregen-circles.h"
#include "seborrhea/seborrhea-spline.h"
#include "seborrhea/seborrhea.h"
#include "sound.h"
#include "units/robot.h"


static List *powerup_list;
static SebFile *powerup_sebum;
static Sebum<SebSample> *pickup_sample;
static SebSpline *the_path;


@implementation Powerup
+ initialize
{
    powerup_sebum = (SebFile *)[base_sebum getSebumByName:"powerup"];
    pickup_sample = (Sebum<SebSample> *)[powerup_sebum getSebumByName:"sound"];
    the_path = (SebSpline *)[powerup_sebum getSebumByName:"followme"];
    return self;
}

- init
{
    [super init];
    w = 35;			/* Slightly bigger than the sprite. */
    h = 35;
    change_tics = 100;
    return self;
}

- (void) givePowerupTo:(Player *)pl
{
    /* Play on a weapon channel, but with higher priority. */
    play_voice(pickup_sample, x, 224, PRIMARY_WEAPON_4);

    /* Also give the player some score. */
    [pl increaseScore:50];
}

- (id) setX:(double)x_ Y:(double)y_
{
    path_y_displacement = y_;

    /* If we are in the left half of the screen, go right first,
       otherwise left. */
    if (x_ < screen_w/2) {
	path = 0;
	path_x_displacement = x_;
    }
    else {
	path = 100;
	path_x_displacement = x_ - 150;//XXX: the_path[path].x;
    }

    return [super setX:x_ Y:y_];
}

- (void) draw:(BITMAP *)dest
{
    if ((change_tics < 30) && (change_tics % 3 == 0)) {
	[sprite drawTo:dest
		X:x - offsetX - [sprite width]/2
		Y:y - offsetY - [sprite height]/2
		Tint:0xff:0xff:0xff:0xc0];
    }
    else
	[super draw:dest];
}

- (BOOL) mayStillInfluenceGameplay { return (y-h < offsetY+screen_h); }

- (int) update
{
    double phi;

    /* Scroll slightly with the map. */
    path_y_displacement += [current_map scrollRate] / 2;

    /* Follow me, follow me, that's good... */
    [the_path follow:&path :&x :&y :999 /* Very fast. */
	      :path_x_displacement :path_y_displacement];

    if (path == 0)
	phi = [the_path angleBetween:0 :1];
    else
	phi = [the_path angleBetween:path-1 :path];

    [spawn_debris([IRLine class], x, y, LOW_LAYER) setAngle:phi];

    return [self mayStillInfluenceGameplay] ? THING_NORMAL : THING_DEAD;
}

	/* Collisions. */
- (void) getX:(double *)x_ Y:(double *)y_ W:(int *)w_ H:(int *)h_
{
    if (x_) *x_ = x;
    if (y_) *y_ = y;
    if (w_) *w_ = w;
    if (h_) *h_ = h;
}

- (BOOL) collidesWith:(Thing<DetectsCollision> *)object
{
    /* This shouldn't get called, since players will handle the
       collision detection for us. */
    assert("Powerup's collidesWith called.  WTF?" && NO);
    (void)object;
}
@end

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

@implementation PrimaryPowerup
- (void) setSpriteTo:(int)state_
{
    if (state_ == PRIMARY_WEAPON_LASER)
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"laser"]];
    else if (state_ == PRIMARY_WEAPON_TOOTHPASTE)
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"toothpaste"]];
    else
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"vulcan"]];
}

- init
{
    [super init];
    sprite = [SebAnimator new];
    state = rnd(FIRST_PRIMARY_WEAPON, LAST_PRIMARY_WEAPON);
    [self setSpriteTo:state];
    return self;
}

- (void) givePowerupTo:(Player *)pl
{
    [pl setWeapon:PRIMARY_WEAPON to:state];
    [super givePowerupTo:pl];
}

- (int) update
{
    if (--change_tics <= 0) {
        if (++state > LAST_PRIMARY_WEAPON)
            state = FIRST_PRIMARY_WEAPON;

	[self setSpriteTo:state];
        change_tics = 100;
    }

    return [super update];
}
@end


@implementation SecondaryPowerup
- (void) setSpriteTo:(int)state_
{
    if (state_ == SECONDARY_WEAPON_HOMING_MISSILE)
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"homing"]];
    else if (state_ == SECONDARY_WEAPON_MISSILE)
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"missile"]];
    else
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"pink"]];
}

- init
{
    [super init];
    sprite = [SebAnimator new];
    state = rnd(FIRST_SECONDARY_WEAPON, LAST_SECONDARY_WEAPON);
    [self setSpriteTo:state];
    return self;
}

- (void) givePowerupTo:(Player *)pl
{
    [pl setWeapon:SECONDARY_WEAPON to:state];
    [super givePowerupTo:pl];
}

- (int) update
{
    if (--change_tics <= 0) {
	if (++state > LAST_SECONDARY_WEAPON)
	    state = FIRST_SECONDARY_WEAPON;

	[self setSpriteTo:state];
        change_tics = 100;
    }

    return [super update];
}
@end


@implementation TertiaryPowerup
- (void) setSpriteTo:(int)state_
{
    if (state_ == TERTIARY_WEAPON_ROBOTS)
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"robot"]];
    else
	sprite = [(SebAnimator *)sprite setAnimation:(SebAnimation *)[powerup_sebum getSebumByName:"satellite"]];
}

- init
{
    [super init];
    sprite = [SebAnimator new];
    state = rnd(FIRST_TERTIARY_WEAPON, LAST_TERTIARY_WEAPON);
    [self setSpriteTo:state];
    return self;
}

- (void) givePowerupTo:(Player *)pl
{
    [pl setWeapon:TERTIARY_WEAPON to:state];
    [super givePowerupTo:pl];
}

- (int) update
{
    if (--change_tics <= 0) {
	if (++state > LAST_TERTIARY_WEAPON)
	    state = FIRST_TERTIARY_WEAPON;

	[self setSpriteTo:state];
        change_tics = 100;
    }

    return [super update];
}
@end

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

@implementation HealthPowerup
- init
{
    [super init];
    sprite = [powerup_sebum getSebumByName:"health"];
    back = [powerup_sebum getSebumByName:"healthbg"];
    return self;
}

- (void) givePowerupTo:(Player *)pl
{
    [pl receiveDamage:-4 type:DAMAGE_TYPE_POWERUP];
    [super givePowerupTo:pl];
}

- (void) draw:(BITMAP *)dest
{
    [back drawTo:dest X:x-offsetX Y:y-offsetY Angle:theta];
    [super draw:dest];
}

- (int) update
{
#if 0
    /* Tie fighter style. */
    if (path > 0)
	theta = [the_path angleBetween:path-1 :path];
    else
	theta = [the_path angleBetween:path :path+1];
    theta += M_PI_2;
#endif

    theta += deg2rad(5.0);
    simplify_angle(&theta);
    return [super update];
}
@end


@implementation NukePowerup
- init
{
    [super init];
    sprite = [powerup_sebum getSebumByName:"nuke"];
    back = [powerup_sebum getSebumByName:"saw"];
    return self;
}

- (void) givePowerupTo:(Player *)pl
{
    spawn_nuke(x, y, [pl playerID]);
    [super givePowerupTo:pl];
}

- (void) draw:(BITMAP *)dest
{
    [back drawTo:dest X:x-offsetX Y:y-offsetY Angle:theta];
    [super draw:dest];
}

- (int) update
{
    theta += deg2rad(8.0);
    simplify_angle(&theta);
    return [super update];
}
@end

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

void draw_powerups(BITMAP *dest)
{
    ListIterator *it;

    foreach (it, powerup_list)
        [[it getItem] draw:dest];
}

void update_powerups(void)
{
    ListIterator *it, *nx;
    int pl;

    foreach_nx (it, nx, powerup_list) {
	BOOL die = NO;
	Powerup *powerup = [it getItem];
	nx = [it next];

	if ([powerup update] == THING_DEAD)
	    die = YES;

	/* Check for collision with players. */
	for (pl = 0; !die && pl < MAX_PLAYERS; pl++) {
	    if (!player[pl])
		continue;

	    if ([player[pl] collidesWith:powerup]) {
		[powerup givePowerupTo:player[pl]];
		die = YES;
	    }
	}

	if (die) {
	    [powerup_list removeItem:powerup];
	    [powerup free];
	}
    }
}

Powerup *spawn_powerup(Class class, double x, double y)
{
    Powerup *powerup = [class new];

    if (powerup) {
        [powerup setX:x Y:y];
	[powerup_list insertItem:powerup];
    }

    return powerup;
}

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

void powerup_init(void)
{
    powerup_list = [LIST_NEW];
    assert(powerup_list);
}

void powerup_shutdown(void)
{
    if (powerup_list)
        powerup_list = [powerup_list free];
}
