/*****************************************************************************/
/*                             Acts of God(esses)                            */
/*****************************************************************************/
/* By Audin Malmin (amalmin@halcyon.com),                                    */
/*    Chris Corwin (),                                                       */
/*    and Onder Ceyhun ().                                                   */
/*                                                                           */
/* Copywrite (c) 1998 Audin Malmin, Ondar Ceyhun, and Chris Corwin.          */
/* This code is made available under the GNU licence.                        */
/*                                                                           */
/* If you make use of this code please drop us a line.                       */
/*****************************************************************************/
/*****************************************************************************/
/* History                                                                   *
 *****************************************************************************
 * ANM	5-7-98	Added splashscreen.
 *              Explicit Frame creation.  Can't get the damned thing to close,
 *                  though.
 *              no more applet code.
 *		5-8-98	More work on exiting properly...
 *				Animated splash screen.
 *				Zapped one un-used frame.
 *		5-9-98	DrawPanel renamed MainPanel.
 *				More fiddling with the splash screen.
 *				General code cleanup.
 * ANM	5-13-98	New movement code.  (the dudes now use a modal movement method.)
 * & CC	6 hour	Better Cathedral display.
 * & OC	block	Cathedral "area of influance" or Holy Ground displayed.
 *				LocalDude reproduction implimented.  (PopulationExplosion thread)
 *				Added menubar code.
 * ANM	5-17-98	Cathedral.isCathedral(), Cathedral.isCathZone(), expanded Cathedral.test().
 *				LocalUtil.waitforimg()
 *				Cathedral constructior now waits for image to load befor displaying it.
 *				Source code split up: Colony, LocalUtil, Splash, AboutDialog
 *				Window title.
 * ANM	5-20-98	Enhanced cathedral code.
 * & CC			Enhanced PopulationExplosion
 *				Implimented "Popup-Zap"...which just kills a dude (removing him from the list.)
 * ANM	5-21-98	Network code.
 * & CC			Network code.
 * & OC			Network code.
 * ANM	5-22-98 More network code work.
 * ANM	5-23-98	Network code finally works!!!!!!!!!
 */
