/* boss1.m,
 *
 * This is the boss for the space level.  No shadows are required.
 * 20% Bonus damage for vulcan.
 */

#include <allegro.h>
#include <assert.h>
#include <math.h>
#ifndef NO_FBLEND
# include "fblend/include/fblend.h"
#endif
#include "colour-conv.h"
#include "common.h"
#include "debris/explosion.h"
#include "debris/large-chunk.h"
#include "debris/particle.h"
#include "debris/satellite-trail.h"
#include "end-level.h"
#include "map.h"
#include "projectiles/fireball.h"
#include "projectiles/pulse-laser.h"
#include "projectiles/swarm-missile.h"
#include "seborrhea/seborrhea-allegro.h"
#include "seborrhea/seborrhea.h"
#include "sound.h"
#include "unit-seeking.h"
#include "units/boss1.h"


#define foreach_child(c)	for (c = 0; c < BOSS1_NUM_CHILDREN; c++)
#define ROTATION_DIST		90.0
#define ROTATION_TICS		(ROTATION_DIST)
#define EXTENSION_DIST		45.0
#define EXTENSION_TICS		(2*EXTENSION_DIST)
#define ROT_PLUS_EXTENSION_TICS	(ROTATION_TICS+EXTENSION_TICS)

#define FANG_FIRE_DELAY			80
#define LASERBALL_CREATION_FRAMES	9

#define BODY_HEALTH		200
#define SMALL_WING_HEALTH	600
#define LARGE_BODY_HEALTH	500
#define STAGE1_HEALTH		(BODY_HEALTH+SMALL_WING_HEALTH+LARGE_BODY_HEALTH)
#define STAGE2_HEALTH		(BODY_HEALTH+SMALL_WING_HEALTH)
#define STAGE3_HEALTH		(BODY_HEALTH)


static SebFile *boss_sebum;
static SebAnimation *fire_anim;
static SebAnimation *laserball_anim;

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

@interface Boss1SmallRightWing: Boss1SmallLeftWing
@end

@interface Boss1LargeLeftWingDebri: Debri
@end

@interface Boss1LargeRightWingDebri: Boss1LargeLeftWingDebri
@end

@interface Boss1LaserBall: Projectile
{
    int fire_tics;
    int fire_method, shots_remaining;
    double fire_angle;
    unsigned int frame;
}
@end

@interface Boss1Satellite: Unit <Boss1Child>
{
    Unit<OwnsChildren> *parent;
    double phi;			/* Angle used for movement. */
    double sign;
    double distance;
}
@end

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

@implementation Boss1
+ (BOOL) loadPrerequisites
{
    return (LOAD_PROJECTILE_DATA_FOR(Fireball) &&
	    LOAD_PROJECTILE_DATA_FOR(PulseLaser) &&
	    LOAD_PROJECTILE_DATA_FOR(SwarmMissile));
}

+ (BOOL) loadData
{
    set_colour_conversion_keep_alpha();

    boss_sebum = [SebFile new];
    if (not [boss_sebum loadSebumDirectory:"data/boss1"])
	return NO;

    set_color_conversion(COLORCONV_TOTAL);

    fire_anim = (SebAnimation *)[boss_sebum getSebumByName:"fang-fire"];
    laserball_anim = (SebAnimation *)[boss_sebum getSebumByName:"laserball"];
    return YES;
}

+ (void) shutdown
{
    FREE_SEBFILE(boss_sebum);
    fire_anim = NULL;
    laserball_anim = NULL;
}

- init
{
    [super init];
    sprite = [boss_sebum getSebumByName:"body-large"];
    health = STAGE1_HEALTH;
    w = [sprite width];
    h = [sprite height];

    outer_gun[0] = [[[[[[RingWeapon new]
			   setProjectile:[Fireball class]]
			  setShotDelay:100] setFireTics:30]
			setStepSize:deg2rad(5.0)] setNumSteps:5];
    outer_gun[1] = [[[[[[RingWeapon new]
			   setProjectile:[Fireball class]]
			  setShotDelay:100] setFireTics:80]
			setStepSize:deg2rad(5.0)] setNumSteps:5];

    flags |= FLAG_MOVING_ENABLED|FLAG_FIRING_ENABLED;

    return self;
}

- free
{
    if (outer_gun[0]) outer_gun[0] = [outer_gun[0] free];
    if (outer_gun[1]) outer_gun[1] = [outer_gun[1] free];
    return [super free];
}

