com.prodp.apsim.APProcessHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.prodp.apsim.APProcessHandler.java

Source

//INFO:
/*
 * 
 *  This program cannot be redistributed under any other persons.
 * 
 * Uses class "com.centerkey.utils.BareBonesBrowserLaunch" a free utility package released
 * under public domain.
 * Also uses packages included in "jgoodies-common-1.2.1.jar" and "jgoodies-looks-2.4.2.jar" for setLookAndFeel.
 * Also uses AnaglyphCanvas3D, at http://sourceforge.net/projects/anaglyphcanvas3/.
 * Uses screenshot utility at http://www.java.net/node/647363.
 * Uses Java 3D library at http://java3d.java.net/
 * 
 */

//Bug 11611I (November 6, 2011 SOLVED)

package com.prodp.apsim;

//imports
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.MouseInfo;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.imageio.ImageIO;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.vecmath.Point3d;
import javax.vecmath.Point3i;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import anaglyphcanvas3d.AnaglyphMode;
import anaglyphcanvas3d.StereoMode;

import com.centerkey.utils.BareBonesBrowserLaunch;
import com.jgoodies.looks.Options;
import com.jgoodies.looks.plastic.Plastic3DLookAndFeel;
import com.jgoodies.looks.plastic.PlasticLookAndFeel;
import com.jgoodies.looks.plastic.theme.DesertBluer;
import com.sun.j3d.utils.image.TextureLoader;

/**
 * 
 * @author Jonathan
 * @version 0.0
 * @since 7-3-2012 (Javadoc Created)
 * 
 */

/**
 * 
 * Main controller for all APSim processes. Handles {@link APProcess}es and
 * switches between them. All necessary functions are called from here, or are
 * declared here.
 * 
 */