/*
 * Audin Malmin
 * 5-4-98
 * Java/OS week 7 (maybe) (I've lost count).
 *
 * This week has seen some major work.  I started out on my own by fiddling with the animation
 * code.  However, this work was not at all successful, so we moved back to the original code
 * when we made further changes.  This is why you won't see it mentioned in the history.
 *
 * When we all got together the first thing we did was splice in Chris's menubar code.  Then
 * we set ourselves to the task of writing real Dude movement code.  The movement code that
 * has been used up until now was only written for debugging purposes.  This original code
 * wasn't nearly smart enough to do even half of what we asked of it.  The new code is far
 * more capable.  It also took several hours of thought and a few hours of coding to produce.
 *
 * After we got the new movement code working, we tackeled the Cathedral code.  This operation
 * was basically the same: replacing temp. code with final production code.  First we put in
 * the "sphere of influance" code.  (This required some other changes...as the influance of a
 * cathedral is based on it's design, which is based on the size of the population which
 * build it.)  Along with this we added support for 5 different cathedral images, each with
 * different spheres of influance.
 *
 * Finally, around 10:00pm we started work on Dude reproduction.  This functionality is
 * encased in the PopulationExplosion thread.
 *
 * At a later date I expanded the Cathedral code a bit, adding functions to test if a point
 * was inside both the cathedral structure itself and if the point was inside the cathedral's
 * zone of influance.  I also added a LocalUtil.waitforimg() function, which is used in the
 * cathedral constructor to pause while the image loads...so as not to let it strobe into
 * existance in the main window.
 *
 * At this time I also worked on the splash screen a bit...it now consistently comes up BEFOR
 * the main program window (it kept popping up after the main window in my 6x86MX200)...
 *
 * This evening also brought the first source code split.  The new source files: LocalUtil,
 * Splash, and AboutDialog were created.  The main source file was renamed "Colony" (from
 * "ColonyApplet"), since the "Applet" part no longer fit.
 *
 * Finally I played around with the javadoc program a bit...and determined that I don't much
 * care for it...  It seems to (at least based on the JDK API documentation) encourage poor
 * documentation of code.  (ie: the JDK documentation is just about as terse as one can possibly
 * get.  Function descriptions that provide no more information than the function name itself
 * are not at all helpful.)  And after seeing just how ugly the comments make the source code,
 * I can see why the JDK docs are so bad...  Javadoc is no replacement for normal, manually
 * written, professional documentation.
 *
 * Current layout (has-a relationships):
 *
 * ColonyApplet --+
 *  |      |      |
 *  |      |  MainPanel --+--------+---------+---------------+-------------+-----+-------+
 *  |      |              |        |         |               |             |     |       |
 *  |   Splash*        LocalDL RemoteDL   LocalCL        %RemoteCL 	       |     |   PopupMenu
 *  |      |            |   |    |   |     |  |            |    |          |     |
 *  |    Window        Dude |   Dude |     | CathUpdate*%  |  CathUpdate*% | PopupMenu
 *  |      |                |        |     |               |               |
 *  |  ImgCanvas        DudeUpdate*  | Cathedral       Cathedral%          |
 *  |   |     |                      |                                     |
 *  | Image Label               DudeUpdate*                       PopulationExplosion*
 *  |
 * MenuBar
 *  |    |
 * Menu Menu
 *
 * Key:		*: thread		%: Not yet implimented
 *
 * (is-a relationships):
 *
 * Frame -> ColonyApplet	: The overall program object.  Contains main(), and one
 *                            MainPanel object (which does all the work).
 *
 * Thread-> Splash			: The splash screen thread.  It just throws up a borderless window
 *                            and paints a graphic in it.  It then sleeps for 10 seconds, after
 *                            which time it just zaps the window and exits.
 *
 * Frame -> MainPanel		: The heavy working object...Does everything.
 *                            The main event loop is here...
 *
 * Menu -> PopupMenu		: Just a normal multi-level popup menu.
 *
 * LocalDL -> RemoteDL
 *
 * LocalCL -> RemoteCL	(At least in the future)
 *
 * Thread  -> PopulationExplosion
 *
 * Thread  -> DudeUpdate
 *
 * Thread  -> CathUpdate
 *
 * So basically, MainPanel is the main program loop.  It sets everything up and
 * handles events.  MainPanel contains two popup menus, two CL objects, two DL objects,
 * and one PopulationExplosion thread.
 *
 * LocalDL is kind of a mutant linked list container.  It does contain a linked list
 * of Dude objects.  In addition it contains a DudeUpdate object.
 *
 * RemoteDL is derived from LocalDL.  It is identical except for the fact that, in the future,
 * it RemoteDL will get it's movement, creation, and destruction information from the remote
 * machine.
 *
 * Dude is the basic dude object.  It contains the dudes color, position, a move() function,
 * a test(x,y) function and a few other little functions.  The most interesting function is
 * move():
 *
 * A dude has two states which it can be in: moving and non-moving.  Everyone starts out as
 * non-moving.  This means that when the move() function is called the dude goes through a
 * little routine in which it decides a) if it WANTS to move, b) which direction it wants to
 * go, and c) how far it wants to go.  If it completes these three steps correctly then it
 * changes it's state to moving.  After figuring out the desination pixel, the movement
 * vecter needed to get there and the number of steps required, the move() function then
 * returns.  On the next call, the MOVING part of the move() function gets called, which
 * actually moves the dude one step, and decrements the total-movement variable.  When the
 * total-movements variable is 0, the dude sets itself back to NON-MOVING and the process can
 * start again.  The final resting place may not in fact be the exact Destination Point, but
 * it will be within 1 or 2 pixels.  In the future a call will be added to the decision making
 * process to test the Destination Point against a list of Dangerous Areas.  The probability
 * of the dude deciding to move into a dangerous area will be less than the probability of
 * moving into a friendly area.
 *
 * DudeUpdate is a thread.  It's job is to animate each of the Dudes.  Basically it
 * runs through the linked list on it's own (it gets a pointer to the "first" Dude
 * object), calling the Dude.move() function for each one.  The DudeUpdate constructor
 * also gets a pointer to the main MainPanel object, so that it can get ahold of the
 * Graphics Context and the background color.
 *
 * LocalCL is another bastard linked list container.  It contains a list of Cathedral objects.
 * RemoteCL will be derived from it; again adding a network interfact.
 *
 * Cathedral contains an image, a position at which to display the image, and a influance
 * variable which controls the size of the influance circle.  In the future I plan to have it
 * animate a small string of images (this depends on our having time to come up with images
 * to animate, though)...  The type of cathedral created depends on the size of the local
 * population at the time of the CL.add() call.  This population size also controls the area
 * of influance of the cathedral (which in turn affects the overall power of the player as
 * the God).  Finally the class contains a find(x,y) function, which returns true if the
 * point defined is within the cathedral image boundary.  Still to be added is an
 * influance(x,y) function which will return either a boolean based on whether the point is
 * within the influance circle, or (this would be neater) an int based on how close the point
 * is to the very center of the influance circle.  A god would therefor grow in power as it
 * approached one of it's temples.
 *
 * PopulationExplosion is a thread that takes care of reproduction and cathedral creation.
 * Basically it is a perpetual loop.  At the top of the loop is a Sleep, whos time is based on
 * the size of the population.  After the sleep is a for loop which increases the population
 * size by 10%.  The sleep time is based on population so that we don't end up with an ehtirely
 * exponental population explosion.  The sleep time grows as the population grows.  It's
 * basically 1 second delay per dude.  This seems to work pretty well.  Although I have the
 * feeling that this will be increased in the future.
 *
 * So what does it all do???????????????
 *
 * * It compiles and runs.
 *
 * * It displays a splash screen for a few seconds, during which time the rest of the program
 *   loads and sets itself up.  (The splash screen is in the forground, so you can watch the
 *   main program window set itself up in the background.)
 *
 * * Optionally, the splash screen can slide around the screen, bouncing off the edges of the
 *   monitor.  This doesn't work very well, though..at least on my mid-range PCI video
 *   hardware (it flickers quite a bit).
 *
 * * Two cathedrals are created and displayed.  These are currently created with temp. code.
 *   PopulationExplosion should be creating these...but it isn't.
 *
 * * 10 dudes are created automatically by the PopulationExplosion constructor.  PE then goes
 *   on to run in the background, creating new dudes every once in a while.
 *
 * * Left-clicking on the screen will create one red Dude directly under the mouse
 *   cursor, and one blue Dude will be created randomly somewhere else.  This will probably
 *   have been removed by the time anyone reads this.
 *
 * * The Dudes (only local dudes are created automatically) will move around the window as
 *   befor, except the movement pattern has changed drastically.  Also Dudes now have the
 *   ability to stand still (which they do a quite a bit, to save CPU cycles).
 *
 * * The Dudes will vary slightly in color.  (Ie: all red dudes are red, but some are
 *   brighter and some are darker.)
 *
 * * Right-clicking on an unoccupied section of screen you will get a popup menu
 *   with a list of acts you can perform on the general area.  These acts will have
 *   a circle of influance on which they will be active.  They will also most likely
 *   "taint" the area with an aura of fear.  (ie: the Dudes should try to stay away
 *   from these areas.)  UPDATE: the new movement code is designed to make this easy to
 *   impliment.
 *
 * * Right-clicking in a small area around a Dude will give you a popup menu with both
 *   the normal options and acts you can inflict on the Dude itself.  The menu also
 *   gives you the Dude's number.  Currently the only working option is "Mutate", which
 *   causes the dude to double in size.
 *
 * * Right-clicking on a cathedral will try to identify the cathedral.  In the future it will
 *   bring up a menu with cathedral options.
 *
 * So what doesn't it do???
 *
 * * You can't currently kill a Dude.  This isn't hard to add, though.
 *
 * * There's a problem with erasing the first drawing of a Dude...so you get some
 *   fluff left over on the background.  UPDATE: The problem has finally been discovered, but
 *   I have yet to fix it.
 *
 * * The above fix will not, however, solve the repainting problem.  I don't know how to fix
 *   this problem without using Double Buffering...which I don't want to get into.  So it will
 *   probably never be fixed.
 *
 *
 * Wow, you really read this far?  I'm stunned....  :-)
 */