- (void) drawHealthMeter:(BITMAP *)dest
{
    int ww;

    if (flags & FLAG_DYING)
	return;

    blit([(SebImageAllegro *)[base_sebum getSebumByName:"glue/meter-gray"] bitmap],
	 dest, 0, 0, dest->w/2-75, dest->h-10, 150, 8);

    if (health <= STAGE2_HEALTH)
	ww = 75 * health / STAGE2_HEALTH;
    else
	ww = 75 * (health-SMALL_WING_HEALTH) / (BODY_HEALTH+LARGE_BODY_HEALTH);

    blit([(SebImageAllegro *)[base_sebum getSebumByName:"glue/meter-purple"] bitmap],
	 dest, 75-ww, 1, dest->w/2-ww, dest->h-9, 2*ww, 6);
}

- (void) disownChild:(Unit *)unit
{
    int c;

    /* This is only used by satellites. */
    assert(unit);
    foreach_child (c)
	if (child[c] == unit) {
	    child[c] = nil;
	    return;
	}
    assert("Attempted to disown non-child-unit from Boss1");
}

- (void) draw:(BITMAP *)dest
{
    if (fang_y > 0) {
	int x_, y_;
	int frame = 0;
	SebImageAllegro *spr;

	if (flags & FLAG_FIRING_ENABLED) {
	    int nframes = [fire_anim numFrames]; 

	    if (fire_tics > 0 && fire_tics < nframes*2) {
		frame = nframes - (fire_tics/2) - 1;
	    }
	    else if (fire_tics > FANG_FIRE_DELAY - 10) {
		frame = nframes - 1;
	    }
	    else if (fire_tics > FANG_FIRE_DELAY - 10 - nframes*2) {
		frame = nframes - (FANG_FIRE_DELAY - 10 - fire_tics)/2 - 1;
		frame = MIN(frame, nframes-1);
	    }
	}

	spr = (SebImageAllegro *)[fire_anim getFrame:frame];

	x_ = x-[spr width]/2-offsetX;
	y_ = y-[spr height]/2-offsetY+fang_y;

#ifndef NO_FBLEND
        fblend_rgba_trans([(SebImageAllegro *)spr bitmap], dest, x_, y_, 0);
#else					/* Normal Allegro routines. */
        set_alpha_blender();
        draw_trans_sprite(dest, [(SebImageAllegro *)sprite bitmap], x_, y_);
#endif					/* End: NO_FBLEND */
    }
    [super draw:dest];
}

- (void) spawnDyingExplosions
{
    if (health < -50)			/* 2 seconds. */
	spawn_debris([LargeChunkCoffee class], x, y, HIGH_LAYER);
    if (health > -150) {		/* 3 seconds. */
	spawn_debris([BigExplosion class],
		     rnd(x-w/2, x+w/2), rnd(y-h/2, y+h/2), HIGH_LAYER);
	play_explosion_sample(x);
    }
    else {
	if (boss == self)
	    boss = nil;

	end_level(5*SECONDS);
        [self delete];
    }

    health--;
    flash_tics += 2;
}

- (void) die
{
    int c;

    foreach_child (c) {
	if (child[c]) {
	    [child[c] die];
	    child[c] = NULL;
	}
    }

    [super die];
}

- (int) update
{
    if (not (flags & FLAG_DYING) &&
	not (child[BOSS1_SATELLITE1] && child[BOSS1_SATELLITE2])) {
	satellite_tics++;

	/* Spawn every second. */
	if (satellite_tics >= 50) {
	    satellite_tics = 0;

	    Unit<Boss1Child> *unit;

	    unit = spawn_unit([Boss1Satellite class], x, y, ACTIVE_AIR_LIST);
	    [unit setParent:self];

	    if (not child[BOSS1_SATELLITE1])
		child[BOSS1_SATELLITE1] = [unit setAngle:deg2rad( 45.0)];
	    else
		child[BOSS1_SATELLITE2] = [unit setAngle:deg2rad(-45.0)];
	}
    }

    if (counter_attack_tics > 0)
	counter_attack_tics--;

    return [super update];
}

