Index: main/Action.java
===================================================================
--- main/Action.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Action.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,8 @@
+package main;
+
+public enum Action {
+  Walking,
+  Standing,
+  Attacking,
+  Dying
+}
Index: main/AreaOfEffect.java
===================================================================
--- main/AreaOfEffect.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/AreaOfEffect.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,44 @@
+package main;
+
+import java.util.Iterator;
+
+public class AreaOfEffect extends Effect {
+
+  private Effect effect;
+  private int range;
+  
+  public AreaOfEffect(Effect effect, int range, TargetType target) {
+    super(EffectType.AreaOfEffect, target);
+    this.effect = effect;
+    this.range = range;
+  }
+  
+  public AreaOfEffect(AreaOfEffect e) {
+    super(e);
+    this.effect = e.effect.copy();
+    this.range = e.range;
+  }
+  
+  public AreaOfEffect copy() {
+    return new AreaOfEffect(this);
+  }
+  
+  public Effect getEffect() {
+    return this.effect;
+  }
+  
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    Map map = LostHavenRPG.map;
+    for (int x = 0; x < map.getLength(); x++) {
+      for (int y = 0; y < map.getHeight(); y++) {
+        Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+        while (iter.hasNext()) {
+          Creature temp = iter.next();
+          if (Point.dist(temp.getLoc(), cr.getLoc()) <= this.range)
+            this.effect.applyEffect(temp); 
+        } 
+      } 
+    } 
+  }
+}
Index: main/ArtifactPoint.java
===================================================================
--- main/ArtifactPoint.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/ArtifactPoint.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,31 @@
+package main;
+
+import java.awt.image.BufferedImage;
+
+public class ArtifactPoint extends Structure {
+
+  private Point target;
+
+  public ArtifactPoint(StructureType type, BufferedImage img) {
+    super(type, img, true);
+    this.target = null;
+  }
+
+  public ArtifactPoint(StructureType type, String imgFile, boolean passable) {
+    super(type, imgFile, passable);
+    this.target = null;
+  }
+
+  public ArtifactPoint(ArtifactPoint copy, Point loc) {
+    super(copy, loc);
+    this.target = copy.target;
+  }
+
+  public Point getTarget() {
+    return this.target;
+  }
+
+  public void setTarget(Point target) {
+    this.target = target;
+  }
+}
Index: main/AttackSpeed.java
===================================================================
--- main/AttackSpeed.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/AttackSpeed.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,41 @@
+package main;
+
+public class AttackSpeed extends TimedEffect {
+
+  private double speedChange;
+  
+  public AttackSpeed(double speedChange, TargetType target, long duration) {
+    super(EffectType.AttackSpeed, target, duration);
+    this.speedChange = speedChange;
+  }
+  
+  public AttackSpeed(AttackSpeed e) {
+    super(e);
+    this.speedChange = e.speedChange;
+  }
+  
+  public AttackSpeed copy() {
+    return new AttackSpeed(this);
+  }
+  
+  public double getSpeedChange() {
+    return this.speedChange;
+  }
+  
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    cr.getWeapon().setAttackSpeed((int)(cr.getWeapon().getAttackSpeed() * this.speedChange));
+    cr.addEffect(this);
+    cr.passive++;
+  }
+  
+  public void cancelEffect(Object o) {
+    Creature cr = (Creature)o;
+    cr.getWeapon().setAttackSpeed((int)(cr.getWeapon().getAttackSpeed() / this.speedChange));
+    cr.passive--;
+  }
+  
+  public String toString() {
+    return Double.toString(this.speedChange);
+  }
+}
Index: main/AttackType.java
===================================================================
--- main/AttackType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/AttackType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,6 @@
+package main;
+
+public enum AttackType {
+  Weapon,
+  Spell
+}
Index: main/Attribute.java
===================================================================
--- main/Attribute.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Attribute.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,8 @@
+package main;
+
+public enum Attribute {
+  Strength,
+  Dexterity,
+  Constitution,
+  Wisdom
+}
Index: main/AuxState.java
===================================================================
--- main/AuxState.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/AuxState.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,5 +2,5 @@
 
 public enum AuxState {
-    None,
-    MsgBox
+  None,
+  MsgBox
 }
Index: main/ChangeDamage.java
===================================================================
--- main/ChangeDamage.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/ChangeDamage.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,43 @@
+package main;
+
+public class ChangeDamage extends TimedEffect {
+
+  private double damageChange;
+  
+  public ChangeDamage(double damageChange, TargetType target, long duration) {
+    super(EffectType.ChangeDamage, target, duration);
+    this.damageChange = damageChange;
+  }
+  
+  public ChangeDamage(ChangeDamage e) {
+    super(e);
+    this.damageChange = e.damageChange;
+  }
+  
+  public ChangeDamage copy() {
+    return new ChangeDamage(this);
+  }
+  
+  public double getDamageChange() {
+    return this.damageChange;
+  }
+  
+  public void setDamageChange(int damageChange) {
+    this.damageChange = damageChange;
+  }
+  
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    EquippedWeapon weap = cr.getWeapon();
+    weap.setDamage((int)(weap.getDamage() * this.damageChange));
+    cr.addEffect(this);
+    cr.passive++;
+  }
+  
+  public void cancelEffect(Object o) {
+    Creature cr = (Creature)o;
+    EquippedWeapon weap = cr.getWeapon();
+    weap.setDamage((int)(weap.getDamage() / this.damageChange));
+    cr.passive--;
+  }
+}
Index: main/Confuse.java
===================================================================
--- main/Confuse.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Confuse.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,25 @@
+package main;
+
+public class Confuse extends TimedEffect {
+
+  public Confuse(TargetType target, long duration) {
+    super(EffectType.Confuse, target, duration);
+  }
+  
+  public Confuse(Confuse e) {
+    super(e);
+  }
+  
+  public Confuse copy() {
+    return new Confuse(this);
+  }
+  
+  public void applyEffect(Object o) {
+    ((Creature)o).addEffect(this);
+    ((Creature)o).confused++;
+  }
+  
+  public void cancelEffect(Object o) {
+    ((Creature)o).confused--;
+  }
+}
Index: main/Creature.java
===================================================================
--- main/Creature.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Creature.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,220 +1,765 @@
 package main;
 