/**
 *@author	Audin Malmin
 *@author	Chris Corwin
 *@author	Onder Ceyhun
 */
import java.awt.event.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.applet.*;
import java.net.*;
import java.io.*;
import corejava.*;
import corejava.Console.*;

import LocalUtil;			// general util functions.
import Splash;				// The splash screen.
import AboutDialog;

/**
 * The main Colony program class.
 *
 * Contains Main().
 *
 * @version 0.005
 * @author Audin Malmin
 * @author Chris Corwin
 * @author Onder Ceyhun
 */
public class Colony extends Frame implements WindowListener, ActionListener {
	Frame f;
	MainPanel dp;

	/** The list of events this class's event handler knows about
	 *  (and can probably deal with).	 */
	String[] eventlst = {						// List of event strings...
		"One Player Game",
		"Two Player Game",
		"Networked Game (client)",
		"Networked Game (server)",
		"Save Game (if you're very lucky)",
		"Options",
		"Exit",
		"Loser",
		"About"
	};

	public static void main(String args[]) {
		System.out.println("Main()\n");

		Frame frame = new Colony("Acts of God(esses)");
	}

/**
 * The bloody constructor.
 *
 * @param	name	string to use as window title.
 */
	public Colony(String name) {
		Thread splash = new Splash(this);
		splash.start();

		MenuBar mbar = new MenuBar();

		Menu filemenu = new Menu("File");

		LocalUtil.makeMenu(filemenu,
				 new Object[] {
					LocalUtil.makeMenu("Start New Game",
						new Object[] {
							"One Player Game",
							"Two Player Game",
							null,
							"Networked Game (client)",
							"Networked Game (server)"
						}, this),
					"Save Game (if you're very lucky)",
					null,
					"Options",
					null,
					"Exit"
				 },
				 this);
				 
		Menu helpmenu = new Menu("Help");

		LocalUtil.makeMenu(helpmenu,
				new Object[] {
					"Loser",
					null,
					"About"
				},
				this);

		mbar.add(filemenu);
		mbar.add(helpmenu);


		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});

		setLayout(new BorderLayout());

		setMenuBar(mbar);
		setTitle(name);
		setSize(800, 600);
		show();

		addWindowListener(this);
	}

    public void actionPerformed(ActionEvent evt) {
		int i = 0;

		Socket s;

		String arg = evt.getActionCommand();

		for(; i < eventlst.length; i++)
			if(arg.equalsIgnoreCase(eventlst[i]))
				break;

		System.out.println("MainMenuEvent: ActionPerformed: i = " + i);

		switch(i) {
			case 0:
				System.out.println("One Player");
				break;
			case 1:
				System.out.println("Two Player");
				break;
			case 2:
				String addr;

				System.out.println("Network Player (client)");
				
				LocalUtil.master = false;

				System.out.println("Address of server: ");
				addr = Console.readString();
				
				try {
					if(addr.length() == 0)
						s = new Socket("127.0.0.1", 4147);
					else
						s = new Socket(addr, 4147);

					System.out.println("connected!!!! (client)");

					dp = new MainPanel(this, s);
					add("Center", dp);
					show();
				} catch (IOException e) {
					System.out.println("error" + e);
				}

				break;
			case 3:
				System.out.println("Network Player (server)");

				LocalUtil.master = true;

				try {
					ServerSocket server = new ServerSocket(4147);

					System.out.println("waiting (server)");
					s = server.accept();

					System.out.println("connected!!!! (server)");

					dp = new MainPanel(this, s);
					add("Center", dp);
					show();

				} catch (IOException e) {
					System.out.println("error" + e);
				}

				break;
			case 4:
				System.out.println("Save Game (if you're very lucky)");
				break;
			case 5:
				System.out.println("Options");
				break;
			case 6:
				System.out.println("Exit");
				System.exit(0);
				break;
			case 7:
				System.out.println("Loser");
				break;
			case 8:
				System.out.println("About");
				break;
			default:
				System.out.println("Wow, something is screwed");
				break;
		}
	}   

	public void windowOpened(WindowEvent e) {
	}
	public void windowClosing(WindowEvent e) {
		System.out.println("windowClosing");
//		dp.destroy();
		dp = null;
		dispose();
	}
	public void windowClosed(WindowEvent e) {
	}
	public void windowIconified(WindowEvent e) {
	}
	public void windowDeiconified(WindowEvent e) {
	}
	public void windowActivated(WindowEvent e) {
	}
	public void windowDeactivated(WindowEvent e) {
	}
}

