// Copyright 1997 Stefan Haustein

import java.applet.*;
import java.net.URL;
import java.awt.*;
import java.awt.image.*;
//import java.awt.event.*;
import java.io.*;
import java.util.*;
//import java.lang.reflect.Array;



public class Dukoids extends Applet {
    Button buttonStart;
    DukeWindow dw = null;
    Frame frame;
    

    public void init () {  
	buttonStart = new Button();
	buttonStart.setLabel("Start Dukoids");

	dw = new DukeWindow (this);
	frame = new DukeFrame (dw, "Dukoids");

	frame.add ("Center", dw);
	frame.pack ();

	frame.setResizable (false);

	//dw.resize (600, 400);
	frame.hide ();

	add (buttonStart);
	validate();
	
	try {
	    Thread.sleep (1000);
	} 
	catch (InterruptedException e) {}


	dw.startup ();
    }
    

    public boolean action(Event e, Object arg) {

	Object target = e.target;
    
	if (target == buttonStart) {
	    frame.show ();

	    return true;
	}
	return false;
    }
}


//*************************** TimeThread ************************************


class TimeThread extends Thread
{
  DukeWindow owner;

  public TimeThread (DukeWindow aw)
  {
    super ("timeThread");
    owner = aw;
  }


    public void run () {

	// bilder da?

	long lastTick = 0;
	
	for (int i = 0; i < owner.imageCount; i++) {
	    try {
		owner.tracker.waitForID (i);
	    }
	    catch (InterruptedException e) {}

	    if (System.currentTimeMillis () - lastTick > 500) {
		int unloaded = 0;

		for (int j = i+1; j < owner.imageCount; j++) {
		    if (!owner.tracker.checkID (j)) {
			unloaded++;
		    }
		}

		owner.unloaded = unloaded;

		owner.repaint ();
		lastTick = System.currentTimeMillis ();
	    }
	}

	owner.unloaded = 0;

	setPriority (MIN_PRIORITY);

	//    long lastTick = 0;
	//    long nextTick = System.currentTimeMillis ();

	long nextTick = System.currentTimeMillis ();


	// loop

	synchronized (this) {

	    while (true) {
		lastTick = nextTick;
		
		yield ();
		try { // give netscape some event processing time...
		    
		    Thread.sleep (10);
		} 
		catch (InterruptedException e) {}
		
		nextTick = System.currentTimeMillis ();
		
		owner.tick (((double) (nextTick-lastTick)) / 50.0);
		
		try {
		    wait ();
		}
		catch (InterruptedException e) {}
	    }
	}
    }
}

//*************************** Sprite ****************************************


class Sprite {
    double x = 300;
    double y = 200;
    double dx = 0;
    double dy = 0;
    int radius;
    int w2;
    int h2;
    
    Image image;
    boolean friend;
    boolean collision = false;
    
    DukeWindow owner;
    
    
    Sprite (DukeWindow aowner, Image aimg, boolean isFriend) {
	owner = aowner;
	image = aimg;
	w2 = aimg.getWidth (aowner) >> 1;
	h2 = aimg.getHeight (aowner) >> 1;
	friend = isFriend;
	
	owner.sprites.addElement (this);

	radius = w2;
	
	if (radius > h2) {
	    radius = h2;
	}                      
    }
    

    void collides (Sprite other) {
	collision = true;
    }

    
    void detectCollision (Sprite s) {
	if ((Math.abs (x - s.x) <= radius + s.radius) &&
	    (Math.abs (y - s.y) <= radius + s.radius)) {
	    if ((!s.collision) && !collision) {
		collides (s);
		s.collides (this);
	    }
	}
    }
    

    public boolean move (double count) {
	if (radius < 0) { // nur fr demo-mode!
	    
	    w2 = image.getWidth (owner) >> 1;
	    h2 = image.getHeight (owner) >> 1;
	    radius = w2;
	    
	    if (radius > h2)
		radius = h2;         
	}

	x += dx * count;
	y += dy * count;
	
	if (x > 600 + w2) {
	    x = - w2;
	    return false;
	}

	if (x < - w2) {
	    x = 600 + w2;
	    return false;
	}

	if (y > 400 + h2) {
	    y = -h2;
	    return false;
	}

	if (y < -h2) {
	    y = 400 + h2;
	    return false;
	}

	return !collision;
    }


