public final class Vec2i implements Serializable {
	private static final long serialVersionUID = 5659632794862666943L;
	
	/**
	 * Since {@link Vec2i} instances are immutable, their x and y fields may be accessed without getters.
	 */
	public final int x, y;
	
	/**
	 * Creates a new vector from an x and y component.
	 * @param x      the x-component of the vector
	 * @param y      the y-component of the vector
	 */
	public Vec2i(int x, int y) {
		this.x = x;
		this.y = y;
	}
	
	/**
	 * Factory method that creates a {@code Vec2i} by {@linkplain Math#round(float) rounding} the components of a {@link Vec2f} to the nearest integer.
	 * @param v      the Vec2f to round
	 * @return       a Vec2i created by rounding the components of {@code v}
	 */
	public final static Vec2i fromRounded(Vec2f v) {
		return new Vec2i(round(v.x), round(v.y));
	}
	
	/**
	 * Factory method that creates a {@code Vec2i} by taking the {@linkplain Math#floor(double) floor} of the components of a {@link Vec2f}.
	 * @param v      the {@code Vec2f} to floor
	 * @return       a {@code Vec2i} created by taking the floor of the components of {@code v}
	 */
	public final static Vec2i fromFloored(Vec2f v) {
		return new Vec2i((int)floor(v.x), (int)floor(v.y));
	}
	
	
	/**
	 * Factory method that creates a {@code Vec2i} by taking the {@linkplain Math#ceil(double) ceiling} of the components of a {@link Vec2f}.
	 * @param v      the {@code Vec2f} to ceil
	 * @return       a {@code Vec2i} created by taking the ceiling of the components of {@code v}
	 */
	public final static Vec2i fromCeiled(Vec2f v) {
		return new Vec2i((int)ceil(v.x), (int)ceil(v.y));
	}
	
	/*
	 * Vector ops
	 */
	
	/**
	 * Multiplies the vector by a scalar.
	 * @param s      the scalar by which to multiply this vector
	 * @return       a new {@link Vec2i} instance where each component has been multiplied by {@code s}
	 */
	public final Vec2i smult(int s) {
		return new Vec2i(x*s, y*s);
	}
	
	/**
	 * Divides the vector by a scalar. Note that this is integer division.
	 * @param s		the scalar by which to divide this vector
	 * @return		a new {@link Vec2i} instance where each component has been divided by
	 * 				{@code s}
	 */
	public final Vec2i sdiv(int s) {
		return new Vec2i(x/s, y/s);
	}
	
	/**
	 * Multiplies the vector piecewise by another vector. NOT A DOT PRODUCT.
	 * @param v      the vector by which to multiply this vector
	 * @return       a new {@link Vec2i} instance where each component has been multiplied by the corresponding component in {@code v}
	 */
	public final Vec2i pmult(Vec2i v) {
		return new Vec2i(x*v.x, y*v.y);
	}
	
	/**
	 * Primitive version of {@link #pmult(Vec2i)}.
	 */
	public final Vec2i pmult(int x, int y) {
		return new Vec2i(this.x * x, this.y * y);
	}
	
	/**
	 * Divides the vector piecewise by another vector.
	 * @param v      the vector by which to divide this vector
	 * @return       a new {@link Vec2i} instance where each component has been divided by the corresponding component in {@code v}
	 */
	public final Vec2i pdiv(Vec2i v) {
		return new Vec2i(x/v.x, y/v.y);
	}
	
	/**
	 * Primitive version of {@link #pdiv(Vec2i)}.
	 */
	public final Vec2i pdiv(int x, int y) {
		return new Vec2i(this.x/x, this.y/y);
	}
	
	/**
	 * Adds another vector to this vector.
	 * @param v      the vector to add to this vector
	 * @return       a new {@link Vec2i} instance where each component has added the corresponding component in {@code v}
	 */
	public final Vec2i plus(Vec2i v) {
		return new Vec2i(x + v.x, y + v.y);
	}
	
	/**
	 * Primitive version of {@link #plus(Vec2i)}.
	 */
	public final Vec2i plus(int x, int y) {
		return new Vec2i(this.x + x, this.y + y);
	}
	
	/**
	 * Subtracts another vector from this vector.
	 * @param v      the vector to subtract from this vector
	 * @return       a new {@link Vec2i} instance where each component has added the corresponding component in {@code v}
	 */
	public final Vec2i minus(Vec2i v) {
		return new Vec2i(x - v.x, y - v.y);
	}
	
	/**
	 * Primitive version of {@link #minus(Vec2i)}.
	 */
	public final Vec2i minus(int x, int y) {
		return new Vec2i(this.x - x, this.y - y);
	}
	
	public Dimension toDimension() {
		return new Dimension(this.x, this.y);
	}
	
	/*
	 * Object overrides
	 */

	@Override
	public final int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	@Override
	public final boolean equals(Object obj) {
		if (obj == null || obj.getClass() != Vec2i.class)
			return false;
		Vec2i other = (Vec2i) obj;
		return x == other.x && y == other.y;
	}

	@Override
	public final String toString() {
		return new StringBuilder("(").append(x).append(", ").append(y).append(")").toString();
	}

	public static Vec2i fromPoint(Point pt) {
		return new Vec2i(pt.x, pt.y);
	}