/**
 * The real main program object,
 * 
 * Basically, MainPanel is the main program loop.  It sets everything up and
 * handles most events.  MainPanel contains two popup menus, two CL objects, two DL objects,
 * and one PopulationExplosion thread.
 */
class MainPanel extends Panel implements MouseListener, MouseMotionListener,
										 ActionListener {
	Socket soc;

	/** Local Dude list */
	LocalDL local;
	/** Remote Dude list */
	RemoteDL remote;

	/** Local Cathderal list */
	LocalCL	localc;
//	RemoteCL remotec;

	/** PopulationExplosion thread pointer */
	PopulationExplosion pe;

	/** Popup menu used when there is a Dude under the cursor. */
	PopupMenu dudepopup;

	/** Popup menu used when there is nothing but green ground under the cursor. */
	PopupMenu bkpopup;

	/** This is set to the Dude currently under the cursor (if there is one) during a
	 * PopupMenuTrigger event. He is the dude on which the specific acts will be enflicted.*/
	Dude deaddude;

	/** The list of events this class's event handler knows about
	 *  (and can probably deal with).	 */
	String[] eventlst = {						// List of event strings...
		"Info",						// 0
		"Giant",					// 1
		"Zap",						// 2
		"Plague",					// 3
		"etc",						// 4
		"Barney",					// 5
		"The pig is comming...",	// 6
		"Giant Foot"				// 7
	};

	Frame frame;

	public MainPanel(Frame f, Socket x) {
		soc = x;
		frame = f;
		setBackground(Color.green.darker().darker());
		addMouseMotionListener(this);
		addMouseListener(this);

		dudepopup = new PopupMenu();
		LocalUtil.makeMenu(dudepopup,
				 new Object[] {
			     	"This menu has been poorly used",
			        null,
			        "Info",
			        LocalUtil.makeMenu("Mutate",
			        	new Object[] {
							"Giant",
							"Barney",
							"The pig is comming...",
							"Giant Foot"							
						}, this),
			        "Zap",
			        "Plague",
			        "etc" },
			        this);

		bkpopup = new PopupMenu();
		LocalUtil.makeMenu(bkpopup,
		         new Object[] {
		         	"Zap",
		         	"Plague",
		         	"etc"
		         }, this);

		add(dudepopup);
		add(bkpopup);
	}

	public void actionPerformed(ActionEvent e) {
		int i = 0;
		
		MenuItem item = (MenuItem)e.getSource();
		String arg = item.getLabel();

		for(; i < eventlst.length; i++)
			if(arg.equalsIgnoreCase(eventlst[i]))
				break;

		System.out.println("ActionPerformed: i = " + i);

		System.out.print("Event: ActionPerformed: ");

		switch(i) {
			case 0:
				System.out.println("Info");
				Dialog ad = new AboutDialog(frame, new Label[] {
														new Label("Dude statistics"),
														new Label("" + deaddude)
												   });
				ad.show();
				break;
			case 1:
				deaddude.setform(1);
				System.out.println("Mutate dude!");
				break;
			case 2:
				System.out.println("Zap (real)!");
				deaddude.erase(this.getGraphics(), this.getBackground());
				local.kill(deaddude.num());
				break;
			case 3:
				System.out.println("Plague");
				break;
			case 4:
				System.out.println("etc...");
				break;
			case 5:
				System.out.println("Barney");
				break;
			case 6:
				System.out.println("Big");
				break;
			case 7:
				System.out.println("Foot");
				break;
			default:
				System.out.println("Wow, something is screwed");
				break;
		}

		deaddude = null;
	}

	public void mouseDragged(MouseEvent e) {
//		System.out.println("Event: MouseDragged.");
		e.consume();
	}

	public void mouseMoved(MouseEvent e) {
//		System.out.println("Event: MouseMoved.");
	}

	public void mousePressed(MouseEvent e) {
//		System.out.println("Event: MousePressed.");
	    e.consume();
	}

	public void mouseReleased(MouseEvent e) {
		System.out.println("Event: MouseReleased.");
	    e.consume();

		if(e.isPopupTrigger()) {
			Dude tempdude;
			Cathedral tempcath;
			System.out.println("Event: PopupTrigger/OneLeftClick.");

			if((tempdude = local.find(e.getX(), e.getY())) != null) {
				System.out.println("Found him!");
				deaddude = tempdude;
				dudepopup.remove(0);
				dudepopup.insert("Dude: " + deaddude.num(), 0);
				dudepopup.show(e.getComponent(), e.getX(), e.getY());
			} else {
				if((tempdude = remote.find(e.getX(), e.getY())) != null) {
					System.out.println("Found him, but he's a heretic!");
					deaddude = tempdude;
					dudepopup.remove(0);
					dudepopup.insert("Remote Dude: " + deaddude.num(), 0);
					dudepopup.show(e.getComponent(), e.getX(), e.getY());
				} else {					
					if((tempcath = localc.find(e.getX(), e.getY())) != null) {
						System.out.println("It's a cathedral!");
					} else {
					System.out.println("No dudes around");
					bkpopup.show(e.getComponent(), e.getX(), e.getY());
					}
				}
			} // End Popup Menu.
		}
	}

	public void mouseEntered(MouseEvent e) {
//		System.out.println("Event: MouseEntered.");
	}

	public void mouseExited(MouseEvent e) {
//		System.out.println("Event: MouseExited.");
	}

	public void mouseClicked(MouseEvent e) {
//		System.out.println("Event: MouseClicked.");
	}

    public void paint(Graphics g) {
		if(localc != null)
			localc.paint(g);
		else {
			localc = new LocalCL(this);
			localc.paint(g);
		}
	
		if(local != null)
			local.paint(g);
		else {
			local  = new LocalDL(Color.cyan, this, soc);
			local  = new LocalDL(Color.blue, this, soc);
			pe = new PopulationExplosion(local, localc, soc);
			pe.start();
		}

		if(remote != null)
			remote.paint(g);
		else
			remote = new RemoteDL(Color.red, this, soc);
	}

	public void destroy() {
		local.destroy();
		remote.destroy();
		localc.destroy();

		local     = null;
		remote    = null;
		localc    = null;
		
	//	remotec;

		dudepopup = null;
		bkpopup   = null;
	
		deaddude  = null;
	}
}