- (BOOL) readyToActivate
{
    if ([super readyToActivate]) {
	boss = self;

	/* Spawn these children later. */
	child[BOSS1_SMALL_LEFT_WING]  = nil;
	child[BOSS1_SMALL_RIGHT_WING] = nil;
	child[BOSS1_SATELLITE1] = nil;
	child[BOSS1_SATELLITE2] = nil;
	satellite_tics = 0;

	[(<SebSample>)[boss_sebum getSebumByName:"evil"] playWithVolume:sound_vol/SOUND_VOLMAX];
	return YES;
    }
    else
	return NO;
}

- (void) fire
{
    if (health > STAGE2_HEALTH) {
	/* Fire the guns mounted on the large wings. */
	double theta;

	theta = ANGLE_TO_CLOSEST_ALLY(x-90,y+80) - 2*deg2rad(5.0);
	[outer_gun[0] fireFromX:x-90 Y:y+80 angle:theta];

	theta = ANGLE_TO_CLOSEST_ALLY(x+90,y+80) - 2*deg2rad(5.0);
	[outer_gun[1] fireFromX:x+90 Y:y+80 angle:theta];
    }
    else if (fang_y > 0) {
	/* Our fangs are out for spawning laser balls. */
	fire_tics--;

	if (fire_tics <= 0) {
	    flags &=~FLAG_INVINCIBLE;
	    fire_tics = FANG_FIRE_DELAY;
	    spawn_projectile([Boss1LaserBall class], x, y+65, NO);
	}
	else if (flags & FLAG_INVINCIBLE)
	    fang_y += 1.0/4.0;
    }
}

- (void) move
{
    if (fang_y > 1 && fire_tics > 60)	/* Just spawned laser ball. */
	return;

    {					/* Sweep. */
	Unit *target = find_closest_unit(x, y, ALLY_LIST);

	if (target) {
	    double dest_x, dest_y;

	    [target getX:&dest_x Y:&dest_y];

	    /* Lazy-style chasing. */
	    if (x > dest_x + 75)
		xv -= .5;
	    else if (x < dest_x - 75)
		xv += .5;
	}

	xv = MID(-2.5, xv*0.95, 2.5);
	x = MID(30, x + xv, screen_w - 30);
    }

    if (health <= STAGE2_HEALTH) {	/* Creep downwards. */
	if (y - offsetY < screen_h*1/2)
	    y += 0.75;
    }
    else {
	if (y - offsetY < screen_h*1/4)
	    y += 0.5;
    }
}

- (int) receiveDamage:(int)damage type:(enum DAMAGE_TYPE)type
{
    if (type == DAMAGE_TYPE_UNIT)
	return 0;
    else if (type == DAMAGE_TYPE_VULCAN)
	bonus_damage++;

    if (bonus_damage >= 5) {
	bonus_damage -= 5;
	damage++;
    }

    damage = [super receiveDamage:damage type:type];
    if (!damage)
	return 0;

    /* Fire counter attack lasers! */
    if ((health > STAGE2_HEALTH) && (counter_attack_tics <= 0)) {
	counter_attack_tics = 30;
	[spawn_projectile([PulseLaser class], x-70, y, NO) setAngle:deg2rad( -75.0)];
	[spawn_projectile([PulseLaser class], x+70, y, NO) setAngle:deg2rad(-105.0)];
    }

    /* Shots push him backwards.  We are in space after all. */
    y -= damage/2.5;

    if (health < BODY_HEALTH) {
	/* Small wings are gone. */
	[child[BOSS1_SMALL_LEFT_WING]  die];
	[child[BOSS1_SMALL_RIGHT_WING] die];
	child[BOSS1_SMALL_LEFT_WING]  = nil;
	child[BOSS1_SMALL_RIGHT_WING] = nil;
    }
    else if (health <= STAGE2_HEALTH && !fang_y) {
	/* Our big wings have recently been shot off. */
	sprite = [boss_sebum getSebumByName:"body"];
	w = [sprite width];
	h = [sprite height];

	child[BOSS1_SMALL_LEFT_WING]  = spawn_unit([Boss1SmallLeftWing class], x, y, ACTIVE_AIR_LIST);
	child[BOSS1_SMALL_RIGHT_WING] = spawn_unit([Boss1SmallRightWing class], x, y, ACTIVE_AIR_LIST);
	[child[BOSS1_SMALL_LEFT_WING]  setParent:self];
	[child[BOSS1_SMALL_RIGHT_WING] setParent:self];
	fang_y = 1;
	fire_tics = 50*4;	/* Wait while our fangs come out. */

	/* Invincible for a while. */
	flags |= FLAG_INVINCIBLE;

	/* Shed our large wings. */
	spawn_debris([Boss1LargeLeftWingDebri  class], x-62, y, LOW_LAYER);
	spawn_debris([Boss1LargeRightWingDebri class], x+62, y, LOW_LAYER);
    }
    return damage;
}