	public Point toPoint() {
		return new Point(this.x, this.y);
	}
}
 public class ConfigurationException extends Exception {
	
	/**
	 * Generated 
	 */
	private static final long serialVersionUID = 5399497813931843528L;

	public ConfigurationException(String msg) {
		super(msg);
	}
	
	public ConfigurationException(Throwable cause) {
		super(cause);
	}

} public abstract class Action implements Saveable {
	
	/**
	 * Generated
	 */
	private static final long serialVersionUID = 786756738941365502L;
	
	/**
	 * The cost of taking this Action
	 */
	private int cost;
	
	public Action(int cost) {
		this.cost = cost;
	}
	
	public void setCost(int cost) {
		this.cost = cost;
	}
	
	public int getCost() { return this.cost; }

	public abstract void apply(EntityActionManager queue);
	
	/*** BEGIN Saveable ***/

	private UUID id;
	
	/** initialize id **/
	{
		this.id = UUID.randomUUID();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Action other = (Action) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.equals(other.id))
			// return true if ids are the same
			return true;
		return false;
	}

	@Override
	public UUID getId() {
		return this.id;
	}
	
	/*** END Saveable ***/
} public abstract class Combatable extends Entity implements Movable {
	
	/**
	 * Generated
	 */
	private static final long serialVersionUID = -7402313976569195377L;
	
	protected int HP;
	protected int startHP;
	protected int team;
	protected Stats baseStats;
	protected Stats stats;
	protected EntityActionManager manager;
	protected Tile location;
	protected HashMap<EquipType,Stackable> equipment = new HashMap<EquipType,Stackable>();
	
	/** #Dies **/
	protected abstract void die();
	
	/**Processes an Enemy kill, gaining xp etc**/
	protected abstract void onKillEntity(Combatable combatable);
	
	/**Gets the equipment of the combatable*/
	public HashMap<EquipType,Stackable> getEquipment() {return equipment;}
	
	/**Attacks an opponent**/
	public void attack(Combatable opp) {
		if(tryHit(opp)) {
			dealDamage(opp);
		}
		else {
			Announcer.announce(this.getDescription() + " misses.");
		}
		this.manager.call(Event.ATTACK);
	}
	
	/**
	 * Does a roll to see whether the this can hit opp
	 */
	private boolean tryHit(Combatable opp) {
		return Math.random() < (((double) this.stats.getAttack()) / (this.stats.getAttack() + opp.stats.getDefense()));
	}
	
	/**
	 * Deals damage to opponent.
	 */
	private void dealDamage(Combatable opp) {
		int attackPower = (int) Math.round(Math.random()*stats.attack);
		Announcer.announce(this.getDescription() +" hits " + opp.getDescription() + ", dealing " + attackPower + " damage.");
		//System.out.println(attackPower);
		opp.takeDamage(new Attack(this,attackPower));
	}

	/**
	 * Takes damage, applies attack effects, checks to see if dead
	 */
	public void takeDamage(Attack attack) {
		changeHP(-attack.power);
		if(HP <= 0) {
			die();
			attack.opponent.onKillEntity(this);
		}
		this.manager.call(Event.ATTACKED);
		//TODO: Attack effects
	}

	/**
	 * @return the hP
	 */
	public Integer getHP() {
		return HP;
	}

	/**
	 * @return the Start HP 
	 */
	public Integer getStartHP() {
		return startHP;
	}
	
	/**
	 * @return the stats
	 */
	public Stats getStats() {
		return stats;
	}
	
	
	public void setStats(Stats s) {
		stats = s;
	}
	
	
	/**
	 * @return the base stats
	 */
	public Stats getBaseStats() {
		return baseStats;
	}

	/**
	 * @return the manager
	 */
	public EntityActionManager getManager() {
		return manager;
	}

	/**
	 * @param manager the manager to set
	 */
	public void setManager(EntityActionManager manager) {
		this.manager = manager;
	}


	public void changeHP(int delta) {
		this.HP += delta;
		this.HP = Math.min(HP, startHP);
	}
	
	/**
	 * Gets the {@link Combatable}'s location on the map.
	 * @return The location of this {@link Combatable} on the {@link Level}
	 */
	@Override
	public Tile getLocation() {
		return location;
	}

	/**
	 * Sets the {@link Combatable}'s location on the map.
	 * @param location The new location of this {@link Combatable}.
	 */
	public void setLocation(Tile location) {
		this.location = location;
	}

	public void move(Direction dir) {
		Vec2i next = location.getLocation().plus(dir.getDelta());
		Tile[][] tiles = location.getLevel().getTiles();
		Tile nextTile = null;
		if (tiles.length > next.x && next.x >= 0 && tiles[0].length > next.y && next.y >= 0)
			nextTile = tiles[next.x][next.y];
		if (nextTile != null && nextTile.isPassable()) {
			location.setEntity(null);
			nextTile.setEntity(this);
		} else if(nextTile != null && nextTile.getEntity() != null && (nextTile.getEntity() instanceof Combatable)) {
			Combatable opp = (Combatable) nextTile.getEntity();
			if(opp.team != this.team) {
				this.attack(opp);
			}
		}

	}

	public void moveTo(Tile tile) {

	}

} public interface EntityActionManager {

	public Combatable getEntity();

	public Tile getLocation();
	public Stats getStats();
	
	public int getActionPoints();
	public void useActionPoints(int ap);
	public void addActionPoints(int ap);
	
	public void on(Event e, Action callback);
	public void call(Event e);
	
	public Action getNextAction();
	public void takeNextAction();
	public void setNextAction(Action nextAction);
	public boolean hasNextAction();

	public void sendMove(Direction dir);
	public void changeHP(int delta);
	public void toggleVisibility();
	public void changeStats(Stats delta);
	public void drop(Stackable s);
	// public void modify(???);
	// public void timed(???);

} public class EntityManager implements Saveable {

	/**
	 * Generated 
	 */
	private static final long serialVersionUID = 6538908714623761737L;
	
	/**
	 * Every EntityActionManager that truly exists.
	 */
	protected List<EntityActionManager> everything = new ArrayList<EntityActionManager>();
	
	protected HashMap<String,List<EntityActionManager>> map = new HashMap<String,List<EntityActionManager>>();

	public EntityManager() {
	}

	protected void addToCategory(String category, EntityActionManager manager) {
		List<EntityActionManager> managers = map.get(category);
		if (managers == null) {
			managers = new ArrayList<EntityActionManager>();
			map.put(category, managers);
		}
		managers.add(manager);
	}


	/**
	 * Convenience method that creates an EntityActionManager for entity
	 * and adds it to map and everything
	 * 
	 * @param entity the Combatable to be registered
	 * @return the EntityActionManager created for entity and then added
	 */
	public EntityActionManager register(Combatable entity) {
		EntityActionManager manager = new LocalEntityActionManager(entity);
		manager.on(Event.DEATH, new Remove(entity, this));
		entity.setManager(manager);
		for (String category : entity.getCategories())
			addToCategory(category, manager);
		// add to everything
		everything.add(manager);
		return manager;
	}

	/**
	 * Register an EntityActionManager by adding it to map and to
	 * everything
	 * 
	 * @param manager EntityActionManager to add
	 * @param categories categories to add manager to
	 */
	public void register(EntityActionManager manager, List<String> categories) {
		for (String category : categories)
			addToCategory(category, manager);
		everything.add(manager);
	}

	/**
	 * Unregister an entity. Remove it from both map and everything.
	 * @param entity the {@link Combatable} to remove
	 */
	public void unregister(Combatable entity) {
		unregister(entity.getManager(), entity.getCategories());
		everything.remove(entity.getManager());
	}

	public void unregister(EntityActionManager manager, List<String> categories) {
		for (String category : categories) {
			List<EntityActionManager> categoryManagers = map.get(category);
			categoryManagers.remove(manager);
		}
	}

	/**
	 * @param category, String category of the EntityActionManagers to get
	 * @return a copy of the List<EntityActionManager> in map with the given
	 * category
	 */
	public List<EntityActionManager> getEntity(String category) {
		List<EntityActionManager> ret = new ArrayList<EntityActionManager>();
		List<EntityActionManager> lst = map.get(category);
		if (lst == null) {
			lst = new ArrayList<EntityActionManager>();
			map.put(category, lst);
		}
		for (EntityActionManager manager : lst) 
			ret.add(manager);
		return ret;
	}
	
	/** Gets the nth manager from the main character.
	 *  If the player does not exist, null is returned.
	 * @param n - the number of the player to get (starts at 0)
	 * @return - player #n
	 */
	public EntityActionManager getPlayer(int n) {
		List<EntityActionManager> mains = getEntity("main");
		if(n >= mains.size()) 
			return null;
		
		return mains.get(n);
	}
	
	/**
	 * Returns a boolean indicating whether or not an EntityActionManager
	 * really exists. True existence is determined by presence in the
	 * everything list, as the List returned by getEntity is just a copy.
	 * Rogue meets existentialism. Boom.
	 * 
	 * @return boolean indicating if an EntityActionManager really exists!
	 */
	public boolean reallyExists(EntityActionManager manager) {
		return everything.contains(manager);
	}
	
	/*** BEGIN Saveable ***/
	
	private UUID id;
	
	/** initialize id **/
	{
		this.id = UUID.randomUUID();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		EntityManager other = (EntityManager) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.equals(other.id))
			// return true if ids are the same
			return true;
		return false;
	}

	@Override
	public UUID getId() {
		return this.id;
	}

	/*** END Saveable ***/
} public enum Event {
	MOVE, ATTACK, ATTACKED, VISIBILITY_CHANGE, DEATH
} public class ChaseMainCharacter extends Action implements Saveable {

	/**
	 * Generated
	 */
	private static final long serialVersionUID = 644910751763675638L;
	
	EntityManager manager = null;

	public ChaseMainCharacter(int cost, EntityManager manager) {
		super(cost);
		this.manager = manager;
	}

	public void apply(EntityActionManager queue) {
		List<EntityActionManager> mains = this.manager.getEntity("main");
		if (mains.size() > 0) {
			Tile myLoc = queue.getLocation();
			Tile mainLoc = mains.get(0).getLocation();
			// Actually pathfind?
			//List<Tile> path = Pathfinder.findPath(myLoc, mainLoc, myLoc.getLevel());
			//Direction dir = path.size() > 0 ? myLoc.dirTo(mainLoc) : Direction.UP;
			Direction dir = myLoc.dirTo(mainLoc); 
			queue.sendMove(dir);
		}
	};
	
	/*** BEGIN Saveable ***/

	private UUID id;
	
	/** initialize id **/
	{
		this.id = UUID.randomUUID();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ChaseMainCharacter other = (ChaseMainCharacter) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.equals(other.id))
			// return true if ids are the same
			return true;
		return false;
	}

	@Override
	public UUID getId() {
		return this.id;
	}

	/*** END Saveable ***/

} public class Move extends Action {

	/**
	 * Generated
	 */
	private static final long serialVersionUID = -7662560441748878059L;

	private Combatable c;
	private final Direction dir;
	
	public Move(int cost, Combatable combatable , Direction dir) {
		super(cost);
		this.c= combatable;
		this.dir = dir;
	}
	
	@Override
	public void apply(EntityActionManager queue) {
		c.move(dir);
	}
	
} public class GameAction {
	
	public GameAction(int context, int action) {
		this.contextClassifier = context;
		this.actionClassifier = action;
	}

	private int contextClassifier;
	
	/**
	 * Fetches the contextClassifier from this {@link GameAction}
	 * @return
	 */
	public int getContextClassifier() {
		return contextClassifier;
	}
	
	private int actionClassifier;
	
	/**
	 * Gets the actionClassifier for this {@link GameAction}
	 * @return
	 */
	public int getActionClassifier() {
		return actionClassifier;
	}

	@Override
	public String toString() {
		return "GameAction(context=" + this.contextClassifier + ", action=" + this.actionClassifier + ")";
	}
	
} public class CumulativeTurnManager extends TurnManager {
	
	/**
	 * Generated
	 */
	private static final long serialVersionUID = 2226734907191258919L;
	
	private final int pointsPerTurn;
	
	// If the player has less than minPtsPerTurn, they won't be given the
	// chance to go again during that turn. This helps to alleviate 
	// player action lag. There may be a better way to do this
	private final int minPtsPerTurn;
	

	public CumulativeTurnManager(Application app, Game game, int pointsPerTurn) {
		super(app, game, pointsPerTurn);
		this.pointsPerTurn = pointsPerTurn;
		this.minPtsPerTurn = pointsPerTurn/2;
	}
	
	@Override
	public void takeTurnWithoutAnnounce(Action playerAction) {
		
		Action nextAction = null;
		int cost;
		
		EntityManager em = game.getCurrentLevel().getManager();
		
		EntityActionManager mainMgr = em.getEntity("main").get(0);
		
		// if the player doesn't have an Action queued, then set one
		if (!mainMgr.hasNextAction()) { 
			mainMgr.setNextAction(playerAction);
		} // otherwise try to do the action that has been queued
		
		// Player's turn:
		nextAction = mainMgr.getNextAction();
		cost = (nextAction instanceof Wait) ? 
				pointsPerTurn : nextAction.getCost();
		
		// if the Player can take the action, then take it
		if (cost <= mainMgr.getActionPoints()) {
			mainMgr.takeNextAction();
			mainMgr.useActionPoints(cost);
		}
		
		// if there are more than minPtsPerTurn remaining, then return 
		// and wait for player's next input
		if (mainMgr.getActionPoints() > minPtsPerTurn) return;
		else { // continue and grant APs for this turn
			mainMgr.addActionPoints(pointsPerTurn);
		}

		// take care of monsters:
		
		List<EntityActionManager> monsterMgrs = em.getEntity("monster");

		for (EntityActionManager mgr: monsterMgrs) {

			do { // actions
				// check to make sure this monster still exists and hasn't been
				// concurrently removed
				if (em.reallyExists(mgr)) { 

					nextAction = mgr.getNextAction();
					cost = (nextAction instanceof Wait) ? 
							pointsPerTurn : nextAction.getCost();

					// if the monster can afford it, take the action
					if (cost <= mgr.getActionPoints()) {
						mgr.takeNextAction();
						mgr.useActionPoints(cost);
					} else { break; }
				} 
			} 
			// until we have too few points to try again
			// TODO: consider making the lower bound minPtsPerTurn? 
			while (mgr.getActionPoints() > 0);
			
			// finally, grant points for this turn
			mgr.addActionPoints(pointsPerTurn);
		}
	}
} public abstract class DefaultMainLayer<A extends Application> implements Layer {
	
	protected final static int ANNOUNCE_OFFSET = 1;
	protected final static Vec2i MAP_SIZE = new Vec2i(80, 24);
	protected final static int MAX_NAME_LENGTH = 30;
	
	protected Game game;

	protected String statusMsg;
	protected String name;
	
	protected Vec2i size;
	protected A app;

	public DefaultMainLayer(A app, Game game, 
			Vec2i screenSize, String startMessage) {
		this.game = game;
		this.app = app;
		this.size = screenSize;
		this.name = "A Roguelike";
		statusMsg = startMessage;
	}
	
	@Override
	public void updateSize(Vec2i newSize) {
		this.size = newSize;
	}

	@Override
	public void doDraw(Section sw) {

		Vec2i middle = size.sdiv(2);

		if (game == null || game.getCurrentLevel() == null) { // draw status message
			sw.drawString(middle.x - statusMsg.length() / 2, 0, statusMsg);
		} else { // otherwise draw level
			drawLevel(sw);
			drawStats(sw);
		}
	}

	private void drawStats(ScreenWriter sw) {
		
		Level currentLevel = game.getCurrentLevel();
		
		// Draw the divider
		String divider = "";
		for (int i = 0; i < size.x; i++) {
			divider += '_';
		}
		sw.drawString(0, ANNOUNCE_OFFSET + MAP_SIZE.y, divider);
		int statStart = ANNOUNCE_OFFSET + MAP_SIZE.y + 1;

		// CHECK FOR WIN/LOSE
		List<EntityActionManager> main = currentLevel.getManager()
				.getEntity("main");
		
		if(main.size() == 0) {
			sw.drawString(0, statStart, "You lose!");
			return;
		}
		MainCharacter player = (MainCharacter) main.get(0).getEntity();

		// DRAW THE STATS
		String line1 = "";
		line1 += "Robert the Rogue   ";
		line1 += "HP: ";
		line1 += player.getHP();
		line1 += "/";
		line1 += player.getStartHP();
		line1 += "   XP: ";
		line1 += player.getXP();
		line1 += "/";
		line1 += player.getNextLevelXP();
		line1 += "       ";
		sw.drawString(0, statStart, line1);
		
		String depthString = "Dungeon Level: " + currentLevel.getDepth();
		sw.drawString(size.x - depthString.length() -2, statStart, depthString);

		// LINE 2
		String line2 = "";
		line2 = "Level: " + player.getPlayerLevel();
		line2 += "  Attack: ";
		line2 += player.getStats().getAttack();
		line2 += "  Defense: ";
		line2 += String.valueOf(player.getStats().getDefense());
		line2 += "       ";
		sw.drawString(0, statStart + 1, line2);

	}

	private void drawLevel(Section sw) {
		game.getCurrentLevel().doDraw(sw.moveUpperLeft(new Vec2i(0, ANNOUNCE_OFFSET)));
	}

	public abstract GameAction getActionForKey(Key k);
	public abstract void propagateAction(GameAction action);
	public abstract void tick(long nanosSincePreviousTick);

} public class DropLayer<A extends Application> extends UseItemLayer<A> {

	public DropLayer(TurnManager tm, A app, Vec2i size, Level currentLevel) {
		super(tm, app, size, currentLevel);
	}

	@Override
	public void doDraw(Section s) {
		s.drawString(0, 0, "Drop what?");
	}

	@Override
	protected void applyItemEffect(Stackable item, EntityActionManager target) {
			Announcer.announce("You drop the " + item.getDescription()+ ".");
			tm.takeTurn(new Drop(item));
	}

} public class HelpLayer<A extends Application> implements Layer{

	private Vec2i size;
	private A app;

	public HelpLayer( A app, Vec2i size) {
		this.app = app;
		this.size = size;
	}

	@Override
	public void doDraw(Section s) {
		s.drawString(1, 0, "- Move with the arrow keys or hjkl");
		s.drawString(1, 1, "- The enemies are letters! Move into them to fight them");
		s.drawString(1, 2, "- < and > are up and down stairs,");
		s.drawString(1, 3, "- Stand on stairs and press the correspsonding key to use them");
		s.drawString(1, 4, "- Other characters are items, walk on them to pick them up");
		s.drawString(1, 5, "- Use 'o' to display a cursor to observe the world");
		s.drawString(1, 6, "- Quaff potions with q");
		s.drawString(1, 7, "- Wield weapons with w");
		s.drawString(1, 8, "- Drop items with d");
		s.drawString(1, 9, "- Bring up inventory with i (quit with space)");
		s.drawString(1, 10, "- Create a new game with N");
		s.drawString(1, 11, "- Save and Quit with Q");
	}

	@Override
	public GameAction getActionForKey(Key k) {
		return new GameAction(0,0); //Always pop
	}

	@Override
	public void propagateAction(GameAction action) {
		app.getLayers().pop(); 
	}

	@Override
	public void updateSize(Vec2i newSize) {
		this.size = newSize;
	}

	@Override
	public void tick(long nanosSincePreviousTick) {}



} public class LookLayer<A extends Application> implements Layer {
	
	private final int OFFSET = 30;
	
	protected Level currentLevel;

	protected Vec2i size;
	protected A app;
	protected Tile loc;

	public LookLayer(A app, Vec2i size,Level currentLevel) {
		this.app = app;
		this.size = size;
		this.currentLevel = currentLevel;

		//Set loc to player
		EntityActionManager player = currentLevel.getManager().getPlayer(0);
		if(player == null) {
			loc = currentLevel.tiles[0][0];
		}
		else {
			loc = player.getLocation();
		}		
	}

	@Override
	public void updateSize(Vec2i newSize) {
		this.size = newSize;
	}

	@Override
	public void doDraw(Section sw) {
		sw.setForegroundColor(Color.YELLOW);
		sw.drawString(loc.getLocation().x, loc.getLocation().y + DefaultMainLayer.ANNOUNCE_OFFSET, "X");
		
		int startX = size.x-OFFSET;
		int currentY = 0;
		if(loc.getLocation().x > size.x - OFFSET) {
			startX = 0;
		}
		
		sw.setForegroundColor(Color.DEFAULT);
		if(loc.getEntity() != null){
			sw.drawString(startX, currentY, loc.getEntity().getDescription());
			currentY++;
		}
		
		for(Stackable s : loc.getStackables()) {
			sw.drawString(startX, currentY, s.getDescription());
			currentY++;
		}
		
		if(loc.getType().getDescription() != null && loc.getReveal()) {
			sw.drawString(startX, currentY, loc.getType().getDescription());
			currentY++;
		}
		
		if(currentY == 0) {
			sw.drawString(startX, currentY, "Nothing here");
		}
		
		
	}

	public GameAction getActionForKey(Key k) {
		switch (k.getKind()) {
		case NormalKey: {
			switch (k.getCharacter()) {
			case 'h':
				return new GameAction(1, 1); // Move left
			case 'j':
				return new GameAction(1, 2); // Move down
			case 'k':
				return new GameAction(1, 3); // Move up
			case 'l':
				return new GameAction(1, 4); // Move right
			case ' ':
				return new GameAction(1, 5); // Close
			default:
				return new GameAction(1, 0); // do nothing
			}
		}
		case ArrowLeft:
			return new GameAction(1, 1); // Move left
		case ArrowDown:
			return new GameAction(1, 2); // Move down
		case ArrowUp:
			return new GameAction(1, 3); // Move up
		case ArrowRight:
			return new GameAction(1, 4); // Move right
		case Escape:
			return new GameAction(1,5); //Close
		default:
			return new GameAction(1, 0); // do nothing
		}
	}

	public  void propagateAction(GameAction action) {
		if (action.getContextClassifier() != 1)
			throw new Error("Received an action for a non-gameplay context.");

		Direction dir = null;

		switch (action.getActionClassifier()) {
		case 1: //Move Left
			dir = Direction.LEFT;
			break;
		case 2: //Move Down
			dir = Direction.DOWN;
			break;
		case 3: //Move Up
			dir = Direction.UP;
			break;
		case 4: //Move Right
			dir = Direction.RIGHT;
			break;
		case 5:
			app.getLayers().pop(); //Remove this layer from stack
			return;
		case 0:
			return;
		}

		Vec2i next = loc.getLocation().plus(dir.getDelta());
		Tile[][] tiles = currentLevel.getTiles();
		if (tiles.length > next.x && next.x >= 0 && tiles[0].length > next.y && next.y >= 0)
			loc = tiles[next.x][next.y];

	}


	public  void tick(long nanosSincePreviousTick){}

} public class PotionLayer<A extends Application> extends UseItemLayer<A> {

	public PotionLayer(TurnManager tm, A app, Vec2i size, Level currentLevel) {
		super(tm, app, size, currentLevel);
	}

	@Override
	public void doDraw(Section s) {
		s.drawString(0, 0, "Quaff what?");
	}

	@Override
	protected void applyItemEffect(Stackable item, EntityActionManager target) {
		if(item.getType() == ItemType.POTION) {
			Potion p = (Potion) item;
			Announcer.announce("You quaff the " + p.getDescription()+ ".");
			tm.takeTurn(p.quaffAction);
			target.getEntity().getInventory().remove(item);
		}
	}

} public class WeaponLayer<A extends Application> extends UseItemLayer<A> {

	public WeaponLayer(TurnManager tm, A app, Vec2i size, Level currentLevel) {
		super(tm, app, size, currentLevel);
	}

	@Override
	public void doDraw(Section s) {
		s.drawString(0, 0, "Wield what?");
	}

	@Override
	protected void applyItemEffect(Stackable item, EntityActionManager target) {
		if(item.getType() == ItemType.WEAPON) {
			Weapon w = (Weapon) item;
			
			Weapon oldWeap = (Weapon) target.getEntity().getEquipment().get(EquipType.WEAPON); 
			if(oldWeap != null) {
				tm.takeTurn(oldWeap.getUnwieldAction());
				target.getEntity().getInventory().add(oldWeap);
			}
			
			Announcer.announce("You wield the " + w.getDescription());
			target.getEntity().getEquipment().put(EquipType.WEAPON, w);
			
			tm.takeTurn(w.getWieldAction());
			target.getEntity().getEquipment().put(EquipType.WEAPON, w);
			target.getEntity().getInventory().remove(item);
		}
	}

} public enum Direction {
	LEFT(-1, 0), RIGHT(1, 0), UP(0, -1), DOWN(0, 1);

	protected Vec2i delta = null;

	Direction(int x, int y) {
		delta = new Vec2i(x, y);
	}

	/**
	 * @return the delta on the map to get to our new tile.
	 */
	public Vec2i getDelta() {
		return delta;
	}
} public class Level implements Saveable, Artist {

	/**
	 * Generated
	 */
	private static final long serialVersionUID = 1187760172149150039L;

	private static final int TIER_STEP_SIZE = 5;

	public final Tile[][] tiles;
	private List<Room> rooms;
	private List<Hallway> hallways;
	protected EntityManager manager = new EntityManager();
	private int depth; //The depth of the level
	public HashSet<Room> revealedRooms = new HashSet<Room>();
	
	public Tile upStairs;
	public Tile downStairs;
	
	public int getTier() {
		 return (1 + (this.depth/TIER_STEP_SIZE));
	}
	
	public int getTierDiff() {
		 return this.depth + 1 -  TIER_STEP_SIZE*(getTier()-1);
	}
	
	/**
	 * @return The entity manager for this {@link Level}.
	 */
	public EntityManager getManager() {
		return manager;
	}

	public void revealRoom(Room r) {
		if(revealedRooms.contains(r))
			return;
		
		for(int i = r.min.x-1; i<=r.max.x+1; i++) {
			for(int j = r.min.y-1; j<=r.max.y+1; j++) {
				tiles[i][j].setReveal(true);
			}
		}
		revealedRooms.add(r);
	}

	public Level(Tile[][] tiles, List<Room> rooms, List<Hallway> hallways) {
		this.tiles = tiles;
		for (Tile[] tiles2 : tiles)
			for (Tile tile : tiles2)
				tile.setLevel(this);
		this.rooms = rooms;
		this.hallways = hallways;
	}

	public Tile[][] getTiles() { return this.tiles; }
	public List<Room> getRooms() { return this.rooms; }
	public List<Hallway> getHallways() { return this.hallways; }	
	public int getDepth(){return depth;}

	public void setDepth(int depth) {this.depth = depth;}

	/*** BEGIN Saveable ***/

	private UUID id;

	/** initialize id **/
	{
		this.id = UUID.randomUUID();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Level other = (Level) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.equals(other.id))
			// return true if ids are the same
			return true;
		return false;
	}

	@Override
	public UUID getId() {
		return this.id;
	}

	/*** END Saveable ***/

	public List<Tile> getNeighbors(Tile current) {
		List<Tile> neighbors = new LinkedList<Tile>();

		if(current.getLocation().x != 0) {
			neighbors.add(tiles[current.getLocation().x-1][current.getLocation().y]);
		}
		if(current.getLocation().x != tiles.length) {
			neighbors.add(tiles[current.getLocation().x+1][current.getLocation().y]);
		}
		if(current.getLocation().y != 0) {
			neighbors.add(tiles[current.getLocation().x][current.getLocation().y-1]);
		}
		if(current.getLocation().y != tiles[0].length) {
			neighbors.add(tiles[current.getLocation().x][current.getLocation().y+1]);
		}
		return neighbors;
	}

	public void revealAround(Tile t) {
		for(int i = t.location.x-1; i<=t.location.x+1;i++) {
			for(int j = t.location.y-1; j<=t.location.y+1;j++) {
				tiles[i][j].setReveal(true);
			}
		}
	}

	/**Removes the player from the world and returns it
	 * Allows for world to be simulated while the player is not there.
	 * @return The main character of the level
	 */
	public MainCharacter removePlayer() {
		List<EntityActionManager> mains = manager.getEntity("main");
		if(mains.size() >0) {
			MainCharacter mc = (MainCharacter) mains.get(0).getEntity();
			mc.getLocation().setEntity(null);
			manager.unregister(mc);
			return mc;
		}
		else
			return null;
	}

	/**Places the character onto the map on the stairs indicated by the boolean argument
	 * @param mc The main character to place
	 *
	 * @param up: If true, place on the down stairs, else, the up stairs
	 */
	public void placeCharacter(MainCharacter mc, boolean up) {
		
		Entity current = (up ? upStairs.getEntity() : downStairs.getEntity());
		if(current != null) {
			RandomGen rand = new RandomGen(System.nanoTime());
		Room r = this.rooms.get(rand.getRandom(rooms.size()));
		boolean placedMonster = false;
		do {
			int mX = rand.getRandom(r.min.x, r.max.x);
			int mY = rand.getRandom(r.min.y, r.max.y);
			Tile t = tiles[mX][mY];
			if(t.getEntity() == null) {
				t.setEntity(current);
				current.setLocation(t);
				placedMonster = true;
			}
		}
		while(!placedMonster);
		}
		
		if(up)
			upStairs.setEntity(mc);
		else
			downStairs.setEntity(mc);
		
		LinkedList<String> attr = new LinkedList<String>();
		manager.register(mc);
	}

	public void doDraw(ScreenWriter sw) {
		Tile t;
		for (int c = 0; c < tiles.length; c++) {
			for (int r = tiles[0].length - 1; r >= 0; r--) { // flip y
				t = tiles[c][r];
				sw.setForegroundColor(t.getColor());
				sw.drawString(c, r,
						String.valueOf(t.getCharacter()),
						ScreenCharacterStyle.Bold);
			}
		}
	}

} public class Room implements Space, Saveable {
	
	/**
	 * Generated
	 */
	private static final long serialVersionUID = -760167898015853500L;
	
	protected List<Room> connectedRooms; //The rooms directly connected to this room

	public Vec2i min; //Min point of room, excluding walls
	public Vec2i max; //Max point of room, excluding walls
	
	public List<Room>  getConnectedRooms() {return this.connectedRooms;}
	public void addRoom(Room room) {connectedRooms.add(room);}
	
	
	
	public Room(Vec2i min, Vec2i max) {
		this.min = min;
		this.max = max;
		this.connectedRooms = new ArrayList<Room>();
	}
	
	public boolean containsTile(Tile t) {
		return (t.location.x >= min.x && t.location.x <= max.x && t.location.y >= min.y && t.location.y <= max.y);
	}
	
	@Override
	public void connectToHallway(Hallway h) {
		for(Room r : h.getRooms()) {
			this.addRoom(r);
			h.addRoom(this);
		}
	}

	
	@Override
	public boolean needDoor() {
		return true;
	}
	
	/*** BEGIN Saveable ***/

	private UUID id;
	
	/** initialize id **/
	{
		this.id = UUID.randomUUID();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Room other = (Room) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.equals(other.id))
			// return true if ids are the same
			return true;
		return false;
	}

	@Override
	public UUID getId() {
		return this.id;
	}

}
 public class Tile extends AStarNode<Tile> implements Saveable, Drawable {
	
	// The Space I belong to
	protected Space space;

	protected Vec2i location = null;
	protected Level level = null;
	protected boolean reveal = false;

	/**Allows you to reveal/hide a tile**/
	public void setReveal(boolean r) {
		this.reveal = r;
	}
	
	/**
	 * Generated
	 */
	private static final long serialVersionUID = -4972149313921847289L;

	public Tile(TileType type) {
		this.type = type;
	}
	
	public Tile(TileType type, Space space) {
		this.type = type;
		this.space = space;
	}
	
	private LinkedList<Stackable> stackables = new LinkedList<Stackable>();

	/**
	 * @return the location
	 */
	public Vec2i getLocation() {
		return location;
	}
	
	public Space getSpace() { return space; }
	public void setSpace(Space space) { this.space = space; }

	/**
	 * @param location the location to set
	 */
	public void setLocation(Vec2i location) {
		this.location = location;
	}

	/**
	 *
	 */
	public Direction dirTo(Tile other) {
		Vec2i otherLoc = other.getLocation();
		if (location.x < otherLoc.x)
			return Direction.RIGHT;
		else if (location.x > otherLoc.x)
			return Direction.LEFT;
		else if (location.y < otherLoc.y)
			return Direction.DOWN;
		else 
			return Direction.UP;
	}

	/**
	 * @return the level
	 */
	public Level getLevel() {
		return level;
	}

	/**
	 * @param level the level to set
	 */
	public void setLevel(Level level) {
		this.level = level;
	}

	public LinkedList<Stackable> getStackables() {
		return stackables;
	}
	
	private Entity entity;

	public Entity getEntity() {
		return entity;
	}

	public void setEntity(Entity entity) {
		if (entity != null)
			entity.setLocation(this);
		this.entity = entity;
	}


	public boolean isPassable() {
		if (entity != null)
			return false;
		else
			return type.isPassable();
	}

	private TileType type;

	public TileType getType() {
		return type;
	}

	public void setType(TileType type) {
		this.type = type;
	}
	
    @Override
    public String toString() {
        return "Tile" + location.toString();
    }

	public boolean getReveal() {
		return this.reveal;
	}

	@Override
	protected int getHScore(Tile goal) {
		return 10*calcManhattan(goal);
	}
	
	@Override 
	public ArrayList<Tile> getNeighbors() {
		
		List<Tile> trueNeighbors = level.getNeighbors(this);
		
		ArrayList<Tile> passableNeighbors = new ArrayList<Tile>();
		
		for (Tile t : trueNeighbors) 
			if ( t.getType().passable ) passableNeighbors.add(t);
		
		return passableNeighbors;
	}
	
	// Convenience method to calculate the Manhattan distance
	protected int calcManhattan(Tile goal) {
		
		int rows = Math.abs(location.y-goal.location.y);
		int cols = Math.abs(location.x-goal.location.x);
		
		return rows + cols;
	}

	@Override
	public int distance(Tile neighbor) {
		int penalty = 0;
		
		Entity occupant = neighbor.getEntity();
		
		// Don't move into other Monsters, silly
		if (occupant != null && !(occupant instanceof MainCharacter)) penalty = 100; 
		
		return 10 + penalty;
	}

	/*** BEGIN Saveable ***/

	private UUID id;
	
	/** initialize id **/
	{
		this.id = UUID.randomUUID();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Tile other = (Tile) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.equals(other.id))
			// return true if ids are the same
			return true;
		return false;
	}

	@Override
	public UUID getId() {
		return this.id;
	}


	public char getCharacter() {
		return getCurrent().getCharacter();
	}

	public Color getColor() {
		return getCurrent().getColor();
	}

	protected Drawable getCurrent() {
		if(this.reveal == false && !(entity instanceof MainCharacter)) {
			return TileType.HIDDEN;
		}
		if (this.entity != null)
			return entity;
		else if(stackables.size() > 0)
			return stackables.getFirst();
		else
			return getType();
	}



}
 public enum TileType implements Drawable {
	WALL_HOR('-',Color.DEFAULT, false, "A rough cave wall"),
	WALL_VER('|',Color.DEFAULT, false, "A rough cave wall"),
	SOLID(' ',Color.DEFAULT, false, null),
	DOOR('+',Color.DEFAULT, true, "A door"),
	UP_STAIRS('<',Color.DEFAULT, true, "Stairs leading upward"),
	DOWN_STAIRS('>',Color.DEFAULT, true, "Stairs leading downward"),
	FLOOR('.',Color.DEFAULT, true, null), 
	HIDDEN(' ', Color.DEFAULT, true, null);


	protected char character;
	protected Color color;
	protected boolean passable;
	protected String description;

	TileType(char character, Color color, boolean passable, String description) {
		this.character = character;
		this.color = color;
		this.passable = passable;
		this.description = description;
	}

	/**
	 * @return The character that represents this tile type 
	 */
	public char getCharacter() {
		return character;
	}

	/**
	 * @return The color that this tile has.
	 */
	public Color getColor() {
		return color;
	}

	public boolean isPassable() {
		return passable;
	}

	public String getDescription() {
		return description;
	}
}
 public class SaveLoadException extends Exception {

	/**
	 * Generated
	 */
	private static final long serialVersionUID = -723895557695580009L;

	public SaveLoadException(String msg) {
		super(msg);
	}
	
	public SaveLoadException(Throwable cause) {
		super(cause);
	}

}
import java.util.ArrayList;
import java.util.List;
import com.googlecode.lanterna.input.Key;
import cs195n.Vec2i;
import edu.brown.cs.roguelike.engine.config.ConfigurationException;
import edu.brown.cs.roguelike.engine.entities.Action;
import edu.brown.cs.roguelike.engine.entities.Combatable;
import edu.brown.cs.roguelike.engine.entities.EntityActionManager;
import edu.brown.cs.roguelike.engine.entities.EntityManager;
import edu.brown.cs.roguelike.engine.entities.Event;
import edu.brown.cs.roguelike.engine.entities.events.ChaseMainCharacter;
import edu.brown.cs.roguelike.engine.entities.events.Move;
import edu.brown.cs.roguelike.engine.events.GameAction;
import edu.brown.cs.roguelike.engine.game.CumulativeTurnManager;
import edu.brown.cs.roguelike.engine.graphics.DefaultMainLayer;
import edu.brown.cs.roguelike.engine.graphics.DropLayer;
import edu.brown.cs.roguelike.engine.graphics.HelpLayer;
import edu.brown.cs.roguelike.engine.graphics.LookLayer;
import edu.brown.cs.roguelike.engine.graphics.PotionLayer;
import edu.brown.cs.roguelike.engine.graphics.WeaponLayer;
import edu.brown.cs.roguelike.engine.level.Direction;
import edu.brown.cs.roguelike.engine.level.Level;
import edu.brown.cs.roguelike.engine.level.Room;
import edu.brown.cs.roguelike.engine.level.Tile;
import edu.brown.cs.roguelike.engine.level.TileType;
import edu.brown.cs.roguelike.engine.save.SaveLoadException;
package edu.brown.cs.roguelike.game;