/**
 * Handles Dude reproduction and Cathedral building.
 *
 * @version %I%, %G%
 * @author Audin Malmin
 * @author Chris Corwin
 * @author Onder Ceyhun
 */
class PopulationExplosion extends Thread {
	LocalDL dlist;
	LocalCL clist;

	PrintWriter out;

	static final int xlist[] = {
		120,
		172,
		215,
		436
	};
	
	static final int ylist[] = {
		648,
		100,
		362,
		512
	};

	public PopulationExplosion(LocalDL l, LocalCL c, Socket s) {
		dlist = l;
		clist = c;

		try {
			out = new PrintWriter(s.getOutputStream(), true);
		} catch (IOException e) {
		}

		for(int i = 10; i > 0; i--)
			dlist.add(100,100, out);
	}

	public void run() {
		int x = 0;
		
		while(true) {
			try {
				Thread.currentThread().sleep(1000 * dlist.getTotal());
			} catch (Exception e) {
				e.printStackTrace();
			}

			for(int i = (int)(dlist.getTotal() * .1); i > 0; i --)
				dlist.add(xlist[++x % 4], ylist[x % 4], out);
				
			clist.add(dlist.getTotal());
		}
	}
}

class LocalDL {
	Dude first;
	Dude last;
	Dude previous;

	Dude curr;

	DudeUpdate du;

	int totaldudes;

	Graphics gc;

	MainPanel dp;

	Socket socket;

	PrintWriter out;

	Color col;

	public LocalDL(Color dudecolor, MainPanel drawp, Socket x) {
		socket = x;

		try {
			out = new PrintWriter(x.getOutputStream(), true);
		} catch (IOException e) {
		}

		dp = drawp;
		gc = dp.getGraphics();
		col = dudecolor;
		first = curr = last = null;
		totaldudes = 0;
	}

	public synchronized boolean add(int x_pos, int y_pos, PrintWriter out) {
		if(first == null) {
			totaldudes = 1;
			previous = null;
			first = curr = last = new Dude(x_pos, y_pos, col, totaldudes);
			if(first != null) {
				curr.draw(gc);
				du = new DudeUpdate(first, dp, out);
				du.start();
			}
		} else {
			totaldudes++;
			previous = curr;
			curr = new Dude(x_pos, y_pos, col, totaldudes);
			last.next = curr;
			last = curr;
			if(curr != null)
				curr.draw(gc);
		}

		out.println("B: " + totaldudes + " " + x_pos + " " + y_pos);
		System.out.println("SEND: B: " + totaldudes + " " + x_pos + " " + y_pos);

		return (curr != null);
	}

	public synchronized Dude find(int x, int y) {
		Dude current;

		for(current = first; current != null; current = current.next)
			if(current.test(x, y))
				return current;

		return null;
	}

	public synchronized boolean kill(int number) {
		Dude current;

		System.out.println("Killing dude number: " + number);

		for(current = first; current.next != null; current = current.next)
			if(current.next.num == number)
				current.next = current.next.next;				

		return false;
	}

	public synchronized Dude current() {
		return curr;
	}

	public synchronized Dude reset() {
		curr = first;
		return curr;
	}

	public synchronized boolean more() {
		if(curr == null)
			return false;
		
		if(curr != null && curr.next != null)
			return true;
		else
			return false;
	}

	public int getTotal() {
		return totaldudes;
	}

	public void paint(Graphics g) {
		Dude current;

		if(first == null)
			return;

		for(current = first; current != null; current = current.next)
			current.draw(g);
	}

	public void destroy() {
		du.stop();
		du = null;
		dp = null;
	}
}

class RemoteDL extends LocalDL {
	RDude first;
	RDude last;
	RDude previous;

	RDude curr;

	RDudeUpdate du;

	RNetDude nd;

