/* $Id$
 *
 * Copyright (c) 1999 Xenonsoft Limited. All Rights Reserved.
 *
 * This software is protected by copyright. You are hereby notified from
 * now by reading this message. This software is also the confidential 
 * and proprietary information of Xenonsoft Limited.  ("Confidential
 * Information"). 
 *
 * This software is distributed under the Xenonsoft Public end user 
 * License ("XPeL"), where the machine-readable source code is provided 
 * under the "Open Source" model.
 * For more information, please read the file "LICENSE-XPL.txt" 
 */

// File:`Starship.java'
//
//
// DESCRIPTION
//	Starship
// 
// Author
//
//	Peter Pilgrim
//	Fri Jun 19 00:09:21 BST 1998
// 
// RCS HEADER ``Starship.java''
// 
//	$Author$
//	$Date$
//	$Source$
//	$Revision$    $State$    $Locker$
//

package xenon.humanoid;

import java.util.Vector;
import java.beans.*;				// for PropertyChangeListener
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;

import xenon.gamekit.*;				// for GameEngine, WorldObject, etc
import xenon.soundkit.*;			// for SoundEngine, PPMCAudioPlayer, etc

public class Starship extends FriendObject 
{
    public static int MAXIMUM_LASER_BLASTS=6;	// was 5
    public static int VERTICAL_VELOCITY = 4;	// per Frame was 4 orig.
    public static int MAXIMUM_VELOCITY = 18;	// per Frame was 16 orig.
    public static int STARSHIP_LEFT_DIRECTION=0;
    public static int STARSHIP_RIGHT_DIRECTION=1;
    
    protected int	direction;

    protected int	cod_counter=0;		// change of direction delay counter
    protected int	fire_delay_counter=0;	// laser fire delay counter
    protected int	displacement=64;	// change of direction displacement

    protected Vector	rescuedHumanList=null;

    protected static Envelope	env1=null, env2=null;
    protected static SoundEngine se1=null, se2=null;
    
    protected static Image	starship_left_image=null;
    protected static Image	starship_right_image=null;
    

    /** Constructor take a single game object */
    public Starship( GameEngine gameEngine ) 
    {
	super( gameEngine );

	// Get the bounds
	width  = starship_left_image.getWidth(component);
	height = starship_left_image.getHeight(component);
	
	bringToRest();
    }

    public void bringToRest()
    {
	wx=48; wy=gameEngine.world_height/2;
	dx_vel=0; dy_vel=0;
	dx_acc=0; dy_acc=0;
	direction = STARSHIP_RIGHT_DIRECTION;

	cod_counter = 0;
	displacement = 64;
	rescuedHumanList = new Vector();
    }

    /** Gets type description type */
    public String getDescriptionType()
    {
	return ("Starship");
    }
    
    /** retrieves a default image used for placating an image of
     * the world object on the screen. For example displaying image
     * on the left of the display and the number of points scored
     * destroying this world object on the right.
     * <b>Subclassers</b> should override this methods.
     */
    public Image getDefaultImage() 
    {
	return (starship_right_image);
    }

    public void addHuman( Human human )
    {
	if ( ! rescuedHumanList.contains( human ) ) {
	    human.setState( Human.STATE_RESCUED );
	    rescuedHumanList.addElement( human );
	    human.setActive(true);
	    human.setVisible(true);
	}
    }
    
    public void removeHuman( Human human )
    {
	if ( rescuedHumanList.contains( human ) )
	    human.setState( Human.STATE_FALLING );
	rescuedHumanList.removeElement( human );
    }
    
    public boolean loadImages()
    {
	if (starship_left_image == null) {
	    // Load starship images
	    starship_left_image = 
		GraphicsUtils.getImageAsClassResource( 
		    this, "images/starship-left.gif" );
	    starship_right_image = 
		GraphicsUtils.getImageAsClassResource( 
		    this, "images/starship-right.gif" );
	
	    GraphicsUtils.loadImageCompletely( component, starship_left_image );
	    GraphicsUtils.loadImageCompletely( component, starship_right_image );
	}
	
	return (true);	
    }


