<NME> BuildingSkill.java
<BEF> package com.planet_ink.coffee_mud.Abilities.Common;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.interfaces.ItemPossessor.Expire;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.core.interfaces.CostDef;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/*
   Copyright 2015-2022 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
public class BuildingSkill extends CraftingSkill implements CraftorAbility
{
	@Override
	public String ID()
	{
		return "BuildingSkill";
	}

	private final static String	localizedName	= CMLib.lang().L("BuildingSkill");

	@Override
	public String name()
	{
		return localizedName;
	}

	private static final String[]	triggerStrings	= I(new String[] { "BUILD", "BUILDING" });

	@Override
	public String[] triggerStrings()
	{
		return triggerStrings;
	}

	@Override
	public int classificationCode()
	{
		return Ability.ACODE_COMMON_SKILL | Ability.DOMAIN_BUILDINGSKILL;
	}

	@Override
	public String supportedResourceString()
	{
		return "";
	}

	protected String getMainResourceName()
	{
		return "Wood";
	}

	protected String getDemolishRoom()
	{
		return "Plains";
	}

	protected String getSoundName()
	{
		return "hammer.wav";
	}

	public BuildingSkill()
	{
		super();
	}

	@Override
	public List<List<String>> fetchMyRecipes(final MOB mob)
	{
		return this.addRecipes(mob, loadRecipes());
	}

	protected boolean canBeDoneSittingDown = false;

	@Override
	public boolean canBeDoneSittingDown()
	{
		return canBeDoneSittingDown;
	}

	protected enum Building
	{
		WALL,
		DOOR,
		ROOM,
		ITEM,
		DEMOLISH,
		TITLE,
		DESC,
		STAIRS,
		EXCAVATE,
		ROOMEFFECT,
		EXITEFFECT,
		DELEFFECT
	}

	protected enum Flag
	{
		DIR,
		DIRUPDOWN,
		NOWALL,
		INDOOR,
		OUTDOOR,
		CAVEONLY,
		NODOWN,
		DOWNONLY,
		WATERONLY,
		WATERSURFACEONLY,
		UNDERWATERONLY,
		SALTWATER,
		FRESHWATER,
		UPONLY
	}

	protected Room		room				= null;
	protected int		dir					= -1;
	protected String[]	recipe				= null;
	protected int		poundsOfMatsUsed	= 0;
	protected String	designTitle			= "";
	protected String	designDescription	= "";

	//protected static final int	RCP_FINALNAME		= 0;
	//protected static final int	RCP_LEVEL			= 1;
	protected static final int	RCP_TICKS		= 2;
	protected final static int	DAT_WOOD		= 3;
	protected final static int	DAT_WOODTYPE	= 4;
	protected final static int	DAT_FLAG		= 5;
	protected final static int	DAT_BUILDCODE	= 6;
	protected final static int	DAT_CLASS		= 7;
	protected final static int	DAT_MISC		= 8;
	protected final static int	DAT_PROPERTIES	= 9;
	protected final static int	DAT_DESC		= 10;
	protected final static int	DAT_BUILDERMASK	= 11;
	protected final static int	DAT_DESCRIPTION	= 12;

	@Override
	public String getRecipeFormat()
	{
		if(Resources.getResource("BUILDING_SKILL_CODES_FLAGS")==null)
		{
			final String[] codes = CMParms.toStringArray(Building.values());
			final String[] flags = CMParms.toStringArray(Flag.values());
			final Pair<String[],String[]> codesFlags = new Pair<String[],String[]>(codes, flags);
			Resources.submitResource("BUILDING_SKILL_CODES_FLAGS", codesFlags);
		}
		return"ITEM_NAME\tITEM_LEVEL\tBUILD_TIME_TICKS\tMATERIALS_REQUIRED\tOPTIONAL_BUILDING_RESOURCE_OR_MATERIAL\t"
			+ "BUILDING_FLAGS\tBUILDING_CODE\tROOM_CLASS_ID||EXIT_CLASS_ID||ALLITEM_CLASS_ID||ROOM_CLASS_ID_OR_NONE\t"
			+ "BUILDING_GRID_SIZE||EXIT_NAMES||STAIRS_DESC\tPCODED_SPELL_LIST\tBUILDING_NOUN\tBUILDER_MASK\tBUILDER_DESC";
	}

	@Override
	public String getRecipeFilename()
	{
		return "";
	}

	@Override
	public String getDecodedComponentsDescription(final MOB mob, final List<String> recipe)
	{
		return null;
	}

	@Override
	protected CostDef getRawTrainingCost()
	{
		return CMProps.getNormalSkillGainCost(ID());
	}

	protected boolean canDescTitleHere(final Room R)
	{
		return true;
	}

	@Override
	public void unInvoke()
	{
		if(canBeUninvoked() && (!super.unInvoked))
		{
			if((affected instanceof MOB)&&(!helping))
			{
				final MOB mob=(MOB)affected;
				if(!aborted)
				{
					if((messedUp)&&(room!=null))
					{
						notifyMessUp(mob, recipe);
					}
					else
					{
						this.buildComplete(mob, recipe, room, dir, designTitle, designDescription);
						if(poundsOfMatsUsed>0)
						{
							final CMMsg msg=CMClass.getMsg(mob,room,this,CMMsg.TYP_ITEMGENERATED|CMMsg.MASK_ALWAYS,null);
							msg.setValue(poundsOfMatsUsed/2);
							if(mob.location().okMessage(mob,msg))
								mob.location().send(mob,msg);
						}
					}
				}
			}
		}
		super.unInvoke();
	}

	protected int[][] getBasicMaterials(final MOB mob, final int woodRequired, String miscType)
	{
		if(miscType.length()==0)
			miscType="rock";
		final int[][] idata=fetchFoundResourceData(mob,
													woodRequired,miscType,null,
													0,null,null,
													false,
													0,null);
		return idata;
	}

	public String[][] getRecipeData(final MOB mob)
	{
		final List<List<String>> recipeData = addRecipes(mob,loadRecipes(getRecipeFilename()));
		final String[][] finalDat = new String[recipeData.size()][];
		for(int i=0;i<recipeData.size();i++)
			finalDat[i] = recipeData.get(i).toArray(new String[recipeData.get(i).size()]);
		return finalDat;
	}

	public Exit generify(final Exit X)
	{
		final Exit E2=CMClass.getExit("GenExit");
		E2.setName(X.name());
		E2.setDisplayText(X.displayText());
		E2.setDescription(X.description());
		E2.setDoorsNLocks(X.hasADoor(),X.isOpen(),X.defaultsClosed(),X.hasALock(),X.isLocked(),X.defaultsLocked());
		E2.setBasePhyStats((PhyStats)X.basePhyStats().copyOf());
		E2.setExitParams(X.doorName(),X.closeWord(),X.openWord(),X.closedText());
		E2.setKeyName(X.keyName());
		E2.setOpenDelayTicks(X.openDelayTicks());
		E2.setReadable(X.isReadable());
		E2.setReadableText(X.readableText());
		E2.setTemporaryDoorLink(X.temporaryDoorLink());
		E2.recoverPhyStats();
		return E2;
	}

	protected void notifyMessUp(final MOB mob, final String[] recipe)
	{
		commonTell(mob,L("You've ruined the "+recipe[DAT_DESC]+"!",CMLib.directions().getDirectionName(dir)));
	}

	protected void demolishRoom(final MOB mob, final Room room)
	{
		final LandTitle title=CMLib.law().getLandTitle(room);
		if(title==null)
			return;
		Room returnToRoom=null;
		Room backupToRoom1=null;
		Room backupToRoom2=null;
		for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
		{
			final Room R=room.getRoomInDir(d);
			if(CMLib.law().doesOwnThisLand(mob, R)||(CMSecurity.isAllowed(mob, R, CMSecurity.SecFlag.CMDROOMS)))
			{
				returnToRoom=R;
				break;
			}
			final LandTitle adjacentTitle=CMLib.law().getLandTitle(R);
			if((adjacentTitle==null)||(adjacentTitle.getOwnerName().length()>0))
				backupToRoom1=R;
			else
			if(R.roomID().length()>0)
				backupToRoom2=R;
		}
		if(returnToRoom==null)
			returnToRoom=backupToRoom1;
		if(returnToRoom==null)
			returnToRoom=backupToRoom2;
		if(returnToRoom==null)
			returnToRoom=mob.getStartRoom();
		if(returnToRoom==null)
			returnToRoom=room.getArea().getRandomProperRoom();
		if(returnToRoom==null)
			returnToRoom=room.getArea().getRandomMetroRoom();
		if(returnToRoom==null)
			returnToRoom=CMLib.map().getRandomRoom();
		final Room theRoomToReturnTo=returnToRoom;
		room.eachInhabitant(new EachApplicable<MOB>()
		{
			@Override
			public void apply(final MOB a)
			{
				theRoomToReturnTo.bringMobHere(a, false);
			}
		});
		room.eachItem(new EachApplicable<Item>()
		{
			@Override
			public void apply(final Item a)
			{
				theRoomToReturnTo.addItem(a,Expire.Player_Drop);
			}
		});
		title.setOwnerName("");
		title.updateLot(null); // this is neat -- this will obliterate leaf rooms around this one.
		if((theRoomToReturnTo!=null)
		&&(theRoomToReturnTo.rawDoors()[Directions.UP]==room)
		&&(theRoomToReturnTo.getRawExit(Directions.UP)!=null))
		{
			theRoomToReturnTo.getRawExit(Directions.UP).destroy();
			theRoomToReturnTo.setRawExit(Directions.UP, null);
		}
		CMLib.map().obliterateMapRoom(room);
	}

	private void removeEffects(final PhysicalAgent E, String extraProp)
	{
		extraProp=extraProp.trim();
		if(extraProp.length()>0)
		{
			final List<String> spells = CMParms.parseAny(extraProp, ")", true);
			for(String spellName : spells)
			{
				final int x=spellName.indexOf('(');
				if(x>0)
					spellName=spellName.substring(0,x);
				final Ability A=E.fetchEffect(spellName);
				if(A!=null)
				{
					A.unInvoke();
					E.delEffect(A);
				}
				else
				{
					final Behavior B=E.fetchBehavior(spellName);
					if(B!=null)
						E.delBehavior(B);
				}
			}
		}
	}

	private void addEffects(final PhysicalAgent E, final Room R2, String extraProp)
	{
		extraProp=extraProp.trim();
		if(extraProp.length()>0)
		{
			final List<String> spells = CMParms.parseAny(extraProp, ")", true);
			for(String spellName : spells)
			{
				String parms="";
				final int x=spellName.indexOf('(');
				if(x>0)
				{
					parms=spellName.substring(x+1);
					spellName=spellName.substring(0,x);
				}
				if(parms.trim().equalsIgnoreCase("@remove"))
				{
					final Ability A=E.fetchEffect(spellName);
					if(A!=null)
					{
						A.unInvoke();
						E.delEffect(A);
					}
					else
					{
						final Behavior B=E.fetchBehavior(spellName);
						if(B!=null)
							E.delBehavior(B);
					}
					continue;
				}
				final Ability A=CMClass.getAbility(spellName);
				if(A!=null)
				{

					if(parms.length()>0)
						A.setMiscText(parms);
					else
					if(A.ID().equals("Prop_RoomView"))
						A.setMiscText(CMLib.map().getExtendedRoomID(R2));
					E.addNonUninvokableEffect(A);
				}
				else
				{
					final Behavior B=CMClass.getBehavior(spellName);
					if(parms.length()>0)
						B.setParms(parms);
					E.addBehavior(B);
				}
			}
		}
	}

	protected Room buildRoomAbility(Room R, final int dir, String extraProp)
	{
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			R=CMLib.map().getRoom(R);
			extraProp=CMStrings.replaceAll(extraProp, "@dir", CMLib.directions().getDirectionName(dir));
			addEffects(R,R,extraProp);
		}
		CMLib.database().DBUpdateRoom(R);
		return R;
	}

	protected Exit buildExitAbility(Room R, final int dir, final String extraProp)
	{
		Exit E=null;
		synchronized(CMClass.getSync("SYNC"+R.roomID()))
		{
			R=CMLib.map().getRoom(R);
			E=R.getExitInDir(dir);
			addEffects(E,R,extraProp);
		}
		CMLib.database().DBUpdateExits(R);
		return E;
	}

	protected Room removeRoomAbility(Room R, final int dir, String extraProp)
	{
		synchronized(CMClass.getSync("SYNC"+R.roomID()))
		{
			R=CMLib.map().getRoom(R);
			extraProp=CMStrings.replaceAll(extraProp, "@dir", CMLib.directions().getDirectionName(dir));
			removeEffects(R,extraProp);
		}
		CMLib.database().DBUpdateRoom(R);
		return R;
	}

	protected Exit removeExitAbility(Room R, final int dir, final String extraProp)
	{
		Exit E=null;
		synchronized(CMClass.getSync("SYNC"+R.roomID()))
		{
			R=CMLib.map().getRoom(R);
			E=R.getExitInDir(dir);
			removeEffects(E,extraProp);
		}
		CMLib.database().DBUpdateExits(R);
		return E;
	}

	protected Room buildNewRoomType(Room room, String newLocale, final String extraProp, int dimension)
	{
		Room R=null;
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			if((dimension == 0)&&(room instanceof GridLocale))
				dimension = ((GridLocale)room).xGridSize();
			room=CMLib.map().getRoom(room);
			if(newLocale.equalsIgnoreCase("IndoorWaterSurface")
			&&((room.domainType()&Room.INDOORS)==0))
				newLocale="WaterSurface";
			R=CMClass.getLocale(newLocale);
			R.setRoomID(room.roomID());
			R.setDisplayText(room.displayText());
			R.setDescription(room.description());
			if(R.image().equalsIgnoreCase(CMLib.protocol().getDefaultMXPImage(room)))
				R.setImage(null);
			if(R instanceof GridLocale)
			{
				((GridLocale)R).setXGridSize(dimension);
				((GridLocale)R).setYGridSize(dimension);
			}

			final Area area=room.getArea();
			if(area!=null)
				area.delProperRoom(room);
			R.setArea(area);
			for(int a=room.numEffects()-1;a>=0;a--)
			{
				final Ability A=room.fetchEffect(a);
				if(A!=null)
				{
					room.delEffect(A);
					R.addEffect(A);
				}
			}
			for(int i=room.numItems()-1;i>=0;i--)
			{
				final Item I=room.getItem(i);
				if(I!=null)
				{
					room.delItem(I);
					R.addItem(I);
				}
			}
			for(int m=room.numInhabitants()-1;m>=0;m--)
			{
				final MOB M=room.fetchInhabitant(m);
				if(M!=null)
				{
					room.delInhabitant(M);
					R.addInhabitant(M);
					M.setLocation(R);
				}
			}
			room.clearSky();
			if(((room.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)||(room.domainType()==Room.DOMAIN_OUTDOORS_WATERSURFACE))
			&&(R.domainType()!=Room.DOMAIN_INDOORS_WATERSURFACE)
			&&(R.domainType()!=Room.DOMAIN_OUTDOORS_WATERSURFACE))
			{
				final Room waterR=room.getRoomInDir(Directions.DOWN);
				if((waterR!=null)
				&&((waterR.domainType()==Room.DOMAIN_INDOORS_UNDERWATER)||(waterR.domainType()==Room.DOMAIN_OUTDOORS_UNDERWATER))
				&&(waterR.roomID().length()>0))
				{
					final LandTitle title=CMLib.law().getLandTitle(waterR);
					if(title!=null)
					{
						title.setOwnerName("");
						title.updateLot(null);
					}
					room.setRawExit(Directions.DOWN, null);
					room.rawDoors()[Directions.DOWN]=null;
					waterR.setRawExit(Directions.UP, null);
					waterR.rawDoors()[Directions.UP]=null;
					CMLib.map().obliterateMapRoom(waterR);
				}
			}
			CMLib.threads().deleteTick(room,-1);
			for(int d=0;d<R.rawDoors().length;d++)
			{
				if((R.rawDoors()[d]==null)
				||(R.rawDoors()[d].roomID().length()>0))
					R.rawDoors()[d]=room.rawDoors()[d];
			}
			for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)
			{
				if((R.rawDoors()[d]==null)
				||(R.rawDoors()[d].roomID().length()>0))
				{
					if(room.getRawExit(d)!=null)
						R.setRawExit(d, (Exit)room.getRawExit(d).copyOf());
				}
			}
			final LandTitle title = CMLib.law().getLandTitle(room);
			if((title!=null)&&(title.gridLayout()))
			{
				final PairVector<Room,int[]> rooms=CMLib.tracking().buildGridList(R, title.getOwnerName(), 100);
				for(int dir=0;dir<Directions.NUM_DIRECTIONS();dir++)
				{
					if(dir==Directions.GATE)
						continue;
					Room R3=R.getRoomInDir(dir);
					if(R3 == null)
					{
						R3=CMLib.tracking().getCalculatedAdjacentRoom(rooms, R3, dir);
						if(R3!=null)
						{
							R.rawDoors()[dir]=R3;
							R3.rawDoors()[Directions.getOpDirectionCode(dir)]=R;
							CMLib.database().DBUpdateExits(R3);
						}
					}
				}
			}

			R.clearSky();
			R.startItemRejuv();
			try
			{
				boolean rebuild=false;
				for(final Enumeration<Room> r=CMLib.map().rooms();r.hasMoreElements();)
				{
					final Room R2=r.nextElement();
					rebuild=false;
					for(int d=0;d<R2.rawDoors().length;d++)
					{
						if(R2.rawDoors()[d]==room)
						{
							rebuild=true;
							R2.rawDoors()[d]=R;
						}
					}
					if((rebuild)&&(R2 instanceof GridLocale))
						((GridLocale)R2).buildGrid();
				}
			}
			catch (final NoSuchElementException e)
			{
			}
			try
			{
				for(final Enumeration<MOB> e=CMLib.players().players();e.hasMoreElements();)
				{
					final MOB M=e.nextElement();
					if(M.getStartRoom()==room)
						M.setStartRoom(R);
					else
					if(M.location()==room)
						M.setLocation(R);
				}
			}
			catch (final NoSuchElementException e)
			{
			}
			if((R.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)
			||(R.domainType()==Room.DOMAIN_OUTDOORS_WATERSURFACE))
			{
				if(dimension > 0)
				{
					final Room R2=CMClass.getLocale("UnderWater");
					R2.setRoomID(R.getArea().getNewRoomID(R,Directions.DOWN));
					R2.setDisplayText(L("Under the water"));
					R2.setDescription(L("You are swimming around under the water."));
					R2.setArea(R.getArea());
					R2.rawDoors()[Directions.UP]=R;
					R2.setRawExit(Directions.UP,CMClass.getExit("Open"));
					R.clearSky();
					R.rawDoors()[Directions.DOWN]=R2;
					R.setRawExit(Directions.DOWN,CMClass.getExit("Open"));
					final LandTitle titleA=CMLib.law().getLandTitle(R);
					if((titleA!=null)&&(CMLib.law().getLandTitle(R2)==null))
					{
						final LandTitle A2=(LandTitle)titleA.copyOf();
						R2.addNonUninvokableEffect((Ability)A2);
					}
					final Ability capacityA = R.fetchEffect("Prop_ReqCapacity");
					if(capacityA != null)
					{
						final Ability A2=(Ability)capacityA.copyOf();
						R2.addNonUninvokableEffect(A2);
					}
					if(CMSecurity.isDebugging(CMSecurity.DbgFlag.PROPERTY))
						Log.debugOut(ID(),R2.roomID()+" created for water.");
					CMLib.database().DBCreateRoom(R2);
					CMLib.database().DBUpdateExits(R2);
				}
			}

			R.getArea().fillInAreaRoom(R);
			addEffects(R,R,extraProp);
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.PROPERTY))
				Log.debugOut(ID(),R.roomID()+" updated.");
			CMLib.database().DBUpdateRoom(R);
			CMLib.database().DBUpdateExits(R);
			final MOB mob=CMClass.getFactoryMOB("the wind",1,R);
			try
			{
				R.executeMsg(mob, CMClass.getMsg(mob, R, CMMsg.MSG_NEWROOM, null));
			}
			finally
			{
				mob.destroy();
			}
			room.destroy();
			R.clearSky();
			R.giveASky(0);
		}
		return R;
	}

	protected void buildDoor(final String[] recipe, Room room, final int dir, final int recipeLevel)
	{
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			final String localeName = recipe[DAT_CLASS];
			String doorName = recipe[DAT_MISC];
			final String spells = recipe[DAT_PROPERTIES];
			//int size = CMath.s_int(recipe[DAT_MISC]);
			String closeWord=null;
			String openWord=null;
			String closedWord=null;
			String displayText="";
			String description="";
			if(doorName.indexOf("|")>0)
			{
				final List<String> split=CMParms.parseAny(doorName, '|',false);
				if(split.get(0).length()>0)
					doorName=split.get(0);
				if((split.size()>1)&&(split.get(1).length()>0))
					openWord=split.get(1);
				if((split.size()>2)&&(split.get(2).length()>0))
					closeWord=split.get(2);
				if((split.size()>3)&&(split.get(3).length()>0))
					closedWord=split.get(3);
				if((split.size()>4)&&(split.get(4).length()>0))
					displayText=split.get(4);
				if((split.size()>5)&&(split.get(5).length()>0))
					description=split.get(5);
			}
			if(closeWord == null)
				closeWord="close";
			if(openWord == null)
				openWord="open";
			if(closedWord == null)
				closedWord=CMLib.english().startWithAorAn("closed "+doorName);
			room=CMLib.map().getRoom(room);
			final Exit X=CMClass.getExit(localeName);
			X.setName(CMLib.english().startWithAorAn(doorName));
			X.setDescription(description);
			X.setDisplayText(displayText);
			X.setOpenDelayTicks(9999);
			X.setExitParams(doorName,closeWord,openWord,closedWord);
			String desc = recipe[DAT_MISC];
			String addParms = recipe[DAT_PROPERTIES];
			String localeClass = recipe[DAT_CLASS];
			
			int opDir = Directions.getOpDirectionCode(dir);
			room=CMLib.map().getRoom(room);
			{
				final Room oroom = room.rawDoors()[dir].prepareRoomInDir(room, dir);
				final Exit X2=(Exit)X.copyOf();
				X2.recoverPhyStats();
				X2.text();
				oroom.setRawExit(Directions.getOpDirectionCode(dir),X2);
				CMLib.database().DBUpdateExits(oroom);
			}
			CMLib.database().DBUpdateExits(room);
		}
	}

	protected int findFloorNumber(final Room room, final Set<Room> done, final int floor)
	{
		final LandTitle title = CMLib.law().getLandTitle(room);
		if(title == null)
			return floor;
		for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
		{
			final Room R=room.getRoomInDir(d);
			if((R!=null)&&(!done.contains(R)))
			{
				done.add(R);
				if(d==Directions.UP)
				{
					final int f=findFloorNumber(R,done,floor-1);
					if(f != Integer.MIN_VALUE)
						return f;
				}
				else
				if(d==Directions.DOWN)
				{
					final int f=findFloorNumber(R,done,floor+1);
					if(f != Integer.MIN_VALUE)
						return f;
				}
				else
				{
					final int f=findFloorNumber(R,done,floor);
					if(f != Integer.MIN_VALUE)
						return f;
				}
			}
		}
		return Integer.MIN_VALUE;
	}

	protected Room buildStairs(final MOB mob, Room room, final int dir, final String[] recipe)
	{
		Room newRoom;
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			String desc = recipe[DAT_MISC];
			final String addParms = recipe[DAT_PROPERTIES];
			final String localeClass = recipe[DAT_CLASS];
			if(desc.equals("0"))
				desc="";

			final int opDir = Directions.getOpDirectionCode(dir);
			room=CMLib.map().getRoom(room);
			final int floor=findFloorNumber(room, new HashSet<Room>(), 1);
			if(localeClass.length()==0)
				newRoom=CMClass.getLocale(CMClass.classID(room));
			else
				newRoom=CMClass.getLocale(localeClass);
			newRoom.setRoomID(room.getArea().getNewRoomID(room,dir));
			if(newRoom.roomID().length()==0)
			{
				final String verbDesc = recipe[DAT_DESC];
				commonTell(mob,L("You've failed to build the "+verbDesc+"!",CMLib.directions().getDirectionName(dir)));
				return null;
			}
			newRoom.setArea(room.getArea());
			LandTitle newTitle=CMLib.law().getLandTitle(room);
			if((newTitle!=null)&&(CMLib.law().getLandTitle(newRoom)==null))
			{
				final List<Room> allRooms = newTitle.getConnectedPropertyRooms();
				if(allRooms.size()>0)
				{
					final Ability cap = allRooms.get(0).fetchEffect("Prop_ReqCapacity");
					if(cap != null)
					{
						newRoom.addNonUninvokableEffect((Ability)cap.copyOf());
					}
				}
				newTitle = newTitle.generateNextRoomTitle();
				newTitle.setLandPropertyID(newRoom.roomID());
				newRoom.addNonUninvokableEffect((Ability)newTitle);
			}

			int newFloorNum;
			int curFloorNum;
			if(dir == Directions.DOWN)
			{
				newFloorNum = floor;
				curFloorNum = (floor - 1);
			}
			else
			{
				newFloorNum = (floor+1);
				curFloorNum = floor;
			}

			final Exit newExit;
			final Exit returnExit;

			if(((dir == Directions.UP)||(dir == Directions.DOWN))&&(newFloorNum > 0))
			{
				newExit=CMClass.getExit("GenExit");
				newExit.setName(L("a passageway"));
				newExit.setDescription(L(desc,CMLib.directions().getDirectionName(dir),""+newFloorNum+CMath.numAppendage(newFloorNum)));
				newExit.setDisplayText(L(desc,CMLib.directions().getDirectionName(dir),""+newFloorNum+CMath.numAppendage(newFloorNum)));
				addEffects(newExit, room.getRoomInDir(dir),addParms);
				newExit.recoverPhyStats();
				newExit.text();

				returnExit=CMClass.getExit("GenExit");
				returnExit.setName(L("a passageway"));
				returnExit.setDescription(L(desc,CMLib.directions().getDirectionName(opDir),""+curFloorNum+CMath.numAppendage(curFloorNum)));
				returnExit.setDisplayText(L(desc,CMLib.directions().getDirectionName(opDir),""+curFloorNum+CMath.numAppendage(curFloorNum)));
				addEffects(returnExit, room.getRoomInDir(dir),addParms);
				returnExit.recoverPhyStats();
				returnExit.text();
			}
			else
			{
				newExit=CMClass.getExit("GenExit");
				newExit.setName(L("a passageway"));
				addEffects(newExit, room.getRoomInDir(dir),addParms);
				newExit.recoverPhyStats();
				newExit.text();

				returnExit=CMClass.getExit("GenExit");
				returnExit.setName(L("a passageway"));
				addEffects(returnExit, room.getRoomInDir(dir),addParms);
				returnExit.recoverPhyStats();
				returnExit.text();
			}
			room.rawDoors()[dir]=newRoom;
			room.setRawExit(dir,newExit);

			newRoom.rawDoors()[opDir]=room;
			newRoom.setRawExit(opDir,returnExit);

			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.PROPERTY))
				Log.debugOut(ID(),newRoom.roomID()+" created and put up for sale.");
			CMLib.database().DBCreateRoom(newRoom);
			if(newTitle!=null)
			{
				if(newTitle.gridLayout())
				{
					final PairVector<Room,int[]> rooms=CMLib.tracking().buildGridList(newRoom, newTitle.getOwnerName(), 100);
					for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
					{
						if(d==Directions.GATE)
							continue;
						Room R3=newRoom.getRoomInDir(d);
						if(R3 == null)
						{
							R3=CMLib.tracking().getCalculatedAdjacentRoom(rooms, R3, d);
							if(R3!=null)
							{
								newRoom.rawDoors()[d]=R3;
								if(R3.rawDoors()[Directions.getOpDirectionCode(d)]==null)
									R3.rawDoors()[Directions.getOpDirectionCode(d)]=newRoom;
							}
						}
					}
				}
				CMLib.law().colorRoomForSale(newRoom, newTitle, true);
				newTitle.updateLot(null);
			}
			newRoom.getArea().fillInAreaRoom(newRoom);
			CMLib.database().DBUpdateExits(newRoom);
			CMLib.database().DBUpdateExits(room);
			newRoom.executeMsg(mob, CMClass.getMsg(mob, newRoom, CMMsg.MSG_NEWROOM, null));
		}
		return newRoom;
	}

	protected void buildWall(Room room, final int dir)
	{
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			room=CMLib.map().getRoom(room);
			room.setRawExit(dir,null);
			if(room.rawDoors()[dir]!=null)
			{
				final Room oroom = room.rawDoors()[dir].prepareRoomInDir(room, dir);
				oroom.setRawExit(Directions.getOpDirectionCode(dir),null);
				CMLib.database().DBUpdateExits(oroom);
			}
			CMLib.database().DBUpdateExits(room);
			final LandTitle title=CMLib.law().getLandTitle(room);
			if(title != null)
				title.updateLot(null);
		}
	}

	protected void buildTitle(Room room, final String designTitle)
	{
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			room=CMLib.map().getRoom(room);
			Room returnRoom = null;
			for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
			{
				if(room.getRoomInDir(d)!=null)
					returnRoom=room.getRoomInDir(d);
			}
			room.setDisplayText(designTitle);
			CMLib.database().DBUpdateRoom(room);
			if(room instanceof GridLocale)
			{
				((GridLocale)room).clearGrid(returnRoom);
				((GridLocale)room).buildGrid();
			}
			final LandTitle T=CMLib.law().getLandTitle(room);
			if(T != null)
				T.updateLot(null);
		}
	}

	protected void buildDesc(Room room, final int dir, final String designDescription)
	{
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			room=CMLib.map().getRoom(room);
			if(room == null)
				return;
			if(dir>=0)
			{
				Exit E=room.getExitInDir(dir);
				if(E!=null)
				{
					if((!E.isGeneric())&&(room.getRawExit(dir)==E))
					{
						E=generify(E);
						room.setRawExit(dir,E);
					}
					E.setDescription(designDescription);
					CMLib.database().DBUpdateExits(room);
				}
			}
			else
			{
				Room returnRoom = null;
				for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
				{
					if(room.getRoomInDir(d)!=null)
						returnRoom=room.getRoomInDir(d);
				}
				room.setDescription(designDescription);
				if(room instanceof GridLocale)
				{
					((GridLocale)room).clearGrid(returnRoom);
					((GridLocale)room).buildGrid();
				}
				CMLib.database().DBUpdateRoom(room);
				final LandTitle T=CMLib.law().getLandTitle(room);
				if(T != null)
					T.updateLot(null);
			}
		}
	}

	protected void demolish(final MOB mob, Room room, final int dir, final String[] recipe)
	{
		synchronized(CMClass.getSync("SYNC"+room.roomID()))
		{
			room=CMLib.map().getRoom(room);
			if(dir<0)
			{

				if((CMLib.law().isHomeRoomUpstairs(room))
				||(recipe[DAT_CLASS].trim().length()==0))
				{
					demolishRoom(mob,room);
				}
				else
					convertToPlains(room,recipe[DAT_CLASS]);
			}
			else
			{
				room.setRawExit(dir,CMClass.getExit("Open"));
				if(room.rawDoors()[dir]!=null)
				{
					room.rawDoors()[dir].setRawExit(Directions.getOpDirectionCode(dir),CMClass.getExit("Open"));
					CMLib.database().DBUpdateExits(room.rawDoors()[dir]);
				}
				CMLib.database().DBUpdateExits(room);
			}
		}
	}

	protected void buildComplete(final MOB mob, final String[] recipe, final Room room, final int dir, final String designTitle, final String designDescription)
	{
		final Building doingCode = Building.valueOf(recipe[DAT_BUILDCODE]);
		switch(doingCode)
		{
		case DEMOLISH:
		{
			this.demolish(mob, room, dir, recipe);
			break;
		}
		case DESC:
		{
			this.buildDesc(room, dir, designDescription);
			break;
		}
		case DOOR:
		{
			this.buildDoor(recipe, room, dir, CMath.s_int(recipe[RCP_LEVEL]));
			break;
		}
		case ITEM:
		{
			final Item I=CMClass.getItem(recipe[DAT_CLASS]);
			room.addItem(I);
			I.setExpirationDate(0);
			break;
		}
		case ROOM:
		{
			final String localeName = recipe[DAT_CLASS];
			final String spells = recipe[DAT_PROPERTIES];
			final int size = CMath.s_int(recipe[DAT_MISC]);
			this.buildNewRoomType(room, localeName, spells, size);
			break;
		}
		case DELEFFECT:
		{
			final String spells = recipe[DAT_PROPERTIES];
			if(dir >=0)
				this.removeExitAbility(room, dir, spells);
			else
				this.removeRoomAbility(room, dir, spells);
			break;
		}
		case ROOMEFFECT:
		{
			final String spells = recipe[DAT_PROPERTIES];
			this.buildRoomAbility(room, dir, spells);
			break;
		}
		case EXITEFFECT:
		{
			final String spells = recipe[DAT_PROPERTIES];
			this.buildExitAbility(room, dir, spells);
			break;
		}
		case EXCAVATE:
		case STAIRS:
		{
			this.buildStairs(mob, room, dir, recipe);
			break;
		}
		case TITLE:
		{
			this.buildTitle(room, designTitle);
			break;
		}
		case WALL:
		{
			this.buildWall(room, dir);
			break;
		}
		}
	}

	protected Room convertToPlains(final Room room, final String localeID)
	{
		final Room R=CMClass.getLocale(localeID);
		R.setRoomID(room.roomID());
		R.setDisplayText(room.displayText());
		R.setDescription(room.description());
		final Area area=room.getArea();
		if(area!=null)
			area.delProperRoom(room);
		R.setArea(room.getArea());
		for(int a=room.numEffects()-1;a>=0;a--)
		{
			final Ability A=room.fetchEffect(a);
			if((A!=null)
			&&(!A.ID().equalsIgnoreCase("Prop_Crawlspace")))
			{
				room.delEffect(A);
				R.addEffect(A);
			}
		}
		for(int i=room.numItems()-1;i>=0;i--)
		{
			final Item I=room.getItem(i);
			if(I!=null)
			{
				room.delItem(I);
				R.addItem(I);
			}
		}
		for(int m=room.numInhabitants()-1;m>=0;m--)
		{
			final MOB M=room.fetchInhabitant(m);
			if(M!=null)
			{
				room.delInhabitant(M);
				R.addInhabitant(M);
				M.setLocation(R);
			}
		}
		CMLib.threads().deleteTick(room,-1);
		for(int d=0;d<R.rawDoors().length;d++)
			R.rawDoors()[d]=room.rawDoors()[d];
		for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)
			R.setRawExit(d,room.getRawExit(d));
		CMLib.threads().rejuv(R, Tickable.TICKID_ROOM_ITEM_REJUV);
		R.startItemRejuv();
		try
		{
			for(final Enumeration<Room> r=CMLib.map().rooms();r.hasMoreElements();)
			{
				final Room R2=r.nextElement();
				for(int d=0;d<R2.rawDoors().length;d++)
				{
					if(R2.rawDoors()[d]==room)
					{
						R2.rawDoors()[d]=R;
						if(R2 instanceof GridLocale)
							((GridLocale)R2).buildGrid();
					}
				}
			}
		}
		catch (final NoSuchElementException e)
		{
		}
		R.getArea().fillInAreaRoom(R);
		CMLib.database().DBUpdateRoom(R);
		CMLib.database().DBUpdateExits(R);
		room.destroy();
		return R;
	}

	public boolean isHomePeerRoom(final Room R, final boolean anyType)
	{
		return ifHomePeerLandTitle(R,anyType)!=null;
	}

	public boolean isHomePeerTitledRoom(final Room R, final boolean anyType)
	{
		final LandTitle title = ifHomePeerLandTitle(R,anyType);
		if(title == null)
			return false;
		return title.getOwnerName().length()>0;
	}

	public boolean countsAsACave(final Room R)
	{
		if(R==null)
			return false;
		if((R.domainType()==Room.DOMAIN_INDOORS_CAVE)
		||((R.getAtmosphere()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_ROCK))
			return true;
		if(R.domainType()==Room.DOMAIN_INDOORS_CAVE_SEAPORT)
			return true;
		if(R.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)
		{
			for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
				if(countsAsACave(R.getRoomInDir(d)))
					return true;
		}
		return false;
	}

	public LandTitle ifHomePeerLandTitle(final Room R, final boolean anyType)
	{
		if((R!=null)
		&&(R.ID().length()>0)
		&&(anyType||CMath.bset(R.domainType(),Room.INDOORS)))
			return CMLib.law().getLandTitle(R);
		return null;
	}

	public String establishVerb(final MOB mob, final String[] recipe)
	{
		String verb="";
		final Building doingCode = Building.valueOf(recipe[DAT_BUILDCODE]);
		if(doingCode == Building.DEMOLISH)
		{
			if(dir<0)
			{
				final Room R=mob.location();
				boolean roomClassFound = false;
				for(final List<String> recipeChk : loadRecipes(getRecipeFilename()))
				{
					if(R.ID().equalsIgnoreCase(recipeChk.get(DAT_CLASS)))
						roomClassFound=true;
				}
				if((R.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)
				||(R.domainType()==Room.DOMAIN_OUTDOORS_WATERSURFACE))
					verb=L("demolishing the pool");
				else
				if((R.domainType()==Room.DOMAIN_INDOORS_UNDERWATER)
				||(R.domainType()==Room.DOMAIN_OUTDOORS_UNDERWATER))
				{
					commonTell(mob,null,null,L("You must demolish a pool from above."));
					return "";
				}
				else
				if(!roomClassFound)
				{
					commonTell(mob,null,null,L("This building was not made with @x1, you can`t demolish it.",name()));
					return "";
				}
				else
				if(CMLib.law().isHomeRoomUpstairs(R))
					verb=L("demolishing the room");
				else
					verb=L("demolishing the roof");
			}
			else
				verb=L("demolishing the wall @x1",CMLib.directions().getInDirectionName(dir).toLowerCase());
		}
		else
			verb = L("building the "+recipe[DAT_DESC],CMLib.directions().getInDirectionName(dir).toLowerCase());
		return verb;
	}

	private Set<Flag> makeFlags(final String[] recipe)
	{
		final Set<Flag> flags = new HashSet<Flag>();
		final String[] flagStrs = CMParms.parse(recipe[DAT_FLAG].toUpperCase()).toArray(new String[0]);
		for(final String flag : flagStrs)
		{
			final Flag F=(Flag)CMath.s_valueOf(Flag.class, flag);
			if(F!=null)
				flags.add(F);
		}
		return flags;
	}

	@Override
	public boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel)
	{
		if(checkStop(mob, commands))
			return true;

		randomRecipeFix(mob,addRecipes(mob,loadRecipes(getRecipeFilename())),commands,0);

		if(commands.size()==0)
		{
			commonTell(mob,L("What kind of @x1, where? Try @x2 list.",name(),CMStrings.capitalizeAndLower(this.triggerStrings()[0])));
			return false;
		}
		poundsOfMatsUsed	= 0;
		canBeDoneSittingDown = false;
		final String str=commands.get(0);
		final String[][] data=getRecipeData(mob);
		final LandTitle title = CMLib.law().getLandTitle(mob.location());
		final double landValue = ((title == null) ? 0 : title.getPrice()) / 100.0;
		final String landCurrency = CMLib.beanCounter().getCurrency(mob.location());
		final boolean getInfo = ("INFO").startsWith(str.toUpperCase());
		if(("LIST").startsWith(str.toUpperCase())||getInfo)
		{
			boolean hasValueTag = false;
			String mask=CMParms.combine(commands,1);
			if((getInfo && mask.length()==0))
				mask="ALL";
			int colWidth=CMLib.lister().fixColWidth(20,mob.session());
			final StringBuffer buf;
			if(getInfo)
			{
				colWidth=CMLib.lister().fixColWidth(13,mob.session());
				buf=new StringBuffer(CMStrings.padRight(L("Item"),colWidth) + L(" Description\n\r",this.getMainResourceName()));
			}
			else
				buf=new StringBuffer(CMStrings.padRight(L("Item"),colWidth) + L(" @x1 required\n\r",this.getMainResourceName()));
			for(int r=0;r<data.length;r++)
			{
				if(((data[r][DAT_BUILDERMASK].length()==0)
					||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))
					||CMSecurity.isASysOp(mob))
				&&((mask==null)
					||(mask.length()==0)
					||mask.equalsIgnoreCase("all")
					||CMLib.english().containsString(CMStrings.padRight(data[r][RCP_FINALNAME],colWidth),mask)))
				{
					buf.append(CMStrings.padRight(data[r][RCP_FINALNAME],colWidth)+" ");
					if(getInfo)
					{
						if(DAT_DESCRIPTION < data[r].length)
							buf.append(data[r][DAT_DESCRIPTION]).append("\n\r");
					}
					else
					{
						String material=data[r][DAT_WOODTYPE];
						if(material.equalsIgnoreCase("VALUE"))
						{
							hasValueTag=true;
							final String woodStr = data[r][DAT_WOOD];
							if(CMath.isInteger(woodStr))
							{
								int wood=CMath.s_int(woodStr);
								wood=adjustWoodRequired(wood,mob);
								if(title == null)
									buf.append(wood+"% ??\n\r");
								else
									buf.append(CMLib.beanCounter().nameCurrencyLong(landCurrency, landValue * wood)).append("\n\r");
							}
							else
								buf.append("??\n\r");
						}
						else
						if(material.equalsIgnoreCase("MONEY"))
						{
							final String woodStr = data[r][DAT_WOOD];
							if(CMath.isInteger(woodStr))
							{
								int wood=CMath.s_int(woodStr);
								wood=adjustWoodRequired(wood,mob);
								buf.append(CMLib.beanCounter().nameCurrencyLong(landCurrency, wood)).append("\n\r");
							}
							else
								buf.append("??\n\r");
						}
						else
						{
							final String wood=getComponentDescription(mob,Arrays.asList(data[r]),DAT_WOOD);
							if(wood.length()>5)
								material="";
							if(material.equalsIgnoreCase("wooden"))
								material="wood";
							buf.append(wood+" "+material.toLowerCase()+"\n\r");
						}
					}
				}
			}
			if((title == null) && hasValueTag)
				buf.append(L("\n\rYou can't tell anything about some costs from this location.\n\r"));
			commonTell(mob,buf.toString());
			return true;
		}
		else
		if(("SURVEY").startsWith(str.toUpperCase()))
		{
			//TODO: finish this
			//TODO: OK, but what's this do again?
		}

		designTitle="";
		designDescription="";
		String startStr=null;
		int duration=15;
		recipe = null;
		Building doingCode = null;
		dir=-1;

		room=null;
		messedUp=false;

		final String firstWord=commands.get(0);

		helpingAbility=null;

		if(firstWord.equalsIgnoreCase("help"))
		{
			messedUp=!proficiencyCheck(mob,0,auto);
			duration=25;
			commands.remove(0);
			final MOB targetMOB=getTarget(mob,commands,givenTarget,false,true);
			if(targetMOB==null)
				return false;
			if(targetMOB==mob)
			{
				commonTell(mob,L("You can not do that."));
				return false;
			}
			helpingAbility=targetMOB.fetchEffect(ID());
			if(helpingAbility==null)
			{
				commonTell(mob,L("@x1 is not building anything.",targetMOB.Name()));
				return false;
			}
			if(!super.invoke(mob,commands,givenTarget,auto,asLevel))
			{
				helpingAbility=null;
				return false;
			}
			helping=true;
			verb=L("helping @x1 with @x2",targetMOB.name(),helpingAbility.name());
			startStr=L("<S-NAME> start(s) @x1",verb);
			final CMMsg msg=CMClass.getMsg(mob,null,this,getActivityMessageType(),startStr+".");
			if(mob.location().okMessage(mob,msg))
			{
				mob.location().send(mob,msg);
				beneficialAffect(mob,mob,asLevel,duration);
			}
			return true;
		}

		boolean canBuild=CMLib.law().doesOwnThisLand(mob,mob.location()) || CMSecurity.isAllowed(mob, mob.location(), CMSecurity.SecFlag.CMDROOMS);
		final String allWords=CMParms.combine(commands,0).toUpperCase();
		for(int r=0;r<data.length;r++)
		{
			final Building buildCode = Building.valueOf(data[r][DAT_BUILDCODE]);
			if((data[r][0].toUpperCase().equals(allWords))
			&&((data[r][DAT_BUILDERMASK].length()==0)
				||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))
				||CMSecurity.isASysOp(mob)))
			{
				doingCode=buildCode;
				recipe = data[r];
			}
		}
		if((doingCode==null) || (recipe == null))
		{
			for(int r=0;r<data.length;r++)
			{
				final Building buildCode = Building.valueOf(data[r][DAT_BUILDCODE]);
				if((data[r][0].toUpperCase().startsWith(allWords))
				&&((data[r][DAT_BUILDERMASK].length()==0)
					||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))
					||CMSecurity.isASysOp(mob)))
				{
					doingCode=buildCode;
					recipe = data[r];
				}
			}
		}
		if((doingCode==null) || (recipe == null))
		{
			for(int r=0;r<data.length;r++)
			{
				final Building buildCode = Building.valueOf(data[r][DAT_BUILDCODE]);
				if((data[r][0].toUpperCase().startsWith(firstWord.toUpperCase()))
				&&((data[r][DAT_BUILDERMASK].length()==0)
					||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))
					||CMSecurity.isASysOp(mob)))
				{
					doingCode=buildCode;
					recipe = data[r];
				}
			}
		}
		if((doingCode == null)||(recipe == null))
		{
			commonTell(mob,L("'@x1' is not a valid @x2 project.  Try LIST.",firstWord,name()));
			return false;
		}

		final Set<Flag> flags = makeFlags(recipe);

		if((mob.location()!=null) // this is correct!
		&&((mob.location() instanceof Boardable) || (mob.location().getArea() instanceof Boardable)))
		{
			commonTell(mob,L("You may not do @x1 projects here.",name()));
			return false;
		}
		final String dirName=commands.get(commands.size()-1);
		dir=CMLib.directions().getGoodDirectionCode(dirName);

		if((doingCode == Building.DEMOLISH)&&(dirName.equalsIgnoreCase("roof")||(dirName.equalsIgnoreCase("ceiling"))))
		{
			this.canBeDoneSittingDown = true;
			final Room upRoom=mob.location().getRoomInDir(Directions.UP);
			if(isHomePeerRoom(upRoom,flags.contains(Flag.CAVEONLY)))
			{
				commonTell(mob,L("You need to demolish the upstairs rooms first."));
				return false;
			}
			if(flags.contains(Flag.CAVEONLY))
			{
				if((!countsAsACave(mob.location()))&&(!countsAsACave(upRoom)))
				{
					commonTell(mob,L("This can only be done underground."));
					return false;
				}
			}
			if(mob.location().domainType() == Room.DOMAIN_INDOORS_CAVE)
			{
				commonTell(mob,L("A cave can not have its roof demolished."));
				return false;
			}
			if(!CMath.bset(mob.location().domainType(), Room.INDOORS))
			{
				commonTell(mob,L("There is no ceiling here!"));
				return false;
			}
			if(CMLib.law().isHomeRoomUpstairs(mob.location()))
			{
				commonTell(mob,L("You can't demolish a ceiling in an upstairs room.  Try demolishing the room."));
				return false;
			}
			dir=-1;
		}
		else
		if((doingCode == Building.DEMOLISH)&&(dirName.equalsIgnoreCase("room")))
		{
			if(flags.contains(Flag.CAVEONLY))
			{
				if(!countsAsACave(mob.location()))
				{
					commonTell(mob,L("This can only be done underground."));
					return false;
				}
			}
			this.canBeDoneSittingDown = true;
			if((!CMLib.law().doesOwnThisLand(mob, mob.location()))
			&&(!CMSecurity.isAllowed(mob, mob.location(), CMSecurity.SecFlag.CMDROOMS))
			&&(title!=null)
			&&(title.getOwnerName().length()>0))
			{
				commonTell(mob,L("You can't demolish property you don't own."));
				return false;
			}
			if((title==null)||(!title.allowsExpansionConstruction()))
			{
				commonTell(mob,L("You aren't permitted to demolish this room."));
				return false;
			}
			if(!flags.contains(Flag.CAVEONLY))
			{
				if(!CMLib.law().isHomeRoomUpstairs(mob.location()))
				{
					commonTell(mob,L("You can only demolish upstairs/downstairs rooms.  You might try just demolishing the ceiling/roof?"));
					return false;
				}
			}
			int numAdjacentProperties=0;
			for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
			{
				final Room adjacentRoom=mob.location().getRoomInDir(d);
				if(isHomePeerTitledRoom(adjacentRoom,flags.contains(Flag.CAVEONLY)))
				{
					numAdjacentProperties++;
				}
			}
			if(numAdjacentProperties>1)
			{
				mob.tell(L("You can not demolish a room if there is more than one room adjacent to it.  Demolish those first."));
				return false;
			}
			dir=-1;
			canBuild=true;
		}
		else
		if(((dir<0)||(dir==Directions.UP)||(dir==Directions.DOWN))
		&&(flags.contains(Flag.DIR)))
		{
			commonTell(mob,L("A valid direction in which to build must also be specified."));
			return false;
		}
		else
		if((dir<0)
		&&(flags.contains(Flag.DIRUPDOWN)))
		{
			commonTell(mob,L("A valid direction in which to build must also be specified."));
			return false;
		}

		if((flags.contains(Flag.NOWALL))
		&&(dir>=0)
		&&(mob.location().getExitInDir(dir)==null))
		{
			commonTell(mob,L("There is a wall that way that needs to be demolished first."));
			return false;
		}

		int woodRequired=adjustWoodRequired(CMath.s_int(recipe[DAT_WOOD]),mob);
		if(((mob.location().domainType()&Room.INDOORS)==0)
		&&(flags.contains(Flag.INDOOR)))
		{
			commonTell(mob,L("That can only be built after a roof, which includes the frame."));
			return false;
		}
		else
		if(((mob.location().domainType()&Room.INDOORS)>0)
		&&(flags.contains(Flag.OUTDOOR)))
		{
			commonTell(mob,L("That can only be built outdoors!"));
			return false;
		}

		if(doingCode == Building.STAIRS)
		{
			if((dir!=Directions.UP)&&(dir!=Directions.DOWN))
			{
				commonTell(mob,L("A valid direction in which to build must also be specified.  Try UP or DOWN."));
				return false;
			}
		}

		if(doingCode == Building.EXCAVATE)
		{
			if(dir==Directions.DOWN)
			{
				switch(mob.location().domainType())
				{
				case Room.DOMAIN_INDOORS_METAL:
				case Room.DOMAIN_INDOORS_STONE:
				case Room.DOMAIN_INDOORS_WOOD:
				{
					final int floorNumber = this.findFloorNumber(mob.location(), new HashSet<Room>(), 1);
					if(floorNumber > 1)
					{
						commonTell(mob,L("You cannot excavate from above the ground."));
						return false;
					}
					break;
				}
				case Room.DOMAIN_OUTDOORS_AIR:
				case Room.DOMAIN_OUTDOORS_UNDERWATER:
				case Room.DOMAIN_OUTDOORS_WATERSURFACE:
				case Room.DOMAIN_INDOORS_AIR:
				case Room.DOMAIN_INDOORS_UNDERWATER:
				case Room.DOMAIN_INDOORS_WATERSURFACE:
					commonTell(mob,L("You can only excavate down into the ground."));
					return false;
				}
				flags.remove(Flag.CAVEONLY);  // caveonly only matters if complex DOWN rules don't apply.
			}
		}

		if((doingCode == Building.STAIRS)||(doingCode == Building.EXCAVATE))
		{
			if((title==null)||(!title.allowsExpansionConstruction()))
			{
				commonTell(mob,L("The title here does not permit the building of new places."));
				return false;
			}
			if((!CMath.bset(mob.location().domainType(), Room.INDOORS))&&(dir==Directions.UP))
			{
				commonTell(mob,L("You need to build a ceiling (or roof) first!"));
				return false;
			}
			final Room inR=mob.location().getRoomInDir(dir);
			if(inR!=null)
			{
				if(dir == Directions.UP)
					commonTell(mob,L("There is already something up here."));
				else
				if(dir == Directions.DOWN)
					commonTell(mob,L("There is already something down here."));
				else
					commonTell(mob,L("There is already something over there."));
				return false;
			}

			final List<Room> allRooms = title.getConnectedPropertyRooms();
			if(allRooms.size()>0)
			{
				final Ability cap = allRooms.get(0).fetchEffect("Prop_ReqCapacity");
				if(cap != null)
				{
					final int roomLimit = CMParms.getParmInt(cap.text(),"rooms",Integer.MAX_VALUE);
					if(allRooms.size() >= roomLimit)
					{
						commonTell(mob,L("You are not allowed to add more rooms."));
						return false;
					}
				}
			}
		}

		if(doingCode == Building.WALL)
		{
			final Room nextRoom=mob.location().getRoomInDir(dir);
			if((nextRoom!=null)&&(CMLib.law().getLandTitle(nextRoom)==null))
			{
				commonTell(mob,L("You can not build a wall blocking off the main entrance!"));
				return false;
			}
			if(mob.location().getExitInDir(dir)==null)
			{
				commonTell(mob,L("There is already a wall in that direction!"));
				return false;
			}
		}

		if(flags.contains(Flag.NODOWN))
		{
			final Room nextRoom=mob.location().getRoomInDir(Directions.DOWN);
			final Exit exitRoom=mob.location().getExitInDir(Directions.DOWN);
			if((nextRoom!=null)||(exitRoom!=null))
			{
				commonTell(mob,L("You may not build that here!"));
				return false;
			}
		}

		if(flags.contains(Flag.DOWNONLY))
		{
			final Room nextRoom=mob.location().getRoomInDir(Directions.DOWN);
			final Exit exitRoom=mob.location().getExitInDir(Directions.DOWN);
			if((nextRoom!=null)&&(exitRoom!=null)&&(nextRoom.roomID().length()>0))
			{
				commonTell(mob,L("You may not build that here!"));
				return false;
			}
			dir=Directions.DOWN;
		}

		if(flags.contains(Flag.UPONLY))
		{
			final Room nextRoom=mob.location().getRoomInDir(Directions.UP);
			final Exit exitRoom=mob.location().getExitInDir(Directions.UP);
			if((nextRoom!=null)&&(exitRoom!=null)&&(nextRoom.roomID().length()>0))
			{
				commonTell(mob,L("You may not build that here!"));
				return false;
			}
			dir=Directions.UP;
		}

		if(flags.contains(Flag.CAVEONLY))
		{
			if(!countsAsACave(mob.location()))
			{
				commonTell(mob,L("This can only be done underground."));
				return false;
			}
		}

		if(flags.contains(Flag.WATERONLY))
		{
			if((mob.location().domainType()!=Room.DOMAIN_OUTDOORS_WATERSURFACE)
			&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_WATERSURFACE)
			&&(mob.location().domainType()!=Room.DOMAIN_OUTDOORS_UNDERWATER)
			&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_UNDERWATER))
			{
				commonTell(mob,L("This can only be done in water."));
				return false;
			}
		}

		if(flags.contains(Flag.WATERSURFACEONLY))
		{
			if((mob.location().domainType()!=Room.DOMAIN_OUTDOORS_WATERSURFACE)
			&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_WATERSURFACE))
			{
				commonTell(mob,L("This can only be done on the water."));
				return false;
			}
		}

		if(flags.contains(Flag.UNDERWATERONLY))
		{
			if((mob.location().domainType()!=Room.DOMAIN_OUTDOORS_UNDERWATER)
			&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_UNDERWATER))
			{
				commonTell(mob,L("This can only be done under the water."));
				return false;
			}
		}

		if(flags.contains(Flag.SALTWATER))
		{
			if((mob.location().getAtmosphere()!=RawMaterial.RESOURCE_SALTWATER)
			&&((!(mob.location() instanceof Drink))||(((Drink)mob.location()).liquidType()!=RawMaterial.RESOURCE_SALTWATER)))
			{
				commonTell(mob,L("This can only be done in salt water."));
				return false;
			}
		}

		if(flags.contains(Flag.FRESHWATER))
		{
			if((mob.location().getAtmosphere()!=RawMaterial.RESOURCE_FRESHWATER)
			&&((!(mob.location() instanceof Drink))||(((Drink)mob.location()).liquidType()!=RawMaterial.RESOURCE_SALTWATER)))
			{
				commonTell(mob,L("This can only be done in fresh water."));
				return false;
			}
		}

		if(doingCode == Building.TITLE)
		{
			if(!canDescTitleHere(mob.location()))
			{
				commonTell(mob,L("You can't do that here."));
				return false;
			}
			String titleStr=CMParms.combine(commands,1);
			if(titleStr.length()==0)
			{
				commonTell(mob,L("A title must be specified."));
				return false;
			}
			titleStr=CMLib.coffeeFilter().secondaryUserInputFilter(titleStr);
			if(titleStr.length()>253)
			{
				commonTell(mob,L("That title is too long."));
				return false;
			}
			final TrackingLibrary.TrackingFlags trackingFlags=CMLib.tracking().newFlags();
			final List<Room> checkSet=CMLib.tracking().getRadiantRooms(mob.location(),trackingFlags,20);
			for (final Room room2 : checkSet)
			{
				final Room R=CMLib.map().getRoom(room2);
				if(R!=null)
				{
					if(R.displayText(mob).equalsIgnoreCase(titleStr))
					{
						commonTell(mob,L("That title has already been taken.  Choose another."));
						return false;
					}
				}
			}
			designTitle=titleStr;
		}
		else
		if(doingCode == Building.DESC)
		{
			if(commands.size()<3)
			{
				commonTell(mob,L("You must specify an exit direction or the word room, followed by a description for it."));
				return false;
			}

			if(!canDescTitleHere(mob.location()))
			{
				commonTell(mob,L("You can't do that here."));
				return false;
			}

			if(CMLib.directions().getGoodDirectionCode(commands.get(1))>=0)
			{
				dir=CMLib.directions().getGoodDirectionCode(commands.get(1));
				if(mob.location().getExitInDir(dir)==null)
				{
					commonTell(mob,L("There is no exit @x1 to describe.",CMLib.directions().getInDirectionName(dir)));
					return false;
				}
				commands.remove(1);
			}
			else
			if(!commands.get(1).equalsIgnoreCase("room"))
			{
				commonTell(mob,L("'@x1' is neither the word room, nor an exit direction.",(commands.get(1))));
				return false;
			}
			else
				commands.remove(1);

			String descStr=CMParms.combine(commands,1);
			descStr=CMLib.coffeeFilter().secondaryUserInputFilter(descStr);
			if(descStr.length()==0)
			{
				commonTell(mob,L("A description must be specified."));
				return false;
			}
			designDescription=descStr;
		}

		int[][] idata;
		if(recipe[DAT_WOODTYPE].equalsIgnoreCase("MONEY"))
		{
			idata=null;
			int wood=CMath.s_int(recipe[DAT_WOOD]);
			wood=adjustWoodRequired(wood,mob);
			if(CMLib.beanCounter().getTotalAbsoluteValue(mob, landCurrency) < wood)
			{
				commonTell(mob,L("You'll need @x1 to do that.",CMLib.beanCounter().nameCurrencyLong(landCurrency, wood)));
				return false;
			}
			woodRequired=0;
		}
		else
		if(recipe[DAT_WOODTYPE].equalsIgnoreCase("VALUE"))
		{
			idata=null;
			int wood=CMath.s_int(recipe[DAT_WOOD]);
			wood=adjustWoodRequired(wood,mob);
			final double roomValue = landValue * wood;
			if(CMLib.beanCounter().getTotalAbsoluteValue(mob, landCurrency) < roomValue)
			{
				commonTell(mob,L("You'll need @x1 to do that.",CMLib.beanCounter().nameCurrencyLong(landCurrency, roomValue)));
				return false;
			}
			woodRequired=0;
		}
		else
		{
			idata=this.getBasicMaterials(mob, woodRequired, recipe[DAT_WOODTYPE]);

			if(idata==null)
				return false;
			woodRequired=idata[0][FOUND_AMT];
		}

		if(!canBuild)
		{
			if((dir>=0)&&(flags.contains(Flag.DIR)))
			{
				final Room R=mob.location().getRoomInDir(dir);
				if((R!=null)
				&&(CMLib.law().doesOwnThisLand(mob,R)))
					canBuild=true;
			}
		}
		if(!canBuild)
		{
			commonTell(mob,L("You'll need the permission of the owner to do that."));
			return false;
		}

		if(!super.invoke(mob,commands,givenTarget,auto,asLevel))
			return false;

		room=mob.location();
		if(room.getGridParent() != null)
			room = room.getGridParent();

		if((woodRequired>0)&&(idata!=null))
			CMLib.materials().destroyResourcesValue(mob.location(),woodRequired,idata[0][FOUND_CODE],idata[0][FOUND_SUB],0,0);
		else
		if(recipe[DAT_WOODTYPE].equalsIgnoreCase("VALUE"))
		{
			int wood=CMath.s_int(recipe[DAT_WOOD]);
			wood=adjustWoodRequired(wood,mob);
			final double roomValue = landValue * wood;
			CMLib.beanCounter().subtractMoney(mob, landCurrency, roomValue);
		}
		else
		if(recipe[DAT_WOODTYPE].equalsIgnoreCase("MONEY"))
		{
			int wood=CMath.s_int(recipe[DAT_WOOD]);
			wood=adjustWoodRequired(wood,mob);
			CMLib.beanCounter().subtractMoney(mob, landCurrency, wood);
		}

		verb = establishVerb(mob, recipe);
		if(verb.length()==0)
			return false;
		messedUp=!proficiencyCheck(mob,0,auto);
		startStr=L("<S-NAME> start(s) @x1",verb);
		playSound=this.getSoundName();
		duration=getDuration(CMath.s_int(recipe[RCP_TICKS]),mob,CMath.s_int(recipe[RCP_LEVEL]),10);
		poundsOfMatsUsed += woodRequired;

		final CMMsg msg=CMClass.getMsg(mob,null,this,getActivityMessageType(),startStr+".");
		if(mob.location().okMessage(mob,msg))
		{
			mob.location().send(mob,msg);
			beneficialAffect(mob,mob,asLevel,duration);
		}
		return true;
	}
}

<MSG> 

git-svn-id: svn://192.168.1.10/public/CoffeeMud@15018 0d6f1817-ed0e-0410-87c9-987e46238f29

<DFF> @@ -722,6 +722,8 @@ public class BuildingSkill extends CraftingSkill implements CraftorAbility
 			String desc = recipe[DAT_MISC];
 			String addParms = recipe[DAT_PROPERTIES];
 			String localeClass = recipe[DAT_CLASS];
+			if(desc.equals("0"))
+				desc="";
 			
 			int opDir = Directions.getOpDirectionCode(dir);
 			room=CMLib.map().getRoom(room);

{'commit': 'diff --git a/com/planet_ink/coffee_mud/Abilities/Common/BuildingSkill.java b/com/planet_ink/coffee_mud/Abilities/Common/BuildingSkill.java\nindex a5be92a015..7763ddd158 100644\n--- a/com/planet_ink/coffee_mud/Abilities/Common/BuildingSkill.java\n+++ b/com/planet_ink/coffee_mud/Abilities/Common/BuildingSkill.java\n@@ -722,6 +722,8 @@ public class BuildingSkill extends CraftingSkill implements CraftorAbility\n \t\t\tString desc = recipe[DAT_MISC];\r\n \t\t\tString addParms = recipe[DAT_PROPERTIES];\r\n \t\t\tString localeClass = recipe[DAT_CLASS];\r\n+\t\t\tif(desc.equals("0"))\r\n+\t\t\t\tdesc="";\r\n \t\t\t\r\n \t\t\tint opDir = Directions.getOpDirectionCode(dir);\r\n \t\t\troom=CMLib.map().getRoom(room);\r\n', 'commit_subject': '', 'repo_name': 'bozimmerman/CoffeeMud', 'committer': {'name': 'bozimmerman', 'email': 'dc45fe023482b3b890b5e574e53c1d051f45b1ef@zimmers.net', 'time_sec': '1490415207', 'tz_offset': '0', 'date': {'seconds': '1490415207'}}, 'commit_tree': 'dfb44f108b5457a60bb13856e35b5f4021d32dd2', 'commit_message': '\n\ngit-svn-id: svn://192.168.1.10/public/CoffeeMud@15018 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'language_name': 'Java', 'license': 'apache-2.0', 'hunks': '@@ -722,6 +722,8 @@ public class BuildingSkill extends CraftingSkill implements CraftorAbility\n \t\t\tString desc = recipe[DAT_MISC];\r\n \t\t\tString addParms = recipe[DAT_PROPERTIES];\r\n \t\t\tString localeClass = recipe[DAT_CLASS];\r\n+\t\t\tif(desc.equals("0"))\r\n+\t\t\t\tdesc="";\r\n \t\t\t\r\n \t\t\tint opDir = Directions.getOpDirectionCode(dir);\r\n \t\t\troom=CMLib.map().getRoom(room);\r\n', 'addition_count': 2, 'deletion_count': 0, 'src_file': 'a/com/planet_ink/coffee_mud/Abilities/Common/BuildingSkill.java', 'tgt_file': 'b/com/planet_ink/coffee_mud/Abilities/Common/BuildingSkill.java', 'file_extension': '.java', 'before_file': 'package com.planet_ink.coffee_mud.Abilities.Common;\nimport com.planet_ink.coffee_mud.core.interfaces.*;\nimport com.planet_ink.coffee_mud.core.interfaces.ItemPossessor.Expire;\nimport com.planet_ink.coffee_mud.core.*;\nimport com.planet_ink.coffee_mud.core.collections.*;\nimport com.planet_ink.coffee_mud.Abilities.interfaces.*;\nimport com.planet_ink.coffee_mud.Areas.interfaces.*;\nimport com.planet_ink.coffee_mud.Behaviors.interfaces.*;\nimport com.planet_ink.coffee_mud.CharClasses.interfaces.*;\nimport com.planet_ink.coffee_mud.Commands.interfaces.*;\nimport com.planet_ink.coffee_mud.Common.interfaces.*;\nimport com.planet_ink.coffee_mud.Exits.interfaces.*;\nimport com.planet_ink.coffee_mud.Items.interfaces.*;\nimport com.planet_ink.coffee_mud.Libraries.interfaces.*;\nimport com.planet_ink.coffee_mud.core.interfaces.CostDef;\nimport com.planet_ink.coffee_mud.Locales.interfaces.*;\nimport com.planet_ink.coffee_mud.MOBS.interfaces.*;\nimport com.planet_ink.coffee_mud.Races.interfaces.*;\n\nimport java.util.*;\n\n/*\n   Copyright 2015-2022 Bo Zimmerman\n\n   Licensed under the Apache License, Version 2.0 (the "License");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n\t   http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an "AS IS" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\npublic class BuildingSkill extends CraftingSkill implements CraftorAbility\n{\n\t@Override\n\tpublic String ID()\n\t{\n\t\treturn "BuildingSkill";\n\t}\n\n\tprivate final static String\tlocalizedName\t= CMLib.lang().L("BuildingSkill");\n\n\t@Override\n\tpublic String name()\n\t{\n\t\treturn localizedName;\n\t}\n\n\tprivate static final String[]\ttriggerStrings\t= I(new String[] { "BUILD", "BUILDING" });\n\n\t@Override\n\tpublic String[] triggerStrings()\n\t{\n\t\treturn triggerStrings;\n\t}\n\n\t@Override\n\tpublic int classificationCode()\n\t{\n\t\treturn Ability.ACODE_COMMON_SKILL | Ability.DOMAIN_BUILDINGSKILL;\n\t}\n\n\t@Override\n\tpublic String supportedResourceString()\n\t{\n\t\treturn "";\n\t}\n\n\tprotected String getMainResourceName()\n\t{\n\t\treturn "Wood";\n\t}\n\n\tprotected String getDemolishRoom()\n\t{\n\t\treturn "Plains";\n\t}\n\n\tprotected String getSoundName()\n\t{\n\t\treturn "hammer.wav";\n\t}\n\n\tpublic BuildingSkill()\n\t{\n\t\tsuper();\n\t}\n\n\t@Override\n\tpublic List<List<String>> fetchMyRecipes(final MOB mob)\n\t{\n\t\treturn this.addRecipes(mob, loadRecipes());\n\t}\n\n\tprotected boolean canBeDoneSittingDown = false;\n\n\t@Override\n\tpublic boolean canBeDoneSittingDown()\n\t{\n\t\treturn canBeDoneSittingDown;\n\t}\n\n\tprotected enum Building\n\t{\n\t\tWALL,\n\t\tDOOR,\n\t\tROOM,\n\t\tITEM,\n\t\tDEMOLISH,\n\t\tTITLE,\n\t\tDESC,\n\t\tSTAIRS,\n\t\tEXCAVATE,\n\t\tROOMEFFECT,\n\t\tEXITEFFECT,\n\t\tDELEFFECT\n\t}\n\n\tprotected enum Flag\n\t{\n\t\tDIR,\n\t\tDIRUPDOWN,\n\t\tNOWALL,\n\t\tINDOOR,\n\t\tOUTDOOR,\n\t\tCAVEONLY,\n\t\tNODOWN,\n\t\tDOWNONLY,\n\t\tWATERONLY,\n\t\tWATERSURFACEONLY,\n\t\tUNDERWATERONLY,\n\t\tSALTWATER,\n\t\tFRESHWATER,\n\t\tUPONLY\n\t}\n\n\tprotected Room\t\troom\t\t\t\t= null;\n\tprotected int\t\tdir\t\t\t\t\t= -1;\n\tprotected String[]\trecipe\t\t\t\t= null;\n\tprotected int\t\tpoundsOfMatsUsed\t= 0;\n\tprotected String\tdesignTitle\t\t\t= "";\n\tprotected String\tdesignDescription\t= "";\n\n\t//protected static final int\tRCP_FINALNAME\t\t= 0;\n\t//protected static final int\tRCP_LEVEL\t\t\t= 1;\n\tprotected static final int\tRCP_TICKS\t\t= 2;\n\tprotected final static int\tDAT_WOOD\t\t= 3;\n\tprotected final static int\tDAT_WOODTYPE\t= 4;\n\tprotected final static int\tDAT_FLAG\t\t= 5;\n\tprotected final static int\tDAT_BUILDCODE\t= 6;\n\tprotected final static int\tDAT_CLASS\t\t= 7;\n\tprotected final static int\tDAT_MISC\t\t= 8;\n\tprotected final static int\tDAT_PROPERTIES\t= 9;\n\tprotected final static int\tDAT_DESC\t\t= 10;\n\tprotected final static int\tDAT_BUILDERMASK\t= 11;\n\tprotected final static int\tDAT_DESCRIPTION\t= 12;\n\n\t@Override\n\tpublic String getRecipeFormat()\n\t{\n\t\tif(Resources.getResource("BUILDING_SKILL_CODES_FLAGS")==null)\n\t\t{\n\t\t\tfinal String[] codes = CMParms.toStringArray(Building.values());\n\t\t\tfinal String[] flags = CMParms.toStringArray(Flag.values());\n\t\t\tfinal Pair<String[],String[]> codesFlags = new Pair<String[],String[]>(codes, flags);\n\t\t\tResources.submitResource("BUILDING_SKILL_CODES_FLAGS", codesFlags);\n\t\t}\n\t\treturn"ITEM_NAME\\tITEM_LEVEL\\tBUILD_TIME_TICKS\\tMATERIALS_REQUIRED\\tOPTIONAL_BUILDING_RESOURCE_OR_MATERIAL\\t"\n\t\t\t+ "BUILDING_FLAGS\\tBUILDING_CODE\\tROOM_CLASS_ID||EXIT_CLASS_ID||ALLITEM_CLASS_ID||ROOM_CLASS_ID_OR_NONE\\t"\n\t\t\t+ "BUILDING_GRID_SIZE||EXIT_NAMES||STAIRS_DESC\\tPCODED_SPELL_LIST\\tBUILDING_NOUN\\tBUILDER_MASK\\tBUILDER_DESC";\n\t}\n\n\t@Override\n\tpublic String getRecipeFilename()\n\t{\n\t\treturn "";\n\t}\n\n\t@Override\n\tpublic String getDecodedComponentsDescription(final MOB mob, final List<String> recipe)\n\t{\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected CostDef getRawTrainingCost()\n\t{\n\t\treturn CMProps.getNormalSkillGainCost(ID());\n\t}\n\n\tprotected boolean canDescTitleHere(final Room R)\n\t{\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void unInvoke()\n\t{\n\t\tif(canBeUninvoked() && (!super.unInvoked))\n\t\t{\n\t\t\tif((affected instanceof MOB)&&(!helping))\n\t\t\t{\n\t\t\t\tfinal MOB mob=(MOB)affected;\n\t\t\t\tif(!aborted)\n\t\t\t\t{\n\t\t\t\t\tif((messedUp)&&(room!=null))\n\t\t\t\t\t{\n\t\t\t\t\t\tnotifyMessUp(mob, recipe);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.buildComplete(mob, recipe, room, dir, designTitle, designDescription);\n\t\t\t\t\t\tif(poundsOfMatsUsed>0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfinal CMMsg msg=CMClass.getMsg(mob,room,this,CMMsg.TYP_ITEMGENERATED|CMMsg.MASK_ALWAYS,null);\n\t\t\t\t\t\t\tmsg.setValue(poundsOfMatsUsed/2);\n\t\t\t\t\t\t\tif(mob.location().okMessage(mob,msg))\n\t\t\t\t\t\t\t\tmob.location().send(mob,msg);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsuper.unInvoke();\n\t}\n\n\tprotected int[][] getBasicMaterials(final MOB mob, final int woodRequired, String miscType)\n\t{\n\t\tif(miscType.length()==0)\n\t\t\tmiscType="rock";\n\t\tfinal int[][] idata=fetchFoundResourceData(mob,\n\t\t\t\t\t\t\t\t\t\t\t\t\twoodRequired,miscType,null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t0,null,null,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t\t\t\t\t\t\t0,null);\n\t\treturn idata;\n\t}\n\n\tpublic String[][] getRecipeData(final MOB mob)\n\t{\n\t\tfinal List<List<String>> recipeData = addRecipes(mob,loadRecipes(getRecipeFilename()));\n\t\tfinal String[][] finalDat = new String[recipeData.size()][];\n\t\tfor(int i=0;i<recipeData.size();i++)\n\t\t\tfinalDat[i] = recipeData.get(i).toArray(new String[recipeData.get(i).size()]);\n\t\treturn finalDat;\n\t}\n\n\tpublic Exit generify(final Exit X)\n\t{\n\t\tfinal Exit E2=CMClass.getExit("GenExit");\n\t\tE2.setName(X.name());\n\t\tE2.setDisplayText(X.displayText());\n\t\tE2.setDescription(X.description());\n\t\tE2.setDoorsNLocks(X.hasADoor(),X.isOpen(),X.defaultsClosed(),X.hasALock(),X.isLocked(),X.defaultsLocked());\n\t\tE2.setBasePhyStats((PhyStats)X.basePhyStats().copyOf());\n\t\tE2.setExitParams(X.doorName(),X.closeWord(),X.openWord(),X.closedText());\n\t\tE2.setKeyName(X.keyName());\n\t\tE2.setOpenDelayTicks(X.openDelayTicks());\n\t\tE2.setReadable(X.isReadable());\n\t\tE2.setReadableText(X.readableText());\n\t\tE2.setTemporaryDoorLink(X.temporaryDoorLink());\n\t\tE2.recoverPhyStats();\n\t\treturn E2;\n\t}\n\n\tprotected void notifyMessUp(final MOB mob, final String[] recipe)\n\t{\n\t\tcommonTell(mob,L("You\'ve ruined the "+recipe[DAT_DESC]+"!",CMLib.directions().getDirectionName(dir)));\n\t}\n\n\tprotected void demolishRoom(final MOB mob, final Room room)\n\t{\n\t\tfinal LandTitle title=CMLib.law().getLandTitle(room);\n\t\tif(title==null)\n\t\t\treturn;\n\t\tRoom returnToRoom=null;\n\t\tRoom backupToRoom1=null;\n\t\tRoom backupToRoom2=null;\n\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t{\n\t\t\tfinal Room R=room.getRoomInDir(d);\n\t\t\tif(CMLib.law().doesOwnThisLand(mob, R)||(CMSecurity.isAllowed(mob, R, CMSecurity.SecFlag.CMDROOMS)))\n\t\t\t{\n\t\t\t\treturnToRoom=R;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfinal LandTitle adjacentTitle=CMLib.law().getLandTitle(R);\n\t\t\tif((adjacentTitle==null)||(adjacentTitle.getOwnerName().length()>0))\n\t\t\t\tbackupToRoom1=R;\n\t\t\telse\n\t\t\tif(R.roomID().length()>0)\n\t\t\t\tbackupToRoom2=R;\n\t\t}\n\t\tif(returnToRoom==null)\n\t\t\treturnToRoom=backupToRoom1;\n\t\tif(returnToRoom==null)\n\t\t\treturnToRoom=backupToRoom2;\n\t\tif(returnToRoom==null)\n\t\t\treturnToRoom=mob.getStartRoom();\n\t\tif(returnToRoom==null)\n\t\t\treturnToRoom=room.getArea().getRandomProperRoom();\n\t\tif(returnToRoom==null)\n\t\t\treturnToRoom=room.getArea().getRandomMetroRoom();\n\t\tif(returnToRoom==null)\n\t\t\treturnToRoom=CMLib.map().getRandomRoom();\n\t\tfinal Room theRoomToReturnTo=returnToRoom;\n\t\troom.eachInhabitant(new EachApplicable<MOB>()\n\t\t{\n\t\t\t@Override\n\t\t\tpublic void apply(final MOB a)\n\t\t\t{\n\t\t\t\ttheRoomToReturnTo.bringMobHere(a, false);\n\t\t\t}\n\t\t});\n\t\troom.eachItem(new EachApplicable<Item>()\n\t\t{\n\t\t\t@Override\n\t\t\tpublic void apply(final Item a)\n\t\t\t{\n\t\t\t\ttheRoomToReturnTo.addItem(a,Expire.Player_Drop);\n\t\t\t}\n\t\t});\n\t\ttitle.setOwnerName("");\n\t\ttitle.updateLot(null); // this is neat -- this will obliterate leaf rooms around this one.\n\t\tif((theRoomToReturnTo!=null)\n\t\t&&(theRoomToReturnTo.rawDoors()[Directions.UP]==room)\n\t\t&&(theRoomToReturnTo.getRawExit(Directions.UP)!=null))\n\t\t{\n\t\t\ttheRoomToReturnTo.getRawExit(Directions.UP).destroy();\n\t\t\ttheRoomToReturnTo.setRawExit(Directions.UP, null);\n\t\t}\n\t\tCMLib.map().obliterateMapRoom(room);\n\t}\n\n\tprivate void removeEffects(final PhysicalAgent E, String extraProp)\n\t{\n\t\textraProp=extraProp.trim();\n\t\tif(extraProp.length()>0)\n\t\t{\n\t\t\tfinal List<String> spells = CMParms.parseAny(extraProp, ")", true);\n\t\t\tfor(String spellName : spells)\n\t\t\t{\n\t\t\t\tfinal int x=spellName.indexOf(\'(\');\n\t\t\t\tif(x>0)\n\t\t\t\t\tspellName=spellName.substring(0,x);\n\t\t\t\tfinal Ability A=E.fetchEffect(spellName);\n\t\t\t\tif(A!=null)\n\t\t\t\t{\n\t\t\t\t\tA.unInvoke();\n\t\t\t\t\tE.delEffect(A);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfinal Behavior B=E.fetchBehavior(spellName);\n\t\t\t\t\tif(B!=null)\n\t\t\t\t\t\tE.delBehavior(B);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addEffects(final PhysicalAgent E, final Room R2, String extraProp)\n\t{\n\t\textraProp=extraProp.trim();\n\t\tif(extraProp.length()>0)\n\t\t{\n\t\t\tfinal List<String> spells = CMParms.parseAny(extraProp, ")", true);\n\t\t\tfor(String spellName : spells)\n\t\t\t{\n\t\t\t\tString parms="";\n\t\t\t\tfinal int x=spellName.indexOf(\'(\');\n\t\t\t\tif(x>0)\n\t\t\t\t{\n\t\t\t\t\tparms=spellName.substring(x+1);\n\t\t\t\t\tspellName=spellName.substring(0,x);\n\t\t\t\t}\n\t\t\t\tif(parms.trim().equalsIgnoreCase("@remove"))\n\t\t\t\t{\n\t\t\t\t\tfinal Ability A=E.fetchEffect(spellName);\n\t\t\t\t\tif(A!=null)\n\t\t\t\t\t{\n\t\t\t\t\t\tA.unInvoke();\n\t\t\t\t\t\tE.delEffect(A);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Behavior B=E.fetchBehavior(spellName);\n\t\t\t\t\t\tif(B!=null)\n\t\t\t\t\t\t\tE.delBehavior(B);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfinal Ability A=CMClass.getAbility(spellName);\n\t\t\t\tif(A!=null)\n\t\t\t\t{\n\n\t\t\t\t\tif(parms.length()>0)\n\t\t\t\t\t\tA.setMiscText(parms);\n\t\t\t\t\telse\n\t\t\t\t\tif(A.ID().equals("Prop_RoomView"))\n\t\t\t\t\t\tA.setMiscText(CMLib.map().getExtendedRoomID(R2));\n\t\t\t\t\tE.addNonUninvokableEffect(A);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfinal Behavior B=CMClass.getBehavior(spellName);\n\t\t\t\t\tif(parms.length()>0)\n\t\t\t\t\t\tB.setParms(parms);\n\t\t\t\t\tE.addBehavior(B);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected Room buildRoomAbility(Room R, final int dir, String extraProp)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\tR=CMLib.map().getRoom(R);\n\t\t\textraProp=CMStrings.replaceAll(extraProp, "@dir", CMLib.directions().getDirectionName(dir));\n\t\t\taddEffects(R,R,extraProp);\n\t\t}\n\t\tCMLib.database().DBUpdateRoom(R);\n\t\treturn R;\n\t}\n\n\tprotected Exit buildExitAbility(Room R, final int dir, final String extraProp)\n\t{\n\t\tExit E=null;\n\t\tsynchronized(CMClass.getSync("SYNC"+R.roomID()))\n\t\t{\n\t\t\tR=CMLib.map().getRoom(R);\n\t\t\tE=R.getExitInDir(dir);\n\t\t\taddEffects(E,R,extraProp);\n\t\t}\n\t\tCMLib.database().DBUpdateExits(R);\n\t\treturn E;\n\t}\n\n\tprotected Room removeRoomAbility(Room R, final int dir, String extraProp)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+R.roomID()))\n\t\t{\n\t\t\tR=CMLib.map().getRoom(R);\n\t\t\textraProp=CMStrings.replaceAll(extraProp, "@dir", CMLib.directions().getDirectionName(dir));\n\t\t\tremoveEffects(R,extraProp);\n\t\t}\n\t\tCMLib.database().DBUpdateRoom(R);\n\t\treturn R;\n\t}\n\n\tprotected Exit removeExitAbility(Room R, final int dir, final String extraProp)\n\t{\n\t\tExit E=null;\n\t\tsynchronized(CMClass.getSync("SYNC"+R.roomID()))\n\t\t{\n\t\t\tR=CMLib.map().getRoom(R);\n\t\t\tE=R.getExitInDir(dir);\n\t\t\tremoveEffects(E,extraProp);\n\t\t}\n\t\tCMLib.database().DBUpdateExits(R);\n\t\treturn E;\n\t}\n\n\tprotected Room buildNewRoomType(Room room, String newLocale, final String extraProp, int dimension)\n\t{\n\t\tRoom R=null;\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\tif((dimension == 0)&&(room instanceof GridLocale))\n\t\t\t\tdimension = ((GridLocale)room).xGridSize();\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\tif(newLocale.equalsIgnoreCase("IndoorWaterSurface")\n\t\t\t&&((room.domainType()&Room.INDOORS)==0))\n\t\t\t\tnewLocale="WaterSurface";\n\t\t\tR=CMClass.getLocale(newLocale);\n\t\t\tR.setRoomID(room.roomID());\n\t\t\tR.setDisplayText(room.displayText());\n\t\t\tR.setDescription(room.description());\n\t\t\tif(R.image().equalsIgnoreCase(CMLib.protocol().getDefaultMXPImage(room)))\n\t\t\t\tR.setImage(null);\n\t\t\tif(R instanceof GridLocale)\n\t\t\t{\n\t\t\t\t((GridLocale)R).setXGridSize(dimension);\n\t\t\t\t((GridLocale)R).setYGridSize(dimension);\n\t\t\t}\n\n\t\t\tfinal Area area=room.getArea();\n\t\t\tif(area!=null)\n\t\t\t\tarea.delProperRoom(room);\n\t\t\tR.setArea(area);\n\t\t\tfor(int a=room.numEffects()-1;a>=0;a--)\n\t\t\t{\n\t\t\t\tfinal Ability A=room.fetchEffect(a);\n\t\t\t\tif(A!=null)\n\t\t\t\t{\n\t\t\t\t\troom.delEffect(A);\n\t\t\t\t\tR.addEffect(A);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor(int i=room.numItems()-1;i>=0;i--)\n\t\t\t{\n\t\t\t\tfinal Item I=room.getItem(i);\n\t\t\t\tif(I!=null)\n\t\t\t\t{\n\t\t\t\t\troom.delItem(I);\n\t\t\t\t\tR.addItem(I);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor(int m=room.numInhabitants()-1;m>=0;m--)\n\t\t\t{\n\t\t\t\tfinal MOB M=room.fetchInhabitant(m);\n\t\t\t\tif(M!=null)\n\t\t\t\t{\n\t\t\t\t\troom.delInhabitant(M);\n\t\t\t\t\tR.addInhabitant(M);\n\t\t\t\t\tM.setLocation(R);\n\t\t\t\t}\n\t\t\t}\n\t\t\troom.clearSky();\n\t\t\tif(((room.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)||(room.domainType()==Room.DOMAIN_OUTDOORS_WATERSURFACE))\n\t\t\t&&(R.domainType()!=Room.DOMAIN_INDOORS_WATERSURFACE)\n\t\t\t&&(R.domainType()!=Room.DOMAIN_OUTDOORS_WATERSURFACE))\n\t\t\t{\n\t\t\t\tfinal Room waterR=room.getRoomInDir(Directions.DOWN);\n\t\t\t\tif((waterR!=null)\n\t\t\t\t&&((waterR.domainType()==Room.DOMAIN_INDOORS_UNDERWATER)||(waterR.domainType()==Room.DOMAIN_OUTDOORS_UNDERWATER))\n\t\t\t\t&&(waterR.roomID().length()>0))\n\t\t\t\t{\n\t\t\t\t\tfinal LandTitle title=CMLib.law().getLandTitle(waterR);\n\t\t\t\t\tif(title!=null)\n\t\t\t\t\t{\n\t\t\t\t\t\ttitle.setOwnerName("");\n\t\t\t\t\t\ttitle.updateLot(null);\n\t\t\t\t\t}\n\t\t\t\t\troom.setRawExit(Directions.DOWN, null);\n\t\t\t\t\troom.rawDoors()[Directions.DOWN]=null;\n\t\t\t\t\twaterR.setRawExit(Directions.UP, null);\n\t\t\t\t\twaterR.rawDoors()[Directions.UP]=null;\n\t\t\t\t\tCMLib.map().obliterateMapRoom(waterR);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCMLib.threads().deleteTick(room,-1);\n\t\t\tfor(int d=0;d<R.rawDoors().length;d++)\n\t\t\t{\n\t\t\t\tif((R.rawDoors()[d]==null)\n\t\t\t\t||(R.rawDoors()[d].roomID().length()>0))\n\t\t\t\t\tR.rawDoors()[d]=room.rawDoors()[d];\n\t\t\t}\n\t\t\tfor(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)\n\t\t\t{\n\t\t\t\tif((R.rawDoors()[d]==null)\n\t\t\t\t||(R.rawDoors()[d].roomID().length()>0))\n\t\t\t\t{\n\t\t\t\t\tif(room.getRawExit(d)!=null)\n\t\t\t\t\t\tR.setRawExit(d, (Exit)room.getRawExit(d).copyOf());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinal LandTitle title = CMLib.law().getLandTitle(room);\n\t\t\tif((title!=null)&&(title.gridLayout()))\n\t\t\t{\n\t\t\t\tfinal PairVector<Room,int[]> rooms=CMLib.tracking().buildGridList(R, title.getOwnerName(), 100);\n\t\t\t\tfor(int dir=0;dir<Directions.NUM_DIRECTIONS();dir++)\n\t\t\t\t{\n\t\t\t\t\tif(dir==Directions.GATE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tRoom R3=R.getRoomInDir(dir);\n\t\t\t\t\tif(R3 == null)\n\t\t\t\t\t{\n\t\t\t\t\t\tR3=CMLib.tracking().getCalculatedAdjacentRoom(rooms, R3, dir);\n\t\t\t\t\t\tif(R3!=null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tR.rawDoors()[dir]=R3;\n\t\t\t\t\t\t\tR3.rawDoors()[Directions.getOpDirectionCode(dir)]=R;\n\t\t\t\t\t\t\tCMLib.database().DBUpdateExits(R3);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tR.clearSky();\n\t\t\tR.startItemRejuv();\n\t\t\ttry\n\t\t\t{\n\t\t\t\tboolean rebuild=false;\n\t\t\t\tfor(final Enumeration<Room> r=CMLib.map().rooms();r.hasMoreElements();)\n\t\t\t\t{\n\t\t\t\t\tfinal Room R2=r.nextElement();\n\t\t\t\t\trebuild=false;\n\t\t\t\t\tfor(int d=0;d<R2.rawDoors().length;d++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(R2.rawDoors()[d]==room)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\trebuild=true;\n\t\t\t\t\t\t\tR2.rawDoors()[d]=R;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif((rebuild)&&(R2 instanceof GridLocale))\n\t\t\t\t\t\t((GridLocale)R2).buildGrid();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (final NoSuchElementException e)\n\t\t\t{\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor(final Enumeration<MOB> e=CMLib.players().players();e.hasMoreElements();)\n\t\t\t\t{\n\t\t\t\t\tfinal MOB M=e.nextElement();\n\t\t\t\t\tif(M.getStartRoom()==room)\n\t\t\t\t\t\tM.setStartRoom(R);\n\t\t\t\t\telse\n\t\t\t\t\tif(M.location()==room)\n\t\t\t\t\t\tM.setLocation(R);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (final NoSuchElementException e)\n\t\t\t{\n\t\t\t}\n\t\t\tif((R.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)\n\t\t\t||(R.domainType()==Room.DOMAIN_OUTDOORS_WATERSURFACE))\n\t\t\t{\n\t\t\t\tif(dimension > 0)\n\t\t\t\t{\n\t\t\t\t\tfinal Room R2=CMClass.getLocale("UnderWater");\n\t\t\t\t\tR2.setRoomID(R.getArea().getNewRoomID(R,Directions.DOWN));\n\t\t\t\t\tR2.setDisplayText(L("Under the water"));\n\t\t\t\t\tR2.setDescription(L("You are swimming around under the water."));\n\t\t\t\t\tR2.setArea(R.getArea());\n\t\t\t\t\tR2.rawDoors()[Directions.UP]=R;\n\t\t\t\t\tR2.setRawExit(Directions.UP,CMClass.getExit("Open"));\n\t\t\t\t\tR.clearSky();\n\t\t\t\t\tR.rawDoors()[Directions.DOWN]=R2;\n\t\t\t\t\tR.setRawExit(Directions.DOWN,CMClass.getExit("Open"));\n\t\t\t\t\tfinal LandTitle titleA=CMLib.law().getLandTitle(R);\n\t\t\t\t\tif((titleA!=null)&&(CMLib.law().getLandTitle(R2)==null))\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal LandTitle A2=(LandTitle)titleA.copyOf();\n\t\t\t\t\t\tR2.addNonUninvokableEffect((Ability)A2);\n\t\t\t\t\t}\n\t\t\t\t\tfinal Ability capacityA = R.fetchEffect("Prop_ReqCapacity");\n\t\t\t\t\tif(capacityA != null)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Ability A2=(Ability)capacityA.copyOf();\n\t\t\t\t\t\tR2.addNonUninvokableEffect(A2);\n\t\t\t\t\t}\n\t\t\t\t\tif(CMSecurity.isDebugging(CMSecurity.DbgFlag.PROPERTY))\n\t\t\t\t\t\tLog.debugOut(ID(),R2.roomID()+" created for water.");\n\t\t\t\t\tCMLib.database().DBCreateRoom(R2);\n\t\t\t\t\tCMLib.database().DBUpdateExits(R2);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tR.getArea().fillInAreaRoom(R);\n\t\t\taddEffects(R,R,extraProp);\n\t\t\tif(CMSecurity.isDebugging(CMSecurity.DbgFlag.PROPERTY))\n\t\t\t\tLog.debugOut(ID(),R.roomID()+" updated.");\n\t\t\tCMLib.database().DBUpdateRoom(R);\n\t\t\tCMLib.database().DBUpdateExits(R);\n\t\t\tfinal MOB mob=CMClass.getFactoryMOB("the wind",1,R);\n\t\t\ttry\n\t\t\t{\n\t\t\t\tR.executeMsg(mob, CMClass.getMsg(mob, R, CMMsg.MSG_NEWROOM, null));\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tmob.destroy();\n\t\t\t}\n\t\t\troom.destroy();\n\t\t\tR.clearSky();\n\t\t\tR.giveASky(0);\n\t\t}\n\t\treturn R;\n\t}\n\n\tprotected void buildDoor(final String[] recipe, Room room, final int dir, final int recipeLevel)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\tfinal String localeName = recipe[DAT_CLASS];\n\t\t\tString doorName = recipe[DAT_MISC];\n\t\t\tfinal String spells = recipe[DAT_PROPERTIES];\n\t\t\t//int size = CMath.s_int(recipe[DAT_MISC]);\n\t\t\tString closeWord=null;\n\t\t\tString openWord=null;\n\t\t\tString closedWord=null;\n\t\t\tString displayText="";\n\t\t\tString description="";\n\t\t\tif(doorName.indexOf("|")>0)\n\t\t\t{\n\t\t\t\tfinal List<String> split=CMParms.parseAny(doorName, \'|\',false);\n\t\t\t\tif(split.get(0).length()>0)\n\t\t\t\t\tdoorName=split.get(0);\n\t\t\t\tif((split.size()>1)&&(split.get(1).length()>0))\n\t\t\t\t\topenWord=split.get(1);\n\t\t\t\tif((split.size()>2)&&(split.get(2).length()>0))\n\t\t\t\t\tcloseWord=split.get(2);\n\t\t\t\tif((split.size()>3)&&(split.get(3).length()>0))\n\t\t\t\t\tclosedWord=split.get(3);\n\t\t\t\tif((split.size()>4)&&(split.get(4).length()>0))\n\t\t\t\t\tdisplayText=split.get(4);\n\t\t\t\tif((split.size()>5)&&(split.get(5).length()>0))\n\t\t\t\t\tdescription=split.get(5);\n\t\t\t}\n\t\t\tif(closeWord == null)\n\t\t\t\tcloseWord="close";\n\t\t\tif(openWord == null)\n\t\t\t\topenWord="open";\n\t\t\tif(closedWord == null)\n\t\t\t\tclosedWord=CMLib.english().startWithAorAn("closed "+doorName);\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\tfinal Exit X=CMClass.getExit(localeName);\n\t\t\tX.setName(CMLib.english().startWithAorAn(doorName));\n\t\t\tX.setDescription(description);\n\t\t\tX.setDisplayText(displayText);\n\t\t\tX.setOpenDelayTicks(9999);\n\t\t\tX.setExitParams(doorName,closeWord,openWord,closedWord);\n\t\t\tString desc = recipe[DAT_MISC];\r\n\t\t\tString addParms = recipe[DAT_PROPERTIES];\r\n\t\t\tString localeClass = recipe[DAT_CLASS];\r\n\t\t\t\r\n\t\t\tint opDir = Directions.getOpDirectionCode(dir);\r\n\t\t\troom=CMLib.map().getRoom(room);\r\n\t\t\t{\n\t\t\t\tfinal Room oroom = room.rawDoors()[dir].prepareRoomInDir(room, dir);\n\t\t\t\tfinal Exit X2=(Exit)X.copyOf();\n\t\t\t\tX2.recoverPhyStats();\n\t\t\t\tX2.text();\n\t\t\t\toroom.setRawExit(Directions.getOpDirectionCode(dir),X2);\n\t\t\t\tCMLib.database().DBUpdateExits(oroom);\n\t\t\t}\n\t\t\tCMLib.database().DBUpdateExits(room);\n\t\t}\n\t}\n\n\tprotected int findFloorNumber(final Room room, final Set<Room> done, final int floor)\n\t{\n\t\tfinal LandTitle title = CMLib.law().getLandTitle(room);\n\t\tif(title == null)\n\t\t\treturn floor;\n\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t{\n\t\t\tfinal Room R=room.getRoomInDir(d);\n\t\t\tif((R!=null)&&(!done.contains(R)))\n\t\t\t{\n\t\t\t\tdone.add(R);\n\t\t\t\tif(d==Directions.UP)\n\t\t\t\t{\n\t\t\t\t\tfinal int f=findFloorNumber(R,done,floor-1);\n\t\t\t\t\tif(f != Integer.MIN_VALUE)\n\t\t\t\t\t\treturn f;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\tif(d==Directions.DOWN)\n\t\t\t\t{\n\t\t\t\t\tfinal int f=findFloorNumber(R,done,floor+1);\n\t\t\t\t\tif(f != Integer.MIN_VALUE)\n\t\t\t\t\t\treturn f;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfinal int f=findFloorNumber(R,done,floor);\n\t\t\t\t\tif(f != Integer.MIN_VALUE)\n\t\t\t\t\t\treturn f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Integer.MIN_VALUE;\n\t}\n\n\tprotected Room buildStairs(final MOB mob, Room room, final int dir, final String[] recipe)\n\t{\n\t\tRoom newRoom;\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\tString desc = recipe[DAT_MISC];\n\t\t\tfinal String addParms = recipe[DAT_PROPERTIES];\n\t\t\tfinal String localeClass = recipe[DAT_CLASS];\n\t\t\tif(desc.equals("0"))\n\t\t\t\tdesc="";\n\n\t\t\tfinal int opDir = Directions.getOpDirectionCode(dir);\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\tfinal int floor=findFloorNumber(room, new HashSet<Room>(), 1);\n\t\t\tif(localeClass.length()==0)\n\t\t\t\tnewRoom=CMClass.getLocale(CMClass.classID(room));\n\t\t\telse\n\t\t\t\tnewRoom=CMClass.getLocale(localeClass);\n\t\t\tnewRoom.setRoomID(room.getArea().getNewRoomID(room,dir));\n\t\t\tif(newRoom.roomID().length()==0)\n\t\t\t{\n\t\t\t\tfinal String verbDesc = recipe[DAT_DESC];\n\t\t\t\tcommonTell(mob,L("You\'ve failed to build the "+verbDesc+"!",CMLib.directions().getDirectionName(dir)));\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tnewRoom.setArea(room.getArea());\n\t\t\tLandTitle newTitle=CMLib.law().getLandTitle(room);\n\t\t\tif((newTitle!=null)&&(CMLib.law().getLandTitle(newRoom)==null))\n\t\t\t{\n\t\t\t\tfinal List<Room> allRooms = newTitle.getConnectedPropertyRooms();\n\t\t\t\tif(allRooms.size()>0)\n\t\t\t\t{\n\t\t\t\t\tfinal Ability cap = allRooms.get(0).fetchEffect("Prop_ReqCapacity");\n\t\t\t\t\tif(cap != null)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewRoom.addNonUninvokableEffect((Ability)cap.copyOf());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnewTitle = newTitle.generateNextRoomTitle();\n\t\t\t\tnewTitle.setLandPropertyID(newRoom.roomID());\n\t\t\t\tnewRoom.addNonUninvokableEffect((Ability)newTitle);\n\t\t\t}\n\n\t\t\tint newFloorNum;\n\t\t\tint curFloorNum;\n\t\t\tif(dir == Directions.DOWN)\n\t\t\t{\n\t\t\t\tnewFloorNum = floor;\n\t\t\t\tcurFloorNum = (floor - 1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewFloorNum = (floor+1);\n\t\t\t\tcurFloorNum = floor;\n\t\t\t}\n\n\t\t\tfinal Exit newExit;\n\t\t\tfinal Exit returnExit;\n\n\t\t\tif(((dir == Directions.UP)||(dir == Directions.DOWN))&&(newFloorNum > 0))\n\t\t\t{\n\t\t\t\tnewExit=CMClass.getExit("GenExit");\n\t\t\t\tnewExit.setName(L("a passageway"));\n\t\t\t\tnewExit.setDescription(L(desc,CMLib.directions().getDirectionName(dir),""+newFloorNum+CMath.numAppendage(newFloorNum)));\n\t\t\t\tnewExit.setDisplayText(L(desc,CMLib.directions().getDirectionName(dir),""+newFloorNum+CMath.numAppendage(newFloorNum)));\n\t\t\t\taddEffects(newExit, room.getRoomInDir(dir),addParms);\n\t\t\t\tnewExit.recoverPhyStats();\n\t\t\t\tnewExit.text();\n\n\t\t\t\treturnExit=CMClass.getExit("GenExit");\n\t\t\t\treturnExit.setName(L("a passageway"));\n\t\t\t\treturnExit.setDescription(L(desc,CMLib.directions().getDirectionName(opDir),""+curFloorNum+CMath.numAppendage(curFloorNum)));\n\t\t\t\treturnExit.setDisplayText(L(desc,CMLib.directions().getDirectionName(opDir),""+curFloorNum+CMath.numAppendage(curFloorNum)));\n\t\t\t\taddEffects(returnExit, room.getRoomInDir(dir),addParms);\n\t\t\t\treturnExit.recoverPhyStats();\n\t\t\t\treturnExit.text();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewExit=CMClass.getExit("GenExit");\n\t\t\t\tnewExit.setName(L("a passageway"));\n\t\t\t\taddEffects(newExit, room.getRoomInDir(dir),addParms);\n\t\t\t\tnewExit.recoverPhyStats();\n\t\t\t\tnewExit.text();\n\n\t\t\t\treturnExit=CMClass.getExit("GenExit");\n\t\t\t\treturnExit.setName(L("a passageway"));\n\t\t\t\taddEffects(returnExit, room.getRoomInDir(dir),addParms);\n\t\t\t\treturnExit.recoverPhyStats();\n\t\t\t\treturnExit.text();\n\t\t\t}\n\t\t\troom.rawDoors()[dir]=newRoom;\n\t\t\troom.setRawExit(dir,newExit);\n\n\t\t\tnewRoom.rawDoors()[opDir]=room;\n\t\t\tnewRoom.setRawExit(opDir,returnExit);\n\n\t\t\tif(CMSecurity.isDebugging(CMSecurity.DbgFlag.PROPERTY))\n\t\t\t\tLog.debugOut(ID(),newRoom.roomID()+" created and put up for sale.");\n\t\t\tCMLib.database().DBCreateRoom(newRoom);\n\t\t\tif(newTitle!=null)\n\t\t\t{\n\t\t\t\tif(newTitle.gridLayout())\n\t\t\t\t{\n\t\t\t\t\tfinal PairVector<Room,int[]> rooms=CMLib.tracking().buildGridList(newRoom, newTitle.getOwnerName(), 100);\n\t\t\t\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(d==Directions.GATE)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tRoom R3=newRoom.getRoomInDir(d);\n\t\t\t\t\t\tif(R3 == null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tR3=CMLib.tracking().getCalculatedAdjacentRoom(rooms, R3, d);\n\t\t\t\t\t\t\tif(R3!=null)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tnewRoom.rawDoors()[d]=R3;\n\t\t\t\t\t\t\t\tif(R3.rawDoors()[Directions.getOpDirectionCode(d)]==null)\n\t\t\t\t\t\t\t\t\tR3.rawDoors()[Directions.getOpDirectionCode(d)]=newRoom;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCMLib.law().colorRoomForSale(newRoom, newTitle, true);\n\t\t\t\tnewTitle.updateLot(null);\n\t\t\t}\n\t\t\tnewRoom.getArea().fillInAreaRoom(newRoom);\n\t\t\tCMLib.database().DBUpdateExits(newRoom);\n\t\t\tCMLib.database().DBUpdateExits(room);\n\t\t\tnewRoom.executeMsg(mob, CMClass.getMsg(mob, newRoom, CMMsg.MSG_NEWROOM, null));\n\t\t}\n\t\treturn newRoom;\n\t}\n\n\tprotected void buildWall(Room room, final int dir)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\troom.setRawExit(dir,null);\n\t\t\tif(room.rawDoors()[dir]!=null)\n\t\t\t{\n\t\t\t\tfinal Room oroom = room.rawDoors()[dir].prepareRoomInDir(room, dir);\n\t\t\t\toroom.setRawExit(Directions.getOpDirectionCode(dir),null);\n\t\t\t\tCMLib.database().DBUpdateExits(oroom);\n\t\t\t}\n\t\t\tCMLib.database().DBUpdateExits(room);\n\t\t\tfinal LandTitle title=CMLib.law().getLandTitle(room);\n\t\t\tif(title != null)\n\t\t\t\ttitle.updateLot(null);\n\t\t}\n\t}\n\n\tprotected void buildTitle(Room room, final String designTitle)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\tRoom returnRoom = null;\n\t\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t\t{\n\t\t\t\tif(room.getRoomInDir(d)!=null)\n\t\t\t\t\treturnRoom=room.getRoomInDir(d);\n\t\t\t}\n\t\t\troom.setDisplayText(designTitle);\n\t\t\tCMLib.database().DBUpdateRoom(room);\n\t\t\tif(room instanceof GridLocale)\n\t\t\t{\n\t\t\t\t((GridLocale)room).clearGrid(returnRoom);\n\t\t\t\t((GridLocale)room).buildGrid();\n\t\t\t}\n\t\t\tfinal LandTitle T=CMLib.law().getLandTitle(room);\n\t\t\tif(T != null)\n\t\t\t\tT.updateLot(null);\n\t\t}\n\t}\n\n\tprotected void buildDesc(Room room, final int dir, final String designDescription)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\tif(room == null)\n\t\t\t\treturn;\n\t\t\tif(dir>=0)\n\t\t\t{\n\t\t\t\tExit E=room.getExitInDir(dir);\n\t\t\t\tif(E!=null)\n\t\t\t\t{\n\t\t\t\t\tif((!E.isGeneric())&&(room.getRawExit(dir)==E))\n\t\t\t\t\t{\n\t\t\t\t\t\tE=generify(E);\n\t\t\t\t\t\troom.setRawExit(dir,E);\n\t\t\t\t\t}\n\t\t\t\t\tE.setDescription(designDescription);\n\t\t\t\t\tCMLib.database().DBUpdateExits(room);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tRoom returnRoom = null;\n\t\t\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t\t\t{\n\t\t\t\t\tif(room.getRoomInDir(d)!=null)\n\t\t\t\t\t\treturnRoom=room.getRoomInDir(d);\n\t\t\t\t}\n\t\t\t\troom.setDescription(designDescription);\n\t\t\t\tif(room instanceof GridLocale)\n\t\t\t\t{\n\t\t\t\t\t((GridLocale)room).clearGrid(returnRoom);\n\t\t\t\t\t((GridLocale)room).buildGrid();\n\t\t\t\t}\n\t\t\t\tCMLib.database().DBUpdateRoom(room);\n\t\t\t\tfinal LandTitle T=CMLib.law().getLandTitle(room);\n\t\t\t\tif(T != null)\n\t\t\t\t\tT.updateLot(null);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void demolish(final MOB mob, Room room, final int dir, final String[] recipe)\n\t{\n\t\tsynchronized(CMClass.getSync("SYNC"+room.roomID()))\n\t\t{\n\t\t\troom=CMLib.map().getRoom(room);\n\t\t\tif(dir<0)\n\t\t\t{\n\n\t\t\t\tif((CMLib.law().isHomeRoomUpstairs(room))\n\t\t\t\t||(recipe[DAT_CLASS].trim().length()==0))\n\t\t\t\t{\n\t\t\t\t\tdemolishRoom(mob,room);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tconvertToPlains(room,recipe[DAT_CLASS]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\troom.setRawExit(dir,CMClass.getExit("Open"));\n\t\t\t\tif(room.rawDoors()[dir]!=null)\n\t\t\t\t{\n\t\t\t\t\troom.rawDoors()[dir].setRawExit(Directions.getOpDirectionCode(dir),CMClass.getExit("Open"));\n\t\t\t\t\tCMLib.database().DBUpdateExits(room.rawDoors()[dir]);\n\t\t\t\t}\n\t\t\t\tCMLib.database().DBUpdateExits(room);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void buildComplete(final MOB mob, final String[] recipe, final Room room, final int dir, final String designTitle, final String designDescription)\n\t{\n\t\tfinal Building doingCode = Building.valueOf(recipe[DAT_BUILDCODE]);\n\t\tswitch(doingCode)\n\t\t{\n\t\tcase DEMOLISH:\n\t\t{\n\t\t\tthis.demolish(mob, room, dir, recipe);\n\t\t\tbreak;\n\t\t}\n\t\tcase DESC:\n\t\t{\n\t\t\tthis.buildDesc(room, dir, designDescription);\n\t\t\tbreak;\n\t\t}\n\t\tcase DOOR:\n\t\t{\n\t\t\tthis.buildDoor(recipe, room, dir, CMath.s_int(recipe[RCP_LEVEL]));\n\t\t\tbreak;\n\t\t}\n\t\tcase ITEM:\n\t\t{\n\t\t\tfinal Item I=CMClass.getItem(recipe[DAT_CLASS]);\n\t\t\troom.addItem(I);\n\t\t\tI.setExpirationDate(0);\n\t\t\tbreak;\n\t\t}\n\t\tcase ROOM:\n\t\t{\n\t\t\tfinal String localeName = recipe[DAT_CLASS];\n\t\t\tfinal String spells = recipe[DAT_PROPERTIES];\n\t\t\tfinal int size = CMath.s_int(recipe[DAT_MISC]);\n\t\t\tthis.buildNewRoomType(room, localeName, spells, size);\n\t\t\tbreak;\n\t\t}\n\t\tcase DELEFFECT:\n\t\t{\n\t\t\tfinal String spells = recipe[DAT_PROPERTIES];\n\t\t\tif(dir >=0)\n\t\t\t\tthis.removeExitAbility(room, dir, spells);\n\t\t\telse\n\t\t\t\tthis.removeRoomAbility(room, dir, spells);\n\t\t\tbreak;\n\t\t}\n\t\tcase ROOMEFFECT:\n\t\t{\n\t\t\tfinal String spells = recipe[DAT_PROPERTIES];\n\t\t\tthis.buildRoomAbility(room, dir, spells);\n\t\t\tbreak;\n\t\t}\n\t\tcase EXITEFFECT:\n\t\t{\n\t\t\tfinal String spells = recipe[DAT_PROPERTIES];\n\t\t\tthis.buildExitAbility(room, dir, spells);\n\t\t\tbreak;\n\t\t}\n\t\tcase EXCAVATE:\n\t\tcase STAIRS:\n\t\t{\n\t\t\tthis.buildStairs(mob, room, dir, recipe);\n\t\t\tbreak;\n\t\t}\n\t\tcase TITLE:\n\t\t{\n\t\t\tthis.buildTitle(room, designTitle);\n\t\t\tbreak;\n\t\t}\n\t\tcase WALL:\n\t\t{\n\t\t\tthis.buildWall(room, dir);\n\t\t\tbreak;\n\t\t}\n\t\t}\n\t}\n\n\tprotected Room convertToPlains(final Room room, final String localeID)\n\t{\n\t\tfinal Room R=CMClass.getLocale(localeID);\n\t\tR.setRoomID(room.roomID());\n\t\tR.setDisplayText(room.displayText());\n\t\tR.setDescription(room.description());\n\t\tfinal Area area=room.getArea();\n\t\tif(area!=null)\n\t\t\tarea.delProperRoom(room);\n\t\tR.setArea(room.getArea());\n\t\tfor(int a=room.numEffects()-1;a>=0;a--)\n\t\t{\n\t\t\tfinal Ability A=room.fetchEffect(a);\n\t\t\tif((A!=null)\n\t\t\t&&(!A.ID().equalsIgnoreCase("Prop_Crawlspace")))\n\t\t\t{\n\t\t\t\troom.delEffect(A);\n\t\t\t\tR.addEffect(A);\n\t\t\t}\n\t\t}\n\t\tfor(int i=room.numItems()-1;i>=0;i--)\n\t\t{\n\t\t\tfinal Item I=room.getItem(i);\n\t\t\tif(I!=null)\n\t\t\t{\n\t\t\t\troom.delItem(I);\n\t\t\t\tR.addItem(I);\n\t\t\t}\n\t\t}\n\t\tfor(int m=room.numInhabitants()-1;m>=0;m--)\n\t\t{\n\t\t\tfinal MOB M=room.fetchInhabitant(m);\n\t\t\tif(M!=null)\n\t\t\t{\n\t\t\t\troom.delInhabitant(M);\n\t\t\t\tR.addInhabitant(M);\n\t\t\t\tM.setLocation(R);\n\t\t\t}\n\t\t}\n\t\tCMLib.threads().deleteTick(room,-1);\n\t\tfor(int d=0;d<R.rawDoors().length;d++)\n\t\t\tR.rawDoors()[d]=room.rawDoors()[d];\n\t\tfor(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)\n\t\t\tR.setRawExit(d,room.getRawExit(d));\n\t\tCMLib.threads().rejuv(R, Tickable.TICKID_ROOM_ITEM_REJUV);\n\t\tR.startItemRejuv();\n\t\ttry\n\t\t{\n\t\t\tfor(final Enumeration<Room> r=CMLib.map().rooms();r.hasMoreElements();)\n\t\t\t{\n\t\t\t\tfinal Room R2=r.nextElement();\n\t\t\t\tfor(int d=0;d<R2.rawDoors().length;d++)\n\t\t\t\t{\n\t\t\t\t\tif(R2.rawDoors()[d]==room)\n\t\t\t\t\t{\n\t\t\t\t\t\tR2.rawDoors()[d]=R;\n\t\t\t\t\t\tif(R2 instanceof GridLocale)\n\t\t\t\t\t\t\t((GridLocale)R2).buildGrid();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (final NoSuchElementException e)\n\t\t{\n\t\t}\n\t\tR.getArea().fillInAreaRoom(R);\n\t\tCMLib.database().DBUpdateRoom(R);\n\t\tCMLib.database().DBUpdateExits(R);\n\t\troom.destroy();\n\t\treturn R;\n\t}\n\n\tpublic boolean isHomePeerRoom(final Room R, final boolean anyType)\n\t{\n\t\treturn ifHomePeerLandTitle(R,anyType)!=null;\n\t}\n\n\tpublic boolean isHomePeerTitledRoom(final Room R, final boolean anyType)\n\t{\n\t\tfinal LandTitle title = ifHomePeerLandTitle(R,anyType);\n\t\tif(title == null)\n\t\t\treturn false;\n\t\treturn title.getOwnerName().length()>0;\n\t}\n\n\tpublic boolean countsAsACave(final Room R)\n\t{\n\t\tif(R==null)\n\t\t\treturn false;\n\t\tif((R.domainType()==Room.DOMAIN_INDOORS_CAVE)\n\t\t||((R.getAtmosphere()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_ROCK))\n\t\t\treturn true;\n\t\tif(R.domainType()==Room.DOMAIN_INDOORS_CAVE_SEAPORT)\n\t\t\treturn true;\n\t\tif(R.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)\n\t\t{\n\t\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t\t\tif(countsAsACave(R.getRoomInDir(d)))\n\t\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic LandTitle ifHomePeerLandTitle(final Room R, final boolean anyType)\n\t{\n\t\tif((R!=null)\n\t\t&&(R.ID().length()>0)\n\t\t&&(anyType||CMath.bset(R.domainType(),Room.INDOORS)))\n\t\t\treturn CMLib.law().getLandTitle(R);\n\t\treturn null;\n\t}\n\n\tpublic String establishVerb(final MOB mob, final String[] recipe)\n\t{\n\t\tString verb="";\n\t\tfinal Building doingCode = Building.valueOf(recipe[DAT_BUILDCODE]);\n\t\tif(doingCode == Building.DEMOLISH)\n\t\t{\n\t\t\tif(dir<0)\n\t\t\t{\n\t\t\t\tfinal Room R=mob.location();\n\t\t\t\tboolean roomClassFound = false;\n\t\t\t\tfor(final List<String> recipeChk : loadRecipes(getRecipeFilename()))\n\t\t\t\t{\n\t\t\t\t\tif(R.ID().equalsIgnoreCase(recipeChk.get(DAT_CLASS)))\n\t\t\t\t\t\troomClassFound=true;\n\t\t\t\t}\n\t\t\t\tif((R.domainType()==Room.DOMAIN_INDOORS_WATERSURFACE)\n\t\t\t\t||(R.domainType()==Room.DOMAIN_OUTDOORS_WATERSURFACE))\n\t\t\t\t\tverb=L("demolishing the pool");\n\t\t\t\telse\n\t\t\t\tif((R.domainType()==Room.DOMAIN_INDOORS_UNDERWATER)\n\t\t\t\t||(R.domainType()==Room.DOMAIN_OUTDOORS_UNDERWATER))\n\t\t\t\t{\n\t\t\t\t\tcommonTell(mob,null,null,L("You must demolish a pool from above."));\n\t\t\t\t\treturn "";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\tif(!roomClassFound)\n\t\t\t\t{\n\t\t\t\t\tcommonTell(mob,null,null,L("This building was not made with @x1, you can`t demolish it.",name()));\n\t\t\t\t\treturn "";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\tif(CMLib.law().isHomeRoomUpstairs(R))\n\t\t\t\t\tverb=L("demolishing the room");\n\t\t\t\telse\n\t\t\t\t\tverb=L("demolishing the roof");\n\t\t\t}\n\t\t\telse\n\t\t\t\tverb=L("demolishing the wall @x1",CMLib.directions().getInDirectionName(dir).toLowerCase());\n\t\t}\n\t\telse\n\t\t\tverb = L("building the "+recipe[DAT_DESC],CMLib.directions().getInDirectionName(dir).toLowerCase());\n\t\treturn verb;\n\t}\n\n\tprivate Set<Flag> makeFlags(final String[] recipe)\n\t{\n\t\tfinal Set<Flag> flags = new HashSet<Flag>();\n\t\tfinal String[] flagStrs = CMParms.parse(recipe[DAT_FLAG].toUpperCase()).toArray(new String[0]);\n\t\tfor(final String flag : flagStrs)\n\t\t{\n\t\t\tfinal Flag F=(Flag)CMath.s_valueOf(Flag.class, flag);\n\t\t\tif(F!=null)\n\t\t\t\tflags.add(F);\n\t\t}\n\t\treturn flags;\n\t}\n\n\t@Override\n\tpublic boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel)\n\t{\n\t\tif(checkStop(mob, commands))\n\t\t\treturn true;\n\n\t\trandomRecipeFix(mob,addRecipes(mob,loadRecipes(getRecipeFilename())),commands,0);\n\n\t\tif(commands.size()==0)\n\t\t{\n\t\t\tcommonTell(mob,L("What kind of @x1, where? Try @x2 list.",name(),CMStrings.capitalizeAndLower(this.triggerStrings()[0])));\n\t\t\treturn false;\n\t\t}\n\t\tpoundsOfMatsUsed\t= 0;\n\t\tcanBeDoneSittingDown = false;\n\t\tfinal String str=commands.get(0);\n\t\tfinal String[][] data=getRecipeData(mob);\n\t\tfinal LandTitle title = CMLib.law().getLandTitle(mob.location());\n\t\tfinal double landValue = ((title == null) ? 0 : title.getPrice()) / 100.0;\n\t\tfinal String landCurrency = CMLib.beanCounter().getCurrency(mob.location());\n\t\tfinal boolean getInfo = ("INFO").startsWith(str.toUpperCase());\n\t\tif(("LIST").startsWith(str.toUpperCase())||getInfo)\n\t\t{\n\t\t\tboolean hasValueTag = false;\n\t\t\tString mask=CMParms.combine(commands,1);\n\t\t\tif((getInfo && mask.length()==0))\n\t\t\t\tmask="ALL";\n\t\t\tint colWidth=CMLib.lister().fixColWidth(20,mob.session());\n\t\t\tfinal StringBuffer buf;\n\t\t\tif(getInfo)\n\t\t\t{\n\t\t\t\tcolWidth=CMLib.lister().fixColWidth(13,mob.session());\n\t\t\t\tbuf=new StringBuffer(CMStrings.padRight(L("Item"),colWidth) + L(" Description\\n\\r",this.getMainResourceName()));\n\t\t\t}\n\t\t\telse\n\t\t\t\tbuf=new StringBuffer(CMStrings.padRight(L("Item"),colWidth) + L(" @x1 required\\n\\r",this.getMainResourceName()));\n\t\t\tfor(int r=0;r<data.length;r++)\n\t\t\t{\n\t\t\t\tif(((data[r][DAT_BUILDERMASK].length()==0)\n\t\t\t\t\t||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))\n\t\t\t\t\t||CMSecurity.isASysOp(mob))\n\t\t\t\t&&((mask==null)\n\t\t\t\t\t||(mask.length()==0)\n\t\t\t\t\t||mask.equalsIgnoreCase("all")\n\t\t\t\t\t||CMLib.english().containsString(CMStrings.padRight(data[r][RCP_FINALNAME],colWidth),mask)))\n\t\t\t\t{\n\t\t\t\t\tbuf.append(CMStrings.padRight(data[r][RCP_FINALNAME],colWidth)+" ");\n\t\t\t\t\tif(getInfo)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(DAT_DESCRIPTION < data[r].length)\n\t\t\t\t\t\t\tbuf.append(data[r][DAT_DESCRIPTION]).append("\\n\\r");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tString material=data[r][DAT_WOODTYPE];\n\t\t\t\t\t\tif(material.equalsIgnoreCase("VALUE"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thasValueTag=true;\n\t\t\t\t\t\t\tfinal String woodStr = data[r][DAT_WOOD];\n\t\t\t\t\t\t\tif(CMath.isInteger(woodStr))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint wood=CMath.s_int(woodStr);\n\t\t\t\t\t\t\t\twood=adjustWoodRequired(wood,mob);\n\t\t\t\t\t\t\t\tif(title == null)\n\t\t\t\t\t\t\t\t\tbuf.append(wood+"% ??\\n\\r");\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tbuf.append(CMLib.beanCounter().nameCurrencyLong(landCurrency, landValue * wood)).append("\\n\\r");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tbuf.append("??\\n\\r");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\tif(material.equalsIgnoreCase("MONEY"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfinal String woodStr = data[r][DAT_WOOD];\n\t\t\t\t\t\t\tif(CMath.isInteger(woodStr))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint wood=CMath.s_int(woodStr);\n\t\t\t\t\t\t\t\twood=adjustWoodRequired(wood,mob);\n\t\t\t\t\t\t\t\tbuf.append(CMLib.beanCounter().nameCurrencyLong(landCurrency, wood)).append("\\n\\r");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tbuf.append("??\\n\\r");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfinal String wood=getComponentDescription(mob,Arrays.asList(data[r]),DAT_WOOD);\n\t\t\t\t\t\t\tif(wood.length()>5)\n\t\t\t\t\t\t\t\tmaterial="";\n\t\t\t\t\t\t\tif(material.equalsIgnoreCase("wooden"))\n\t\t\t\t\t\t\t\tmaterial="wood";\n\t\t\t\t\t\t\tbuf.append(wood+" "+material.toLowerCase()+"\\n\\r");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif((title == null) && hasValueTag)\n\t\t\t\tbuf.append(L("\\n\\rYou can\'t tell anything about some costs from this location.\\n\\r"));\n\t\t\tcommonTell(mob,buf.toString());\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\tif(("SURVEY").startsWith(str.toUpperCase()))\n\t\t{\n\t\t\t//TODO: finish this\n\t\t\t//TODO: OK, but what\'s this do again?\n\t\t}\n\n\t\tdesignTitle="";\n\t\tdesignDescription="";\n\t\tString startStr=null;\n\t\tint duration=15;\n\t\trecipe = null;\n\t\tBuilding doingCode = null;\n\t\tdir=-1;\n\n\t\troom=null;\n\t\tmessedUp=false;\n\n\t\tfinal String firstWord=commands.get(0);\n\n\t\thelpingAbility=null;\n\n\t\tif(firstWord.equalsIgnoreCase("help"))\n\t\t{\n\t\t\tmessedUp=!proficiencyCheck(mob,0,auto);\n\t\t\tduration=25;\n\t\t\tcommands.remove(0);\n\t\t\tfinal MOB targetMOB=getTarget(mob,commands,givenTarget,false,true);\n\t\t\tif(targetMOB==null)\n\t\t\t\treturn false;\n\t\t\tif(targetMOB==mob)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You can not do that."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\thelpingAbility=targetMOB.fetchEffect(ID());\n\t\t\tif(helpingAbility==null)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("@x1 is not building anything.",targetMOB.Name()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(!super.invoke(mob,commands,givenTarget,auto,asLevel))\n\t\t\t{\n\t\t\t\thelpingAbility=null;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\thelping=true;\n\t\t\tverb=L("helping @x1 with @x2",targetMOB.name(),helpingAbility.name());\n\t\t\tstartStr=L("<S-NAME> start(s) @x1",verb);\n\t\t\tfinal CMMsg msg=CMClass.getMsg(mob,null,this,getActivityMessageType(),startStr+".");\n\t\t\tif(mob.location().okMessage(mob,msg))\n\t\t\t{\n\t\t\t\tmob.location().send(mob,msg);\n\t\t\t\tbeneficialAffect(mob,mob,asLevel,duration);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tboolean canBuild=CMLib.law().doesOwnThisLand(mob,mob.location()) || CMSecurity.isAllowed(mob, mob.location(), CMSecurity.SecFlag.CMDROOMS);\n\t\tfinal String allWords=CMParms.combine(commands,0).toUpperCase();\n\t\tfor(int r=0;r<data.length;r++)\n\t\t{\n\t\t\tfinal Building buildCode = Building.valueOf(data[r][DAT_BUILDCODE]);\n\t\t\tif((data[r][0].toUpperCase().equals(allWords))\n\t\t\t&&((data[r][DAT_BUILDERMASK].length()==0)\n\t\t\t\t||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))\n\t\t\t\t||CMSecurity.isASysOp(mob)))\n\t\t\t{\n\t\t\t\tdoingCode=buildCode;\n\t\t\t\trecipe = data[r];\n\t\t\t}\n\t\t}\n\t\tif((doingCode==null) || (recipe == null))\n\t\t{\n\t\t\tfor(int r=0;r<data.length;r++)\n\t\t\t{\n\t\t\t\tfinal Building buildCode = Building.valueOf(data[r][DAT_BUILDCODE]);\n\t\t\t\tif((data[r][0].toUpperCase().startsWith(allWords))\n\t\t\t\t&&((data[r][DAT_BUILDERMASK].length()==0)\n\t\t\t\t\t||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))\n\t\t\t\t\t||CMSecurity.isASysOp(mob)))\n\t\t\t\t{\n\t\t\t\t\tdoingCode=buildCode;\n\t\t\t\t\trecipe = data[r];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif((doingCode==null) || (recipe == null))\n\t\t{\n\t\t\tfor(int r=0;r<data.length;r++)\n\t\t\t{\n\t\t\t\tfinal Building buildCode = Building.valueOf(data[r][DAT_BUILDCODE]);\n\t\t\t\tif((data[r][0].toUpperCase().startsWith(firstWord.toUpperCase()))\n\t\t\t\t&&((data[r][DAT_BUILDERMASK].length()==0)\n\t\t\t\t\t||(CMLib.masking().maskCheck(data[r][DAT_BUILDERMASK], mob, false))\n\t\t\t\t\t||CMSecurity.isASysOp(mob)))\n\t\t\t\t{\n\t\t\t\t\tdoingCode=buildCode;\n\t\t\t\t\trecipe = data[r];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif((doingCode == null)||(recipe == null))\n\t\t{\n\t\t\tcommonTell(mob,L("\'@x1\' is not a valid @x2 project.  Try LIST.",firstWord,name()));\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal Set<Flag> flags = makeFlags(recipe);\n\n\t\tif((mob.location()!=null) // this is correct!\n\t\t&&((mob.location() instanceof Boardable) || (mob.location().getArea() instanceof Boardable)))\n\t\t{\n\t\t\tcommonTell(mob,L("You may not do @x1 projects here.",name()));\n\t\t\treturn false;\n\t\t}\n\t\tfinal String dirName=commands.get(commands.size()-1);\n\t\tdir=CMLib.directions().getGoodDirectionCode(dirName);\n\n\t\tif((doingCode == Building.DEMOLISH)&&(dirName.equalsIgnoreCase("roof")||(dirName.equalsIgnoreCase("ceiling"))))\n\t\t{\n\t\t\tthis.canBeDoneSittingDown = true;\n\t\t\tfinal Room upRoom=mob.location().getRoomInDir(Directions.UP);\n\t\t\tif(isHomePeerRoom(upRoom,flags.contains(Flag.CAVEONLY)))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You need to demolish the upstairs rooms first."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(flags.contains(Flag.CAVEONLY))\n\t\t\t{\n\t\t\t\tif((!countsAsACave(mob.location()))&&(!countsAsACave(upRoom)))\n\t\t\t\t{\n\t\t\t\t\tcommonTell(mob,L("This can only be done underground."));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(mob.location().domainType() == Room.DOMAIN_INDOORS_CAVE)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("A cave can not have its roof demolished."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(!CMath.bset(mob.location().domainType(), Room.INDOORS))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("There is no ceiling here!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(CMLib.law().isHomeRoomUpstairs(mob.location()))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You can\'t demolish a ceiling in an upstairs room.  Try demolishing the room."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdir=-1;\n\t\t}\n\t\telse\n\t\tif((doingCode == Building.DEMOLISH)&&(dirName.equalsIgnoreCase("room")))\n\t\t{\n\t\t\tif(flags.contains(Flag.CAVEONLY))\n\t\t\t{\n\t\t\t\tif(!countsAsACave(mob.location()))\n\t\t\t\t{\n\t\t\t\t\tcommonTell(mob,L("This can only be done underground."));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.canBeDoneSittingDown = true;\n\t\t\tif((!CMLib.law().doesOwnThisLand(mob, mob.location()))\n\t\t\t&&(!CMSecurity.isAllowed(mob, mob.location(), CMSecurity.SecFlag.CMDROOMS))\n\t\t\t&&(title!=null)\n\t\t\t&&(title.getOwnerName().length()>0))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You can\'t demolish property you don\'t own."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif((title==null)||(!title.allowsExpansionConstruction()))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You aren\'t permitted to demolish this room."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(!flags.contains(Flag.CAVEONLY))\n\t\t\t{\n\t\t\t\tif(!CMLib.law().isHomeRoomUpstairs(mob.location()))\n\t\t\t\t{\n\t\t\t\t\tcommonTell(mob,L("You can only demolish upstairs/downstairs rooms.  You might try just demolishing the ceiling/roof?"));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint numAdjacentProperties=0;\n\t\t\tfor(int d=0;d<Directions.NUM_DIRECTIONS();d++)\n\t\t\t{\n\t\t\t\tfinal Room adjacentRoom=mob.location().getRoomInDir(d);\n\t\t\t\tif(isHomePeerTitledRoom(adjacentRoom,flags.contains(Flag.CAVEONLY)))\n\t\t\t\t{\n\t\t\t\t\tnumAdjacentProperties++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(numAdjacentProperties>1)\n\t\t\t{\n\t\t\t\tmob.tell(L("You can not demolish a room if there is more than one room adjacent to it.  Demolish those first."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdir=-1;\n\t\t\tcanBuild=true;\n\t\t}\n\t\telse\n\t\tif(((dir<0)||(dir==Directions.UP)||(dir==Directions.DOWN))\n\t\t&&(flags.contains(Flag.DIR)))\n\t\t{\n\t\t\tcommonTell(mob,L("A valid direction in which to build must also be specified."));\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\tif((dir<0)\n\t\t&&(flags.contains(Flag.DIRUPDOWN)))\n\t\t{\n\t\t\tcommonTell(mob,L("A valid direction in which to build must also be specified."));\n\t\t\treturn false;\n\t\t}\n\n\t\tif((flags.contains(Flag.NOWALL))\n\t\t&&(dir>=0)\n\t\t&&(mob.location().getExitInDir(dir)==null))\n\t\t{\n\t\t\tcommonTell(mob,L("There is a wall that way that needs to be demolished first."));\n\t\t\treturn false;\n\t\t}\n\n\t\tint woodRequired=adjustWoodRequired(CMath.s_int(recipe[DAT_WOOD]),mob);\n\t\tif(((mob.location().domainType()&Room.INDOORS)==0)\n\t\t&&(flags.contains(Flag.INDOOR)))\n\t\t{\n\t\t\tcommonTell(mob,L("That can only be built after a roof, which includes the frame."));\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\tif(((mob.location().domainType()&Room.INDOORS)>0)\n\t\t&&(flags.contains(Flag.OUTDOOR)))\n\t\t{\n\t\t\tcommonTell(mob,L("That can only be built outdoors!"));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(doingCode == Building.STAIRS)\n\t\t{\n\t\t\tif((dir!=Directions.UP)&&(dir!=Directions.DOWN))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("A valid direction in which to build must also be specified.  Try UP or DOWN."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(doingCode == Building.EXCAVATE)\n\t\t{\n\t\t\tif(dir==Directions.DOWN)\n\t\t\t{\n\t\t\t\tswitch(mob.location().domainType())\n\t\t\t\t{\n\t\t\t\tcase Room.DOMAIN_INDOORS_METAL:\n\t\t\t\tcase Room.DOMAIN_INDOORS_STONE:\n\t\t\t\tcase Room.DOMAIN_INDOORS_WOOD:\n\t\t\t\t{\n\t\t\t\t\tfinal int floorNumber = this.findFloorNumber(mob.location(), new HashSet<Room>(), 1);\n\t\t\t\t\tif(floorNumber > 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tcommonTell(mob,L("You cannot excavate from above the ground."));\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase Room.DOMAIN_OUTDOORS_AIR:\n\t\t\t\tcase Room.DOMAIN_OUTDOORS_UNDERWATER:\n\t\t\t\tcase Room.DOMAIN_OUTDOORS_WATERSURFACE:\n\t\t\t\tcase Room.DOMAIN_INDOORS_AIR:\n\t\t\t\tcase Room.DOMAIN_INDOORS_UNDERWATER:\n\t\t\t\tcase Room.DOMAIN_INDOORS_WATERSURFACE:\n\t\t\t\t\tcommonTell(mob,L("You can only excavate down into the ground."));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tflags.remove(Flag.CAVEONLY);  // caveonly only matters if complex DOWN rules don\'t apply.\n\t\t\t}\n\t\t}\n\n\t\tif((doingCode == Building.STAIRS)||(doingCode == Building.EXCAVATE))\n\t\t{\n\t\t\tif((title==null)||(!title.allowsExpansionConstruction()))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("The title here does not permit the building of new places."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif((!CMath.bset(mob.location().domainType(), Room.INDOORS))&&(dir==Directions.UP))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You need to build a ceiling (or roof) first!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfinal Room inR=mob.location().getRoomInDir(dir);\n\t\t\tif(inR!=null)\n\t\t\t{\n\t\t\t\tif(dir == Directions.UP)\n\t\t\t\t\tcommonTell(mob,L("There is already something up here."));\n\t\t\t\telse\n\t\t\t\tif(dir == Directions.DOWN)\n\t\t\t\t\tcommonTell(mob,L("There is already something down here."));\n\t\t\t\telse\n\t\t\t\t\tcommonTell(mob,L("There is already something over there."));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfinal List<Room> allRooms = title.getConnectedPropertyRooms();\n\t\t\tif(allRooms.size()>0)\n\t\t\t{\n\t\t\t\tfinal Ability cap = allRooms.get(0).fetchEffect("Prop_ReqCapacity");\n\t\t\t\tif(cap != null)\n\t\t\t\t{\n\t\t\t\t\tfinal int roomLimit = CMParms.getParmInt(cap.text(),"rooms",Integer.MAX_VALUE);\n\t\t\t\t\tif(allRooms.size() >= roomLimit)\n\t\t\t\t\t{\n\t\t\t\t\t\tcommonTell(mob,L("You are not allowed to add more rooms."));\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif(doingCode == Building.WALL)\n\t\t{\n\t\t\tfinal Room nextRoom=mob.location().getRoomInDir(dir);\n\t\t\tif((nextRoom!=null)&&(CMLib.law().getLandTitle(nextRoom)==null))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You can not build a wall blocking off the main entrance!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(mob.location().getExitInDir(dir)==null)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("There is already a wall in that direction!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.NODOWN))\n\t\t{\n\t\t\tfinal Room nextRoom=mob.location().getRoomInDir(Directions.DOWN);\n\t\t\tfinal Exit exitRoom=mob.location().getExitInDir(Directions.DOWN);\n\t\t\tif((nextRoom!=null)||(exitRoom!=null))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You may not build that here!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.DOWNONLY))\n\t\t{\n\t\t\tfinal Room nextRoom=mob.location().getRoomInDir(Directions.DOWN);\n\t\t\tfinal Exit exitRoom=mob.location().getExitInDir(Directions.DOWN);\n\t\t\tif((nextRoom!=null)&&(exitRoom!=null)&&(nextRoom.roomID().length()>0))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You may not build that here!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdir=Directions.DOWN;\n\t\t}\n\n\t\tif(flags.contains(Flag.UPONLY))\n\t\t{\n\t\t\tfinal Room nextRoom=mob.location().getRoomInDir(Directions.UP);\n\t\t\tfinal Exit exitRoom=mob.location().getExitInDir(Directions.UP);\n\t\t\tif((nextRoom!=null)&&(exitRoom!=null)&&(nextRoom.roomID().length()>0))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You may not build that here!"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdir=Directions.UP;\n\t\t}\n\n\t\tif(flags.contains(Flag.CAVEONLY))\n\t\t{\n\t\t\tif(!countsAsACave(mob.location()))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("This can only be done underground."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.WATERONLY))\n\t\t{\n\t\t\tif((mob.location().domainType()!=Room.DOMAIN_OUTDOORS_WATERSURFACE)\n\t\t\t&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_WATERSURFACE)\n\t\t\t&&(mob.location().domainType()!=Room.DOMAIN_OUTDOORS_UNDERWATER)\n\t\t\t&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_UNDERWATER))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("This can only be done in water."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.WATERSURFACEONLY))\n\t\t{\n\t\t\tif((mob.location().domainType()!=Room.DOMAIN_OUTDOORS_WATERSURFACE)\n\t\t\t&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_WATERSURFACE))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("This can only be done on the water."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.UNDERWATERONLY))\n\t\t{\n\t\t\tif((mob.location().domainType()!=Room.DOMAIN_OUTDOORS_UNDERWATER)\n\t\t\t&&(mob.location().domainType()!=Room.DOMAIN_INDOORS_UNDERWATER))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("This can only be done under the water."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.SALTWATER))\n\t\t{\n\t\t\tif((mob.location().getAtmosphere()!=RawMaterial.RESOURCE_SALTWATER)\n\t\t\t&&((!(mob.location() instanceof Drink))||(((Drink)mob.location()).liquidType()!=RawMaterial.RESOURCE_SALTWATER)))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("This can only be done in salt water."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(flags.contains(Flag.FRESHWATER))\n\t\t{\n\t\t\tif((mob.location().getAtmosphere()!=RawMaterial.RESOURCE_FRESHWATER)\n\t\t\t&&((!(mob.location() instanceof Drink))||(((Drink)mob.location()).liquidType()!=RawMaterial.RESOURCE_SALTWATER)))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("This can only be done in fresh water."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif(doingCode == Building.TITLE)\n\t\t{\n\t\t\tif(!canDescTitleHere(mob.location()))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You can\'t do that here."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString titleStr=CMParms.combine(commands,1);\n\t\t\tif(titleStr.length()==0)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("A title must be specified."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttitleStr=CMLib.coffeeFilter().secondaryUserInputFilter(titleStr);\n\t\t\tif(titleStr.length()>253)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("That title is too long."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfinal TrackingLibrary.TrackingFlags trackingFlags=CMLib.tracking().newFlags();\n\t\t\tfinal List<Room> checkSet=CMLib.tracking().getRadiantRooms(mob.location(),trackingFlags,20);\n\t\t\tfor (final Room room2 : checkSet)\n\t\t\t{\n\t\t\t\tfinal Room R=CMLib.map().getRoom(room2);\n\t\t\t\tif(R!=null)\n\t\t\t\t{\n\t\t\t\t\tif(R.displayText(mob).equalsIgnoreCase(titleStr))\n\t\t\t\t\t{\n\t\t\t\t\t\tcommonTell(mob,L("That title has already been taken.  Choose another."));\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tdesignTitle=titleStr;\n\t\t}\n\t\telse\n\t\tif(doingCode == Building.DESC)\n\t\t{\n\t\t\tif(commands.size()<3)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You must specify an exit direction or the word room, followed by a description for it."));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif(!canDescTitleHere(mob.location()))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You can\'t do that here."));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif(CMLib.directions().getGoodDirectionCode(commands.get(1))>=0)\n\t\t\t{\n\t\t\t\tdir=CMLib.directions().getGoodDirectionCode(commands.get(1));\n\t\t\t\tif(mob.location().getExitInDir(dir)==null)\n\t\t\t\t{\n\t\t\t\t\tcommonTell(mob,L("There is no exit @x1 to describe.",CMLib.directions().getInDirectionName(dir)));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tcommands.remove(1);\n\t\t\t}\n\t\t\telse\n\t\t\tif(!commands.get(1).equalsIgnoreCase("room"))\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("\'@x1\' is neither the word room, nor an exit direction.",(commands.get(1))));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcommands.remove(1);\n\n\t\t\tString descStr=CMParms.combine(commands,1);\n\t\t\tdescStr=CMLib.coffeeFilter().secondaryUserInputFilter(descStr);\n\t\t\tif(descStr.length()==0)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("A description must be specified."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdesignDescription=descStr;\n\t\t}\n\n\t\tint[][] idata;\n\t\tif(recipe[DAT_WOODTYPE].equalsIgnoreCase("MONEY"))\n\t\t{\n\t\t\tidata=null;\n\t\t\tint wood=CMath.s_int(recipe[DAT_WOOD]);\n\t\t\twood=adjustWoodRequired(wood,mob);\n\t\t\tif(CMLib.beanCounter().getTotalAbsoluteValue(mob, landCurrency) < wood)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You\'ll need @x1 to do that.",CMLib.beanCounter().nameCurrencyLong(landCurrency, wood)));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\twoodRequired=0;\n\t\t}\n\t\telse\n\t\tif(recipe[DAT_WOODTYPE].equalsIgnoreCase("VALUE"))\n\t\t{\n\t\t\tidata=null;\n\t\t\tint wood=CMath.s_int(recipe[DAT_WOOD]);\n\t\t\twood=adjustWoodRequired(wood,mob);\n\t\t\tfinal double roomValue = landValue * wood;\n\t\t\tif(CMLib.beanCounter().getTotalAbsoluteValue(mob, landCurrency) < roomValue)\n\t\t\t{\n\t\t\t\tcommonTell(mob,L("You\'ll need @x1 to do that.",CMLib.beanCounter().nameCurrencyLong(landCurrency, roomValue)));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\twoodRequired=0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tidata=this.getBasicMaterials(mob, woodRequired, recipe[DAT_WOODTYPE]);\n\n\t\t\tif(idata==null)\n\t\t\t\treturn false;\n\t\t\twoodRequired=idata[0][FOUND_AMT];\n\t\t}\n\n\t\tif(!canBuild)\n\t\t{\n\t\t\tif((dir>=0)&&(flags.contains(Flag.DIR)))\n\t\t\t{\n\t\t\t\tfinal Room R=mob.location().getRoomInDir(dir);\n\t\t\t\tif((R!=null)\n\t\t\t\t&&(CMLib.law().doesOwnThisLand(mob,R)))\n\t\t\t\t\tcanBuild=true;\n\t\t\t}\n\t\t}\n\t\tif(!canBuild)\n\t\t{\n\t\t\tcommonTell(mob,L("You\'ll need the permission of the owner to do that."));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(!super.invoke(mob,commands,givenTarget,auto,asLevel))\n\t\t\treturn false;\n\n\t\troom=mob.location();\n\t\tif(room.getGridParent() != null)\n\t\t\troom = room.getGridParent();\n\n\t\tif((woodRequired>0)&&(idata!=null))\n\t\t\tCMLib.materials().destroyResourcesValue(mob.location(),woodRequired,idata[0][FOUND_CODE],idata[0][FOUND_SUB],0,0);\n\t\telse\n\t\tif(recipe[DAT_WOODTYPE].equalsIgnoreCase("VALUE"))\n\t\t{\n\t\t\tint wood=CMath.s_int(recipe[DAT_WOOD]);\n\t\t\twood=adjustWoodRequired(wood,mob);\n\t\t\tfinal double roomValue = landValue * wood;\n\t\t\tCMLib.beanCounter().subtractMoney(mob, landCurrency, roomValue);\n\t\t}\n\t\telse\n\t\tif(recipe[DAT_WOODTYPE].equalsIgnoreCase("MONEY"))\n\t\t{\n\t\t\tint wood=CMath.s_int(recipe[DAT_WOOD]);\n\t\t\twood=adjustWoodRequired(wood,mob);\n\t\t\tCMLib.beanCounter().subtractMoney(mob, landCurrency, wood);\n\t\t}\n\n\t\tverb = establishVerb(mob, recipe);\n\t\tif(verb.length()==0)\n\t\t\treturn false;\n\t\tmessedUp=!proficiencyCheck(mob,0,auto);\n\t\tstartStr=L("<S-NAME> start(s) @x1",verb);\n\t\tplaySound=this.getSoundName();\n\t\tduration=getDuration(CMath.s_int(recipe[RCP_TICKS]),mob,CMath.s_int(recipe[RCP_LEVEL]),10);\n\t\tpoundsOfMatsUsed += woodRequired;\n\n\t\tfinal CMMsg msg=CMClass.getMsg(mob,null,this,getActivityMessageType(),startStr+".");\n\t\tif(mob.location().okMessage(mob,msg))\n\t\t{\n\t\t\tmob.location().send(mob,msg);\n\t\t\tbeneficialAffect(mob,mob,asLevel,duration);\n\t\t}\n\t\treturn true;\n\t}\n}\n', 'message': '\n\ngit-svn-id: svn://192.168.1.10/public/CoffeeMud@15018 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'author_name': 'bozimmerman', 'lang': 'java'}