	public RemoteDL(Color dudecolor, MainPanel drawp, Socket s) {
		super(dudecolor, drawp, s);

		nd = new RNetDude(first, dp, socket, this);
		nd.start();
	}

	public synchronized boolean add(int n, int x_pos, int y_pos) {
		System.out.println("RemoteDL.add(" + n + ")");

		if(first == null) {
			totaldudes = 1;
			previous = null;
			first = curr = last = new RDude(x_pos, y_pos, col, n);
			if(first != null) {
				curr.draw(gc);
				du = new RDudeUpdate(first, dp);
				du.start();
			}
		} else {
			totaldudes++;
			previous = curr;
			curr = new RDude(x_pos, y_pos, col, n);
			last.next = curr;
			last = curr;
			if(curr != null)
				curr.draw(gc);
		}
		return (curr != null);
	}
}

class RNetDude extends Thread {
	Dude fst;
	Dude curr;
	Graphics gc;
	MainPanel dp;
	Color bg;

	Socket soc;

	RemoteDL remoteDL;

	BufferedReader in;

	String str;

	public RNetDude(RDude first, MainPanel drawp, Socket s, RemoteDL dl) {
		soc = s;
		remoteDL = dl;

		try {
			in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		} catch (IOException e) {
		}

		fst = first;
		dp = drawp;
		gc = dp.getGraphics();
		bg = dp.getBackground();

		System.out.println("RNetDude: created.");
	}

	public void run() {
		System.out.println("RNetDude: run.");
		while(true) {
			try {
				str = in.readLine();
			} catch (IOException e) {
			}		

			switch(str.charAt(0)) {
				case 'D':
				case 'd': {
					int found = 0;
					StringTokenizer st = new StringTokenizer(str, " ");

					st.nextToken();
					int dnum  = Format.atoi(st.nextToken());
					int destx = Format.atoi(st.nextToken());
					int desty = Format.atoi(st.nextToken());
					int dx    = Format.atoi(st.nextToken());
					int dy    = Format.atoi(st.nextToken());
					int moves = Format.atoi(st.nextToken());

//					System.out.println("RCVD: D: " + dnum + " " + destx + " " + desty + " " +
//					                   dx + " " + dy + " " + moves);

//					System.out.print("Search dude: " + dnum + "...");
					
					RDude current;

					for(current = remoteDL.first; current != null; current = current.next) {
//						System.out.println("Checking: " + current.num);
						if(current.num == dnum) {
							found = 1;
//							System.out.println("Found him!!!");
							current.moveto(destx, desty, dx, dy, moves);
						}
					}

					if(found == 0)
						System.out.println("COULDN'T FIND HIM!!");
					break;
				}
				case 'C':
					break;
				case 'K':
					break;
				case 'B': {
//					System.out.println("UNPARSED: " + str);

					StringTokenizer st = new StringTokenizer(str, " ");

					st.nextToken();
					int dnum = Format.atoi(st.nextToken());
					int dx = Format.atoi(st.nextToken());
					int dy = Format.atoi(st.nextToken());

//					System.out.println("RCVD: B: " + dnum + " " + dx + " " + dy);

					remoteDL.add(dnum, dx, dy);
					break;
				}
				default:
					System.out.println("RCVD: (STRANGE): " + str);
					break;
			}
		}			
	}
}

class RDudeUpdate extends Thread {
	RDude fst;
	RDude curr;
	Graphics gc;
	MainPanel dp;
	Color bg;

	public RDudeUpdate(RDude first, MainPanel drawp) {
		fst = first;
		dp = drawp;
		gc = dp.getGraphics();
		bg = dp.getBackground();
		System.out.println("RDudeUpdate created.");
	}

	public void run() {
		System.out.println("RDudeUpdate: run.");

		if(gc == null)
			System.out.println("wow.1.1.1");
		while(true)
			for(curr = fst; curr != null; curr = curr.next) {
				if(curr != null && gc != null && bg != null)
					curr.move(gc, bg);
			}
	}
}

class RDude extends Dude {
	RDude next;

	RDude(int x1, int y1, Color color, int n) {
		super(x1, y1, color, n);
	}

	void move(Graphics g, Color bg) {
		if(!moving) {
			//
		} else {
//			System.out.println("....Remote dude MOVING!");
			g.setXORMode(bg);

			draw(g);

			if(form != formwanted)
				form = formwanted;

			x += dx;
			y += dy;

			if((moves--) == 0) {
				x = destx;
				y = desty;
				moving = false;
			}

			draw(g);
		}
	}
}

class Dude {
	int x, y;
	int dx = 1;
	int dy = 1;

	int destx, desty;
	
	Color col;

	int form = NORMAL;

	int formwanted = NORMAL;

	int num;

	boolean drawn = false;

	boolean moving = false;

	int moves = 0;

	int newdude = 500000;

	Dude next;

	static final int NORMAL = 0;
	static final int BIG    = 1;
	static final int LITTLE = 2;

	Dude(int x1, int y1, Color color, int n) {
		System.out.println("Dude: New dude, number " + n + ".");

		int funny;
		x = (short)x1;
		y = (short)y1;
		col = color;
		num = n;
		next = null;
	
		// This "funny" stuff randomly assigns a shade to the new Dude.
		
		if((funny = (int)((Math.random() - 0.5) * 7)) > 0)
			for(; funny > 0; funny--)
			    col = col.brighter();
		else
			for(; funny < 0; funny++)
				col = col.darker();
	}