    void randomPos () {
	do {
	    x = Math.random () * 600.0;
	    y = Math.random () * 400.0;
	}
	while ((x > 150) && (x < 450) && (y > 50) && (y < 350));  
    }
    

    void randomDir (double baseX, double baseY, double maxSpeed) {
	do {
	    dx = baseX + Math.random () * maxSpeed - maxSpeed / 2;
	    dy = baseY + Math.random () * maxSpeed - maxSpeed / 2;
	}
	while (Math.abs (dx) < 1 || Math.abs (dy) < 1);
    }   
}



//*************************** Duke ******************************************


class Duke extends Sprite {

    double dead = 0;
    double angle = 0;
    int lives = 2;
    boolean doubleFire = false;

    Duke (DukeWindow aowner) {
	super (aowner, aowner.dukes [0], true);
    }


    void collides (Sprite other)  {
	if (dead == 0) {
	    dead = 1;
	    doubleFire = false;
	    
	    if (owner.sound) owner.soundDead.play ();
	}
    }
    

    public boolean move (double count) {
	// dont call super: no motion!
	
	if (dead > 0) {
	    if (dead < 20) {
		image = owner.explode [((int) dead-1) / 4];
		dead += count;
	    }
	    else if (lives > 0) {
		lives--;
		dead = -100;
		owner.showStatus ();
	    }
	    else {
		if (dead <= 200) dead += count;
		else owner.gameOver ();
		
		image = owner.imageGameOver;
		friend = false;  // do not let the other shit explode
	    }
	}
	else {
	    if (dead < 0) {
		dead += count;
		if (dead > 0) dead = 0;
	    } 

	    angle += owner.motion * count * Math.PI / 16;
	
	    if (angle > 2*Math.PI) angle -= 2 * Math.PI;
	    else if (angle < 0) angle += 2 * Math.PI;
	    
	    if (owner.fire) {
		Sprite kb = new KillerBean (owner, true, 300, 200, angle);
		
		if (doubleFire) {
		    kb.x -= kb.dy/1.5;
		    kb.y += kb.dx/1.5;
		    new KillerBean (owner, true, 300+kb.dy/1.5, 200-kb.dx/1.5, angle);
		}

		owner.fire = false;

		if (owner.sound) {
		    if (doubleFire) owner.soundGun2.play ();
		    else owner.soundGun.play ();
		}
	    }

	    if (owner.engine) {
		double dx = 0.4 * Math.sin (angle) * count;
		double dy = 0.4 * Math.cos (angle) * count;
	    
		owner.globalDx += dx;
		owner.globalDy += dy;
		
		for (int i = owner.sprites.size () - 1; i >= 0; i--) {
		    Sprite s = (Sprite) owner.sprites.elementAt (i);
		    s.dx = s.dx + dx;
		    s.dy = s.dy + dy;
		}

		if ((owner.ticker & 2) == 2) 
		    image = owner.dukesBurn1 
			[(int) Math.round (16.0*angle/Math.PI) & 31];
		else
		    image = owner.dukesBurn2 
			[(int) Math.round (16.0*angle/Math.PI) & 31];
	    }
	    else 
		image = owner.dukes 
		    [(int) Math.round (16.0*angle/Math.PI) & 31];
	}
	
	w2 = image.getWidth (owner) >> 1;
	h2 = image.getHeight (owner) >> 1;
	
	return true; 
    }
}


//*************************** KillerBean ************************************


class KillerBean extends Sprite {

    KillerBean (DukeWindow aowner, boolean isfriend, 
		double ax, double ay, double aangle)
    {
	super (aowner, isfriend ? aowner.killerBean : aowner.badBean, isfriend);

	dx = - 14.0 * Math.sin (aangle);
	dy = - 14.0 * Math.cos (aangle);
	
	x = ax + 2*dx;
	y = ay + 2*dy;
    }
}



class Foo extends Sprite {
    Image images[];
    int img = 0;
    int imageCount = 1;
    
    Foo (DukeWindow aowner, Image aimages [], int aImageCount) {
	super (aowner, aimages [0], false);
	images = aimages;
	imageCount = aImageCount;
    }
    