    public boolean loadAudioClips()
    {

	if (env1 == null ) {
	    env1 = new Envelope( 
		1,  1,1, -1,1, -3,9,     1, 2 ,25, 0, 26, -1, 16, -2, 128 );
	    se1 = new SoundEngine(
		8000, 3.25, 
		111, 525, 80, 
		new WhiteNoiseWave(), env1 /* new Envelope() */ );
	    
	    se1.setFlags( se1.getFlags() | SoundEngine.FM_DELTA_RANDOM_FLAG | SoundEngine.NOISE_MIX_GATE_FLAG );
	    se1.makeWave();
	    
	    /* 
	       public Envelope( int pitch_step_unit, 
		     int pi1, int pn1, int pi2, int pn2, int pi3, int pn3,
		     int amplitude_step_unit , 
		     int ad, int an, int dd, int dn, int sd, int sn, int rd, int rn ) 
	       public SoundEngine( int sampling_rate, double time_duration, 
			int volume, int pitch, int duration,
			Wave wave, Envelope envelope )
	    */
	    env2 = new Envelope( 
		1,  6,4, 12,4, 24,6,     1, 25 ,13, -1, 10, 0, 0, -3, 128 );
	    
	    se2 = new SoundEngine( 
		8000, 1.00, 
		34, 512, 70,
		StaticWave.SQUARE_WAVE, env2 );
	    se2.setFlags( se2.getFlags() | SoundEngine.NOISE_MIX_GATE_FLAG );
	    se2.makeWave();
	}
	
	return (true);	
    }
    
    public void setDirection( int newDirection )
    {
	direction = newDirection;
    }
    
    public int getDirection()
    {
	return (direction);
    }

    public WorldObject	update()
    {
	HumanoidGameEngine hengine = (HumanoidGameEngine)gameEngine;
	
	// Handle change of direction counter
	if ( cod_counter > 0) --cod_counter;
	// Handle laser fire delay counter
	if ( fire_delay_counter > 0) --fire_delay_counter;
	
	if (gameEngine.frameClock % 4 == 0 ) {
	    // take care of absolute velocity decrease if no thrust 
	    if ( dx_vel > 0 ) 
		--dx_vel;
	    else if ( dx_vel < 0 )
		++dx_vel;
	}
	    
	if ( direction == STARSHIP_RIGHT_DIRECTION &&
	     displacement > 64 )
	    displacement -= MAXIMUM_VELOCITY;
	else if ( direction == STARSHIP_LEFT_DIRECTION &&
		  displacement < gameEngine.screen_width - 96 )
	    displacement += MAXIMUM_VELOCITY;
	
	
	if ( isVisible() && isActive() ) {
	    //
	    // Only if the starship is visible & active do we allow
	    // keyboard control
	    //
	    if ( hengine.down_key_pressed  && wy > 12 )
		wy = wy - VERTICAL_VELOCITY;
	    else if ( hengine.up_key_pressed && wy < gameEngine.screen_height-2 )
		wy = wy + VERTICAL_VELOCITY;
	
	    if ( hengine.cod_key_pressed && cod_counter == 0 ) {
		cod_counter = 10;
		if ( direction == STARSHIP_RIGHT_DIRECTION ) {
		// FIXME: could set maximum velocity here
		    // dx_vel = -2;
		    dx_vel -= 4;
		    direction = STARSHIP_LEFT_DIRECTION;
		}
		else {
		    // dx_vel = 2;
		    dx_vel += 4;    
		    direction = STARSHIP_RIGHT_DIRECTION;
		}
	    }
	    
	    if ( hengine.thrust_key_pressed ) {
		if ( direction == STARSHIP_RIGHT_DIRECTION ) {
		    // FIXME: could set maximum velocity here
		    if ( dx_vel < MAXIMUM_VELOCITY ) {
			dx_acc = 2 ;
			dx_vel += 2;
		    }
		}
		else {
		    if ( dx_vel > -MAXIMUM_VELOCITY ) {
			dx_acc = -2;
			dx_vel -= 2;
		    }
		}
	    }


	    if ( hengine.fire_key_pressed && fire_delay_counter == 0 &&
		 LaserBlast.getInstanceCount() < MAXIMUM_LASER_BLASTS ) {
		
		PPMCAudioPlayer mcap = hengine.getMCAudioPlayer();
		mcap.play( 0, se1.getSoundData() );
		mcap.play( 1, se2.getSoundData() );
		fire_delay_counter = 3;
		
		// Create a laser blast
		int laser_xstart;
		int laser_dxvel = dx_vel + 
		    (direction == STARSHIP_RIGHT_DIRECTION ? 4 : -4 );
		if ( direction == STARSHIP_RIGHT_DIRECTION )
		    laser_dxvel = Math.max( 4, dx_vel + 4 );
		else
		    laser_dxvel = Math.min( -4, dx_vel - 4 );

		laser_xstart = wx + (direction == STARSHIP_RIGHT_DIRECTION  ? 16 : -4 );
		hengine.addWorldObject( 
		    new LaserBlast( hengine, laser_xstart, wy-8, laser_dxvel, 0 ) );
	    }

	    if ( hengine.hyperspace_key_pressed ) {
		jumpHyperspace();
		return (this);
	    }
	}
	
	    
	// 
	// Setting game engine's world offset X coordinate and the
	// starship X coordinate
	//
	wx = ( wx + dx_vel + hengine.world_width ) % hengine.world_width;
	hengine.world_xpos = 
	    ( wx - displacement + hengine.world_width ) % hengine.world_width;

	// Check if we have any captured humans and if the ship
	// is touching the surface of the planet, land the humans
	// to the ground and complete the rescue.
	if ( wy < Human.OPTIMAL_HEIGHT+5 && rescuedHumanList.size() > 0 ) 
	    dropAllRescuedHumans( true );
	
	// Optimized for speed, move any captured humans to
	// world coordinate underneath the starship
	int hx = wx + 4;
	int hy = wy - height;
	for (int q=0; q<rescuedHumanList.size(); ++q) {
	    Human human = (Human)rescuedHumanList.elementAt(q);
	    human.wx = hx;
	    human.wy = hy;
	}

	return (this);	
    }