- derive_fixedAngle;
- derive_setXYAlwaysCentred;
- derive_airBossCollisionLists;
@end

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

@implementation Boss1SmallLeftWing
- init
{
    [super init];
    sprite  = [boss_sebum getSebumByName:"wing-l1"];
    sprite2 = [boss_sebum getSebumByName:"wing-l2"];
    sprite3 = [boss_sebum getSebumByName:"wing-l"];
    rotatable_unit = YES;
    angle = deg2rad(-90.0);

    /* The wing hasn't expanded yet, so make the height smaller. */
    w = [sprite width] - 10;
    h = [sprite height] - 80 - EXTENSION_DIST;

    bay1 = [[[[Weapon new] 
		setProjectile:[SwarmMissile class]]
	       setShotDelay:80] setFireTics:0];
    bay2 = [[[[Weapon new] 
		setProjectile:[SwarmMissile class]]
	       setShotDelay:80] setFireTics:2];
    bay3 = [[[[Weapon new] 
		setProjectile:[SwarmMissile class]]
	       setShotDelay:80] setFireTics:4];

    flags |= FLAG_MOVING_ENABLED|FLAG_INVINCIBLE;
    return self;
}

- free 
{
    if (bay1) bay1 = [bay1 free];
    if (bay2) bay2 = [bay2 free];
    if (bay3) bay3 = [bay3 free];
    return [super free]; 
}

- (void) die
{
    [super die];
    sprite = [boss_sebum getSebumByName:"wing-ld"];
}

- (void) spawnDyingExplosions
{
    if (health < -50)			/* 1 seconds. */
	spawn_debris([LargeChunkCoffee class], x, y, HIGH_LAYER);
    if (health > -100) {		/* 2 seconds. */
	spawn_debris([BigExplosion class],
		     rnd(x-w/2, x+w/2), rnd(y-h/2, y+h/2), HIGH_LAYER);
	play_explosion_sample(x);
    }
    else
        [self delete];

    health--;
    flash_tics += 2;
}

- (void) setParent:(Unit<OwnsChildren> *)unit { parent = unit; }

- (void) draw:(BITMAP *)dest
{
    double backup_x = x;
    double backup_y = y;
    int sign = (angle < 0) ? -1 : 1;

    if (t < ROTATION_TICS) {
	/* Yeah, I know this isn't good.  This is to make the
	   animation not jerk when we change the sprites.  I don't
	   know where the numbers come from :/
	 */

	x +=  3 * (ROTATION_TICS - t)/ROTATION_TICS * sign;
	y -= 29 * (ROTATION_TICS - t)/ROTATION_TICS;
    }
    
    /* Draw sprite2 manually. */
    if (t < ROTATION_TICS) {		/* Rotation sequence. */
	int w_ = [sprite2 width];
	int h_ = [sprite2 height];
	double x_ = x - offsetX - EXTENSION_DIST*cos(deg2rad(t*ROTATION_DIST/ROTATION_TICS)) * sign;
	double y_ = y - offsetY - EXTENSION_DIST*sin(deg2rad(t*ROTATION_DIST/ROTATION_TICS));

	pivot_sprite(dest, [(SebImageAllegro *)sprite2 bitmap], x_, y_,
		     w_/2, h_/2, rad2fix(angle));
    }
    else if (t < ROT_PLUS_EXTENSION_TICS) {	/* Extension sequence. */
	int w_ = [sprite2 width];
	int h_ = [sprite2 height];
	double x_ = x - offsetX - w_/2;
	double y_ = y - offsetY - h_/2;
	y_ += (t - ROT_PLUS_EXTENSION_TICS) * (EXTENSION_DIST/EXTENSION_TICS);

	draw_sprite(dest, [(SebImageAllegro *)sprite2 bitmap], x_, y_);
    }

    [super draw:dest];
    x = backup_x;
    y = backup_y;
}