	boolean test(int x1, int y1) {
		return (x1 < x + 8 && x - 8 < x1) && (y1 < y + 15 && y1 > y - 15);
	}

	void setform(int x) {
		formwanted = x;
	}

	void moveto(int desx, int desy, int xd, int yd, int m) {
//		System.out.println("moveto()");
		destx = desx;
		desty = desy;
		dx = xd;
		dy = yd;
		moves = m;
		moving = true;
	}

	void move(Graphics g, Color bg, PrintWriter out) {
		if(newdude > 0) {
			newdude--;
			return;
		}
		
		if(!moving) {
			if(Math.random() < 0.0025) {
				int direction = (int)(Math.random() * 8);
				int distance  = (int)(Math.random() * 100);

				switch(direction) {
					case 0:
						dx = 0;
						dy = -1;
						break;
					case 1:
						dx = 1;
						dy = -1;
						break;
					case 2:
						dx = 1;
						dy = 0;
						break;
					case 3:
						dx = 1;
						dy = 1;
						break;
					case 4:
						dx = 0;
						dy = 1;
						break;
					case 5:
						dx = -1;
						dy = 1;
						break;
					case 6:
						dx = -1;
						dy = 0;
						break;
					case 7:
						dx = -1;
						dy = -1;
						break;
					default:
						System.out.println("Wow, weird...");
						break;
				}

				destx = (dx * distance) + x;
				desty = (dy * distance) + y;

				if((0 < destx && destx < 1024) && (0 < desty  && desty < 768)) {
					moves = distance;
					moving = true;					
					out.println("D " + num + " " + destx + " " + desty + " "
					            + dx + " " + dy + " " + moves);
//					System.out.println("SEND: D " + num + " " + destx + " " + desty + " "
//					            + dx + " " + dy + " " + moves);

//					out.println("D " + num + " " + destx + " " + desty);
//					System.out.println("SEND: D " + num + " " + destx + " " + desty);
				}
			}
		} else {
			g.setXORMode(bg);

			draw(g);

			if(form != formwanted)
				form = formwanted;

			x += dx;
			y += dy;

			if((moves--) == 0) {
				x = destx;
				y = desty;
				moving = false;
			}

			draw(g);
		}
	}

	void draw(Graphics g) {
		if(g == null)
			return;
			
		switch(form) {
			case NORMAL:
				g.setColor(Color.black);

				g.drawLine(x, y + 4 , x, y + 10);

				g.drawLine(x, y + 10, x - 3, y + 17);
				g.drawLine(x, y + 10, x + 3, y + 17);
				g.drawLine(x, y + 2, x - 4, y + 10);
				g.drawLine(x, y + 2, x + 4, y + 10);

				g.setColor(col);
				g.fillOval(x - 4, y - 5, 8, 8);
			break;
			
			case BIG:
				g.setColor(Color.black);

				g.drawLine(x, y + 4 , x, y + 20);

				g.drawLine(x, y + 20, x - 3, y + 17);
				g.drawLine(x, y + 20, x + 3, y + 17);
				g.drawLine(x, y + 2, x - 4, y + 20);
				g.drawLine(x, y + 2, x + 4, y + 20);

				g.setColor(col);
				g.fillOval(x - 8, y - 10, 16, 16);
			break;
		}
	}

	void erase(Graphics g, Color bg) {
		g.setColor(bg);

		switch(form) {
			case NORMAL:
				g.drawLine(x, y + 4 , x, y + 10);

				g.drawLine(x, y + 10, x - 3, y + 17);
				g.drawLine(x, y + 10, x + 3, y + 17);
				g.drawLine(x, y + 2, x - 4, y + 10);
				g.drawLine(x, y + 2, x + 4, y + 10);

				g.fillOval(x - 4, y - 5, 8, 8);
			break;
			
			case BIG:
				g.drawLine(x, y + 4 , x, y + 20);

				g.drawLine(x, y + 20, x - 3, y + 17);
				g.drawLine(x, y + 20, x + 3, y + 17);
				g.drawLine(x, y + 2, x - 4, y + 20);
				g.drawLine(x, y + 2, x + 4, y + 20);

				g.fillOval(x - 8, y - 10, 16, 16);
			break;
		}
	}	

	public int num() {
		return num;
	}

	public String toString() {
		return ("Dude[col=" + col + ", X=" + x + ", Y=" + y + "]");
	}
}

class DudeUpdate extends Thread {
	Dude fst;
	Dude curr;
	Graphics gc;
	MainPanel dp;
	Color bg;

	PrintWriter out;

	public DudeUpdate(Dude first, MainPanel drawp, PrintWriter o) {
		out = o;
		fst = first;
		dp = drawp;
		gc = dp.getGraphics();
		bg = dp.getBackground();
		System.out.println("DudeUpdate created.");
	}

	public void run() {
		System.out.println("DudeUpdate: run.");
		while(true)
			for(curr = fst; curr != null; curr = curr.next) {
				if(curr != null && gc != null && bg != null)
					curr.move(gc, bg, out);
			}
	}
}

class LocalCL {
	Cathedral first;
	Cathedral last;
	Cathedral previous;

	Cathedral curr;

	int total;

	Graphics gc;

	MainPanel dp;

	Color col;

	boolean t1 = false,
	        t2 = false,
	        t3 = false,
	        t4 = false;

