@SuppressWarnings("unchecked")
	protected Room layOutRooms(Area A, LayoutManager layoutManager, int size, int direction, XMLTag piece, Map<String,Object> defined) throws CMException
	{
		if(CMSecurity.isDebugging(CMSecurity.DbgFlag.MUDPERCOLATOR))
			Log.debugOut("MudPercolator","Using LayoutManager:"+layoutManager.name());
		final Random random=new Random(System.currentTimeMillis());
		final List<LayoutNode> roomsToLayOut = layoutManager.generate(size,direction);
		if((roomsToLayOut==null)||(roomsToLayOut.size()==0))
			throw new CMException("Unable to fill area of size "+size+" off layout "+layoutManager.name());
		int numLeafs=0;
		for(int i=0;i<roomsToLayOut.size();i++)
		{
			final LayoutNode node=roomsToLayOut.get(i);
			if(node.type()==LayoutTypes.leaf)
				numLeafs++;
		}
		defined.put("AREA_NUMLEAFS", ""+numLeafs);

		// now break our rooms into logical groups, generate those rooms.
		final List<List<LayoutNode>> roomGroups = new Vector<List<LayoutNode>>();
		final LayoutNode magicRoomNode = roomsToLayOut.get(0);
		final HashSet<LayoutNode> nodesAlreadyGrouped=new HashSet<LayoutNode>();
		boolean keepLooking=true;
		while(keepLooking)
		{
			keepLooking=false;
			for(int i=0;i<roomsToLayOut.size();i++)
			{
				final LayoutNode node=roomsToLayOut.get(i);
				if(node.type()==LayoutTypes.leaf)
				{
					final Vector<LayoutNode> group=new Vector<LayoutNode>();
					group.add(node);
					nodesAlreadyGrouped.add(node);
					for(final Integer linkDir : node.links().keySet())
					{
						final LayoutNode dirNode=node.links().get(linkDir);
						if(!nodesAlreadyGrouped.contains(dirNode))
						{
							if((dirNode.type()==LayoutTypes.leaf)
							||(dirNode.type()==LayoutTypes.interior)&&(node.isFlagged(LayoutFlags.offleaf)))
							{
								group.addElement(dirNode);
								nodesAlreadyGrouped.add(dirNode);
							}
						}
					}
					for(final LayoutNode n : group)
						roomsToLayOut.remove(n);
					if(group.size()>0)
					{
						// randomize the leafs a bit
						if(roomGroups.size()==0)
							roomGroups.add(group);
						else 
							roomGroups.add(random.nextInt(roomGroups.size()),group);
					}
					keepLooking=true;
					break;
				}
			}
		}
		keepLooking=true;
		while(keepLooking)
		{
			keepLooking=false;
			for(int i=0;i<roomsToLayOut.size();i++)
			{
				final LayoutNode node=roomsToLayOut.get(i);
				if(node.type()==LayoutTypes.street)
				{
					final List<LayoutNode> group=new Vector<LayoutNode>();
					group.add(node);
					nodesAlreadyGrouped.add(node);
					final LayoutRuns run=node.getFlagRuns();
					if(run==LayoutRuns.ns)
					{
						layoutFollow(node,LayoutTypes.street,Directions.NORTH,nodesAlreadyGrouped,group);
						layoutFollow(node,LayoutTypes.street,Directions.SOUTH,nodesAlreadyGrouped,group);
					}
					else
					if(run==LayoutRuns.ew)
					{
						layoutFollow(node,LayoutTypes.street,Directions.EAST,nodesAlreadyGrouped,group);
						layoutFollow(node,LayoutTypes.street,Directions.WEST,nodesAlreadyGrouped,group);
					}
					else
					if(run==LayoutRuns.nesw)
					{
						layoutFollow(node,LayoutTypes.street,Directions.NORTHEAST,nodesAlreadyGrouped,group);
						layoutFollow(node,LayoutTypes.street,Directions.SOUTHWEST,nodesAlreadyGrouped,group);
					}
					else
					if(run==LayoutRuns.nwse)
					{
						layoutFollow(node,LayoutTypes.street,Directions.NORTHWEST,nodesAlreadyGrouped,group);
						layoutFollow(node,LayoutTypes.street,Directions.SOUTHEAST,nodesAlreadyGrouped,group);
					}
					else
					{
						int topDir=-1;
						int topSize=-1;
						for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
							if(node.links().get(Integer.valueOf(d))!=null)
							{
								final List<LayoutNode> grpCopy=new XVector<LayoutNode>(group);
								final HashSet<LayoutNode> nodesAlreadyGroupedCopy=(HashSet<LayoutNode>)nodesAlreadyGrouped.clone();
								layoutFollow(node,LayoutTypes.street,d,nodesAlreadyGroupedCopy,grpCopy);
								if(node.links().get(Integer.valueOf(Directions.getOpDirectionCode(d)))!=null)
									layoutFollow(node,LayoutTypes.street,Directions.getOpDirectionCode(d),nodesAlreadyGroupedCopy,grpCopy);
								if(grpCopy.size()>topSize)
								{
									topSize=grpCopy.size();
									topDir=d;
								}
							}
						if(topDir>=0)
						{
							layoutFollow(node,LayoutTypes.street,topDir,nodesAlreadyGrouped,group);
							if(node.links().get(Integer.valueOf(Directions.getOpDirectionCode(topDir)))!=null)
								layoutFollow(node,LayoutTypes.street,Directions.getOpDirectionCode(topDir),nodesAlreadyGrouped,group);
						}
					}
					for(final LayoutNode n : group)
						roomsToLayOut.remove(n);
					if(group.size()>0)
						roomGroups.add(group);
					keepLooking=true;
				}
			}
		}

		while(roomsToLayOut.size() >0)
		{
			final LayoutNode node=roomsToLayOut.get(0);
			final Vector<LayoutNode> group=new Vector<LayoutNode>();
			group.add(node);
			nodesAlreadyGrouped.add(node);
			layoutRecursiveFill(node,nodesAlreadyGrouped,group,node.type());
			for(final LayoutNode n : group)
				roomsToLayOut.remove(n);
			roomGroups.add(group);
		}
		
		// make CERTAIN that the magic first room in the layout always
		// gets ID#0.
		List<LayoutNode> magicGroup=null;
		for(int g=0;g<roomGroups.size();g++)
		{
			final List<LayoutNode> group=roomGroups.get(g);
			if(group.contains(magicRoomNode))
			{
				magicGroup=group;
				break;
			}
		}
		if(CMSecurity.isDebugging(CMSecurity.DbgFlag.MUDPERCOLATOR))
		for(int g=0;g<roomGroups.size();g++)
		{
			final Vector<LayoutNode> group=(Vector<LayoutNode>)roomGroups.get(g);
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.MUDPERCOLATOR))
				Log.debugOut("MudPercolator","GROUP:"+A.Name()+": "+group.firstElement().type().toString()+": "+group.size());
		}
		final Map<List<LayoutNode>,Map<String,Object>> groupDefinitions=new Hashtable<List<LayoutNode>,Map<String,Object>>();
		for(final List<LayoutNode> group : roomGroups)
		{
			final Map<String,Object> newDefined=new Hashtable<String,Object>();
			newDefined.putAll(defined);
			groupDefinitions.put(group, newDefined);
		}
		
		Map<String,Object> groupDefined = groupDefinitions.get(magicGroup);
		final Room magicRoom = processRoom(A,direction,piece,magicRoomNode,groupDefined);
		for(Map<String,Object> otherDefineds : groupDefinitions.values())
		{
			otherDefineds.remove("ROOMTAG_NODEGATEEXIT");
			otherDefineds.remove("ROOMTAG_GATEEXITROOM");
		}
		updateLayoutDefinitions(defined,groupDefined,groupDefinitions,roomGroups);

		//now generate the rooms and add them to the area
		for(final List<LayoutNode> group : roomGroups)
		{
			groupDefined = groupDefinitions.get(group);

			for(final LayoutNode node : group)
			{
				if(node!=magicRoomNode)
					processRoom(A,direction,piece,node,groupDefined);
			}
			updateLayoutDefinitions(defined,groupDefined,groupDefinitions,roomGroups);
		}

		for(final Integer linkDir : magicRoomNode.links().keySet())
		{
			final LayoutNode linkNode=magicRoomNode.getLink(linkDir.intValue());
			if((magicRoom.getRawExit(linkDir.intValue())==null) || (linkNode.room() == null))
				Log.errOut("MUDPercolator","Generated an unpaired node for "+magicRoom.roomID());
			else
				magicRoom.rawDoors()[linkDir.intValue()]=linkNode.room();
		}
		
		//now do a final-link on the rooms
		for(final List<LayoutNode> group : roomGroups)
		{
			for(final LayoutNode node : group)
			{
				final Room R=node.room();
				if(node != magicRoomNode)
				{
					if((R==null)||(node.links().keySet().size()==0))
						Log.errOut("MUDPercolator",layoutManager.name()+" generated a linkless node: "+node.toString());
					else
					for(final Integer linkDir : node.links().keySet())
					{
						final LayoutNode linkNode=node.getLink(linkDir.intValue());
						if((R.getRawExit(linkDir.intValue())==null) || (linkNode.room() == null))
							Log.errOut("MUDPercolator","Generated an unpaired node for "+R.roomID());
						else
							R.rawDoors()[linkDir.intValue()]=linkNode.room();
					}
				}
			}
		}
		return magicRoom;
	}