    /** drop all the rescued humans */
    public void dropAllRescuedHumans( boolean giveBonus ) 
    {
	// Drop each human
	for (int q=0; q<rescuedHumanList.size(); ++q) {
	    Human human = (Human)rescuedHumanList.elementAt(q);
	    human.setActive(true);
	    human.setVisible(true);
	    // BUGFIX: place the drop humans a little behind
	    // the starship to avoid shooting them again!
	    // *PP* Mon Jul 05 23:40:04 BST 1999
	    if ( direction == STARSHIP_RIGHT_DIRECTION )
		human.wx = wx - 16;
	    else
		human.wx = wx + 16;
	    human.wy = Human.OPTIMAL_HEIGHT;
	    human.dx_vel = Math.random() > 0.5 ? 2 : -2 ;
	    human.dy_vel = 0;
	    human.setImprisoner( null );
	    human.setState( Human.STATE_FREE );
	    
	    if ( giveBonus ) {
		PPMCAudioPlayer mcap = gameEngine.getMCAudioPlayer();
		mcap.play( 3, human.getRescueSound().getSoundData() );
		gameEngine.incrementScore( 500 );

		BonusFlyer flyer = new BonusFlyer( 
		    gameEngine, wx+q*8, wy+q*8, dx_vel, 0, 500 );
		gameEngine.addWorldObject( flyer );

		if ( q == 2 ) {
		    gameEngine.incrementScore( 750 );
		    flyer = new BonusFlyer( 
			gameEngine, wx+q*10, wy+q*10, dx_vel, 5, 750 );
		    gameEngine.addWorldObject( flyer );
		}
	    }
	}
	
	// Disown them all
	rescuedHumanList.removeAllElements();
    }

    public void jumpHyperspace()
    {
	HumanoidGameEngine hengine = (HumanoidGameEngine)gameEngine;
	
	wx = (int)(hengine.world_width * Math.random() );
	wy = (int)(hengine.world_height * Math.random() / 2.0 + 
		   hengine.world_height	/ 4.0 );
	hengine.world_xpos =  
	    ( wx - displacement + hengine.world_width ) % hengine.world_width;
	
	setActive(false);
	setVisible(false);
	
	dx_vel=0; dy_vel=0;
	dx_acc=0; dy_acc=0;
	cod_counter = 0;
	displacement = ( direction == STARSHIP_RIGHT_DIRECTION  ? 
			 64 : gameEngine.screen_width - 96 );

	hengine.setPlayerObject(null);

	// Notice how an anonymous property listener is used to
	// reactive and remap the starship
	Implosion implosion = new Implosion( hengine, this, wx, wy, Color.white, 20 );
	hengine.addWorldObject( implosion );
	implosion.addPropertyChangeListener( new PropertyChangeListener() {
	    public void propertyChange(PropertyChangeEvent evt) {
		if ( evt.getPropertyName().equals( WorldObject.DESTROYED_PROPERTY ) )
		{
		    WorldObject sprite = (WorldObject)evt.getSource();
		    GameEngine eng = sprite.getGameEngine();
		    eng.setPlayerObject(Starship.this);
		    setActive(true);
		    setVisible(true);
		}
	    }
	} ) ;
    }
	
    public WorldObject  drawObject( Graphics g )
    {
	// paint Starship
	if ( isVisible() ) {

	    translateWorldToScreen();
	    
	    // paint Starship
	    if ( isViewable() )
		g.drawImage( (direction == STARSHIP_RIGHT_DIRECTION ?
			      starship_right_image : starship_left_image ), 
			     scrx, scry, component );
	}

	return (this);
    }
    
    public WorldObject undrawObject( Graphics g )
    {
	return (this);
    }


    /** Get the radar color for this world object */
    public Color  getRadarColor()
    {
	return Color.cyan;
    }
}


// fini
