import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.MediaTracker;

import java.awt.Point;

import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;
import java.lang.Thread;

import java.util.Random;

import java.util.Date;



import netscape.javascript.JSObject;

public class game extends Applet implements Runnable
{
	private int lifespan = 80000;
	private player whore, ripper;
	private mainplay angel;

	private boolean ripperkill = false, ripper_whore_close = false;

	private static game_pnt[] key_points = new game_pnt [8];
	static
	{
		String[] s0 = {"east", "south"};
		key_points[0] = new game_pnt (s0, 124, 50);
		String[] s1 = {"east", "south", "west"};
		key_points[1] = new game_pnt (s1, 247, 50);
		String[] s2 = {"south", "west", "north"};
		key_points[2] = new game_pnt (s2, 442, 50);


		String[] s3 = {"east", "south", "north"};
		key_points[3] = new game_pnt (s3, 247, 164);
		String[] s4 = {"north", "south", "west"};
		key_points[4] = new game_pnt (s4, 442, 164);

		String[] s5 = {"east", "south", "north"};
		key_points[5] = new game_pnt (s5, 124, 280);
		String[] s6 = {"north", "east", "west"};
		key_points[6] = new game_pnt (s6, 247, 280);
		String[] s7 = {"west", "north"};
		key_points[7] = new game_pnt (s7, 442, 280);

	}




	private Image[] load_im = new Image[200];
	private Image load_strip;

	private int shoot_count = 0, kiss_count = 0, kill_count = 0;
	private boolean shoot = false, kiss = false;

	private Image town_strip, players;
	private int town_counter = 0;

	private String load_message_1 = "loading...", load_message_2 = "kill the ripper";
	private int load_fore = 0xffffffff, load_back = 0xff000000, load_width = 100, load_counter = 199;
	private Color load_fore_col= new Color(load_fore), load_back_col= new Color(load_back);
	private int load_fore_cheat, load_back_cheat;

	private Image off_screen, background;
	private int width = 786, height = 367, console_x = 518, console_y = 59, red_shift = 273;

	private Rectangle board_rect = new Rectangle(76, 0, 422, 367);
	private Rectangle cons_rect = new Rectangle(console_x, console_y, 271, 250);
	private Rectangle west_rect = new Rectangle(console_x + 27, console_y + 95, 46, 53);
	private Rectangle north_rect = new Rectangle(console_x + 108, console_y + 12, 54, 45);
	private Rectangle east_rect = new Rectangle(console_x + 200, console_y + 95, 46, 53);
	private Rectangle south_rect = new Rectangle(console_x + 109, console_y + 184, 52, 46);
	private Rectangle love_rect = new Rectangle(console_x + 4, console_y + 175, 73, 38);
	private Rectangle hate_rect = new Rectangle(console_x + 193, console_y + 175, 73, 38);
	private Rectangle tv_rect = new Rectangle(console_x + 76, console_y + 63, 121, 118);
	private Rectangle film_rect = new Rectangle(console_x + 86, console_y + 72, 100, 100);

	private Thread thr;
	private boolean alive = true, intro = true, game = false, west = false, north = false, east = false, south = false, love = false, hate = false;

	private static Font font;
	private static int load_height;
	private static FontMetrics font_metrics;
	static
	{
		String f = "SansSerif";
		String[] fl = Toolkit.getDefaultToolkit().getFontList();
		for (int i = 0; i < fl.length; i++)
		{
			if ((fl[i] == f)||(fl[i] == "Helvetica"))
			{
				f = fl[i];
				break;
			}
		}
		font = new Font(f, Font.BOLD, 13);
		font_metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
		load_height = font_metrics.getHeight();
	}