- (int) update
{
    if (t < ROT_PLUS_EXTENSION_TICS) {
	[self move];

	if (t < ROTATION_TICS) {
	    if (angle < 0)
		angle += deg2rad(ROTATION_DIST / ROTATION_TICS);
	    if (angle > 0)
		angle -= deg2rad(ROTATION_DIST / ROTATION_TICS);
	}
	else
	    rotatable_unit = NO;
	
	/* Finished activating? */
	t++;
	if (t >= ROT_PLUS_EXTENSION_TICS) {
	    sprite = sprite3;
	    sprite2 = nil;
	    sprite3 = nil;

	    /* Make thec ollision size proper. */
	    w = [sprite width] - 10;
	    h = [sprite height] - 80;

	    flags &=~FLAG_INVINCIBLE;
	    flags |= FLAG_FIRING_ENABLED;
	}
	return THING_NORMAL;
    }
    else
	return [super update];
}

- (void) move
{
    /* Parent not set?  This should never happen. */
    assert(parent && "Parent (Boss1) went missing!");

    if (unit_exists(parent, ACTIVE_AIR_LIST)) {
	[parent getX:&x Y:&y];
	x -= 61.0;
	y += 36.0;
    }
    else {
	/* Parent removed before we were.  This should never happen
	   because the parent remains invincible until we die. */
	assert("Parent (Boss1) deleted before child!\n" && NO);
    }
}

- (void) fire
{
    [bay1 fireFromX:x-7  Y:y-20];
    [bay2 fireFromX:x+10 Y:y-20];
    [bay3 fireFromX:x+28 Y:y-20];
}

- (int) receiveDamage:(int)damage type:(enum DAMAGE_TYPE)type
{
    if (flags & FLAG_INVINCIBLE)
	return 0;
    if (parent) {
	damage = [parent receiveDamage:damage type:type];
	if (damage)
	    flash_tics = MIN(flash_tics+9, 25);
	return damage;
    }
    return 0;
}

- derive_alwaysInfluenceGameplay;
+ derive_airBossCollisionLists;
@end


@implementation Boss1SmallRightWing
- init
{
    [super init];
    sprite  = [boss_sebum getSebumByName:"wing-r1"];
    sprite2 = [boss_sebum getSebumByName:"wing-r2"];
    sprite3 = [boss_sebum getSebumByName:"wing-r"];
    rotatable_unit = YES;
    angle = deg2rad(ROTATION_DIST);

    [bay1 setFireTics:35];
    [bay2 setFireTics:40];
    [bay3 setFireTics:45];

    return self;
}

- (void) die
{
    [super die];

    /* Note if you look VERY closely, you'll notice the picture is
       wrong.  This is because I'm lazy drawing-wise. */
    sprite = [boss_sebum getSebumByName:"wing-rd"];
}

- (void) move
{
    /* Parent not set?  This should never happen. */
    assert(parent && "Parent (Boss1) went missing!");

    [parent getX:&x Y:&y];
    x += 60.0;
    y += 36.0;
}

- (void) fire
{
    [bay1 fireFromX:x+7  Y:y-20];
    [bay2 fireFromX:x-10 Y:y-20];
    [bay3 fireFromX:x-28 Y:y-20];
}
@end

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

@implementation Boss1LargeLeftWingDebri
- init
{
    [super init];
    health = 75;
    sprite = [boss_sebum getSebumByName:"bigwing-l"];
    w = [sprite width];
    h = [sprite height];
    angle = deg2rad(-150.0);
    speed = 1.8;
    return self;
}

- (int) update
{
    spawn_debris([BigExplosion class], 
		 rnd(x-w/2, x+w/2), rnd(y-h/2, y+h/2), MEDIUM_LAYER);
    play_explosion_sample(x);

    health--;
    if (health <= 50)
	spawn_debris([LargeChunkRed class], x, y, MEDIUM_LAYER);

    return [super update];
}
@end

@implementation Boss1LargeRightWingDebri
- init
{
    [super init];
    sprite = [boss_sebum getSebumByName:"bigwing-r"];
    angle = deg2rad(-30.0);
    return self;
}
@end

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

@implementation Boss1LaserBall
- init
{
    [super init];

    sprite = [laserball_anim getFrame:0];
    frame = 0;
    fire_method = rnd(1, 3);
    shots_remaining = 7;

    return self;
}

