package mapmaker; import java.awt.*; import java.lang.*; import java.util.*; import mapmaker.mapcmd.*; import util.*; /** part of a Map */ public class CRoom implements Room { // elements of exits may never be null protected Exit[] exits = new Exit[Dir.DIRNR]; protected AreaMap map; protected boolean marked = false; protected NamedColor color = null; /** @param map must be map containing room */ public CRoom(AreaMap map) { this.map = map; for (int i = 0; i < exits.length; i++) exits[i] = new Exit(); } // CRoom /** executes the given command from the mapmaker.mapcmd package; * the following commands are supported: * RCmdSetExitBlocked, * RCmdKillLink, * RCmdSwapExits; * should only be delegated by Map object */ public void execute(RoomCommand cmd) { if (cmd instanceof RCmdSetExitBlocked) { setExitBlocked(((RCmdSetExitBlocked)cmd).dir, ((RCmdSetExitBlocked)cmd).blocked); return; } if (cmd instanceof RCmdKillLink) { killLink(((RCmdKillLink)cmd).dir); return; } if (cmd instanceof RCmdSwapExits) { swapExits(((RCmdSwapExits)cmd).dir1, ((RCmdSwapExits)cmd).dir2); return; } if (cmd instanceof RCmdSetMarked) { setMarked(((RCmdSetMarked)cmd).marked); return; } if (cmd instanceof RCmdSetColor) { setColor(((RCmdSetColor)cmd).color); return; } } // execute /** creates a deep-copy of the given room's data (except links!) * and replaces its own data with it * @param orgRoom the room to be copied */ public void deepCopyFrom(Room orgRoom) { for (int dir = 0; dir < Dir.DIRNR; dir++) exits[dir] = new Exit(orgRoom.exitBlocked(dir)); setColor(orgRoom.getColor()); } // deepCopyFrom public Room cloneRoom(AreaMap map) { Room room = new CRoom(map); room.deepCopyFrom(this); return room; } // cloneRoom /** returns the room's position on the map */ public Point getPos() { return map.getRoomPos(this); } // getPos /** should only be called by Map or Link object */ public void setLink(Link link, int dir) { if (!exitLinked(dir)) exits[dir].link = link; else throw new IllegalArgumentException(); } // setLink public Link getLink(int dir) { return exits[dir].link; } // getLink public int getExitDir(Link link) { for (int i = 0; i < Dir.DIRNR; i++) if ( link == exits[i].link) return i; throw new IllegalArgumentException(); } // getExitDir public boolean exitLinked(int dir) { return (exits[dir].link != null); } // exitLinked public boolean exitBlocked(int dir) { return exits[dir].blocked; } // exitBlocked /** sets an exit to be blocked or free * @param dir exit to be configured */ protected void setExitBlocked(int dir, boolean blocked) { if (exitLinked(dir)) { exits[dir].blocked = blocked; } } // setExitBlocked /** swaps the links at the exits given through dir1, dir2 */ protected void swapExits(int dir1, int dir2) { // shouldn't be called by map Exit buf = exits[dir1]; exits[dir1] = exits[dir2]; exits[dir2] = buf; } // swapExits public void unlink(Link link) { // sets the corresponding exit to null but does nothing else // called by a link when it self-destructs exits[getExitDir(link)] = new Exit(); } // unlink /** removes the link at the exit indicated by dir if there * is one, otherwise does nothing */ protected void killLink(int dir) { if (exitLinked(dir)) { exits[dir].link.suicide(); } } // killLink /** should only be called by Map parent */ public void suicide() { for (int i = 0; i < Dir.DIRNR; i++) killLink(i); map.removeRoom(this); } // suicide /* returns up or down direction if one or both are free; * if neither is free, returns -1; * used by bestFreeExit; */ private int bestUpDownExitTo(Room target) { if (exitLinked(Dir.up) && exitLinked(Dir.down)) return -1; if (exitLinked(Dir.up)) return Dir.down; else if (exitLinked(Dir.down)) return Dir.up; else { // now select one of the two, based on their screen position Point targetPos = target.getPos(); Point pos = getPos(); if (pos.y < targetPos.y || (pos.y <= targetPos.y && pos.x < targetPos.x)) { return Dir.down; } else return Dir.up; } } // bestUpDownExitTo /** returns the exit that points the "best" in the direction * of the give room among the free ones; * if no free exits returns -1 */ public int bestFreeExitTo(Room target) { // find smallest free exit int bestExit = 0; while (bestExit < Dir.DIRNR && exitLinked(bestExit)) bestExit++; if (bestExit >= Dir.DIRNR) return -1; if (bestExit >= Dir.PLANEDIRNR) return bestUpDownExitTo(target); // norm how well an exit fits is the scalar product between // the wanted direction and the exit direction Point thisPos = getPos(); Point targetPos = target.getPos(); Point delta = new Point(targetPos.x - thisPos.x, targetPos.y - thisPos.y); double[] scalars = new double[Dir.PLANEDIRNR]; for (int i = 0; i < Dir.PLANEDIRNR; i++) scalars[i] = MapMath.dirScalar(delta, Dir.screenDir(i)); // find best free exit for (int i = bestExit + 1; i < Dir.PLANEDIRNR; i++) if (!exitLinked(i) && scalars[i] > scalars[bestExit]) bestExit = i; // if not a 'perfect' direction, return up/down exit if (!MapMath.positiveParallel(delta, Dir.screenDir(bestExit))) { int bestUpDown = bestUpDownExitTo(target); if (bestUpDown != -1) return bestUpDown; } return bestExit; } // bestFreeExitTo /** return the Room objects linked to the room * @param upDownToo if false, does not return rooms linked * over 'up' or 'down' exits */ public Set neighbours(Object upDownToo) { // evaluate if up/down connections considered too int maxIndex = Dir.DIRNR - 2; if (((Boolean)upDownToo).booleanValue()) maxIndex = Dir.DIRNR; Set neighbours = new HashSet(maxIndex); for (int i = 0; i < maxIndex; i++) if (exitLinked(i)) neighbours.add(getLink(i).opposite(this)); return neighbours; } // neighbours public boolean getMarked() { return marked; } // getMarked /** sets the room as marked */ protected void setMarked(boolean marked) { this.marked = marked; } // setMarked public NamedColor getColor() { return color; } protected void setColor(NamedColor color) { this.color = color; } } // Room /** wrappes a link and its accessability */ class Exit { public Link link; public boolean blocked; public Exit() { this(null, false); } // Exit /** @param blocked the state of accessability */ public Exit(boolean blocked) { this(null, blocked); } // Exit /** @param link the link to be wrapped */ public Exit(Link link) { this(link, false); } // Exit /** @param link the link to be wrapped * @param blocked the state of accessability */ public Exit(Link link, boolean blocked) { this.link = link; this.blocked = blocked; } // Exit } // Exit