    void collides (Sprite other) {
      
	super.collides (other);
	
	if (owner.sound)
	    owner.explosion.play ();
    }


    public boolean move (double count) {
	boolean b = super.move (count);

	image = images [(owner.ticker >> 1) % imageCount] ;
	//                                        ^1.1: Array.getLength
	w2 = image.getWidth (owner) >> 1;
	h2 = image.getHeight (owner) >> 1;
	
	return !collision;
    }
}


//*************************** Saucer ****************************************

class Saucer extends Sprite {

    Saucer (DukeWindow aOwner) {
	
	super (aOwner, aOwner.imageSaucer, false);
	x = 0;
	y = 80;
	dx = 3;
	dy = 0;

	if (owner.sound)    
	    owner.soundComputer.play ();
    
	owner.showStatus ();
    }


    void collides (Sprite other) {
	super.collides (other);
	
	if (owner.sound) {
	    owner.soundDrip.play ();
	}

	if (owner.duke.doubleFire) {
	    owner.duke.lives++;
	}
	else {
	    owner.duke.doubleFire = true;
	}
    }
}


//*************************** Asteroid **************************************


class Asteroid extends Foo {

    int type = 0;
    int subType = 0; // 0 1 2
    
    Asteroid (DukeWindow aowner, int aSubType) {

	super (aowner, aowner.bigAsteroidsR, 10);

	subType = aSubType;

	randomPos ();
	randomDir (owner.globalDx, owner.globalDy, 4);
    
	if (dx - owner.globalDx < 0) 
	    images = owner.bigAsteroidsL;
	
    }


    Asteroid (Asteroid creator, Image aimages[], int aImageCount) {
	
	super (creator.owner, aimages, aImageCount);
	type = creator.type + 1;
	subType = creator.subType;
	
	x = creator.x;
	y = creator.y;
	
	randomDir (creator.dx, creator.dy, 7);
	
	x = x + dx;
	y = y + dy;
    }
    
    
    void collides (Sprite other) {

	super.collides (other);
      
	if (type == 0) {
	    for (int i = 0; i < Math.min (subType+2, 3); i++)
		new Asteroid (this, owner.mediumAsteroids, 32);
      
	    owner.addScore (20);
	}
	else if (type == 1) {
	    new Asteroid (this, owner.smallAsteroidsL, 16);
	    new Asteroid (this, owner.smallAsteroidsR, 16);

	    if (subType == 2)
		new Asteroid (this, owner.smallAsteroidsM, 16);
	    
	    owner.addScore (30);
	}
	else {
	    owner.addScore (50);
	}
    }
}


//*************************** Alien *****************************************


class Alien extends Foo {

    Alien (DukeWindow aowner) {
      
	super (aowner, aowner.aliens, 12);
	randomPos ();
	randomDir (owner.globalDx, owner.globalDy, 6);
    }


    void collides (Sprite other) {

	super.collides (other);

	for (int i = 0; i < 6; i++)
	    new KillerBean (owner, false, x, y, Math.random () * Math.PI * 2);
    
	owner.addScore (200);
    }


    public boolean move (double count) {

      super.move (count);

      if (owner.duke != null) {

	  double r = Math.random ();

	  if (r < 0.003 * count)
	      randomDir (owner.globalDx, owner.globalDy, 6);
      

	  if (r < 0.05*count) {
	      if (owner.sound)
        
		  owner.cannon.play ();
        
	      new KillerBean 
		  (owner, false, x, y, Math.random () * Math.PI * 2);
	  }
      }

      return !collision;
    }
}



//******************************** DukeWindow *******************************


class DukeWindow extends Canvas {

    Image[] dukes;
    Image[] dukesBurn1;
    Image[] dukesBurn2;
    Image[] bigAsteroidsL;
    Image[] bigAsteroidsR;
    Image[] mediumAsteroids;
    Image[] smallAsteroidsL;
    Image[] smallAsteroidsM;
    Image[] smallAsteroidsR;
    Image[] aliens;
    Image[] explode;

    Image killerBean;
    Image badBean;
    Image imageSaucer;
    Image imageGameOver;
    Image imageMiniduke;