- (void) draw:(BITMAP *)dest
{
    if (sprite) {
	int sprite_w_2 = [sprite width]/2;
	int sprite_h_2 = [sprite height]/2;
	int x_ = x-sprite_w_2-offsetX;
	int y_ = y-sprite_h_2-offsetY;

#ifndef NO_FBLEND
        fblend_rgba_trans([(SebImageAllegro *)sprite bitmap], dest, x_, y_, 0);
#else					/* Normal Allegro routines. */
        set_alpha_blender();
        draw_trans_sprite(dest, [(SebImageAllegro *)sprite bitmap], x_, y_);
#endif					/* End: NO_FBLEND */
    }
}

- (int) update
{
    /* Animate. */
    if ((frame/3) < [laserball_anim numFrames]-1) {
	frame++;
	sprite = [laserball_anim getFrame:frame/3];
    }
    else {
	/* Loop the animation. */
	frame -= 8*3;
	sprite = [laserball_anim getFrame:frame/3];
    }

    /* Still in creation process.  Don't fire yet. */
    if (frame/3 < LASERBALL_CREATION_FRAMES)
	return THING_NORMAL;

    /* Spit out some particles. */
    {
	int c;
	Particle *p;

	switch (rnd(0, 2)) {
	  case 0: c = makecol(0xff, 0xff, 0x80); break; /* Yellow */
	  case 1: c = makecol(0x80, 0x80, 0xff); break; /* Blue */
	  default:
	  case 2: c = makecol(0xff, 0xff, 0xff); break; /* White */
	}

	p = (Particle *)spawn_debris([Particle class], x, y, HIGH_LAYER);
	[[p setAngle:deg2rad(rnd(-180.0, 180.0))] setColour:c];
    }

    /* Ready to fire? */
    fire_tics--;
    if (fire_tics <= 0) {
	switch (fire_method) {
	  case 1:			/* Target player. */
	      fire_angle = ANGLE_TO_CLOSEST_ALLY(x, y);

	      if (fire_angle > deg2rad(90.0))
		  fire_angle = -M_PI;
	      else if (fire_angle > deg2rad(0.0))
		  fire_angle = 0.0;
	      break;
	  case 2:			/* Sweep ccw. */
	      if (shots_remaining == 7)
		  fire_angle = deg2rad(-140.0);

	      fire_angle += deg2rad(100.0/7);
	      break;

	  default:
	  case 3:			/* Sweep cw. */
	      if (shots_remaining == 7)
		  fire_angle = deg2rad(-40.0);
	      fire_angle -= deg2rad(100.0/7);
	}

	[spawn_projectile([PulseLaser class], x, y, NO) setAngle:fire_angle];

	shots_remaining--;
	if (shots_remaining <= 0)
	    return THING_DEAD;
	fire_tics = 7;
    }

    return THING_NORMAL;
}

+ (int) collisionLists { return 0; }
@end

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

@implementation Boss1Satellite
- init
{
    [super init];

    sprite = [boss_sebum getSebumByName:"satellite"];
    rotatable_unit = YES;
    health = 5;
    w = h = 24;
    distance = 400;
    flags |= FLAG_MOVING_ENABLED;

    return self;
}

- (void) delete
{
    if (parent) {
	[parent disownChild:self];
	parent = nil;
    }
    [super delete];
}

- (id) setAngle:(double)theta { sign = (theta > 0) ? -1 : 1; phi = theta; return [super setAngle:theta]; }
- (void) setParent:(Unit<OwnsChildren> *)unit { parent = unit; }

- (void) move
{
    /* Parent not set?  This should never happen. */
    assert(parent && "Parent (Boss1) went missing!");

    if (distance > 100)
	distance -= 3;

    if (unit_exists(parent, ACTIVE_AIR_LIST)) {
	double dest_x, dest_y, deltax, deltay;

	spawn_debris([SatelliteTrail class], x, y, MEDIUM_LAYER);

	[parent getX:&dest_x Y:&dest_y];
	phi += deg2rad(5.0 - sin(phi)) * sign;

	deltax = x;
	deltay = y;
	x = dest_x + distance*cos(phi) + 50.0*sin(phi)*sign;
	y = dest_y + 130.0*sin(phi) + 100.0;
	simplify_angle(&phi);

	angle = atan2(deltay - y, x - deltax);
    }
    else {
	/* Parent removed before we were.  This should never happen
	   because the parent informs us when it dies. */
	assert("Parent (Boss1) deleted before child!\n" && NO);
    }
}

- derive_alwaysInfluenceGameplay;
@end
