/* $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:`Lander.java'
//
//
// DESCRIPTION
//	Lander World Object
// 
// Author
//
//	Peter Pilgrim
//	Tue May 18 23:09:23 BST 1999
// 
// RCS HEADER ``Lander.java''
// 
//	$Author$
//	$Date$
//	$Source$
//	$Revision$    $State$    $Locker$
//

package xenon.humanoid;

import java.util.Vector;
import java.awt.*;
import java.awt.image.*;

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

public class Lander extends EnemyObject
{
    public final static int	NUMBER_LANDER_IMAGES=3;

    public final static int	STATE_HUNTING=1;
    public final static int	STATE_CAPTURED=2;
    public final static int	STATE_MUTANT=3;

    public final static int	STATE_CAPTURING=4;

    public final static int	MIN_CRUISING_HEIGHT=55;
    public final static int	MAX_CRUISING_HEIGHT=60;
    
    // Images of these type of world
    // Object are stored in an array
    protected static Image[]		landerImages=null;
    protected static Image[]		mutantImages=null;

    // Private object instance counter
    private static ObjectCounter	landerCounter = new ObjectCounter();
    

    /** magnitude velocity of lander movement */
    protected int			magnitude;
    /** the lander's state */
    protected int			state;
    /** the responsiveness 1=fastest and 5=slowest */ 
    protected int			response;
    
    protected int			state_counter=0;
    /** captured human */
    protected Human			capturedHuman=null;
    
    /** probability to fire a bomblet */
    protected double			bombletProbability = 0.0256;

    /** Simple Constructor take a single game object 
     * 
     * @param gameEngine the game engine
     */
    public Lander( GameEngine gameEngine ) 
    {
	super( gameEngine );
    }
    
    
    /** Constructor take a single game object 
     * 
     * @param gameEngine the game engine
     * @param state the state of the lander (STATE_HUNTING, STATE_MUTANT)
     * @param wx the world x coodinate
     * @param wy the world x coodinate
     * @param magnitude the magnitude (overall speed)
     * @param response the responsive factor the lower the number 
     * the quicker the lander reacts.
     */
    public Lander( GameEngine gameEngine, int state,
		   int wx, int wy, int magnitude, int response ) 
    {
	super( gameEngine );

	this.state = state;
	this.wx = wx;
	this.wy = wy;
	this.image_index = 0;
	this.magnitude = Math.abs(magnitude);
	this.response = response;
	setResponse( response );
	dx_vel = ( Math.random() < 0.5 ) ? this.magnitude : -this.magnitude;
	if ( wy > MIN_CRUISING_HEIGHT )
	    dy_vel = -4;
	else if ( wy < MAX_CRUISING_HEIGHT )
	    dy_vel = 4;

	// Get the bounds
	width = landerImages[0].getWidth(component);
	height = landerImages[0].getHeight(component);
	
	landerCounter.increment();		/* ! */
    }

    /** Gets type description type */
    public String getDescriptionType()
    {
	if ( state == STATE_MUTANT )
	    return ("Mutant");
	
	return ("Lander");
    }
    
    /** 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() 
    {
	if ( state == STATE_MUTANT )
	    return (mutantImages[0]);
	return (landerImages[0]);
    }
    
    public static int getInstanceCount() 
    {
	return landerCounter.getCount();
    }
    
    public static void setInstanceCount( int value ) 
    {
	landerCounter.setCount( value );
    }
    
    public boolean loadImages()
    {
	if (landerImages == null) {

	    // Load lander images
	    landerImages = new Image[NUMBER_LANDER_IMAGES];

	    for ( int j=0; j<NUMBER_LANDER_IMAGES; ++j) {
		landerImages[j] = 
		    GraphicsUtils.getImageAsClassResource( 
			this, "images/lander"+j+".gif" );

	    }
	    
	    mutantImages = new Image[NUMBER_LANDER_IMAGES];
	    
	    for ( int j=0; j<NUMBER_LANDER_IMAGES; ++j) {
		mutantImages[j] = 
		    GraphicsUtils.getImageAsClassResource( 
			this, "images/mutant"+j+".gif" );
	    }

	    GraphicsUtils.loadImageListCompletely( component, landerImages );
	    GraphicsUtils.loadImageListCompletely( component, mutantImages );
	}
	
	return (true);
    }

    public void setState( int newState )
    {
	state = newState;
    }
    
    public int getState()
    {
	return (state);
    }
    
    public void setCapturedHuman( Human newHuman )
    {
	capturedHuman = newHuman;
    }

    public Human getCapturedHuman()
    {
	return (capturedHuman);
    }

    public Human findNearestHuman()
    {
	if (wy > MAX_CRUISING_HEIGHT )
	    return (null);

	Vector humanList = Human.getHumanList();

	for (int j=0; j<humanList.size(); ++j) {
	    Human human = (Human)humanList.elementAt(j);
	    if ( human.getState() == Human.STATE_FREE &&
		 Math.abs(human.wx - wx) < 50 ) {
		return (human);
	    }
	}

	return (null);
    }

    /** Sets the response */
    public void setResponse( int newResponse )
    {
	response = newResponse;
	if (response < 0) response = 0;
	if (response > GameUtilities.RESPROW ) response = GameUtilities.RESPROW;
    }
    
    /** Gets the response */
    public int getResponse()
    {
	return (response);
    }
    
    /** Sets the magnitude */
    public void setMagnitude( int newMagnitude )
    {
	magnitude = newMagnitude;
    }
    
    /** Gets the magnitude */
    public int getMagnitude()
    {
	return (magnitude);
    }
    
    /** Sets the bomblet probability */
    public void setBombletProbability( double newValue )
    {
	bombletProbability = newValue;
    }
    
    /** Gets the bomblet probability */
    public double getBombletProbability()
    {
	return (bombletProbability);
    }
    
    /** update world object */
    public WorldObject	update()
    {
	if ( !GameUtilities.isResponsive( response, gameEngine ) )
	    return this;

	switch (state) {
	  case STATE_HUNTING:
	  {
	      if ( wy > MIN_CRUISING_HEIGHT )
		  dy_vel = -4;
	      else if ( wy < MAX_CRUISING_HEIGHT )
		  dy_vel = 4;
	      Human human = findNearestHuman();
	      if (human != null) {
		  state = STATE_CAPTURING;
		  state_counter=32;
	      }
	  }
	  break;

	  case STATE_CAPTURING:
	  {
	      if ( state_counter > 0 ) { 
		  --state_counter;
		  if (state_counter == 0) {
		      // Unsuccessful attempt to capture ran out of time
		      state = STATE_HUNTING;
		      dx_vel = ( Math.random() < 0.5 ) ? 
			  this.magnitude : -this.magnitude;
		  }
		  else {
		      Human human = findNearestHuman();
		      if ( human != null ) {
			  if ( human.wx > wx )
			      dx_vel = 4;
			  if ( human.wx < wx )
			      dx_vel -= 4;
			  if ( human.wy > wy )
			      dy_vel = 1;
			  if ( human.wy < wy )
			      dy_vel = -1;
		      }
		  }
	      }
	      
	      if ( wy < 0 ) {
		  dy_vel = 4;
		  state = STATE_HUNTING;
		  state_counter=-1;
		  dx_vel = ( Math.random() < 0.5 ) ? 
		      this.magnitude : -this.magnitude;
	      }
	  }
	  break;

	  case STATE_CAPTURED:
	  {
	      dx_vel = 0;
	      dy_vel = 2;
	      if ( wy > gameEngine.world_height+7 ) {
		  if ( capturedHuman != null ) {
		      // Double check: if this lander is not the
		      // imprisoner then it has not captured the human
		      // (or has lost it), therefore it should not
		      // mutate!
		      if ( capturedHuman.getImprisoner() != this )
			  capturedHuman = null;
		  }
		  if ( capturedHuman != null ) {
		      PPMCAudioPlayer mcap = gameEngine.getMCAudioPlayer();
		      mcap.play( 10, capturedHuman.getOuchSound().getSoundData() );
		      mcap.play( 11, capturedHuman.getGiantBiteSound().getSoundData() );
		      mutate();
		  }
		  else {
		      state = STATE_HUNTING;
		      dx_vel = ( Math.random() < 0.5 ) ? 
			  this.magnitude : -this.magnitude;
		      dy_vel = -10;
		  }
	      }
	  }
	  break;
	      
	  case STATE_MUTANT:
	  {
	      WorldObject playerObject = gameEngine.playerObject;
	      if ( state_counter > 0 ) 
		  --state_counter;
	      if ( playerObject != null && state_counter < 1) {
		  state_counter = 5+(int)( Math.random() * 4.0 );
		  if ( playerObject.wx > wx ) 
		      dx_vel = this.magnitude;
		  if ( playerObject.wx < wx )
		      dx_vel = -this.magnitude;
		  if ( playerObject.wy > wy )
		      dy_vel = this.magnitude;
		  if ( playerObject.wy < wy )
		      dy_vel = -this.magnitude;
	      }

	      wx += gameEngine.frameClock % 2 == 0 ? 3 : -3 ;
	      wy += gameEngine.frameClock % 2 == 1 ? 3 : -3 ;
	  }
	  break;

	}
	
	wx = ( wx + dx_vel + gameEngine.world_width ) % gameEngine.world_width;
	wy += dy_vel;
	if ( wy < 0 ) { 
	    wy = 0; dy_vel = 8;
	}
	else if ( wy > gameEngine.world_height+15 ) {
	    wy = gameEngine.world_height+15; dy_vel = -8;
	}
	
	if ( capturedHuman != null ) {
	    capturedHuman.wx = wx;
	    capturedHuman.wy = wy-height;
	}
	
	if ( Math.random() < bombletProbability &&
	     scrx > 0 && scrx < gameEngine.screen_width &&
	     gameEngine.playerObject != null &&
	     Math.abs( gameEngine.playerObject.wx - wx ) < gameEngine.screen_width/2 ) 
	{
	    if ( Bomblet.getInstanceCount() < Bomblet.OPTIMAL_NUMBER ) {
		int xvelocity = (gameEngine.playerObject.wx - wx) / 20;
		int yvelocity = (gameEngine.playerObject.wy - wy) / 20;
		
		Bomblet bomblet = new Bomblet( 
		    gameEngine, wx + xvelocity, wy + yvelocity, 
		    xvelocity, yvelocity, 35 );
		gameEngine.addWorldObject( bomblet );
	    }
	}
	
	if ( gameEngine.frameClock % 10 == 0 && Math.random() < 0.02515 )
	    dx_vel = -dx_vel;
	
	return (this);
    }
    
    /** draw object if displayed */
    public WorldObject	drawObject( Graphics g ) 
    {
	if ( isVisible() ) {
	    
	    translateWorldToScreen();
	    
	    if ( isViewable() )
		if (state == STATE_MUTANT)
		    g.drawImage( mutantImages[image_index], scrx, scry, component );
		else
		    g.drawImage( landerImages[image_index], scrx, scry, component );


	    if ( GameUtilities.isResponsive( response, gameEngine ) )
		image_index = (image_index+1) % NUMBER_LANDER_IMAGES;
	}

	return (this);
    }
    
    /** undraw object if displayed */
    public WorldObject	undrawObject( Graphics g )
    {
	return (this);
    }

    public void mutate()
    {
	state = STATE_MUTANT;
	magnitude += 2;
	magnitude = Math.max( magnitude, 5 );
	bombletProbability += 0.0512;
	bombletProbability = Math.max( bombletProbability, 0.125 );
	bombletProbability = Math.min( bombletProbability, 0.667 );
	if (response > 2) response -= 2;
	response = 0;
	
	if ( capturedHuman != null ) {
	    capturedHuman.destroy();
	    capturedHuman = null;
	}
    }
    
    public void	destroy()
    {
	if ( capturedHuman != null ) {
	    capturedHuman.setState( Human.STATE_FALLING );
	}
	capturedHuman = null;			/* ! */
	landerCounter.decrement();		/* ! */
	super.destroy();
    }
    
    /** Gets the points score for this world object */
    public int getPointsScored()
    {
	return (150);
    }
    
    /** Get the radar color for this world object */
    public Color  getRadarColor()
    {
	switch (state) {

	  case STATE_CAPTURED:
	  case STATE_HUNTING:
	    return Color.green;

	  case STATE_CAPTURING:
	    return Color.red;

	  case STATE_MUTANT:
	    return Color.magenta;
	}
	
	return Color.green;
    }

}

// fini