    AudioClip harp; 
    AudioClip soundComputer; 
    AudioClip cannon; 
    AudioClip explosion; 
    AudioClip soundEngine;
    AudioClip soundGun;
    AudioClip soundGun2;
    AudioClip soundDead;
    AudioClip soundDrip;
    
    MediaTracker tracker;
    Image offImage;
    Graphics offGraphics;
    Vector sprites;
    Dukoids owner;
    Duke duke;
    
    boolean sound = false;

    int ticker;
    double _ticker = 0;
    int imageCount = 0;
    int score = 0;
    int kills = 0;
    int motion = 0;
    int level = 0;
    int asteroidCount;
    int asteroidBaseType;
    int asteroidAdvanced;
    
    double x0 = 200;
    double y0 = 100;
    
    double globalDx = 0;
    double globalDy = 0;
    
    int unloaded = 9999;

    boolean fire = false;
    boolean engine = false;
    
    int sleepTime = 5;      // fuck needed for Netscape

    TimeThread timeThread;


    DukeWindow (Dukoids aOwner) {

	super ();
	owner = aOwner;
    }

    void addScore (int sc) {

	score += sc;
	kills++;

	if (level > 2) {

	    if ((kills & 15) == 0)
      
		new Alien (this);
      

	    if ((kills & 63) == 8)
      
		new Saucer (this);
      
	}

	showStatus ();
    }


    void startup ()  {

	//Toolkit toolkit = Toolkit.getDefaultToolkit ();

	tracker = new MediaTracker(this);
	
	dukes = new Image[32];
	dukesBurn1 = new Image[32];
	dukesBurn2 = new Image[32];
	bigAsteroidsR = new Image[10];
	bigAsteroidsL = new Image[10];
	mediumAsteroids = new Image [32];
	smallAsteroidsL = new Image [16];
	smallAsteroidsM = new Image [16];
	smallAsteroidsR = new Image [16];
	explode = new Image [5];
	aliens = new Image[12];
	
	imageMiniduke = getImage ("miniduke.gif");
	imageSaucer = getImage ("java2.gif");
	killerBean = getImage ("bean2.gif");
	badBean = getImage ("ax.gif");
	dukes[0] = getImage ("duke2.gif");

	dukesBurn1[0] = getImage ("dukeb1.gif");
	dukesBurn2[0] = getImage ("dukeb2.gif");

	mediumAsteroids [0] = getImage ("dos2.gif");
	smallAsteroidsL [0] = getImage ("dosl.gif");
	smallAsteroidsR [0] = getImage ("dosr.gif");
	smallAsteroidsM [0] = getImage ("sm.gif");
	
	aliens [0] = getImage ("de00.gif");
	aliens [1] = getImage ("de01.gif");
	aliens [2] = getImage ("de02.gif");
	aliens [3] = getImage ("de03.gif");
	aliens [4] = getImage ("de04.gif");
	aliens [5] = getImage ("de05.gif");
	aliens [6] = getImage ("de06.gif");
	aliens [7] = getImage ("de07.gif");
	aliens [8] = getImage ("de07.gif");
	aliens [9] = getImage ("de08.gif");
	aliens [10] = getImage ("de09.gif");
	aliens [11] = getImage ("de10.gif");

	explode [0] = getImage ("bang1.gif");
	explode [1] = getImage ("puff1.gif");
	explode [2] = getImage ("puff2.gif");
	explode [3] = getImage ("puff3.gif");
	explode [4] = getImage ("puff4.gif");

	imageGameOver = getImage ("over.gif");

    
	Image winR = getImage ("win.gif");
	Image winL = getImage ("winl.gif");

	//    Image win95L = getImage ("win95.gif");
	//    Image win95R = getImage ("win95l.gif");

	for (int i = 0; i < 10; i++) { 
	    ImageFilter filter = new WaveFilter (4, 40, i*4);

	    bigAsteroidsR [i] = createImage 
		(new FilteredImageSource (winR.getSource (), filter));
	    
	    bigAsteroidsL [i] = createImage 
		(new FilteredImageSource (winL.getSource (), filter));

	    //      bigAsteroids95R [i] = createImage (new FilteredImageSource (win95R.getSource (), filter));
	    //      bigAsteroids95L [i] = createImage (new FilteredImageSource (win95L.getSource (), filter));

	    trackImage (bigAsteroidsR[i]);
	    trackImage (bigAsteroidsL[i]);

	    //      trackImage (bigAsteroids95R[i]);
	    //      trackImage (bigAsteroids95L[i]);
	}


	for (int i = 1; i < 32; i++) {
	    ImageFilter filter = new RotateFilter (Math.PI * 2.0 * i / 32.0);
	    
	    dukesBurn1 [i] = createImage 
		(new FilteredImageSource 
		    (dukesBurn1 [0].getSource (), filter));
	    
	    dukesBurn2 [i] = createImage 
		(new FilteredImageSource 
		    (dukesBurn2 [0].getSource (), filter));
	
	    dukes [i] = createImage 
		(new FilteredImageSource (dukes[0].getSource (), filter));
	
	    mediumAsteroids [i] = createImage 
		(new FilteredImageSource 
		    (mediumAsteroids[0].getSource (), filter));
      
	    trackImage (dukesBurn1[i]);
	    trackImage (dukesBurn2[i]);
	    trackImage (dukes[i]);
	    trackImage (mediumAsteroids [i]);
	}


	for (int i = 1; i < 16; i++) {

	    ImageFilter filter = new RotateFilter 
		(Math.PI * 2.0 * i / 16.0);
	
	    smallAsteroidsL [i] = createImage 
		(new FilteredImageSource 
		    (smallAsteroidsL [0].getSource (), filter));
	    
	    smallAsteroidsM [i] = createImage 
		(new FilteredImageSource 
		    (smallAsteroidsM [0].getSource (), filter));
      
	    smallAsteroidsR [i] = createImage 
		(new FilteredImageSource 
		    (smallAsteroidsR [0].getSource (), filter));

	    trackImage (smallAsteroidsL[i]);
	    trackImage (smallAsteroidsM[i]);
	    trackImage (smallAsteroidsR[i]);
	}

	gameOver ();

	timeThread = new TimeThread (this);
	timeThread.start ();
	
	harp = getAudioClip ("harp.au");
	soundComputer = getAudioClip ("computer.au");
	cannon = getAudioClip ("cannon.au");
	explosion = getAudioClip ("explosion.au");
	soundEngine = getAudioClip ("engine.au");
	soundGun = getAudioClip ("gun.au");
	soundGun2 = getAudioClip ("gun2.au");
	soundDead = getAudioClip ("dead.au");
	soundDrip = getAudioClip ("drip.au");
    }


