changeset 18:7f74e064dad5

refactored code
author zzzzrrr <mason.green@gmail.com>
date Wed, 25 Mar 2009 11:28:25 -0400
parents 82efafc87d54
children 08ddf9e71b88
files ai.d ai/ai.d ai/human.d ai/steer.d ai/utilities.d boundaryListener.d build-dmd-win.bat contactListener.d example.d game.d main.d melee.d melee/boundaryListener.d melee/contactListener.d melee/melee.d models.d openmelee.d openmelee.geany orz.d planet.d render.d render/render.d ship.d ships/models.d ships/orz.d ships/planet.d ships/ship.d ships/urQuan.d steer.d test/example.d test/testScript.md testScript.md urQuan.d utilities.d
diffstat 34 files changed, 2527 insertions(+), 2548 deletions(-) [+]
line wrap: on
line diff
--- a/ai.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.ai;
-
-import tango.io.Stdout : Stdout;
-import tango.math.Math : atan2, abs, PI;
-
-import blaze.common.bzMath: bzVec2, bzClamp;
-import openmelee.steer : Steer;
-import openmelee.ship : Ship;
-
-class AI {
-
-
-	Steer steer;
-	Ship ship;
-	float maxPredictionTime = 0.1f;
-	
-	this(Ship ship) {
-		this.ship = ship;
-		steer = new Steer(ship);
-	}
-	
-	void move(Ship enemy) {
-	    
-        // Elementary steering AI 
-        
-	    steer.update();
-	    bzVec2 st;
-	    st = steer.steerForPursuit(enemy.state, maxPredictionTime);
-	    ship.state.target = st;
-        st = ship.rBody.localPoint(st);
-        float x = st.x;
-		float y = st.y;
-        st = -bzVec2(y,-x);
-        float angle = atan2(st.x, st.y);
-		
-		if(abs(angle) > 0.05) {
-            if(!ship.state.turn) {
-                if(st.x >= 0) {
-                    ship.turnRight();
-                    ship.state.turn = true;
-                } else {
-                    ship.state.turn = true;
-                    ship.turnLeft();
-                }
-           }
-		} else {
-			ship.rBody.angularVelocity = 0.0f;
-			ship.state.turn = false;
-		}
-		
-		float range = (ship.state.position - enemy.state.position).length; 
-		if(range > 2 && abs(angle) < PI/4) {
-			ship.thrust();
-		}
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ai/ai.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ai.ai;
+
+import tango.io.Stdout : Stdout;
+import tango.math.Math : atan2, abs, PI;
+
+import blaze.common.bzMath: bzVec2, bzClamp;
+
+import openmelee.ai.steer : Steer;
+import openmelee.ships.ship : Ship;
+
+class AI {
+
+
+	Steer steer;
+	Ship ship;
+	float maxPredictionTime = 0.1f;
+	
+	this(Ship ship) {
+		this.ship = ship;
+		steer = new Steer(ship);
+	}
+	
+	void move(Ship enemy) {
+	    
+        // Elementary steering AI 
+        
+	    steer.update();
+	    bzVec2 st;
+	    st = steer.steerForPursuit(enemy.state, maxPredictionTime);
+	    ship.state.target = st;
+        st = ship.rBody.localPoint(st);
+        float x = st.x;
+		float y = st.y;
+        st = -bzVec2(y,-x);
+        float angle = atan2(st.x, st.y);
+		
+		if(abs(angle) > 0.05) {
+            if(!ship.state.turn) {
+                if(st.x >= 0) {
+                    ship.turnRight();
+                    ship.state.turn = true;
+                } else {
+                    ship.state.turn = true;
+                    ship.turnLeft();
+                }
+           }
+		} else {
+			ship.rBody.angularVelocity = 0.0f;
+			ship.state.turn = false;
+		}
+		
+		float range = (ship.state.position - enemy.state.position).length; 
+		if(range > 2 && abs(angle) < PI/4) {
+			ship.thrust();
+		}
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ai/human.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ai.human;
+
+import xf.hybrid.Event;
+import xf.input.KeySym;
+
+import openmelee.ships.ship;
+
+class Human
+{
+
+    Ship ship;
+    
+	this(Ship ship) {
+        this.ship = ship;
+	}
+
+	EventHandling onClick(MouseButtonEvent e) {
+        return EventHandling.Stop;
+	}
+
+	EventHandling onKey(KeyboardEvent e) {
+        // Key pressed
+		if (e.down) {
+			switch (e.keySym) {
+            case KeySym.space:
+                drawAABBs = !drawAABBs;
+                break;
+			case KeySym.Escape:
+                quit = true;
+                break;
+            case KeySym.Up:            
+                thrust = true;
+                break;
+            case KeySym.Left:            
+                ship.turnLeft();
+                break;
+            case KeySym.Right:             
+                ship.turnRight();
+                break;
+            case KeySym.Down:             
+                break;
+			default:
+				break;
+			}
+        // Key released
+		} else {
+		    if(e.keySym == KeySym.Up) {
+		         thrust = false;
+		    } else if (e.keySym == KeySym.Left || e.keySym == KeySym.Right) {
+                ship.rBody.angularVelocity = 0.0f;
+            }
+		}
+		return EventHandling.Stop;
+	}
+
+	// Mouse move
+	EventHandling onMove(MouseMoveEvent e) {
+		return EventHandling.Stop;
+	}
+
+	EventHandling onDT(TimeUpdateEvent e){
+		return EventHandling.Continue;
+	}
+
+	EventHandling onMouseEnter(MouseEnterEvent e) {
+		return EventHandling.Continue;
+	}
+
+	EventHandling onMouseLeave(MouseLeaveEvent e) {
+		return EventHandling.Continue;
+	}
+
+	bool quit;
+    bool thrust;
+    bool drawAABBs;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ai/steer.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * Based on OpenSteer, Copyright (c) 2002-2003, Sony Computer Entertainment America
+ * Original author: Craig Reynolds
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ai.steer;
+
+import blaze.common.bzMath: bzDot, bzClamp, bzVec2;
+import blaze.dynamics.bzBody: bzBody;
+
+import openmelee.ships.ship : Ship, State;
+import openmelee.ai.utilities;
+
+class Steer 
+{
+    // Constructor: initializes state
+    this (Ship ship)
+    {
+        m_ship = ship;
+    }
+    
+	struct PathIntersection
+    {
+        bool intersect;
+        float distance;
+        bzVec2 surfacePoint;
+        bzVec2 surfaceNormal;
+        bzBody obstacle;
+    }
+
+    // reset state
+    void reset () {
+        // initial state of wander behavior
+        m_wanderSide = 0;
+        m_wanderUp = 0;
+    }
+    
+    void update() {
+        m_position = m_ship.state.position;
+        m_velocity = m_ship.state.velocity;
+        m_speed = m_ship.state.speed;
+        m_maxForce = m_ship.state.maxForce;
+        m_forward = m_ship.state.forward;
+    }
+
+    // -------------------------------------------------- steering behaviors
+
+    bzVec2 steerForWander (float dt) {
+        // random walk m_wanderSide and m_wanderUp between -1 and +1
+        float speed = 12 * dt; // maybe this (12) should be an argument?
+        m_wanderSide = scalarRandomWalk (m_wanderSide, speed, -1, +1);
+        m_wanderUp   = scalarRandomWalk (m_wanderUp,   speed, -1, +1);
+
+        // return a pure lateral steering vector: (+/-Side) + (+/-Up)
+        return (m_side * m_wanderSide) + (m_up * m_wanderUp);
+    }
+
+    // Seek behavior
+    bzVec2 steerForSeek (bzVec2 target) {
+        bzVec2 desiredVelocity = target - m_position;
+        return desiredVelocity - m_velocity;
+    }
+
+    // Flee behavior
+    bzVec2 steerForFlee (bzVec2 target) {
+        bzVec2 desiredVelocity = m_position - target;
+        return desiredVelocity - m_velocity;
+    }
+
+    /*
+    // xxx proposed, experimental new seek/flee [cwr 9-16-02]
+    bzVec2 xxxsteerForFlee (bzVec2 target) {
+        bzVec2 offset = m_position - target;
+        bzVec2 desiredVelocity = bzClamp(offset.truncateLength (maxSpeed ());
+        return desiredVelocity - m_velocity;
+    }
+
+    bzVec2 xxxsteerForSeek (bzVec2 target) {
+        //  bzVec2 offset = target - position;
+        bzVec2 offset = target - m_position;
+        bzVec2 desiredVelocity = offset.truncateLength (maxSpeed ()); //xxxnew
+        return desiredVelocity - m_velocity;
+    }
+    */
+
+	/*
+    // ------------------------------------------------------------------------
+    // Obstacle Avoidance behavior
+    //
+    // Returns a steering force to avoid a given obstacle.  The purely
+    // lateral steering force will turn our vehicle towards a silhouette edge
+    // of the obstacle.  Avoidance is required when (1) the obstacle
+    // intersects the vehicle's current path, (2) it is in front of the
+    // vehicle, and (3) is within minTimeToCollision seconds of travel at the
+    // vehicle's current velocity.  Returns a zero vector value (bzVec2::zero)
+    // when no avoidance is required.
+    bzVec2 steerToAvoidObstacle (float minTimeToCollision, Obstacle obstacle) {
+
+        bzVec2 avoidance = obstacle.steerToAvoid (this, minTimeToCollision);
+        return avoidance;
+    }
+
+    // avoids all obstacles in an ObstacleGroup
+    */
+    
+    bzVec2 steerToAvoidObstacles (float minTimeToCollision, bzBody obstacles) {
+
+        bzVec2 avoidance;
+        PathIntersection nearest, next;
+        float minDistanceToCollision = minTimeToCollision * m_speed;
+
+        next.intersect = false;
+        nearest.intersect = false;
+
+        // test all obstacles for intersection with my forward axis,
+        // select the one whose point of intersection is nearest
+        for (bzBody o; o; o = o.next) {
+            // This code which presumes the obstacle is spherical
+            //findNextIntersectionWithSphere (o, next);
+
+            if (!nearest.intersect || (next.intersect && next.distance < nearest.distance)) {
+					nearest = next;
+            }
+        }
+
+        // when a nearest intersection was found
+        if (nearest.intersect && (nearest.distance < minDistanceToCollision)) {
+            // compute avoidance steering force: take offset from obstacle to me,
+            // take the component of that which is lateral (perpendicular to my
+            // forward direction), set length to maxForce, add a bit of forward
+            // component (in capture the flag, we never want to slow down)
+            bzVec2 offset = m_position - nearest.obstacle.position;
+            //avoidance = Vector3Helpers.PerpendicularComponent(offset, this.Forward);
+            avoidance.normalize;
+            avoidance *= m_maxForce;
+            avoidance += m_forward * m_maxForce * 0.75f;
+        }
+
+        return avoidance;
+    }
+
+    /*
+    // ------------------------------------------------------------------------
+    // Unaligned collision avoidance behavior: avoid colliding with other
+    // nearby vehicles moving in unconstrained directions.  Determine which
+    // (if any) other other vehicle we would collide with first, then steers
+    // to avoid the site of that potential collision.  Returns a steering
+    // force vector, which is zero length if there is no impending collision.
+
+    bzVec2 steerToAvoidNeighbors (float minTimeToCollision, AVGroup others) {
+
+        // first priority is to prevent immediate interpenetration
+        bzVec2 separation = steerToAvoidCloseNeighbors (0, others);
+        if (separation != bzVec2::zero) return separation;
+
+        // otherwise, go on to consider potential future collisions
+        float steer = 0;
+        Ship threat;
+
+        // Time (in seconds) until the most immediate collision threat found
+        // so far.  Initial value is a threshold: don't look more than this
+        // many frames into the future.
+        float minTime = minTimeToCollision;
+
+        // xxx solely for annotation
+        bzVec2 xxxThreatPositionAtNearestApproach;
+        bzVec2 xxxOurPositionAtNearestApproach;
+
+        // for each of the other vehicles, determine which (if any)
+        // pose the most immediate threat of collision.
+        for (AVIterator i = others.begin(); i != others.end(); i++)
+        {
+            Ship other = i;
+            if (other !is this)
+            {
+                // avoid when future positions are this close (or less)
+                float collisionDangerThreshold = radius * 2;
+
+                // predicted time until nearest approach of "this" and "other"
+                float time = predictNearestApproachTime (other);
+
+                // If the time is in the future, sooner than any other
+                // threatened collision...
+                if ((time >= 0)  (time < minTime))
+                {
+                    // if the two will be close enough to collide,
+                    // make a note of it
+                    if (computeNearestApproachPositions (other, time)
+                        < collisionDangerThreshold)
+                    {
+                        minTime = time;
+                        threat = other;
+                        xxxThreatPositionAtNearestApproach
+                            = hisPositionAtNearestApproach;
+                        xxxOurPositionAtNearestApproach
+                            = ourPositionAtNearestApproach;
+                    }
+                }
+            }
+        }
+
+        // if a potential collision was found, compute steering to avoid
+        if (threat)
+        {
+            // parallel: +1, perpendicular: 0, anti-parallel: -1
+            float parallelness = m_forward.dot(threat.forward);
+            float angle = 0.707f;
+
+            if (parallelness < -angle)
+            {
+                // anti-parallel "head on" paths:
+                // steer away from future threat position
+                bzVec2 offset = xxxThreatPositionAtNearestApproach - m_position;
+                float sideDot = offset.dot(m_side());
+                steer = (sideDot > 0) ? -1.0f : 1.0f;
+            }
+            else
+            {
+                if (parallelness > angle)
+                {
+                    // parallel paths: steer away from threat
+                    bzVec2 offset = threat.position - m_position;
+                    float sideDot = bzDot(offset, m_side);
+                    steer = (sideDot > 0) ? -1.0f : 1.0f;
+                }
+                else
+                {
+                    // perpendicular paths: steer behind threat
+                    // (only the slower of the two does this)
+                    if (threat.speed() <= speed)
+                    {
+                        float sideDot = bzDot(m_side, threat.velocity);
+                        steer = (sideDot > 0) ? -1.0f : 1.0f;
+                    }
+                }
+            }
+        }
+
+        return m_side() * steer;
+    }
+	*/
+	
+    // Given two vehicles, based on their current positions and velocities,
+    // determine the time until nearest approach
+    float predictNearestApproachTime (State other) {
+
+        // imagine we are at the origin with no velocity,
+        // compute the relative velocity of the other vehicle
+        bzVec2 myVelocity = m_velocity;
+        bzVec2 otherVelocity = other.velocity;
+        bzVec2 relVelocity = otherVelocity - myVelocity;
+        float relSpeed = relVelocity.length;
+
+        // for parallel paths, the vehicles will always be at the same distance,
+        // so return 0 (aka "now") since "there is no time like the present"
+        if (relSpeed == 0) return 0;
+
+        // Now consider the path of the other vehicle in this relative
+        // space, a line defined by the relative position and velocity.
+        // The distance from the origin (our vehicle) to that line is
+        // the nearest approach.
+
+        // Take the unit tangent along the other vehicle's path
+        bzVec2 relTangent = relVelocity / relSpeed;
+
+        // find distance from its path to origin (compute offset from
+        // other to us, find length of projection onto path)
+        bzVec2 relPosition = m_position - other.position;
+        float projection = bzDot(relTangent, relPosition);
+
+        return projection / relSpeed;
+    }
+
+    // Given the time until nearest approach (predictNearestApproachTime)
+    // determine position of each vehicle at that time, and the distance
+    // between them
+    float computeNearestApproachPositions (State other, float time) {
+
+        bzVec2 myTravel =  m_forward *  m_speed * time;
+        bzVec2 otherTravel = other.forward * other.speed * time;
+
+        bzVec2 myFinal =  m_position + myTravel;
+        bzVec2 otherFinal = other.position + otherTravel;
+
+        return (myFinal - otherFinal).length;
+    }
+
+    // ------------------------------------------------------------------------
+    // pursuit of another vehicle ( version with ceiling on prediction time)
+
+    bzVec2 steerForPursuit (State quarry) {
+        return steerForPursuit (quarry, float.max);
+    }
+
+    bzVec2 steerForPursuit (State quarry, float maxPredictionTime) {
+
+        // offset from this to quarry, that distance, unit vector toward quarry
+        bzVec2 offset = quarry.position - m_position;
+        float distance = offset.length;
+        bzVec2 unitOffset = offset / distance;
+
+        // how parallel are the paths of "this" and the quarry
+        // (1 means parallel, 0 is pependicular, -1 is anti-parallel)
+        float parallelness = bzDot(m_forward , quarry.forward);
+
+        // how "forward" is the direction to the quarry
+        // (1 means dead ahead, 0 is directly to the side, -1 is straight back)
+        float forwardness = bzDot(m_forward , unitOffset);
+
+        float directTravelTime = distance / m_speed;
+        int f = intervalComparison (forwardness,  -0.707f, 0.707f);
+        int p = intervalComparison (parallelness, -0.707f, 0.707f);
+
+        float timeFactor = 0; // to be filled in below
+
+        // Break the pursuit into nine cases, the cross product of the
+        // quarry being [ahead, aside, or behind] us and heading
+        // [parallel, perpendicular, or anti-parallel] to us.
+        switch (f)
+        {
+        case +1:
+            switch (p)
+            {
+            case +1:          // ahead, parallel
+                timeFactor = 4;
+                break;
+            case 0:           // ahead, perpendicular
+                timeFactor = 1.8f;
+                break;
+            case -1:          // ahead, anti-parallel
+                timeFactor = 0.85f;
+                break;
+            }
+            break;
+        case 0:
+            switch (p)
+            {
+            case +1:          // aside, parallel
+                timeFactor = 1;
+                break;
+            case 0:           // aside, perpendicular
+                timeFactor = 0.8f;
+                break;
+            case -1:          // aside, anti-parallel
+                timeFactor = 4;
+                break;
+            }
+            break;
+        case -1:
+            switch (p)
+            {
+            case +1:          // behind, parallel
+                timeFactor = 0.5f;
+                break;
+            case 0:           // behind, perpendicular
+                timeFactor = 2;
+                break;
+            case -1:          // behind, anti-parallel
+                timeFactor = 2;
+                break;
+            }
+            break;
+        }
+
+        // estimated time until intercept of quarry
+        float et = directTravelTime * timeFactor;
+
+        // xxx experiment, if kept, this limit should be an argument
+        float etl = (et > maxPredictionTime) ? maxPredictionTime : et;
+
+        // estimated position of quarry at intercept
+        bzVec2 target = quarry.predictFuturePosition(etl);
+
+        return target; //steerForSeek (target);
+    }
+
+    // ------------------------------------------------------------------------
+    // evasion of another vehicle
+    bzVec2 steerForEvasion (State menace,  float maxPredictionTime)  {
+
+        // offset from this to menace, that distance, unit vector toward menace
+        bzVec2 offset = menace.position - m_position;
+        float distance = offset.length;
+
+        float roughTime = distance / menace.speed;
+        float predictionTime = ((roughTime > maxPredictionTime) ? maxPredictionTime : roughTime);
+        bzVec2 target = menace.predictFuturePosition (predictionTime);
+
+        return steerForFlee (target);
+    }
+
+
+    // ------------------------------------------------------------------------
+    // tries to maintain a given speed, returns a maxForce-clipped steering
+    // force along the forward/backward axis
+    bzVec2 steerForTargetSpeed (float targetSpeed) {
+        float mf = m_maxForce;
+        float speedError = targetSpeed - m_speed;
+        return m_forward * bzClamp(speedError, -mf, +mf);
+    }
+
+
+    // ----------------------------------------------------------- utilities
+    bool isAhead (bzVec2 target) {return isAhead (target, 0.707f);};
+    bool isAside (bzVec2 target) {return isAside (target, 0.707f);};
+    bool isBehind (bzVec2 target) {return isBehind (target, -0.707f);};
+
+    bool isAhead (bzVec2 target, float cosThreshold)
+    {
+        bzVec2 targetDirection = target - m_position;
+        targetDirection.normalize();
+        return bzDot(m_forward, targetDirection) > cosThreshold;
+    }
+
+    bool isAside (bzVec2 target, float cosThreshold)
+    {
+        bzVec2 targetDirection = target - m_position;
+        targetDirection.normalize();
+        float dp = bzDot(m_forward, targetDirection);
+        return (dp < cosThreshold) && (dp > -cosThreshold);
+    }
+
+    bool isBehind (bzVec2 target, float cosThreshold)
+    {
+        bzVec2 targetDirection = target - m_position;
+        targetDirection.normalize();
+        return bzDot(m_forward, targetDirection) < cosThreshold;
+    }
+    
+    private:
+    
+    Ship m_ship;
+    
+    bzVec2 m_position;
+    bzVec2 m_velocity;
+    bzVec2 m_up;
+    bzVec2 m_side;
+    bzVec2 m_forward;
+	
+	float m_speed = 0;
+	float m_maxForce = 0;
+    
+    // Wander behavior
+    float m_wanderSide;
+    float m_wanderUp;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ai/utilities.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ai.utilities;
+ 
+import tango.math.random.Kiss : Kiss;
+
+float scalarRandomWalk(float initial, float walkspeed, float min, float max) {
+	
+	float next = initial + (((randomRange(0, 1) * 2) - 1) * walkspeed);
+	if (next < min) return min;
+	if (next > max) return max;
+	return next;
+}
+
+// ----------------------------------------------------------------------------
+// classify a value relative to the interval between two bounds:
+//     returns -1 when below the lower bound
+//     returns  0 when between the bounds (inside the interval)
+//     returns +1 when above the upper bound
+int intervalComparison(float x, float lowerBound, float upperBound)
+{
+    if (x < lowerBound) return -1;
+    if (x > upperBound) return +1;
+    return 0;
+}
+
+T randomRange(T = int) (T min, T max)
+{
+    return min + Kiss.instance.natural() % (max + 1 - min);
+}
--- a/boundaryListener.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * Based on Box2D by Erin Catto, http://www.box2d.org
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.boundaryListener;
-
-import openmelee.melee;
-
-// bzWorld boundary callback
-class BoundaryListener : bzBoundaryListener
-{
-	Melee melee;
-
-	this(Melee melee)
-	{
-		this.melee = melee;
-	}
-
-	void violation(bzBody rBody)
-	{
-        melee.boundaryViolated(rBody);
-	}
-}
--- a/build-dmd-win.bat	Tue Mar 24 16:47:42 2009 -0400
+++ b/build-dmd-win.bat	Wed Mar 25 11:28:25 2009 -0400
@@ -1,4 +1,3 @@
-jake -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O main.d
-::rebuild -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O main.d
-::xfBuild main.d -oMain.exe -- -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O 
+jake -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O openmelee.d
+::rebuild -I.. -I../blaze -I../xf/ext zlib.lib -inline -release -O openmelee.d
 
--- a/contactListener.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * Based on Box2D by Erin Catto, http://www.box2d.org
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.contactListener;
-
-import openmelee.melee;
-
-// bzWorld contact callback
-class ContactListener : bzContactListener
-{
-
-	Melee melee;
-
-	this(Melee melee) {
-		this.melee = melee;
-	}
-
-	void add(bzContactPoint point)
-	{
-		if (melee.pointCount == k_maxContactPoints) {
-			return;
-		}
-
-		ContactPoint *cp = &melee.points[melee.pointCount];
-		cp.shape1 = point.shape1;
-		cp.shape2 = point.shape2;
-		cp.position = point.position;
-		cp.normal = point.normal;
-		cp.id = point.id;
-		cp.state = ContactState.e_contactAdded;
-
-		++melee.pointCount;
-	}
-
-	void persist(bzContactPoint point)
-	{
-		if (melee.pointCount == k_maxContactPoints) {
-			return;
-		}
-
-		ContactPoint *cp = &melee.points[melee.pointCount];
-		cp.shape1 = point.shape1;
-		cp.shape2 = point.shape2;
-		cp.position = point.position;
-		cp.normal = point.normal;
-		cp.id = point.id;
-		cp.state = ContactState.e_contactPersisted;
-
-		++melee.pointCount;
-	}
-
-	void remove(bzContactPoint point)
-	{
-		if (melee.pointCount == k_maxContactPoints) {
-			return;
-		}
-
-		ContactPoint *cp = &melee.points[melee.pointCount];
-		cp.shape1 = point.shape1;
-		cp.shape2 = point.shape2;
-		cp.position = point.position;
-		cp.normal = point.normal;
-		cp.id = point.id;
-		cp.state = ContactState.e_contactRemoved;
-
-		++melee.pointCount;
-	}
-
-	void result(bzContactResult point) {}
-
-}
--- a/example.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-// jake -I../minid -I.. -version=old example.d
-module example;
-
-import xf.xpose2.Expose;
-import xf.xpose2.MiniD;
-import tango.text.convert.Format;
-
-struct vec3 {
-	float x, y, z;
-	
-	char[] toString() {
-		return Format("x={} y={} z={}", x, y, z);
-	}
-	
-	mixin(xpose2(`
-		.*
-		toString
-		_ctor overload vec3 function(float, float, float)
-	`));
-	mixin xposeMiniD;
-}
-
-class Poop {
-	int x = 0;
-	
-	this() {
-		this.x = 123456;
-	}
-	
-	this(int x) {
-		this.x = x;
-	}
-	
-	this (char[] zomg) {
-		this.x = zomg.length;
-	}
-	
-	int max(int a, int b) {
-		return a > b ? a : b;
-	}
-	
-	int dmax(int a, int b) {
-		return max(max(a, b), x);
-	}
-	
-	int dmax(int a, int b, int c) {
-		return dmax(a, dmax(b, c));
-	}
-	
-	vec3 getVec3(int x, int y, int z) {
-		return vec3(x, y, z);
-	}
-    
-}
-
-mixin(xpose2(`Poop`,`
-	dmax
-	dmax overload int function(int, int, int) ; md { rename "dmax2" }
-	getVec3
-	.*
-	_ctor
-	_ctor overload Poop function(int)
-	_ctor overload Poop function(char[])
-`));
-
-mixin xposeMiniD!(`Poop`);
-
-void main(char[][] args)
-{
-	MDVM vm;
-	auto t = openVM(&vm);
-	loadStdlibs(t);
-    xposeMiniD_initAll(t);
-    superPush(t, new Poop(10));
-    newGlobal(t, "poop");
-    importModule(t, args[1]);
-    lookup(t, "poop");
-	auto poop = superGet!(Poop)(t, -1);
-	Stdout.formatln("poop.dmax(1, 2) == {}", poop.dmax(1, 2));
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/game.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.game;
+
+import openmelee.melee.melee;
+
+class Game
+{
+    Melee melee;
+    
+    this() {
+        melee = new Melee;
+    }
+    
+    void run() {
+        melee.init();
+    }
+
+}
--- a/main.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.main;
-
-import tango.io.Stdout : Stdout;
-
-version(distrib) import tango.io.vfs.ZipFolder;
-import tango.time.StopWatch;
-import fc = tango.text.convert.Float : toString;
-import tango.util.log.Trace;
-import tango.math.Math;
-
-import xf.core.JobHub;
-import xf.hybrid.Hybrid;
-import xf.hybrid.backend.GL;
-import xf.omg.core.LinearAlgebra;
-
-import blaze.common.bzMath: bzVec2;
-
-import openmelee.melee;
-import openmelee.render;
-import openmelee.ai;
-
-const ITERS_PER_SECOND = 100;
-
-void main() {
-
-    Settings settings;
-    float timeStep = settings.hz > 0.0f ? 1.0f / settings.hz : 0.0f;
-    version(distrib) gui.vfs.mount(new ZipFolder("./gui.zip"));
-    scope cfg = loadHybridConfig("./gui.cfg");
-    scope renderer = new Renderer;
-    auto whut = new Render(&settings);
-    auto ai = new AI(whut.ship2);
-    
-    gui.begin(cfg).retained;
-    gui.push(`main`);
-    GLViewport(`glview`).renderingHandler(&whut.draw)
-    .addHandler(&whut.onClick)
-    .addHandler(&whut.onMove)
-    .addHandler(&whut.onKey)
-    .addHandler(&whut.onDT)
-    .addHandler(&whut.onMouseEnter)
-    .addHandler(&whut.onMouseLeave)
-    .grabKeyboardFocus;
-    gui.pop();
-    gui.immediate.end;
-
-    StopWatch timer;
-
-    jobHub.addRepeatableJob( {
-        // Update physics
-        whut.world.step(timeStep, settings.velocityIterations,
-                        settings.positionIterations);
-    }, ITERS_PER_SECOND);
-
-    bool running = true;
-
-    jobHub.addPreFrameJob( {
-        // Update AI
-        ai.move(whut.ship1);
-    });
-
-    jobHub.addPostFrameJob( {
-        
-        // Limit velocity
-        whut.ship1.limitVelocity();
-        whut.ship2.limitVelocity();
-        whut.ship1.updateState();
-        whut.ship2.updateState();
-        
-        gui.begin(cfg);
-        gui.push(`main`);
-        if (gui().getProperty!(bool)("frame.closeClicked")) {
-            running = false;
-        }
-
-
-        if(whut.thrust) {
-            whut.ship1.thrust();
-        }
-
-        gui().setProperty!(bool)("showCursor", true);
-        gui.pop();
-        gui.end;
-        gui.render(renderer);
-        
-    });
-
-    while (running && !whut.quit) {
-        float delta = timer.stop;
-        timer.start;
-        jobHub.update(delta);
-    }
-}
--- a/melee.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,320 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.melee;
-
-import tango.io.Stdout: Stdout;
-
-import Integer = tango.text.convert.Integer;
-import tango.math.Math;
-
-import xf.hybrid.Event;
-import xf.input.KeySym;
-import xf.omg.core.LinearAlgebra;
-
-public import blaze.all;
-
-import openmelee.boundaryListener;
-import openmelee.contactListener;
-import openmelee.ship;
-import openmelee.urQuan;
-import openmelee.orz;
-import openmelee.planet;
-
-// Cursor scale factor
-const CURSORSIZE = 0.05f;
-
-const INIT_SPAWN_SIZE = 0.5f;
-
-// Dragging stuffs
-const BUNGEE_K = 1.5f;
-// Damping factor for dragging
-const DRAGDAMP = 20.0f;
-
-// Size of hinges
-const HINGE_RADIUS = 0.05f;
-
-// Smallest allowed dimension
-const MIN_DIMENSION = 0.1;
-
-const k_maxContactPoints = 2048;
-
-enum ContactState {
-    e_contactAdded,
-    e_contactPersisted,
-    e_contactRemoved
-}
-
-struct ContactPoint {
-    bzShape shape1;
-    bzShape shape2;
-    bzVec2 normal;
-    bzVec2 position;
-    bzVec2 velocity;
-    bzContactID id;
-    ContactState state;
-}
-
-// Melee settings. Some can be controlled in the GUI.
-struct Settings {
-    float hz = 60;
-    int velocityIterations = 3;
-    int positionIterations = 1;
-    bool drawShapes = true;
-    bool drawJoints = true;
-    bool drawControllers;
-    bool drawCoreShapes;
-    bool drawAABBs;
-    bool drawOBBs;
-    bool drawPairs;
-    bool drawContactPoints;
-    bool drawContactNormals;
-    bool drawContactForces;
-    bool drawFrictionForces;
-    bool drawCOMs;
-    bool drawStats;
-    bool enableWarmStarting;
-    bool enableTOI;
-}
-
-// Dirty, dirty hack for communicating config changes to Main
-// TODO: Harass h3 to add .changed to hybrid so this isn't necessary
-struct ConfigChange (T)
-{
-    protected
-    {
-        bool _pending = false;
-        T _value;
-    }
-
-    T value()
-    {
-        _pending = false;
-        return _value;
-    }
-
-    void value(T change)
-    {
-        _value = change;
-        _pending = true;
-    }
-
-    bool pending()
-    {
-        return _pending;
-    }
-
-    T opAssign(T change)
-    {
-        value(change);
-        return _value;
-    }
-
-    bool opEquals(T other)
-    {
-        return _value == other;
-    }
-}
-
-class Melee
-{
-
-	this(Settings *settings)
-	{
-		this.settings = settings;
-		spawnRect = vec2(INIT_SPAWN_SIZE, INIT_SPAWN_SIZE);
-		// bzWorld boundary callback
-		m_boundaryListener = new BoundaryListener(this);
-		// bzContact callback
-		m_contactListener = new ContactListener(this);
-        init();
-	}
-
-	void init() {
-	    // Define world boundaries
-		worldAABB.lowerBound.set(-400.0f, -250.0f);
-		worldAABB.upperBound.set(400.0f, 250.0f);
-		world = new bzWorld(worldAABB, gravity, allowSleep);
-		world.boundaryListener = m_boundaryListener;
-		world.contactListener = m_contactListener;
-		viewCenter = vec2(10, 10);
-		ship1 = new UrQuan(world);
-		ship2 = new Orz(world);
-        ship2.rBody.angle = 3.14159265/4;
-        auto planet = new Planet(world);
-	}
-
-	void drag()
-	{
-
-	}
-
-	EventHandling onClick(MouseButtonEvent e)
-	{
-        return EventHandling.Stop;
-	}
-
-	EventHandling onKey(KeyboardEvent e)
-	{
-        // Key pressed
-		if (e.down) {
-			switch (e.keySym) {
-            case KeySym.space:
-                settings.drawAABBs = !settings.drawAABBs;
-                break;
-			case KeySym.Escape:
-                quit = true;
-                break;
-            case KeySym.Up:            
-                thrust = true;
-                break;
-            case KeySym.Left:            
-                ship1.turnLeft();
-                break;
-            case KeySym.Right:             
-                ship1.turnRight();
-                break;
-            case KeySym.Down:             
-                break;
-			default:
-				break;
-			}
-        // Key released
-		} else {
-		    if(e.keySym == KeySym.Up) {
-		         thrust = false;
-		    } else if (e.keySym == KeySym.Left || e.keySym == KeySym.Right) {
-                ship1.rBody.angularVelocity = 0.0f;
-            }
-		}
-		return EventHandling.Stop;
-	}
-
-	// Mouse move
-	EventHandling onMove(MouseMoveEvent e)
-	{
-		return EventHandling.Stop;
-	}
-
-	EventHandling onDT(TimeUpdateEvent e)
-	{
-		return EventHandling.Continue;
-	}
-
-	EventHandling onMouseEnter(MouseEnterEvent e)
-	{
-		return EventHandling.Continue;
-	}
-
-	EventHandling onMouseLeave(MouseLeaveEvent e)
-	{
-		return EventHandling.Continue;
-	}
-
-	protected
-	{
-		const bzVec2 gravity = bzVec2(0.0f, 0.0f);
-		bool allowSleep = false;
-
-		vec2 spawnRect;
-
-		vec2[] drawing;
-
-		vec2i screenSize = vec2i.zero;
-		vec2 mousePos = vec2.zero;
-
-		bool scaling = false;
-		bool full = false;
-
-		vec2 spawnStart;
-
-		bool preserveBullet = false;
-
-		float waterDelta = 0;
-	}
-
-	void boundaryViolated(bzBody rBody)
-	{
-        float x,y;
-        
-        if(rBody.position.x > worldAABB.upperBound.x) {
-            x = worldAABB.lowerBound.x + 5;
-            rBody.position = bzVec2(x, rBody.position.y);
-        } else if (rBody.position.x < worldAABB.lowerBound.x) {
-            x = worldAABB.upperBound.x - 5;
-            rBody.position = bzVec2(x, rBody.position.y);
-        } else if (rBody.position.y > worldAABB.upperBound.y) {
-            y = worldAABB.lowerBound.y + 5;
-            rBody.position = bzVec2(rBody.position.x, y);
-        } else if(rBody.position.y < worldAABB.lowerBound.y) {
-            y = worldAABB.upperBound.y - 5;
-            rBody.position = bzVec2(rBody.position.x, y);
-        }        
-	}
-
-	bool quit;
-
-	// Ortho view zoom
-	float zoom = 40;
-	int pointCount;
-	vec2 viewCenter;
-
-	bzWorld world;
-	Settings *settings;
-
-	// bzWorld boundary listener. Destroy bodies that leave world bzAABB
-	bzBoundaryListener m_boundaryListener;
-	bzContactListener m_contactListener;
-	ContactPoint[k_maxContactPoints] points;
-
-	ConfigChange!(vec2) editChange;
-    bool thrust;
-    bzAABB worldAABB;
-    
-	Ship ship1;
-	Ship ship2;
-}
-
-// Utility functions
-bzVec2 toBlaze(vec2 vec)
-{
-    auto ret = bzVec2(vec.x, vec.y);
-    return ret;
-}
-
-vec2 rotate(vec2 point, float rad)
-{
-    return vec2(cos(rad) * point.x - sin(rad) * point.y, sin(rad) * point.x + cos(rad) * point.y);
-}
-
-class Select
-{
-	bool select;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/melee/boundaryListener.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * Based on Box2D by Erin Catto, http://www.box2d.org
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.melee.boundaryListener;
+
+import blaze.dynamics.bzWorldCallbacks : bzBoundaryListener;
+import blaze.dynamics.bzBody : bzBody;
+
+import openmelee.melee.melee;
+
+// bzWorld boundary callback
+class BoundaryListener : bzBoundaryListener
+{
+	Melee melee;
+
+	this(Melee melee)
+	{
+		this.melee = melee;
+	}
+
+	void violation(bzBody rBody)
+	{
+        melee.boundaryViolated(rBody);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/melee/contactListener.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * Based on Box2D by Erin Catto, http://www.box2d.org
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.melee.contactListener;
+
+import blaze.collision.bzCollision : bzContactID;
+import blaze.dynamics.contact.bzContact : bzContactPoint, bzContactResult;
+import blaze.dynamics.bzWorldCallbacks : bzContactListener;
+import blaze.collision.shapes.bzShape : bzShape;
+import blaze.common.bzMath : bzVec2;
+
+import openmelee.melee.melee;
+
+enum ContactState {
+    e_contactAdded,
+    e_contactPersisted,
+    e_contactRemoved
+}
+
+struct ContactPoint {
+    bzShape shape1;
+    bzShape shape2;
+    bzVec2 normal;
+    bzVec2 position;
+    bzVec2 velocity;
+    bzContactID id;
+    ContactState state;
+}
+
+// bzWorld contact callback
+class ContactListener : bzContactListener
+{
+
+	Melee melee;
+
+	this(Melee melee) {
+		this.melee = melee;
+	}
+
+	void add(bzContactPoint point)
+	{
+		if (melee.pointCount == k_maxContactPoints) {
+			return;
+		}
+
+		ContactPoint *cp = &melee.points[melee.pointCount];
+		cp.shape1 = point.shape1;
+		cp.shape2 = point.shape2;
+		cp.position = point.position;
+		cp.normal = point.normal;
+		cp.id = point.id;
+		cp.state = ContactState.e_contactAdded;
+
+		++melee.pointCount;
+	}
+
+	void persist(bzContactPoint point)
+	{
+		if (melee.pointCount == k_maxContactPoints) {
+			return;
+		}
+
+		ContactPoint *cp = &melee.points[melee.pointCount];
+		cp.shape1 = point.shape1;
+		cp.shape2 = point.shape2;
+		cp.position = point.position;
+		cp.normal = point.normal;
+		cp.id = point.id;
+		cp.state = ContactState.e_contactPersisted;
+
+		++melee.pointCount;
+	}
+
+	void remove(bzContactPoint point)
+	{
+		if (melee.pointCount == k_maxContactPoints) {
+			return;
+		}
+
+		ContactPoint *cp = &melee.points[melee.pointCount];
+		cp.shape1 = point.shape1;
+		cp.shape2 = point.shape2;
+		cp.position = point.position;
+		cp.normal = point.normal;
+		cp.id = point.id;
+		cp.state = ContactState.e_contactRemoved;
+
+		++melee.pointCount;
+	}
+
+	void result(bzContactResult point) {}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/melee/melee.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.melee.melee;
+
+import tango.io.Stdout;
+
+version(distrib) import tango.io.vfs.ZipFolder;
+import tango.time.StopWatch;
+import fc = tango.text.convert.Float : toString;
+import tango.util.log.Trace;
+
+import xf.core.JobHub;
+import xf.hybrid.Hybrid;
+import xf.hybrid.backend.GL;
+
+import blaze.common.bzMath : bzVec2;
+import blaze.bzWorld : bzWorld;
+import blaze.collision.bzCollision : bzAABB;
+
+import openmelee.melee.boundaryListener;
+import openmelee.melee.contactListener;
+import openmelee.render.render;
+import openmelee.ai.ai;
+import openmelee.ai.human;
+import openmelee.ships.urQuan;
+import openmelee.ships.orz;
+import openmelee.ships.planet;
+
+const ITERS_PER_SECOND = 100;
+const k_maxContactPoints = 100;
+
+// Melee settings
+struct Settings {
+    float hz = 60;
+    int velocityIterations = 3;
+    int positionIterations = 1;
+    bool drawShapes = true;
+    bool drawJoints = true;
+    bool drawControllers;
+    bool drawCoreShapes;
+    bool drawAABBs;
+    bool drawOBBs;
+    bool drawPairs;
+    bool drawContactPoints;
+    bool drawContactNormals;
+    bool drawContactForces;
+    bool drawFrictionForces;
+    bool drawCOMs;
+    bool drawStats;
+    bool enableWarmStarting;
+    bool enableTOI;
+}
+
+class Melee {
+    
+    Settings settings;
+    float timeStep;
+    const bzVec2 gravity = bzVec2(0.0f, 0.0f);
+    bool allowSleep;
+    Render draw;
+    
+    AI ai;
+    Human human;
+    Ship ship1;
+	Ship ship2;
+    
+    bool running;
+    
+    StopWatch timer;
+    bzAABB worldAABB;
+    bzWorld world;
+    bzBoundaryListener m_boundaryListener;
+	bzContactListener m_contactListener;
+	ContactPoint[k_maxContactPoints] points;
+    int pointCount;
+    
+    this() {
+    }
+    
+    void init() {
+
+        timeStep = settings.hz > 0.0f ? 1.0f / settings.hz : 0.0f;
+        version(distrib) gui.vfs.mount(new ZipFolder("./gui.zip"));
+        scope cfg = loadHybridConfig("./gui.cfg");
+        scope renderer = new Renderer;
+    
+		m_boundaryListener = new BoundaryListener(this);
+		m_contactListener = new ContactListener(this);
+        initWorld();
+        running = true;
+        
+        draw = new Render(world, ship1, ship2, settings);
+        human = new Human(ship1);
+        ai = new AI(ship2);
+    
+        gui.begin(cfg).retained;
+        gui.push(`main`);
+        GLViewport(`glview`).renderingHandler(&draw.draw)
+        .addHandler(&human.onClick)
+        .addHandler(&human.onMove)
+        .addHandler(&human.onKey)
+        .addHandler(&human.onDT)
+        .addHandler(&human.onMouseEnter)
+        .addHandler(&human.onMouseLeave)
+        .grabKeyboardFocus;
+        gui.pop();
+        gui.immediate.end;
+    
+        jobHub.addRepeatableJob( {
+            // Update physics
+            world.step(timeStep, settings.velocityIterations, settings.positionIterations);
+        }, ITERS_PER_SECOND);
+
+        jobHub.addPreFrameJob( {
+            // Update AI
+            ai.move(ship1);
+        });
+
+        jobHub.addPostFrameJob( {
+            
+            // Limit velocity
+            ship1.limitVelocity();
+            ship2.limitVelocity();
+            ship1.updateState();
+            ship2.updateState();
+            
+            gui.begin(cfg);
+            gui.push(`main`);
+            if (gui().getProperty!(bool)("frame.closeClicked")) {
+                running = false;
+            }
+
+            if(human.thrust) {
+                ship1.thrust();
+            }
+
+            gui().setProperty!(bool)("showCursor", true);
+            gui.pop();
+            gui.end;
+            gui.render(renderer);
+            
+        });
+        
+        while (running && !human.quit) {
+            float delta = timer.stop;
+            timer.start;
+            jobHub.update(delta);
+        }
+    }
+
+    void initWorld() {
+	    // Define world boundaries
+		worldAABB.lowerBound.set(-400.0f, -250.0f);
+		worldAABB.upperBound.set(400.0f, 250.0f);
+		world = new bzWorld(worldAABB, gravity, allowSleep);
+		world.boundaryListener = m_boundaryListener;
+		world.contactListener = m_contactListener;
+		ship1 = new UrQuan(world);
+		ship2 = new Orz(world);
+        ship2.rBody.angle = 3.14159265/4;
+        auto planet = new Planet(world);
+	}
+    
+    void boundaryViolated(bzBody rBody)
+	{
+        float x,y;
+        
+        if(rBody.position.x > worldAABB.upperBound.x) {
+            x = worldAABB.lowerBound.x + 5;
+            rBody.position = bzVec2(x, rBody.position.y);
+        } else if (rBody.position.x < worldAABB.lowerBound.x) {
+            x = worldAABB.upperBound.x - 5;
+            rBody.position = bzVec2(x, rBody.position.y);
+        } else if (rBody.position.y > worldAABB.upperBound.y) {
+            y = worldAABB.lowerBound.y + 5;
+            rBody.position = bzVec2(rBody.position.x, y);
+        } else if(rBody.position.y < worldAABB.lowerBound.y) {
+            y = worldAABB.upperBound.y - 5;
+            rBody.position = bzVec2(rBody.position.x, y);
+        }        
+	}
+}
--- a/models.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,239 +0,0 @@
-void spathiEluder(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
-{
-	vertex.length = bodyVertex.length = 11;
-
-	vertex[0].length = bodyVertex[0].length = 19;
-	// Hull
-	bodyVertex[0][0] = bzVec2(2.24,31.54);
-	bodyVertex[0][1] = bzVec2(14.14,28.28);
-	bodyVertex[0][2] = bzVec2(23.89,20.72);
-	bodyVertex[0][3] = bzVec2(30.00,10.00);
-	bodyVertex[0][4] = bzVec2(31.54, -2.24);
-	bodyVertex[0][5] = bzVec2(28.28,-14.14);
-	bodyVertex[0][6] = bzVec2(22.48,-21.62);
-	bodyVertex[0][7] = bzVec2(20.72,-23.89);
-	bodyVertex[0][8] = bzVec2(10.00,-30.00);
-	bodyVertex[0][9] = bzVec2(-2.24,-31.54);
-	bodyVertex[0][10] = bzVec2(-8.19,-29.91);
-	bodyVertex[0][11] = bzVec2(-19.02,-24.50);
-	bodyVertex[0][12] = bzVec2(-23.89,-20.72);
-	bodyVertex[0][13] = bzVec2(-30.00,-10.00);
-	bodyVertex[0][14] = bzVec2(-31.00,-5.00);
-	bodyVertex[0][15] = bzVec2(-31.00,5.00);
-	bodyVertex[0][16] = bzVec2(-28.28,14.14);
-	bodyVertex[0][17] = bzVec2(-20.72,23.89);
-	bodyVertex[0][18] = bzVec2(-10.00,30.00);
-
-	vertex[1].length = bodyVertex[1].length = 4;
-	// Body
-	bodyVertex[1][0] = bzVec2(14.14,28.28);
-	bodyVertex[1][1] = bzVec2(23.89,20.72);
-	bodyVertex[1][2] = bzVec2(44.56,47.70);
-	bodyVertex[1][3] = bzVec2(35.97,54.29);
-
-	vertex[2].length = bodyVertex[2].length = 4;
-	// Top Strut
-	bodyVertex[2][0] = bzVec2(22.48,-21.62);
-	bodyVertex[2][1] = bzVec2(28.28,-14.14);
-	bodyVertex[2][2] = bzVec2(53.41,-29.39);
-	bodyVertex[2][3] = bzVec2(46.67,-37.86);
-
-	vertex[3].length = bodyVertex[3].length = 4;
-	// Top Wing
-	bodyVertex[3][0] = bzVec2(-19.02,-24.50);
-	bodyVertex[3][1] = bzVec2(-8.19,-29.91);
-	bodyVertex[3][2] = bzVec2(-36.26,-62.25);
-	bodyVertex[3][3] = bzVec2(-42.85,-53.66);
-
-	vertex[4].length = bodyVertex[4].length = 4;
-	// Bottom Strut
-	bodyVertex[4][0] = bzVec2(-31.00,-5.00);
-	bodyVertex[4][1] = bzVec2(-31.00,5.00);
-	bodyVertex[4][2] = bzVec2(-46.00,5.00);
-	bodyVertex[4][3] = bzVec2(-46.00,-5.00);
-
-	vertex[5].length = bodyVertex[5].length = 4;
-	// Bottom Wing
-	bodyVertex[5][0] = bzVec2(-10.00,30.00);
-	bodyVertex[5][1] = bzVec2(-20.72,23.89);
-	bodyVertex[5][2] = bzVec2(-50.00,65.86);
-	bodyVertex[5][3] = bzVec2(-40.00,70.00);
-
-	vertex[6].length = bodyVertex[6].length = 8;
-	// Rear, top Pod
-	bodyVertex[6][0] = bzVec2(-40.00 ,70.00);
-	bodyVertex[6][1] = bzVec2(-35.86 , 80.00);
-	bodyVertex[6][2] = bzVec2( -40.00, 90.00);
-	bodyVertex[6][3] = bzVec2( -50.00,94.14);
-	bodyVertex[6][4] = bzVec2( -60.00, 90.00);
-	bodyVertex[6][5] = bzVec2(-64.14 , 80.00);
-	bodyVertex[6][6] = bzVec2(-60.00 , 70.00);
-	bodyVertex[6][7] = bzVec2(-50.00 ,65.86);
-
-	vertex[7].length = bodyVertex[7].length = 8;
-	// Rear, middle pod
-	bodyVertex[7][0] = bzVec2(-46.00,5.00);
-	bodyVertex[7][1] = bzVec2( -46.00,-5.00);
-	bodyVertex[7][2] = bzVec2(-54.47 ,-12.02);
-	bodyVertex[7][3] = bzVec2( -65.30, -13.11);
-	bodyVertex[7][4] = bzVec2( -73.02, -5.53);
-	bodyVertex[7][5] = bzVec2( -73.11,5.30);
-	bodyVertex[7][6] = bzVec2( -65.53, 13.02);
-	bodyVertex[7][7] = bzVec2( -54.70,13.11);
-
-
-	vertex[8].length = bodyVertex[8].length = 8;
-	// Rear, bottom pod
-	bodyVertex[8][0] = bzVec2( -36.26,-62.25);
-	bodyVertex[8][1] = bzVec2( -42.85,-53.66);
-	bodyVertex[8][2] = bzVec2( -53.59,-52.25);
-	bodyVertex[8][3] = bzVec2( -62.17,-58.84);
-	bodyVertex[8][4] = bzVec2( -63.59,-69.57);
-	bodyVertex[8][5] = bzVec2( -57.00,-78.16);
-	bodyVertex[8][6] = bzVec2( -46.26,-79.57);
-	bodyVertex[8][7] = bzVec2( -37.68,-72.98);
-
-	vertex[9].length = bodyVertex[9].length = 8;
-
-	bodyVertex[9][0] = bzVec2( 53.41, -29.39);
-	bodyVertex[9][1] = bzVec2( 46.67,-37.86);
-	bodyVertex[9][2] = bzVec2( 47.89,-47.61);
-	bodyVertex[9][3] = bzVec2( 56.36, -55.35);
-	bodyVertex[9][4] = bzVec2( 67.12,-54.12);
-	bodyVertex[9][5] = bzVec2( 73.86, -45.65);
-	bodyVertex[9][6] = bzVec2( 72.63,-34.90);
-	bodyVertex[9][7] = bzVec2( 64.16,-28.16);
-
-	vertex[10].length = bodyVertex[10].length = 8;
-
-	bodyVertex[10][0] = bzVec2( 35.97, 54.29);
-	bodyVertex[10][1] = bzVec2( 44.56, 47.70);
-	bodyVertex[10][2] = bzVec2( 56.29, 49.12 );
-	bodyVertex[10][3] = bzVec2( 61.88, 57.70);
-	bodyVertex[10][4] = bzVec2( 60.47, 68.44);
-	bodyVertex[10][5] = bzVec2( 51.88, 75.02);
-	bodyVertex[10][6] = bzVec2( 41.15, 73.61);
-	bodyVertex[10][7] = bzVec2( 34.56, 65.02);
-}
-
-void urQuanDreadnought(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
-{
-	vertex.length = bodyVertex.length = 6;
-
-	vertex[0].length = bodyVertex[0].length = 8;
-	// Head
-	bodyVertex[0][0] = bzVec2(42,49);
-	bodyVertex[0][1] = bzVec2(63,49);
-	bodyVertex[0][2] = bzVec2(70,45.5);
-	bodyVertex[0][3] = bzVec2(73.5,38.5);
-	bodyVertex[0][4] = bzVec2(73.5,-42);
-	bodyVertex[0][5] = bzVec2(70,-49);
-	bodyVertex[0][6] = bzVec2(63,-56);
-	bodyVertex[0][7] = bzVec2(42,-56);
-
-	vertex[1].length = bodyVertex[1].length = 4;
-	// Body
-	bodyVertex[1][0] = bzVec2(-70,-28);
-	bodyVertex[1][1] = bzVec2(-70,24.5);
-	bodyVertex[1][2] = bzVec2(42,24.5);
-	bodyVertex[1][3] = bzVec2(42,-31.5);
-
-	vertex[2].length = bodyVertex[2].length = 4;
-	// Top Strut
-	bodyVertex[2][0] = bzVec2(0,24.5);
-	bodyVertex[2][1] = bzVec2(-28,24.5);
-	bodyVertex[2][2] = bzVec2(-28,42);
-	bodyVertex[2][3] = bzVec2(0,42);
-
-	vertex[3].length = bodyVertex[3].length = 4;
-	// Top Wing
-	bodyVertex[3][0] = bzVec2(-70,42);
-	bodyVertex[3][1] = bzVec2(-49,63);
-	bodyVertex[3][2] = bzVec2(28,63);
-	bodyVertex[3][3] = bzVec2(28,42);
-
-	vertex[4].length = bodyVertex[4].length = 4;
-	// Bottom Strut
-	bodyVertex[4][0] = bzVec2(0,-31.5);
-	bodyVertex[4][1] = bzVec2(0,-49);
-	bodyVertex[4][2] = bzVec2(-28,-49);
-	bodyVertex[4][3] = bzVec2(-28,-31.5);
-
-	vertex[5].length = bodyVertex[5].length = 4;
-	// Bottom Wing
-	bodyVertex[5][0] = bzVec2(-70,-49);
-	bodyVertex[5][1] = bzVec2(28,-49);
-	bodyVertex[5][2] = bzVec2(28,-70);
-	bodyVertex[5][3] = bzVec2(-42,-70);
-}
-
-void yehatTerminator(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
-{
-	vertex.length = bodyVertex.length = 4;
-	vertex[0].length = bodyVertex[0].length = 3;
-	// Cockpit
-	bodyVertex[0][0] = bzVec2(-14,21);
-	bodyVertex[0][1] = bzVec2(0,42);
-	bodyVertex[0][2] = bzVec2(14,21);
-
-
-	vertex[1].length = bodyVertex[1].length = 4;
-	// Body
-	bodyVertex[1][0] = bzVec2(-14,21);
-	bodyVertex[1][1] = bzVec2(-14,-21);
-	bodyVertex[1][2] = bzVec2(14,-21);
-	bodyVertex[1][3] = bzVec2(14,21);
-
-	vertex[2].length = bodyVertex[2].length = 4;
-	// Right Wing
-	bodyVertex[2][0] = bzVec2(14,21);
-	bodyVertex[2][1] = bzVec2(14,-21);
-	bodyVertex[2][2] = bzVec2(70,0);
-	bodyVertex[2][3] = bzVec2(84,59.5);
-
-	vertex[3].length = bodyVertex[3].length = 4;
-	// Left Wing
-	bodyVertex[3][0] = bzVec2(-14,21);
-	bodyVertex[3][1] = bzVec2(-14,-21);
-	bodyVertex[3][2] = bzVec2(-70,0);
-	bodyVertex[3][3] = bzVec2(-84,59.5);
-}
-
-void orzNemesis(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
-{
-	vertex.length = bodyVertex.length = 3;
-	vertex[0].length = bodyVertex[0].length = 4;
-	// Body
-	bodyVertex[0][0] = bzVec2(-28,21);
-	bodyVertex[0][1] = bzVec2(-28,-28);
-	bodyVertex[0][2] = bzVec2(42,-21);
-	bodyVertex[0][3] = bzVec2(42,14);
-
-	vertex[1].length = bodyVertex[1].length = 5;
-	// Top Wing
-	bodyVertex[1][0] = bzVec2(-28,21);
-	bodyVertex[1][1] = bzVec2(-70,63);
-	bodyVertex[1][2] = bzVec2(-49,63);
-	bodyVertex[1][3] = bzVec2(70,14);
-	bodyVertex[1][4] = bzVec2(42,14);
-
-	vertex[2].length = bodyVertex[2].length = 5;
-	// Bottom Wing
-	bodyVertex[2][0] = bzVec2(-28,-28);
-	bodyVertex[2][1] = bzVec2(-70,-63);
-	bodyVertex[2][2] = bzVec2(-49,-63);
-	bodyVertex[2][3] = bzVec2(70,-21);
-	bodyVertex[2][4] = bzVec2(42,-21);
-}
-
-void triangle(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
-{
-
-	vertex.length = bodyVertex.length = 1;
-	vertex[0].length = bodyVertex[0].length = 3;
-
-	bodyVertex[0][0] = bzVec2(0.0f,7.0f);
-	bodyVertex[0][1] = bzVec2(7.0f,-7.0f);
-	bodyVertex[0][2] = bzVec2(-7.0f,-7.0f);
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/openmelee.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.openmelee;
+
+import openmelee.game;
+
+void main(char[][] args) {
+    
+    auto game = new Game();
+    game.run();
+    
+}
--- a/openmelee.geany	Tue Mar 24 16:47:42 2009 -0400
+++ b/openmelee.geany	Wed Mar 25 11:28:25 2009 -0400
@@ -14,17 +14,17 @@
 run_cmd=
 
 [files]
-current_page=0
-FILE_NAME_0=3305;D;0;16;0;1;1;C:\\workspace\\openmelee\\main.d;0
-FILE_NAME_1=1783;D;0;16;0;1;1;C:\\workspace\\openmelee\\melee.d;0
+current_page=1
+FILE_NAME_0=3173;D;0;16;0;1;1;C:\\workspace\\openmelee\\main.d;0
+FILE_NAME_1=7284;D;0;16;0;1;1;C:\\workspace\\openmelee\\melee.d;0
 FILE_NAME_2=2250;D;0;16;0;1;1;C:\\workspace\\openmelee\\orz.d;0
-FILE_NAME_3=9747;D;0;16;0;1;1;C:\\workspace\\openmelee\\render.d;0
-FILE_NAME_4=2659;D;0;16;0;1;1;C:\\workspace\\openmelee\\ship.d;0
+FILE_NAME_3=9310;D;0;16;0;1;1;C:\\workspace\\openmelee\\render.d;0
+FILE_NAME_4=4052;D;0;16;0;1;1;C:\\workspace\\openmelee\\ship.d;0
 FILE_NAME_5=6206;D;0;16;0;1;1;C:\\workspace\\openmelee\\steer.d;0
 FILE_NAME_6=2252;D;0;16;0;1;1;C:\\workspace\\openmelee\\urQuan.d;0
 FILE_NAME_7=0;None;0;16;0;1;1;C:\\workspace\\openmelee\\build-dmd-win.bat;0
 FILE_NAME_8=304;Conf;0;16;0;1;0;C:\\workspace\\openmelee\\gui.cfg;0
 FILE_NAME_9=1648;D;0;16;0;1;0;C:\\workspace\\openmelee\\planet.d;0
-FILE_NAME_10=2827;D;0;16;0;1;1;C:\\workspace\\openmelee\\ai.d;0
+FILE_NAME_10=2757;D;0;16;0;1;1;C:\\workspace\\openmelee\\ai.d;0
 FILE_NAME_11=814;D;0;16;0;1;1;C:\\workspace\\openmelee\\example.d;0
 FILE_NAME_12=19;None;0;16;0;1;1;C:\\workspace\\openmelee\\testScript.md;0
--- a/orz.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.orz;
-
-import blaze.dynamics.bzBody : bzBody;
-import blaze.bzWorld: bzWorld;
-import blaze.dynamics.bzBodyDef;
-import blaze.collision.shapes.bzPolygon : bzPolyDef;
-import blaze.common.bzMath: bzVec2, PI;
-
-import openmelee.ship;
-
-// UrQuan Dreadnought
-class Orz : Ship
-{
-
-    float scale = 0.025;
-
-    this(bzWorld world) {
-        
-        super(world);
-        engineForce = bzVec2(500, 0);
-        turnForce = bzVec2(0, 5000);
-        rightTurnPoint = bzVec2(-0.5, 0);
-        leftTurnPoint = bzVec2(0.5, 0);
-
-        auto bodyDef = new bzBodyDef;
-        //bodyDef.isBullet = true;
-        bodyDef.position = bzVec2(20,15);
-        bodyDef.angle = PI/2;
-        bodyDef.allowFreeze = false;
-
-        rBody = world.createBody(bodyDef);
-        float density = 5.0f;
-
-        // Body
-        auto b = new bzPolyDef(density);
-        b.vertices.length = 4;
-        b.vertices[0] = bzVec2(42,14) * scale;
-        b.vertices[1] = bzVec2(-28,21) * scale;
-        b.vertices[2] = bzVec2(-28,-28) * scale;
-        b.vertices[3] = bzVec2(42,-21) * scale;
-        shapes.add(rBody.createShape(b));
-
-        // Top Wing
-        auto tWing = new bzPolyDef(density);
-        tWing.vertices.length = 5;
-        tWing.vertices[4] = bzVec2(-28,21) * scale;
-        tWing.vertices[3] = bzVec2(-70,63) * scale;
-        tWing.vertices[2] = bzVec2(-49,63) * scale;
-        tWing.vertices[1] = bzVec2(70,14) * scale;
-        tWing.vertices[0] = bzVec2(42,14) * scale;
-        shapes.add(rBody.createShape(tWing));
-
-        // Bottom Wing
-        auto bWing = new bzPolyDef(density);
-        bWing.vertices.length = 5;
-        bWing.vertices[0] = bzVec2(-28,-28) * scale;
-        bWing.vertices[1] = bzVec2(-70,-63) * scale;
-        bWing.vertices[2] = bzVec2(-49,-63) * scale;
-        bWing.vertices[3] = bzVec2(70,-21) * scale;
-        bWing.vertices[4] = bzVec2(42,-21) * scale;
-        shapes.add(rBody.createShape(bWing));
-
-        rBody.setMassFromShapes();
-        setPlanetGravity();
-      }
-}
--- a/planet.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.planet;
-
-import blaze.dynamics.bzBody : bzBody;
-import blaze.bzWorld: bzWorld;
-import blaze.dynamics.bzBodyDef;
-import blaze.collision.shapes.bzCircle : bzCircleDef;
-import blaze.common.bzMath: bzVec2;
-
-class Planet 
-{
-    
-    bzWorld world;
-    
-    this(bzWorld world) {
-        this.world = world;
-        init();
-    }
-    
-    void init() {
-        // Create planet
-        bzVec2 position = bzVec2.zeroVect;
-        float angle = 0.0f;
-        auto bd = new bzBodyDef(position, angle);
-        auto rBody = world.createBody(bd);
-        float radius = 7.0f;
-        float density = 7.5f;
-        auto sd = new bzCircleDef(density, radius);
-        float friction = 1.0f;
-        float restitution = 0.1f;
-        sd.friction = friction;
-        sd.restitution = restitution;
-        rBody.createShape(sd);   
-    }    
-}
--- a/render.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,577 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * Based on Box2D by Erin Catto, http://www.box2d.org
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.render;
-
-import xf.dog.Dog;
-import xf.omg.core.LinearAlgebra;
-import xf.hybrid.Event;
-import xf.hybrid.Font;
-
-import openmelee.melee;
-
-// Water stuffs
-const MAX_CIRCLE_RES = 32;
-
-/// Color for drawing. Each value has the range [0,1].
-struct Color {
-    static Color opCall(float r, float g, float b)
-    {
-        Color u;
-        u.r = r;
-        u.g = g;
-        u.b = b;
-        return u;
-    }
-
-    float r = 0;
-    float g = 0;
-    float b = 0;
-}
-
-class Render : Melee
-{
-
-this(Settings *settings) {
-
-	super(settings);
-}
-
-void drawCircle(GL gl, vec2 center, float radius, bool water = false, float theta = float.nan)
-{
-    int segs = cast(int)(radius) + 20;
-    if (segs > MAX_CIRCLE_RES) segs = MAX_CIRCLE_RES;
-    double coef = 2.0 * PI / segs;
-
-    auto realTheta = (theta <>= 0 ? theta : 0);
-    if (water) {
-        gl.immediate(GL_TRIANGLE_FAN,
-        {
-            gl.Vertex2fv(center.ptr);
-            for (int n = 0; n <= segs; n++) {
-                double rads = n * coef;
-                gl.Vertex2f(radius * cos(rads + realTheta) + center.x, radius * sin(rads + realTheta) + center.y);
-            }
-        });
-    }
-
-    gl.immediate(GL_LINE_STRIP,
-    {
-        for (int n = 0; n <= segs; n++) {
-            double rads = n * coef;
-            gl.Vertex2f(radius * cos(rads + realTheta) + center.x, radius * sin(rads + realTheta) + center.y);
-        }
-        if (theta <>= 0)
-            gl.Vertex2fv(center.ptr);
-    });
-}
-
-void drawSolidCircle(GL gl, vec2 center, float radius, vec2 axis, Color color)
-{
-    const k_segments = 25.0f;
-    const k_increment = 2.0f * PI / k_segments;
-    float theta = 0.0f;
-    gl.Enable(GL_BLEND);
-    gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    gl.Color4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
-    gl.Begin(GL_TRIANGLE_FAN);
-    for (int i = 0; i < k_segments; ++i) {
-        vec2 v = center + radius * vec2(cos(theta), sin(theta));
-        gl.Vertex2f(v.x, v.y);
-        theta += k_increment;
-    }
-    gl.End();
-    gl.Disable(GL_BLEND);
-
-    theta = 0.0f;
-    gl.Color4f(color.r, color.g, color.b, 1.0f);
-    gl.Begin(GL_LINE_LOOP);
-    for (int i = 0; i < k_segments; ++i) {
-        vec2 v = center + radius * vec2(cos(theta), sin(theta));
-        gl.Vertex2f(v.x, v.y);
-        theta += k_increment;
-    }
-    gl.End();
-
-    vec2 p = center + radius * axis;
-    gl.Begin(GL_LINES);
-    gl.Vertex2f(center.x, center.y);
-    gl.Vertex2f(p.x, p.y);
-    gl.End();
-}
-
-void drawPolygon(GL gl, vec2[] glVerts, Color color)
-{
-    gl.Color3f(color.r, color.g, color.b);
-    gl.immediate(GL_LINE_LOOP,
-    {
-        foreach (v; glVerts)
-            gl.Vertex2fv(v.ptr);
-    });
-}
-
-void drawSolidPolygon(GL gl, vec2[] vertices, Color color)
-{
-    gl.Enable(GL_BLEND);
-    gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    gl.Color4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
-    gl.Begin(GL_TRIANGLE_FAN);
-    for (int i = 0; i < vertices.length; ++i) {
-        gl.Vertex2f(vertices[i].x, vertices[i].y);
-    }
-    gl.End();
-    gl.Disable(GL_BLEND);
-
-    gl.Color4f(color.r, color.g, color.b, 1.0f);
-    gl.Begin(GL_LINE_LOOP);
-    for (int i = 0; i < vertices.length; ++i) {
-        gl.Vertex2f(vertices[i].x, vertices[i].y);
-    }
-    gl.End();
-}
-
-
-void drawPoint(GL gl, vec2 p, float size, Color color)
-{
-    gl.Color3f(color.r, color.g, color.b);
-    gl.PointSize(size);
-    gl.Begin(GL_POINTS);
-    gl.Vertex2f(p.x, p.y);
-    gl.End();
-    gl.PointSize(1.0f);
-}
-
-void drawSegment(GL gl, vec2 begin, vec2 end, Color color)
-{
-    gl.Color3f(color.r, color.g, color.b);
-    gl.immediate(GL_LINES,
-    {
-        gl.Vertex2fv(begin.ptr);
-        gl.Vertex2fv(end.ptr);
-    });
-}
-
-// TODO: handle inequal radii correctly
-void connectCircles(GL gl, vec2 center1, float radius1, vec2 center2, float radius2)
-{
-    auto d = center2 - center1;
-    if (!d.length)
-        return;
-    d *= (d.length - radius1) / d.length;
-    center1 += d;
-    center2 -= d;
-    gl.immediate(GL_LINES,
-    {
-        gl.Vertex2fv(center1.ptr);
-        gl.Vertex2fv(center2.ptr);
-    });
-}
-
-void drawXForm(GL gl, bzXForm xf)
-{
-    bzVec2 p1 = xf.position, p2;
-    const k_axisScale = 0.4f;
-
-    gl.Begin(GL_LINES);
-    {
-        gl.Color3f(1.0f, 0.0f, 0.0f);
-        gl.Vertex2f(p1.x, p1.y);
-        p2 = p1 + k_axisScale * xf.R.col1;
-        gl.Vertex2f(p2.x, p2.y);
-
-        gl.Color3f(0.0f, 1.0f, 0.0f);
-        gl.Vertex2f(p1.x, p1.y);
-        p2 = p1 + k_axisScale * xf.R.col2;
-        gl.Vertex2f(p2.x, p2.y);
-    }
-    gl.End();
-}
-
-void drawSpring(GL gl, vec2 a, vec2 b, uint zigs)
-{
-    zigs++;
-
-    // Portion of length dedicated to connectors
-    const float connPart = 0.2;
-
-    vec2 inc = (b - a) / (zigs);
-    // One step from a to b
-    vec2 zigLen = inc * (1 - connPart);
-    // Length of a connector
-    vec2 connLen = inc * (connPart / 2) * zigs;
-    // Width of a zig
-    vec2 zigWidth = (b - a).rotatedHalfPi.normalized;
-    gl.immediate(GL_LINE_STRIP,
-    {
-        gl.Vertex2fv(a.ptr);
-
-        a += connLen;
-        gl.Vertex2fv(a.ptr);
-
-        bool dir = true;
-        a += zigWidth / 2 + zigLen / 2;
-        for (int i = 0; i < zigs; i++) {
-         gl.Vertex2fv(a.ptr);
-         a += zigLen;
-         if (dir) {
-             a -= zigWidth;
-         }else {
-             a += zigWidth;
-         }
-         dir = !dir;
-        }
-
-        gl.Vertex2fv((b - connLen).ptr);
-        gl.Vertex2fv(b.ptr);
-    });
-}
-
-void drawShape(GL gl, bzShape shape, bzXForm xf, Color color, bool core)
-{
-    Color coreColor = Color(0.9f, 0.6f, 0.6f);
-
-    switch (shape.type) {
-    case bzShapeType.CIRCLE:
-        auto circle = cast(bzCircle)shape;
-
-        vec2 center = vec2.from(bzMul(xf, circle.localPosition));
-        float radius = circle.radius;
-        vec2 axis = vec2.from(xf.R.col1);
-
-        gl.drawSolidCircle(center, radius, axis, color);
-
-        if (core) {
-            gl.Color3f(coreColor.r, coreColor.g, coreColor.b);
-            gl.drawCircle(center, radius - k_toiSlop);
-        }
-        break;
-    case bzShapeType.POLYGON:
-    {
-        bzPolygon poly = cast(bzPolygon)shape;
-        bzVec2[] vertices = poly.worldVertices;
-        vec2[]  verts;
-        verts.length = vertices.length;
-        foreach (int i, v; vertices) {
-            verts[i] = vec2.from(v);
-        }
-
-        gl.drawSolidPolygon(verts, color);
-
-        if (core) {
-            bzVec2[] localCoreVertices = poly.coreVertices;
-            verts.length = localCoreVertices.length;
-            for (int i = 0; i < localCoreVertices.length; ++i) {
-                verts[i] = vec2.from(bzMul(xf, localCoreVertices[i]));
-            }
-            gl.drawPolygon(verts, coreColor);
-        }
-    }
-    break;
-
-    case bzShapeType.EDGE:
-    {
-        bzEdge edge = cast(bzEdge)shape;
-
-        vec2 p1 = vec2.from(bzMul(xf, edge.vertex1));
-        vec2 p2 = vec2.from(bzMul(xf, edge.vertex2));
-        gl.drawSegment(p1, p2, color);
-
-        if (core) {
-            p1 = vec2.from(bzMul(xf, edge.coreVertex1));
-            p2 = vec2.from(bzMul(xf, edge.coreVertex2));
-            gl.drawSegment(p1, p2, coreColor);
-        }
-    }
-    break;
-    }
-}
-
-void draw(vec2i screenSize, GL gl)
-{
-   
-    vec2 point1 = vec2.from(ship1.rBody.position);
-    vec2 point2 = vec2.from(ship2.rBody.position);
-    vec2 range = point1 - point2;
-    zoom = bzClamp(1000/range.length, 2, 60);
-    viewCenter = point1 - (range * 0.5f);
-        
-    this.screenSize = screenSize;
-
-    gl.LoadIdentity();
-    gl.MatrixMode(GL_PROJECTION);
-    gl.LoadIdentity();
-    
-    float left = -screenSize.x / zoom;
-    float right = screenSize.x / zoom;
-    float bottom = -screenSize.y / zoom;
-    float top = screenSize.y / zoom;
-
-    gl.gluOrtho2D(left, right, bottom, top);
-    gl.Translatef(-viewCenter.x, -viewCenter.y, 0);
-    gl.MatrixMode(GL_MODELVIEW);
-    gl.Disable(GL_DEPTH_TEST);
-    gl.LoadIdentity();
-    gl.Clear(GL_COLOR_BUFFER_BIT);
-
-    vec2 pp1 = vec2.from(ship2.rBody.position);
-    vec2 pp2 = vec2.from(ship2.state.target);
-    
-    //gl.drawSegment(pp1, pp2, Color(0, 1, 0));
-    
-    // Draw dynamic bodies
-    if (settings.drawShapes) {
-        for (bzBody b = world.bodyList; b; b = b.next) {
-            for (bzShape shape = b.shapeList; shape; shape = shape.next) {
-
-                bzShape s = shape;
-                bzXForm xf = b.xf;
-
-                if (b.isStatic) {
-                    gl.drawShape(s, xf, Color(0.5f, 0.9f, 0.5f), settings.drawCoreShapes);
-                }else if (b.isSleeping) {
-                    gl.drawShape(s, xf, Color(0.5f, 0.5f, 0.9f), settings.drawCoreShapes);
-                }else if (b.userData) {
-                    auto ss = cast(Select)b.userData;
-                    if (ss) {
-                        gl.drawShape(s, xf, Color(0, .5, 1), settings.drawCoreShapes);
-                    }
-                }else {
-                    gl.drawShape(s, xf, Color(0.9f, 0.9f, 0.9f), settings.drawCoreShapes);
-                }
-
-                gl.LoadIdentity();
-                gl.Flush();
-            }
-        }
-    }
-
-    // Draw joints
-    if (settings.drawJoints) {
-        Color color = Color(0, 0, 1);
-        gl.Color3f(0, 0, 1);
-
-        gl.LineWidth(1);
-        for (bzJoint joint = world.jointList; joint; joint = joint.next) {
-            auto distance = cast(bzDistanceJoint)joint;
-            auto pulley = cast(bzPulleyJoint)joint;
-            auto revolute = cast(bzRevoluteJoint)joint;
-            auto prismatic = cast(bzPrismaticJoint)joint;
-            auto line = cast(bzLineJoint)joint;
-            if (distance) {
-                color = Color(.5, .5, 0);
-                // Endpoints
-                vec2 a = vec2.from(distance.anchor1);
-                vec2 b = vec2.from(distance.anchor2);
-                // Circles
-                gl.drawCircle(a, HINGE_RADIUS);
-                gl.drawCircle(b, HINGE_RADIUS);
-                // Connecting line
-                gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-            }else if (pulley) {
-                auto a = vec2.from(pulley.anchor1);
-                auto b = vec2.from(pulley.groundAnchor1);
-                auto c = vec2.from(pulley.groundAnchor2);
-                auto d = vec2.from(pulley.anchor2);
-                gl.drawCircle(a, HINGE_RADIUS);
-                gl.drawCircle(b, HINGE_RADIUS);
-                gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-                gl.drawSegment(b, c, color);
-                gl.drawCircle(c, HINGE_RADIUS);
-                gl.drawCircle(d, HINGE_RADIUS);
-                gl.connectCircles(c, HINGE_RADIUS, d, HINGE_RADIUS);
-            }else if (revolute) {
-                auto a = vec2.from(revolute.rBody1.position);
-                auto b = vec2.from(revolute.anchor1);
-                auto c = vec2.from(revolute.rBody2.position);
-                gl.drawCircle(a, HINGE_RADIUS);
-                gl.drawCircle(b, HINGE_RADIUS);
-                gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-                gl.drawCircle(c, HINGE_RADIUS);
-                gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS);
-            }else if (prismatic) {
-                auto a = vec2.from(prismatic.rBody1.position);
-                auto b = vec2.from(prismatic.anchor1);
-                auto c = vec2.from(prismatic.rBody2.position);
-                gl.drawCircle(a, HINGE_RADIUS);
-                gl.drawCircle(b, HINGE_RADIUS);
-                gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-                gl.drawCircle(c, HINGE_RADIUS);
-                gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS);
-            }else if (line) {
-                auto a = vec2.from(line.rBody1.position);
-                auto b = vec2.from(line.anchor1);
-                auto c = vec2.from(line.rBody2.position);
-                gl.drawCircle(a, HINGE_RADIUS);
-                gl.drawCircle(b, HINGE_RADIUS);
-                gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-                gl.drawCircle(c, HINGE_RADIUS);
-                gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS);
-            }
-        }
-
-        if (settings.drawControllers) {
-            bzForceGenerator[] forces = world.forces;
-            foreach (f; forces) {
-                auto spring1 = cast(bzSpring1) f;
-                auto spring2 = cast(bzSpring2) f;
-                auto buoyancy = cast(bzBuoyancy) f;
-
-                if (spring1) {
-                    auto bungee1 = cast(bzBungee1)spring1;
-                    if (bungee1) {
-                        gl.Color3f(.5, .5, 0);
-                        // Endpoints
-                        vec2 a = vec2.from(bungee1.rBody.position);
-                        vec2 b = vec2.from(bungee1.anchor);
-                        // Circles
-                        gl.drawCircle(a, HINGE_RADIUS);
-                        gl.drawCircle(b, HINGE_RADIUS);
-                        // Connecting line
-                        gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-                    }else {
-                        uint zigs = 10;
-                        auto anchor1 = vec2.from(spring1.anchor);
-                        auto anchor2 = vec2.from(spring1.rBody.position);
-                        gl.drawSpring(anchor1, anchor2, zigs);
-                    }
-                }
-
-                if (spring2) {
-                    auto bungee2 = cast(bzBungee2)spring2;
-                    if (bungee2) {
-                        gl.Color3f(.5, .5, 0);
-                        // Endpoints
-                        vec2 a = vec2.from(bungee2.rBody.position);
-                        vec2 b = vec2.from(bungee2.otherBody.position);
-                        // Circles
-                        gl.drawCircle(a, HINGE_RADIUS);
-                        gl.drawCircle(b, HINGE_RADIUS);
-                        // Connecting line
-                        gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
-                    }else {
-                        uint zigs = 10;
-                        auto anchor1 = vec2.from(spring2.otherBody.position);
-                        auto anchor2 = vec2.from(spring2.rBody.position);
-                        gl.drawSpring(anchor1, anchor2, zigs);
-                    }
-                }
-
-                if(buoyancy) {
-                    float plane = buoyancy.planeOffset;
-                    vec2 p1 = vec2(-50, plane);
-                    vec2 p2 = vec2(50, plane);
-                    gl.drawSegment(p1, p2, color);
-                }
-            }
-        }
-    }
-
-    if(settings.drawPairs) {
-
-        bzBroadPhase bp = world.broadPhase;
-		bzVec2 invQ;
-		invQ.set(1.0f / bp.m_quantizationFactor.x, 1.0f / bp.m_quantizationFactor.y);
-		Color color = Color(0.9f, 0.9f, 0.3f);
-
-        const k_tableCapacity = bzPairManager.TABLE_CAPACITY;
-
-		for (int i = 0; i < k_tableCapacity; ++i)
-		{
-			ushort index = bp.m_pairManager.m_hashTable[i];
-			while (index < bp.m_pairManager.m_pairs.length)
-			{
-                if(index == bzPairManager.NULL_PROXY) {
-                    break;
-                }
-				bzPair pair = bp.m_pairManager.m_pairs[index];
-				bzProxy p1 = bp.m_proxyPool[pair.proxyId1];
-				bzProxy p2 = bp.m_proxyPool[pair.proxyId2];
-
-				bzAABB b1, b2;
-				b1.lowerBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p1.lowerBounds[0]].value;
-				b1.lowerBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p1.lowerBounds[1]].value;
-				b1.upperBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p1.upperBounds[0]].value;
-				b1.upperBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p1.upperBounds[1]].value;
-				b2.lowerBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p2.lowerBounds[0]].value;
-				b2.lowerBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p2.lowerBounds[1]].value;
-				b2.upperBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p2.upperBounds[0]].value;
-				b2.upperBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p2.upperBounds[1]].value;
-
-				bzVec2 x1 = 0.5f * (b1.lowerBound + b1.upperBound);
-				bzVec2 x2 = 0.5f * (b2.lowerBound + b2.upperBound);
-
-				gl.drawSegment(vec2.from(x1),vec2.from(x2), color);
-
-				index = pair.next;
-			}
-		}
-    }
-
-    // Draw the world bounds
-    bzBroadPhase bp = world.broadPhase;
-    bzVec2 worldLower = bp.m_worldAABB.lowerBound;
-    bzVec2 worldUpper = bp.m_worldAABB.upperBound;
-    Color color = Color(0.3f, 0.9f, 0.9f);        
-    vec2 vs[4];
-    vs[0] = vec2(worldLower.x, worldLower.y);
-    vs[1] = vec2(worldUpper.x, worldLower.y);
-    vs[2] = vec2(worldUpper.x, worldUpper.y);
-    vs[3] = vec2(worldLower.x, worldUpper.y);
-    drawPolygon(gl, vs, color);
-    
-    // Draw axis aligned bounding boxes (bzAABB)
-    if (settings.drawAABBs) {
-        bzVec2 invQ;
-        invQ.set(1.0f / bp.m_quantizationFactor.x, 1.0f / bp.m_quantizationFactor.y);
-        color = Color(1.0f, 1.0f, 1.0f);
-        
-        for (int i = 0; i < k_maxProxies; ++i) {
-            bzProxy p = bp.m_proxyPool[i];
-            if (!p.isValid) {
-                continue;
-            }
-
-            bzAABB b;
-            b.lowerBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.lowerBounds[0]].value;
-            b.lowerBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.lowerBounds[1]].value;
-            b.upperBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.upperBounds[0]].value;
-            b.upperBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.upperBounds[1]].value;
-
-            vs[0] = vec2(b.lowerBound.x, b.lowerBound.y);
-            vs[1] = vec2(b.upperBound.x, b.lowerBound.y);
-            vs[2] = vec2(b.upperBound.x, b.upperBound.y);
-            vs[3] = vec2(b.lowerBound.x, b.upperBound.y);
-
-            drawPolygon(gl, vs, color);
-        }
-    }
-}
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render/render.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * Based on Box2D by Erin Catto, http://www.box2d.org
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.render.render;
+
+import tango.math.Math : PI;
+
+import xf.dog.Dog;
+import xf.omg.core.LinearAlgebra;
+import xf.hybrid.Event;
+import xf.hybrid.Font;
+
+import blaze.bzWorld : bzWorld;
+import blaze.dynamics.bzBody : bzBody;
+import blaze.collision.shapes.bzShape : bzShape;
+import blaze.collision.shapes.bzShapeType;
+import blaze.collision.shapes.bzCircle : bzCircle;
+import blaze.collision.shapes.bzPolygon : bzPolygon;
+import blaze.collision.shapes.bzEdge : bzEdge;
+import blaze.collision.nbody.bzBroadPhase : bzBroadPhase, bzProxy;
+import blaze.collision.bzCollision : bzAABB;
+import blaze.common.bzMath : bzXForm, bzVec2, bzMul, bzClamp;
+import blaze.common.bzConstants : k_toiSlop,k_maxProxies;
+
+import openmelee.ships.ship : Ship;
+import openmelee.melee.melee : Settings;
+
+// Cursor scale factor
+const CURSORSIZE = 0.05f;
+const INIT_SPAWN_SIZE = 0.5f;
+// Dragging stuffs
+const BUNGEE_K = 1.5f;
+// Damping factor for dragging
+const DRAGDAMP = 20.0f;
+// Size of hinges
+const HINGE_RADIUS = 0.05f;
+// Smallest allowed dimension
+const MIN_DIMENSION = 0.1;
+const MAX_CIRCLE_RES = 32;
+
+/// Color for drawing. Each value has the range [0,1].
+struct Color {
+    static Color opCall(float r, float g, float b)
+    {
+        Color u;
+        u.r = r;
+        u.g = g;
+        u.b = b;
+        return u;
+    }
+
+    float r = 0;
+    float g = 0;
+    float b = 0;
+}
+
+class Render 
+{
+
+	float zoom = 40;
+	vec2 viewCenter;
+    bzWorld world;
+    vec2i screenSize;
+    bool scaling = false;
+    bool full = false;
+    Settings settings;
+    Ship ship1, ship2;
+    
+    this(bzWorld world, Ship s1, Ship s2, Settings settings) {
+        this.settings = settings;
+        ship1 = s1;
+        ship2 = s2;
+        this.world = world;
+        viewCenter = vec2(10, 10);
+        screenSize = vec2i.zero;
+    }
+
+    void drawCircle(GL gl, vec2 center, float radius, bool water = false, float theta = float.nan)
+    {
+        int segs = cast(int)(radius) + 20;
+        if (segs > MAX_CIRCLE_RES) segs = MAX_CIRCLE_RES;
+        double coef = 2.0 * PI / segs;
+
+        auto realTheta = (theta <>= 0 ? theta : 0);
+        if (water) {
+            gl.immediate(GL_TRIANGLE_FAN,
+            {
+                gl.Vertex2fv(center.ptr);
+                for (int n = 0; n <= segs; n++) {
+                    double rads = n * coef;
+                    gl.Vertex2f(radius * cos(rads + realTheta) + center.x, radius * sin(rads + realTheta) + center.y);
+                }
+            });
+        }
+
+        gl.immediate(GL_LINE_STRIP,
+        {
+            for (int n = 0; n <= segs; n++) {
+                double rads = n * coef;
+                gl.Vertex2f(radius * cos(rads + realTheta) + center.x, radius * sin(rads + realTheta) + center.y);
+            }
+            if (theta <>= 0)
+                gl.Vertex2fv(center.ptr);
+        });
+    }
+
+    void drawSolidCircle(GL gl, vec2 center, float radius, vec2 axis, Color color)
+    {
+        const k_segments = 25.0f;
+        const k_increment = 2.0f * PI / k_segments;
+        float theta = 0.0f;
+        gl.Enable(GL_BLEND);
+        gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        gl.Color4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
+        gl.Begin(GL_TRIANGLE_FAN);
+        for (int i = 0; i < k_segments; ++i) {
+            vec2 v = center + radius * vec2(cos(theta), sin(theta));
+            gl.Vertex2f(v.x, v.y);
+            theta += k_increment;
+        }
+        gl.End();
+        gl.Disable(GL_BLEND);
+
+        theta = 0.0f;
+        gl.Color4f(color.r, color.g, color.b, 1.0f);
+        gl.Begin(GL_LINE_LOOP);
+        for (int i = 0; i < k_segments; ++i) {
+            vec2 v = center + radius * vec2(cos(theta), sin(theta));
+            gl.Vertex2f(v.x, v.y);
+            theta += k_increment;
+        }
+        gl.End();
+
+        vec2 p = center + radius * axis;
+        gl.Begin(GL_LINES);
+        gl.Vertex2f(center.x, center.y);
+        gl.Vertex2f(p.x, p.y);
+        gl.End();
+    }
+
+    void drawPolygon(GL gl, vec2[] glVerts, Color color)
+    {
+        gl.Color3f(color.r, color.g, color.b);
+        gl.immediate(GL_LINE_LOOP,
+        {
+            foreach (v; glVerts)
+                gl.Vertex2fv(v.ptr);
+        });
+    }
+
+    void drawSolidPolygon(GL gl, vec2[] vertices, Color color)
+    {
+        gl.Enable(GL_BLEND);
+        gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        gl.Color4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
+        gl.Begin(GL_TRIANGLE_FAN);
+        for (int i = 0; i < vertices.length; ++i) {
+            gl.Vertex2f(vertices[i].x, vertices[i].y);
+        }
+        gl.End();
+        gl.Disable(GL_BLEND);
+
+        gl.Color4f(color.r, color.g, color.b, 1.0f);
+        gl.Begin(GL_LINE_LOOP);
+        for (int i = 0; i < vertices.length; ++i) {
+            gl.Vertex2f(vertices[i].x, vertices[i].y);
+        }
+        gl.End();
+    }
+
+
+    void drawPoint(GL gl, vec2 p, float size, Color color)
+    {
+        gl.Color3f(color.r, color.g, color.b);
+        gl.PointSize(size);
+        gl.Begin(GL_POINTS);
+        gl.Vertex2f(p.x, p.y);
+        gl.End();
+        gl.PointSize(1.0f);
+    }
+
+    void drawSegment(GL gl, vec2 begin, vec2 end, Color color)
+    {
+        gl.Color3f(color.r, color.g, color.b);
+        gl.immediate(GL_LINES,
+        {
+            gl.Vertex2fv(begin.ptr);
+            gl.Vertex2fv(end.ptr);
+        });
+    }
+
+    // TODO: handle inequal radii correctly
+    void connectCircles(GL gl, vec2 center1, float radius1, vec2 center2, float radius2)
+    {
+        auto d = center2 - center1;
+        if (!d.length)
+            return;
+        d *= (d.length - radius1) / d.length;
+        center1 += d;
+        center2 -= d;
+        gl.immediate(GL_LINES,
+        {
+            gl.Vertex2fv(center1.ptr);
+            gl.Vertex2fv(center2.ptr);
+        });
+    }
+
+    void drawXForm(GL gl, bzXForm xf)
+    {
+        bzVec2 p1 = xf.position, p2;
+        const k_axisScale = 0.4f;
+
+        gl.Begin(GL_LINES);
+        {
+            gl.Color3f(1.0f, 0.0f, 0.0f);
+            gl.Vertex2f(p1.x, p1.y);
+            p2 = p1 + k_axisScale * xf.R.col1;
+            gl.Vertex2f(p2.x, p2.y);
+
+            gl.Color3f(0.0f, 1.0f, 0.0f);
+            gl.Vertex2f(p1.x, p1.y);
+            p2 = p1 + k_axisScale * xf.R.col2;
+            gl.Vertex2f(p2.x, p2.y);
+        }
+        gl.End();
+    }
+
+    void drawSpring(GL gl, vec2 a, vec2 b, uint zigs)
+    {
+        zigs++;
+
+        // Portion of length dedicated to connectors
+        const float connPart = 0.2;
+
+        vec2 inc = (b - a) / (zigs);
+        // One step from a to b
+        vec2 zigLen = inc * (1 - connPart);
+        // Length of a connector
+        vec2 connLen = inc * (connPart / 2) * zigs;
+        // Width of a zig
+        vec2 zigWidth = (b - a).rotatedHalfPi.normalized;
+        gl.immediate(GL_LINE_STRIP,
+        {
+            gl.Vertex2fv(a.ptr);
+
+            a += connLen;
+            gl.Vertex2fv(a.ptr);
+
+            bool dir = true;
+            a += zigWidth / 2 + zigLen / 2;
+            for (int i = 0; i < zigs; i++) {
+             gl.Vertex2fv(a.ptr);
+             a += zigLen;
+             if (dir) {
+                 a -= zigWidth;
+             }else {
+                 a += zigWidth;
+             }
+             dir = !dir;
+            }
+
+            gl.Vertex2fv((b - connLen).ptr);
+            gl.Vertex2fv(b.ptr);
+        });
+    }
+
+    void drawShape(GL gl, bzShape shape, bzXForm xf, Color color, bool core)
+    {
+        Color coreColor = Color(0.9f, 0.6f, 0.6f);
+
+        switch (shape.type) {
+        case bzShapeType.CIRCLE:
+            auto circle = cast(bzCircle)shape;
+
+            vec2 center = vec2.from(bzMul(xf, circle.localPosition));
+            float radius = circle.radius;
+            vec2 axis = vec2.from(xf.R.col1);
+
+            gl.drawSolidCircle(center, radius, axis, color);
+
+            if (core) {
+                gl.Color3f(coreColor.r, coreColor.g, coreColor.b);
+                gl.drawCircle(center, radius - k_toiSlop);
+            }
+            break;
+        case bzShapeType.POLYGON:
+        {
+            bzPolygon poly = cast(bzPolygon)shape;
+            bzVec2[] vertices = poly.worldVertices;
+            vec2[]  verts;
+            verts.length = vertices.length;
+            foreach (int i, v; vertices) {
+                verts[i] = vec2.from(v);
+            }
+
+            gl.drawSolidPolygon(verts, color);
+
+            if (core) {
+                bzVec2[] localCoreVertices = poly.coreVertices;
+                verts.length = localCoreVertices.length;
+                for (int i = 0; i < localCoreVertices.length; ++i) {
+                    verts[i] = vec2.from(bzMul(xf, localCoreVertices[i]));
+                }
+                gl.drawPolygon(verts, coreColor);
+            }
+        }
+        break;
+
+        case bzShapeType.EDGE:
+        {
+            bzEdge edge = cast(bzEdge)shape;
+
+            vec2 p1 = vec2.from(bzMul(xf, edge.vertex1));
+            vec2 p2 = vec2.from(bzMul(xf, edge.vertex2));
+            gl.drawSegment(p1, p2, color);
+
+            if (core) {
+                p1 = vec2.from(bzMul(xf, edge.coreVertex1));
+                p2 = vec2.from(bzMul(xf, edge.coreVertex2));
+                gl.drawSegment(p1, p2, coreColor);
+            }
+        }
+        break;
+        }
+    }
+
+    void draw(vec2i screenSize, GL gl)
+    {
+       
+        vec2 point1 = vec2.from(ship1.rBody.position);
+        vec2 point2 = vec2.from(ship2.rBody.position);
+        vec2 range = point1 - point2;
+        zoom = bzClamp(1000/range.length, 2, 60);
+        viewCenter = point1 - (range * 0.5f);
+            
+        this.screenSize = screenSize;
+
+        gl.LoadIdentity();
+        gl.MatrixMode(GL_PROJECTION);
+        gl.LoadIdentity();
+        
+        float left = -screenSize.x / zoom;
+        float right = screenSize.x / zoom;
+        float bottom = -screenSize.y / zoom;
+        float top = screenSize.y / zoom;
+
+        gl.gluOrtho2D(left, right, bottom, top);
+        gl.Translatef(-viewCenter.x, -viewCenter.y, 0);
+        gl.MatrixMode(GL_MODELVIEW);
+        gl.Disable(GL_DEPTH_TEST);
+        gl.LoadIdentity();
+        gl.Clear(GL_COLOR_BUFFER_BIT);
+
+        vec2 pp1 = vec2.from(ship2.rBody.position);
+        vec2 pp2 = vec2.from(ship2.state.target);
+        
+        //gl.drawSegment(pp1, pp2, Color(0, 1, 0));
+        
+        // Draw dynamic bodies
+        if (settings.drawShapes) {
+            for (bzBody b = world.bodyList; b; b = b.next) {
+                for (bzShape shape = b.shapeList; shape; shape = shape.next) {
+
+                    bzShape s = shape;
+                    bzXForm xf = b.xf;
+
+                    if (b.isStatic) {
+                        gl.drawShape(s, xf, Color(0.5f, 0.9f, 0.5f), settings.drawCoreShapes);
+                    }else if (b.isSleeping) {
+                        gl.drawShape(s, xf, Color(0.5f, 0.5f, 0.9f), settings.drawCoreShapes);
+                    }else {
+                        gl.drawShape(s, xf, Color(0.9f, 0.9f, 0.9f), settings.drawCoreShapes);
+                    }
+
+                    gl.LoadIdentity();
+                    gl.Flush();
+                }
+            }
+        }
+
+        // Draw joints
+        /*
+        if (settings.drawJoints) {
+            Color color = Color(0, 0, 1);
+            gl.Color3f(0, 0, 1);
+
+            gl.LineWidth(1);
+            for (bzJoint joint = world.jointList; joint; joint = joint.next) {
+                auto distance = cast(bzDistanceJoint)joint;
+                auto pulley = cast(bzPulleyJoint)joint;
+                auto revolute = cast(bzRevoluteJoint)joint;
+                auto prismatic = cast(bzPrismaticJoint)joint;
+                auto line = cast(bzLineJoint)joint;
+                if (distance) {
+                    color = Color(.5, .5, 0);
+                    // Endpoints
+                    vec2 a = vec2.from(distance.anchor1);
+                    vec2 b = vec2.from(distance.anchor2);
+                    // Circles
+                    gl.drawCircle(a, HINGE_RADIUS);
+                    gl.drawCircle(b, HINGE_RADIUS);
+                    // Connecting line
+                    gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                }else if (pulley) {
+                    auto a = vec2.from(pulley.anchor1);
+                    auto b = vec2.from(pulley.groundAnchor1);
+                    auto c = vec2.from(pulley.groundAnchor2);
+                    auto d = vec2.from(pulley.anchor2);
+                    gl.drawCircle(a, HINGE_RADIUS);
+                    gl.drawCircle(b, HINGE_RADIUS);
+                    gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                    gl.drawSegment(b, c, color);
+                    gl.drawCircle(c, HINGE_RADIUS);
+                    gl.drawCircle(d, HINGE_RADIUS);
+                    gl.connectCircles(c, HINGE_RADIUS, d, HINGE_RADIUS);
+                }else if (revolute) {
+                    auto a = vec2.from(revolute.rBody1.position);
+                    auto b = vec2.from(revolute.anchor1);
+                    auto c = vec2.from(revolute.rBody2.position);
+                    gl.drawCircle(a, HINGE_RADIUS);
+                    gl.drawCircle(b, HINGE_RADIUS);
+                    gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                    gl.drawCircle(c, HINGE_RADIUS);
+                    gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS);
+                }else if (prismatic) {
+                    auto a = vec2.from(prismatic.rBody1.position);
+                    auto b = vec2.from(prismatic.anchor1);
+                    auto c = vec2.from(prismatic.rBody2.position);
+                    gl.drawCircle(a, HINGE_RADIUS);
+                    gl.drawCircle(b, HINGE_RADIUS);
+                    gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                    gl.drawCircle(c, HINGE_RADIUS);
+                    gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS);
+                }else if (line) {
+                    auto a = vec2.from(line.rBody1.position);
+                    auto b = vec2.from(line.anchor1);
+                    auto c = vec2.from(line.rBody2.position);
+                    gl.drawCircle(a, HINGE_RADIUS);
+                    gl.drawCircle(b, HINGE_RADIUS);
+                    gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                    gl.drawCircle(c, HINGE_RADIUS);
+                    gl.connectCircles(b, HINGE_RADIUS, c, HINGE_RADIUS);
+                }
+            }
+           
+
+            if (settings.drawControllers) {
+                bzForceGenerator[] forces = world.forces;
+                foreach (f; forces) {
+                    auto spring1 = cast(bzSpring1) f;
+                    auto spring2 = cast(bzSpring2) f;
+                    auto buoyancy = cast(bzBuoyancy) f;
+
+                    if (spring1) {
+                        auto bungee1 = cast(bzBungee1)spring1;
+                        if (bungee1) {
+                            gl.Color3f(.5, .5, 0);
+                            // Endpoints
+                            vec2 a = vec2.from(bungee1.rBody.position);
+                            vec2 b = vec2.from(bungee1.anchor);
+                            // Circles
+                            gl.drawCircle(a, HINGE_RADIUS);
+                            gl.drawCircle(b, HINGE_RADIUS);
+                            // Connecting line
+                            gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                        }else {
+                            uint zigs = 10;
+                            auto anchor1 = vec2.from(spring1.anchor);
+                            auto anchor2 = vec2.from(spring1.rBody.position);
+                            gl.drawSpring(anchor1, anchor2, zigs);
+                        }
+                    }
+
+                    if (spring2) {
+                        auto bungee2 = cast(bzBungee2)spring2;
+                        if (bungee2) {
+                            gl.Color3f(.5, .5, 0);
+                            // Endpoints
+                            vec2 a = vec2.from(bungee2.rBody.position);
+                            vec2 b = vec2.from(bungee2.otherBody.position);
+                            // Circles
+                            gl.drawCircle(a, HINGE_RADIUS);
+                            gl.drawCircle(b, HINGE_RADIUS);
+                            // Connecting line
+                            gl.connectCircles(a, HINGE_RADIUS, b, HINGE_RADIUS);
+                        }else {
+                            uint zigs = 10;
+                            auto anchor1 = vec2.from(spring2.otherBody.position);
+                            auto anchor2 = vec2.from(spring2.rBody.position);
+                            gl.drawSpring(anchor1, anchor2, zigs);
+                        }
+                    }
+
+                    if(buoyancy) {
+                        float plane = buoyancy.planeOffset;
+                        vec2 p1 = vec2(-50, plane);
+                        vec2 p2 = vec2(50, plane);
+                        gl.drawSegment(p1, p2, color);
+                    }
+                }
+            }
+        }
+        */
+        
+        // Draw the world bounds
+        bzBroadPhase bp = world.broadPhase;
+        bzVec2 worldLower = bp.m_worldAABB.lowerBound;
+        bzVec2 worldUpper = bp.m_worldAABB.upperBound;
+        Color color = Color(0.3f, 0.9f, 0.9f);        
+        vec2 vs[4];
+        vs[0] = vec2(worldLower.x, worldLower.y);
+        vs[1] = vec2(worldUpper.x, worldLower.y);
+        vs[2] = vec2(worldUpper.x, worldUpper.y);
+        vs[3] = vec2(worldLower.x, worldUpper.y);
+        drawPolygon(gl, vs, color);
+        
+        // Draw axis aligned bounding boxes (bzAABB)
+        if (settings.drawAABBs) {
+            bzVec2 invQ;
+            invQ.set(1.0f / bp.m_quantizationFactor.x, 1.0f / bp.m_quantizationFactor.y);
+            color = Color(1.0f, 1.0f, 1.0f);
+            
+            for (int i = 0; i < k_maxProxies; ++i) {
+                bzProxy p = bp.m_proxyPool[i];
+                if (!p.isValid) {
+                    continue;
+                }
+
+                bzAABB b;
+                b.lowerBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.lowerBounds[0]].value;
+                b.lowerBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.lowerBounds[1]].value;
+                b.upperBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.upperBounds[0]].value;
+                b.upperBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.upperBounds[1]].value;
+
+                vs[0] = vec2(b.lowerBound.x, b.lowerBound.y);
+                vs[1] = vec2(b.upperBound.x, b.lowerBound.y);
+                vs[2] = vec2(b.upperBound.x, b.upperBound.y);
+                vs[3] = vec2(b.lowerBound.x, b.upperBound.y);
+
+                drawPolygon(gl, vs, color);
+            }
+        }
+    }
+}
--- a/ship.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.ship;
-
-import tango.io.Stdout: Stdout;
-
-import tango.util.container.LinkedList : LinkedList;
-import blaze.dynamics.bzBody : bzBody;
-import blaze.collision.shapes.bzShape : bzShape;
-import blaze.common.bzMath: bzVec2, bzCross, bzClamp;
-import blaze.bzWorld : bzWorld;
-import blaze.dynamics.forces.bzAttractor: bzAttractor;
-
-alias LinkedList!(bzShape) ShapeList;
-
-struct State 
-{
-	bzVec2 position;
-    bzVec2 velocity;
-    bzVec2 up;
-    bzVec2 side;
-    bzVec2 forward;
-    bzVec2 target;
-	float speed = 0;
-	float maxForce = 0;
-	bool turn;
-	
-	float enemyAngle;
-	
-	bzVec2 predictFuturePosition(float dt) {
-	    return (position + velocity * dt);
-    }
-}
-
-abstract class Ship
-{
-    bzWorld world;
-    bzBody rBody;
-    ShapeList shapes;
-    bzVec2 engineForce;
-    bzVec2 turnForce;
-    bzVec2 leftTurnPoint;
-    bzVec2 rightTurnPoint;
-	State state;
-	
-    float battery;
-    float crew;
-    
-    float maxLinVel = 50;
-    float maxAngVel = 2;
-    
-    this(bzWorld world) {
-        
-        this.world = world;
-        shapes = new ShapeList;
-    }
-
-    void thrust() {
-        rBody.force += engineForce.rotate(rBody.angle);
-    }
-
-    void turnLeft() {
-        
-        rBody.torque += bzCross(leftTurnPoint.rotate(rBody.angle), 
-                                turnForce.rotate(rBody.angle));
-    }
-    
-    void turnRight() {
-        
-        rBody.torque += bzCross(rightTurnPoint.rotate(rBody.angle), 
-                                turnForce.rotate(rBody.angle));
-    }
-    
-    void setPlanetGravity() {
-        float minRadius = 0.1;
-        float maxRadius = 10;
-        float strength = 4.0;
-        bzVec2 center = bzVec2(0,0);
-        auto attractor = new bzAttractor(rBody, center, strength, minRadius, maxRadius);
-        world.addForce(attractor);
-    }
-    
-    void limitVelocity() {
-        float vx = rBody.linearVelocity.x;
-        float vy = rBody.linearVelocity.y;
-        float av = rBody.angularVelocity;
-        rBody.linearVelocity.x = bzClamp(vx, -maxLinVel, maxLinVel);
-        rBody.linearVelocity.y = bzClamp(vy, -maxLinVel, maxLinVel);
-        rBody.angularVelocity = bzClamp(av, -maxAngVel, maxAngVel);
-    }
-    
-    void updateState() {
-    	state.velocity = rBody.linearVelocity;
-    	state.speed = state.velocity.length;
-    	state.position = rBody.position;
-    	state.forward = engineForce.rotate(rBody.angle);
-    }
-    
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ships/models.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,239 @@
+void spathiEluder(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
+{
+	vertex.length = bodyVertex.length = 11;
+
+	vertex[0].length = bodyVertex[0].length = 19;
+	// Hull
+	bodyVertex[0][0] = bzVec2(2.24,31.54);
+	bodyVertex[0][1] = bzVec2(14.14,28.28);
+	bodyVertex[0][2] = bzVec2(23.89,20.72);
+	bodyVertex[0][3] = bzVec2(30.00,10.00);
+	bodyVertex[0][4] = bzVec2(31.54, -2.24);
+	bodyVertex[0][5] = bzVec2(28.28,-14.14);
+	bodyVertex[0][6] = bzVec2(22.48,-21.62);
+	bodyVertex[0][7] = bzVec2(20.72,-23.89);
+	bodyVertex[0][8] = bzVec2(10.00,-30.00);
+	bodyVertex[0][9] = bzVec2(-2.24,-31.54);
+	bodyVertex[0][10] = bzVec2(-8.19,-29.91);
+	bodyVertex[0][11] = bzVec2(-19.02,-24.50);
+	bodyVertex[0][12] = bzVec2(-23.89,-20.72);
+	bodyVertex[0][13] = bzVec2(-30.00,-10.00);
+	bodyVertex[0][14] = bzVec2(-31.00,-5.00);
+	bodyVertex[0][15] = bzVec2(-31.00,5.00);
+	bodyVertex[0][16] = bzVec2(-28.28,14.14);
+	bodyVertex[0][17] = bzVec2(-20.72,23.89);
+	bodyVertex[0][18] = bzVec2(-10.00,30.00);
+
+	vertex[1].length = bodyVertex[1].length = 4;
+	// Body
+	bodyVertex[1][0] = bzVec2(14.14,28.28);
+	bodyVertex[1][1] = bzVec2(23.89,20.72);
+	bodyVertex[1][2] = bzVec2(44.56,47.70);
+	bodyVertex[1][3] = bzVec2(35.97,54.29);
+
+	vertex[2].length = bodyVertex[2].length = 4;
+	// Top Strut
+	bodyVertex[2][0] = bzVec2(22.48,-21.62);
+	bodyVertex[2][1] = bzVec2(28.28,-14.14);
+	bodyVertex[2][2] = bzVec2(53.41,-29.39);
+	bodyVertex[2][3] = bzVec2(46.67,-37.86);
+
+	vertex[3].length = bodyVertex[3].length = 4;
+	// Top Wing
+	bodyVertex[3][0] = bzVec2(-19.02,-24.50);
+	bodyVertex[3][1] = bzVec2(-8.19,-29.91);
+	bodyVertex[3][2] = bzVec2(-36.26,-62.25);
+	bodyVertex[3][3] = bzVec2(-42.85,-53.66);
+
+	vertex[4].length = bodyVertex[4].length = 4;
+	// Bottom Strut
+	bodyVertex[4][0] = bzVec2(-31.00,-5.00);
+	bodyVertex[4][1] = bzVec2(-31.00,5.00);
+	bodyVertex[4][2] = bzVec2(-46.00,5.00);
+	bodyVertex[4][3] = bzVec2(-46.00,-5.00);
+
+	vertex[5].length = bodyVertex[5].length = 4;
+	// Bottom Wing
+	bodyVertex[5][0] = bzVec2(-10.00,30.00);
+	bodyVertex[5][1] = bzVec2(-20.72,23.89);
+	bodyVertex[5][2] = bzVec2(-50.00,65.86);
+	bodyVertex[5][3] = bzVec2(-40.00,70.00);
+
+	vertex[6].length = bodyVertex[6].length = 8;
+	// Rear, top Pod
+	bodyVertex[6][0] = bzVec2(-40.00 ,70.00);
+	bodyVertex[6][1] = bzVec2(-35.86 , 80.00);
+	bodyVertex[6][2] = bzVec2( -40.00, 90.00);
+	bodyVertex[6][3] = bzVec2( -50.00,94.14);
+	bodyVertex[6][4] = bzVec2( -60.00, 90.00);
+	bodyVertex[6][5] = bzVec2(-64.14 , 80.00);
+	bodyVertex[6][6] = bzVec2(-60.00 , 70.00);
+	bodyVertex[6][7] = bzVec2(-50.00 ,65.86);
+
+	vertex[7].length = bodyVertex[7].length = 8;
+	// Rear, middle pod
+	bodyVertex[7][0] = bzVec2(-46.00,5.00);
+	bodyVertex[7][1] = bzVec2( -46.00,-5.00);
+	bodyVertex[7][2] = bzVec2(-54.47 ,-12.02);
+	bodyVertex[7][3] = bzVec2( -65.30, -13.11);
+	bodyVertex[7][4] = bzVec2( -73.02, -5.53);
+	bodyVertex[7][5] = bzVec2( -73.11,5.30);
+	bodyVertex[7][6] = bzVec2( -65.53, 13.02);
+	bodyVertex[7][7] = bzVec2( -54.70,13.11);
+
+
+	vertex[8].length = bodyVertex[8].length = 8;
+	// Rear, bottom pod
+	bodyVertex[8][0] = bzVec2( -36.26,-62.25);
+	bodyVertex[8][1] = bzVec2( -42.85,-53.66);
+	bodyVertex[8][2] = bzVec2( -53.59,-52.25);
+	bodyVertex[8][3] = bzVec2( -62.17,-58.84);
+	bodyVertex[8][4] = bzVec2( -63.59,-69.57);
+	bodyVertex[8][5] = bzVec2( -57.00,-78.16);
+	bodyVertex[8][6] = bzVec2( -46.26,-79.57);
+	bodyVertex[8][7] = bzVec2( -37.68,-72.98);
+
+	vertex[9].length = bodyVertex[9].length = 8;
+
+	bodyVertex[9][0] = bzVec2( 53.41, -29.39);
+	bodyVertex[9][1] = bzVec2( 46.67,-37.86);
+	bodyVertex[9][2] = bzVec2( 47.89,-47.61);
+	bodyVertex[9][3] = bzVec2( 56.36, -55.35);
+	bodyVertex[9][4] = bzVec2( 67.12,-54.12);
+	bodyVertex[9][5] = bzVec2( 73.86, -45.65);
+	bodyVertex[9][6] = bzVec2( 72.63,-34.90);
+	bodyVertex[9][7] = bzVec2( 64.16,-28.16);
+
+	vertex[10].length = bodyVertex[10].length = 8;
+
+	bodyVertex[10][0] = bzVec2( 35.97, 54.29);
+	bodyVertex[10][1] = bzVec2( 44.56, 47.70);
+	bodyVertex[10][2] = bzVec2( 56.29, 49.12 );
+	bodyVertex[10][3] = bzVec2( 61.88, 57.70);
+	bodyVertex[10][4] = bzVec2( 60.47, 68.44);
+	bodyVertex[10][5] = bzVec2( 51.88, 75.02);
+	bodyVertex[10][6] = bzVec2( 41.15, 73.61);
+	bodyVertex[10][7] = bzVec2( 34.56, 65.02);
+}
+
+void urQuanDreadnought(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
+{
+	vertex.length = bodyVertex.length = 6;
+
+	vertex[0].length = bodyVertex[0].length = 8;
+	// Head
+	bodyVertex[0][0] = bzVec2(42,49);
+	bodyVertex[0][1] = bzVec2(63,49);
+	bodyVertex[0][2] = bzVec2(70,45.5);
+	bodyVertex[0][3] = bzVec2(73.5,38.5);
+	bodyVertex[0][4] = bzVec2(73.5,-42);
+	bodyVertex[0][5] = bzVec2(70,-49);
+	bodyVertex[0][6] = bzVec2(63,-56);
+	bodyVertex[0][7] = bzVec2(42,-56);
+
+	vertex[1].length = bodyVertex[1].length = 4;
+	// Body
+	bodyVertex[1][0] = bzVec2(-70,-28);
+	bodyVertex[1][1] = bzVec2(-70,24.5);
+	bodyVertex[1][2] = bzVec2(42,24.5);
+	bodyVertex[1][3] = bzVec2(42,-31.5);
+
+	vertex[2].length = bodyVertex[2].length = 4;
+	// Top Strut
+	bodyVertex[2][0] = bzVec2(0,24.5);
+	bodyVertex[2][1] = bzVec2(-28,24.5);
+	bodyVertex[2][2] = bzVec2(-28,42);
+	bodyVertex[2][3] = bzVec2(0,42);
+
+	vertex[3].length = bodyVertex[3].length = 4;
+	// Top Wing
+	bodyVertex[3][0] = bzVec2(-70,42);
+	bodyVertex[3][1] = bzVec2(-49,63);
+	bodyVertex[3][2] = bzVec2(28,63);
+	bodyVertex[3][3] = bzVec2(28,42);
+
+	vertex[4].length = bodyVertex[4].length = 4;
+	// Bottom Strut
+	bodyVertex[4][0] = bzVec2(0,-31.5);
+	bodyVertex[4][1] = bzVec2(0,-49);
+	bodyVertex[4][2] = bzVec2(-28,-49);
+	bodyVertex[4][3] = bzVec2(-28,-31.5);
+
+	vertex[5].length = bodyVertex[5].length = 4;
+	// Bottom Wing
+	bodyVertex[5][0] = bzVec2(-70,-49);
+	bodyVertex[5][1] = bzVec2(28,-49);
+	bodyVertex[5][2] = bzVec2(28,-70);
+	bodyVertex[5][3] = bzVec2(-42,-70);
+}
+
+void yehatTerminator(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
+{
+	vertex.length = bodyVertex.length = 4;
+	vertex[0].length = bodyVertex[0].length = 3;
+	// Cockpit
+	bodyVertex[0][0] = bzVec2(-14,21);
+	bodyVertex[0][1] = bzVec2(0,42);
+	bodyVertex[0][2] = bzVec2(14,21);
+
+
+	vertex[1].length = bodyVertex[1].length = 4;
+	// Body
+	bodyVertex[1][0] = bzVec2(-14,21);
+	bodyVertex[1][1] = bzVec2(-14,-21);
+	bodyVertex[1][2] = bzVec2(14,-21);
+	bodyVertex[1][3] = bzVec2(14,21);
+
+	vertex[2].length = bodyVertex[2].length = 4;
+	// Right Wing
+	bodyVertex[2][0] = bzVec2(14,21);
+	bodyVertex[2][1] = bzVec2(14,-21);
+	bodyVertex[2][2] = bzVec2(70,0);
+	bodyVertex[2][3] = bzVec2(84,59.5);
+
+	vertex[3].length = bodyVertex[3].length = 4;
+	// Left Wing
+	bodyVertex[3][0] = bzVec2(-14,21);
+	bodyVertex[3][1] = bzVec2(-14,-21);
+	bodyVertex[3][2] = bzVec2(-70,0);
+	bodyVertex[3][3] = bzVec2(-84,59.5);
+}
+
+void orzNemesis(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
+{
+	vertex.length = bodyVertex.length = 3;
+	vertex[0].length = bodyVertex[0].length = 4;
+	// Body
+	bodyVertex[0][0] = bzVec2(-28,21);
+	bodyVertex[0][1] = bzVec2(-28,-28);
+	bodyVertex[0][2] = bzVec2(42,-21);
+	bodyVertex[0][3] = bzVec2(42,14);
+
+	vertex[1].length = bodyVertex[1].length = 5;
+	// Top Wing
+	bodyVertex[1][0] = bzVec2(-28,21);
+	bodyVertex[1][1] = bzVec2(-70,63);
+	bodyVertex[1][2] = bzVec2(-49,63);
+	bodyVertex[1][3] = bzVec2(70,14);
+	bodyVertex[1][4] = bzVec2(42,14);
+
+	vertex[2].length = bodyVertex[2].length = 5;
+	// Bottom Wing
+	bodyVertex[2][0] = bzVec2(-28,-28);
+	bodyVertex[2][1] = bzVec2(-70,-63);
+	bodyVertex[2][2] = bzVec2(-49,-63);
+	bodyVertex[2][3] = bzVec2(70,-21);
+	bodyVertex[2][4] = bzVec2(42,-21);
+}
+
+void triangle(inout bzVec2[][] bodyVertex, inout bzVec2[][] vertex)
+{
+
+	vertex.length = bodyVertex.length = 1;
+	vertex[0].length = bodyVertex[0].length = 3;
+
+	bodyVertex[0][0] = bzVec2(0.0f,7.0f);
+	bodyVertex[0][1] = bzVec2(7.0f,-7.0f);
+	bodyVertex[0][2] = bzVec2(-7.0f,-7.0f);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ships/orz.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ships.orz;
+
+import blaze.dynamics.bzBody : bzBody;
+import blaze.bzWorld: bzWorld;
+import blaze.dynamics.bzBodyDef;
+import blaze.collision.shapes.bzPolygon : bzPolyDef;
+import blaze.common.bzMath: bzVec2, PI;
+
+import openmelee.ships.ship;
+
+// UrQuan Dreadnought
+class Orz : Ship
+{
+
+    float scale = 0.025;
+
+    this(bzWorld world) {
+        
+        super(world);
+        engineForce = bzVec2(500, 0);
+        turnForce = bzVec2(0, 5000);
+        rightTurnPoint = bzVec2(-0.5, 0);
+        leftTurnPoint = bzVec2(0.5, 0);
+
+        auto bodyDef = new bzBodyDef;
+        //bodyDef.isBullet = true;
+        bodyDef.position = bzVec2(20,15);
+        bodyDef.angle = PI/2;
+        bodyDef.allowFreeze = false;
+
+        rBody = world.createBody(bodyDef);
+        float density = 5.0f;
+
+        // Body
+        auto b = new bzPolyDef(density);
+        b.vertices.length = 4;
+        b.vertices[0] = bzVec2(42,14) * scale;
+        b.vertices[1] = bzVec2(-28,21) * scale;
+        b.vertices[2] = bzVec2(-28,-28) * scale;
+        b.vertices[3] = bzVec2(42,-21) * scale;
+        shapes.add(rBody.createShape(b));
+
+        // Top Wing
+        auto tWing = new bzPolyDef(density);
+        tWing.vertices.length = 5;
+        tWing.vertices[4] = bzVec2(-28,21) * scale;
+        tWing.vertices[3] = bzVec2(-70,63) * scale;
+        tWing.vertices[2] = bzVec2(-49,63) * scale;
+        tWing.vertices[1] = bzVec2(70,14) * scale;
+        tWing.vertices[0] = bzVec2(42,14) * scale;
+        shapes.add(rBody.createShape(tWing));
+
+        // Bottom Wing
+        auto bWing = new bzPolyDef(density);
+        bWing.vertices.length = 5;
+        bWing.vertices[0] = bzVec2(-28,-28) * scale;
+        bWing.vertices[1] = bzVec2(-70,-63) * scale;
+        bWing.vertices[2] = bzVec2(-49,-63) * scale;
+        bWing.vertices[3] = bzVec2(70,-21) * scale;
+        bWing.vertices[4] = bzVec2(42,-21) * scale;
+        shapes.add(rBody.createShape(bWing));
+
+        rBody.setMassFromShapes();
+        setPlanetGravity();
+      }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ships/planet.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ships.planet;
+
+import blaze.dynamics.bzBody : bzBody;
+import blaze.bzWorld: bzWorld;
+import blaze.dynamics.bzBodyDef;
+import blaze.collision.shapes.bzCircle : bzCircleDef;
+import blaze.common.bzMath: bzVec2;
+
+class Planet 
+{
+    
+    bzWorld world;
+    
+    this(bzWorld world) {
+        this.world = world;
+        init();
+    }
+    
+    void init() {
+        // Create planet
+        bzVec2 position = bzVec2.zeroVect;
+        float angle = 0.0f;
+        auto bd = new bzBodyDef(position, angle);
+        auto rBody = world.createBody(bd);
+        float radius = 7.0f;
+        float density = 7.5f;
+        auto sd = new bzCircleDef(density, radius);
+        float friction = 1.0f;
+        float restitution = 0.1f;
+        sd.friction = friction;
+        sd.restitution = restitution;
+        rBody.createShape(sd);   
+    }    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ships/ship.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ships.ship;
+
+import tango.io.Stdout: Stdout;
+
+import tango.util.container.LinkedList : LinkedList;
+import blaze.dynamics.bzBody : bzBody;
+import blaze.collision.shapes.bzShape : bzShape;
+import blaze.common.bzMath: bzVec2, bzCross, bzClamp;
+import blaze.bzWorld : bzWorld;
+import blaze.dynamics.forces.bzAttractor: bzAttractor;
+
+alias LinkedList!(bzShape) ShapeList;
+
+struct State 
+{
+	bzVec2 position;
+    bzVec2 velocity;
+    bzVec2 up;
+    bzVec2 side;
+    bzVec2 forward;
+    bzVec2 target;
+	float speed = 0;
+	float maxForce = 0;
+	bool turn;
+	
+	float enemyAngle;
+	
+	bzVec2 predictFuturePosition(float dt) {
+	    return (position + velocity * dt);
+    }
+}
+
+abstract class Ship
+{
+    bzWorld world;
+    bzBody rBody;
+    ShapeList shapes;
+    bzVec2 engineForce;
+    bzVec2 turnForce;
+    bzVec2 leftTurnPoint;
+    bzVec2 rightTurnPoint;
+	State state;
+	
+    float battery;
+    float crew;
+    
+    float maxLinVel = 50;
+    float maxAngVel = 2;
+    
+    this(bzWorld world) {
+        
+        this.world = world;
+        shapes = new ShapeList;
+    }
+
+    void thrust() {
+        rBody.force += engineForce.rotate(rBody.angle);
+    }
+
+    void turnLeft() {
+        
+        rBody.torque += bzCross(leftTurnPoint.rotate(rBody.angle), 
+                                turnForce.rotate(rBody.angle));
+    }
+    
+    void turnRight() {
+        
+        rBody.torque += bzCross(rightTurnPoint.rotate(rBody.angle), 
+                                turnForce.rotate(rBody.angle));
+    }
+    
+    void setPlanetGravity() {
+        float minRadius = 0.1;
+        float maxRadius = 10;
+        float strength = 4.0;
+        bzVec2 center = bzVec2(0,0);
+        auto attractor = new bzAttractor(rBody, center, strength, minRadius, maxRadius);
+        world.addForce(attractor);
+    }
+    
+    void limitVelocity() {
+        float vx = rBody.linearVelocity.x;
+        float vy = rBody.linearVelocity.y;
+        float av = rBody.angularVelocity;
+        rBody.linearVelocity.x = bzClamp(vx, -maxLinVel, maxLinVel);
+        rBody.linearVelocity.y = bzClamp(vy, -maxLinVel, maxLinVel);
+        rBody.angularVelocity = bzClamp(av, -maxAngVel, maxAngVel);
+    }
+    
+    void updateState() {
+    	state.velocity = rBody.linearVelocity;
+    	state.speed = state.velocity.length;
+    	state.position = rBody.position;
+    	state.forward = engineForce.rotate(rBody.angle);
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ships/urQuan.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009, Mason Green (zzzzrrr)
+ * http://www.dsource.org/projects/openmelee
+ * 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the polygonal nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+module openmelee.ships.urQuan;
+
+import blaze.dynamics.bzBody : bzBody;
+import blaze.bzWorld: bzWorld;
+import blaze.dynamics.bzBodyDef;
+import blaze.collision.shapes.bzPolygon : bzPolyDef;
+import blaze.common.bzMath: bzVec2;
+
+import openmelee.ships.ship;
+
+// UrQuan Dreadnought
+class UrQuan : Ship
+{
+
+    float scale = 0.025;
+
+    this(bzWorld world) {
+
+        super(world);
+        engineForce = bzVec2(500, 0);
+        turnForce = bzVec2(0, 9000);
+        rightTurnPoint = bzVec2(-0.5, 0);
+        leftTurnPoint = bzVec2(0.5, 0);
+        
+        auto bodyDef = new bzBodyDef;
+        //bodyDef.isBullet = true;
+        bodyDef.position = bzVec2(30,5);
+        bodyDef.allowFreeze = false;
+        rBody = world.createBody(bodyDef);
+        float density = 5.0f;
+
+        // Head
+        auto head = new bzPolyDef;
+        head.vertices.length = 8;
+        head.vertices[0] = bzVec2(42,49) * scale;
+        head.vertices[7] = bzVec2(63,49) * scale;
+        head.vertices[6] = bzVec2(70,45.5) * scale;
+        head.vertices[5] = bzVec2(73.5,38.5) * scale;
+        head.vertices[4] = bzVec2(73.5,-42) * scale;
+        head.vertices[3] = bzVec2(70,-49) * scale;
+        head.vertices[2] = bzVec2(63,-56) * scale;
+        head.vertices[1] = bzVec2(42,-56) * scale;
+        shapes.add(rBody.createShape(head));
+
+        // Body
+        auto b = new bzPolyDef(density);
+        b.vertices.length = 4;
+        b.vertices[0] = bzVec2(-70,-28) * scale;
+        b.vertices[3] = bzVec2(-70,24.5) * scale;
+        b.vertices[2] = bzVec2(42,24.5) * scale;
+        b.vertices[1] = bzVec2(42,-31.5) * scale;
+        shapes.add(rBody.createShape(b));
+
+        // Top Strut
+        auto tStrut = new bzPolyDef(density);
+        tStrut.vertices.length = 4;
+        tStrut.vertices[0] = bzVec2(0,24.5) * scale;
+        tStrut.vertices[3] = bzVec2(-28,24.5) * scale;
+        tStrut.vertices[2] = bzVec2(-28,42) * scale;
+        tStrut.vertices[1] = bzVec2(0,42) * scale;
+        shapes.add(rBody.createShape(tStrut));
+
+        // Top Wing
+        auto tWing = new bzPolyDef(density);
+        tWing.vertices.length = 4;
+        tWing.vertices[0] = bzVec2(-70,42) * scale;
+        tWing.vertices[3] = bzVec2(-49,63) * scale;
+        tWing.vertices[2] = bzVec2(28,63) * scale;
+        tWing.vertices[1] = bzVec2(28,42) * scale;
+        shapes.add(rBody.createShape(tWing));
+
+        // Bottom Strut
+        auto bStrut = new bzPolyDef(density);
+        bStrut.vertices.length = 4;
+        bStrut.vertices[0] = bzVec2(0,-31.5) * scale;
+        bStrut.vertices[3] = bzVec2(0,-49) * scale;
+        bStrut.vertices[2] = bzVec2(-28,-49) * scale;
+        bStrut.vertices[1] = bzVec2(-28,-31.5) * scale;
+        shapes.add(rBody.createShape(bStrut));
+
+        // Bottom Wing
+        auto bWing = new bzPolyDef(density);
+        bWing.vertices.length = 4;
+        bWing.vertices[0] = bzVec2(-70,-49) * scale;
+        bWing.vertices[3] = bzVec2(28,-49) * scale;
+        bWing.vertices[2] = bzVec2(28,-70) * scale;
+        bWing.vertices[1] = bzVec2(-42,-70) * scale;
+        shapes.add(rBody.createShape(bWing));
+
+        rBody.setMassFromShapes();
+        setPlanetGravity();
+    }
+}
--- a/steer.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,473 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * Based on OpenSteer, Copyright (c) 2002-2003, Sony Computer Entertainment America
- * Original author: Craig Reynolds
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.steer;
-
-import blaze.common.bzMath: bzDot, bzClamp, bzVec2;
-import blaze.dynamics.bzBody: bzBody;
-
-import openmelee.ship : Ship, State;
-import openmelee.utilities;
-
-class Steer 
-{
-    // Constructor: initializes state
-    this (Ship ship)
-    {
-        m_ship = ship;
-    }
-    
-	struct PathIntersection
-    {
-        bool intersect;
-        float distance;
-        bzVec2 surfacePoint;
-        bzVec2 surfaceNormal;
-        bzBody obstacle;
-    }
-
-    // reset state
-    void reset () {
-        // initial state of wander behavior
-        m_wanderSide = 0;
-        m_wanderUp = 0;
-    }
-    
-    void update() {
-        m_position = m_ship.state.position;
-        m_velocity = m_ship.state.velocity;
-        m_speed = m_ship.state.speed;
-        m_maxForce = m_ship.state.maxForce;
-        m_forward = m_ship.state.forward;
-    }
-
-    // -------------------------------------------------- steering behaviors
-
-    bzVec2 steerForWander (float dt) {
-        // random walk m_wanderSide and m_wanderUp between -1 and +1
-        float speed = 12 * dt; // maybe this (12) should be an argument?
-        m_wanderSide = scalarRandomWalk (m_wanderSide, speed, -1, +1);
-        m_wanderUp   = scalarRandomWalk (m_wanderUp,   speed, -1, +1);
-
-        // return a pure lateral steering vector: (+/-Side) + (+/-Up)
-        return (m_side * m_wanderSide) + (m_up * m_wanderUp);
-    }
-
-    // Seek behavior
-    bzVec2 steerForSeek (bzVec2 target) {
-        bzVec2 desiredVelocity = target - m_position;
-        return desiredVelocity - m_velocity;
-    }
-
-    // Flee behavior
-    bzVec2 steerForFlee (bzVec2 target) {
-        bzVec2 desiredVelocity = m_position - target;
-        return desiredVelocity - m_velocity;
-    }
-
-    /*
-    // xxx proposed, experimental new seek/flee [cwr 9-16-02]
-    bzVec2 xxxsteerForFlee (bzVec2 target) {
-        bzVec2 offset = m_position - target;
-        bzVec2 desiredVelocity = bzClamp(offset.truncateLength (maxSpeed ());
-        return desiredVelocity - m_velocity;
-    }
-
-    bzVec2 xxxsteerForSeek (bzVec2 target) {
-        //  bzVec2 offset = target - position;
-        bzVec2 offset = target - m_position;
-        bzVec2 desiredVelocity = offset.truncateLength (maxSpeed ()); //xxxnew
-        return desiredVelocity - m_velocity;
-    }
-    */
-
-	/*
-    // ------------------------------------------------------------------------
-    // Obstacle Avoidance behavior
-    //
-    // Returns a steering force to avoid a given obstacle.  The purely
-    // lateral steering force will turn our vehicle towards a silhouette edge
-    // of the obstacle.  Avoidance is required when (1) the obstacle
-    // intersects the vehicle's current path, (2) it is in front of the
-    // vehicle, and (3) is within minTimeToCollision seconds of travel at the
-    // vehicle's current velocity.  Returns a zero vector value (bzVec2::zero)
-    // when no avoidance is required.
-    bzVec2 steerToAvoidObstacle (float minTimeToCollision, Obstacle obstacle) {
-
-        bzVec2 avoidance = obstacle.steerToAvoid (this, minTimeToCollision);
-        return avoidance;
-    }
-
-    // avoids all obstacles in an ObstacleGroup
-    */
-    
-    bzVec2 steerToAvoidObstacles (float minTimeToCollision, bzBody obstacles) {
-
-        bzVec2 avoidance;
-        PathIntersection nearest, next;
-        float minDistanceToCollision = minTimeToCollision * m_speed;
-
-        next.intersect = false;
-        nearest.intersect = false;
-
-        // test all obstacles for intersection with my forward axis,
-        // select the one whose point of intersection is nearest
-        for (bzBody o; o; o = o.next) {
-            // This code which presumes the obstacle is spherical
-            //findNextIntersectionWithSphere (o, next);
-
-            if (!nearest.intersect || (next.intersect && next.distance < nearest.distance)) {
-					nearest = next;
-            }
-        }
-
-        // when a nearest intersection was found
-        if (nearest.intersect && (nearest.distance < minDistanceToCollision)) {
-            // compute avoidance steering force: take offset from obstacle to me,
-            // take the component of that which is lateral (perpendicular to my
-            // forward direction), set length to maxForce, add a bit of forward
-            // component (in capture the flag, we never want to slow down)
-            bzVec2 offset = m_position - nearest.obstacle.position;
-            //avoidance = Vector3Helpers.PerpendicularComponent(offset, this.Forward);
-            avoidance.normalize;
-            avoidance *= m_maxForce;
-            avoidance += m_forward * m_maxForce * 0.75f;
-        }
-
-        return avoidance;
-    }
-
-    /*
-    // ------------------------------------------------------------------------
-    // Unaligned collision avoidance behavior: avoid colliding with other
-    // nearby vehicles moving in unconstrained directions.  Determine which
-    // (if any) other other vehicle we would collide with first, then steers
-    // to avoid the site of that potential collision.  Returns a steering
-    // force vector, which is zero length if there is no impending collision.
-
-    bzVec2 steerToAvoidNeighbors (float minTimeToCollision, AVGroup others) {
-
-        // first priority is to prevent immediate interpenetration
-        bzVec2 separation = steerToAvoidCloseNeighbors (0, others);
-        if (separation != bzVec2::zero) return separation;
-
-        // otherwise, go on to consider potential future collisions
-        float steer = 0;
-        Ship threat;
-
-        // Time (in seconds) until the most immediate collision threat found
-        // so far.  Initial value is a threshold: don't look more than this
-        // many frames into the future.
-        float minTime = minTimeToCollision;
-
-        // xxx solely for annotation
-        bzVec2 xxxThreatPositionAtNearestApproach;
-        bzVec2 xxxOurPositionAtNearestApproach;
-
-        // for each of the other vehicles, determine which (if any)
-        // pose the most immediate threat of collision.
-        for (AVIterator i = others.begin(); i != others.end(); i++)
-        {
-            Ship other = i;
-            if (other !is this)
-            {
-                // avoid when future positions are this close (or less)
-                float collisionDangerThreshold = radius * 2;
-
-                // predicted time until nearest approach of "this" and "other"
-                float time = predictNearestApproachTime (other);
-
-                // If the time is in the future, sooner than any other
-                // threatened collision...
-                if ((time >= 0)  (time < minTime))
-                {
-                    // if the two will be close enough to collide,
-                    // make a note of it
-                    if (computeNearestApproachPositions (other, time)
-                        < collisionDangerThreshold)
-                    {
-                        minTime = time;
-                        threat = other;
-                        xxxThreatPositionAtNearestApproach
-                            = hisPositionAtNearestApproach;
-                        xxxOurPositionAtNearestApproach
-                            = ourPositionAtNearestApproach;
-                    }
-                }
-            }
-        }
-
-        // if a potential collision was found, compute steering to avoid
-        if (threat)
-        {
-            // parallel: +1, perpendicular: 0, anti-parallel: -1
-            float parallelness = m_forward.dot(threat.forward);
-            float angle = 0.707f;
-
-            if (parallelness < -angle)
-            {
-                // anti-parallel "head on" paths:
-                // steer away from future threat position
-                bzVec2 offset = xxxThreatPositionAtNearestApproach - m_position;
-                float sideDot = offset.dot(m_side());
-                steer = (sideDot > 0) ? -1.0f : 1.0f;
-            }
-            else
-            {
-                if (parallelness > angle)
-                {
-                    // parallel paths: steer away from threat
-                    bzVec2 offset = threat.position - m_position;
-                    float sideDot = bzDot(offset, m_side);
-                    steer = (sideDot > 0) ? -1.0f : 1.0f;
-                }
-                else
-                {
-                    // perpendicular paths: steer behind threat
-                    // (only the slower of the two does this)
-                    if (threat.speed() <= speed)
-                    {
-                        float sideDot = bzDot(m_side, threat.velocity);
-                        steer = (sideDot > 0) ? -1.0f : 1.0f;
-                    }
-                }
-            }
-        }
-
-        return m_side() * steer;
-    }
-	*/
-	
-    // Given two vehicles, based on their current positions and velocities,
-    // determine the time until nearest approach
-    float predictNearestApproachTime (State other) {
-
-        // imagine we are at the origin with no velocity,
-        // compute the relative velocity of the other vehicle
-        bzVec2 myVelocity = m_velocity;
-        bzVec2 otherVelocity = other.velocity;
-        bzVec2 relVelocity = otherVelocity - myVelocity;
-        float relSpeed = relVelocity.length;
-
-        // for parallel paths, the vehicles will always be at the same distance,
-        // so return 0 (aka "now") since "there is no time like the present"
-        if (relSpeed == 0) return 0;
-
-        // Now consider the path of the other vehicle in this relative
-        // space, a line defined by the relative position and velocity.
-        // The distance from the origin (our vehicle) to that line is
-        // the nearest approach.
-
-        // Take the unit tangent along the other vehicle's path
-        bzVec2 relTangent = relVelocity / relSpeed;
-
-        // find distance from its path to origin (compute offset from
-        // other to us, find length of projection onto path)
-        bzVec2 relPosition = m_position - other.position;
-        float projection = bzDot(relTangent, relPosition);
-
-        return projection / relSpeed;
-    }
-
-    // Given the time until nearest approach (predictNearestApproachTime)
-    // determine position of each vehicle at that time, and the distance
-    // between them
-    float computeNearestApproachPositions (State other, float time) {
-
-        bzVec2 myTravel =  m_forward *  m_speed * time;
-        bzVec2 otherTravel = other.forward * other.speed * time;
-
-        bzVec2 myFinal =  m_position + myTravel;
-        bzVec2 otherFinal = other.position + otherTravel;
-
-        return (myFinal - otherFinal).length;
-    }
-
-    // ------------------------------------------------------------------------
-    // pursuit of another vehicle ( version with ceiling on prediction time)
-
-    bzVec2 steerForPursuit (State quarry) {
-        return steerForPursuit (quarry, float.max);
-    }
-
-    bzVec2 steerForPursuit (State quarry, float maxPredictionTime) {
-
-        // offset from this to quarry, that distance, unit vector toward quarry
-        bzVec2 offset = quarry.position - m_position;
-        float distance = offset.length;
-        bzVec2 unitOffset = offset / distance;
-
-        // how parallel are the paths of "this" and the quarry
-        // (1 means parallel, 0 is pependicular, -1 is anti-parallel)
-        float parallelness = bzDot(m_forward , quarry.forward);
-
-        // how "forward" is the direction to the quarry
-        // (1 means dead ahead, 0 is directly to the side, -1 is straight back)
-        float forwardness = bzDot(m_forward , unitOffset);
-
-        float directTravelTime = distance / m_speed;
-        int f = intervalComparison (forwardness,  -0.707f, 0.707f);
-        int p = intervalComparison (parallelness, -0.707f, 0.707f);
-
-        float timeFactor = 0; // to be filled in below
-
-        // Break the pursuit into nine cases, the cross product of the
-        // quarry being [ahead, aside, or behind] us and heading
-        // [parallel, perpendicular, or anti-parallel] to us.
-        switch (f)
-        {
-        case +1:
-            switch (p)
-            {
-            case +1:          // ahead, parallel
-                timeFactor = 4;
-                break;
-            case 0:           // ahead, perpendicular
-                timeFactor = 1.8f;
-                break;
-            case -1:          // ahead, anti-parallel
-                timeFactor = 0.85f;
-                break;
-            }
-            break;
-        case 0:
-            switch (p)
-            {
-            case +1:          // aside, parallel
-                timeFactor = 1;
-                break;
-            case 0:           // aside, perpendicular
-                timeFactor = 0.8f;
-                break;
-            case -1:          // aside, anti-parallel
-                timeFactor = 4;
-                break;
-            }
-            break;
-        case -1:
-            switch (p)
-            {
-            case +1:          // behind, parallel
-                timeFactor = 0.5f;
-                break;
-            case 0:           // behind, perpendicular
-                timeFactor = 2;
-                break;
-            case -1:          // behind, anti-parallel
-                timeFactor = 2;
-                break;
-            }
-            break;
-        }
-
-        // estimated time until intercept of quarry
-        float et = directTravelTime * timeFactor;
-
-        // xxx experiment, if kept, this limit should be an argument
-        float etl = (et > maxPredictionTime) ? maxPredictionTime : et;
-
-        // estimated position of quarry at intercept
-        bzVec2 target = quarry.predictFuturePosition(etl);
-
-        return target; //steerForSeek (target);
-    }
-
-    // ------------------------------------------------------------------------
-    // evasion of another vehicle
-    bzVec2 steerForEvasion (State menace,  float maxPredictionTime)  {
-
-        // offset from this to menace, that distance, unit vector toward menace
-        bzVec2 offset = menace.position - m_position;
-        float distance = offset.length;
-
-        float roughTime = distance / menace.speed;
-        float predictionTime = ((roughTime > maxPredictionTime) ? maxPredictionTime : roughTime);
-        bzVec2 target = menace.predictFuturePosition (predictionTime);
-
-        return steerForFlee (target);
-    }
-
-
-    // ------------------------------------------------------------------------
-    // tries to maintain a given speed, returns a maxForce-clipped steering
-    // force along the forward/backward axis
-    bzVec2 steerForTargetSpeed (float targetSpeed) {
-        float mf = m_maxForce;
-        float speedError = targetSpeed - m_speed;
-        return m_forward * bzClamp(speedError, -mf, +mf);
-    }
-
-
-    // ----------------------------------------------------------- utilities
-    bool isAhead (bzVec2 target) {return isAhead (target, 0.707f);};
-    bool isAside (bzVec2 target) {return isAside (target, 0.707f);};
-    bool isBehind (bzVec2 target) {return isBehind (target, -0.707f);};
-
-    bool isAhead (bzVec2 target, float cosThreshold)
-    {
-        bzVec2 targetDirection = target - m_position;
-        targetDirection.normalize();
-        return bzDot(m_forward, targetDirection) > cosThreshold;
-    }
-
-    bool isAside (bzVec2 target, float cosThreshold)
-    {
-        bzVec2 targetDirection = target - m_position;
-        targetDirection.normalize();
-        float dp = bzDot(m_forward, targetDirection);
-        return (dp < cosThreshold) && (dp > -cosThreshold);
-    }
-
-    bool isBehind (bzVec2 target, float cosThreshold)
-    {
-        bzVec2 targetDirection = target - m_position;
-        targetDirection.normalize();
-        return bzDot(m_forward, targetDirection) < cosThreshold;
-    }
-    
-    private:
-    
-    Ship m_ship;
-    
-    bzVec2 m_position;
-    bzVec2 m_velocity;
-    bzVec2 m_up;
-    bzVec2 m_side;
-    bzVec2 m_forward;
-	
-	float m_speed = 0;
-	float m_maxForce = 0;
-    
-    // Wander behavior
-    float m_wanderSide;
-    float m_wanderUp;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/example.d	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,80 @@
+// jake -I../minid -I.. -version=old example.d
+module example;
+
+import xf.xpose2.Expose;
+import xf.xpose2.MiniD;
+import tango.text.convert.Format;
+
+struct vec3 {
+	float x, y, z;
+	
+	char[] toString() {
+		return Format("x={} y={} z={}", x, y, z);
+	}
+	
+	mixin(xpose2(`
+		.*
+		toString
+		_ctor overload vec3 function(float, float, float)
+	`));
+	mixin xposeMiniD;
+}
+
+class Poop {
+	int x = 0;
+	
+	this() {
+		this.x = 123456;
+	}
+	
+	this(int x) {
+		this.x = x;
+	}
+	
+	this (char[] zomg) {
+		this.x = zomg.length;
+	}
+	
+	int max(int a, int b) {
+		return a > b ? a : b;
+	}
+	
+	int dmax(int a, int b) {
+		return max(max(a, b), x);
+	}
+	
+	int dmax(int a, int b, int c) {
+		return dmax(a, dmax(b, c));
+	}
+	
+	vec3 getVec3(int x, int y, int z) {
+		return vec3(x, y, z);
+	}
+    
+}
+
+mixin(xpose2(`Poop`,`
+	dmax
+	dmax overload int function(int, int, int) ; md { rename "dmax2" }
+	getVec3
+	.*
+	_ctor
+	_ctor overload Poop function(int)
+	_ctor overload Poop function(char[])
+`));
+
+mixin xposeMiniD!(`Poop`);
+
+void main(char[][] args)
+{
+	MDVM vm;
+	auto t = openVM(&vm);
+	loadStdlibs(t);
+    xposeMiniD_initAll(t);
+    superPush(t, new Poop(10));
+    newGlobal(t, "poop");
+    importModule(t, args[1]);
+    lookup(t, "poop");
+	auto poop = superGet!(Poop)(t, -1);
+	Stdout.formatln("poop.dmax(1, 2) == {}", poop.dmax(1, 2));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testScript.md	Wed Mar 25 11:28:25 2009 -0400
@@ -0,0 +1,25 @@
+module testscript
+
+local blah = vec3(1, 2, 30000)
+writeln("created a vec3: ", blah)
+
+writeln("Hello world")
+poop.x = 123
+writeln("poop.dmax2(1, 20, 150) == ", poop.dmax2(1, 20, 150))
+writeln("poop.dmax(1, 20) == ", poop.dmax(1, 20))
+
+writeln("getting a vec3...")
+local v3 = poop.getVec3(3, 6, 9)
+writeln("x = ", v3.x, " y = ", v3.y, " z = ", v3.z)
+
+class Zomg : Poop {
+    /+this() {
+        super(1, 2, 3)
+    }+/
+    
+    function dmax(a : int, b : int) {
+        return 1337
+    }
+}
+
+poop = Zomg()
--- a/testScript.md	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-module testscript
-
-local blah = vec3(1, 2, 30000)
-writeln("created a vec3: ", blah)
-
-writeln("Hello world")
-poop.x = 123
-writeln("poop.dmax2(1, 20, 150) == ", poop.dmax2(1, 20, 150))
-writeln("poop.dmax(1, 20) == ", poop.dmax(1, 20))
-
-writeln("getting a vec3...")
-local v3 = poop.getVec3(3, 6, 9)
-writeln("x = ", v3.x, " y = ", v3.y, " z = ", v3.z)
-
-class Zomg : Poop {
-    /+this() {
-        super(1, 2, 3)
-    }+/
-    
-    function dmax(a : int, b : int) {
-        return 1337
-    }
-}
-
-poop = Zomg()
--- a/urQuan.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.urQuan;
-
-import blaze.dynamics.bzBody : bzBody;
-import blaze.bzWorld: bzWorld;
-import blaze.dynamics.bzBodyDef;
-import blaze.collision.shapes.bzPolygon : bzPolyDef;
-import blaze.common.bzMath: bzVec2;
-
-import openmelee.ship;
-
-// UrQuan Dreadnought
-class UrQuan : Ship
-{
-
-    float scale = 0.025;
-
-    this(bzWorld world) {
-
-        super(world);
-        engineForce = bzVec2(500, 0);
-        turnForce = bzVec2(0, 9000);
-        rightTurnPoint = bzVec2(-0.5, 0);
-        leftTurnPoint = bzVec2(0.5, 0);
-        
-        auto bodyDef = new bzBodyDef;
-        //bodyDef.isBullet = true;
-        bodyDef.position = bzVec2(30,5);
-        bodyDef.allowFreeze = false;
-        rBody = world.createBody(bodyDef);
-        float density = 5.0f;
-
-        // Head
-        auto head = new bzPolyDef;
-        head.vertices.length = 8;
-        head.vertices[0] = bzVec2(42,49) * scale;
-        head.vertices[7] = bzVec2(63,49) * scale;
-        head.vertices[6] = bzVec2(70,45.5) * scale;
-        head.vertices[5] = bzVec2(73.5,38.5) * scale;
-        head.vertices[4] = bzVec2(73.5,-42) * scale;
-        head.vertices[3] = bzVec2(70,-49) * scale;
-        head.vertices[2] = bzVec2(63,-56) * scale;
-        head.vertices[1] = bzVec2(42,-56) * scale;
-        shapes.add(rBody.createShape(head));
-
-        // Body
-        auto b = new bzPolyDef(density);
-        b.vertices.length = 4;
-        b.vertices[0] = bzVec2(-70,-28) * scale;
-        b.vertices[3] = bzVec2(-70,24.5) * scale;
-        b.vertices[2] = bzVec2(42,24.5) * scale;
-        b.vertices[1] = bzVec2(42,-31.5) * scale;
-        shapes.add(rBody.createShape(b));
-
-        // Top Strut
-        auto tStrut = new bzPolyDef(density);
-        tStrut.vertices.length = 4;
-        tStrut.vertices[0] = bzVec2(0,24.5) * scale;
-        tStrut.vertices[3] = bzVec2(-28,24.5) * scale;
-        tStrut.vertices[2] = bzVec2(-28,42) * scale;
-        tStrut.vertices[1] = bzVec2(0,42) * scale;
-        shapes.add(rBody.createShape(tStrut));
-
-        // Top Wing
-        auto tWing = new bzPolyDef(density);
-        tWing.vertices.length = 4;
-        tWing.vertices[0] = bzVec2(-70,42) * scale;
-        tWing.vertices[3] = bzVec2(-49,63) * scale;
-        tWing.vertices[2] = bzVec2(28,63) * scale;
-        tWing.vertices[1] = bzVec2(28,42) * scale;
-        shapes.add(rBody.createShape(tWing));
-
-        // Bottom Strut
-        auto bStrut = new bzPolyDef(density);
-        bStrut.vertices.length = 4;
-        bStrut.vertices[0] = bzVec2(0,-31.5) * scale;
-        bStrut.vertices[3] = bzVec2(0,-49) * scale;
-        bStrut.vertices[2] = bzVec2(-28,-49) * scale;
-        bStrut.vertices[1] = bzVec2(-28,-31.5) * scale;
-        shapes.add(rBody.createShape(bStrut));
-
-        // Bottom Wing
-        auto bWing = new bzPolyDef(density);
-        bWing.vertices.length = 4;
-        bWing.vertices[0] = bzVec2(-70,-49) * scale;
-        bWing.vertices[3] = bzVec2(28,-49) * scale;
-        bWing.vertices[2] = bzVec2(28,-70) * scale;
-        bWing.vertices[1] = bzVec2(-42,-70) * scale;
-        shapes.add(rBody.createShape(bWing));
-
-        rBody.setMassFromShapes();
-        setPlanetGravity();
-    }
-}
--- a/utilities.d	Tue Mar 24 16:47:42 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2009, Mason Green (zzzzrrr)
- * http://www.dsource.org/projects/openmelee
- * 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * * Neither the name of the polygonal nor the names of its contributors may be
- *   used to endorse or promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-module openmelee.utilities;
- 
-import tango.math.random.Kiss : Kiss;
-
-float scalarRandomWalk(float initial, float walkspeed, float min, float max) {
-	
-	float next = initial + (((randomRange(0, 1) * 2) - 1) * walkspeed);
-	if (next < min) return min;
-	if (next > max) return max;
-	return next;
-}
-
-// ----------------------------------------------------------------------------
-// classify a value relative to the interval between two bounds:
-//     returns -1 when below the lower bound
-//     returns  0 when between the bounds (inside the interval)
-//     returns +1 when above the upper bound
-int intervalComparison(float x, float lowerBound, float upperBound)
-{
-    if (x < lowerBound) return -1;
-    if (x > upperBound) return +1;
-    return 0;
-}
-
-T randomRange(T = int) (T min, T max)
-{
-    return min + Kiss.instance.natural() % (max + 1 - min);
-}