/**
 * A simple demo layer that shows basic rendering of levels, saving/loading, and
 * procedural generation
 *
 * @author lelberty
 *
 */
public class MainLayer extends DefaultMainLayer<GUIApp> {

	public MainLayer(GUIApp guiApp, RogueGame game, Vec2i size, String startMessage) {
		super(guiApp, game, size, startMessage);
		if(game != null && game.getCurrentLevel() != null)
			checkReveal();
	}



	@Override
	public GameAction getActionForKey(Key k) {
		switch (k.getKind()) {
		case NormalKey: {
			switch (k.getCharacter()) {
			case 'N':
				return new GameAction(1, 1); // generate new level
			case 'S':
				return new GameAction(1, 2); // save current level
				// 			case 'L':
				// return new GameAction(1, 3); // load saved level
			case 'Q':
				return new GameAction(1, 4); // quit
			case 'h':
				return new GameAction(1, 5); // Move left
			case 'j':
				return new GameAction(1, 7); // Move down
			case 'k':
				return new GameAction(1, 6); // Move up
			case 'l':
				return new GameAction(1, 8); // Move right
			case 'i':
				return new GameAction(1, 9); // Open Inventory
			case 'q':
				return new GameAction(1, 10); // QuaffPotion
			case 'w':
				return new GameAction(1, 11); // Wield
			case 'o':
				return new GameAction(1, 12); // observe
			case '<':
				return new GameAction(1, 13); // up stairs
			case '>':
				return new GameAction(1, 14); // down stairs
			case 'd':
				return new GameAction(1, 15); // drop
			case '?':
				return new GameAction(1, 16); // help
			default:
				return new GameAction(1, 0); // do nothing
			}
		}
		case ArrowLeft:
			return new GameAction(1, 5); // Move left
		case ArrowUp:
			return new GameAction(1, 6); // Move up
		case ArrowDown:
			return new GameAction(1, 7); // Move down
		case ArrowRight:
			return new GameAction(1, 8); // Move right
		default:
			return new GameAction(1, 0); // do nothing
		}
	}

	// CHECK FOR WIN/LOSE
	private void checkLose() {
		List<EntityActionManager> main = game.getCurrentLevel().getManager().getEntity("main");
		if (main.isEmpty()) {
			app.deleteSaveFile();
			return;
		}
	}

	@Override
	public void propagateAction(GameAction action) {

		Level currentLevel = game.getCurrentLevel();
		CumulativeTurnManager tm = app.getTurnManager();

		EntityActionManager mainManager = null;

		Move moveAction;
		Combatable c = null;