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