    public void trackImage (Image i) {
      
	tracker.addImage (i, imageCount);
	imageCount++;
    }

    public AudioClip getAudioClip (String s) {
	return owner.getAudioClip (owner.getCodeBase(), "audio/" + s);
    }


    public Image getImage (String s) {
	try {
	    Image i = owner.getImage 
		(new URL (owner.getCodeBase () + "images/" + s));
	    
	    trackImage (i);
	    return i;
	} 
	catch (java.net.MalformedURLException e) {}
	
	return null;
    }


    public Dimension getPreferredSize () {
	return new Dimension (600, 400);
    }

    public Dimension getMinimumSize () {
	return new Dimension (600, 400);
    }

    public Dimension getMaximumSize () {
	return new Dimension (600, 400);
    }


    void newGame () {
	globalDx = 0;
	globalDy = 0;
	sprites = new Vector ();
	duke = new Duke (this);
	level = 0;
	score = 0;
	kills = 0;
	engine = false;
	fire = false;
	asteroidCount = 2;
	asteroidBaseType = 0;
	asteroidAdvanced = 0;
	
	newLevel ();
    }

    
    void gameOver () {
	Sprite s;
	
	duke = null;
	sprites = new Vector ();

	s = new Asteroid (this, 0);
	s.x  = 75;
	s.y  = 75;
	s.dx = 0;
	s.dy = 0;

	s = new Foo (this, mediumAsteroids, 32);
	s.x  = 75;
	s.y  = 150;
	s.dx = 0;
	s.dy = 0;
	
	s = new Foo (this, smallAsteroidsL, 16);
	s.x  = 75;
	s.y  = 225;
	s.dx = 0;
	s.dy = 0;
	
	s = new Alien (this);
	s.x  = 75;
	s.y  = 300;
	s.dx = 0;
	s.dy = 0; 
    }