-import java.awt.image.*;
-
-public class Creature {
-	private String name;
-	private CreatureType type;
-	private BufferedImage img;
-	private Gender gender;
-	private int level;
-	private Point loc;
-	private int speed;
-	private long lastMoved;
-	private int attackSpeed;
-	private int damage;
-	private long lastAttacked;
-	private int strength;
-	private int dexterity;
-	private int constitution;
-	private int charisma;
-	private int wisdom;
-	private int intelligence;
-	private int hitpoints;
-	private int manapoints;
-	private int maxHitpoints;
-	private int maxManapoints;
-	
-	public Creature() {
-		name = "";
-		gender = Gender.None;
-		loc = new Point(0, 0);
-	}
-
-	public Creature(String name) {
-		this.name = name;
-		loc = new Point(0, 0);
-	}
-	
-	public Creature(String name, Gender gender) {
-		this.name = name;
-		this.gender = gender;
-		loc = new Point(0, 0);
-	}
-	
-	public int getAttackSpeed() {
-		return attackSpeed;
-	}
-
-	public int getCharisma() {
-		return charisma;
-	}
-
-	public int getConstitution() {
-		return constitution;
-	}
-
-	public int getDamage() {
-		return damage;
-	}
-
-	public int getDexterity() {
-		return dexterity;
-	}
-
-	public Gender getGender() {
-		return gender;
-	}
-
-	public int getHitpoints() {
-		return hitpoints;
-	}
-	
-	public BufferedImage getImg() {
-		return img;
-	}
-
-	public int getIntelligence() {
-		return intelligence;
-	}
-
-	public long getLastAttacked() {
-		return lastAttacked;
-	}
-
-	public long getLastMoved() {
-		return lastMoved;
-	}
-
-	public int getLevel() {
-		return level;
-	}
-
-	public Point getLoc() {
-		return loc;
-	}
-
-	public int getManapoints() {
-		return manapoints;
-	}
-
-	public int getMaxHitpoints() {
-		return maxHitpoints;
-	}
-
-	public int getMaxManapoints() {
-		return maxManapoints;
-	}
-
-	public String getName() {
-		return name;
-	}
-
-	public int getSpeed() {
-		return speed;
-	}
-
-	public int getStrength() {
-		return strength;
-	}
-	
-	public CreatureType getType() {
-		return type;
-	}
-
-	public int getWisdom() {
-		return wisdom;
-	}
-
-	public void setAttackSpeed(int attackSpeed) {
-		this.attackSpeed = attackSpeed;
-	}
-
-	public void setCharisma(int charisma) {
-		this.charisma = charisma;
-	}
-
-	public void setConstitution(int constitution) {
-		this.constitution = constitution;
-	}
-
-	public void setDamage(int damage) {
-		this.damage = damage;
-	}
-
-	public void setDexterity(int dexterity) {
-		this.dexterity = dexterity;
-	}
-
-	public void setGender(Gender gender) {
-		this.gender = gender;
-	}
-
-	public void setHitpoints(int hitpoints) {
-		this.hitpoints = hitpoints;
-	}
-	
-	public void setImg(BufferedImage img) {
-		this.img = img;
-	}
- 
-	public void setIntelligence(int intelligence) {
-		this.intelligence = intelligence;
-	}
-
-	public void setLastAttacked(long lastAttacked) {
-		this.lastAttacked = lastAttacked;
-	}
-
-	public void setLastMoved(long lastMoved) {
-		this.lastMoved = lastMoved;
-	}
-
-	public void setLevel(int level) {
-		this.level = level;
-	}
-
-	public void setLoc(Point loc) {
-		this.loc = loc;
-	}
-
-	public void setManapoints(int manapoints) {
-		this.manapoints = manapoints;
-	}
-
-	public void setMaxHitpoints(int maxHitpoints) {
-		this.maxHitpoints = maxHitpoints;
-	}
-
-	public void setMaxManapoints(int maxManapoints) {
-		this.maxManapoints = maxManapoints;
-	}
-
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	public void setSpeed(int speed) {
-		this.speed = speed;
-	}
-
-	public void setStrength(int strength) {
-		this.strength = strength;
-	}
-	
-	public void setType(CreatureType type) {
-		this.type = type;
-	}
-
-	public void setWisdom(int wisdom) {
-		this.wisdom = wisdom;
-	}
-	
-	public String toString() {
-		return name;
-	}
-	
-	public boolean equals(Object c) {
-		return name.trim().equals(((Creature)c).name.trim());
-	}
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.*;
+import javax.imageio.ImageIO;
+
+import gamegui.Animation;
+
+public class Creature implements Comparable<Creature> {
+
+  public int passive;
+  public int thorns;
+  public int confused;
+  public int sand;
+
+  protected String name;
+  protected CreatureType type;
+  protected Model model;
+  protected int level;
+  protected Point loc;
+  protected int[] attributes;  
+  protected int hitpoints;
+  protected int manapoints;
+  protected int maxHitpoints;
+  protected int maxManapoints;
+  protected int experience;
+  protected EquippedWeapon weapon;
+  protected EquippedWeapon spell;
+
+  private Point target;
+  private Creature enemyTarget;
+  private int speed;
+  private long lastMoved;
+  private long lastAttacked;
+  private LinkedList<ItemDrop> drops;
+  private LinkedList<EffectTimer> effectTimers;
+  private boolean disabled;
+  private SpawnPoint spawnPoint;
+  private boolean dying;
+
+  public Creature() {
+    this.name = "";
+    this.loc = new Point(0, 0);
+    this.target = this.loc;
+    this.attributes = new int[4];
+    this.experience = 0;
+    this.weapon = null;
+    this.spell = null;
+    this.drops = new LinkedList<ItemDrop>();
+    this.effectTimers = new LinkedList<EffectTimer>();
+    this.disabled = false;
+    this.spawnPoint = null;
+    this.passive = 0;
+    this.thorns = 0;
+    this.confused = 0;
+    this.sand = 0;
+    this.dying = false;
+  }
+
+  public Creature(String name, CreatureType type) {
+    this.name = name;
+    this.type = type;
+    this.loc = new Point(0, 0);
+    this.target = this.loc;
+    this.attributes = new int[4];
+    this.experience = 0;
+    this.weapon = null;
+    this.spell = null;
+    this.drops = new LinkedList<ItemDrop>();
+    this.effectTimers = new LinkedList<EffectTimer>();
+    this.disabled = false;
+    this.spawnPoint = null;
+    this.passive = 0;
+    this.thorns = 0;
+    this.confused = 0;
+    this.sand = 0;
+    this.dying = false;
+  }
+
+  public Creature(Creature cr) {
+    this.name = cr.name;
+    this.type = cr.type;
+    this.model = new Model(cr.model);
+    this.level = cr.level;
+    this.loc = new Point(cr.loc);
+    this.target = cr.target;
+    this.speed = cr.speed;
+    this.lastMoved = cr.lastMoved;
+    this.lastAttacked = cr.lastAttacked;
+    this.attributes = (int[])cr.attributes.clone();
+    this.hitpoints = cr.hitpoints;
+    this.manapoints = cr.manapoints;
+    this.maxHitpoints = cr.maxHitpoints;
+    this.maxManapoints = cr.maxManapoints;
+    this.experience = cr.experience;
+    this.weapon = cr.weapon.copy((Point)null);
+    if (cr.spell != null)
+      this.spell = cr.spell.copy((Point)null); 
+    this.drops = cr.drops;
+    this.effectTimers = new LinkedList<EffectTimer>(cr.effectTimers);
+    this.disabled = cr.disabled;
+    this.spawnPoint = null;
+    this.passive = cr.passive;
+    this.thorns = cr.thorns;
+    this.confused = cr.confused;
+    this.sand = cr.sand;
+    this.dying = cr.dying;
+  }
+
+  public Creature copy() {
+    return new Creature(this);
+  }
+
+  public void setDying(boolean dying) {
+    if (!this.model.hasDeath())
+      return; 
+    if (dying) {
+      this.model.setAction(Action.Dying);
+    } else {
+      this.model.getAnimation(Direction.West, Action.Dying).reset();
+    }
+    this.dying = dying;
+  }
+
+  public boolean isDying() {
+    return this.dying;
+  }
+
+  public void checkTimedEffects() {
+    Iterator<EffectTimer> iter = this.effectTimers.iterator();
+    while (iter.hasNext()) {
+      EffectTimer cur = iter.next();
+      if (cur.expired()) {
+        cur.getEffect().cancelEffect(this);
+        iter.remove();
+      }
+    }
+  }
+
+  public void addEffect(TimedEffect e) {
+    this.effectTimers.add(new EffectTimer(e, e.getDuration()));
+  }
+
+  public Point move() {
+    Point newLoc;
+    double dist = (this.speed * (System.currentTimeMillis() - this.lastMoved) / 1000L);
+    if (this.lastMoved == 0L) {
+      dist = 0.0D;
+    }
+    if (this.enemyTarget != null) {
+      this.target = this.enemyTarget.loc;
+    }
+    this.lastMoved = System.currentTimeMillis();
+    if (Point.dist(this.loc, this.target) <= dist) {
+      newLoc = this.target;
+      if (this.model.getAction() != Action.Attacking) {
+        this.model.setAction(Action.Standing);
+      }
+    } else {
+      if (this.model.getAction() != Action.Attacking) {
+        this.model.setAction(Action.Walking);
+      }
+      int xDif = (int)(Point.xDif(this.loc, this.target) * dist / Point.dist(this.loc, this.target));
+      int yDif = (int)(Point.yDif(this.loc, this.target) * dist / Point.dist(this.loc, this.target));
+      newLoc = new Point(this.loc.getX(), this.loc.getXMin() + xDif, this.loc.getY(), this.loc.getYMin() + yDif);
+      newLoc.setX(newLoc.getX() + newLoc.getXMin() / 100);
+      newLoc.setXMin(newLoc.getXMin() % 100);
+      newLoc.setY(newLoc.getY() + newLoc.getYMin() / 100);
+      newLoc.setYMin(newLoc.getYMin() % 100);
+      if (newLoc.getXMin() < 0) {
+        newLoc.setX(newLoc.getX() - 1);
+        newLoc.setXMin(newLoc.getXMin() + 100);
+      } else if (newLoc.getYMin() < 0) {
+        newLoc.setY(newLoc.getY() - 1);
+        newLoc.setYMin(newLoc.getYMin() + 100);
+      }
+      this.model.setDirection(calcDirection());
+    }
+    return newLoc;
+  }
+
+  private Direction calcDirection() {
+    Direction dir;
+    int xDif2 = Point.xDif(this.loc, this.target);
+    int yDif2 = Point.yDif(this.target, this.loc);
+    double angle = 1.5707963267948966D;
+    if (xDif2 == 0) {
+      if (yDif2 != 0) {
+        angle *= Math.abs(yDif2 / yDif2);
+      }
+    } else {
+      angle = Math.atan(yDif2 / xDif2);
+    }
+    if (angle >= 0.7853981633974483D) {
+      dir = Direction.North;
+    } else if (-0.7853981633974483D <= angle && angle <= 0.7853981633974483D) {
+      dir = Direction.East;
+    } else {
+      dir = Direction.South;
+    }
+    if (xDif2 < 0)
+      switch (dir) {
+        case North:
+          dir = Direction.South;
+          break;
+        case South:
+          dir = Direction.North;
+          break;
+        case East:
+          dir = Direction.West;
+          break;
+      } 
+    return dir;
+  }
+
+  public Projectile attack(Creature enemy, AttackType attType) {
+    Weapon weap = selectWeapon(attType);
+    if (System.currentTimeMillis() - this.lastAttacked >= (weap.getAttackSpeed() * 100) && !this.disabled) {
+      this.lastAttacked = System.currentTimeMillis();
+      Point oldTarget = this.target;
+      this.target = enemy.getLoc();
+      this.model.setDirection(calcDirection());
+      this.target = oldTarget;
+      if (attType != AttackType.Spell) {
+        this.model.setAction(Action.Attacking);
+        this.model.getAnimation(this.model.getDirection(), this.model.getAction()).reset();
+      }
+      if (weap.getType() == ItemType.RangedWeapon || weap.getType() == ItemType.Spell) {
+        Point loc = new Point(this.loc);
+        loc.setY(loc.getY() - 55);
+        return weap.spawnProjectile(loc, enemy.getLoc());
+      }
+      weap.applyContactEffects(enemy);
+    }
+    return null;
+  }
+
+  public Projectile attack(Point targ, AttackType attType) {
+    Weapon weap = selectWeapon(attType);
+    if ((System.currentTimeMillis() - this.lastAttacked) / 100L >= weap.getAttackSpeed() && !this.disabled) {
+      this.lastAttacked = System.currentTimeMillis();
+      Point oldTarget = this.target;
+      this.target = targ;
+      this.model.setDirection(calcDirection());
+      this.target = oldTarget;
+      if (attType != AttackType.Spell) {
+        this.model.setAction(Action.Attacking);
+        this.model.getAnimation(this.model.getDirection(), this.model.getAction()).reset();
+      }
+      if (weap.getType() == ItemType.RangedWeapon || weap.getType() == ItemType.Spell) {
+        Point loc = new Point(this.loc);
+        loc.setY(loc.getY() - 30);
+        return weap.spawnProjectile(loc, targ);
+      }
+    }
+    return null;
+  }
+
+  private Weapon selectWeapon(AttackType attType) {
+    switch (attType) {
+      case Weapon:
+        return this.weapon;
+      case Spell:
+        return this.spell;
+    }
+    return this.weapon;
+  }
+
+  public void addDrop(ItemDrop item) {
+    this.drops.add(item);
+  }
+
+  public LinkedList<ItemDrop> getDrops() {
+    return this.drops;
+  }
+
+  public LinkedList<Item> spawnDrops() {
+    LinkedList<Item> newDrops = new LinkedList<Item>();
+    Iterator<ItemDrop> iter = this.drops.iterator();
+    Random gen = new Random();
+    while (iter.hasNext()) {
+      ItemDrop cur = iter.next();
+      if (gen.nextDouble() <= cur.getDropChance()) {
+        newDrops.add(cur.getItem());
+      }
+    }
+    return newDrops;
+  }
+
+  public void draw(Graphics g, int playerX, int playerY) {
+    if (this.passive > 0) {
+      g.drawImage(LostHavenRPG.passiveAura, 375 + this.loc.getX() - playerX, 300 - this.model.getHeight() + this.loc.getY() - playerY, null);
+    }
+    if (this.thorns > 0) {
+      g.drawImage(LostHavenRPG.thornsAura, 375 + this.loc.getX() - playerX, 300 - this.model.getHeight() + this.loc.getY() - playerY, null);
+    }
+    this.model.draw(g, 400 + this.loc.getX() - playerX, 300 + this.loc.getY() - playerY);
+    if (this.confused > 0) {
+      g.drawImage(LostHavenRPG.confuseAura, 375 + this.loc.getX() - playerX, 300 - this.model.getHeight() + this.loc.getY() - playerY, null);
+    }
+    if (this.sand > 0) {
+      g.drawImage(LostHavenRPG.sandAura, 375 + this.loc.getX() - playerX, 300 - this.model.getHeight() + this.loc.getY() - playerY, null);
+    }
+    g.setColor(Color.red);
+    g.fillRect(360 + this.loc.getX() - playerX, 290 - this.model.getHeight() + this.loc.getY() - playerY, 80 * this.hitpoints / this.maxHitpoints, 10);
+    g.setColor(Color.black);
+    Font font12 = new Font("Arial", 0, 12);
+    FontMetrics metrics = g.getFontMetrics(font12);
+    g.setFont(font12);
+    g.drawString(this.name, 400 + this.loc.getX() - playerX - metrics.stringWidth(this.name) / 2, 300 - this.model.getHeight() + this.loc.getY() - playerY);
+  }
+
+  public void save(PrintWriter out) {
+    out.println(this.type);
+    if (this.spawnPoint == null) {
+      out.println(-1);
+    } else {
+      out.println(this.spawnPoint.idNum);
+    }
+    out.println(String.valueOf(this.loc.getX()) + "," + this.loc.getY());
+    out.println(this.model.getDirection());
+    out.println(this.model.getAction());
+    out.println(this.hitpoints);
+    out.println(this.manapoints);
+    out.println(this.passive);
+    out.println(this.thorns);
+    out.println(this.confused);
+    out.println(this.sand);
+    if (this.weapon == null) {
+      out.println("None");
+    } else {
+      out.println(this.weapon.getName());
+    }
+  }
+
+  public static Creature loadTemplate(BufferedReader in) {
+    Creature cr = new Creature();
+    BufferedImage[] imgProj = new BufferedImage[8];
+    try {
+      cr.name = in.readLine();
+      cr.type = CreatureType.valueOf(cr.name);
+      if (cr.type == CreatureType.Player) {
+        cr = Player.loadTemplate(in);
+      }
+      cr.setSpeed(Integer.parseInt(in.readLine()));
+      cr.setAttribute(Attribute.Strength, Integer.parseInt(in.readLine()));
+      cr.setAttribute(Attribute.Dexterity, Integer.parseInt(in.readLine()));
+      cr.setAttribute(Attribute.Constitution, Integer.parseInt(in.readLine()));
+      cr.setAttribute(Attribute.Wisdom, Integer.parseInt(in.readLine()));
+      ItemType wType = ItemType.valueOf(in.readLine());
+      if (wType == ItemType.RangedWeapon) {
+        imgProj = LostHavenRPG.imgBow;
+      } else if (wType == ItemType.Spell) {
+        imgProj[0] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[1] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[2] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[3] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[4] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[5] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[6] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+        imgProj[7] = ImageIO.read(cr.getClass().getResource("../images/" + cr.type.toString() + "/proj.png"));
+      }
+      cr.model = cr.loadModel(cr.type.toString());
+      cr.setWeapon(new Weapon("None", wType, "Dagger.png", imgProj, 10, Integer.parseInt(in.readLine()), Integer.parseInt(in.readLine())));
+      cr.setExperience(Integer.parseInt(in.readLine()));
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+    return cr;
+  }
+
+  public Model loadModel(String folder) {
+    boolean noAnims = false;
+    Animation anmStandN = new Animation("north", 0, 0, 40, 60, 300, true);
+    Animation anmStandS = new Animation("south", 0, 0, 40, 60, 300, true);
+    Animation anmStandE = new Animation("east", 0, 0, 40, 60, 300, true);
+    Animation anmStandW = new Animation("west", 0, 0, 40, 60, 300, true);
+    Animation anmWalkN = new Animation("north", 0, 0, 40, 60, 300, true);
+    Animation anmWalkS = new Animation("south", 0, 0, 40, 60, 300, true);
+    Animation anmWalkE = new Animation("east", 0, 0, 40, 60, 300, true);
+    Animation anmWalkW = new Animation("west", 0, 0, 40, 60, 300, true);
+    Animation anmAttackN = new Animation("north", 0, 0, 40, 60, 500, false);
+    Animation anmAttackS = new Animation("south", 0, 0, 40, 60, 500, false);
+    Animation anmAttackE = new Animation("east", 0, 0, 40, 60, 500, false);
+    Animation anmAttackW = new Animation("west", 0, 0, 40, 60, 500, false);
+    Animation anmDeath = new Animation("west", 0, 0, 40, 60, 1000, false);
+    BufferedImage walkN = null;
+    BufferedImage walkS = null;
+    BufferedImage walkE = null;
+    BufferedImage walkW = null;
+    boolean hasDeathAnim = (getClass().getResource("../images/" + folder + "/Death1.png") != null);
+    try {
+      if (getClass().getResource("../images/" + folder + "/WalkN.png") != null) {
+        walkN = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkN.png"));
+        anmWalkN.addFrame(walkN);
+        walkS = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkS.png"));
+        anmWalkS.addFrame(walkS);
+        walkE = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkE.png"));
+        anmWalkE.addFrame(walkE);
+        walkW = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkW.png"));
+        anmWalkW.addFrame(walkW);
+      } else if (getClass().getResource("../images/" + folder + "/WalkN1.png") != null) {
+        walkN = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkN1.png"));
+        anmWalkN.addFrame(walkN);
+        anmWalkN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkN2.png")));
+        walkS = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkS1.png"));
+        anmWalkS.addFrame(walkS);
+        anmWalkS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkS2.png")));
+        walkE = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkE1.png"));
+        anmWalkE.addFrame(walkE);
+        anmWalkE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkE2.png")));
+        walkW = ImageIO.read(getClass().getResource("../images/" + folder + "/WalkW1.png"));
+        anmWalkW.addFrame(walkW);
+        anmWalkW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkW2.png")));
+      } else {
+        noAnims = true;
+        anmStandN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmStandS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmStandE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmStandW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmWalkW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmAttackN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmAttackS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmAttackE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+        anmAttackW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/" + folder + ".png")));
+      }
+      if (!noAnims) {
+        BufferedImage standN, standS, standE, standW;
+        if (getClass().getResource("../images/" + folder + "/StandN.png") == null) {
+          standN = walkN;
+          standS = walkS;
+          standE = walkE;
+          standW = walkW;
+        } else {
+          standN = ImageIO.read(getClass().getResource("../images/" + folder + "/StandN.png"));
+          standS = ImageIO.read(getClass().getResource("../images/" + folder + "/StandS.png"));
+          standE = ImageIO.read(getClass().getResource("../images/" + folder + "/StandE.png"));
+          standW = ImageIO.read(getClass().getResource("../images/" + folder + "/StandW.png"));
+          anmWalkN = new Animation("north", 0, 0, 40, 60, 150, true);
+          anmWalkS = new Animation("south", 0, 0, 40, 60, 150, true);
+          anmWalkE = new Animation("east", 0, 0, 40, 60, 150, true);
+          anmWalkW = new Animation("west", 0, 0, 40, 60, 150, true);
+          anmWalkN.addFrame(walkN);
+          anmWalkN.addFrame(standN);
+          anmWalkN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkN2.png")));
+          anmWalkN.addFrame(standN);
+          anmWalkS.addFrame(walkS);
+          anmWalkS.addFrame(standS);
+          anmWalkS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkS2.png")));
+          anmWalkS.addFrame(standS);
+          anmWalkE.addFrame(walkE);
+          anmWalkE.addFrame(standE);
+          anmWalkE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkE2.png")));
+          anmWalkE.addFrame(standE);
+          anmWalkW.addFrame(walkW);
+          anmWalkW.addFrame(standW);
+          anmWalkW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/WalkW2.png")));
+          anmWalkW.addFrame(standW);
+        } 
+        anmStandN.addFrame(standN);
+        anmStandS.addFrame(standS);
+        anmStandE.addFrame(standE);
+        anmStandW.addFrame(standW);
+        if (getClass().getResource("../images/" + folder + "/AttackN.png") != null) {
+          anmAttackN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackN.png")));
+          anmAttackN.addFrame(standN);
+          anmAttackS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackS.png")));
+          anmAttackS.addFrame(standS);
+          anmAttackE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackE.png")));
+          anmAttackE.addFrame(standE);
+          anmAttackW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackW.png")));
+          anmAttackW.addFrame(standW);
+        } else if (getClass().getResource("../images/" + folder + "/AttackN1.png") != null) {
+          anmAttackN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackN1.png")));
+          anmAttackN.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackN2.png")));
+          anmAttackS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackS1.png")));
+          anmAttackS.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackS2.png")));
+          anmAttackE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackE1.png")));
+          anmAttackE.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackE2.png")));
+          anmAttackW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackW1.png")));
+          anmAttackW.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/AttackW2.png")));
+        } else {
+          anmAttackN.addFrame(standN);
+          anmAttackS.addFrame(standS);
+          anmAttackE.addFrame(standE);
+          anmAttackW.addFrame(standW);
+        } 
+        if (hasDeathAnim) {
+          anmDeath.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/Death1.png")));
+          anmDeath.addFrame(ImageIO.read(getClass().getResource("../images/" + folder + "/Death2.png")));
+        } 
+      }
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+    Model model = new Model(hasDeathAnim);
+    model.addAnimation(Direction.North, Action.Standing, anmStandN);
+    model.addAnimation(Direction.South, Action.Standing, anmStandS);
+    model.addAnimation(Direction.East, Action.Standing, anmStandE);
+    model.addAnimation(Direction.West, Action.Standing, anmStandW);
+    model.addAnimation(Direction.North, Action.Walking, anmWalkN);
+    model.addAnimation(Direction.South, Action.Walking, anmWalkS);
+    model.addAnimation(Direction.East, Action.Walking, anmWalkE);
+    model.addAnimation(Direction.West, Action.Walking, anmWalkW);
+    model.addAnimation(Direction.North, Action.Attacking, anmAttackN);
+    model.addAnimation(Direction.South, Action.Attacking, anmAttackS);
+    model.addAnimation(Direction.East, Action.Attacking, anmAttackE);
+    model.addAnimation(Direction.West, Action.Attacking, anmAttackW);
+    if (hasDeathAnim) {
+      model.addAnimation(Direction.West, Action.Dying, anmDeath);
+    }
+    return model;
+  }
+
+  public void load(BufferedReader in) {
+    try {
+      this.spawnPoint = LostHavenRPG.getSpawnPoint(Integer.parseInt(in.readLine()));
+      if (this.spawnPoint != null) {
+        this.spawnPoint.numSpawns++;
+      } 
+      String strLoc = in.readLine();
+      getLoc().setX(Integer.valueOf(strLoc.substring(0, strLoc.indexOf(","))).intValue());
+      getLoc().setY(Integer.valueOf(strLoc.substring(strLoc.indexOf(",") + 1)).intValue());
+      getModel().setDirection(Direction.valueOf(in.readLine()));
+      getModel().setAction(Action.valueOf(in.readLine()));
+      setHitpoints(Integer.valueOf(in.readLine()).intValue());
+      setManapoints(Integer.valueOf(in.readLine()).intValue());
+      this.passive = Integer.valueOf(in.readLine()).intValue();
+      this.thorns = Integer.valueOf(in.readLine()).intValue();
+      this.confused = Integer.valueOf(in.readLine()).intValue();
+      this.sand = Integer.valueOf(in.readLine()).intValue();
+      String strWeapon = in.readLine();
+      if (!strWeapon.equals("None")) {
+        setWeapon((Weapon)LostHavenRPG.items.get(strWeapon));
+      }
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+  protected int attributeIndex(Attribute attribute) {
+    switch (attribute) {
+      case Strength:
+        return 0;
+      case Dexterity:
+        return 1;
+      case Constitution:
+        return 2;
+      case Wisdom:
+        return 3;
+    }
+    return -1;
+  }
+
+  public int getAttribute(Attribute attribute) {
+    return this.attributes[attributeIndex(attribute)];
+  }
+
+  public void setAttribute(Attribute attribute, int num) {
+    this.attributes[attributeIndex(attribute)] = num;
+    updateStats();
+  }
+
+  private void updateStats() {
+    this.hitpoints = 2 * this.attributes[2];
+    this.maxHitpoints = 2 * this.attributes[2];
+    this.manapoints = 2 * this.attributes[3];
+    this.maxManapoints = 2 * this.attributes[3];
+  }
+
+  public Creature getEnemyTarget() {
+    return this.enemyTarget;
+  }
+
+  public int getExperience() {
+    return this.experience;
+  }
+
+  public int getHitpoints() {
+    return this.hitpoints;
+  }
+
+  public Model getModel() {
+    return this.model;
+  }
+
+  public long getLastAttacked() {
+    return this.lastAttacked;
+  }
+
+  public long getLastMoved() {
+    return this.lastMoved;
+  }
+
+  public int getLevel() {
+    return this.level;
+  }
+
+  public Point getLoc() {
+    return this.loc;
+  }
+
+  public int getManapoints() {
+    return this.manapoints;
+  }
+
+  public int getMaxHitpoints() {
+    return this.maxHitpoints;
+  }
+
+  public int getMaxManapoints() {
+    return this.maxManapoints;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public int getSpeed() {
+    return this.speed;
+  }
+
+  public Point getTarget() {
+    return this.target;
+  }
+
+  public CreatureType getType() {
+    return this.type;
+  }
+
+  public EquippedWeapon getWeapon() {
+    return this.weapon;
+  }
+
+  public EquippedWeapon getSpell() {
+    return this.spell;
+  }
+
+  public void setEnemyTarget(Creature enemyTarget) {
+    this.enemyTarget = enemyTarget;
+  }
+
+  public void setExperience(int experience) {
+    this.experience = experience;
+  }
+
+  public void setHitpoints(int hitpoints) {
+    if (hitpoints > 0) {
+      this.hitpoints = hitpoints;
+    } else {
+      this.hitpoints = 0;
+    }
+  }
+
+  public void setModel(Model model) {
+    this.model = model;
+  }
+
+  public void setLastAttacked(long lastAttacked) {
+    this.lastAttacked = lastAttacked;
+  }
+
+  public void setLastMoved(long lastMoved) {
+    this.lastMoved = lastMoved;
+  }
+
+  public void setLevel(int level) {
+    this.level = level;
+  }
+
+  public void setLoc(Point loc) {
+    this.loc = loc;
+  }
+
+  public void setManapoints(int manapoints) {
+    if (manapoints > 0) {
+      this.manapoints = manapoints;
+    } else {
+      this.manapoints = 0;
+    }
+  }
+
+  public void setMaxHitpoints(int maxHitpoints) {
+    this.maxHitpoints = maxHitpoints;
+  }
+
+  public void setMaxManapoints(int maxManapoints) {
+    this.maxManapoints = maxManapoints;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public void setSpeed(int speed) {
+    this.speed = speed;
+  }
+
+  public void setTarget(Point target) {
+    this.target = target;
+  }
+
+  public void setType(CreatureType type) {
+    this.type = type;
+  }
+
+  public void setWeapon(Weapon weapon) {
+    this.weapon = new EquippedWeapon(this, weapon);
+    this.weapon.update();
+    (this.model.getAnimation(Direction.North, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.North, Action.Attacking)).frames.size());
+    (this.model.getAnimation(Direction.South, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.South, Action.Attacking)).frames.size());
+    (this.model.getAnimation(Direction.East, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.East, Action.Attacking)).frames.size());
+    (this.model.getAnimation(Direction.West, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.West, Action.Attacking)).frames.size());
+  }
+
+  public void setSpell(Weapon spell) {
+    this.spell = new EquippedWeapon(this, spell);
+    this.spell.update();
+  }
+
+  public SpawnPoint getSpawnPoint() {
+    return this.spawnPoint;
+  }
+
+  public void setSpawnPoint(SpawnPoint sp) {
+    this.spawnPoint = sp;
+  }
+
+  public boolean farFromSpawnPoint() {
+    if (this.spawnPoint == null)
+      return false; 
+    return (Point.dist(this.spawnPoint.getLoc(), this.loc) > 800.0D);
+  }
+
+  public boolean closerToSpawnPoint(Point newLoc) {
+    if (this.spawnPoint == null)
+      return false; 
+    return (Point.dist(this.spawnPoint.getLoc(), this.loc) >= Point.dist(this.spawnPoint.getLoc(), newLoc));
+  }
+
+  public void disable() {
+    this.disabled = true;
+  }
+
+  public void enable() {
+    this.disabled = false;
+  }
+
+  public String toString() {
+    return this.name;
+  }
+
+  public int compareTo(Creature c) {
+    return 0;
+  }
 }
Index: main/CreatureType.java
===================================================================
--- main/CreatureType.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/CreatureType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,4 +2,20 @@
 
 public enum CreatureType {
-
+  Archer,
+  Bandit,
+  Blob,
+  BlueBlob,
+  Specter,
+  CrystalGuard,
+  DesertAmbusher,
+  Ghost,
+  Goblin,
+  Golem,
+  Guardian,
+  Player,
+  Pyromancer,
+  RustedArmor,
+  BurningCorpse,
+  Wight,
+  Zombie
 }
Index: main/Damage.java
===================================================================
--- main/Damage.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Damage.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,40 @@
+package main;
+
+public class Damage extends Effect {
+
+  private int damage;
+
+  public Damage(int damage, TargetType target) {
+    super(EffectType.Damage, target);
+    this.damage = damage;
+  }
+
+  public Damage(Damage e) {
+    super(e);
+    this.damage = e.damage;
+  }
+
+  public Damage copy() {
+    return new Damage(this);
+  }
+
+  public int getDamage() {
+    return this.damage;
+  }
+
+  public void setDamage(int damage) {
+    this.damage = damage;
+  }
+
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    cr.setHitpoints(cr.getHitpoints() - this.damage);
+    if (cr.getHitpoints() > cr.getMaxHitpoints()) {
+      cr.setHitpoints(cr.getMaxHitpoints());
+    }
+  }
+
+  public String toString() {
+    return Integer.toString(this.damage);
+  }
+}
Index: main/Direction.java
===================================================================
--- main/Direction.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Direction.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,12 @@
+package main;
+
+public enum Direction {
+  North,
+  South,
+  East,
+  West,
+  NorthEast,
+  SouthEast,
+  SouthWest,
+  NorthWest
+}
Index: main/Disable.java
===================================================================
--- main/Disable.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Disable.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,29 @@
+package main;
+
+public class Disable extends TimedEffect {
+
+  public Disable(TargetType target, long duration) {
+    super(EffectType.Disable, target, duration);
+  }
+
+  public Disable(Disable e) {
+    super(e);
+  }
+
+  public Disable copy() {
+    return new Disable(this);
+  }
+
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    cr.disable();
+    cr.addEffect(this);
+    cr.passive++;
+  }
+
+  public void cancelEffect(Object o) {
+    Creature cr = (Creature)o;
+    cr.enable();
+    cr.passive--;
+  }
+}
Index: main/Effect.java
===================================================================
--- main/Effect.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Effect.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,35 @@
+package main;
+
+public class Effect {
+
+  private EffectType type;  
+  private TargetType target;
+  
+  public Effect(EffectType type, TargetType target) {
+    this.type = type;
+    this.target = target;
+  }
+
+  public Effect(Effect e) {
+    this.type = e.type;
+    this.target = e.target;
+  }
+
+  public Effect copy() {
+    return new Effect(this);
+  }
+
+  public EffectType getType() {
+    return this.type;
+  }
+
+  public TargetType getTarget() {
+    return this.target;
+  }
+
+  public void applyEffect(Object o) {
+  }
+  
+  public void cancelEffect(Object o) {
+  }
+}
Index: main/EffectTimer.java
===================================================================
--- main/EffectTimer.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/EffectTimer.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,22 @@
+package main;
+
+public class EffectTimer {
+
+  private Effect effect;  
+  private long duration;
+  private long startTime;
+
+  public EffectTimer(Effect effect, long duration) {
+    this.effect = effect;
+    this.duration = duration;
+    this.startTime = System.currentTimeMillis();
+  }
+
+  public Effect getEffect() {
+    return this.effect;
+  }
+
+  public boolean expired() {
+    return (System.currentTimeMillis() - this.startTime >= this.duration);
+  }
+}
Index: main/EffectType.java
===================================================================
--- main/EffectType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/EffectType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,14 @@
+package main;
+
+public enum EffectType {
+  AreaOfEffect,
+  AttackSpeed,
+  ChangeDamage,
+  Confuse,
+  Damage,
+  Disable,
+  ManaDrain,
+  MoveSpeed,
+  Penetrate,
+  ProjectileSpeed
+}
Index: main/EquippedWeapon.java
===================================================================
--- main/EquippedWeapon.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/EquippedWeapon.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,127 @@
+package main;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public class EquippedWeapon extends Weapon {
+
+  Creature controller;  
+  Weapon baseWeapon;
+  
+  public EquippedWeapon(Creature controller, Weapon baseWeapon) {
+    super(baseWeapon, (Point)null);
+    this.controller = controller;
+    this.baseWeapon = baseWeapon;
+  }
+
+  public EquippedWeapon(EquippedWeapon copy, Point loc) {
+    super(copy, loc);
+    this.controller = copy.controller;
+    this.baseWeapon = copy.baseWeapon;
+  }
+
+  public EquippedWeapon copy(Point loc) {
+    return new EquippedWeapon(this, loc);
+  }
+
+  public void update() {
+    Player p;
+    boolean[] usedGems;
+    int x;
+    if (this.controller.getType() == CreatureType.Player) {
+      p = (Player)this.controller;
+    } else {
+      return;
+    }
+    this.spawnEffects.clear();
+    this.contactEffects.clear();
+    this.contactEffects.add(new Damage(0, TargetType.Enemy));
+    setAttackSpeed(this.baseWeapon.getAttackSpeed() - this.controller.getAttribute(Attribute.Dexterity) / 2 + 3);
+    switch (getType()) {
+      case LightWeapon:
+        setDamage(this.baseWeapon.getDamage() + p.getAttribute(Attribute.Strength) - 6 + 2 * p.getSkill(Skill.LightWeapons));
+        break;
+      case HeavyWeapon:
+        setDamage(this.baseWeapon.getDamage() + 2 * p.getAttribute(Attribute.Strength) - 12 + 4 * p.getSkill(Skill.HeavyWeapons));
+        break;
+      case RangedWeapon:
+        setDamage(this.baseWeapon.getDamage() + 2 * p.getSkill(Skill.RangedWeapons));
+        break;
+      case Spell:
+        usedGems = new boolean[4];
+        setSpawnEffect(new ManaDrain((p.getGemValue() + 1) / 2, TargetType.Creator));
+        if (p.getGemNum("FireGem") > 0) {
+          setDamage(4 * p.getGemNum("FireGem"));
+          usedGems[2] = true;
+        }
+        if (p.getGemNum("WaterGem") > 0) {
+          setContactEffect(new ChangeDamage(0.8D, TargetType.Enemy, (5000 * p.getGemNum("WaterGem"))));
+          usedGems[3] = true;
+        }
+        if (p.getGemNum("AirGem") > 0) {
+          setSpawnEffect(new ProjectileSpeed(100 * p.getGemNum("AirGem"), TargetType.Projectile));
+          usedGems[0] = true;
+        }
+        if (p.getGemNum("EarthGem") > 0) {
+          setContactEffect(new AreaOfEffect(new Damage(4 * p.getGemNum("EarthGem"), TargetType.Enemy), 100, TargetType.Enemy));
+          usedGems[1] = true;
+        }
+        if (p.getGemNum("SteamMetaGem") > 0) {
+          setContactEffect(new Confuse(TargetType.Enemy, (2000 * p.getGemNum("SteamMetaGem"))));
+          usedGems[2] = true;
+          usedGems[3] = true;
+        }
+        if (p.getGemNum("SandMetaGem") > 0) {
+          setContactEffect(new Disable(TargetType.Enemy, (4000 * p.getGemNum("SandMetaGem"))));
+          usedGems[0] = true;
+          usedGems[2] = true;
+        }
+        if (p.getGemNum("MetalMetaGem") > 0) {
+          setSpawnEffect(new Penetrate(TargetType.Projectile));
+          usedGems[1] = true;
+          usedGems[2] = true;
+        }
+        if (p.getGemNum("IceMetaGem") > 0) {
+          setContactEffect(new MoveSpeed(0.0D, TargetType.Enemy, (3000 * p.getGemNum("IceMetaGem"))));
+          setContactEffect(new Disable(TargetType.Enemy, (3000 * p.getGemNum("IceMetaGem"))));
+          usedGems[0] = true;
+          usedGems[3] = true;
+        }
+        if (p.getGemNum("MudMetaGem") > 0) {
+          setContactEffect(new MoveSpeed(0.6D, TargetType.Enemy, (8000 * p.getGemNum("MudMetaGem"))));
+          usedGems[1] = true;
+          usedGems[3] = true;
+        }
+        if (p.getGemNum("CrystalMetaGem") > 0) {
+          setSpawnEffect(new Damage(-5 * p.getGemNum("CrystalMetaGem"), TargetType.Creator));
+          usedGems[0] = true;
+          usedGems[1] = true;
+        }
+        for (x = 0; x < 8; x++) {
+          this.imgProj[x] = generateSprite(x, usedGems);
+        }
+        break;
+    }
+  }
+
+  private BufferedImage generateSprite(int dir, boolean[] usedGems) {
+    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
+    GraphicsDevice device = env.getDefaultScreenDevice();
+    GraphicsConfiguration gc = device.getDefaultConfiguration();
+    BufferedImage source = gc.createCompatibleImage(50, 50, 3);
+    Graphics2D srcGraphics = source.createGraphics();
+    if (usedGems[1]) {
+      srcGraphics.drawImage(LostHavenRPG.earthSprites[dir], 0, 0, null);
+    }
+    if (usedGems[0]) {
+      srcGraphics.drawImage(LostHavenRPG.airSprites[dir], 0, 0, null);
+    }
+    if (usedGems[2]) {
+      srcGraphics.drawImage(LostHavenRPG.fireSprites[dir], 0, 0, null);
+    }
+    if (usedGems[3]) {
+      srcGraphics.drawImage(LostHavenRPG.waterSprites[dir], 0, 0, null);
+    }
+    return source;
+  }
+}
Index: main/GameState.java
===================================================================
--- main/GameState.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/GameState.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,13 +2,17 @@
 
 public enum GameState {
-    Main,
-    CreateAccount,
-    CreateClass,
-    LoadGame,
-    Info,
-    Credits,
-    Game,
-    GameMenu,
-    GameInventory,
-    GameStats
+  Main,
+  CreateAccount,
+  LoadGame,
+  Info,
+  Credits,
+  Game,
+  GameIntro,
+  GameInfo,
+  GameMenu,
+  GameStats,
+  GameGems,
+  GameInventory,
+  GameMap,
+  Victory
 }
Index: main/Gem.java
===================================================================
--- main/Gem.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Gem.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,31 @@
+package main;
+
+public class Gem extends Item {
+
+  private int value;
+  private Effect effect;
+
+  public Gem(String name, String img, int value, Effect effect) {
+    super(name, ItemType.Gem, "Gems/" + img);
+    this.value = value;
+    this.effect = effect;
+  }
+
+  public Gem(Gem copy, Point loc) {
+    super(copy, loc);
+    this.value = copy.value;
+    this.effect = copy.effect;
+  }
+
+  public Gem copy(Point loc) {
+    return new Gem(this, loc);
+  }
+
+  public int getValue() {
+    return this.value;
+  }
+
+  public Effect getEffect() {
+    return this.effect;
+  }
+}
Index: main/GemType.java
===================================================================
--- main/GemType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/GemType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,14 @@
+package main;
+
+public enum GemType {
+  Fire,
+  Water,
+  Air,
+  Earth,
+  Steam,
+  Sand,
+  Metal,
+  Ice,
+  Mud,
+  Crystal
+}
Index: main/Gender.java
===================================================================
--- main/Gender.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Gender.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,6 +2,6 @@
 
 public enum Gender {
-    None,
-    Male,
-    Female
+  None,
+  Male,
+  Female
 }
Index: main/Item.java
===================================================================
--- main/Item.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Item.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,78 @@
+package main;
+
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
+import java.io.IOException;
+
+public class Item {
+
+  private String name;
+  private ItemType type;
+  private BufferedImage img;
+  private Point loc;
+  private String description;
+
+  public Item(String name, ItemType type, String strImg) {
+    this.name = name;
+    this.type = type;
+    this.loc = null;
+    try {
+      this.img = ImageIO.read(getClass().getResource("../images/" + strImg));
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+  public Item(Item copy, Point loc) {
+    this.name = copy.name;
+    this.type = copy.type;
+    this.img = copy.img;
+    this.loc = loc;
+    this.description = copy.description;
+  }
+
+  public Item copy(Point loc) {
+    return new Item(this, loc);
+  }
+
+  public BufferedImage getImg() {
+    return this.img;
+  }
+
+  public String getDescription() {
+    return this.description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public boolean isRelic() {
+    return (this.type == ItemType.Relic);
+  }
+
+  public void draw(Graphics g, int playerX, int playerY) {
+    g.drawImage(this.img, 375 + this.loc.getX() - playerX, 275 + this.loc.getY() - playerY, null);
+  }
+
+  public void drawStatic(Graphics g, int x, int y) {
+    g.drawImage(this.img, x, y, null);
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public ItemType getType() {
+    return this.type;
+  }
+
+  public Point getLoc() {
+    return this.loc;
+  }
+
+  public void setLoc(Point loc) {
+    this.loc = loc;
+  }
+}
Index: main/ItemDrop.java
===================================================================
--- main/ItemDrop.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/ItemDrop.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,25 @@
+package main;
+
+public class ItemDrop {
+
+  private Item item;
+  private double dropChance;
+  
+  public ItemDrop(Item item, double dropChance) {
+    this.item = item;
+    this.dropChance = dropChance;
+  }
+
+  public ItemDrop(Item item, int percentDrop) {
+    this.item = item;
+    this.dropChance = percentDrop / 100.0D;
+  }
+
+  public Item getItem() {
+    return this.item;
+  }
+
+  public double getDropChance() {
+    return this.dropChance;
+  }
+}
Index: main/ItemType.java
===================================================================
--- main/ItemType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/ItemType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,12 @@
+package main;
+
+public enum ItemType {
+  Hand,
+  LightWeapon,
+  HeavyWeapon,
+  RangedWeapon,
+  Spell,
+  Gem,
+  Relic,
+  None
+}
Index: main/Job.java
===================================================================
--- main/Job.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Job.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,10 +2,10 @@
 
 public enum Job {
-    None,
-    Fighter,
-    Ranger,
-    Barbarian,
-    Sorceror,
-    Druid,
-    Wizard
+  None,
+  Fighter,
+  Ranger,
+  Barbarian,
+  Sorceror,
+  Druid,
+  Wizard
 }
Index: main/Land.java
===================================================================
--- main/Land.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Land.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,23 +1,27 @@
 package main;
 
-import java.awt.image.*;
+import java.awt.image.BufferedImage;
 
 public class Land extends MapElement {
-	private LandType type;
-	
-	public Land(LandType type, BufferedImage img, boolean passable) {
-		super(img, passable);
 
-		this.type = type;
-	}
-	
-	public Land(LandType type, String imgFile, boolean passable) {
-		super(imgFile, passable);
-		
-		this.type = type;
-	}
-	
-	public LandType getType() {
-		return type;
-	}
+  private LandType type;
+
+  public Land(LandType type, BufferedImage img, boolean passable) {
+    super(img, passable);
+    this.type = type;
+  }
+
+  public Land(LandType type, String imgFile, boolean passable) {
+    super(imgFile, passable);
+    this.type = type;
+  }
+
+  public Land(Land copy) {
+    super(copy);
+    this.type = copy.type;
+  }
+
+  public LandType getType() {
+    return this.type;
+  }
 }
Index: main/LandType.java
===================================================================
--- main/LandType.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/LandType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,5 +2,20 @@
 
 public enum LandType {
-	Grass,
-	Ocean
+  Lava,
+  Metal,
+  Charred,
+  Swamp,
+  Vines,
+  Crystal,
+  CrystalFormation,
+  Water,
+  Forest,
+  Tree,
+  Plains,
+  Desert,
+  Mountains,
+  Cave,
+  Ocean,
+  Snow,
+  Steam;
 }
Index: main/Location.java
===================================================================
--- main/Location.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Location.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,39 +1,50 @@
 package main;
 
-import java.util.*;
+import java.util.PriorityQueue;
 
 public class Location {
-	private Land land;
-	private Structure structure;
-	private PriorityQueue<Creature> creatures;
-	
-	public Location(Land land, Structure structure) {
-		this.land = land;
-		this.structure = structure;
-		this.creatures = new PriorityQueue<Creature>();
-	}
 
-	public void addCreature(Creature creature) {
-		creatures.add(creature);
-	}
-	
-	public Land getLand() {
-		return land;
-	}
+  private Land land;
+  private Structure structure;
+  private PriorityQueue<Creature> creatures;
 
-	public Structure getStruct() {
-		return structure;
-	}
-	
-	public void setLand(Land type) {
-		land = type;
-	}
-	
-	public void setStruct(Structure type) {
-		structure = type;
-	}
-	
-	public boolean isPassable() {
-		return land.isPassable() && structure.isPassable();
-	}
+  public Location(Land land, Structure structure) {
+    this.land = land;
+    this.structure = structure;
+    this.creatures = new PriorityQueue<Creature>();
+  }
+
+  public void addCreature(Creature creature) {
+    this.creatures.add(creature);
+  }
+
+  public void spawnCreature(Creature creature, Point loc) {
+    Creature newC = creature.copy();
+    newC.setLoc(loc);
+    this.creatures.add(newC);
+  }
+
+  public Land getLand() {
+    return this.land;
+  }
+
+  public Structure getStruct() {
+    return this.structure;
+  }
+
+  public PriorityQueue<Creature> getCreatures() {
+    return this.creatures;
+  }
+
+  public void setLand(Land type) {
+    this.land = type;
+  }
+
+  public void setStruct(Structure type) {
+    this.structure = type;
+  }
+
+  public boolean isPassable() {
+    return (this.land.isPassable() && this.structure.isPassable());
+  }
 }
Index: main/LostHavenRPG.java
===================================================================
--- main/LostHavenRPG.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/LostHavenRPG.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,608 +1,1600 @@
 package main;
 
-import java.awt.*;
+import java.awt.Color;
+import java.awt.DisplayMode;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.MouseInfo;
+import java.awt.event.*;
 import java.awt.image.*;
-import java.awt.event.*;
 import java.io.*;
-import java.util.*;
-import javax.imageio.*;
-import java.text.*;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import javax.imageio.ImageIO;
 
 import gamegui.*;
 
-/* 
- * This is the main class in the project. It initializes wide-screen mode and is responsible for drawing to the screen and handling
- * input.
- * 
- * The classes in the gamegui folder are similar to the Swing classes in that they help in building a gui. They are all extended from
- * the Member class and instances of each of them can be added to a Window class. They also have input-handling functionality.
- */
+public class LostHavenRPG implements KeyListener, MouseListener {
 
-public class LostHavenRPG implements KeyListener, MouseListener {
-	private static DisplayMode[] BEST_DISPLAY_MODES = new DisplayMode[] {
-        new DisplayMode(800, 600, 32, 0),
-        new DisplayMode(800, 600, 16, 0),
-        new DisplayMode(800, 600, 8, 0)
+  private static DisplayMode[] BEST_DISPLAY_MODES = new DisplayMode[] {
+      new DisplayMode(800, 600, 32, 0),
+      new DisplayMode(800, 600, 16, 0), 
+      new DisplayMode(800, 600, 8, 0)
     };
-	
-	boolean done;
-	boolean changePending;
-	
-	Player player;
-	Map map;
-	
-	//all "types" should be enums so it's easier to see all possible values while programming
-	HashMap<LandType, Land> landMap;
-	HashMap<StructureType, Structure> structMap;
-	HashMap<CreatureType, Creature> creatureMap;
-	
-	BufferedImage girl;
-	BufferedImage guy;
-    
-    public GameState gameState;
-    public AuxState auxState;
-    
-    //GUI elements
-    Frame frmMain;
-    
-    gamegui.Window wndMain;
-    gamegui.Window wndCreateAccount;
-    gamegui.Window wndChooseClass;
-    gamegui.Window wndGameInfo;
-    gamegui.Window wndCredits;
-    
-    RadioGroup rdgGenderSelection;
-    RadioGroup rdgClassSelection;
-    
-    gamegui.Window wndMessage;
-    gamegui.Window wndProgress;
-    gamegui.Window wndConnecting;
-    
-    Textbox selectedText;
 
-    public LostHavenRPG(GraphicsDevice device) {
-        try {
-            GraphicsConfiguration gc = device.getDefaultConfiguration();
-            frmMain = new Frame(gc);
-            frmMain.setUndecorated(true);
-            frmMain.setIgnoreRepaint(true);
-            device.setFullScreenWindow(frmMain);
-            
-            if (device.isDisplayChangeSupported()) {
-                chooseBestDisplayMode(device);
-            }
-            
-            frmMain.addMouseListener(this);
-            frmMain.addKeyListener(this);
-            frmMain.createBufferStrategy(2);
-            BufferStrategy bufferStrategy = frmMain.getBufferStrategy();
+  public static Map map;
+  public static HashMap<LandType, Land> landMap;
+  public static HashMap<StructureType, Structure> structMap;
+  public static HashMap<CreatureType, Creature> creatureMap;
+  public static HashMap<String, Item> items;
+  public static LinkedList<RespawnPoint> respawnPoints;
+  public static LinkedList<SpawnPoint> spawnPoints;
 
-            player = new Player();
-            done = false;
-            changePending = false;
-            
-            gameState = GameState.Main;
-            auxState = AuxState.None;
-            
-            loadMap();
-        	map = new Map("mapInfo.txt", "structInfo.txt", landMap, structMap);
-        	map.getLoc(10, 10).addCreature(new Creature());
-            initGUIElements();
-            
-            while (!done) {
-                Graphics g = bufferStrategy.getDrawGraphics();
-                move();
-                render(g);
-                g.dispose();
-                bufferStrategy.show();
-            }
-        }
-        catch (Exception e) {
-            e.printStackTrace();
-        }
-        finally {
-            device.setFullScreenWindow(null);
-        }
+  public static BufferedImage[] airSprites;
+  public static BufferedImage[] earthSprites;
+  public static BufferedImage[] fireSprites;
+  public static BufferedImage[] waterSprites;
+  public static BufferedImage[] imgBow;
+  public static BufferedImage passiveAura;
+  public static BufferedImage thornsAura;
+  public static BufferedImage confuseAura;
+  public static BufferedImage sandAura;
+  public static Model rangedModel;
+  public static Model meleeModel;
+  public static ScrollList lstInventory;
+  public static ScrollList lstGems;
+  public static Frame frmMain;
+
+  public GameState gameState;
+  public AuxState auxState;
+
+  boolean done;
+  boolean changePending;
+  Player player;
+
+  LinkedList<Gem> gems;
+  LinkedList<Item> drops;
+  LinkedList<Projectile> projectiles;
+  
+  BufferedImage imgMap;
+  
+  Window wndMain;
+  Window wndCreateAccount;
+  Window wndGameIntro;
+  Window wndGameInfo;
+  Window wndCredits;
+  Window wndStatDisplay;
+  Window wndGameInventory;
+  Window wndGameGems;
+  Window wndGameStats;
+  Window wndGameMap;
+  Window wndVictory;
+  Window[] wndTutorials;
+  Window wndTutOptions;
+  Window wndMessage;
+
+  int wndTutCur;  
+  boolean infoStart;
+  
+  Button btnMenu;
+  Button btnStats;
+  Button btnGems;
+  Button btnInventory;
+  Button btnMap;
+  
+  Label lblGemVal;
+  Label lblSpellCost;
+  
+  Graphics g;
+  
+  Point startPoint;
+  
+  Textbox selectedText;
+  
+  boolean started = false;
+  
+  public LostHavenRPG(GraphicsDevice device) {
+    try {
+      GraphicsConfiguration gc = device.getDefaultConfiguration();
+      frmMain = new Frame(gc);
+      frmMain.setUndecorated(true);
+      frmMain.setIgnoreRepaint(true);
+      device.setFullScreenWindow(frmMain);
+      if (device.isDisplayChangeSupported())
+        chooseBestDisplayMode(device); 
+      frmMain.addMouseListener(this);
+      frmMain.addKeyListener(this);
+      frmMain.createBufferStrategy(2);
+      BufferStrategy bufferStrategy = frmMain.getBufferStrategy();
+      this.g = bufferStrategy.getDrawGraphics();
+      this.done = false;
+      this.changePending = false;
+      this.gameState = GameState.Main;
+      this.auxState = AuxState.None;
+      loadMapElements();
+      this.imgMap = ImageIO.read(getClass().getResource("../images/mapLarge.png"));
+      map = new Map("map.png", "structInfo2.txt", landMap, structMap, true);
+      this.startPoint = new Point(7050, 6150);
+      loadItems();
+      loadCreatures();
+      loadSpawnPoints();
+      loadSpellSprites();
+      initGUIElements();
+      this.started = true;
+      while (!this.done) {
+        this.g = bufferStrategy.getDrawGraphics();
+        handleEvents();
+        render(this.g);
+        this.g.dispose();
+        bufferStrategy.show();
+      } 
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      device.setFullScreenWindow(null);
+    } 
+  }
+  
+  private void initGUIElements() {
+    Font font11 = new Font("Arial", 0, 11);
+    Font font12 = new Font("Arial", 0, 12);
+    Font font14 = new Font("Arial", 0, 14);
+    Font font24 = new Font("Arial", 0, 24);
+    BufferedImage imgSub = null;
+    BufferedImage imgAdd = null;
+    BufferedImage imgSkillSub = null;
+    BufferedImage imgSkillAdd = null;
+    String strengthDesc = "Raises damage inflicted by melee weapons.";
+    String dexterityDesc = "Lowers cooldown of all weapons.";
+    String constitutionDesc = "Raises starting hitpoints and hitpoints gained per level.";
+    String wisdomDesc = "Raises starting manapoints and manapoints gained per level.";
+    String lightWeaponDesc = "Raises damage inflicted by light weapons.";
+    String heavyWeaponDesc = "Raises damage inflicted by heavy weapons.";
+    String rangedWeaponDesc = "Raises damage inflicted by ranged weapons.";
+    String evocationDesc = "Raises the possible number of activated gems.";
+    this.wndMain = new Window("main", 0, 0, 800, 600, true);
+    Animation anmTitle = new Animation("title", 144, 0, 512, 95, 83, true);
+    try {
+      anmTitle.addFrame(ImageIO.read(getClass().getResource("../images/Frame1.png")));
+      anmTitle.addFrame(ImageIO.read(getClass().getResource("../images/Frame2.png")));
+      anmTitle.addFrame(ImageIO.read(getClass().getResource("../images/Frame3.png")));
+      anmTitle.addFrame(ImageIO.read(getClass().getResource("../images/Frame4.png")));
+      anmTitle.addFrame(ImageIO.read(getClass().getResource("../images/Frame5.png")));
+      imgSub = ImageIO.read(getClass().getResource("../images/imgSub.png"));
+      imgAdd = ImageIO.read(getClass().getResource("../images/imgAdd.png"));
+      imgSkillSub = ImageIO.read(getClass().getResource("../images/imgSkillSub.png"));
+      imgSkillAdd = ImageIO.read(getClass().getResource("../images/imgSkillAdd.png"));
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+    this.wndMain.add(anmTitle);
+    this.wndMain.add(new Button("new game", 500, 140, 200, 40, "New Game", font12));
+    this.wndMain.add(new Button("load game", 500, 230, 200, 40, "Load Game", font12));
+    this.wndMain.add(new Button("game info", 500, 320, 200, 40, "Game Information", font12));
+    this.wndMain.add(new Button("credits", 500, 410, 200, 40, "Credits", font12));
+    this.wndMain.add(new Button("quit", 500, 500, 200, 40, "Quit", font12));
+    this.wndStatDisplay = new Window("stat display", 0, 550, 799, 49, false);
+    this.wndGameStats = new Window("game stats", 0, 125, 320, 425);
+    this.wndGameGems = new Window("inventory", 359, 299, 220, 251);
+    lstGems = new ScrollList("gem list", 0, 0, 200, 251, null, null);
+    lstGems.addScrollBar(new ScrollBar("scrollgem", 200, 0, 20, 251, 3));
+    this.wndGameGems.add(lstGems);
+    this.wndGameInventory = new Window("inventory", 579, 299, 220, 251);
+    lstInventory = new ScrollList("inventory list", 0, 0, 200, 251, null, null);
+    lstInventory.addScrollBar(new ScrollBar("scrollinv", 200, 0, 20, 251, 3));
+    this.wndGameInventory.add(lstInventory);
+    this.wndGameMap = new Window("inventory", 443, 357, 356, 193);
+    this.btnMenu = new Button("main menu", 360, 10, 80, 20, "Main Menu", font12);
+    this.btnStats = new Button("stats", 600, 5, 80, 16, "Character", font12);
+    this.btnInventory = new Button("inventory", 700, 5, 80, 16, "Inventory", font12);
+    this.btnGems = new Button("gems", 600, 29, 80, 16, "Gems", font12);
+    this.btnMap = new Button("map", 700, 29, 80, 16, "Map", font12);
+    this.lblGemVal = new Label("gemVal", 515, 5, 67, 20, "Gem Value: 0", font12, Align.Right);
+    this.lblSpellCost = new Label("spellCost", 515, 24, 67, 20, "Spell Cost: 0", font12, Align.Right);
+    this.wndStatDisplay.add(this.btnMenu);
+    this.wndStatDisplay.add(this.btnStats);
+    this.wndStatDisplay.add(this.btnGems);
+    this.wndStatDisplay.add(this.btnInventory);
+    this.wndStatDisplay.add(this.btnMap);
+    this.wndStatDisplay.add(this.lblGemVal);
+    this.wndStatDisplay.add(this.lblSpellCost);
+    this.wndGameStats.add(new Label("strength", 10, 20, 70, 30, "Strength", font12, Align.Left));
+    this.wndGameStats.add(new Label("strengthDesc", 20, 45, 70, 15, strengthDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("dexterity", 10, 65, 70, 30, "Dexterity", font12, Align.Left));
+    this.wndGameStats.add(new Label("dexterityDesc", 20, 90, 70, 15, dexterityDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("constitution", 10, 110, 70, 30, "Constitution", font12, Align.Left));
+    this.wndGameStats.add(new Label("constitutionDesc", 20, 135, 70, 15, constitutionDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("wisdom", 10, 155, 70, 30, "Wisdom", font12, Align.Left));
+    this.wndGameStats.add(new Label("wisdomDesc", 20, 180, 70, 15, wisdomDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("strVal", 220, 20, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("dexVal", 220, 65, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("conVal", 220, 110, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("wisVal", 220, 155, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("light weapons", 10, 220, 70, 30, "Light Weapons", font12, Align.Left));
+    this.wndGameStats.add(new Label("lightWeaponDesc", 20, 245, 70, 15, lightWeaponDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("heavy weapons", 10, 265, 70, 30, "Heavy Weapons", font12, Align.Left));
+    this.wndGameStats.add(new Label("heavyWeaponDesc", 20, 290, 70, 15, heavyWeaponDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("ranged weapons", 10, 310, 70, 30, "Ranged Weapons", font12, Align.Left));
+    this.wndGameStats.add(new Label("rangedWeaponDesc", 20, 335, 70, 15, rangedWeaponDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("evocation", 10, 355, 70, 30, "Evocation", font12, Align.Left));
+    this.wndGameStats.add(new Label("evocationDesc", 20, 380, 70, 15, evocationDesc, font11, Align.Left));
+    this.wndGameStats.add(new Label("lightWeaponVal", 220, 220, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("heavyWeaponVal", 220, 265, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("rangedWeaponVal", 220, 310, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("evocationVal", 220, 355, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Label("skillVal", 130, 395, 70, 30, "0", font12, Align.Left));
+    this.wndGameStats.add(new Button("lightWeapon+", 265, 230, 20, 20, imgSkillAdd));
+    this.wndGameStats.add(new Button("heavyWeapon+", 265, 275, 20, 20, imgSkillAdd));
+    this.wndGameStats.add(new Button("rangedWeapon+", 265, 320, 20, 20, imgSkillAdd));
+    this.wndGameStats.add(new Button("evocation+", 265, 365, 20, 20, imgSkillAdd));
+    this.wndGameStats.add(new Label("skilllPoints", 50, 395, 70, 30, "Remaining Points:", font12, Align.Right));
+    this.wndCreateAccount = new Window("create account", 0, 0, 800, 600, true);
+    this.wndCreateAccount.add(new Label("title", 250, 15, 300, 20, "Create an Account", font24));
+    this.wndCreateAccount.add(new Textbox("user", 340, 100, 190, 30, "Username:", font12, false));
+    this.wndCreateAccount.add(new Label("strength", 90, 190, 70, 30, "Strength", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("strengthDesc", 115, 220, 70, 15, strengthDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("dexterity", 90, 250, 70, 30, "Dexterity", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("dexterityDesc", 115, 280, 70, 15, dexterityDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("constitution", 90, 310, 70, 30, "Constitution", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("constitutionDesc", 115, 340, 70, 15, constitutionDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("wisdom", 90, 370, 70, 30, "Wisdom", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("wisdomDesc", 115, 400, 70, 15, wisdomDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("attPoints", 110, 430, 70, 30, "Remaining Points:", font12, Align.Right));
+    this.wndCreateAccount.add(new Label("strVal", 264, 190, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("dexVal", 264, 250, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("conVal", 264, 310, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("wisVal", 264, 370, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("attVal", 215, 430, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Button("strength-", 205, 198, 20, 20, imgSub));
+    this.wndCreateAccount.add(new Button("strength+", 315, 198, 20, 20, imgAdd));
+    this.wndCreateAccount.add(new Button("dexterity-", 205, 258, 20, 20, imgSub));
+    this.wndCreateAccount.add(new Button("dexterity+", 315, 258, 20, 20, imgAdd));
+    this.wndCreateAccount.add(new Button("constitution-", 205, 318, 20, 20, imgSub));
+    this.wndCreateAccount.add(new Button("constitution+", 315, 318, 20, 20, imgAdd));
+    this.wndCreateAccount.add(new Button("wisdom-", 205, 378, 20, 20, imgSub));
+    this.wndCreateAccount.add(new Button("wisdom+", 315, 378, 20, 20, imgAdd));
+    this.wndCreateAccount.add(new Label("light weapons", 450, 190, 70, 30, "Light Weapons", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("lightWeaponDesc", 475, 220, 70, 15, lightWeaponDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("heavy weapons", 450, 250, 70, 30, "Heavy Weapons", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("heavyWeaponDesc", 475, 280, 70, 15, heavyWeaponDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("ranged weapons", 450, 310, 70, 30, "Ranged Weapons", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("rangedWeaponDesc", 475, 340, 70, 15, rangedWeaponDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("evocation", 450, 370, 70, 30, "Evocation", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("evocationDesc", 475, 400, 70, 15, evocationDesc, font11, Align.Left));
+    this.wndCreateAccount.add(new Label("skilllPoints", 475, 430, 70, 30, "Remaining Points:", font12, Align.Right));
+    this.wndCreateAccount.add(new Label("lightWeaponVal", 620, 190, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("heavyWeaponVal", 620, 250, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("rangedWeaponVal", 620, 310, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("evocationVal", 620, 370, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Label("skillVal", 575, 430, 70, 30, "0", font12, Align.Left));
+    this.wndCreateAccount.add(new Button("lightWeapon-", 565, 198, 20, 20, imgSkillSub));
+    this.wndCreateAccount.add(new Button("lightWeapon+", 675, 198, 20, 20, imgSkillAdd));
+    this.wndCreateAccount.add(new Button("heavyWeapon-", 565, 258, 20, 20, imgSkillSub));
+    this.wndCreateAccount.add(new Button("heavyWeapon+", 675, 258, 20, 20, imgSkillAdd));
+    this.wndCreateAccount.add(new Button("rangedWeapon-", 565, 318, 20, 20, imgSkillSub));
+    this.wndCreateAccount.add(new Button("rangedWeapon+", 675, 318, 20, 20, imgSkillAdd));
+    this.wndCreateAccount.add(new Button("evocation-", 565, 378, 20, 20, imgSkillSub));
+    this.wndCreateAccount.add(new Button("evocation+", 675, 378, 20, 20, imgSkillAdd));
+    this.wndCreateAccount.add(new Button("create", 245, 520, 140, 30, "Create", font12));
+    this.wndCreateAccount.add(new Button("cancel", 415, 520, 140, 30, "Cancel", font12));
+    this.wndCredits = new Window("main", 0, 0, 800, 600, true);
+    this.wndCredits.add(new Label("title", 250, 15, 300, 20, "Credits", font24));
+    this.wndCredits.add(new Label("1", 250, 160, 300, 20, "Dmitry Portnoy", font14));
+    this.wndCredits.add(new Label("2", 250, 180, 300, 20, "Team Leader", font12));
+    this.wndCredits.add(new Label("3", 250, 195, 300, 20, "Programmer", font12));
+    this.wndCredits.add(new Label("4", 250, 235, 300, 20, "Daniel Vu", font14));
+    this.wndCredits.add(new Label("5", 250, 255, 300, 20, "Graphic Artist", font12));
+    this.wndCredits.add(new Label("6", 250, 295, 300, 20, "Jason Cooper", font14));
+    this.wndCredits.add(new Label("7", 250, 315, 300, 20, "Designer", font12));
+    this.wndCredits.add(new Label("8", 250, 355, 300, 20, "Special thanks to", font14));
+    this.wndCredits.add(new Label("9", 250, 375, 300, 20, "The Game Creation Society", font12));
+    this.wndTutorials = new Window[13];
+    String[] text = new String[13];
+    this.wndTutOptions = new Window("tut", 300, 0, 499, 140, false);
+    this.wndTutOptions.add(new Button("previous", 260, 110, 60, 20, "Previous", font12, Align.Center));
+    this.wndTutOptions.add(new Button("next", 340, 110, 60, 20, "Next", font12, Align.Center));
+    this.wndTutOptions.add(new Button("skip", 420, 110, 60, 20, "Skip", font12, Align.Center));
+    this.wndTutOptions.add(new Label("num", 200, 110, 60, 20, "1 / 13", font12, Align.Center));
+    text[0] = "When you die, you will be resurrected with no penalty at the closest purple pedestal you have activated. To activate a pedestal, you must walk over it. If you have not activated any purple pedestals, you will resurrect where you started the game. It is important to activate any purple pedestals you see to save you time when you die. You can also use purple pedestals to replenish all of your HP and MP by stepping on them.";
+    text[1] = "When you kill enemies, they sometimes drop items. Left-click on an item to pick it up and place it in your inventory. Be careful when picking up items while there are many enemies around because they might kill you.";
+    text[2] = "You can open your inventory from the bar at the bottom of the screen. You can mouse over any item in your inventory and see a description and relevant stats. Left-clicking any weapon in your inventory will equip it and unequip your previously equiped weapon. Left-clicking a gem will activate it provided you have enough evocation skill to do so. ";
+    text[3] = "You can equip both melee and ranged weapons. If you have a ranged weapon, like this bow, you can fire at enemies from a safe distance. Ranged weapons never run out of ammunition. However, they cannot shoot through most obstacles.";
+    text[4] = "Melee weapons are divided into light and heavy weapons. Heavy weapons are slower, but deal much more damage when they hit. Anyone can equip light weapons, but only players with at least 12 Strenth can use heavy weapons. The damage bonus from Strength is also greater for heavy weapons. The heavy weapon skill also adds more damage per hit than the light weapon skill.";
+    text[5] = "You can open the Character window from the bottom bar. The character window shows the levels of all your skills and attributes. When you level up, you get two extra skill points and you can spend them here. Choose your skills carefully since you can't repick your skills later. You also get HP equal to your constitution and MP equal to your wisdom when you level up.";
+    text[6] = "Assuming you have at least one gem activated and enough mana, you can cast spells by right-clicking anywhere on the map.  The total mana cost of your spell is half of the value of all the gems used to cast it.Any enemies that your spell collides with will be affected. However, you use MP every time you cast a spell, so you have to be careful not run out of MP in a battle. There are many gems with different effects and you should carefully choose which gems you want to use for your spell.";
+    text[7] = "The Gems window is accessible from the bottom bar. Activated gems will appear in this window and will contribute their effects to any spells you cast. Each gem has a set amount of energy that is displayed if you mouse over it. Your evocation skill limits the amount of gems you can activate at once, so you should raise it to increase the power of your spells. You can left-click on any gem in the Gems window to place it back in your inventory if you want to activate a different gem.";
+    text[8] = "The images under the Blob indicate that it is afflicted with some status effects. Status effects caused by spells usually last a few seconds, but it varies depending on the gem.";
+    text[9] = "You can activate mutliple gems of the same type to get increased effects. Gems that have one-time effects, like dealing damage, will simply do more of that. Gems that cause the target to gain a temporary status effect will make that effect last longer if multiple gems are used.";
+    text[10] = "These red portals will open your way out of this world. However, they have been protected by the magic of powerful artifacts. These artifacts are scattered throughout the land and you will need at least four to break the enchantment and activate these portals.";
+    text[11] = "Yellow pedestals are portals to the aforementioned artifacts. Be warned that these artifacts are well-guarded and capturing them could prove deadly. To view this tutorial again, click on Game Information from the main menu. Good luck!";
+    text[12] = "You can access the world map from the bottom bar. However, it does not show the locations of respawn or teleportation points. The Rectangular areas on top of the screen are the locations of the relics, but you must use teleportation points to get to them.";
+    FontMetrics metrics = this.g.getFontMetrics(font12);
+    try {
+      for (int x = 0; x < this.wndTutorials.length; x++) {
+        this.wndTutorials[x] = new Window("tut" + x, 0, 0, 800, 600, true);
+        this.wndTutorials[x].add(new Button("screen", 0, 0, 800, 600, ImageIO.read(getClass().getResource("../images/Tutorial/ss" + (x + 1) + ".png"))));
+        this.wndTutorials[x].add(this.wndTutOptions);
+        MultiTextbox multiTextbox = new MultiTextbox("text", 305, 5, 490, 100, "", false, font12, metrics);
+        multiTextbox.setBorder(false);
+        multiTextbox.setText(text[x]);
+        this.wndTutorials[x].add(multiTextbox);
+      } 
+      this.wndTutCur = 0;
+      this.wndGameIntro = new Window("intro", 0, 0, 800, 600, true);
+      this.wndGameIntro.add(new Label("intro", 250, 15, 300, 20, "Introduction", font24));
+      MultiTextbox txt = new MultiTextbox("text", 50, 150, 650, 350, "", false, font12, metrics);
+      txt.setBorder(false);
+      txt.setText("You suddenly wake up in a strange place you do not recognize. You must find some weapons and some way to get out.\n\nKill creatures to get gems to power your spells and weapons to defend yourself with.\n\nTo get out of this world, you must unlock a portal that is protected by powerful artifacts. Each artifact can be accessed by a yellow portal somewhere in the world. You must collect at least four artifacts to unlock the portal.");
+      this.wndGameIntro.add(txt);
+      this.wndVictory = new Window("victory", 0, 0, 800, 600, true);
+      this.wndVictory.add(new Label("victory", 250, 15, 300, 20, "Victory", font24));
+      MultiTextbox txtV = new MultiTextbox("text", 50, 150, 650, 350, "", false, font12, metrics);
+      txtV.setBorder(false);
+      txtV.setText("As you strike the final blow, the specter's body evaporates, leaving behind a pile of torn clothing. In the last seconds of its existance, you manage to peer under its hood and catch a glimpse of your own face. Was that simply proof of your failing psyche or does some darker secret lie behind that horrible monster? When you once again become aware of your surroundings, you find yourself alone in a forest clearing. There is no sign of hostile monsters and the only indications of your ordeal are your incredible fatigue and a pile of rags a few feet away from you. Was the world you were trapped in merely an illusion or is there some other explanation? You cannot be sure, but you hope to never have to remember that place again.");
+      this.wndVictory.add(txtV);
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+    this.wndMessage = new Window("message", 290, 135, 220, 160);
+    this.wndMessage.add(new Label("label", 20, 15, 180, 12, "none", font12));
+    this.wndMessage.add(new Button("button", 70, 115, 80, 30, "OK", font12));
+  }
+  
+  private void loadMapElements() {
+    landMap = new HashMap<LandType, Land>();
+    structMap = new HashMap<StructureType, Structure>();
+    respawnPoints = new LinkedList<RespawnPoint>();
+    BufferedImage nullImg = null;
+    landMap.put(LandType.Lava, new Land(LandType.Lava, "Terrain/orange.png", true));
+    landMap.put(LandType.Metal, new Land(LandType.Metal, "Terrain/darkRed.png", true));
+    landMap.put(LandType.Charred, new Land(LandType.Charred, "Terrain/red.png", true));
+    landMap.put(LandType.Swamp, new Land(LandType.Swamp, "Terrain/lightBrown.png", true));
+    landMap.put(LandType.Vines, new Land(LandType.Vines, "Terrain/darkBrown.png", true));
+    landMap.put(LandType.Crystal, new Land(LandType.Crystal, "Terrain/purple.png", true));
+    landMap.put(LandType.CrystalFormation, new Land(LandType.CrystalFormation, "Terrain/darkPurple.png", false));
+    landMap.put(LandType.Water, new Land(LandType.Water, "Terrain/blue.png", true));
+    landMap.put(LandType.Forest, new Land(LandType.Forest, "Terrain/darkGreen.png", true));
+    landMap.put(LandType.Tree, new Land(LandType.Tree, "Terrain/brown.png", false));
+    landMap.put(LandType.Plains, new Land(LandType.Plains, "Terrain/lightGreen.png", true));
+    landMap.put(LandType.Desert, new Land(LandType.Desert, "Terrain/yellow.png", true));
+    landMap.put(LandType.Mountains, new Land(LandType.Mountains, "Terrain/mountain.png", false));
+    landMap.put(LandType.Cave, new Land(LandType.Cave, "Terrain/mountain.png", false));
+    landMap.put(LandType.Ocean, new Land(LandType.Ocean, "Terrain/darkBlue.png", false));
+    landMap.put(LandType.Snow, new Land(LandType.Snow, "Terrain/lightBlue.png", true));
+    landMap.put(LandType.Steam, new Land(LandType.Steam, "Terrain/lightGray.png", true));
+    structMap.put(StructureType.None, new Structure(StructureType.None, nullImg, true));
+    structMap.put(StructureType.LoginPedestal, new Structure(StructureType.LoginPedestal, "YellowPedestal.png", true));
+    structMap.put(StructureType.RejuvenationPedestal, new Structure(StructureType.RejuvenationPedestal, "PurplePedestal.png", true));
+    structMap.put(StructureType.LifePedestal, new Structure(StructureType.LifePedestal, "RedPedestal.png", true));
+    structMap.put(StructureType.ManaPedestal, new Structure(StructureType.ManaPedestal, "BluePedestal.png", true));
+    structMap.put(StructureType.RespawnPoint, new RespawnPoint(StructureType.RespawnPoint, "PurplePedestal.png", true));
+    structMap.put(StructureType.ArtifactPoint, new ArtifactPoint(StructureType.ArtifactPoint, "YellowPedestal.png", true));
+    structMap.put(StructureType.BossPoint, new ArtifactPoint(StructureType.BossPoint, "RedPedestal.png", true));
+  }
+  
+  private void loadCreatures() {
+    creatureMap = new HashMap<CreatureType, Creature>();
+    try {
+      BufferedReader in = new BufferedReader(new FileReader("creatures.txt"));
+      while (in.ready()) {
+        Creature cr = Creature.loadTemplate(in);
+        String strItem;
+        while (!(strItem = in.readLine()).equals("---"))
+          cr.addDrop(new ItemDrop(items.get(strItem.substring(0, strItem.indexOf(","))), Integer.valueOf(strItem.substring(strItem.indexOf(",") + 2)).intValue())); 
+        creatureMap.put(cr.getType(), cr);
+      } 
+      in.close();
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+    ((Player)creatureMap.get(CreatureType.Player)).initGems(this.gems);
+    meleeModel = ((Player)creatureMap.get(CreatureType.Player)).loadModel("Player");
+    rangedModel = ((Player)creatureMap.get(CreatureType.Player)).loadModel("Player/ranged");
+  }
+  
+  private void loadSpawnPoints() {
+    spawnPoints = new LinkedList<SpawnPoint>();
+    try {
+      BufferedReader in = new BufferedReader(new FileReader("spawnPoints.txt"));
+      while (in.ready()) {
+        Creature type = creatureMap.get(CreatureType.valueOf(in.readLine()));
+        String strLoc = in.readLine();
+        int x = Integer.parseInt(strLoc.substring(0, strLoc.indexOf(",")));
+        strLoc = strLoc.substring(strLoc.indexOf(",") + 1);
+        int y = Integer.parseInt(strLoc.substring(0, strLoc.indexOf(",")));
+        strLoc = strLoc.substring(strLoc.indexOf(",") + 1);
+        int xMin = Integer.parseInt(strLoc.substring(0, strLoc.indexOf(",")));
+        int yMin = Integer.parseInt(strLoc.substring(strLoc.indexOf(",") + 1));
+        Point loc = new Point(x * 100 + xMin, y * 100 + yMin);
+        SpawnPoint sp = new SpawnPoint(type, loc, Integer.parseInt(in.readLine()), Integer.parseInt(in.readLine()));
+        spawnPoints.add(sp);
+      } 
+      in.close();
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+  }
+  
+  private void loadItems() {
+    items = new HashMap<String, Item>();
+    this.drops = new LinkedList<Item>();
+    this.projectiles = new LinkedList<Projectile>();
+    this.gems = new LinkedList<Gem>();
+    try {
+      imgBow = new BufferedImage[8];
+      imgBow[0] = ImageIO.read(getClass().getResource("../images/projectiles/bowNN.png"));
+      imgBow[1] = ImageIO.read(getClass().getResource("../images/projectiles/bowNE.png"));
+      imgBow[2] = ImageIO.read(getClass().getResource("../images/projectiles/bowEE.png"));
+      imgBow[3] = ImageIO.read(getClass().getResource("../images/projectiles/bowSE.png"));
+      imgBow[4] = ImageIO.read(getClass().getResource("../images/projectiles/bowSS.png"));
+      imgBow[5] = ImageIO.read(getClass().getResource("../images/projectiles/bowSW.png"));
+      imgBow[6] = ImageIO.read(getClass().getResource("../images/projectiles/bowWW.png"));
+      imgBow[7] = ImageIO.read(getClass().getResource("../images/projectiles/bowNW.png"));
+      BufferedReader in = new BufferedReader(new FileReader("items.txt"));
+      while (in.ready()) {
+        Item item;
+        int attackSpeed, range, damage, value;
+        String name = in.readLine();
+        String img = String.valueOf(in.readLine()) + ".png";
+        ItemType type = ItemType.valueOf(in.readLine());
+        String desc = in.readLine();
+        BufferedImage[] imgProj = new BufferedImage[8];
+        switch (type) {
+          case LightWeapon:
+          case HeavyWeapon:
+            attackSpeed = Integer.valueOf(in.readLine()).intValue();
+            damage = Integer.valueOf(in.readLine()).intValue();
+            item = new Weapon(name, type, img, null, attackSpeed, 55, damage);
+            break;
+          case RangedWeapon:
+            attackSpeed = Integer.valueOf(in.readLine()).intValue();
+            range = Integer.valueOf(in.readLine()).intValue();
+            damage = Integer.valueOf(in.readLine()).intValue();
+            imgProj = imgBow;
+            item = new Weapon(name, type, img, imgProj, attackSpeed, range, damage);
+            break;
+          case Gem:
+            value = Integer.valueOf(in.readLine()).intValue();
+            item = new Gem(name, img, value, null);
+            this.gems.add((Gem)item);
+            break;
+          default:
+            item = new Item(name, type, img);
+            break;
+        } 
+        item.setDescription(desc);
+        items.put(name, item);
+      } 
+      in.close();
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+  }
+  
+  private void loadSpellSprites() {
+    airSprites = loadSprites("air");
+    earthSprites = loadSprites("earth");
+    fireSprites = loadSprites("fire");
+    waterSprites = loadSprites("water");
+    try {
+      passiveAura = ImageIO.read(getClass().getResource("../images/spells/auras/passiveAura.png"));
+      thornsAura = ImageIO.read(getClass().getResource("../images/spells/auras/thornsAura.png"));
+      confuseAura = ImageIO.read(getClass().getResource("../images/spells/auras/confuseAura.png"));
+      sandAura = ImageIO.read(getClass().getResource("../images/spells/auras/sandAura.png"));
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+  }
+  
+  private BufferedImage[] loadSprites(String name) {
+    BufferedImage[] sprites = new BufferedImage[8];
+    try {
+      sprites[0] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellNN.png"));
+      sprites[1] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellNE.png"));
+      sprites[2] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellEE.png"));
+      sprites[3] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellSE.png"));
+      sprites[4] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellSS.png"));
+      sprites[5] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellSW.png"));
+      sprites[6] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellWW.png"));
+      sprites[7] = ImageIO.read(getClass().getResource("../images/spells/" + name + "/" + name + "SpellNW.png"));
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+    return sprites;
+  }
+  
+  public static SpawnPoint getSpawnPoint(int idNum) {
+    if (idNum == -1)
+      return null; 
+    Iterator<SpawnPoint> iter = spawnPoints.iterator();
+    while (iter.hasNext()) {
+      SpawnPoint sp;
+      if ((sp = iter.next()).idNum == idNum)
+        return sp; 
+    } 
+    return null;
+  }
+  
+  private void spawnCreatures() {
+    Iterator<SpawnPoint> iter = spawnPoints.iterator();
+    while (iter.hasNext()) {
+      SpawnPoint sp = iter.next();
+      if (Point.dist(this.player.getLoc(), sp.getLoc()) >= 600.0D) {
+        Creature cr = sp.spawn();
+        if (cr != null)
+          map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100).addCreature(cr); 
+      } 
+    } 
+  }
+  
+  private void moveCreatures() {
+    int xLow = this.player.getLoc().getX() / 100 - 8;
+    int xHigh = xLow + 17;
+    int yLow = this.player.getLoc().getY() / 100 - 6;
+    int yHigh = yLow + 13;
+    if (xLow < 0)
+      xLow = 0; 
+    if (xHigh > map.getLength())
+      xHigh = map.getLength(); 
+    if (yLow < 0)
+      yLow = 0; 
+    if (yHigh > map.getHeight())
+      yHigh = map.getHeight(); 
+    for (int x = 0; x < map.getLength(); x++) {
+      for (int y = 0; y < map.getHeight(); y++) {
+        Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+        while (iter.hasNext()) {
+          Creature cr = iter.next();
+          if (cr != this.player)
+            if (cr.confused > 0) {
+              cr.setEnemyTarget(findClosestTarget(cr));
+            } else {
+              cr.setEnemyTarget(this.player);
+            }  
+          if (cr.isDying() && cr.getModel().getAnimation(Direction.West, Action.Dying).reachedEnd()) {
+            cr.setDying(false);
+            if (cr == this.player) {
+              respawnPlayer();
+              continue;
+            } 
+            if (cr.getType() == CreatureType.Specter)
+              this.gameState = GameState.Victory; 
+            continue;
+          } 
+          if (!cr.getModel().hasDeath() && cr.getHitpoints() <= 0) {
+            iter.remove();
+            continue;
+          } 
+          move(cr);
+          if (cr.getLoc().getX() / 100 != x || cr.getLoc().getY() / 100 != y) {
+            iter.remove();
+            map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100).addCreature(cr);
+          } 
+        } 
+      } 
+    } 
+  }
+  
+  private void move(Creature cr) {
+    if (cr.isDying())
+      return; 
+    Point newLoc = cr.move();
+    int minDist = 50;
+    boolean creatureBlocking = false;
+    cr.checkTimedEffects();
+    Iterator<Item> itemIter = this.drops.iterator();
+    while (itemIter.hasNext()) {
+      Item item = itemIter.next();
+      if (item.getLoc().equals(this.player.getLoc())) {
+        this.player.pickUp(item);
+        itemIter.remove();
+      } 
+    } 
+    int xLow = newLoc.getX() / 100 - 2;
+    int xHigh = xLow + 5;
+    int yLow = newLoc.getY() / 100 - 2;
+    int yHigh = yLow + 5;
+    if (xLow < 0)
+      xLow = 0; 
+    if (xHigh > map.getLength())
+      xHigh = map.getLength(); 
+    if (yLow < 0)
+      yLow = 0; 
+    if (yHigh > map.getHeight())
+      yHigh = map.getHeight(); 
+    for (int x = xLow; x < xHigh; x++) {
+      for (int y = yLow; y < yHigh; y++) {
+        Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+        while (iter.hasNext()) {
+          Creature temp = iter.next();
+          if (cr.getEnemyTarget() == temp && Point.dist(temp.getLoc(), newLoc) <= cr.getWeapon().getRange()) {
+            creatureBlocking = true;
+            if (temp != this.player || !temp.isDying()) {
+              Projectile proj = cr.attack(temp, AttackType.Weapon);
+              if (proj != null) {
+                proj.setCreator(cr);
+                proj.applySpawnEffects();
+                addProjectile(proj);
+                continue;
+              } 
+              if (temp.getHitpoints() <= 0) {
+                temp.setDying(true);
+                if (temp != this.player) {
+                  killCreature(temp);
+                  this.player.setEnemyTarget(null);
+                  this.player.setTarget(this.player.getLoc());
+                } 
+              } 
+            } 
+            continue;
+          } 
+          if (temp != cr && Point.dist(temp.getLoc(), newLoc) < minDist)
+            creatureBlocking = true; 
+        } 
+      } 
+    } 
+    if (map.getLoc(newLoc.getX() / 100, newLoc.getY() / 100).isPassable() && !creatureBlocking && (!cr.farFromSpawnPoint() || cr.closerToSpawnPoint(newLoc))) {
+      Location oldLoc = map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100);
+      cr.setLoc(newLoc);
+      if (cr == this.player)
+        switch (map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100).getStruct().getType()) {
+          case RespawnPoint:
+            ((RespawnPoint)map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100).getStruct()).mark();
+          case RejuvenationPedestal:
+            this.player.setManapoints(this.player.getMaxManapoints());
+          case LifePedestal:
+            this.player.setHitpoints(this.player.getMaxHitpoints());
+            break;
+          case ManaPedestal:
+            this.player.setManapoints(this.player.getMaxManapoints());
+            break;
+          case BossPoint:
+            if (!this.player.readyForBoss())
+              break; 
+          case ArtifactPoint:
+            if (oldLoc != map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100)) {
+              this.player.setLoc(((ArtifactPoint)map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100).getStruct()).getTarget());
+              this.player.setTarget(this.player.getLoc());
+            } 
+            break;
+        }  
+    } else {
+      cr.setTarget(cr.getLoc());
+      if (cr.getModel().getAction() != Action.Attacking)
+        cr.getModel().setAction(Action.Standing); 
+    } 
+  }
+  
+  private Creature findClosestTarget(Creature cr) {
+    int xLow = cr.getLoc().getX() / 100 - 5;
+    int xHigh = xLow + 11;
+    int yLow = cr.getLoc().getY() / 100 - 5;
+    int yHigh = yLow + 11;
+    Creature closestCr = this.player;
+    double minDist = Point.dist(cr.getLoc(), this.player.getLoc());
+    if (xLow < 0)
+      xLow = 0; 
+    if (xHigh > map.getLength())
+      xHigh = map.getLength(); 
+    if (yLow < 0)
+      yLow = 0; 
+    if (yHigh > map.getHeight())
+      yHigh = map.getHeight(); 
+    for (int x = xLow; x < xHigh; x++) {
+      for (int y = yLow; y < yHigh; y++) {
+        Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+        while (iter.hasNext()) {
+          Creature cur = iter.next();
+          if (cr != cur && minDist > Point.dist(cr.getLoc(), cur.getLoc())) {
+            minDist = Point.dist(cr.getLoc(), cur.getLoc());
+            closestCr = cur;
+          } 
+        } 
+      } 
+    } 
+    return closestCr;
+  }
+  
+  private synchronized void addProjectile(Projectile proj) {
+    this.projectiles.add(proj);
+  }
+  
+  private synchronized void moveProjectiles() {
+    Iterator<Projectile> iter = this.projectiles.iterator();
+    while (iter.hasNext()) {
+      Projectile proj = iter.next();
+      move(proj);
+      if (proj.isDone())
+        iter.remove(); 
+    } 
+  }
+  
+  private void move(Projectile proj) {
+    Point newLoc = proj.move();
+    int minDist = 50;
+    for (int x = 0; x < map.getLength(); x++) {
+      for (int y = 0; y < map.getHeight(); y++) {
+        Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+        while (iter.hasNext()) {
+          Creature cr = iter.next();
+          if (Point.dist(cr.getLoc(), newLoc) < minDist && ((cr == this.player && !proj.isPlayerOwned()) || (cr != this.player && proj.isPlayerOwned()))) {
+            if (!proj.penetrates())
+              proj.setDone(true); 
+            if (!proj.creatureIsEffected(cr)) {
+              proj.applyContactEffects(cr);
+              proj.effectCreature(cr);
+            } 
+            if (cr.getHitpoints() <= 0) {
+              cr.setDying(true);
+              if (cr != this.player) {
+                killCreature(cr);
+                this.player.setEnemyTarget(null);
+                this.player.setTarget(this.player.getLoc());
+              } 
+            } 
+          } 
+        } 
+      } 
+    } 
+    Location curLoc = map.getLoc(newLoc.getX() / 100, newLoc.getY() / 100);
+    if (proj.getTarget().equals(newLoc) || (!curLoc.isPassable() && curLoc.getLand().getType() != LandType.Ocean)) {
+      proj.setDone(true);
+    } else {
+      proj.setLoc(newLoc);
+    } 
+  }
+  
+  private void killCreature(Creature cr) {
+    Iterator<Item> itemIter = cr.spawnDrops().iterator();
+    while (itemIter.hasNext())
+      this.drops.add(((Item)itemIter.next()).copy(cr.getLoc())); 
+    if (cr.getSpawnPoint() != null)
+      cr.getSpawnPoint().removeCreature(); 
+    this.player.setExperience(this.player.getExperience() + cr.getExperience());
+    if (this.player.getExperience() >= (Math.pow(this.player.getLevel(), 2.0D) + this.player.getLevel()) * 500.0D) {
+      this.player.setExperience(this.player.getExperience() - (int)((Math.pow(this.player.getLevel(), 2.0D) + this.player.getLevel()) * 500.0D));
+      this.player.increaseLevel();
+    } 
+  }
+  
+  public void respawnPlayer() {
+    Iterator<RespawnPoint> iter = respawnPoints.iterator();
+    RespawnPoint closest = null;
+    double dist = 0.0D;
+    this.player.setHitpoints(this.player.getMaxHitpoints());
+    this.player.setManapoints(this.player.getMaxManapoints());
+    while (iter.hasNext()) {
+      RespawnPoint cur = iter.next();
+      if (cur.isMarked() && (closest == null || dist > Point.dist(cur.getLoc(), this.player.getLoc()))) {
+        closest = cur;
+        dist = Point.dist(cur.getLoc(), this.player.getLoc());
+      } 
+    } 
+    if (closest == null) {
+      this.player.setLoc(this.startPoint);
+    } else {
+      this.player.setLoc(closest.getLoc());
+    } 
+    this.player.setEnemyTarget(null);
+    this.player.setTarget(this.player.getLoc());
+  }
+  
+  private void handleEvents() {
+    switch (this.gameState) {
+      case Game:
+      case GameMenu:
+      case GameStats:
+      case GameGems:
+      case GameInventory:
+      case GameMap:
+        spawnCreatures();
+        moveCreatures();
+        moveProjectiles();
+        break;
+    } 
+  }
+  
+  private void render(Graphics g) {
+    g.setColor(Color.black);
+    g.fillRect(0, 0, 800, 600);
+    switch (this.gameState) {
+      case Main:
+        drawMain(g);
+        break;
+      case CreateAccount:
+        drawCreateAccount(g);
+        break;
+      case LoadGame:
+        drawLoadGame(g);
+        break;
+      case Info:
+        drawInfo(g);
+        break;
+      case Credits:
+        drawCredits(g);
+        break;
+      case Victory:
+        this.wndVictory.draw(g);
+        break;
+      case GameIntro:
+        this.wndGameIntro.draw(g);
+        break;
+      case GameInfo:
+        this.wndTutorials[this.wndTutCur].draw(g);
+        break;
+      case Game:
+        drawMap(g);
+        drawItems(g);
+        drawCreatures(g);
+        drawProjectiles(g);
+        drawStatDisplay(g);
+        break;
+      case GameMenu:
+        drawMap(g);
+        drawItems(g);
+        drawCreatures(g);
+        drawProjectiles(g);
+        drawStatDisplay(g);
+        drawGameMenu(g);
+        break;
+      case GameStats:
+        drawMap(g);
+        drawItems(g);
+        drawCreatures(g);
+        drawProjectiles(g);
+        drawStatDisplay(g);
+        drawGameStats(g);
+        break;
+      case GameGems:
+        drawMap(g);
+        drawItems(g);
+        drawCreatures(g);
+        drawProjectiles(g);
+        drawStatDisplay(g);
+        drawGameGems(g);
+        break;
+      case GameInventory:
+        drawMap(g);
+        drawItems(g);
+        drawCreatures(g);
+        drawProjectiles(g);
+        drawStatDisplay(g);
+        drawGameInventory(g);
+        break;
+      case GameMap:
+        drawMap(g);
+        drawItems(g);
+        drawCreatures(g);
+        drawProjectiles(g);
+        drawStatDisplay(g);
+        drawGameMap(g);
+        break;
+    } 
+    switch (this.auxState) {
+      case MsgBox:
+        this.wndMessage.draw(g);
+        break;
+    } 
+  }
+  
+  public void save() {
+    PrintWriter out = null;
+    try {
+      out = new PrintWriter(new FileWriter("save.txt"));
+      for (int x = 0; x < map.getLength(); x++) {
+        for (int y = 0; y < map.getHeight(); y++) {
+          Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+          while (iter.hasNext()) {
+            Creature cr = iter.next();
+            cr.save(out);
+          } 
+        } 
+      } 
+      out.close();
+      out = new PrintWriter(new FileWriter("savedItems.txt"));
+      Iterator<Item> itrItem = this.drops.iterator();
+      while (itrItem.hasNext()) {
+        Item cur = itrItem.next();
+        out.println(cur.getName());
+        out.println(String.valueOf(cur.getLoc().getX()) + "," + cur.getLoc().getY());
+      } 
+      out.close();
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    } 
+  }
+  
+  public void reset() {
+    this.drops.clear();
+    this.projectiles.clear();
+    map.clearCreatures();
+    lstInventory.clear();
+    Iterator<SpawnPoint> iter = spawnPoints.iterator();
+    while (iter.hasNext())
+      ((SpawnPoint)iter.next()).clear(); 
+    SpawnPoint.numSpawnPoints = 0;
+    Iterator<RespawnPoint> rpIter = respawnPoints.iterator();
+    while (rpIter.hasNext())
+      ((RespawnPoint)rpIter.next()).unmark(); 
+  }
+  
+  public void placeQuestObjects() {
+    this.drops.add(((Item)items.get("Power Sword")).copy(new Point(1650, 150)));
+    this.drops.add(((Item)items.get("Ice Scroll")).copy(new Point(3250, 150)));
+    this.drops.add(((Item)items.get("BNW Shirt")).copy(new Point(4950, 250)));
+    this.drops.add(((Item)items.get("Studded Leather Armor")).copy(new Point(6750, 150)));
+    this.drops.add(((Item)items.get("Patched Leather")).copy(new Point(8450, 150)));
+    this.drops.add(((Item)items.get("Fire Scroll")).copy(new Point(1650, 1450)));
+    Creature cr = ((Creature)creatureMap.get(CreatureType.Specter)).copy();
+    cr.setLoc(new Point(15850, 350));
+    map.getLoc(158, 3).addCreature(cr);
+  }
+  
+  public boolean load() {
+    reset();
+    try {
+      BufferedReader in = new BufferedReader(new FileReader("save.txt"));
+      while (in.ready()) {
+        CreatureType type = CreatureType.valueOf(in.readLine());
+        Creature cr = ((Creature)creatureMap.get(type)).copy();
+        cr.load(in);
+        if (cr.getType() == CreatureType.Player)
+          this.player = (Player)cr; 
+        map.getLoc(cr.getLoc().getX() / 100, cr.getLoc().getY() / 100).addCreature(cr);
+      } 
+      Iterator<Creature> iter = map.getLoc(this.player.getTarget().getX() / 100, this.player.getTarget().getY() / 100).getCreatures().iterator();
+      while (iter.hasNext()) {
+        Creature cr2 = iter.next();
+        if (cr2.getLoc().equals(this.player.getTarget()) && cr2 != this.player)
+          this.player.setEnemyTarget(cr2); 
+      } 
+      in.close();
+      in = new BufferedReader(new FileReader("savedItems.txt"));
+      while (in.ready()) {
+        Item cur = items.get(in.readLine());
+        String loc = in.readLine();
+        int x = Integer.parseInt(loc.substring(0, loc.indexOf(",")));
+        int y = Integer.parseInt(loc.substring(loc.indexOf(",") + 1));
+        this.drops.add(cur.copy(new Point(x, y)));
+      } 
+      in.close();
+    } catch (FileNotFoundException fnfe) {
+      showMessage("There is no saved game to load");
+      return false;
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+      return false;
+    } 
+    return true;
+  }
+  
+  public static String dateString() {
+    return (new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")).format(new Date());
+  }
+  
+  public void showMessage(String text) {
+    this.auxState = AuxState.MsgBox;
+    ((Label)this.wndMessage.getMember("label")).setText(text);
+  }
+  
+  private void drawMain(Graphics g) {
+    this.wndMain.draw(g);
+    g.setColor(Color.red);
+    g.drawRect(10, 100, 380, 490);
+    g.drawRect(410, 100, 380, 490);
+  }
+  
+  private void drawCreateAccount(Graphics g) {
+    ((Label)this.wndCreateAccount.getMember("strVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Strength)));
+    ((Label)this.wndCreateAccount.getMember("dexVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Dexterity)));
+    ((Label)this.wndCreateAccount.getMember("conVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Constitution)));
+    ((Label)this.wndCreateAccount.getMember("wisVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Wisdom)));
+    ((Label)this.wndCreateAccount.getMember("attVal")).setText(Integer.toString(this.player.getAttributePoints()));
+    ((Label)this.wndCreateAccount.getMember("lightWeaponVal")).setText(Integer.toString(this.player.getSkill(Skill.LightWeapons)));
+    ((Label)this.wndCreateAccount.getMember("heavyWeaponVal")).setText(Integer.toString(this.player.getSkill(Skill.HeavyWeapons)));
+    ((Label)this.wndCreateAccount.getMember("rangedWeaponVal")).setText(Integer.toString(this.player.getSkill(Skill.RangedWeapons)));
+    ((Label)this.wndCreateAccount.getMember("evocationVal")).setText(Integer.toString(this.player.getSkill(Skill.Evocation)));
+    ((Label)this.wndCreateAccount.getMember("skillVal")).setText(Integer.toString(this.player.getSkillPoints()));
+    this.wndCreateAccount.draw(g);
+  }
+  
+  private void drawLoadGame(Graphics g) {
+    Font tempFont = new Font("Arial", 0, 12);
+    FontMetrics metrics = g.getFontMetrics(tempFont);
+    g.setFont(tempFont);
+    g.setColor(Color.green);
+    g.drawString("There is not a whole lot here right now. You can click anywhere on the screen to get back to the main menu.", 0, metrics.getHeight());
+  }
+  
+  private void drawInfo(Graphics g) {
+    if (this.infoStart) {
+      this.wndGameIntro.draw(g);
+    } else {
+      this.wndTutorials[this.wndTutCur].draw(g);
+    } 
+  }
+  
+  private void drawCredits(Graphics g) {
+    this.wndCredits.draw(g);
+  }
+  
+  private void drawMap(Graphics g) {
+    int locX = this.player.getLoc().getX();
+    int locY = this.player.getLoc().getY();
+    int xLow = locX / 100 - 5;
+    int xHigh = xLow + 11;
+    int yLow = locY / 100 - 4;
+    int yHigh = yLow + 9;
+    if (xLow < 0)
+      xLow = 0; 
+    if (xHigh > map.getLength())
+      xHigh = map.getLength(); 
+    if (yLow < 0)
+      yLow = 0; 
+    if (yHigh > map.getHeight())
+      yHigh = map.getHeight(); 
+    int x;
+    for (x = xLow; x < xHigh; x++) {
+      for (int y = yLow; y < yHigh; y++) {
+        if (map.getLoc(x, y).getLand().getType() != LandType.Mountains && map.getLoc(x, y).getLand().getType() != LandType.Cave) {
+          g.drawImage(map.getLoc(x, y).getLand().getImg(), 400 + x * 100 - locX, 300 + y * 100 - locY, null);
+          g.drawImage(map.getLoc(x, y).getStruct().getImg(), 400 + x * 100 - locX, 300 + y * 100 - locY, null);
+        } else {
+          g.drawImage(((Land)landMap.get(LandType.Metal)).getImg(), 400 + x * 100 - locX, 300 + y * 100 - locY, null);
+        } 
+      } 
+    } 
+    for (x = xLow; x < xHigh; x++) {
+      for (int y = yLow; y < yHigh; y++) {
+        if (map.getLoc(x, y).getLand().getType() == LandType.Mountains || map.getLoc(x, y).getLand().getType() == LandType.Cave)
+          g.drawImage(map.getLoc(x, y).getLand().getImg(), 390 + x * 100 - locX, 290 + y * 100 - locY, null); 
+      } 
+    } 
+  }
+  
+  private void drawItems(Graphics g) {
+    Iterator<Item> iter = this.drops.iterator();
+    while (iter.hasNext())
+      ((Item)iter.next()).draw(g, this.player.getLoc().getX(), this.player.getLoc().getY()); 
+  }
+  
+  private synchronized void drawProjectiles(Graphics g) {
+    Iterator<Projectile> iter = this.projectiles.iterator();
+    while (iter.hasNext())
+      ((Projectile)iter.next()).draw(g, this.player.getLoc().getX(), this.player.getLoc().getY()); 
+  }
+  
+  private void drawCreatures(Graphics g) {
+    int xLow = this.player.getLoc().getX() / 100 - 5;
+    int xHigh = xLow + 11;
+    int yLow = this.player.getLoc().getY() / 100 - 4;
+    int yHigh = yLow + 9;
+    if (xLow < 0)
+      xLow = 0; 
+    if (xHigh > map.getLength())
+      xHigh = map.getLength(); 
+    if (yLow < 0)
+      yLow = 0; 
+    if (yHigh > map.getHeight())
+      yHigh = map.getHeight(); 
+    for (int x = xLow; x < xHigh; x++) {
+      for (int y = yLow; y < yHigh; y++) {
+        Iterator<Creature> iter = map.getLoc(x, y).getCreatures().iterator();
+        while (iter.hasNext())
+          ((Creature)iter.next()).draw(g, this.player.getLoc().getX(), this.player.getLoc().getY()); 
+      } 
+    } 
+  }
+  
+  private void drawStatDisplay(Graphics g) {
+    this.lblGemVal.setText("Gem Value: " + this.player.getGemValue());
+    this.lblSpellCost.setText("Spell Cost: " + ((this.player.getGemValue() + 1) / 2));
+    this.wndStatDisplay.draw(g);
+    g.drawString(this.player.getName(), 20, 562);
+    g.drawString("Level " + this.player.getLevel(), 20, 574);
+    g.drawString("Relics Acquired: " + this.player.getRelicCount() + "/4", 20, 591);
+    g.setColor(Color.yellow);
+    g.drawRect(240, 578, 80, 15);
+    g.fillRect(240, 578, 80 * this.player.getExperience() / (int)((Math.pow(this.player.getLevel(), 2.0D) + this.player.getLevel()) * 500.0D), 15);
+    g.setColor(Color.green);
+    g.drawRect(125, 557, 80, 15);
+    g.fillRect(125, 557, 80 * this.player.getHitpoints() / this.player.getMaxHitpoints(), 15);
+    g.setColor(Color.blue);
+    g.drawRect(240, 557, 80, 15);
+    g.fillRect(240, 557, 80 * this.player.getManapoints() / this.player.getMaxManapoints(), 15);
+    g.setColor(Color.red);
+    g.drawString("HP:", 100, 569);
+    g.drawString("MP:", 215, 569);
+    g.drawString("Experience:", 170, 591);
+    g.drawString(String.valueOf(this.player.getExperience()) + "/" + ((int)(Math.pow(this.player.getLevel(), 2.0D) + this.player.getLevel()) * 500), 245, 590);
+    g.drawString(String.valueOf(this.player.getHitpoints()) + "/" + this.player.getMaxHitpoints(), 130, 569);
+    g.drawString(String.valueOf(this.player.getManapoints()) + "/" + this.player.getMaxManapoints(), 245, 569);
+    this.player.getWeapon().drawStatic(g, 450 + this.wndStatDisplay.getX(), this.wndStatDisplay.getY());
+    int mouseX = (MouseInfo.getPointerInfo().getLocation()).x;
+    int mouseY = (MouseInfo.getPointerInfo().getLocation()).y;
+    if (450 + this.wndStatDisplay.getX() <= mouseX && mouseX < 500 + this.wndStatDisplay.getX() && this.wndStatDisplay.getY() <= mouseY && mouseY < 50 + this.wndStatDisplay.getY()) {
+      String itemType;
+      Item i = (this.player.getWeapon()).baseWeapon;
+      Window popup = new Window("popup", mouseX - 126, mouseY - 100, 126, 100, false);
+      Font font12 = new Font("Arial", 0, 12);
+      switch (i.getType()) {
+        case LightWeapon:
+          itemType = "Light Weapon";
+          break;
+        case HeavyWeapon:
+          itemType = "Heavy Weapon";
+          break;
+        case RangedWeapon:
+          itemType = "Ranged Weapon";
+          break;
+        default:
+          itemType = i.getType().toString();
+          break;
+      } 
+      popup.add(new Label("info", 8, 5, 110, 15, "Equipped Weapon", font12));
+      popup.add(new Label("name", 8, 20, 110, 15, i.getName(), font12));
+      popup.add(new Label("type", 8, 50, 110, 15, itemType, font12));
+      popup.add(new Label("damage", 8, 65, 110, 15, "Damage: " + ((Weapon)i).getDamage() + "(" + this.player.getWeapon().getDamage() + ")", font12));
+      popup.add(new Label("damage", 8, 80, 110, 15, "Cooldown: " + (((Weapon)i).getAttackSpeed() / 10.0D) + "(" + (this.player.getWeapon().getAttackSpeed() / 10.0D) + ")" + " s", font12));
+      popup.draw(g);
+    } 
+  }
+  
+  private void drawGameMenu(Graphics g) {}
+  
+  private void drawGameStats(Graphics g) {
+    ((Label)this.wndGameStats.getMember("strVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Strength)));
+    ((Label)this.wndGameStats.getMember("dexVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Dexterity)));
+    ((Label)this.wndGameStats.getMember("conVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Constitution)));
+    ((Label)this.wndGameStats.getMember("wisVal")).setText(Integer.toString(this.player.getAttribute(Attribute.Wisdom)));
+    ((Label)this.wndGameStats.getMember("lightWeaponVal")).setText(Integer.toString(this.player.getSkill(Skill.LightWeapons)));
+    ((Label)this.wndGameStats.getMember("heavyWeaponVal")).setText(Integer.toString(this.player.getSkill(Skill.HeavyWeapons)));
+    ((Label)this.wndGameStats.getMember("rangedWeaponVal")).setText(Integer.toString(this.player.getSkill(Skill.RangedWeapons)));
+    ((Label)this.wndGameStats.getMember("evocationVal")).setText(Integer.toString(this.player.getSkill(Skill.Evocation)));
+    ((Label)this.wndGameStats.getMember("skillVal")).setText(Integer.toString(this.player.getSkillPoints()));
+    this.wndGameStats.draw(g);
+  }
+  
+  private void drawGameGems(Graphics g) {
+    this.wndGameGems.draw(g);
+    int mouseX = (MouseInfo.getPointerInfo().getLocation()).x;
+    int mouseY = (MouseInfo.getPointerInfo().getLocation()).y;
+    Window w = this.wndGameGems;
+    if (w.getX() < mouseX && mouseX < w.getX() + w.getWidth() - 20 && w.getY() < mouseY && mouseY < w.getY() + w.getHeight())
+      this.player.drawInventory(g, lstGems, this.wndGameGems.getX() + 1, this.wndGameGems.getY() + 1); 
+  }
+  
+  private void drawGameInventory(Graphics g) {
+    this.wndGameInventory.draw(g);
+    int mouseX = (MouseInfo.getPointerInfo().getLocation()).x;
+    int mouseY = (MouseInfo.getPointerInfo().getLocation()).y;
+    Window w = this.wndGameInventory;
+    if (w.getX() < mouseX && mouseX < w.getX() + w.getWidth() - 20 && w.getY() < mouseY && mouseY < w.getY() + w.getHeight())
+      this.player.drawInventory(g, lstInventory, this.wndGameInventory.getX() + 1, this.wndGameInventory.getY() + 1); 
+  }
+  
+  private void drawGameMap(Graphics g) {
+    this.wndGameMap.draw(g);
+    g.drawImage(this.imgMap, this.wndGameMap.getX() + 10, this.wndGameMap.getY() + 10, null);
+  }
+  
+  private void selectText(Textbox text) {
+    if (this.selectedText != null)
+      this.selectedText.setSelected(false); 
+    this.selectedText = text;
+    if (text != null)
+      text.setSelected(true); 
+  }
+  
+  private static DisplayMode getBestDisplayMode(GraphicsDevice device) {
+    for (int x = 0; x < BEST_DISPLAY_MODES.length; x++) {
+      DisplayMode[] modes = device.getDisplayModes();
+      for (int i = 0; i < modes.length; i++) {
+        if (modes[i].getWidth() == BEST_DISPLAY_MODES[x].getWidth() && modes[i].getHeight() == BEST_DISPLAY_MODES[x].getHeight() && modes[i].getBitDepth() == BEST_DISPLAY_MODES[x].getBitDepth())
+          return BEST_DISPLAY_MODES[x]; 
+      } 
+    } 
+    return null;
+  }
+  
+  public static void chooseBestDisplayMode(GraphicsDevice device) {
+    DisplayMode best = getBestDisplayMode(device);
+    if (best != null)
+      device.setDisplayMode(best); 
+  }
+  
+  public void mousePressed(MouseEvent e) {
+    boolean targetSet;
+    if (!this.started)
+      return; 
+    switch (this.auxState) {
+      case None:
+        switch (this.gameState) {
+          case Main:
+            if (this.wndMain.getMember("new game").isClicked(e.getX(), e.getY())) {
+              this.player = (Player)((Creature)creatureMap.get(CreatureType.Player)).copy();
+              this.gameState = GameState.CreateAccount;
+              break;
+            } 
+            if (this.wndMain.getMember("load game").isClicked(e.getX(), e.getY())) {
+              if (load()) {
+                this.player.setTimeLoggedOn(System.currentTimeMillis());
+                this.gameState = GameState.Game;
+              } 
+              break;
+            } 
+            if (this.wndMain.getMember("game info").isClicked(e.getX(), e.getY())) {
+              this.infoStart = true;
+              this.gameState = GameState.Info;
+              break;
+            } 
+            if (this.wndMain.getMember("credits").isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.Credits;
+              break;
+            } 
+            if (this.wndMain.getMember("quit").isClicked(e.getX(), e.getY()))
+              this.done = true; 
+            break;
+          case CreateAccount:
+            if (this.wndCreateAccount.getMember("user").isClicked(e.getX(), e.getY())) {
+              selectText((Textbox)this.wndCreateAccount.getMember("user"));
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("create").isClicked(e.getX(), e.getY())) {
+              String user = ((Textbox)this.wndCreateAccount.getMember("user")).getText();
+              if (user.equals("")) {
+                showMessage("The username is empty");
+                break;
+              } 
+              if (this.player.getAttributePoints() > 0) {
+                showMessage("You still have attribute points remaining");
+                break;
+              } 
+              if (this.player.getSkillPoints() > 0) {
+                showMessage("You still have skill points remaining");
+                break;
+              } 
+              this.wndCreateAccount.clear();
+              reset();
+              placeQuestObjects();
+              this.player.setName(user);
+              this.player.setLoc(this.startPoint);
+              this.player.setTarget(this.player.getLoc());
+              this.player.setWeapon((Weapon)items.get("Branch"));
+              this.player.pickUp((this.player.getWeapon()).baseWeapon);
+              map.getLoc(this.startPoint.getX() / 100, this.startPoint.getY() / 100).addCreature(this.player);
+              this.gameState = GameState.GameIntro;
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("cancel").isClicked(e.getX(), e.getY())) {
+              selectText(null);
+              this.wndCreateAccount.clear();
+              this.gameState = GameState.Main;
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("strength-").isClicked(e.getX(), e.getY())) {
+              this.player.repickAttribute(Attribute.Strength);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("strength+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateAttribute(Attribute.Strength);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("dexterity-").isClicked(e.getX(), e.getY())) {
+              this.player.repickAttribute(Attribute.Dexterity);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("dexterity+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateAttribute(Attribute.Dexterity);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("constitution-").isClicked(e.getX(), e.getY())) {
+              this.player.repickAttribute(Attribute.Constitution);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("constitution+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateAttribute(Attribute.Constitution);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("wisdom-").isClicked(e.getX(), e.getY())) {
+              this.player.repickAttribute(Attribute.Wisdom);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("wisdom+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateAttribute(Attribute.Wisdom);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("lightWeapon-").isClicked(e.getX(), e.getY())) {
+              this.player.repickSkill(Skill.LightWeapons);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("lightWeapon+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.LightWeapons);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("heavyWeapon-").isClicked(e.getX(), e.getY())) {
+              this.player.repickSkill(Skill.HeavyWeapons);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("heavyWeapon+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.HeavyWeapons);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("rangedWeapon-").isClicked(e.getX(), e.getY())) {
+              this.player.repickSkill(Skill.RangedWeapons);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("rangedWeapon+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.RangedWeapons);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("evocation-").isClicked(e.getX(), e.getY())) {
+              this.player.repickSkill(Skill.Evocation);
+              break;
+            } 
+            if (this.wndCreateAccount.getMember("evocation+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.Evocation);
+              break;
+            } 
+            this.wndCreateAccount.handleEvent(e);
+            break;
+          case Info:
+            if (this.infoStart) {
+              this.infoStart = false;
+              break;
+            } 
+            if (this.wndTutOptions.getMember("previous").isClicked(e.getX(), e.getY())) {
+              if (this.wndTutCur != 0)
+                this.wndTutCur--; 
+            } else if (this.wndTutOptions.getMember("next").isClicked(e.getX(), e.getY())) {
+              this.wndTutCur++;
+              if (this.wndTutCur > 12) {
+                this.gameState = GameState.Main;
+                this.wndTutCur = 0;
+              } 
+            } else if (this.wndTutOptions.getMember("skip").isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.Main;
+              this.wndTutCur = 0;
+            } 
+            ((Label)this.wndTutOptions.getMember("num")).setText(String.valueOf(this.wndTutCur + 1) + " / 13");
+            break;
+          case Credits:
+            this.gameState = GameState.Main;
+            break;
+          case Victory:
+            this.gameState = GameState.Credits;
+            break;
+          case GameIntro:
+            this.gameState = GameState.GameInfo;
+            break;
+          case GameInfo:
+            if (this.wndTutOptions.getMember("previous").isClicked(e.getX(), e.getY())) {
+              if (this.wndTutCur != 0)
+                this.wndTutCur--; 
+            } else if (this.wndTutOptions.getMember("next").isClicked(e.getX(), e.getY())) {
+              this.wndTutCur++;
+              if (this.wndTutCur > 12) {
+                this.gameState = GameState.Game;
+                this.player.setTimeLoggedOn(System.currentTimeMillis());
+                this.wndTutCur = 0;
+              } 
+            } else if (this.wndTutOptions.getMember("skip").isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.Game;
+              this.player.setTimeLoggedOn(System.currentTimeMillis());
+              this.wndTutCur = 0;
+            } 
+            ((Label)this.wndTutOptions.getMember("num")).setText(String.valueOf(this.wndTutCur + 1) + " / 13");
+            break;
+          case Game:
+            targetSet = false;
+            if (e.getY() < 550) {
+              if (this.player.isDying())
+                return; 
+              int newX = this.player.getLoc().getX() + e.getX() - 400;
+              int newY = this.player.getLoc().getY() + e.getY() - 300;
+              if (e.getButton() == 1) {
+                Iterator<Item> itemIter = this.drops.iterator();
+                while (itemIter.hasNext()) {
+                  Item item = itemIter.next();
+                  if (item.getLoc().getX() - 25 <= newX && newX <= item.getLoc().getX() + 25 && 
+                    item.getLoc().getY() - 25 <= newY && newY <= item.getLoc().getY() + 25) {
+                    this.player.setTarget(item.getLoc());
+                    targetSet = true;
+                  } 
+                } 
+                for (int x = 0; x < map.getLength(); x++) {
+                  for (int y = 0; y < map.getHeight(); y++) {
+                    Iterator<Creature> crIter = map.getLoc(x, y).getCreatures().iterator();
+                    while (crIter.hasNext()) {
+                      Creature cr = crIter.next();
+                      if (cr.getLoc().getX() - cr.getModel().getWidth() / 2 <= newX && newX <= cr.getLoc().getX() + cr.getModel().getWidth() / 2 && cr != this.player && 
+                        cr.getLoc().getY() - cr.getModel().getHeight() <= newY && newY <= cr.getLoc().getY()) {
+                        this.player.setEnemyTarget(cr);
+                        targetSet = true;
+                      } 
+                    } 
+                  } 
+                } 
+                if (map.getLoc((int)Math.floor((newX / 100)), (int)Math.floor((newY / 100))).isPassable() && !targetSet) {
+                  this.player.setTarget(new Point(newX, newY));
+                  this.player.setEnemyTarget(null);
+                } 
+                if (this.player.getEnemyTarget() != null && this.player.getWeapon().getType() == ItemType.RangedWeapon) {
+                  Projectile proj = this.player.attack(this.player.getEnemyTarget(), AttackType.Weapon);
+                  if (proj != null) {
+                    proj.setCreator(this.player);
+                    proj.applySpawnEffects();
+                    addProjectile(proj);
+                  } 
+                  this.player.setTarget(this.player.getLoc());
+                  this.player.setEnemyTarget(null);
+                } 
+                break;
+              } 
+              if (e.getButton() == 3 && 
+                this.player.getSpell() != null && this.player.getManapoints() >= ((ManaDrain)this.player.getSpell().getSpawnEffect(EffectType.ManaDrain)).getMana()) {
+                Point loc = new Point(this.player.getLoc());
+                loc.setY(loc.getY() - 55);
+                Projectile proj = this.player.attack(new Point(newX, newY), AttackType.Spell);
+                if (proj != null) {
+                  proj.setCreator(this.player);
+                  proj.applySpawnEffects();
+                  addProjectile(proj);
+                } 
+              } 
+              break;
+            } 
+            if (this.btnMenu.isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.Main;
+              this.player.updateTimePlayed();
+              save();
+              break;
+            } 
+            if (this.btnStats.isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.GameStats;
+              break;
+            } 
+            if (this.btnGems.isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.GameGems;
+              break;
+            } 
+            if (this.btnInventory.isClicked(e.getX(), e.getY())) {
+              this.gameState = GameState.GameInventory;
+              break;
+            } 
+            if (this.btnMap.isClicked(e.getX(), e.getY()))
+              this.gameState = GameState.GameMap; 
+            break;
+          case GameStats:
+            if (this.wndGameStats.getMember("lightWeapon+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.LightWeapons);
+              break;
+            } 
+            if (this.wndGameStats.getMember("heavyWeapon+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.HeavyWeapons);
+              break;
+            } 
+            if (this.wndGameStats.getMember("rangedWeapon+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.RangedWeapons);
+              break;
+            } 
+            if (this.wndGameStats.getMember("evocation+").isClicked(e.getX(), e.getY())) {
+              this.player.allocateSkill(Skill.Evocation);
+              break;
+            } 
+            this.gameState = GameState.Game;
+            break;
+          case GameGems:
+            if (this.wndGameGems.isClicked(e.getX(), e.getY())) {
+              if (e.getX() < this.wndGameGems.getX() + this.wndGameGems.getWidth() - 20) {
+                int x = (e.getX() - this.wndGameGems.getX() - 1) / 50;
+                int y = (e.getY() - this.wndGameGems.getY() - 1 + lstGems.getTextStart()) / 50;
+                if (x + 4 * y >= 0 && x + 4 * y < lstGems.getList().size()) {
+                  Item i = this.player.getGems().get(x + y * 4);
+                  switch (i.getType()) {
+                    case Gem:
+                      this.player.deactivateGem((Gem)i);
+                      break;
+                  } 
+                } 
+              } 
+              this.wndGameGems.handleEvent(e);
+              break;
+            } 
+            this.gameState = GameState.Game;
+            break;
+          case GameInventory:
+            if (this.wndGameInventory.isClicked(e.getX(), e.getY())) {
+              if (e.getX() < this.wndGameInventory.getX() + this.wndGameInventory.getWidth() - 20) {
+                int x = (e.getX() - this.wndGameInventory.getX() - 1) / 50;
+                int y = (e.getY() - this.wndGameInventory.getY() - 1 + lstInventory.getTextStart()) / 50;
+                if (x + 4 * y >= 0 && x + 4 * y < this.player.getInventory().size()) {
+                  Item i = this.player.getInventory().get(x + y * 4);
+                  switch (i.getType()) {
+                    case HeavyWeapon:
+                      if (this.player.getAttribute(Attribute.Strength) < 12) {
+                        showMessage("You must have at least 12 Strength");
+                        break;
+                      } 
+                    case LightWeapon:
+                    case RangedWeapon:
+                      this.player.setWeapon((Weapon)i);
+                      break;
+                    case Gem:
+                      if (!this.player.activateGem((Gem)i))
+                        showMessage("You must have a higher Evocation skill"); 
+                      break;
+                  } 
+                } 
+              } 
+              this.wndGameInventory.handleEvent(e);
+              break;
+            } 
+            this.gameState = GameState.Game;
+            break;
+          case GameMap:
+            this.gameState = GameState.Game;
+            break;
+        } 
+        break;
+      case MsgBox:
+        if (this.wndMessage.getMember("button").isClicked(e.getX(), e.getY()))
+          this.auxState = AuxState.None; 
+        break;
+    } 
+  }
+  
+  public void mouseReleased(MouseEvent e) {
+  }
+  
+  public void mouseEntered(MouseEvent e) {
+  }
+  
+  public void mouseExited(MouseEvent e) {
+  }
+  
+  public void mouseClicked(MouseEvent e) {
+  }
+  
+  public void keyTyped(KeyEvent e) {
+  }
+  
+  public void keyPressed(KeyEvent e) {
+    if (this.selectedText != null)
+      this.selectedText.handleEvent(e); 
+    if (e.getKeyCode() == 27)
+      if (this.gameState == GameState.Game) {
+        this.gameState = GameState.Main;
+        save();
+      } else {
+        this.done = true;
+      }  
+  }
+
+  public void keyReleased(KeyEvent e) {
+  }
+
+  public static void main(String[] args) {
+    try {
+      PrintStream st = new PrintStream(new FileOutputStream("err.txt", true));
+      System.setErr(st);
+      System.setOut(st);
+      System.out.println("-----[ Session started on " + dateString() + " ]-----");
+      GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
+      GraphicsDevice device = env.getDefaultScreenDevice();
+      new LostHavenRPG(device);
+    } catch (Exception e) {
+      e.printStackTrace();
     }
-    
-    private void initGUIElements() {    	
-    	Font font10 = new Font("Arial", Font.PLAIN, 10);
-    	Font font11 = new Font("Arial", Font.PLAIN, 11);
-    	Font font12 = new Font("Arial", Font.PLAIN, 12);
-    	Font font14 = new Font("Arial", Font.PLAIN, 14);
-    	Font font24 = new Font("Arial", Font.PLAIN, 24);
-    	
-    	wndMain = new gamegui.Window("main", 0, 0, 800, 600, true);
-    	
-    	Animation anmTitle = new Animation("title", 144, 0, 512, 95, 1000/12);
-    	
-    	try {
-    		anmTitle.addFrame(ImageIO.read(getClass().getResource("images/Frame1.png")));
-    		anmTitle.addFrame(ImageIO.read(getClass().getResource("images/Frame2.png")));
-    		anmTitle.addFrame(ImageIO.read(getClass().getResource("images/Frame3.png")));
-    		anmTitle.addFrame(ImageIO.read(getClass().getResource("images/Frame4.png")));
-    		anmTitle.addFrame(ImageIO.read(getClass().getResource("images/Frame5.png")));
-    	}catch(IOException ioe) {
-    		ioe.printStackTrace();
-    	}
-    	wndMain.add(anmTitle);
-        
-    	wndMain.add(new gamegui.Button("new game", 500, 140, 200, 40, "New Game", font12));
-    	wndMain.add(new gamegui.Button("load game", 500, 230, 200, 40, "Load Game", font12));
-    	wndMain.add(new gamegui.Button("game info", 500, 320, 200, 40, "Game Information", font12));
-    	wndMain.add(new gamegui.Button("credits", 500, 410, 200, 40, "Credits", font12));
-    	wndMain.add(new gamegui.Button("quit", 500, 500, 200, 40, "Quit", font12));
-    	
-    	wndCreateAccount = new gamegui.Window("create account", 0, 0, 800, 600, true);
-    	
-    	rdgGenderSelection = new RadioGroup("gender selection", 400, 315, 190, 30, "Gender:", font12);
-    	
-    	rdgGenderSelection.add(new RadioButton("male", 438, 318, 24, 24, "Male", font11, false));
-    	rdgGenderSelection.add(new RadioButton("female", 528, 318, 24, 24, "Female", font11, false));
-    	
-    	wndCreateAccount.add(new gamegui.Label("title", 250, 15, 300, 20, "Create an Account", font24, true));
-    	wndCreateAccount.add(new Textbox("user", 400, 150, 190, 30, "Username:", font12, false));
-    	wndCreateAccount.add(rdgGenderSelection);
-    	wndCreateAccount.add(new gamegui.Label("show class", 330, 370, 70, 30, "None", font12, false));
-    	wndCreateAccount.add(new gamegui.Button("choose class", 400, 370, 190, 30, "Choose Your Class", font12));
-    	wndCreateAccount.add(new gamegui.Button("create", 245, 520, 140, 30, "Create", font12));
-    	wndCreateAccount.add(new gamegui.Button("cancel", 415, 520, 140, 30, "Cancel", font12));
-    
-    	wndChooseClass = new gamegui.Window("choose class", 0, 0, 800, 600, true);
-    	
-    	rdgClassSelection = new RadioGroup("class selection", 0, 0, 0, 0, "", font12);
-    	
-    	rdgClassSelection.add(new RadioButton("fighter", 138, 88, 24, 24, "Fighter", font14, true));
-    	rdgClassSelection.add(new RadioButton("ranger", 138, 158, 24, 24, "Ranger", font14, true));
-    	rdgClassSelection.add(new RadioButton("barbarian", 138, 228, 24, 24, "Barbarian", font14, true));
-    	rdgClassSelection.add(new RadioButton("sorceror", 138, 298, 24, 24, "Sorceror", font14, true));
-    	rdgClassSelection.add(new RadioButton("druid", 138, 368, 24, 24, "Druid", font14, true));
-    	rdgClassSelection.add(new RadioButton("wizard", 138, 438, 24, 24, "Wizard", font14, true));
-    	
-    	wndChooseClass.add(new gamegui.Label("title", 250, 15, 300, 20, "Choose a Character", font24, true));
-    	wndChooseClass.add(rdgClassSelection);
-    	wndChooseClass.add(new gamegui.Label("fighter", 170, 114, 170, 0, "A resolute and steadfast champion who has perfected the art of battle and his skill in melee weapons", font10, false));
-    	wndChooseClass.add(new gamegui.Label("ranger", 170, 184, 170, 0, "A skilled combatant who sneaks up on his opponents or shoots them from afar before they know it", font10, false));
-    	wndChooseClass.add(new gamegui.Label("barbarian", 170, 254, 170, 0, "A wild warrior who is unstoppable in battle and uses his own fury to strengthen his attacks", font10, false));
-    	wndChooseClass.add(new gamegui.Label("sorceror", 170, 324, 170, 0, "A chaotic spellcaster who uses his charisma and force of will to power his spells", font10, false));
-    	wndChooseClass.add(new gamegui.Label("druid", 170, 394, 170, 0, "A mystical enchanter who relies on the power of nature and his wisdom to work his magic", font10, false));
-    	wndChooseClass.add(new gamegui.Label("wizard", 170, 464, 170, 0, "A methodical and studious character who studies his opponents to know how to best attack them", font10, false));
-    	wndChooseClass.add(new gamegui.Button("select", 245, 520, 140, 30, "Select", font12));
-    	wndChooseClass.add(new gamegui.Button("cancel", 415, 520, 140, 30, "Cancel", font12));
-    	
-    	wndMessage = new gamegui.Window("message", 290, 135, 220, 160, false);
-		wndMessage.add(new gamegui.Label("label", 70, 15, 80, 12, "none", font12, true));
-		wndMessage.add(new gamegui.Button("button", 70, 115, 80, 30, "OK", font12));
-    }
-    
-    private void loadMap() {
-    	landMap = new HashMap<LandType, Land>();
-    	structMap = new HashMap<StructureType, Structure>();
-    	BufferedImage nullImg = null;
-    	
-    	try {
-    		girl = ImageIO.read(getClass().getResource("images/ArmoredGirl.png"));
-    		guy = ImageIO.read(getClass().getResource("images/ArmoredGuy.png"));
-    	}catch(IOException ioe) {
-    		ioe.printStackTrace();
-    	}
-    		
-    	landMap.put(LandType.Ocean, new Land(LandType.Ocean, "Ocean.png", false));
-    	landMap.put(LandType.Grass, new Land(LandType.Grass, "Grass.png", true));
-    	
-    	structMap.put(StructureType.None, new Structure(StructureType.None, nullImg, true));
-    	structMap.put(StructureType.BlueOrb, new Structure(StructureType.BlueOrb, "Blue Orb.png", false));
-    	structMap.put(StructureType.Cave, new Structure(StructureType.Cave, "Cave.png", false));
-    	structMap.put(StructureType.Gravestone, new Structure(StructureType.Gravestone, "Gravestone.png", false));
-    	structMap.put(StructureType.GraveyardFence1, new Structure(StructureType.GraveyardFence1, "HorGrave.png", false));
-    	structMap.put(StructureType.GraveyardFence2, new Structure(StructureType.GraveyardFence2, "VerGrave.png", false));
-    	structMap.put(StructureType.PicketFence1, new Structure(StructureType.PicketFence1, "HorPalisade.png", false));
-    	structMap.put(StructureType.PicketFence2, new Structure(StructureType.PicketFence2, "VerPalisade.png", false));
-    	structMap.put(StructureType.Hut, new Structure(StructureType.Hut, "Hut.png", false));
-    	structMap.put(StructureType.WitchHut, new Structure(StructureType.WitchHut, "Witch Hut.png", false));
-    	structMap.put(StructureType.Tent, new Structure(StructureType.Tent, "Tent.png", false));
-    	structMap.put(StructureType.LargeTent, new Structure(StructureType.LargeTent, "LargeTent.png", false));
-    	structMap.put(StructureType.House, new Structure(StructureType.House, "House.png", false));
-    	structMap.put(StructureType.Tree, new Structure(StructureType.Tree, "Trees.png", false));
-    	structMap.put(StructureType.BlueOrb, new Structure(StructureType.BlueOrb, "Blue Orb.png", false));
-    	structMap.put(StructureType.RedOrb, new Structure(StructureType.RedOrb, "Red Orb.png", false));
-    	structMap.put(StructureType.LoginPedestal, new Structure(StructureType.LoginPedestal, "YellowPedestal.png", true));
-    	structMap.put(StructureType.RejuvenationPedestal, new Structure(StructureType.RejuvenationPedestal, "PurplePedestal.png", true));
-    	structMap.put(StructureType.LifePedestal, new Structure(StructureType.LifePedestal, "RedPedestal.png", true));
-    	structMap.put(StructureType.ManaPedestal, new Structure(StructureType.ManaPedestal, "BluePedestal.png", true));
-    }
-    
-    private void move() {
-    	double dist = player.getSpeed()*(System.currentTimeMillis()-player.getLastMoved())/1000;
-    	Point lastLoc = player.getLoc();
-    	Point target = player.getTarget();
-    	Point newLoc;
-    	
-    	player.setLastMoved(System.currentTimeMillis());
-    	if(Point.dist(lastLoc, player.getTarget()) <= dist)
-    		player.setLoc(player.getTarget());
-    	else {
-    		int xDif = (int)(Point.xDif(lastLoc, target)*dist/Point.dist(lastLoc, target));
-    		int yDif = (int)(Point.yDif(lastLoc, target)*dist/Point.dist(lastLoc, target));
-    		newLoc = new Point(lastLoc.getX(), lastLoc.getXMin()+xDif, lastLoc.getY(), lastLoc.getYMin()+yDif);
-    		newLoc.setX(newLoc.getX()+newLoc.getXMin()/100);
-    		newLoc.setXMin(newLoc.getXMin()%100);
-    		newLoc.setY(newLoc.getY()+newLoc.getYMin()/100);
-    		newLoc.setYMin(newLoc.getYMin()%100);
-    		if(newLoc.getXMin()<0) {
-    			newLoc.setX(newLoc.getX()-1);
-    			newLoc.setXMin(newLoc.getXMin()+100);
-    		}else if(newLoc.getYMin()<0) {
-    			newLoc.setY(newLoc.getY()-1);
-    			newLoc.setYMin(newLoc.getYMin()+100);
-    		}
-    		if(map.getLoc(newLoc.getX()/100, newLoc.getY()/100).isPassable())
-    			player.setLoc(newLoc);
-    		else
-    			player.setTarget(player.getLoc());
-    	}
-    }
-    
-    private void render(Graphics g) {
-    	g.setColor(Color.black);
-        g.fillRect(0, 0, 800, 600);
-        
-        switch(gameState) {
-		case Main:
-			drawMain(g);
-			break;
-		case CreateAccount:
-			drawCreateAccount(g);
-			break;
-		case CreateClass:
-			drawCreateClass(g);
-			break;
-		case LoadGame:
-			drawLoadGame(g);
-			break;
-		case Info:
-			drawInfo(g);
-			break;
-		case Credits:
-			drawCredits(g);
-			break;
-		case Game:
-			calculateMapVertices();
-            drawMap(g);
-            drawItems(g);
-            drawCreatures(g);
-            drawChar(g);
-            drawStatDisplay(g);
-            drawChat(g);
-			break;
-		case GameMenu:
-			calculateMapVertices();
-            drawMap(g);
-            drawItems(g);
-            drawCreatures(g);
-            drawChar(g);
-            drawStatDisplay(g);
-            drawChat(g);
-            drawGameMenu(g);
-			break;
-		case GameInventory:
-			calculateMapVertices();
-            drawMap(g);
-            drawItems(g);
-            drawCreatures(g);
-            drawChar(g);
-            drawStatDisplay(g);
-            drawChat(g);
-            drawGameInventory(g);
-			break;
-		case GameStats:
-			calculateMapVertices();
-            drawMap(g);
-            drawItems(g);
-            drawCreatures(g);
-            drawChar(g);
-            drawStatDisplay(g);
-            drawChat(g);
-            drawGameStats(g);
-			break;
-		}
-        
-        switch(auxState) {
-		case None:
-			break;
-		case MsgBox:
-			wndMessage.draw(g);
-			break;
-		}
-    }
-    
-    public static String dateString() {
-		return new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(new Date());
-	}
-    
-    public void showMessage(String text) {
-    	auxState = AuxState.MsgBox;
-    	((gamegui.Label)wndMessage.getMember("label")).setText(text);
-    }
-    
-    private void calculateMapVertices() {
-    
-    }
-    
-    private void drawMain(Graphics g) {
-    	wndMain.draw(g);
-    	
-    	g.setColor(Color.red);
-		g.drawRect(10, 100, 380, 490);
-		g.drawRect(410, 100, 380, 490);
-    }
-    
-    private void drawCreateAccount(Graphics g) {    	
-    	wndCreateAccount.draw(g);
-    }
-    
-    private void drawCreateClass(Graphics g) {
-    	wndChooseClass.draw(g);
-    }
-    
-    private void drawLoadGame(Graphics g) {
-    	Font tempFont = new Font("Arial", Font.PLAIN, 12);
-    	FontMetrics metrics = g.getFontMetrics(tempFont);
-    	
-    	g.setFont(tempFont);
-    	g.setColor(Color.green);
-    	g.drawString("There is not a whole lot here right now. You can click anywhere on the screen to get back to the main menu.", 0, metrics.getHeight());
-    }
-    
-    private void drawInfo(Graphics g) {
-    	Font tempFont = new Font("Arial", Font.PLAIN, 12);
-    	FontMetrics metrics = g.getFontMetrics(tempFont);
-    	
-    	g.setFont(tempFont);
-    	g.setColor(Color.green);
-    	g.drawString("There is not a whole lot here right now. You can click anywhere on the screen to get back to the main menu.", 0, metrics.getHeight());
-    }
-    
-    private void drawCredits(Graphics g) {
-    	Font tempFont = new Font("Arial", Font.PLAIN, 12);
-    	FontMetrics metrics = g.getFontMetrics(tempFont);
-    	
-    	g.setFont(tempFont);
-    	g.setColor(Color.green);
-    	g.drawString("There is not a whole lot here right now. You can click anywhere on the screen to get back to the main menu.", 0, metrics.getHeight());
-    }
-    
-    private void drawMap(Graphics g) {
-    	int locX = player.getLoc().getX();
-    	int locY = player.getLoc().getY();
-    	int xLow = locX/100-4;
-    	int xHigh = xLow+9;
-    	int yLow = locY/100-3;
-    	int yHigh = yLow+7;
-    	
-    	if(xLow<0)
-    		xLow = 0;
-    	if(xHigh>=map.getLength())
-    		xHigh = map.getLength()-1;
-    	if(yLow<0)
-    		yLow = 0;
-    	if(yHigh>=map.getHeight())
-    		yHigh = map.getHeight()-1;
-
-		for(int x=xLow; x<xHigh; x++) {
-			for(int y=yLow; y<yHigh; y++) {
-				g.drawImage(map.getLoc(x, y).getLand().getImg(), 400+x*100-locX, 300+y*100-locY, null);				
-				g.drawImage(map.getLoc(x, y).getStruct().getImg(), 400+x*100-locX, 300+y*100-locY, null);
-			}
-		}
-    }
-    
-    private void drawItems(Graphics g) {
-    
-    }
-    
-    private void drawCreatures(Graphics g) {
-    
-    }
-    
-    private void drawChar(Graphics g) {
-    	switch(player.getGender()) {
-		case Female:
-			g.drawImage(girl, 375, 200, null);
-			break;
-		case Male:
-			g.drawImage(guy, 375, 200, null);
-			break;
-		}
-    }
-    
-    private void drawStatDisplay(Graphics g) {
-    
-    }
-    
-    private void drawChat(Graphics g) {
-    
-    }
-    
-    private void drawGameMenu(Graphics g) {
-    
-    }
-    
-    private void drawGameInventory(Graphics g) {
-    
-    }
-    
-    private void drawGameStats(Graphics g) {
-    
-    }
-    
-    private void selectText(Textbox text) {
-    	if(selectedText != null)
-			selectedText.setSelected(false);
-		selectedText = text;
-		
-		if(text != null)
-			text.setSelected(true);
-    }
-    
-    private static DisplayMode getBestDisplayMode(GraphicsDevice device) {
-        for (int x = 0; x < BEST_DISPLAY_MODES.length; x++) {
-            DisplayMode[] modes = device.getDisplayModes();
-            for (int i = 0; i < modes.length; i++) {
-                if (modes[i].getWidth() == BEST_DISPLAY_MODES[x].getWidth()
-                   && modes[i].getHeight() == BEST_DISPLAY_MODES[x].getHeight()
-                   && modes[i].getBitDepth() == BEST_DISPLAY_MODES[x].getBitDepth()) 
-                {
-                    return BEST_DISPLAY_MODES[x];
-                }
-            }
-        }
-        return null;
-    }
-    
-    public static void chooseBestDisplayMode(GraphicsDevice device) {
-        DisplayMode best = getBestDisplayMode(device);
-        if (best != null) {
-            device.setDisplayMode(best);
-        }
-    }
-    
-    public void mousePressed(MouseEvent e) {
-    	switch(auxState) {
-		case None:
-			switch(gameState) {
-			case Main:				
-				if(wndMain.getMember("new game").isClicked(e.getX(),e.getY()))
-					gameState = GameState.CreateAccount;
-				else if(wndMain.getMember("load game").isClicked(e.getX(),e.getY()))
-					gameState = GameState.LoadGame;
-				else if(wndMain.getMember("game info").isClicked(e.getX(),e.getY()))
-					gameState = GameState.Info;
-				else if(wndMain.getMember("credits").isClicked(e.getX(),e.getY()))
-					gameState = GameState.Credits;
-				else if(wndMain.getMember("quit").isClicked(e.getX(),e.getY()))
-					done = true;
-				break;
-			case CreateAccount:
-				if(wndCreateAccount.getMember("user").isClicked(e.getX(),e.getY()))
-					selectText((Textbox)wndCreateAccount.getMember("user"));
-				else if(wndCreateAccount.getMember("choose class").isClicked(e.getX(),e.getY())) {
-					selectText(null);
-					gameState = GameState.CreateClass;
-				}else if(wndCreateAccount.getMember("create").isClicked(e.getX(),e.getY())) {
-					String user = ((Textbox)wndCreateAccount.getMember("user")).getText();
-					Gender gender = Gender.valueOf(rdgGenderSelection.getButton(rdgGenderSelection.getSelected()).getLabel());
-					
-					if(user.equals("")) {
-						showMessage("The username is empty");
-					}else if(gender == Gender.None) {
-						showMessage("No gender has been selected");
-					}else{
-						player = new Player(user, gender);
-						player.setSpeed(200);
-						player.setLoc(new Point(750, 860));
-						player.setTarget(player.getLoc());
-						gameState = GameState.Game;
-					}
-				}else if(wndCreateAccount.getMember("cancel").isClicked(e.getX(),e.getY())) {
-					selectText(null);
-					wndCreateAccount.clear();
-					wndChooseClass.clear();
-					gameState = GameState.Main;
-				}else if(wndCreateAccount.handleEvent(e)) {
-				}
-				break;
-			case CreateClass:
-				if(wndChooseClass.getMember("select").isClicked(e.getX(),e.getY())) {
-					gameState = GameState.CreateAccount;
-				}
-				else if(wndChooseClass.getMember("cancel").isClicked(e.getX(),e.getY())) {
-					gameState = GameState.CreateAccount;
-				}else if(wndChooseClass.handleEvent(e)) {
-				}
-				break;
-			case LoadGame:
-				gameState = GameState.Main;
-				break;
-			case Info:
-				gameState = GameState.Main;
-				break;
-			case Credits:
-				gameState = GameState.Main;
-				break;
-			case Game:
-				int newX = player.getLoc().getX()+e.getX()-400;
-				int newY = player.getLoc().getY()+e.getY()-300;
-				if(map.getLoc((int)(Math.floor(newX/100)), (int)(Math.floor(newY/100))).isPassable()) {
-					player.setTarget(new Point(newX, newY));
-					player.setLastMoved(System.currentTimeMillis());
-				}
-				break;
-			case GameMenu:
-				break;
-			case GameInventory:
-				break;
-			case GameStats:
-				break;
-			}
-			break;
-		case MsgBox:
-			if(wndMessage.getMember("button").isClicked(e.getX(), e.getY())) {
-				auxState = AuxState.None;
-			}
-			break;
-		}
-	}
-	
-	public void mouseReleased(MouseEvent e) {
-		
-	}
-	
-	public void mouseEntered(MouseEvent e) {
-
-	}
-	
-	public void mouseExited(MouseEvent e) {
-	    
-	}
-	
-	public void mouseClicked(MouseEvent e) {
-		
-	}
- 
-	public void keyTyped(KeyEvent e) {
-		
-	}
-	
-	public void keyPressed(KeyEvent e) {
-		if(selectedText != null)
-			selectedText.handleEvent(e);
-		
-		if(e.getKeyCode() == KeyEvent.VK_ESCAPE) 
-			if(gameState == GameState.Game)
-				gameState = GameState.Main;
-			else
-				done = true;
-	}
-	
-	public void keyReleased(KeyEvent e) {
-	
-	}
-    
-	public static void main(String[] args) {
-		try {
-			PrintStream st = new PrintStream(new FileOutputStream("err.txt", true));
-			System.setErr(st);
-			System.setOut(st);
-			System.out.println("-----[ Session started on " + dateString() + " ]-----");
-			
-			GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
-			GraphicsDevice device = env.getDefaultScreenDevice();
-			new LostHavenRPG(device);
-        }
-		catch (Exception e) {
-			e.printStackTrace();
-        }
-		System.exit(0);
-	}
+    System.exit(0);
+  }
 }
Index: main/ManaDrain.java
===================================================================
--- main/ManaDrain.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/ManaDrain.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,34 @@
+package main;
+
+public class ManaDrain extends Effect {
+
+  private int mana;
+
+  public ManaDrain(int mana, TargetType target) {
+    super(EffectType.ManaDrain, target);
+    this.mana = mana;
+  }
+
+  public ManaDrain(ManaDrain e) {
+    super(e);
+    this.mana = e.mana;
+  }
+
+  public ManaDrain copy() {
+    return new ManaDrain(this);
+  }
+
+  public int getMana() {
+    return this.mana;
+  }
+
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    cr.setManapoints(cr.getManapoints() - this.mana);
+    if (cr.getManapoints() < 0) {
+      cr.setManapoints(0);
+    } else if (cr.getManapoints() > cr.getMaxManapoints()) {
+      cr.setManapoints(cr.getMaxManapoints());
+    }
+  }
+}
Index: main/Map.java
===================================================================
--- main/Map.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Map.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,78 +1,168 @@
 package main;
 
+import java.awt.Color;
+import java.awt.image.BufferedImage;
 import java.io.*;
-import java.util.*;
+import java.util.HashMap;
+
+import javax.imageio.ImageIO;
 
 public class Map {
-	private Location[][] grid;
-	
-	public Map(int x, int y) {
-		grid = new Location[x][y];
-	}
-	
-	public Map(String landFile, String structFile, HashMap<LandType, Land> landMap, HashMap<StructureType, Structure> structMap) {
-		try {
-			int length, height, x, y;
-			String str, loc;
-			BufferedReader in = new BufferedReader(new FileReader(landFile));
-			
-			str = in.readLine();
-			length = Integer.parseInt(str.substring(0, str.indexOf("x")));
-			height = Integer.parseInt(str.substring(str.indexOf("x")+1));
-			grid = new Location[length][height];
-			
-			for(x=0; x<height; x++) {
-				str = in.readLine();
-				for(y=0; y<length; y++) {
-					if(str.indexOf(",") == -1)
-						loc = str;
-					else {
-						loc = str.substring(0, str.indexOf(","));
-						str = str.substring(str.indexOf(",")+1);
-					}
-					
-					if(loc.equals("o")) {
-						loc = "Ocean";
-					}else if(loc.equals("1")) {
-						loc = "Grass";
-					}
-					
-					grid[y][x] = new Location(landMap.get(LandType.valueOf(loc)), structMap.get(StructureType.None));
-				}
-			}
-			
-			in.close();
-			
-			in = new BufferedReader(new FileReader(structFile));
-			
-			while((loc = in.readLine()) != null) {
-				str = in.readLine();
-				x = Integer.valueOf(str.substring(0, str.indexOf(",")));
-				y = Integer.valueOf(str.substring(str.indexOf(",")+1));
-				
-				grid[x-1][y-1].setStruct(structMap.get(StructureType.valueOf(loc)));
-			}
-		} catch(IOException ioe) {
-			ioe.printStackTrace();
-		}
-	}
-	
-	public Location getLoc(int x, int y) {
-		return grid[x][y];
-	}
-	
-	public void setLoc(Location loc, int x, int y) {
-		grid[x][y] = loc;
-	}
-	
-	public int getLength() {
-		return grid.length;
-	}
-	
-	public int getHeight() {
-		if(grid.length>0)
-			return grid[0].length;
-		else
-			return 0;
-	}
+
+  private Location[][] grid;
+
+  public Map(int x, int y) {
+    this.grid = new Location[x][y];
+  }
+
+  public Map(String mapFile, String structFile, HashMap<LandType, Land> landMap, HashMap<StructureType, Structure> structMap, boolean readMapFromImage) {
+    if (readMapFromImage) {
+      try {
+        BufferedImage img = ImageIO.read(getClass().getResource("../images/" + mapFile));
+        int length = img.getHeight();
+        int height = img.getWidth();
+        this.grid = new Location[height][length];
+        int x;
+        for (x = 0; x < height; x++) {
+          for (int y = 0; y < length; y++) {
+            String loc;
+            Color clr = new Color(img.getRGB(x, y));
+            if (clr.getRed() == 243 && clr.getGreen() == 119 && clr.getBlue() == 0) {
+              loc = "Lava";
+            } else if (clr.getRed() == 128 && clr.getGreen() == 0 && clr.getBlue() == 0) {
+              loc = "Metal";
+            } else if (clr.getRed() == 255 && clr.getGreen() == 0 && clr.getBlue() == 0) {
+              loc = "Charred";
+            } else if (clr.getRed() == 95 && clr.getGreen() == 155 && clr.getBlue() == 0) {
+              loc = "Swamp";
+            } else if (clr.getRed() == 0 && clr.getGreen() == 67 && clr.getBlue() == 0) {
+              loc = "Vines";
+            } else if (clr.getRed() == 255 && clr.getGreen() == 0 && clr.getBlue() == 255) {
+              loc = "Crystal";
+            } else if (clr.getRed() == 128 && clr.getGreen() == 0 && clr.getBlue() == 128) {
+              loc = "CrystalFormation";
+            } else if (clr.getRed() == 0 && clr.getGreen() == 0 && clr.getBlue() == 255) {
+              loc = "Water";
+            } else if (clr.getRed() == 0 && clr.getGreen() == 128 && clr.getBlue() == 0) {
+              loc = "Forest";
+            } else if (clr.getRed() == 139 && clr.getGreen() == 63 && clr.getBlue() == 43) {
+              loc = "Tree";
+            } else if (clr.getRed() == 179 && clr.getGreen() == 247 && clr.getBlue() == 207) {
+              loc = "Plains";
+            } else if (clr.getRed() == 255 && clr.getGreen() == 255 && clr.getBlue() == 0) {
+              loc = "Desert";
+            } else if (clr.getRed() == 83 && clr.getGreen() == 83 && clr.getBlue() == 83) {
+              loc = "Mountains";
+            } else if (clr.getRed() == 8 && clr.getGreen() == 0 && clr.getBlue() == 0) {
+              loc = "Cave";
+            } else if (clr.getRed() == 0 && clr.getGreen() == 0 && clr.getBlue() == 128) {
+              loc = "Ocean";
+            } else if (clr.getRed() == 0 && clr.getGreen() == 255 && clr.getBlue() == 255) {
+              loc = "Snow";
+            } else if (clr.getRed() == 160 && clr.getGreen() == 160 && clr.getBlue() == 164) {
+              loc = "Steam";
+            } else {
+              loc = "Mountains";
+            }
+            this.grid[x][y] = new Location(landMap.get(LandType.valueOf(loc)), structMap.get(StructureType.None));
+          }
+        }
+        BufferedReader in = new BufferedReader(new FileReader(structFile));
+        String str;
+        while ((str = in.readLine()) != null) {
+          String loc = in.readLine();
+          x = Integer.valueOf(loc.substring(0, loc.indexOf(","))).intValue();
+          int y = Integer.valueOf(loc.substring(loc.indexOf(",") + 1)).intValue();
+          Point loc1 = new Point((x - 1) * 100 + 50, (y - 1) * 100 + 50);
+          if (str.equals("ArtifactPoint") || str.equals("BossPoint")) {
+            String strTarg = in.readLine();
+            int x2 = Integer.valueOf(strTarg.substring(0, strTarg.indexOf(","))).intValue();
+            int y2 = Integer.valueOf(strTarg.substring(strTarg.indexOf(",") + 1)).intValue();
+            Point loc2 = new Point((x2 - 1) * 100 + 50, (y2 - 1) * 100 + 50);
+            ArtifactPoint struct = new ArtifactPoint((ArtifactPoint)structMap.get(StructureType.valueOf(str)), loc1);
+            struct.setTarget(loc2);
+            this.grid[x - 1][y - 1].setStruct(struct);
+            ArtifactPoint struct2 = new ArtifactPoint((ArtifactPoint)structMap.get(StructureType.valueOf(str)), loc2);
+            struct2.setTarget(loc1);
+            this.grid[x2 - 1][y2 - 1].setStruct(struct2);
+            continue;
+          }
+          if (str.equals("RespawnPoint")) {
+            this.grid[x - 1][y - 1].setStruct(new RespawnPoint((RespawnPoint)structMap.get(StructureType.valueOf(str)), loc1));
+            LostHavenRPG.respawnPoints.add((RespawnPoint)this.grid[x - 1][y - 1].getStruct());
+            continue;
+          }
+          this.grid[x - 1][y - 1].setStruct(new Structure(structMap.get(StructureType.valueOf(str)), loc1));
+        }
+        in.close();
+      } catch (IOException ioe) {
+        ioe.printStackTrace();
+      }
+    } else {
+      try {
+        BufferedReader in = new BufferedReader(new FileReader("../" + mapFile));
+        String str = in.readLine();
+        int length = Integer.parseInt(str.substring(0, str.indexOf("x")));
+        int height = Integer.parseInt(str.substring(str.indexOf("x") + 1));
+        this.grid = new Location[length][height];
+        int x;
+        for (x = 0; x < height; x++) {
+          str = in.readLine();
+          for (int y = 0; y < length; y++) {
+            String loc;
+            if (str.indexOf(",") == -1) {
+              loc = str;
+            } else {
+              loc = str.substring(0, str.indexOf(","));
+              str = str.substring(str.indexOf(",") + 1);
+            }
+            if (loc.equals("o")) {
+              loc = "OceanOld";
+            } else if (loc.equals("1")) {
+              loc = "GrassOld";
+            }
+            this.grid[y][x] = new Location(landMap.get(LandType.valueOf(loc)), structMap.get(StructureType.None));
+          }
+        }
+        in.close();
+        in = new BufferedReader(new FileReader(structFile));
+        while ((str = in.readLine()) != null) {
+          String loc = in.readLine();
+          x = Integer.valueOf(loc.substring(0, loc.indexOf(","))).intValue();
+          int y = Integer.valueOf(loc.substring(loc.indexOf(",") + 1)).intValue();
+          this.grid[x - 1][y - 1].setStruct(structMap.get(StructureType.valueOf(str)));
+        }
+        in.close();
+      } catch (IOException ioe) {
+        ioe.printStackTrace();
+      }
+    }
+  }
+
+  public void clearCreatures() {
+    for (int x = 0; x < getLength(); x++) {
+      for (int y = 0; y < getHeight(); y++) {
+        getLoc(x, y).getCreatures().clear();
+      }
+    }
+  }
+
+  public Location getLoc(int x, int y) {
+    return this.grid[x][y];
+  }
+
+  public void setLoc(Location loc, int x, int y) {
+    this.grid[x][y] = loc;
+  }
+
+  public int getLength() {
+    return this.grid.length;
+  }
+
+  public int getHeight() {
+    if (this.grid.length > 0) {
+      return (this.grid[0]).length;
+    }
+    return 0;
+  }
 }
Index: main/MapElement.java
===================================================================
--- main/MapElement.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/MapElement.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,5 +1,5 @@
 package main;
 
-import java.awt.image.*;
+import java.awt.image.BufferedImage;
 import java.io.IOException;
 
@@ -7,35 +7,41 @@
 
 public class MapElement {
-	private BufferedImage img;
-	private boolean passable;
-	
-	public MapElement(BufferedImage img, boolean passable) {
-		this.img = img;
-		this.passable = passable;
-	}
-	
-	public MapElement(String imgFile, boolean passable) {
-		try {
-			img = ImageIO.read(getClass().getResource("images/"+imgFile));
-			this.passable = passable;
-		}catch(IOException ioe) {
-    		ioe.printStackTrace();
-    	}
-	}
-	
-	public BufferedImage getImg() {
-		return img;
-	}
-	
-	public boolean isPassable() {
-		return passable;
-	}
-	
-	public void setImg(BufferedImage img) {
-		this.img = img;
-	}
-	
-	public void setPassable(boolean passable) {
-		this.passable = passable;
-	}
+
+  private BufferedImage img;
+  private boolean passable;
+
+  public MapElement(BufferedImage img, boolean passable) {
+    this.img = img;
+    this.passable = passable;
+  }
+
+  public MapElement(String imgFile, boolean passable) {
+    try {
+      this.img = ImageIO.read(getClass().getResource("../images/" + imgFile));
+      this.passable = passable;
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+  public MapElement(MapElement copy) {
+    this.img = copy.img;
+    this.passable = copy.passable;
+  }
+
+  public BufferedImage getImg() {
+    return this.img;
+  }
+
+  public boolean isPassable() {
+    return this.passable;
+  }
+
+  public void setImg(BufferedImage img) {
+    this.img = img;
+  }
+
+  public void setPassable(boolean passable) {
+    this.passable = passable;
+  }
 }
Index: main/Model.java
===================================================================
--- main/Model.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Model.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,110 @@
+package main;
+
+import gamegui.Animation;
+import java.awt.Graphics;
+
+public class Model {
+
+  private Direction dir;
+  private Action action;
+  private Animation[] anims;
+  private boolean death;
+
+  public Model(boolean death) {
+    if (death) {
+      this.anims = new Animation[13];
+    } else {
+      this.anims = new Animation[12];
+    }
+    this.dir = Direction.North;
+    this.action = Action.Walking;
+    this.death = death;
+  }
+
+  public Model(Model copy) {
+    this.dir = copy.dir;
+    this.action = copy.action;
+    if (copy.death) {
+      this.anims = new Animation[13];
+    } else {
+      this.anims = new Animation[12];
+    }
+    for (int x = 0; x < this.anims.length; x++) {
+      this.anims[x] = new Animation(copy.anims[x]);
+    }
+    this.death = copy.death;
+  }
+
+  public boolean hasDeath() {
+    return this.death;
+  }
+
+  public void addAnimation(Direction dir, Action action, Animation anim) {
+    this.anims[getIndex(dir, action)] = anim;
+  }
+
+  public void draw(Graphics g, int x, int y) {
+    if (this.anims[getIndex(this.dir, this.action)] != null) {
+      if (this.action == Action.Attacking && this.anims[getIndex(this.dir, this.action)].reachedEnd()) {
+        this.anims[getIndex(this.dir, this.action)].reset();
+        this.action = Action.Standing;
+      }
+      this.anims[getIndex(this.dir, this.action)].draw(g, x, y);
+    }
+  }
+
+  public int getHeight() {
+    return getAnimation(this.dir, this.action).getHeight();
+  }
+
+  public int getWidth() {
+    return getAnimation(this.dir, this.action).getWidth();
+  }
+
+  private int getIndex(Direction dir, Action action) {
+    int pos = 0;
+    switch (action) {
+      case Walking:
+        pos = 4;
+        break;
+      case Standing:
+        pos = 8;
+        break;
+    }
+    switch (dir) {
+      case South:
+        pos++;
+        break;
+      case East:
+        pos += 2;
+        break;
+      case West:
+        pos += 3;
+        break;
+    }
+    if (action == Action.Dying) {
+      pos = 12;
+    }
+    return pos;
+  }
+
+  public Animation getAnimation(Direction dir, Action action) {
+    return this.anims[getIndex(dir, action)];
+  }
+
+  public Direction getDirection() {
+    return this.dir;
+  }
+
+  public Action getAction() {
+    return this.action;
+  }
+
+  public void setDirection(Direction dir) {
+    this.dir = dir;
+  }
+
+  public void setAction(Action action) {
+    this.action = action;
+  }
+}
Index: main/MoveSpeed.java
===================================================================
--- main/MoveSpeed.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/MoveSpeed.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,51 @@
+package main;
+
+public class MoveSpeed extends TimedEffect {
+
+  private double speedChange;
+  private int oldSpeed;
+  private boolean frozen;
+  
+  public MoveSpeed(double speedChange, TargetType target, long duration) {
+    super(EffectType.MoveSpeed, target, duration);
+    this.speedChange = speedChange;
+    this.frozen = (speedChange == 0.0D);
+  }
+
+  public MoveSpeed(MoveSpeed e) {
+    super(e);
+    this.speedChange = e.speedChange;
+  }
+
+  public MoveSpeed copy() {
+    return new MoveSpeed(this);
+  }
+
+  public double getSpeedChange() {
+    return this.speedChange;
+  }
+
+  public void applyEffect(Object o) {
+    Creature cr = (Creature)o;
+    if (this.frozen) {
+      this.oldSpeed = cr.getSpeed();
+    }
+    cr.setSpeed((int)(cr.getSpeed() * this.speedChange));
+    cr.addEffect(this);
+    cr.thorns++;
+  }
+
+  public void cancelEffect(Object o) {
+    Creature cr = (Creature)o;
+    if (this.frozen) {
+      cr.setSpeed(this.oldSpeed);
+    } else {
+      cr.setSpeed((int)(cr.getSpeed() / this.speedChange));
+    }
+    cr.thorns--;
+  }
+
+  public String toString() {
+    return Double.toString(this.speedChange);
+  }
+}
Index: main/Penetrate.java
===================================================================
--- main/Penetrate.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Penetrate.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,21 @@
+package main;
+
+public class Penetrate extends Effect {
+
+  public Penetrate(TargetType target) {
+    super(EffectType.Penetrate, target);
+  }
+
+  public Penetrate(Penetrate e) {
+    super(e);
+  }
+
+  public Penetrate copy() {
+    return new Penetrate(this);
+  }
+
+  public void applyEffect(Object o) {
+    Projectile proj = (Projectile)o;
+    proj.setPenetrate(true);
+  }
+}
Index: main/Player.java
===================================================================
--- main/Player.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Player.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,46 +1,453 @@
 package main;
 
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.MouseInfo;
+import java.io.*;
+import java.util.*;
+
+import gamegui.*;
+
 public class Player extends Creature {
-	private Point target;
-	private int experience;
-	private int gold;
-	
-	public Player() {
-		super();
-		target = new Point(0, 0);
-	}
-
-	public Player(String name) {
-		super(name);
-		target = new Point(0, 0);
-	}
-	
-	public Player(String name, Gender gender) {
-		super(name, gender);
-		target = new Point(0, 0);
-	}
-	
-	public int getExperience() {
-		return experience;
-	}
-	
-	public int getGold() {
-		return gold;
-	}
-	
-	public Point getTarget() {
-		return target;
-	}
-	
-	public void setExperience(int experience) {
-		this.experience = experience;
-	}
-	
-	public void setGold(int gold) {
-		this.gold = gold;
-	}
-	
-	public void setTarget(Point target) {
-		this.target = target;
-	}
+
+  private int gold;
+  private int attributePoints;
+  private int[] skills;
+  private int skillPoints;
+  private LinkedList<Item> inventory;
+  private LinkedList<Item> gemsUsed;
+  private HashMap<Gem, Integer> gems;
+  private int relicCount;
+  private int gemValue;
+  private long timePlayed;
+  private long timeLoggedOn;
+
+  public Player() {
+    this.name = "Player";
+    this.type = CreatureType.Player;
+    this.skills = new int[9];
+    this.inventory = new LinkedList<Item>();
+    this.gemsUsed = new LinkedList<Item>();
+    this.gems = new HashMap<Gem, Integer>();
+    this.relicCount = 0;
+    this.gemValue = 0;
+    this.timePlayed = 0L;
+    this.timeLoggedOn = 0L;
+  }
+
+  public Player(Player p) {
+    super(p);
+    this.skills = (int[])p.skills.clone();
+    this.attributePoints = p.attributePoints;
+    this.skillPoints = p.skillPoints;
+    this.inventory = new LinkedList<Item>(p.inventory);
+    this.gemsUsed = new LinkedList<Item>(p.gemsUsed);
+    this.gems = new HashMap<Gem, Integer>(p.gems);
+    this.relicCount = p.relicCount;
+    this.gemValue = p.gemValue;
+    this.timePlayed = p.timePlayed;
+    this.timeLoggedOn = p.timeLoggedOn;
+  }
+
+  public Player copy() {
+    return new Player(this);
+  }
+
+  public boolean readyForBoss() {
+    return (this.relicCount >= 4);
+  }
+
+  public void setTimeLoggedOn(long time) {
+    this.timeLoggedOn = time;
+  }
+
+  public long getTimePlayed() {
+    return this.timePlayed;
+  }
+
+  public void updateTimePlayed() {
+    this.timePlayed += System.currentTimeMillis() - this.timeLoggedOn;
+  }
+
+  public void drawInventory(Graphics g, ScrollList inv, int x, int y) {
+    int mouseX = (MouseInfo.getPointerInfo().getLocation()).x;
+    int mouseY = (MouseInfo.getPointerInfo().getLocation()).y;
+    int locX = (mouseX - x) / 50;
+    int locY = (mouseY - y + inv.getTextStart()) / 50;
+    if (locX + 4 * locY >= 0 && locX + 4 * locY < inv.getList().size()) {
+      Window popup;
+      String itemType;
+      MultiTextbox desc;
+      Item i = ((ItemImg)inv.getList().get(locX + locY * 4)).getItem();
+      switch (i.getType()) {
+        case Relic:
+          popup = new Window("popup", mouseX - 100, mouseY - 150, 100, 165, false);
+          break;
+        default:
+          popup = new Window("popup", mouseX - 100, mouseY - 100, 100, 100, false);
+          break;
+      }
+      Font font12 = new Font("Arial", 0, 12);
+      switch (i.getType()) {
+        case LightWeapon:
+          itemType = "Light Weapon";
+          break;
+        case HeavyWeapon:
+          itemType = "Heavy Weapon";
+          break;
+        case RangedWeapon:
+          itemType = "Ranged Weapon";
+          break;
+        default:
+          itemType = i.getType().toString();
+          break;
+      }
+      popup.add(new Label("name", 5, 5, 90, 15, i.getName(), font12));
+      popup.add(new Label("type", 5, 35, 90, 15, itemType, font12));
+      switch (i.getType()) {
+        case LightWeapon:
+        case HeavyWeapon:
+        case RangedWeapon:
+          popup.add(new Label("damage", 5, 50, 90, 15, "Damage: " + ((Weapon)i).getDamage(), font12));
+          popup.add(new Label("damage", 5, 65, 90, 15, "Cooldown: " + (((Weapon)i).getAttackSpeed() / 10.0D) + " s", font12));
+          break;
+        case Spell:
+          popup.add(new Label("energy", 5, 50, 90, 15, "Energy: " + ((Gem)i).getValue(), font12));
+          desc = new MultiTextbox("description", 1, 80, 99, 84, "", false, font12, g.getFontMetrics(font12));
+          desc.setText(i.getDescription());
+          desc.setBorder(false);
+          popup.add(desc);
+          break;
+      }
+      popup.draw(g);
+    }
+  }
+
+  public void save(PrintWriter out) {
+    super.save(out);
+    out.println(this.name);
+    out.println(this.level);
+    out.println(this.timePlayed);
+    out.println(this.experience);
+    out.println(String.valueOf(getTarget().getX()) + "," + getTarget().getY());
+    out.println(getAttribute(Attribute.Strength));
+    out.println(getAttribute(Attribute.Dexterity));
+    out.println(getAttribute(Attribute.Constitution));
+    out.println(getAttribute(Attribute.Wisdom));
+    out.println(getSkill(Skill.LightWeapons));
+    out.println(getSkill(Skill.HeavyWeapons));
+    out.println(getSkill(Skill.RangedWeapons));
+    out.println(getSkill(Skill.Evocation));
+    out.println(getSkill(Skill.Enchantment));
+    out.println(this.attributePoints);
+    out.println(this.skillPoints);
+    out.println(getMaxHitpoints());
+    out.println(getMaxManapoints());
+    Object[] arr = LostHavenRPG.respawnPoints.toArray();
+    for (int x = 0; x < arr.length; x++) {
+      if (((RespawnPoint)arr[x]).isMarked())
+        out.println(x); 
+    }
+    out.println("---");
+    Iterator<Item> iter = this.inventory.iterator();
+    while (iter.hasNext())
+      out.println(((Item)iter.next()).getName()); 
+    out.println("---");
+    iter = this.gemsUsed.iterator();
+    while (iter.hasNext())
+      out.println(((Item)iter.next()).getName()); 
+    out.println("---");
+  }
+
+  public static Player loadTemplate(BufferedReader in) {
+    Player p = new Player();
+    try {
+      p.level = Integer.parseInt(in.readLine());
+      p.setSkill(Skill.LightWeapons, Integer.parseInt(in.readLine()));
+      p.setSkill(Skill.HeavyWeapons, Integer.parseInt(in.readLine()));
+      p.setSkill(Skill.RangedWeapons, Integer.parseInt(in.readLine()));
+      p.setSkill(Skill.Evocation, Integer.parseInt(in.readLine()));
+      p.setSkill(Skill.Enchantment, Integer.parseInt(in.readLine()));
+      p.attributePoints = Integer.parseInt(in.readLine());
+      p.skillPoints = Integer.parseInt(in.readLine());
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+    return p;
+  }
+
+  public void load(BufferedReader in) {
+    try {
+      super.load(in);
+      int hp = getHitpoints();
+      int mp = getManapoints();
+      setName(in.readLine());
+      this.level = Integer.valueOf(in.readLine()).intValue();
+      this.timePlayed = Long.valueOf(in.readLine()).longValue();
+      setExperience(Integer.valueOf(in.readLine()).intValue());
+      String strTarget = in.readLine();
+      getTarget().setX(Integer.valueOf(strTarget.substring(0, strTarget.indexOf(","))).intValue());
+      getTarget().setY(Integer.valueOf(strTarget.substring(strTarget.indexOf(",") + 1)).intValue());
+      setAttribute(Attribute.Strength, Integer.parseInt(in.readLine()));
+      setAttribute(Attribute.Dexterity, Integer.parseInt(in.readLine()));
+      setAttribute(Attribute.Constitution, Integer.parseInt(in.readLine()));
+      setAttribute(Attribute.Wisdom, Integer.parseInt(in.readLine()));
+      setSkill(Skill.LightWeapons, Integer.parseInt(in.readLine()));
+      setSkill(Skill.HeavyWeapons, Integer.parseInt(in.readLine()));
+      setSkill(Skill.RangedWeapons, Integer.parseInt(in.readLine()));
+      setSkill(Skill.Evocation, Integer.parseInt(in.readLine()));
+      setSkill(Skill.Enchantment, Integer.parseInt(in.readLine()));
+      this.attributePoints = Integer.parseInt(in.readLine());
+      this.skillPoints = Integer.parseInt(in.readLine());
+      setMaxHitpoints(Integer.parseInt(in.readLine()));
+      setMaxManapoints(Integer.parseInt(in.readLine()));
+      setHitpoints(hp);
+      setManapoints(mp);
+      setWeapon(this.weapon.baseWeapon);
+      String strItem;
+      while (!(strItem = in.readLine()).equals("---"))
+        ((RespawnPoint)LostHavenRPG.respawnPoints.get(Integer.parseInt(strItem))).mark(); 
+      this.weapon.update();
+      while (!(strItem = in.readLine()).equals("---"))
+        pickUp(LostHavenRPG.items.get(strItem)); 
+      while (!(strItem = in.readLine()).equals("---")) {
+        pickUp(LostHavenRPG.items.get(strItem));
+        activateGem((Gem)LostHavenRPG.items.get(strItem));
+      }
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+  public void pickUp(Item item) {
+    this.inventory.add(item);
+    LostHavenRPG.lstInventory.getList().add(new ItemImg(item));
+    if (item.isRelic()) {
+      this.relicCount++;
+    }
+  }
+
+  public boolean activateGem(Gem item) {
+    if (this.gemValue + item.getValue() > getSkill(Skill.Evocation))
+      return false; 
+    this.gemsUsed.add(item);
+    LostHavenRPG.lstGems.getList().add(new ItemImg(item));
+    this.inventory.remove(item);
+    LostHavenRPG.lstInventory.getList().remove(new ItemImg(item));
+    this.gemValue += item.getValue();
+    addGem((Gem)LostHavenRPG.items.get(item.getName()));
+    if (getSpell() == null) {
+      setSpell(new Weapon("spell", ItemType.Spell, "Dagger.png", new java.awt.image.BufferedImage[8], 10, 250, 0)); 
+    }
+    getSpell().update();
+    return true;
+  }
+
+  public void deactivateGem(Gem item) {
+    this.inventory.add(item);
+    LostHavenRPG.lstInventory.getList().add(new ItemImg(item));
+    this.gemsUsed.remove(item);
+    LostHavenRPG.lstGems.getList().remove(new ItemImg(item));
+    this.gemValue -= item.getValue();
+    removeGem((Gem)LostHavenRPG.items.get(item.getName()));
+    if (this.gemsUsed.size() == 0) {
+      this.spell = null;
+    } else {
+      getSpell().update();
+    }
+  }
+
+  public LinkedList<Item> getInventory() {
+    return this.inventory;
+  }
+
+  public LinkedList<Item> getGems() {
+    return this.gemsUsed;
+  }
+
+  public void initGems(LinkedList<Gem> lstGems) {
+    Iterator<Gem> iter = lstGems.iterator();
+    while (iter.hasNext())
+      this.gems.put(iter.next(), Integer.valueOf(0)); 
+  }
+
+  public void addGem(Gem gem) {
+    this.gems.put(gem, Integer.valueOf(((Integer)this.gems.get(gem)).intValue() + 1));
+  }
+
+  public void removeGem(Gem gem) {
+    this.gems.put(gem, Integer.valueOf(((Integer)this.gems.get(gem)).intValue() - 1));
+  }
+
+  public int getGemNum(String name) {
+    Iterator<Gem> iter = this.gems.keySet().iterator();
+    while (iter.hasNext()) {
+      Gem cur = iter.next();
+      if (cur.getName().equals(name)) {
+        return ((Integer)this.gems.get(cur)).intValue();
+      }
+    }
+    return 0;
+  }
+
+  public void increaseLevel() {
+    this.skillPoints += 2;
+    setHitpoints(getHitpoints() + getAttribute(Attribute.Constitution));
+    setMaxHitpoints(getMaxHitpoints() + getAttribute(Attribute.Constitution));
+    setManapoints(getManapoints() + getAttribute(Attribute.Wisdom));
+    setMaxManapoints(getMaxManapoints() + getAttribute(Attribute.Wisdom));
+    setLevel(getLevel() + 1);
+  }
+
+  public void allocateAttribute(Attribute point) {
+    if (this.attributePoints > 0) {
+      setAttribute(point, getAttribute(point) + 1);
+      this.attributePoints--;
+    }
+  }
+
+  public void repickAttribute(Attribute point) {
+    if (getAttribute(point) > 6) {
+      setAttribute(point, getAttribute(point) - 1);
+      this.attributePoints++;
+    }
+  }
+
+  public void allocateSkill(Skill point) {
+    if (this.skillPoints > 0) {
+      setSkill(point, getSkill(point) + 1);
+      this.skillPoints--;
+      this.weapon.update();
+    }
+  }
+
+  public void repickSkill(Skill point) {
+    if (getSkill(point) > 0) {
+      setSkill(point, getSkill(point) - 1);
+      this.skillPoints++;
+      this.weapon.update();
+    }
+  }
+
+  private int skillIndex(Skill skill) {
+    switch (skill) {
+      case LightWeapons:
+        return 0;
+      case HeavyWeapons:
+        return 1;
+      case RangedWeapons:
+        return 2;
+      case Evocation:
+        return 3;
+      case Enchantment:
+        return 4;
+    }
+    return -1;
+  }
+
+  public int getSkill(Skill skill) {
+    return this.skills[skillIndex(skill)];
+  }
+
+  public void setSkill(Skill skill, int num) {
+    this.skills[skillIndex(skill)] = num;
+  }
+
+  public int getGold() {
+    return this.gold;
+  }
+
+  public int getAttributePoints() {
+    return this.attributePoints;
+  }
+
+  public int getSkillPoints() {
+    return this.skillPoints;
+  }
+
+  public int getGemValue() {
+    return this.gemValue;
+  }
+
+  public int getRelicCount() {
+    return this.relicCount;
+  }
+
+  public void setWeapon(Weapon weapon) {
+    this.weapon = new EquippedWeapon(this, weapon);
+    switch (weapon.getType()) {
+      case LightWeapon:
+      case HeavyWeapon:
+        setModel(LostHavenRPG.meleeModel);
+        break;
+      case RangedWeapon:
+        setModel(LostHavenRPG.rangedModel);
+        break;
+    }
+    this.weapon.update();
+    (this.model.getAnimation(Direction.North, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.North, Action.Attacking)).frames.size());
+    (this.model.getAnimation(Direction.South, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.South, Action.Attacking)).frames.size());
+    (this.model.getAnimation(Direction.East, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.East, Action.Attacking)).frames.size());
+    (this.model.getAnimation(Direction.West, Action.Attacking)).drawInterval = (this.weapon.getAttackSpeed() * 100 / (this.model.getAnimation(Direction.West, Action.Attacking)).frames.size());
+  }
+
+  public void setGold(int gold) {
+    this.gold = gold;
+  }
+
+  public void setAttributePoints(int attributePoints) {
+    this.attributePoints = attributePoints;
+  }
+
+  public void setAttribute(Attribute attribute, int num) {
+    this.attributes[attributeIndex(attribute)] = num;
+    updateStats();
+  }
+
+  private void updateStats() {
+    this.hitpoints = 4 * this.attributes[2];
+    this.maxHitpoints = 4 * this.attributes[2];
+    this.manapoints = 2 * this.attributes[3];
+    this.maxManapoints = 2 * this.attributes[3];
+  }
+
+  public class ItemImg implements Listable {
+    public Item i;
+    public int xCoord;
+    public int yCoord;
+
+    public ItemImg(Item i) {
+      this.i = i;
+    }
+
+    public void draw(int x, int y, Graphics g) {
+      this.xCoord = x;
+      this.yCoord = y;
+      this.i.drawStatic(g, x, y);
+    }
+
+    public Item getItem() {
+      return this.i;
+    }
+
+    public int getHeight() {
+      return this.i.getImg().getHeight();
+    }
+
+    public int getWidth() {
+      return this.i.getImg().getWidth();
+    }
+
+    public int getXOffset() {
+      return 0;
+    }
+
+    public int getYOffset() {
+      return 0;
+    }
+
+    public boolean equals(Object o) {
+      return (((ItemImg)o).i == this.i);
+    }
+  }
 }
Index: main/Point.java
===================================================================
--- main/Point.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Point.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,74 +2,86 @@
 
 public class Point {
-	private int x;
-	private int xMin;
-	private int y;
-	private int yMin;
-	
-	public Point() {
-		x = 0;
-		y = 0;
-	}
 
-	public Point(int x, int y) {
-		this.x = x;
-		this.xMin = 0;
-		this.y = y;
-		this.yMin = 0;
-	}
-	
-	public Point(int x, int xMin, int y, int yMin) {
-		this.x = x;
-		this.xMin = xMin;
-		this.y = y;
-		this.yMin = yMin;
-	}
+  private int x;
+  private int xMin;
+  private int y;
+  private int yMin;
 
-	public int getX() {
-		return x;
-	}
-	
-	public int getXMin() {
-		return xMin;
-	}
+  public Point() {
+    this.x = 0;
+    this.y = 0;
+  }
 
-	public int getY() {
-		return y;
-	}
-	
-	public int getYMin() {
-		return yMin;
-	}
+  public Point(int x, int y) {
+    this.x = x;
+    this.xMin = 0;
+    this.y = y;
+    this.yMin = 0;
+  }
 
-	public void setX(int x) {
-		this.x = x;
-	}
-	
-	public void setXMin(int xMin) {
-		this.xMin = xMin;
-	}
+  public Point(int x, int xMin, int y, int yMin) {
+    this.x = x;
+    this.xMin = xMin;
+    this.y = y;
+    this.yMin = yMin;
+  }
 
-	public void setY(int y) {
-		this.y = y;
-	}
-	
-	public void setYMin(int yMin) {
-		this.yMin = yMin;
-	}
-	
-	public String toString() {
-		return x+","+y;
-	}
-	
-	public static int xDif(Point p1, Point p2) {
-		return 100*(p2.x-p1.x)+p2.xMin-p1.xMin;
-	}
-	
-	public static int yDif(Point p1, Point p2) {
-		return 100*(p2.y-p1.y)+p2.yMin-p1.yMin;
-	}
-	
-	public static double dist(Point p1, Point p2) {
-		return Math.sqrt(Math.pow(p1.x+p1.xMin/100-p2.x-p2.xMin/100, 2)+Math.pow(p1.y+p1.yMin/100-p2.y-p2.yMin/100, 2));
-	}
+  public Point(Point p) {
+    this.x = p.x;
+    this.xMin = p.xMin;
+    this.y = p.y;
+    this.yMin = p.yMin;
+  }
+
+  public int getX() {
+    return this.x;
+  }
+
+  public int getXMin() {
+    return this.xMin;
+  }
+
+  public int getY() {
+    return this.y;
+  }
+
+  public int getYMin() {
+    return this.yMin;
+  }
+
+  public void setX(int x) {
+    this.x = x;
+  }
+
+  public void setXMin(int xMin) {
+    this.xMin = xMin;
+  }
+
+  public void setY(int y) {
+    this.y = y;
+  }
+
+  public void setYMin(int yMin) {
+    this.yMin = yMin;
+  }
+
+  public boolean equals(Point p) {
+    return (this.x == p.x && this.y == p.y && this.xMin == p.xMin && this.yMin == p.yMin);
+  }
+
+  public String toString() {
+    return String.valueOf(this.x) + "," + this.y;
+  }
+
+  public static int xDif(Point p1, Point p2) {
+    return 100 * (p2.x - p1.x) + p2.xMin - p1.xMin;
+  }
+
+  public static int yDif(Point p1, Point p2) {
+    return 100 * (p2.y - p1.y) + p2.yMin - p1.yMin;
+  }
+
+  public static double dist(Point p1, Point p2) {
+    return Math.sqrt(Math.pow((p1.x + p1.xMin / 100 - p2.x - p2.xMin / 100), 2.0D) + Math.pow((p1.y + p1.yMin / 100 - p2.y - p2.yMin / 100), 2.0D));
+  }
 }
Index: main/Projectile.java
===================================================================
--- main/Projectile.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Projectile.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,184 @@
+package main;
+
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.util.*;
+
+public class Projectile {
+
+  private LinkedList<Effect> spawnEffects;
+  private LinkedList<Effect> contactEffects;
+  private BufferedImage img;
+  private int speed;
+  private Point loc;
+  private Point target;
+  private double lastMoved;
+  private boolean done;
+  private boolean penetrate;
+  private Creature creator;
+  private LinkedList<Creature> effectedCreatures;
+
+  public Projectile(BufferedImage img, Point loc) {
+    this.spawnEffects = new LinkedList<Effect>();
+    this.contactEffects = new LinkedList<Effect>();
+    this.speed = 300;
+    this.img = img;
+    this.loc = loc;
+    this.target = loc;
+    this.lastMoved = 0.0D;
+    this.done = false;
+    this.penetrate = false;
+    this.creator = null;
+    this.effectedCreatures = new LinkedList<Creature>();
+  }
+
+  public Projectile(BufferedImage img, Point loc, Creature creator) {
+    this.spawnEffects = new LinkedList<Effect>();
+    this.contactEffects = new LinkedList<Effect>();
+    this.speed = 100;
+    this.img = img;
+    this.loc = loc;
+    this.target = loc;
+    this.lastMoved = 0.0D;
+    this.done = false;
+    this.penetrate = false;
+    this.creator = creator;
+    this.effectedCreatures = new LinkedList<Creature>();
+  }
+
+  public void effectCreature(Creature c) {
+    this.effectedCreatures.add(c);
+  }
+
+  public boolean creatureIsEffected(Creature c) {
+    return this.effectedCreatures.contains(c);
+  }
+
+  public void draw(Graphics g, int playerX, int playerY) {
+    g.drawImage(this.img, 375 + this.loc.getX() - playerX, 275 + this.loc.getY() - playerY, null);
+  }
+
+  public Point move() {
+    Point newLoc;
+    double dist = this.speed * (System.currentTimeMillis() - this.lastMoved) / 1000.0D;
+    if (this.lastMoved == 0.0D) {
+      dist = 0.0D;
+    }
+    this.lastMoved = System.currentTimeMillis();
+    if (Point.dist(this.loc, this.target) <= dist) {
+      newLoc = this.target;
+    } else {
+      int xDif = (int)(Point.xDif(this.loc, this.target) * dist / Point.dist(this.loc, this.target));
+      int yDif = (int)(Point.yDif(this.loc, this.target) * dist / Point.dist(this.loc, this.target));
+      newLoc = new Point(this.loc.getX(), this.loc.getXMin() + xDif, this.loc.getY(), this.loc.getYMin() + yDif);
+      newLoc.setX(newLoc.getX() + newLoc.getXMin() / 100);
+      newLoc.setXMin(newLoc.getXMin() % 100);
+      newLoc.setY(newLoc.getY() + newLoc.getYMin() / 100);
+      newLoc.setYMin(newLoc.getYMin() % 100);
+      if (newLoc.getXMin() < 0) {
+        newLoc.setX(newLoc.getX() - 1);
+        newLoc.setXMin(newLoc.getXMin() + 100);
+      } else if (newLoc.getYMin() < 0) {
+        newLoc.setY(newLoc.getY() - 1);
+        newLoc.setYMin(newLoc.getYMin() + 100);
+      }
+    }
+    return newLoc;
+  }
+
+  public void addSpawnEffect(Effect effect) {
+    this.spawnEffects.add(effect);
+  }
+
+  public void addSpawnEffects(LinkedList<Effect> effects) {
+    this.spawnEffects.addAll(effects);
+  }
+
+  public void applySpawnEffects() {
+    Iterator<Effect> iter = this.spawnEffects.iterator();
+    while (iter.hasNext()) {
+      Effect cur = iter.next();
+      switch (cur.getTarget()) {
+        case Projectile:
+          cur.applyEffect(this);
+        case Creator:
+          cur.applyEffect(this.creator);
+      }
+    }
+  }
+
+  public void addContactEffect(Effect effect) {
+    this.contactEffects.add(effect);
+  }
+
+  public void addContactEffects(LinkedList<Effect> effects) {
+    this.contactEffects.addAll(effects);
+  }
+
+  public void applyContactEffects(Creature cr) {
+    Iterator<Effect> iter = this.contactEffects.iterator();
+    while (iter.hasNext()) {
+      Effect cur = iter.next();
+      switch (cur.getTarget()) {
+        case Projectile:
+          cur.applyEffect(this);
+        case Creator:
+          cur.applyEffect(this.creator);
+        case Enemy:
+          cur.applyEffect(cr);
+      }
+    }
+  }
+
+  public int getSpeed() {
+    return this.speed;
+  }
+
+  public Point getLoc() {
+    return this.loc;
+  }
+
+  public Point getTarget() {
+    return this.target;
+  }
+
+  public boolean isDone() {
+    return this.done;
+  }
+
+  public boolean isPlayerOwned() {
+    return (this.creator.getType() == CreatureType.Player);
+  }
+
+  public boolean penetrates() {
+    return this.penetrate;
+  }
+
+  public Point setTarget() {
+    return this.target;
+  }
+
+  public void setSpeed(int speed) {
+    this.speed = speed;
+  }
+
+  public void setLoc(Point loc) {
+    this.loc = loc;
+  }
+
+  public void setTarget(Point target) {
+    this.target = target;
+  }
+
+  public void setDone(boolean done) {
+    this.done = done;
+  }
+
+  public void setPenetrate(boolean penetrate) {
+    this.penetrate = penetrate;
+  }
+
+  public void setCreator(Creature creator) {
+    this.creator = creator;
+  }
+}
Index: main/ProjectileSpeed.java
===================================================================
--- main/ProjectileSpeed.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/ProjectileSpeed.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,33 @@
+package main;
+
+public class ProjectileSpeed extends Effect {
+
+  private int speedChange;
+  
+  public ProjectileSpeed(int speedChange, TargetType target) {
+    super(EffectType.ProjectileSpeed, target);
+    this.speedChange = speedChange;
+  }
+  
+  public ProjectileSpeed(ProjectileSpeed e) {
+    super(e);
+    this.speedChange = e.speedChange;
+  }
+  
+  public ProjectileSpeed copy() {
+    return new ProjectileSpeed(this);
+  }
+  
+  public double getSpeedChange() {
+    return this.speedChange;
+  }
+  
+  public void applyEffect(Object o) {
+    Projectile proj = (Projectile)o;
+    proj.setSpeed(proj.getSpeed() + this.speedChange);
+  }
+  
+  public String toString() {
+    return Double.toString(this.speedChange);
+  }
+}
Index: main/RespawnPoint.java
===================================================================
--- main/RespawnPoint.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/RespawnPoint.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,35 @@
+package main;
+
+import java.awt.image.BufferedImage;
+
+public class RespawnPoint extends Structure {
+
+  private boolean marked;
+
+  public RespawnPoint(StructureType type, BufferedImage img) {
+    super(type, img, true);
+    this.marked = false;
+  }
+
+  public RespawnPoint(StructureType type, String imgFile, boolean passable) {
+    super(type, imgFile, passable);
+    this.marked = false;
+  }
+
+  public RespawnPoint(RespawnPoint copy, Point loc) {
+    super(copy, loc);
+    this.marked = copy.marked;
+  }
+
+  public boolean isMarked() {
+    return this.marked;
+  }
+
+  public void mark() {
+    this.marked = true;
+  }
+
+  public void unmark() {
+    this.marked = false;
+  }
+}
Index: main/Skill.java
===================================================================
--- main/Skill.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Skill.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,9 @@
+package main;
+
+public enum Skill {
+  LightWeapons,
+  HeavyWeapons,
+  RangedWeapons,
+  Evocation,
+  Enchantment
+}
Index: main/SpawnPoint.java
===================================================================
--- main/SpawnPoint.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/SpawnPoint.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,25 +1,95 @@
 package main;
 
+import java.util.Iterator;
+import java.util.Random;
+
 public class SpawnPoint {
-	Point loc;
-	long lastSpawned;
-	long interval;
-	Creature creature;
-	
-	public SpawnPoint(Point loc, long interval, Creature creature) {
-		this.loc = loc;
-		this.interval = interval;
-		this.creature = creature;
-		lastSpawned = 0;
-	}
-	
-	//calling class handles the actual spawning of the creature if this method returns true
-	public boolean spawn() {
-		if((System.currentTimeMillis()-lastSpawned) >= interval) {
-			lastSpawned = System.currentTimeMillis();
-			return true;
-		}else {
-			return false;
-		}
-	}
+
+  static int numSpawnPoints = 0;
+
+  Point loc;
+  long lastSpawned;
+  long interval;
+  Creature creature;
+  int numSpawns;
+  int maxSpawns;
+  int idNum;
+
+  public SpawnPoint(Creature creature, Point loc, long interval, int maxSpawns) {
+    this.creature = creature;
+    this.loc = loc;
+    this.interval = interval;
+    this.maxSpawns = maxSpawns;
+    this.lastSpawned = 0L;
+    this.idNum = ++numSpawnPoints;
+  }
+
+  public Creature spawn() {
+    if (System.currentTimeMillis() - this.lastSpawned >= this.interval && this.numSpawns < this.maxSpawns) {
+      this.lastSpawned = System.currentTimeMillis();
+      Creature cr = this.creature.copy();
+      Point newLoc = null;
+      Random gen = new Random();
+      boolean goodLoc = false, occupied = false;
+      while (!goodLoc) {
+        newLoc = new Point(this.loc.getX() - 400 + gen.nextInt(800), this.loc.getY() - 400 + gen.nextInt(800));
+        if (newLoc.getX() >= 0 && newLoc.getY() >= 0 && LostHavenRPG.map.getLoc(newLoc.getX() / 100, newLoc.getY() / 100).isPassable() && Point.dist(newLoc, this.loc) <= 400.0D) {
+          occupied = false;
+          int xLow = newLoc.getX() / 100 - 2;
+          int xHigh = xLow + 5;
+          int yLow = newLoc.getY() / 100 - 2;
+          int yHigh = yLow + 5;
+          if (xLow < 0) {
+            xLow = 0;
+          }
+          if (xHigh > LostHavenRPG.map.getLength()) {
+            xHigh = LostHavenRPG.map.getLength();
+          }
+          if (yLow < 0) {
+            yLow = 0;
+          }
+          if (yHigh > LostHavenRPG.map.getHeight()) {
+            yHigh = LostHavenRPG.map.getHeight();
+          }
+          for (int x = xLow; x < xHigh; x++) {
+            for (int y = yLow; y < yHigh; y++) {
+              Iterator<Creature> iter = LostHavenRPG.map.getLoc(x, y).getCreatures().iterator();
+              while (iter.hasNext() && !occupied) {
+                Creature cur = iter.next();
+                if (Point.dist(cur.getLoc(), newLoc) < 100.0D) {
+                  occupied = true;
+                }
+              }
+            }
+          }
+          if (!occupied) {
+            goodLoc = true;
+          }
+        }
+      }
+      cr.setLoc(newLoc);
+      cr.setSpawnPoint(this);
+      this.numSpawns++;
+      return cr;
+    }
+
+    return null;
+  }
+
+  public CreatureType getType() {
+    return this.creature.getType();
+  }
+
+  public Point getLoc() {
+    return this.loc;
+  }
+
+  public void removeCreature() {
+    this.numSpawns--;
+  }
+
+  public void clear() {
+    this.numSpawns = 0;
+    this.lastSpawned = 0L;
+  }
 }
Index: main/Structure.java
===================================================================
--- main/Structure.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/Structure.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -1,23 +1,35 @@
 package main;
 
-import java.awt.image.*;
+import java.awt.image.BufferedImage;
 
 public class Structure extends MapElement {
-	private StructureType type;
-	
-	public Structure(StructureType type, BufferedImage img, boolean passable) {
-		super(img, passable);
-		
-		this.type = type;
-	}
-	
-	public Structure(StructureType type, String imgFile, boolean passable) {
-		super(imgFile, passable);
-		
-		this.type = type;
-	}
-	
-	public StructureType getType() {
-		return type;
-	}
+
+  private StructureType type;
+  private Point loc;
+
+  public Structure(StructureType type, BufferedImage img, boolean passable) {
+    super(img, passable);
+    this.type = type;
+    this.loc = null;
+  }
+
+  public Structure(StructureType type, String imgFile, boolean passable) {
+    super(imgFile, passable);
+    this.type = type;
+    this.loc = null;
+  }
+
+  public Structure(Structure copy, Point loc) {
+    super(copy);
+    this.type = copy.type;
+    this.loc = loc;
+  }
+
+  public StructureType getType() {
+    return this.type;
+  }
+
+  public Point getLoc() {
+    return this.loc;
+  }
 }
Index: main/StructureType.java
===================================================================
--- main/StructureType.java	(revision 08704682fb409c42246c2544c64a8681a3fd9734)
+++ main/StructureType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -2,22 +2,25 @@
 
 public enum StructureType {
-    None,
-    Tent,
-    LargeTent,
-    House,
-    Cave,
-    Gravestone,
-    GraveyardFence1,
-    GraveyardFence2,
-    PicketFence1,
-    PicketFence2,
-    Hut,
-    WitchHut,
-    Tree,
-    BlueOrb,
-    RedOrb,
-    LoginPedestal,
-    RejuvenationPedestal,
-    LifePedestal,
-    ManaPedestal
+  None,
+  Tent,
+  LargeTent,
+  House,
+  Cave,
+  Gravestone,
+  GraveyardFence1,
+  GraveyardFence2,
+  PicketFence1,
+  PicketFence2,
+  Hut,
+  WitchHut,
+  Tree,
+  BlueOrb,
+  RedOrb,
+  LoginPedestal,
+  RejuvenationPedestal,
+  LifePedestal,
+  ManaPedestal,
+  RespawnPoint,
+  ArtifactPoint,
+  BossPoint;
 }
Index: main/TargetType.java
===================================================================
--- main/TargetType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/TargetType.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,7 @@
+package main;
+
+public enum TargetType {
+  Creator,
+  Enemy,
+  Projectile
+}
Index: main/TimedEffect.java
===================================================================
--- main/TimedEffect.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/TimedEffect.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,24 @@
+package main;
+
+public class TimedEffect extends Effect {
+
+  private long duration;
+
+  public TimedEffect(EffectType type, TargetType target, long duration) {
+    super(type, target);
+    this.duration = duration;
+  }
+
+  public TimedEffect(TimedEffect e) {
+    super(e);
+    this.duration = e.duration;
+  }
+
+  public TimedEffect copy() {
+    return new TimedEffect(this);
+  }
+
+  public long getDuration() {
+    return this.duration;
+  }
+}
Index: main/Weapon.java
===================================================================
--- main/Weapon.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
+++ main/Weapon.java	(revision 8edd04e287f45041eeee2cb294847453e4c6aac4)
@@ -0,0 +1,179 @@
+package main;
+
+import java.awt.image.BufferedImage;
+import java.util.*;
+
+public class Weapon extends Item {
+
+  BufferedImage[] imgProj;
+
+  protected LinkedList<Effect> spawnEffects;  
+  protected LinkedList<Effect> contactEffects;
+
+  private int attackSpeed;
+  private int range;
+
+  public Weapon(String name, ItemType type, String img, BufferedImage[] imgProj, int attackSpeed, int range, int damage) {
+    super(name, type, "weapons/" + img);
+    this.imgProj = imgProj;
+    this.spawnEffects = new LinkedList<Effect>();
+    this.contactEffects = new LinkedList<Effect>();
+    this.contactEffects.add(new Damage(damage, TargetType.Enemy));
+    this.attackSpeed = attackSpeed;
+    this.range = range;
+  }
+
+  public Weapon(Weapon copy, Point loc) {
+    super(copy, loc);
+    this.imgProj = copy.imgProj;
+    this.spawnEffects = new LinkedList<Effect>();
+    Iterator<Effect> iter = copy.spawnEffects.iterator();
+    while (iter.hasNext()) {
+      this.spawnEffects.add(((Effect)iter.next()).copy());
+    }
+    this.contactEffects = new LinkedList<Effect>();
+    iter = copy.contactEffects.iterator();
+    while (iter.hasNext()) {
+      this.contactEffects.add(((Effect)iter.next()).copy());
+    }
+    this.attackSpeed = copy.attackSpeed;
+    this.range = copy.range;
+  }
+
+  public Weapon copy(Point loc) {
+    return new Weapon(this, loc);
+  }
+
+  public void addSpawnEffect(Effect effect) {
+    this.spawnEffects.add(effect);
+  }
+
+  public void applySpawnEffects(Creature cr) {
+    Iterator<Effect> iter = this.spawnEffects.iterator();
+    while (iter.hasNext()) {
+      ((Effect)iter.next()).applyEffect(cr);
+    } 
+  }
+
+  public void addContactEffect(Effect effect) {
+    this.contactEffects.add(effect);
+  }
+
+  public void applyContactEffects(Creature cr) {
+    Iterator<Effect> iter = this.contactEffects.iterator();
+    while (iter.hasNext()) {
+      ((Effect)iter.next()).applyEffect(cr);
+    }
+  }
+
+  private int calcDirection(Point loc, Point target) {
+    int dir, xDif2 = Point.xDif(loc, target);
+    int yDif2 = Point.yDif(target, loc);
+    double angle = 1.5707963267948966D;
+    if (xDif2 == 0) {
+      if (yDif2 != 0) {
+        angle *= Math.abs(yDif2) / yDif2;
+      }
+    } else {
+      angle = Math.atan(yDif2 / xDif2);
+    }
+    if (1.1780972450961724D <= angle) {
+      dir = 0;
+    } else if (0.39269908169872414D <= angle && angle < 1.1780972450961724D) {
+      dir = 1;
+    } else if (-0.39269908169872414D <= angle && angle < 0.39269908169872414D) {
+      dir = 2;
+    } else if (-1.1780972450961724D <= angle && angle < -0.39269908169872414D) {
+      dir = 3;
+    } else {
+      dir = 4;
+    }
+    if (xDif2 < 0)
+      dir = (dir + 4) % 8;
+    return dir;
+  }
+
+  public Projectile spawnProjectile(Point loc, Point target) {
+    Projectile proj = new Projectile(this.imgProj[calcDirection(loc, target)], loc);
+    proj.addSpawnEffects(this.spawnEffects);
+    proj.addContactEffects(this.contactEffects);
+    proj.setTarget(target);
+    return proj;
+  }
+
+  public int getDamage() {
+    return ((Damage)getContactEffect(EffectType.Damage)).getDamage();
+  }
+
+  public int getAttackSpeed() {
+    return this.attackSpeed;
+  }
+
+  public int getRange() {
+    return this.range;
+  }
+
+  public Effect getSpawnEffect(EffectType type) {
+    Iterator<Effect> iter = this.spawnEffects.iterator();
+    while (iter.hasNext()) {
+      Effect cur = iter.next();
+      if (cur.getType() == type) {
+        return cur;
+      }
+    }
+    return null;
+  }
+
+  public Effect getContactEffect(EffectType type) {
+    Iterator<Effect> iter = this.contactEffects.iterator();
+    while (iter.hasNext()) {
+      Effect cur = iter.next();
+      if (cur.getType() == type) {
+        return cur; 
+      }
+    }
+    return null;
+  }
+
+  public void setDamage(int damage) {
+    ((Damage)getContactEffect(EffectType.Damage)).setDamage(damage);
+  }
+
+  public void setAttackSpeed(int attackSpeed) {
+    this.attackSpeed = attackSpeed;
+  }
+
+  public void setRange(int range) {
+    this.range = range;
+  }
+
+  public void setSpawnEffect(Effect effect) {
+    Iterator<Effect> iter = this.spawnEffects.iterator();
+    Effect target = null;
+    while (iter.hasNext() && target == null) {
+      Effect cur = iter.next();
+      if (cur.getType() == effect.getType()) {
+        target = cur;
+      }
+    } 
+    if (target != null) {
+      this.spawnEffects.remove(target);
+    }
+    this.spawnEffects.add(effect);
+  }
+
+  public void setContactEffect(Effect effect) {
+    Iterator<Effect> iter = this.contactEffects.iterator();
+    Effect target = null;
+    while (iter.hasNext() && target == null) {
+      Effect cur = iter.next();
+      if (cur.getType() == effect.getType()) {
+        target = cur;
+      }
+    }
+    if (target != null) {
+      this.contactEffects.remove(target);
+    }
+    this.contactEffects.add(effect);
+  }
+}