	public void init()
	{
		background = getImage(getCodeBase(), "background.gif");
		town_strip = getImage(getCodeBase(), "townstrp.jpg");

		players = getImage(getCodeBase(), "players.gif");


		MediaTracker mt = new MediaTracker (this);
		mt.addImage(background, 0);
		mt.addImage(town_strip, 1);
		mt.addImage(players, 2);
		try {((Frame)getParent()).setCursor(Frame.CROSSHAIR_CURSOR);} catch (Throwable t){}

		load_fore_cheat = cheat(load_fore_col);
		load_back_cheat = cheat(load_back_col);
		int[] pixels = new int[100];
		for (int i = 0; i < load_width / 2; i++)
		{
			pixels[i] = (255 << 24) + ((i + 53) << 16);
			pixels[i + load_width / 2] = (255 << 24) + ((102 - i) << 16);
		}
		load_strip = createImage(new MemoryImageSource(load_width, 1, pixels, 0, load_width));

		Dimension dim;
		try {dim = getSize();} catch (Throwable t) {dim=size();}
		width = dim.width;
		height = dim.height;

		off_screen = createImage (width, height);
		Graphics g = off_screen.getGraphics();
		g.setColor(new Color (0xff666666));
		g.fillRect (0, 0, width, height);
		g.dispose();

		try {mt.waitForAll();} catch (Throwable t){}

		drawBackground();
		drawFilm();

		angel = new mainplay(100, 2, "north", key_points[6], key_points);

		ripper = new player(100, 1, "south", key_points[2], key_points);

		whore = new player(100, 0, "north", key_points[3], key_points);

		whore.setAttract (angel, ripper);
		ripper.setAttract (whore, angel);

		whore.start();
		ripper.start();
		angel.start();

		thr = new Thread(this);
		thr.start();
	}

	private int cheat(Color c)
	{
		Image cim = createImage(1,1);
		Graphics cg = cim.getGraphics();
		cg.setColor(c);
		cg.fillRect(0,0,2,2);
		cg.dispose();
		int[] cpx = new int[4];
		try {(new PixelGrabber(cim, 0, 0, 1, 1, cpx, 0, 1)).grabPixels();}
		catch (Throwable t){}
		return cpx[0];
	}

	private Image grad (String s, int k)
	{
		Image im = createImage(load_width, load_height);
		Graphics g = im.getGraphics();
		g.setColor(load_back_col);
		g.fillRect(0, 0, load_width, load_height);
		g.setColor(load_fore_col);
		g.setFont(font);
		g.drawString(s, -  font_metrics.stringWidth(s) + ((load_width + font_metrics.stringWidth(s)) * k ) / load_width, font_metrics.getAscent() - 1);
		g.dispose();
		int[] pixels = new int[load_width * load_height];
		try {(new PixelGrabber(im, 0, 0, load_width, load_height, pixels, 0, load_width)).grabPixels();} catch (InterruptedException e){}
		im.flush();
		for (int j = 0; j < load_height; j++)
		for (int i = 0; i < load_width / 2; i++)
		{
			if (pixels[j * load_width + i] == load_fore_cheat) pixels[j * load_width + i] = (255 << 24) + ((i + 53) << 16);
			else pixels[j * load_width + i] = load_back;
			if (pixels[j * load_width + i + load_width / 2] == load_fore_cheat) pixels[j * load_width + i + load_width / 2] = (255 << 24) + ((102 - i) << 16);
			else pixels[j * load_width + i + load_width / 2] = load_back;
		}
		return createImage(new MemoryImageSource(load_width, load_height, pixels, 0, load_width));
	}

	private void drawClip(Image im, Rectangle r, int x, int y)
	{
		Graphics g = off_screen.getGraphics();
		g.clipRect(r.x, r.y, r.width, r.height);
		g.drawImage(im, x, y, this);
		g.dispose();
	}

	public void drawClip2(Image im, int x, int y, int dx, int dy)
	{
		Graphics g = off_screen.getGraphics();
		g.clipRect(x, y, 40, 40);
		g.drawImage(im, x - dx, y - dy, this);
		g.dispose();
	}


	private void drawFilm()
	{
/*		Image im = createImage (100, 100);
		Graphics g = im.getGraphics();
		g.setColor(new Color (0xff000000));
		g.fillRect(0, 0, 100, 100);
		if (load_strip!=null)
		{
			g.drawImage(load_strip, 0, 49 - load_height / 2, this);
			g.drawImage(load_strip, 0, 51 + load_height / 2, this);
		}
		if (load_im[load_counter]!=null) g.drawImage(load_im[load_counter], 0, 50 - load_height / 2, this);
		g.dispose();*/

		town_counter++;
		if (town_counter == 183) town_counter = 0;
		drawClip (town_strip, film_rect, film_rect.x - town_counter * 100, film_rect.y);
//		drawClip (im, film_rect, film_rect.x, film_rect.y);
//		im.flush();
	}