    void newLevel () {
	
      if (sound) harp.play ();
    
      level++;

      for (int i = 0; i < asteroidCount; i++) {
	  if (i < asteroidAdvanced)
	      new Asteroid (this, asteroidBaseType+1);
	  else
	      new Asteroid (this, asteroidBaseType);
	  
      }

      if (asteroidCount < 3) asteroidCount++;
      else {
	  asteroidAdvanced++;
	  if (asteroidAdvanced > 2) {
	      asteroidAdvanced = 0;
	      asteroidBaseType++;
	  }
	  else if ((asteroidAdvanced == 1) && (asteroidBaseType==2)) {
	      asteroidCount++;
	      asteroidBaseType = 0;
	  }
      }
      
      showStatus ();
    }


    public void showStatus () {
	owner.frame.setTitle ("Dukoids  -  Level: " + level + 
			"  Score: " + score + "  Dukes: "+duke.lives); 
    }


    public boolean handleEvent (Event event) {

	switch (event.id) {
	case event.KEY_ACTION:
	    switch (event.key) {
	    case event.LEFT:
		motion = 1;
		return true;
		
	    case event.RIGHT:
		motion = -1;
		return true;
		
	    case event.UP:
		if ((!engine) && sound && (duke != null))
		    soundEngine.loop ();
		    
		engine = true;
		return true;
	    }
	    break;

	case event.KEY_ACTION_RELEASE:
	    switch (event.key) {
	    case event.LEFT:
		if (motion == 1)
		    motion = 0;
        
		return true;

	    case event.RIGHT:
		if (motion == -1)
		    motion = 0;
        
		return true;
        
	    case event.UP:
		if (engine && sound)
		    soundEngine.stop ();
        
		engine = false;
		return true;
	    }
	    break;
      
	case event.KEY_RELEASE:			
	    switch (event.key) {
	    case 9:
	    case 'a':
	    case 'A':
	    case 'n':
	    case 'N':
		fire = true;
		return true;
		
	    case 'y':
	    case 'Y':
	    case 'z':
	    case 'Z':
		if (motion == 1)
		    motion = 0;
          
		return true;
		
	    case 'x':
	    case 'X':
		if (motion == -1)
		    motion = 0;
          
		return true;
		
	    case 's':
	    case 'S':
		sound = !sound;
		return true;

	    case 'm':
	    case 'M':
		if (engine && sound)
		    soundEngine.stop ();
          
		engine = false;
		return true;
		
	    default:
		if (duke == null)
		    newGame ();
		
	    }
	    break;

	case event.KEY_PRESS:			
	    switch (event.key) {
	    case 'y':
	    case 'Y':
	    case 'z':
	    case 'Z':
		motion = 1;
		return true;
		
	    case 'x':
	    case 'X':
		motion = -1;
		return true;
		
	    case 'm':
	    case 'M':
		if (sound && !engine)
		    soundEngine.loop ();
          
		engine = true;
		return true;
	    }
	    break;

	case Event.WINDOW_DESTROY:
	    gameOver ();
	    hide ();
	    //setVisible (false);
	    //dispose ();
	    //owner.dw = null;
	    break;      
	}              
	
	// return true;
	return super.handleEvent(event);
    }



    
    public void tick (double count) {
	_ticker += count;
	ticker = (int) Math.round (_ticker);
	
	int k = sprites.size ();
	
	while (k-- != 0) {
	    if (!((Sprite) sprites.elementAt (k)).move (count)) {
		sprites.removeElementAt (k);

		if (sprites.size () == 1) {
		    newLevel ();
		}
	    }
	}
	
	x0 += globalDx * count;
	y0 += globalDy * count;
	
	if (x0 > 600) x0 = x0 - 600;
	else if (x0 < 0) x0 = x0 + 600;
	
	if (y0 > 400) y0 = y0 - 400;
	else if (y0 < 400) y0 = y0 + 400;

	
	for (int i = sprites.size () - 1; i >= 1; i--)
	    {
		Sprite s = (Sprite) sprites.elementAt (i);
		
		for (int j = i - 1; j >= 0; j--)
		    {
			Sprite t = (Sprite) sprites.elementAt (j);
			
			if (t.friend != s.friend)
			    {
				t.detectCollision (s);
			    }
		    }
	    }
	
	repaint ();     
    }