// This is the main class for everything: implements all the listeners
public class APProcessHandler extends APObject
        implements ActionListener, ChangeListener, MouseListener, MouseMotionListener, KeyListener {

    /*
     * 
     * The data types are indexed as follows:
     * 
     * -private -protected -default -public
     */

    private static Vector3d OLD_SR_VECTOR;

    // world objects
    private static APRenderer c3d;
    // private SimpleUniverse world;
    private static APSceneGraph scene;
    // private final BranchGroup objects = new BranchGroup();

    // graphics
    private final GraphicsConfigTemplate3D graphics = new GraphicsConfigTemplate3D();
    private GraphicsConfiguration graphicsConfig;
    // main thread
    private volatile Thread threedanim;

    // shape array
    private volatile static QuadArray cMain;

    // wind array
    private volatile static LineArray windLoc;

    // light can only stay in this boundary
    private static BoundingSphere bounds = new BoundingSphere(new Point3d(0, 0, 0), 20);

    // shapes on scene graph
    private static final Shape3D ground = new Shape3D();
    private static final Shape3D aobjects = new Shape3D();
    private static final Shape3D lobjects = new Shape3D();
    private static final Shape3D selections = new Shape3D();

    // selection box
    private static QuadArray selection = new QuadArray(
            APFinalData.brushlocs.get(APFinalData.MAX_BRUSH_SIZE).size() * 24,
            QuadArray.COORDINATES | QuadArray.BY_REFERENCE);
    private static float[] selectionCoords = new float[APFinalData.brushlocs.get(APFinalData.MAX_BRUSH_SIZE).size()
            * 72];

    private static Transform3D mainRoutineFinalTransform = new Transform3D();

    // setting sensitivity
    private static int sensitivity = 90;

    // Temporary Variables
    // This temporary element is the default element. Should be water in the
    // choose dialog.
    private short tempelement = (short) (APFinalData.DEFAULT_MAT.getID() - 1);
    private boolean antiaOn;
    private AnaglyphMode amode = null;

    // Is running?
    private volatile static boolean isRunning;

    // Number of Untitled s
    private int untitled = 1;

    // Client/Server pair
    private static APClient client = new APClient();
    private static APServer server = new APServer();

    // The sky
    private static APSky sky = new APSky();
    private static TransformGroup skyTG = new TransformGroup();
    private static Transform3D skyTrans = new Transform3D();
    private static double skyRot = 0;

    // number of blocks on scene
    private static int blockNumber = 0;

    volatile static boolean isLeftMouseDown = false, isRightMouseDown = false; // do
    // not
    // remove
    // this

    // key detection flags
    volatile static boolean[] keys = new boolean[15];
    volatile static boolean[] prevaction = new boolean[8];

    // mouse control
    static Robot center;

    // Graphics for pause draw and others
    Graphics2D g;

    /**
     * A list of all the {@link APProcess}es active.
     */

    // List of processes and reactions
    public static final APProcessList APList = new APProcessList();

    /**
     * A static list of all the {@link APReaction}s.
     */

    public static final APReactionList APRList = new APReactionList();

    /**
     * Current tool
     */

    private static int tool = APFinalData.TOOL_SPRAYVAC;

    // Main getters and setters

    /**
     * 
     * Moves the player left (relative to position).
     * 
     * @param d
     *            unused
     */

    public static void moveRelativeLeft(double d) {
        APProcess process = APList.getCurrentProcess();

        process.getCurPos().add(Transform3DUtils.rotateEulerVector3d(0,
                (APList.getCurrentProcess().getRotateX() + Math.PI) * APFinalData.DEGREE + 90, 0.05));
    }

    /**
     * 
     * Moves the player right (relative to position).
     * 
     * @param d
     *            unused
     */

    public static void moveRelativeRight(double d) {
        APProcess process = APList.getCurrentProcess();

        process.getCurPos().add(Transform3DUtils.rotateEulerVector3d(0,
                (APList.getCurrentProcess().getRotateX() + Math.PI) * APFinalData.DEGREE - 90, 0.05));
    }

    /**
     * 
     * Moves the player forwards (relative to position).
     * 
     * @param d
     *            unused
     */

    public static void moveRelativeFwd(double d) {
        APProcess process = APList.getCurrentProcess();

        process.getCurPos().add(Transform3DUtils.rotateEulerVector3d(0,
                (APList.getCurrentProcess().getRotateX() + Math.PI) * APFinalData.DEGREE, 0.05));
    }

    /**
     * 
     * Moves the player backwards (relative to position).
     * 
     * @param d
     *            unused
     */

    public static void moveRelativeBack(double d) {
        APProcess process = APList.getCurrentProcess();

        process.getCurPos().add(Transform3DUtils.rotateEulerVector3d(0,
                (APList.getCurrentProcess().getRotateX() + Math.PI) * APFinalData.DEGREE + 180, 0.05));
    }

    /**
     * 
     * Moves the player up.
     * 
     * @param d
     *            unused
     */

    public static void moveRelativeUp(double d) {
        APList.getCurrentProcess().getCurPos().add(new Vector3d(0, 0.05, 0));
    }

    /**
     * 
     * Moves the player down.
     * 
     * @param d
     *            unused
     */

    public static void moveRelativeDown(double d) {
        APProcess process = APList.getCurrentProcess();

        process.getCurPos().add(new Vector3d(0, -0.05, 0));
        if (process.getCurPos().y < 0.3) {
            process.getCurPos().y = 0.3;
        }
    }

    /**
     * 
     * Increments the brush size by one.
     * 
     * It detaches the interchangable {@link BranchGroup} from its parent and
     * attaches the bigger brush {@link BranchGroup} to the visible parent
     * {@link BranchGroup}.
     * 
     */

    public static void incBrushSize() {
        APProcess process = APList.getCurrentProcess();

        if (process.getBrushSize() != APFinalData.MAX_BRUSH_SIZE - 1) {
            process.setBrushSize(process.getBrushSize() + 1);

            // Force the brush to update
            OLD_SR_VECTOR = null;
        }
    }

    /**
     * 
     * Decrements the brush size by one.
     * 
     * It detaches the interchangable {@link BranchGroup} from its parent and
     * attaches the smaller brush {@link BranchGroup} to the visible parent
     * {@link BranchGroup}.
     * 
     */

    public static void decBrushSize() {
        APProcess process = APList.getCurrentProcess();

        if (process.getBrushSize() != 0) {
            process.setBrushSize(process.getBrushSize() - 1);

            // Force the brush to update
            OLD_SR_VECTOR = null;
        }
    }

    /**
     * 
     * Toggles if the scene is frozen.
     * 
     */

    public static void toggleFrozen() {
        APProcess process = APList.getCurrentProcess();

        process.setFrozen(!process.isFrozen());
    }

    /**
     * 
     * Toggles if the selection box is hidden.
     * 
     */

    public static void toggleBoxHide() {
        APProcess process = APList.getCurrentProcess();

        process.setBoxHidden(!process.isBoxHidden());

        if (process.isBoxHidden())
            Arrays.fill(selectionCoords, -1);
        else
            selectionCoords = APFinalData.spherecoords[process.getBrushSize()];
    }

    /**
     * 
     * Gets the cube face array of the controller.
     * 
     * @return the cube face array
     */

    public static QuadArray getGeometry() {
        return cMain;
    }

    /**
     * 
     * Gets the client of the client-server networking system.
     * 
     * @return the client
     */

    public static APClient getClient() {
        return client;
    }

    /**
     * 
     * Gets the server of the client-server networking system.
     * 
     * @return the server
     */

    public static APServer getServer() {
        return server;
    }

    /**
     * 
     * Sets the sensitivity of the mouse.
     * 
     * @param sense
     *            the sensitivity as a percent
     */

    public static void setSensitivity(int sense) {
        sensitivity = sense;
    }

    /**
     * 
     * Gets the sensitivity of the mouse.
     * 
     * @return the sensitivity
     */

    public static int getSensitivity() {
        return sensitivity;
    }

    /**
     * 
     * Sets the brush size to a new brush size.
     * 
     * If the new brush size is greater than the current brush size it increases
     * the brush size until it is equal.
     * 
     * If the new brush size is less than the current brush size it decreases
     * the brush size until it is equal.
     * 
     * Otherwise nothing happens.
     * 
     * @param s
     *            the new brush size.
     */

    public static void setBrushSize(int s) {
        APProcess p = APList.getCurrentProcess();

        if (s > p.getBrushSize()) {
            while (s != p.getBrushSize())
                incBrushSize();
            return;
        }

        if (s < p.getBrushSize()) {
            while (s != p.getBrushSize())
                decBrushSize();
            return;
        }
    }

    /**
     * 
     * Gets the current brush size.
     * 
     * @return the brush size
     */

    public static int getBrushSize() {
        return APList.getCurrentProcess().getBrushSize();
    }

    /**
     * 
     * Sets the current position of the player.
     * 
     * @param p
     *            the position
     */

    public static void setCurrentPosition(Point3d p) {
        APList.getCurrentProcess().setCurPos(p);
    }

    /**
     * 
     * Gets the current position of the player.
     * 
     * @return the position
     */

    public static Point3d getCurrentPosition() {
        return APList.getCurrentProcess().getCurPos();
    }

    /**
     * 
     * Gets the 3D canvas (screen).
     * 
     * @return the canvas
     */

    public static APRenderer getCanvas() {
        return c3d;
    }

    /**
     * 
     * Gets if the program is running.
     * 
     * @return the flag
     */

    public static boolean getIsRunning() {
        return isRunning;
    }

    /**
     * 
     * Gets how many blocks are on the scene.
     * 
     * @return the number of blocks
     */

    public static int getBlockNumber() {
        return blockNumber;
    }

    /**
     * 
     * Sets how many blocks are on the scene
     * 
     * @param blockNumber
     */

    public static void setBlockNumber(int blockNumber) {
        APProcessHandler.blockNumber = blockNumber;
    }

    /**
     * 
     * Get the current tool ID of the player.
     * 
     * @return the ID
     */

    public static int getTool() {
        return tool;
    }

    /**
     * 
     * Set the tool ID of the player.
     * 
     * @param tool
     *            the ID
     */

    public static void setTool(int tool) {
        APProcessHandler.tool = tool;
    }

    // End of the getters and setters

    // Ugly initialization code

    /**
     * 
     * Sets up the whole game.
     * 
     * Sets up things like the GUI, put through {@link APGUI}, the scene
     * objects, and other things in {@link APFinalData}.
     * 
     * @throws AWTException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws UnsupportedLookAndFeelException
     * @throws CloneNotSupportedException
     */

    public void init() throws AWTException, ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException, CloneNotSupportedException {

        // Get the icon
        try {
            APFinalData.apIconImage = new ImageIcon(
                    ImageIO.read(new APMain().getClass().getResource("icons/apsim.png"))).getImage();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // For mouse control and screenshots
        center = new Robot();

        // Set Graphics
        graphics.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED);
        graphics.setDoubleBuffer(GraphicsConfigTemplate.PREFERRED);
        graphicsConfig = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
                .getBestConfiguration(graphics);
        c3d = new APRenderer(graphicsConfig, (JComponent) APFinalData.mainFrame.getContentPane());
        c3d.setVisible(true);

        // setLookAndFeel
        PlasticLookAndFeel.set3DEnabled(true);
        PlasticLookAndFeel.setTabStyle(Plastic3DLookAndFeel.TAB_STYLE_METAL_VALUE);
        PlasticLookAndFeel.setPlasticTheme(new DesertBluer());

        UIManager.setLookAndFeel(new PlasticLookAndFeel());

        Options.setPopupDropShadowEnabled(true);
        Options.setUseNarrowButtons(true);

        // This is the Processor initialization
        final APProcess firstProcess = new APProcess("Untitled" + untitled + ".aps");
        APList.addProcess(firstProcess);

        APProcess process = APList.getCurrentProcess();

        // The default position
        mainRoutineFinalTransform.setTranslation(new Vector3f(0, 0, 0));

        // Set up the grass
        Transform3D grass3d = new Transform3D();
        grass3d.setScale(50);

        TextureAttributes ta = new TextureAttributes();
        ta.setTextureMode(TextureAttributes.MODULATE);
        ta.setPerspectiveCorrectionMode(TextureAttributes.FASTEST);
        ta.setTextureTransform(grass3d);

        Appearance floorapp = new Appearance();
        floorapp.setTexture(
                new TextureLoader(new APRandImage(1024, 1024, BufferedImage.TYPE_3BYTE_BGR, new Color(111, 71, 40),
                        new Color(50, 30, 7), new Color(104, 57, 23), new Color(22, 12, 6)), "RGB", null)
                                .getTexture());
        floorapp.setTextureAttributes(ta);

        // Referencing the position of the floor to floorcoord
        APFinalData.floor.setCoordRefFloat(APFinalData.floorcoord);
        APFinalData.floor.setTexCoordRefFloat(0, APFinalData.floortex);

        // Hide the cursor
        c3d.setCursor(APFinalData.transparentCursor);

        // Main geometry array
        cMain = new QuadArray(APFinalData.LIMIT * 24, QuadArray.COORDINATES | QuadArray.COLOR_4
                | QuadArray.BY_REFERENCE | QuadArray.TEXTURE_COORDINATE_2);

        // Wind geometry array
        windLoc = new LineArray(2 * APFinalData.PRESSURE_COUNT,
                LineArray.COORDINATES | LineArray.COLOR_3 | LineArray.BY_REFERENCE);

        // Dynamic positioning
        cMain.setCapability(GeometryArray.ALLOW_REF_DATA_READ);
        cMain.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE);

        windLoc.setCapability(GeometryArray.ALLOW_REF_DATA_READ);
        windLoc.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE);

        // Set the reference of the geometry array to the coordinates and the
        // colors
        cMain.setCoordRefFloat(process.coords);
        cMain.setColorRefByte(process.colors);
        cMain.setTexCoordRefFloat(0, process.texturecoords);

        windLoc.setCoordRefFloat(process.windcoords);
        windLoc.setColorRefByte(process.windcolors);

        // Now add the reactionary components

        APRList.addReaction(new APReaction(new APPair(APMaterial.WOOD.getID(), APMaterial.LAVA.getID()),
                APMaterial.FIRE, 0.2f, APFinalData.CHANGE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.WOOD.getID(), APMaterial.FIRE.getID()),
                APMaterial.FIRE, 0.1f, APFinalData.CHANGE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.STONE.getID(), APMaterial.LAVA.getID()),
                APMaterial.LAVA, 0.1f, APFinalData.CHANGE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.WATER.getID(), APMaterial.LAVA.getID()),
                APMaterial.STEAM, 0.4f, APFinalData.CHANGE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.LAVA.getID(), APMaterial.WATER.getID()),
                APMaterial.STONE, 0.9f, APFinalData.CHANGE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.FIRE.getID(), APMaterial.WATER.getID()),
                APMaterial.NULL, 0.9f, APFinalData.REMOVE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.WATER.getID(), APMaterial.ICE.getID()),
                APMaterial.ICE, 0.01f, APFinalData.CHANGE_BLOCK_FLAG));
        APRList.addReaction(new APReaction(new APPair(APMaterial.ICE.getID(), APMaterial.LAVA.getID()),
                APMaterial.WATER, 1, APFinalData.CHANGE_BLOCK_FLAG));

        APFinalData.mainFrame.init();

        // Set the canvas size
        c3d.setPreferredSize(new Dimension(550, 300));
        c3d.setLocation(0, 0);

        // Don't let the geometry clip off
        final Appearance polyAppearance = new Appearance();
        polyAppearance.setPolygonAttributes(
                new PolygonAttributes(PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0));
        polyAppearance.setTextureAttributes(new TextureAttributes());
        polyAppearance.setTexture(APFinalData.textures);

        final Appearance lineAppearance = new Appearance();
        lineAppearance.setLineAttributes(new LineAttributes(1, LineAttributes.PATTERN_SOLID, false));

        TransparencyAttributes trans = new TransparencyAttributes();
        trans.setTransparencyMode(TransparencyAttributes.BLENDED);
        polyAppearance.setTransparencyAttributes(trans);

        // Add the GeometryArray to the Shape3D
        aobjects.addGeometry(cMain);
        aobjects.setAppearance(polyAppearance);

        lobjects.addGeometry(windLoc);
        lobjects.setAppearance(lineAppearance);

        ground.addGeometry(APFinalData.floor);
        ground.setAppearance(floorapp);

        // Initialize the AmbientLight
        APFinalData.whiteLight.setInfluencingBounds(bounds);

        // Sky
        Background back = new Background();
        back.setApplicationBounds(new BoundingSphere(new Point3d(0, 0, 0), 1));

        skyTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        skyTrans.rotX(0);
        skyTG.setTransform(skyTrans);
        skyTG.addChild(sky);

        BranchGroup backgeom = new BranchGroup();
        backgeom.addChild(skyTG);

        back.setGeometry(backgeom);

        // Add the brush
        selection.setCapability(QuadArray.ALLOW_REF_DATA_WRITE);
        selections.addGeometry(selection);
        selections.setAppearance(APFinalData.wireframe);

        APFinalData.brushes.addChild(selections);

        // Scene initialization
        scene = new APSceneGraph(c3d);
        scene.addChild(APFinalData.whiteLight, aobjects, lobjects, ground, back, APFinalData.brushes);

        // Add it to the world
        scene.addBranchGraph();

        // Prepare the world
        scene.prepare(mainRoutineFinalTransform);

        // Make the Canvas draw right
        c3d.setDoubleBufferEnable(true);

        // Stereo Red-blue
        c3d.getView().getPhysicalBody().setLeftEyePosition(new Point3d(-0.01, 0, 0));
        c3d.getView().getPhysicalBody().setRightEyePosition(new Point3d(0.01, 0, 0));
        c3d.getView().setDepthBufferFreezeTransparent(false);

        // Note: c3d has no anaglyphs
        c3d.setStereoMode(StereoMode.OFF);

        // Add Canvas listeners
        c3d.addMouseListener(this);
        c3d.addMouseMotionListener(this);
        c3d.addKeyListener(this);

        // Add menubar listeners
        APFinalData.Exit.addActionListener(this);
        APFinalData.New.addActionListener(this);
        APFinalData.Open.addActionListener(this);
        APFinalData.Save.addActionListener(this);
        APFinalData.Save_As.addActionListener(this);
        APFinalData.Import.addActionListener(this);
        APFinalData.Export.addActionListener(this);
        APFinalData.Element.addActionListener(this);
        APFinalData.Exit.addActionListener(this);
        APFinalData.Join_Server.addActionListener(this);
        APFinalData.Create_Server.addActionListener(this);
        APFinalData.View_Options.addActionListener(this);
        APFinalData.Sound.addActionListener(this);
        APFinalData.Online_Help.addActionListener(this);
        APFinalData.About_Us.addActionListener(this);
        APFinalData.Update.addActionListener(this);
        APFinalData.View_Ok.addActionListener(this);
        APFinalData.View_Cancel.addActionListener(this);
        APFinalData.Element_Ok.addActionListener(this);
        APFinalData.Element_Cancel.addActionListener(this);
        APFinalData.antiaDisable.addActionListener(this);
        APFinalData.antiaEnable.addActionListener(this);
        APFinalData.anaglyphFull.addActionListener(this);
        APFinalData.anaglyphGray.addActionListener(this);
        APFinalData.anaglyphHalf.addActionListener(this);
        APFinalData.anaglyphNone.addActionListener(this);
        APFinalData.anaglyphOptim.addActionListener(this);
        APFinalData.anaglyphRedBlue.addActionListener(this);
        APFinalData.anaglyphRedGreen.addActionListener(this);
        APFinalData.senseSlider.addChangeListener(this);
        APFinalData.elementChooser.addActionListener(this);
        APFinalData.SprayVacuum.addActionListener(this);
        APFinalData.Wind.addActionListener(this);

        APFinalData.processSwitch.setBackground(Color.WHITE);
        APFinalData.processSwitch.addChangeListener(this);
        APFinalData.processSwitch.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);

        // APFinalData.mainFrame.setVisible(true);

        // Make the thread
        if (threedanim == null)
            threedanim = new Thread(new APMain(), "APThread");

        // Make it the best thread
        threedanim.setPriority(APSceneGraph.getJ3DThreadPriority());
        threedanim.setDaemon(false);
        threedanim.start();

        // Make it live
        isRunning = true;

        APFinalData.mainFrame.setVisible(true);
    }

    // UPDATE ARRAY METHOD

    /**
     * 
     * Updates the coordinate and color array of the box face array.
     * 
     */

    public final static void updateArray() {

        final APProcess process = APList.getCurrentProcess();

        cMain.setCoordRefFloat(process.coords);
        cMain.setColorRefByte(process.colors);
        cMain.setTexCoordRefFloat(0, process.texturecoords);

        windLoc.setCoordRefFloat(process.windcoords);
        windLoc.setColorRefByte(process.windcolors);
    }

    /**
     * 
     * Update the server processes.
     * 
     * @param srv
     *            the server thread
     * @throws IOException
     */

    public static void updateServer(APServerThread srv) throws IOException {
        srv.getOutputStream().writeByte(APFinalData.REQ_CLIENT_UPDATE);
        srv.getOutputStream().writeObject(APList.getCurrentProcess().status);
    }

    /**
     * 
     * Adds a new pressure coordinate to the world or augments/decreases the
     * power of an existing one.
     * 
     * @param leftclick
     *            whether or not the LMB is depressed
     */

    static final void addWind(boolean leftclick) {

        APProcess process = APList.getCurrentProcess();
        float persistence = 5;

        int empty = APArrayUtils.findEmptySpace(process.pressures);
        if (empty == -1)
            return;

        Point3d sel = new Point3d(selectionCoords[0], selectionCoords[1], selectionCoords[2]);
        Point3i realsel = new Point3i((int) Math.round(sel.x / APFinalData.BOXSIZE),
                (int) Math.round(sel.y / APFinalData.BOXSIZE), (int) Math.round(sel.z / APFinalData.BOXSIZE));

        if (realsel.y < 0)
            return;

        int multiplier = leftclick ? 1 : -1;

        Integer repl;
        if ((repl = process.reversepressuresort.get(realsel)) != null) {

            process.pressures[repl]
                    .setValue(process.pressures[repl].getValue() + multiplier * APFinalData.PRESSURE_ADD);
            process.pressures[repl].setPersistence((float) Math.sqrt(process.pressures[repl].getPersistence())); // converge
            // to 1

            if (Math.abs(process.pressures[repl].getValue()) > APFinalData.PRESSURE_CAP)
                process.pressures[repl].setValue(multiplier * APFinalData.PRESSURE_CAP);
            return;
        }

        process.pressures[empty] = new APPressurePoint(realsel.x, realsel.y, realsel.z,
                multiplier * APFinalData.PRESSURE_ADD, persistence);
        process.reversepressuresort.put(realsel, empty);

        System.arraycopy(new float[] { (float) (sel.x - APFinalData.BOXSIZE / 2), (float) sel.y, (float) sel.z,
                (float) (sel.x + APFinalData.BOXSIZE / 2), (float) sel.y, (float) sel.z, (float) sel.x,
                (float) (sel.y - APFinalData.BOXSIZE / 2), (float) sel.z, (float) sel.x,
                (float) (sel.y + APFinalData.BOXSIZE / 2), (float) sel.z, (float) sel.x, (float) sel.y,
                (float) (sel.z - APFinalData.BOXSIZE / 2), (float) sel.x, (float) sel.y,
                (float) (sel.z + APFinalData.BOXSIZE / 2) }, 0, process.windcoords, empty * 2 * 3 * 3, 18);
    }

    /**
     * 
     * Switch two blocks. Switches their positions.
     * 
     * @param process
     *            the current process
     * @param index1
     *            one block index
     * @param index2
     *            the other block index
     */

    static final void switchBlock(final APProcess process, final int index1, final int index2) {
        short tempstatus = process.status[index1];

        process.status[index1] = process.status[index2];
        process.status[index2] = tempstatus;
    }

    /**
     * 
     * Remove a block. Clears the status ID and resets the position back to the
     * negative y axis. Also clears the coordinate HashMaps.
     * 
     * @param process
     *            the current process
     * @param index
     *            the block index to be removed
     */

    static final void removeBlock(final APProcess process, final int index) {
        process.status[index] = 0;
        process.coords[index * 24 * 3 + 1] = -index - 1;
        process.realcoords[index * 3 + 1] = -index - 1;

        process.reversecoordsort.remove(process.coordsort.get(index));
        process.reversecoordsort.put(null, index);

        process.coordsort.put(index, null);

        blockNumber--;
    }

    /**
     * 
     * Add a block. Sets the status ID and sets the position to the argument.
     * Also sets the coordinate HashMaps. Assumes the index of the block is not
     * defined.
     * 
     * @param process
     *            the current process
     * @param index
     *            the block index to be added
     * @param loc
     *            the location of the new block
     * @param status
     *            the status (material) of the block
     */

    static final void addBlock(final APProcess process, final int index, Point3i loc, short status) {
        process.status[index] = status;

        process.coords[index * 24 * 3] = loc.x * APFinalData.BOXSIZE;
        process.coords[index * 24 * 3 + 1] = loc.y * APFinalData.BOXSIZE;
        process.coords[index * 24 * 3 + 2] = loc.z * APFinalData.BOXSIZE;

        process.realcoords[index * 3] = loc.x;
        process.realcoords[index * 3 + 1] = loc.y;
        process.realcoords[index * 3 + 2] = loc.z;

        process.texturecoords[index * 24 * 2] = 64 * (process.status[index] - 1);

        process.reversecoordsort.put(loc, index);
        process.coordsort.put(index, loc);
    }

    /**
     * 
     * Changes a block to a new block by changing the block ID.
     * 
     * @param process
     *            the current process
     * @param index
     *            the block index to be changed
     * @param material
     *            the new material
     */

    static final void changeBlock(final APProcess process, final int index, final APMaterial material) {
        process.status[index] = material.getID();
    }

    /**
     * 
     * Performs all functions to update the blocks through moving them around or
     * causing reactions. It takes into account the blocks' attributes, defined
     * in {@link APMaterial}.
     * 
     */

    public static void performEnvironmentFunctions() {

        // This is used a lot
        final APProcess process = APList.getCurrentProcess();

        if (process.isFrozen())
            return;

        // Update pressure points
        APArrayUtils.updatePressure(process);

        for (int i = 0; i < APFinalData.LIMIT; i++) {

            // if(process.status[i] == 0) continue;

            // First things first: get all the blocks around the first block
            if (!APArrayUtils.getFlagger().get(i))
                APArrayUtils.computeAroundIndices(process, i);

            // debug(indices.toString());

            // Make all velocities tend to fall toward the ground except buoyant
            // elements, which actually float upwards
            if (process.realcoords[i * 3 + 1] != 0)
                process.dVelocity[i].y -= 9.8f;

            // Make buoyant elements float up
            if (APMaterialsList.isBuoyant(process.status[i]))
                process.dVelocity[i].y += 15.8f;
            // Grounded Blocks
            else if (process.realcoords[i * 3 + 1] == 0 && process.dVelocity[i].y < 0)
                if (process.status[i] == APMaterial.BALL.getID() && Math.abs(process.dVelocity[i].y) > 14f)
                    process.dVelocity[i].y *= -0.5f;
                else
                    process.dVelocity[i].y = 0;

            // Remove the dead fire
            if (Math.signum(process.dVelocity[i].y) == 1 && process.status[i] == APMaterial.FIRE.getID()
                    && APFinalData.random.nextDouble() > 0.9)
                removeBlock(process, i);

            // Change steam
            if (Math.signum(process.dVelocity[i].y) == 1 && process.status[i] == APMaterial.STEAM.getID()
                    && APFinalData.random.nextDouble() > 0.8 && APFinalData.random.nextBoolean())
                if (APFinalData.random.nextBoolean())
                    removeBlock(process, i);
                else
                    changeBlock(process, i, APMaterial.WATER);

            if (!APArrayUtils.getFlagger().get(i))
                APArrayUtils.doReqCheckLoopActions(process, i);

            // Return the "Underground" blocks back home as null
            if (process.status[i] == 0) {

                process.coords[i * 24 * 3] = 0;
                process.coords[i * 24 * 3 + 2] = 0;

                process.realcoords[i * 3] = 0;
                process.realcoords[i * 3 + 2] = 0;

            }

            // if (process.realcoords[i * 3 + 1] != -1)
            APArrayUtils.setCoordBlocks(process.coords, i * 24 * 3);
            if (process.status[i] != 0)
                APArrayUtils.setTextureCoordBlocks(process.texturecoords, i * 24 * 2, process.status[i]);
            APArrayUtils.setColorBlocks(process.colors, APMaterialsList.getMaterialByID(process.status[i]),
                    i * 24 * 4);
        }

        APArrayUtils.clearFlagger();
    }

    // UPDATE POSITION METHOD

    /**
     * 
     * Renews the brush box locations. It takes the original brush location and
     * appends it to the player position.
     * 
     * @param process
     *            the current process
     * @param SR_VECTOR
     *            the original brush location
     */

    public static void renewBrushLocs(APProcess process, Vector3d SR_VECTOR) {

        int size = process.getBrushSize();
        for (int i = 0; i < APFinalData.spherecoords[size].length / 3; i++) {
            selectionCoords[i * 3] = (float) (APFinalData.spherecoords[size][i * 3] + SR_VECTOR.x);
            selectionCoords[i * 3 + 1] = (float) (APFinalData.spherecoords[size][i * 3 + 1] + SR_VECTOR.y);
            selectionCoords[i * 3 + 2] = (float) (APFinalData.spherecoords[size][i * 3 + 2] + SR_VECTOR.z);
        }

        selection.setCoordRefFloat(selectionCoords);

    }

    private static Vector3d genSelectorVector(APProcess process) {
        Vector3d SR_VECTOR = Transform3DUtils.rotateEulerVector3d(
                (Math.PI * 2 - process.getRotateY()) * APFinalData.DEGREE,
                (process.getRotateX() + Math.PI) * APFinalData.DEGREE, 2);
        SR_VECTOR.add(process.getCurPos());
        SR_VECTOR = Transform3DUtils.roundVector3d(SR_VECTOR, APFinalData.BOXSIZE);
        // SR_VECTOR.add(new Point3d(APFinalData.SHIFT));

        return SR_VECTOR;
    }

    /**
     * 
     * Updates the player position and the sky position.
     * 
     */

    public static void updatePosition() {

        APProcess process = APList.getCurrentProcess();

        // Also update the stars' position here
        skyRot += Math.PI / 180 / 200;
        skyTrans.rotX(skyRot);
        skyTG.setTransform(skyTrans);

        process.busy = true;

        TransformGroup platform = scene.getViewingPlatform().getViewPlatformTransform();

        Vector3d SR_VECTOR = genSelectorVector(process);

        if (!SR_VECTOR.equals(OLD_SR_VECTOR)) {
            renewBrushLocs(process, SR_VECTOR);
            OLD_SR_VECTOR = new Vector3d(SR_VECTOR);
        }

        // Viewpoint
        mainRoutineFinalTransform.setTranslation(new Vector3f());
        mainRoutineFinalTransform
                .setEuler(new Vector3d(APList.getCurrentProcess().getRotateY(), process.getRotateX(), 0));
        mainRoutineFinalTransform.setTranslation(Transform3DUtils.convertPoint3dToVector3d(process.getCurPos()));

        bounds.setCenter(process.getCurPos());

        platform.setTransform(mainRoutineFinalTransform);

        process.busy = false;

        return;
    }

    // Kill the task

    /**
     * 
     * Kills the task and ends the game.
     * 
     */

    public static void destroy() {

        isRunning = false;

        c3d.close();
        APList.removeAll();
        APFinalData.mainFrame.removeAll();
        c3d.stopRenderer();
        APFinalData.mainFrame.destroy();
        System.runFinalization();

        System.exit(0);

    }

    /**
     * 
     * Adds a block to the scene.
     * 
     * Calculates the positions added via brush box locations, then loops
     * through them, adding them one by one to the scene. It does not add blocks
     * if there is a block already there. If the limit is reached it stops.
     * 
     * Adding new blocks requires updating coordinates, integral coordinates,
     * and colors.
     * 
     */

    public static void countUp() {

        // This is used a lot
        final APProcess process = APList.getCurrentProcess();

        for (int i = 0; i < APFinalData.brushlocs.get(process.getBrushSize()).size(); i++) {
            // Temporary position
            final Point3d cT = new Point3d(selectionCoords[i * 72], selectionCoords[i * 72 + 1],
                    selectionCoords[i * 72 + 2]);

            // System.out.println(cT);

            // Get the next available index
            process.aCount = APArrayUtils.findEmptySpace(process.status);

            // Conditionals
            if (process.aCount != -1 && !(cT.y < 0)
                    && (process.aCount == 0 || !process.reversecoordsort
                            .containsKey(new Point3i((int) Math.round(cT.x / APFinalData.BOXSIZE),
                                    (int) Math.round(cT.y / APFinalData.BOXSIZE),
                                    (int) Math.round(cT.z / APFinalData.BOXSIZE))))) {

                final int index = process.aCount;

                addBlock(process, index, new Point3i((int) Math.round(cT.x / APFinalData.BOXSIZE),
                        (int) Math.round(cT.y / APFinalData.BOXSIZE), (int) Math.round(cT.z / APFinalData.BOXSIZE)),
                        process.getMaterial().getID());

                // Update coord and color
                APArrayUtils.setCoordBlocks(process.coords, index * 24 * 3);
                APArrayUtils.setColorBlocks(process.colors, process.getMaterial(), index * 24 * 4);

                blockNumber++;
            }
        }
    }

    /**
     * 
     * Removes a block from the scene.
     * 
     * Calculates the positions added via brush box locations, then loops
     * through them, removing blocks one by one from the scene. It does not
     * remove blocks if there is no block there.
     * 
     */

    public static void countDown() {

        // This is used a lot
        final APProcess process = APList.getCurrentProcess();

        for (int i = 0; i < APFinalData.brushlocs.get(process.getBrushSize()).size(); i++) {
            // Temporary position
            final Point3d cT = new Point3d(selectionCoords[i * 72], selectionCoords[i * 72 + 1],
                    selectionCoords[i * 72 + 2]);

            final Integer index;

            // Conditionals
            if (!(cT.y < 0) && (index = process.reversecoordsort.get(new Point3i(
                    (int) Math.round(cT.x / APFinalData.BOXSIZE), (int) Math.round(cT.y / APFinalData.BOXSIZE),
                    (int) Math.round(cT.z / APFinalData.BOXSIZE)))) != null) {

                removeBlock(process, index);

                // Update coord and color
                APArrayUtils.setCoordBlocks(process.coords, index * 24 * 3);
                APArrayUtils.setColorBlocks(process.colors, APMaterial.NULL, index * 24 * 4);
            }
        }
    }

    /**
     * 
     * Updates the player viewpoint based on the mouse position.
     * 
     * Sensitivity also affects mouse movement. The unit is in pi; Eulerian
     * rotation is used.
     * 
     * @param e
     */

    public void processMouseMovement(MouseEvent e) {

        if (!APList.getCurrentProcess().isPaused) {

            final double x = MouseInfo.getPointerInfo().getLocation().getX();
            final double y = MouseInfo.getPointerInfo().getLocation().getY();

            APProcess process = APList.getCurrentProcess();

            final double rotx = process.getRotateX();
            final double roty = process.getRotateY();

            process.setRotateX(rotx - (x - (APFinalData.mainFrame.getX() + c3d.getWidth() / 2)) / 50
                    * (float) (sensitivity / 500f));
            process.setRotateY(roty - (y - (APFinalData.mainFrame.getY() + c3d.getHeight() / 2
                    + APFinalData.processSwitch.getHeight() + APFinalData.top.getHeight())) / 50
                    * (float) (sensitivity / 500f));

            if (process.getRotateX() < 0) {
                process.setRotateX(Math.PI * 2);
            } else {
                if (process.getRotateX() > Math.PI * 2) {
                    process.setRotateX(rotx - Math.PI * 2);
                }
            }
            if (process.getRotateY() < -1.54) {
                process.setRotateY(-1.54);
            }
            if (process.getRotateY() > 1.54) {
                process.setRotateY(1.54);
            }

        }

    }

    /**
     * Updates game data in the event that a process is changed.
     */

    public static void updateProcess() {

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
            isLeftMouseDown = false;
        if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
            isRightMouseDown = false;
        e.consume();
    }

    @Override
    public void mousePressed(MouseEvent e) {
        if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
            isLeftMouseDown = true;
        if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
            isRightMouseDown = true;

        e.consume();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
            isLeftMouseDown = true;
        if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
            isRightMouseDown = true;

        if (!APList.getCurrentProcess().isRobot && !APList.getCurrentProcess().isPaused)
            processMouseMovement(e);

        e.consume();
    }

    @Override
    public void mouseMoved(MouseEvent e) {

        if (!APList.getCurrentProcess().isRobot && !APList.getCurrentProcess().isPaused)
            processMouseMovement(e);

        e.consume();
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        switch (keyCode) {
        case KeyEvent.VK_A:
            keys[0] = true;
            break;
        case KeyEvent.VK_D:
            keys[1] = true;
            break;
        case KeyEvent.VK_W:
            keys[2] = true;
            break;
        case KeyEvent.VK_S:
            keys[3] = true;
            break;
        case KeyEvent.VK_Q:
            keys[5] = true;
            break;
        case KeyEvent.VK_Z:
            keys[6] = true;
            break;
        case KeyEvent.VK_F1:
            keys[7] = true;
            break;
        case KeyEvent.VK_F2:
            keys[8] = true;
            break;
        case KeyEvent.VK_ESCAPE:
            keys[4] = true;
            break;
        case KeyEvent.VK_E:
            keys[9] = true;
            break;
        case KeyEvent.VK_C:
            keys[10] = true;
            break;
        case KeyEvent.VK_F3:
            keys[11] = true;
            break;
        case KeyEvent.VK_F4:
            keys[12] = true;
            break;
        case KeyEvent.VK_H:
            keys[13] = true;
            break;
        case KeyEvent.VK_SHIFT:
            keys[14] = true;
            break;
        default:
            APFinalData.debug(keyCode);
        }
        e.consume();
    }

    @Override
    public void keyReleased(KeyEvent e) {

        int keyCode = e.getKeyCode();

        switch (keyCode) {
        case KeyEvent.VK_A:
            keys[0] = false;
            break;
        case KeyEvent.VK_D:
            keys[1] = false;
            break;
        case KeyEvent.VK_W:
            keys[2] = false;
            break;
        case KeyEvent.VK_S:
            keys[3] = false;
            break;
        case KeyEvent.VK_Q:
            keys[5] = false;
            break;
        case KeyEvent.VK_Z:
            keys[6] = false;
            break;
        case KeyEvent.VK_ESCAPE:
            keys[4] = false;
            prevaction[0] = false;
            break;
        case KeyEvent.VK_F1:
            keys[7] = false;
            prevaction[1] = false;
            break;
        case KeyEvent.VK_F2:
            keys[8] = false;
            prevaction[2] = false;
            break;
        case KeyEvent.VK_E:
            keys[9] = false;
            prevaction[3] = false;
            break;
        case KeyEvent.VK_C:
            keys[10] = false;
            prevaction[4] = false;
            break;
        case KeyEvent.VK_F3:
            keys[11] = false;
            prevaction[5] = false;
            break;
        case KeyEvent.VK_F4:
            keys[12] = false;
            prevaction[6] = false;
            break;
        case KeyEvent.VK_H:
            keys[13] = false;
            prevaction[7] = false;
            break;
        case KeyEvent.VK_SHIFT:
            keys[14] = false;
            break;
        default:
            APFinalData.debug(keyCode);
        }

        e.consume();
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == APFinalData.Exit) {
            int pref = JOptionPane.showOptionDialog(new JFrame(),
                    "Do you want to exit now? All worlds will be lost.", "Exit Confirmation",
                    JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, APFinalData.prefoptions,
                    APFinalData.prefoptions[2]);
            if (pref == JOptionPane.YES_OPTION) {
                isRunning = false;
                destroy();
            }
        } else if (e.getSource() == APFinalData.New) {

            untitled++;
            APList.addProcess(new APProcess("Untitled" + untitled + ".aps"));
            APList.changeCurrentProcess(APList.getProcess(APList.getCurrentProcess().getProcessID()));

        } else if (e.getSource() == APFinalData.Online_Help)

            BareBonesBrowserLaunch.openURL("http://apsim.wikispaces.com/APSimulator+Tutorial+and+Help");

        else if (e.getSource() == APFinalData.View_Options) {

            APFinalData.senseBox.setText(((Integer) sensitivity).toString());
            APFinalData.viewop.setVisible(true);

        } else if (e.getSource() == APFinalData.View_Ok) {

            dumpViewVariables();
            APFinalData.viewop.setVisible(false);

        } else if (e.getSource() == APFinalData.About_Us) {

            JLabel aboutLabel = new JLabel("<html><div style=\"width:350px;\">APSimulator "
                    + APFinalData.getVersion() + "<br />" + "Credits<br /><br />" + "Founder: Jonathan Ni<br />"
                    + "Lead Programmer: Jonathan Ni<br />" + "Programmer: Sachin Pandey<br />"
                    + "Physics Adviser: Sachin Pandey<br />" + "PR: Jonathan Ni<br />"
                    + "Project Manager: Jonathan Ni<br />" + "Graphics: Alex Yu, Michael Zhang<br />"
                    + "Original 64x64 Textures: Jonathan Ni, Alex Yu <br /><br />" + "Libraries used: <br />"
                    + "<ul>" + "<li>Java 3D</li>" + "<li>Bare Bones Browser Launch</li>"
                    + "<li>JGoodies Core & LookAndFeel</li>" + "<li>Anaglyph Canvas3D</li>"
                    + "<li>Screenshot Utility: http://www.java.net/node/647363</li>" + "</ul><br />" + "<ul>"
                    + "<li><a href=\"http://apsim.dyndns.org/productiveproductions/licenses/apsimlicense.txt\">APSim License: http://apsim.dyndns.org/productiveproductions/licenses/apsimlicense.txt</a></li>"
                    + "<li><a href=\"http://apsim.dyndns.org/productiveproductions/licenses/APACHE-LICENSE-2.0.txt\">Apache License 2.0: http://apsim.dyndns.org/productiveproductions/licenses/APACHE-LICENSE-2.0.txt</a></li>"
                    + "<li><a href=\"http://apsim.dyndns.org/productiveproductions/licenses/LICENSE-Java3D-v1_5_1.txt\">Java 3D License: http://apsim.dyndns.org/productiveproductions/licenses/LICENSE-Java3D-v1_5_1.txt</a></li>"
                    + "<li><a href=\"http://apsim.dyndns.org/productiveproductions/licenses/JGoodies_LICENSE.txt\">JGoodies License: http://apsim.dyndns.org/productiveproductions/licenses/JGoodies_LICENSE.txt</a></li>"
                    + "</ul>" + "<p>&copy;" + Calendar.getInstance().get(Calendar.YEAR)
                    + " Productive Productions All Rights Reserved</p><br />"
                    + "<p>BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE (AS OF THE TIME OF THIS WRITING), "
                    + "THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. "
                    + " EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS "
                    + "AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, "
                    + "EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED "
                    + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. "
                    + "THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. "
                    + "SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, "
                    + "REPAIR OR CORRECTION.</p></div></html>");

            JFrame about = new JFrame();

            about.setIconImage(APFinalData.apIconImage);
            about.setTitle("About APSimulator");
            about.setSize(550, 400);

            JScrollPane scroll = new JScrollPane(aboutLabel);

            about.add(scroll);

            about.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            about.setVisible(true);

        } else if (e.getSource() == APFinalData.Save) {
            Matcher mat = Pattern.compile("Untitled[0-9]+\\.aps")
                    .matcher(APList.getCurrentProcess().save.getPath());
            if (mat.find())
                try {
                    saveAs();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            else {
                try {
                    APList.getCurrentProcess().save.write();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        } else if (e.getSource() == APFinalData.Save_As)
            try {
                saveAs();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        else if (e.getSource() == APFinalData.Element)
            APFinalData.elementop.setVisible(true);

        else if (e.getSource() == APFinalData.Element_Ok) {
            APList.getCurrentProcess().setMaterial(APMaterialsList.getMaterialByID((short) (tempelement + 1)));
            APFinalData.elementop.setVisible(false);

        } else if (e.getSource() == APFinalData.elementChooser)
            tempelement = (short) ((JComboBox) e.getSource()).getSelectedIndex();

        else if (e.getSource() == APFinalData.Element_Cancel)
            APFinalData.elementop.setVisible(false);

        else if (e.getSource() == APFinalData.View_Cancel)
            APFinalData.viewop.setVisible(false);

        else if (e.getSource() == APFinalData.antiaDisable)
            antiaOn = false;

        else if (e.getSource() == APFinalData.antiaEnable)
            antiaOn = true;

        else if (e.getSource() == APFinalData.Open)
            try {
                open();
            } catch (IOException io) {
                io.printStackTrace();
            }
        else if (e.getSource() == APFinalData.Update)
            try {
                checkUpdate();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        else if (e.getSource() == APFinalData.Join_Server)
            try {
                client.init();
            } catch (Exception e1) {
                e1.printStackTrace();
            }

        else if (e.getSource() == APFinalData.Create_Server)
            try {
                server.init();
            } catch (Exception e1) {
                e1.printStackTrace();
            }

        else if (e.getSource() == APFinalData.anaglyphFull)
            amode = AnaglyphMode.COLOR_ANAGLYPHS;

        else if (e.getSource() == APFinalData.anaglyphGray)
            amode = AnaglyphMode.GRAY_ANAGLYPHS;

        else if (e.getSource() == APFinalData.anaglyphHalf)
            amode = AnaglyphMode.HALFCOLOR_ANAGLYPHS;

        else if (e.getSource() == APFinalData.anaglyphNone)
            amode = null;

        else if (e.getSource() == APFinalData.anaglyphOptim)
            amode = AnaglyphMode.OPTIMIZED_ANAGLYPHS;

        else if (e.getSource() == APFinalData.anaglyphRedBlue)
            amode = AnaglyphMode.REDBLUE_ANAGLYPHS;

        else if (e.getSource() == APFinalData.anaglyphRedGreen)
            amode = AnaglyphMode.REDGREEN_ANAGLYPHS;

        else if (e.getSource() == APFinalData.SprayVacuum) {
            setTool(APFinalData.TOOL_SPRAYVAC);
            APFinalData.SprayVacuum.setSelected(true);
            APFinalData.Wind.setSelected(false);
        }

        else if (e.getSource() == APFinalData.Wind) {
            setTool(APFinalData.TOOL_WIND);
            APFinalData.SprayVacuum.setSelected(false);
            APFinalData.Wind.setSelected(true);
        }
    }

    private void checkUpdate() throws IOException {

        String ver = null;

        try {
            ver = new BufferedReader(new InputStreamReader(
                    new URL("http://apsim.dyndns.org/productiveproductions/apsim/version.txt").openStream()))
                            .readLine();

        } catch (MalformedURLException e) {
            e.printStackTrace();
            return;

        } catch (IOException e) {
            e.printStackTrace();
            return;

        }

        System.out.println("Client version is " + APFinalData.getVersion() + " while server version is " + ver);

        if (!APFinalData.getVersion().equals(ver)) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Runtime.getRuntime().exec("java -jar APDownloader.jar");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            destroy();
        } else
            JOptionPane.showMessageDialog(new JFrame(), "No updates!");

    }

    /**
     * 
     * Opens an APSim file and reads it into the game data.
     * 
     * @throws IOException
     */

    public void open() throws IOException {
        JFileChooser dialog = new JFileChooser(".");
        FileFilter aps = new APFileFilter();
        dialog.addChoosableFileFilter(aps);

        int openval = dialog.showDialog(new JFrame(), "Open");

        if (openval == JFileChooser.APPROVE_OPTION) {

            if (dialog.getSelectedFile().exists())
                // Be careful
                synchronized (APList) {
                    APList.addProcess(new APProcess(dialog.getSelectedFile().getAbsolutePath()));
                    APList.getCurrentProcess().save.read();
                }
            else
                JOptionPane.showMessageDialog(new JFrame(), "Error reading file!");
        }
    }

    /**
     * 
     * Exports a 3D model of the scene.
     * 
     * @throws FileNotFoundException
     * @throws IOException
     */

    public static void exportModel() throws FileNotFoundException, IOException {
        JFileChooser choose = new JFileChooser(".");
        APOBJFilter filter = new APOBJFilter();
        choose.setFileFilter(filter);
        File file;

        int saveVal = choose.showSaveDialog(new JFrame());

        if (saveVal == JFileChooser.APPROVE_OPTION) {

            if (!choose.getSelectedFile().getName().endsWith(".obj"))
                file = new File(choose.getSelectedFile().getAbsolutePath() + ".obj");
            else
                file = choose.getSelectedFile();

            APOBJWriter writer = new APOBJWriter(file, APList.getCurrentProcess());
            writer.write();
            writer.close();
        }
    }

    /**
     * 
     * Records a series of 3D models; these can be rendered as a sequence when
     * converted to renderable format.
     * 
     * It uses a timer to take a snapshot of the scene every while.
     * 
     */

    public static void startRecording() {
        JOptionPane.showMessageDialog(new JFrame(),
                "WARNING: This function may spew as many as 10000+ files in \n"
                        + "very little time. (BETA FUNCTION) If you want to use this function \n"
                        + ", please do not use numbers at the end of the file name.");

        JFileChooser choose = new JFileChooser(".");
        APOBJFilter filter = new APOBJFilter();
        choose.setFileFilter(filter);
        File file;

        int saveVal = choose.showSaveDialog(new JFrame());

        if (saveVal == JFileChooser.APPROVE_OPTION) {

            // Do not include .obj file extension (the video recorder will take
            // care of that)
            if (choose.getSelectedFile().getName().endsWith(".obj"))
                file = new File(choose.getSelectedFile().getAbsolutePath().substring(0,
                        choose.getSelectedFile().getAbsolutePath().length() - 4));
            else
                file = choose.getSelectedFile();

            APProcess process = APList.getCurrentProcess();

            process.isRecording = true;
            process.setRecord(new APVideoRecorder(file));
            process.getRecord().start();

        }
    }

    /**
     * 
     * Stops recording the scene.
     * 
     * @see #startRecording()
     * 
     */

    public static void stopRecording() {
        APProcess process = APList.getCurrentProcess();

        process.isRecording = false;
        process.getRecord().stop();
    }

    /**
     * 
     * Saves the world (haha) under a different name.
     * 
     * @throws IOException
     */

    public void saveAs() throws IOException {

        JFileChooser dialog = new JFileChooser(".");
        FileFilter aps = new APFileFilter();
        dialog.addChoosableFileFilter(aps);

        int saveVal = dialog.showSaveDialog(new JFrame());

        if (saveVal == JFileChooser.APPROVE_OPTION) {
            if (!dialog.getSelectedFile().getName().endsWith(".apsg"))
                APList.getCurrentProcess().save = new APWorld(dialog.getSelectedFile().getAbsolutePath() + ".apsg");
            else
                APList.getCurrentProcess().save = new APWorld(dialog.getSelectedFile().getAbsolutePath());

            // Be careful

            APList.getCurrentProcess().save.write();
        }
    }

    /**
     * 
     * Dumps the options set in the view options dialog into the game.
     * 
     */

    public void dumpViewVariables() {

        if (antiaOn)
            c3d.getView().setSceneAntialiasingEnable(true);
        else
            c3d.getView().setSceneAntialiasingEnable(false);

        try {
            if (Integer.parseInt(APFinalData.senseBox.getText()) < 100
                    && Integer.parseInt(APFinalData.senseBox.getText()) >= 0) {
                sensitivity = Integer.parseInt(APFinalData.senseBox.getText());
            } else {
                APFinalData.senseBox.setText("90");
                sensitivity = 90;
            }
        } catch (NumberFormatException ignore) {
            sensitivity = 90;
            APFinalData.senseBox.setText("90");
        }

        if (amode != null) {
            c3d.setStereoMode(StereoMode.ANAGLYPH);
            c3d.setAnaglyphMode(amode);
        } else
            c3d.setStereoMode(StereoMode.OFF);
    }

    @Override
    public void stateChanged(ChangeEvent ce) {
        if (ce.getSource() == APFinalData.senseSlider) {
            sensitivity = (int) ((JSlider) ce.getSource()).getValue();
            APFinalData.senseBox.setText(((Integer) sensitivity).toString());

        } else if (ce.getSource() == APFinalData.processSwitch)
            APList.changeCurrentProcessByIndex(((JTabbedPane) ce.getSource()).getSelectedIndex());
    }

    /**
     * 
     * Draw the pause RESUME [ESC] text on top of the canvas directly.
     * 
     * @param x
     *            the x coordinate of the text
     * @param y
     *            the y coordinate of the text
     */

    public static void drawPause(final int x, final int y) {
        final Graphics g = c3d.getGraphics();

        if (g != null) {
            g.setColor(Color.green);
            g.drawString("RESUME [ESC]", x, y);
        }
    }

    // unused listener functions

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }
}