	private void drawTV()
	{
		drawClip (background, tv_rect, 0, 0);
		drawFilm();
	}


	private void drawBoard()
	{
		drawClip (background, board_rect, 0, 0);
	}

	private void drawBackground()
	{
		drawClip (background, new Rectangle(0, 0, width, height), 0, 0);
	}

	private void drawConsole()
	{
		drawClip (background, cons_rect, 0, 0);

		if (west) drawClip (background, west_rect, - red_shift, 0);
		if (north) drawClip (background, north_rect, - red_shift, 0);
		if (east) drawClip (background, east_rect, - red_shift, 0);
		if (south) drawClip (background, south_rect, - red_shift, 0);
		if (love) drawClip (background, love_rect, - red_shift, 0);
		if (hate) drawClip (background, hate_rect, - red_shift, 0);

	}

	public void run()
	{
		long millisecs = new Date().getTime();
		int ripperwhore_break = 5 * 1000;
		long ripperwhore_millisecs = new Date().getTime();
		while ((alive) && (intro))
		{
			if (new Date().getTime() - millisecs > lifespan)
			{
				alive = false;
				killThread();
				try
				{
					JSObject thiswin = JSObject.getWindow(this);
					thiswin.eval("self.document.location.href='opener.htm'");
				}
				catch (Throwable t){}
			}


			if ((load_im[load_counter] == null) && (load_counter < 100)) load_im[load_counter] = grad (load_message_1, load_counter);
			if ((load_im[load_counter] == null) && (load_counter >= 100)) load_im[load_counter] = grad (load_message_2, load_counter - 100);
			drawBoard();
			drawConsole();
			drawFilm();

			if (kill_count == 0)
			{
			if (  (Math.abs(ripper.pos.x - whore.pos.x) < 30) && (Math.abs(ripper.pos.y - whore.pos.y) < 30) )

				{
					ripper_whore_close = true;
					ripper.freeze = true;
				}
			}

			if ((!shoot) && (!kiss) && (!ripper_whore_close))
			{
				drawClip2 (players, whore.pos.x, whore.pos.y, whore.counter * 40, whore.which);
				drawClip2 (players, ripper.pos.x, ripper.pos.y, ripper.counter * 40, ripper.which);
				drawClip2 (players, angel.pos.x, angel.pos.y, angel.counter * 40, angel.which);
			}

			else if (shoot)
			{
				drawClip2 (players, whore.pos.x, whore.pos.y, whore.counter * 40, whore.which);


				if (ripperkill)
				{
					if (shoot_count < 15) drawClip2 (players, ripper.pos.x, ripper.pos.y, ripper.counter * 40, ripper.which + 5 * 40);
				}
				else
				drawClip2 (players, ripper.pos.x, ripper.pos.y, ripper.counter * 40, ripper.which);

				drawClip2 (players, angel.pos.x, angel.pos.y, angel.counter * 40, angel.which + 6 * 40);
				shoot_count ++;
				if (shoot_count == 32)
				{
					shoot_count = 0;
					shoot = false;
					angel.freeze = false;
					ripper.freeze = false;
					ripperkill = false;
				}
			}

			else if (kiss)
			{
				drawClip2 (players, ripper.pos.x, ripper.pos.y, ripper.counter * 40, ripper.which);
				drawClip2 (players, angel.pos.x, angel.pos.y, (kiss_count % 24) * 40, 7 * 40);
				kiss_count++;
				if (kiss_count == 48)
				{
					kiss_count = 0;
					kiss = false;
					angel.freeze = false;
					ripper.freeze = false;
					whore.freeze = false;
				}
			}
			else if (ripper_whore_close)
			{

				drawClip2 (players, angel.pos.x, angel.pos.y, angel.counter * 40, angel.which);
				drawClip2 (players, ripper.pos.x, ripper.pos.y, kill_count * 40, 4 * 40);

				kill_count++;

				if (kill_count == 72)
				{
					ripper.freeze = false;
					kill_count = 0;
					ripper_whore_close = false;
//					angel.freeze = false;
//					whore.freeze = false;
				}
			}

			paint(getGraphics());
			try {thr.sleep (40);} catch (Throwable t){}
		}

//			if (load_counter == 99) try {thr.sleep(1000);} catch (Throwable t){}
//			if (load_counter == 199) try {thr.sleep(1000);} catch (Throwable t){}
//			load_counter--;
//			if (load_counter <= 0) load_counter = 199;


		while ((alive) && (game))
		{
			drawBackground();
			paint(getGraphics());
			try {thr.sleep (100);} catch (Throwable t){}
		}
	}