    public void paint(Graphics g)  {
	update (g);
    }
    

    public void update (Graphics g) {
	Dimension d = size();

	if ((d.width > 700) || (d.height > 550)) {
	    resize (600, 400);
	}
	
	if (offGraphics == null)
	    {
		offImage = createImage(600, 400);
		offGraphics = offImage.getGraphics();
		/*
		  Font font = offGraphics.getFont();
		  offGraphics.setFont(font = new Font(font.getName(),
		  font.getStyle(),
		  font.getSize() * 2));
		*/
	    }
	
	offGraphics.setColor(Color.white);
	offGraphics.fillRect(0, 0, d.width, d.height);
	offGraphics.setColor(Color.black);

	if (duke != null) {
	    /*      offGraphics.drawString (level + "  " + score, 20,50);

		    for (int i = 0; i < duke.lives; i++)
		    {
		    offGraphics.drawImage (imageMiniduke, 575 - i*15, 50, this);
		    }
	    */
	    for (int i = 0; i < 3; i++) {
		int x = (((int) Math.round (x0)) + i * 200) % 600;
		offGraphics.drawLine (x, 0, x, 400); 
		    
		if (i < 2) {
		    int y = (((int) Math.round (y0)) + i * 200) % 400;
		    offGraphics.drawLine (0, y, 600, y);
		}
	    }
	}
	else {
	    offGraphics.drawString ("20", 130,  85);
	    offGraphics.drawString ("30", 130, 160);
	    offGraphics.drawString ("50", 130, 235);
	    offGraphics.drawString ("200", 130, 310);
	    
	    offGraphics.drawString ("DUKOIDS keys", 300, 60);
	    
	    offGraphics.drawString ("y / cursor left:", 300, 110);
	    offGraphics.drawString ("rotate left",      450, 110);
	    offGraphics.drawString ("z / x / cursor right:",300, 135);
	    offGraphics.drawString ("rotate right",     450, 135);
	    offGraphics.drawString ("n / tab:",         300, 160);
	    offGraphics.drawString ("fire",             450, 160);
	    offGraphics.drawString ("m / cursor up:",   300, 185);
	    offGraphics.drawString ("accelerate",       450, 185);
	    offGraphics.drawString ("s:",               300, 235);
	    offGraphics.drawString ("switch sound " + (sound ? "off" : "on"), 450, 235);
	    offGraphics.drawString ("p:",               300, 260);   
	    offGraphics.drawString ("pause ", 450, 260);
	    offGraphics.drawString ("any other key starts game...", 300, 310);
	}
	
	if (unloaded == 0) {
	    for (int i = 0; i < sprites.size (); i++) {
		Sprite s = (Sprite) sprites.elementAt (i);
		offGraphics.drawImage (s.image,
				       (int) Math.round (s.x) - s.w2,
				       (int) Math.round (s.y) - s.h2,
				       this);
	    }
	}
	else {
	    if (imageCount != 0) {
		offGraphics.drawString 
		    ("please wait -- loading and converting images... "
		     +(100-(100*unloaded)/imageCount)+ " %", 20, 340);
	    }
	    offGraphics.drawRect (20, 350, 2*imageCount, 5);
	    offGraphics.fillRect (20, 350, 2*(imageCount - unloaded), 5);
	}
	
	g.drawImage(offImage, 0, 0, this);


	synchronized (timeThread) {
	    timeThread.notifyAll ();
	}
    }
}




class DukeFrame extends Frame {

    DukeWindow owner;

    DukeFrame (DukeWindow owner, String t) {
	super (t);

	this.owner = owner;
    }


    public boolean handleEvent (Event event) {

        if (event.id == Event.WINDOW_DESTROY) {
	    owner.gameOver ();
	    hide ();
	    //setVisible (false);
	    //dispose ();
	    //owner.dw = null;
      
	}              
	
	// return true;
	return super.handleEvent(event);
    }

}