	public LocalCL(MainPanel drawp) {
		dp = drawp;
		gc = dp.getGraphics();
		first = curr = last = null;
		total = 0;
	}

//	public synchronized boolean add(int x_pos, int y_pos, int pop) {
	public synchronized boolean add(int pop) {	
		int x = 100, y = 100;
		
		String file = null;
		int inf = 0;

		if(pop == 0) {
		}

		if(pop >= 1 && !t1) {
			if(LocalUtil.master) {
				x = 120;
				y = 648;
			} else {
				x = 1024 - 120;
				y = 768  - 120;
			}

			file = new String("3dk022.gif");
			inf = 120;
			t1 = true;
		}

		if(pop >= 25 && t1 && !t2) {
			if(LocalUtil.master) {
				x = 172;
				y = 100;
			} else {
				x = 1024 - 172;
				y = 768 - 100;
			}

			file = new String("Lcondos1.gif");
			inf = 200;
			t2 = true;
		}

		if(pop >= 50 && t1 && t2 && !t3) {
			if(LocalUtil.master) {
				x = 215;
				y = 362;
			} else {
				x = 1024 - 215;
				y = 768  - 362;
			}

			file = new String("Gas.gif");
			inf = 220;
			t3 = true;
		}

		if(pop >= 100 && t1 && t2 && t3 && !t4) {
			if(LocalUtil.master) {
				x = 436;
				y = 512;
			} else {
				x = 1024 - 436;
				y = 768  - 512;
			}

			file = new String("temple.gif");
			inf = 300;
			t4 = true;
		}

		if(file == null || inf == 0)
			return false;

		if(first == null) {
			total = 0;
			previous = null;
			first = curr = last = new Cathedral(file, x, y, inf, total, dp);
			if(first != null) {
				curr.draw(gc);
//				du = new DudeUpdate(first, dp);
//				du.start();
			}
		} else {
			previous = curr;
			curr = new Cathedral(file, x, y, inf, total, dp);
			last.next = curr;
			last = curr;
			if(first != null) {
				curr.draw(gc);
			}
		}
		total++;
		return (curr != null);
	}

	public synchronized Cathedral find(int x, int y) {
		Cathedral current;

		for(current = first; current != null; current = current.next)
			if(current.isCathedral(x, y))
				return current;

		return null;
	}

	public synchronized Cathedral current() {
		return curr;
	}

	public synchronized Cathedral reset() {
		curr = first;
		return curr;
	}

	public synchronized boolean more() {
		if(curr == null)
			return false;
		
		if(curr != null && curr.next != null)
			return true;
		else
			return false;
	}

	public int getTotal() {
		return total;
	}

	public void paint(Graphics g) {
		Cathedral current;

		for(current = first; current != null; current = current.next)
			current.draw(g);
	}

	public void destroy() {
		dp = null;
	}
}

class RemoteCL extends LocalCL {
	Socket incomming;

	public RemoteCL(MainPanel drawp, Socket x) {
		super(drawp);
		incomming = x;
	}
}

class Cathedral {
	String name;
	Image img;
	MainPanel dp;
	int x, y, num;
	int height, width;

	int inf;

	Cathedral next;

	public Cathedral(String n, int x1, int y1, int influence, int nu, MainPanel d) {
		name = n;
		x = x1;
		y = y1;
		num = nu;
		dp = d;

		inf = influence;

		img = Toolkit.getDefaultToolkit().getImage(name);
		LocalUtil.waitforimg(dp, img);
		
		if(img != null) {
			height = img.getHeight(dp);
			width  = img.getWidth(dp);
		}

		System.out.println("Width: " + width + "Height: " + height);
	}

/*
 * returns true if the point (x1,y1) is inside the cathedral.
 *         false if not.
 */
	boolean isCathedral(int x1, int y1) {
		return (x1 > x - width/2 && x1 < x + width/2) &&
		       (y1 > y - height/2 && y1 < y + height/2);
	}

/*
 * returns a non-zero number based on wether the point (x1,y1) is in the cathedral's zone.
 * returns 0 if it isn't.
 */
	int isCathZone(int x1, int y1) {
		int ret = pyth(x1, y1);
			
		if(ret <= (inf / 2)) {
			System.out.println("Cathedeal: Test returning " + ret);
			return ret;
		} else
			return 0;
	}

/*
 * Kind of an amalgam of the above 2 functions.  not very useful anymore...
 */
	int test(int x1, int y1) {
		if((x1 > x - width/2  && x1 < x + width/2) &&
		   (y1 > y - height/2 && y1 < y + height/2)) {
			System.out.println("Cathedeal: Test returning 100");
			return 100;
		} else {
			int ret = pyth(x1, y1);
			
			if(ret <= (inf / 2)) {
				System.out.println("Cathedeal: Test returning " + ret);
				return ret;
			} else {
				System.out.println("Cathedeal: Test returning 0");
				return 0;
			}
		}
	}

	private int pyth(int x1, int y1) {
		int ret;

		int a = Math.abs(x - x1);
		int b = Math.abs(y - y1);

		System.out.println("Pyth returning: " + (int)Math.sqrt((a * a) + (b * b)));

		return((int)Math.sqrt((a * a) + (b * b)));
	}

	public void draw(Graphics g) {
		if(g != null && img != null && dp != null) {
			g.drawImage(img, x - (img.getWidth(dp)/2), y - (img.getHeight(dp)/2), dp);
			g.drawOval(x - (inf /2 ), y - (inf /2), inf, inf);
		}
	}
}