	private Graphics clipGraphics (Graphics g, Rectangle r)
	{
		Graphics graph = g;
		g.clipRect(r.x, r.y, r.width, r.height);
		return g;
	}

	public void paint (Graphics g)
	{
		g.drawImage(off_screen, 0, 0, this);
	}

	public void update (Graphics g)
	{
		paint (g);
	}

	public boolean mouseMove (Event e, int x, int y)
	{
		if (game == false)
		{
			if (west_rect.inside(x, y)) west = true; else west = false;
			if (north_rect.inside(x, y)) north = true; else north = false;
			if (east_rect.inside(x, y)) east = true; else east = false;
			if (south_rect.inside(x, y)) south = true; else south = false;
			if (love_rect.inside(x, y)) love = true; else love = false;
			if (hate_rect.inside(x, y)) hate = true; else hate = false;
		}

		return true;
	}

	public void doHate()
	{
		if ((!kiss) && (!ripper_whore_close))
		{
			ripper.setDir (ripper.dir);
			ripper.freeze = true;
			shoot = true;
			shoot_count = 0;

			if (  (Math.abs(angel.pos.x - ripper.pos.x) < 4) |   (Math.abs(angel.pos.y - ripper.pos.y) < 4) ) //(angel.pos.y == ripper.pos.y))
			{
				if ( ((angel.dir == "west") && (ripper.pos.x < angel.pos.x)) |
				((angel.dir == "east") && (ripper.pos.x > angel.pos.x)) |
				((angel.dir == "north") &&  (ripper.pos.y < angel.pos.y)) |
				((angel.dir == "south") && (ripper.pos.y > angel.pos.y)) )
				{
					ripperkill = true;
				}
				angel.setDir (angel.dir);
				angel.freeze = true;
			}
		}
	}

	public void doLove()
	{
		if ((!shoot) && (!ripper_whore_close))
		{
			if (  (Math.abs(angel.pos.x - whore.pos.x) < 20) && (Math.abs(angel.pos.y - whore.pos.y) < 20) )
			{
				kiss = true;
				angel.freeze = true;
				whore.freeze = true;
				ripper.freeze = true;
			}
		}
	}

	public boolean mouseDown (Event e, int x, int y)
	{
		if (game == false)
		{
			if (west_rect.inside(x, y)) angel.stear("west");
			if (north_rect.inside(x, y)) angel.stear("north");
			if (east_rect.inside(x, y)) angel.stear("east");
			if (south_rect.inside(x, y)) angel.stear("south");
			if (love_rect.inside(x, y)) doLove();
			if (hate_rect.inside(x, y)) doHate();
		}

		return true;
	}


	public void killThread()
	{
		alive = false;
	}
}



class player extends Thread
{
	int counter = 0;
	int upper, lower;
	public Point pos;
	game_pnt[] gps;
	game_pnt gpnt;
	public boolean freeze = false;

	private boolean alive = true;

	public player escape;
	public player attract;

	String dir = "";

	public int pause;

	public int which;

	private Random rand = new Random((int) (Math.random() * 1000));


	public player(int i, int j, String s, game_pnt p, game_pnt[] gp)
	{
		gps = gp;
		gpnt = p;
		pos = new Point (p.x, p.y);
		pause = i;
		which = j * 40;
		dir = s;
		setDir (dir);
	}

	public void setAttract(player a, player e)
	{
		attract = a;
		escape = e;
	}

	public void setNewPoint()
	{
		int p = Math.abs(rand.nextInt()) % (gps.length - 1);
		if (gps[p] == gpnt) gpnt = gps[p + 1];
		else gpnt = gps[p];
		pos.x = gpnt.x;
		pos.y = gpnt.y;
	}

	public void setDir(String s)
	{
		if (s=="west")
		{
			upper = 8;
			lower = 0;
			counter = 0;
		}
		if (s=="east")
		{
			upper = 16;
			lower = 8;
			counter = 8;
		}
		if (s=="north")
		{
			upper = 24;
			lower = 16;
			counter = 16;
		}
		if (s=="south")
		{
			upper = 32;
			lower = 24;
			counter = 24;
		}
	}

	public void changeDir()
	{

		if (dir == "north") pos.y = pos.y - 4;
		else if (dir == "south") pos.y = pos.y + 4;
		else if (dir == "east") pos.x = pos.x + 4;
		else if (dir == "west") pos.x = pos.x - 4;
	}

	public void run()
	{
		while (alive)
		{
			try {sleep(pause);} catch (Throwable t){}
			counter++;
			if (counter == upper) counter = lower;

			if (!freeze)
			{

				changeDir();

				if (pos.y > 322)
				{
					dir = "south";
					setDir (dir);
					pos.x = 442;
					pos.y = 50;
					gpnt = gps[2];
				}

				else if (pos.y < 10)
				{
					pos.x = 124;
					pos.y = 280;
					dir = "north";
					setDir (dir);
					gpnt = gps[6];
				}

				int i = gps.length;
				while (true)
				{
					i--;
					if (i < 0) break;
					if (gps[i] == gpnt) i --;
					if (i < 0) break;

					if ( (gps[i].x - pos.x) * (gps[i].x - pos.x) +
					(gps[i].y - pos.y) * (gps[i].y - pos.y) < 25)
					{

						gpnt = gps[i];

						int east = 0;
						int north = 0;

						if ((attract != null) && (escape != null))
						{
							if (attract.pos.x > pos.x) east++;
							else east--;

							if (escape.pos.x > pos.x) east--;
							else east++;

							if (attract.pos.y < pos.y) north--;
							else north++;

							if (escape.pos.y < pos.y) north++;
							else north--;
						}

						if ((north > 0) && (gpnt.containDir("north"))) dir = "north";
						else if ((north < 0) && (gpnt.containDir("south"))) dir = "south";
						else if ((east < 0) && (gpnt.containDir("west"))) dir = "west";
						else if ((east > 0) && (gpnt.containDir("east"))) dir = "east";
						else dir = gps[i].str[(Math.abs (rand.nextInt())) % (gps[i].str.length)];

						setDir (dir);
						break;
					}
				}
			}
		}
	}

	public void killThread()
	{
		alive = false;
	}
}

class mainplay extends player
{

	public mainplay (int i, int j, String s, game_pnt p, game_pnt[] gp)
	{
		super (i, j, s, p, gp);
	}

/*	public void setDir(String s)
	{
		if (s=="west")
		{
			upper = 8;
			lower = 0;
			counter = 0;
		}
		if (s=="east")
		{
			upper = 16;
			lower = 8;
			counter = 8;
		}
		if (s=="north")
		{
			upper = 24;
			lower = 16;
			counter = 16;
		}
		if (s=="south")
		{
			upper = 32;
			lower = 24;
			counter = 24;
		}
	}*/

	public void stear(String s)
	{
		if ((s == "south") && (dir == "north"))
		{
			dir = s;
			setDir (s);
			if (gpnt == gps[0]) gpnt = gps[1];
			else gpnt = gps[0];
		}
		if ((s == "north") && (dir == "south"))
		{
			dir = s;
			setDir (s);
			if (gpnt == gps[0]) gpnt = gps[1];
			else gpnt = gps[0];
		}
		if ((s == "west") && (dir == "east"))
		{
			dir = s;
			setDir (s);
			if (gpnt == gps[0]) gpnt = gps[1];
			else gpnt = gps[0];
		}
		if ((s == "east") && (dir == "west"))
		{
			dir = s;
			setDir (s);
			if (gpnt == gps[0]) gpnt = gps[1];
			else gpnt = gps[0];
		}
	}
}

class game_pnt extends Point
{
	public Point pos;
	public String[] str;

	public game_pnt(String[] bols, int x, int y)
	{
		super (x, y);
		str = bols;
	}

	public boolean closeTo (Point p)
	{
		if ( ((p.y - y) * (p.y - y) + (x - p.x) * (x - p.x)) < 4) return true;
		else return false;
	}

	public boolean containDir(String s)
	{
		boolean b = false;
		for (int i = 0; i < str.length; i++) if (str[i] == s) b = true;
		return b;
	}
}