/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Behaviors;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMSecurity.DbgFlag;
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.Libraries.interfaces.DatabaseEngine.PlayerData;
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.*;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/*
   Copyright 2002-2016 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.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class Arrest extends StdBehavior implements LegalBehavior
{
	@Override
	public String ID()
	{
		return "Arrest";
	}

	@Override
	public long flags()
	{
		return Behavior.FLAG_LEGALBEHAVIOR;
	}

	@Override
	protected int canImproveCode()
	{
		return Behavior.CAN_AREAS;
	}

	protected String			lastAreaName	= null;

	protected boolean			loadAttempt		= false;
	protected Map<MOB, Double>	finesAssessed	= new Hashtable<MOB, Double>();

	@Override
	public boolean isFullyControlled()
	{
		return true;
	}

	protected String getLawParms()
	{
		final String parms=getParms();
		final int x=parms.indexOf(';');
		if(x>=0)
			return parms.substring(0, x).trim();
		return parms;
	}

	public final String getExtraLawString()
	{
		final String extraLawString=getParms();
		final int x=extraLawString.indexOf(';');
		if(x>=0)
		{
			return extraLawString.substring(x+1).trim();
		}
		return "";
	}

	public final Properties getExtraLawParms()
	{
		final String extraLawString=getExtraLawString();
		final Properties p=new Properties();
		if(extraLawString.length()>0)
			p.putAll(CMParms.parseEQParms(extraLawString));
		return p;
	}

	@Override
	public String accountForYourself()
	{
		return "legaliness";
	}

	public void DebugLogLostConvicts(String lead, LegalWarrant W, MOB officer)
	{
		final StringBuilder errLogMsg=new StringBuilder("("+lastAreaName+"): ");
		errLogMsg.append(!W.criminal().location().isInhabitant(officer)?L("AE1 "):"");
		errLogMsg.append(W.criminal().amDead()?L("AE2 "):"");
		errLogMsg.append(!CMLib.flags().isAliveAwakeMobile(W.criminal(),true)?L("AE3 "):"");
		errLogMsg.append(!CMLib.flags().isInTheGame(W.criminal(),true)?L("AE4 "):"");
		errLogMsg.append(W.crime().equalsIgnoreCase("pardoned")?L("AE5 "):"");
		errLogMsg.append(!((W.travelAttemptTime()==0)||((System.currentTimeMillis()-W.travelAttemptTime())<(5*60*1000)))?L("AE6 "):"");
		errLogMsg.append(!CMLib.flags().isAliveAwakeMobile(officer,true)?L("AE7 "):"");
		errLogMsg.append(!CMLib.flags().isBound(W.criminal())?L("AE8 "):"");
		if(CMSecurity.isDebugging(DbgFlag.ARREST))
			Log.debugOut("Arrest",lead+errLogMsg.toString());
	}

	@Override
	public boolean frame(Area myArea, MOB accused, MOB framed)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		LegalWarrant W=null;
		if(laws!=null)
		{
			for(int i=0;(W=laws.getWarrant(accused,i))!=null;i++)
			{
				if(W.criminal()==accused)
				{
					W.setCriminal(framed);
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean arrest(Area myArea, MOB officer, MOB accused)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		final LegalWarrant W=(laws!=null)?laws.getWarrant(accused,0):null;
		if((W!=null)&&((W.arrestingOfficer()==null)||(W.arrestingOfficer().location()!=accused.location())))
		{
			W.setArrestingOfficer(myArea,officer);
			CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),
					L("You are under arrest @x1! Sit down on the ground immediately!",restOfCharges(laws,W.criminal())),false,false);
			W.setState(Law.STATE_ARRESTING);
			return true;
		}
		return false;
	}

	@Override
	public int revoltChance()
	{
		return 0;
	}

	@Override
	public Law legalInfo(Area myArea)
	{
		if(!theLawIsEnabled())
			return null;
		return getLaws(myArea,false);
	}

	@Override
	public boolean isElligibleOfficer(Area myArea, MOB mob)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if((mob.isMonster())
		&&(mob.location()!=null)
		&&(laws!=null)
		&&(isElligibleOfficer(laws,mob,mob.location().getArea())))
			return true;
		return false;
	}

	@Override
	public boolean hasWarrant(Area myArea, MOB accused)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		return (laws!=null)?((laws.getWarrant(accused,0))!=null):false;
	}

	@Override
	public boolean isAnyOfficer(Area myArea, MOB mob)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if((mob.isMonster())
		&&(mob.location()!=null)
		&&(laws!=null)
		&&(isAnyKindOfOfficer(laws,mob)))
			return true;
		return false;
	}

	@Override
	public boolean isJudge(Area myArea, MOB mob)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if((mob.isMonster())
		&&(mob.location()!=null)
		&&(laws!=null)
		&&(isTheJudge(laws,mob)))
			return true;
		return false;
	}

	@Override
	public void modifyAssessedFines(double d, MOB mob)
	{
		final Double D=finesAssessed.get(mob);
		if(D!=null)
			finesAssessed.remove(mob);
		if(d>0)
			finesAssessed.put(mob,Double.valueOf(d));
	}

	@Override
	public double finesOwed(MOB mob)
	{
		if(!theLawIsEnabled())
			return 0.0;
		final Double D=finesAssessed.get(mob);
		if(D!=null)
			return D.doubleValue();
		return 0.0;
	}

	@Override
	public boolean updateLaw(Area myArea)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if(laws!=null)
		{
			laws.resetLaw();
			if(getLawParms().equalsIgnoreCase("custom")
			&&(myArea!=null))
			{
				CMLib.database().DBReCreateData(myArea.Name(),"ARREST",myArea.Name()+"/ARREST",laws.rawLawString());
				return true;
			}
		}
		return false;
	}

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

	@Override
	public String conquestInfo(Area myArea)
	{
		return "";
	}

	@Override
	public int controlPoints()
	{
		return 0;
	}

	@Override
	public void setControlPoints(String clanID, int newControlPoints)
	{
	}

	@Override
	public int getControlPoints(String clanID)
	{
		return 0;
	}

	@Override
	public List<MOB> getCriminals(Area myArea, String searchStr)
	{
		final Vector<MOB> V=new Vector<MOB>();
		if(!theLawIsEnabled())
			return V;
		final Law laws=getLaws(myArea,false);
		final boolean debugging=CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST);
		for(final LegalWarrant W : laws.warrants())
		{
			if((isStillACrime(W,debugging))
			&&((searchStr==null)||(CMLib.english().containsString(W.criminal().name(),searchStr)))
			&&(!V.contains(W.criminal())))
				V.addElement(W.criminal());
		}
		return V;
	}

	@Override
	public List<LegalWarrant> getWarrantsOf(Area myArea, MOB accused)
	{
		final Vector<LegalWarrant> V=new Vector<LegalWarrant>();
		if(!theLawIsEnabled())
			return V;
		final Law laws=getLaws(myArea,false);
		final boolean debugging=CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST);
		for(final LegalWarrant W : laws.warrants())
		{
			if((isStillACrime(W,debugging))&&((accused==null)||(W.criminal()==accused)))
				V.addElement(W);
		}
		return V;
	}

	public boolean addWarrant(Law laws, LegalWarrant W)
	{
		if(!theLawIsEnabled())
			return false;
		if((laws!=null)&&(!laws.warrants().contains(W)))
		{
			final Room R=CMLib.map().roomLocation(W.criminal());
			if(R!=null)
			{
				MOB accuser=W.witness();
				if(accuser==null)
					accuser=W.victim();
				if(accuser==null)
					accuser=W.criminal();
				final CMMsg msg=CMClass.getMsg(accuser, W.criminal(), W.victim(), 
						CMMsg.MASK_ALWAYS|CMMsg.MSG_LEGALWARRANT, CMMsg.MSG_LEGALWARRANT, CMMsg.MSG_LEGALWARRANT, W.crime());
				if(R.okMessage(W.criminal(),msg))
					R.send(W.criminal(), msg);
				else
					return false;
			}
			laws.warrants().add(W);
			if(W.criminal()!=null)
			{
				final List<String> channels=CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.WARRANTS);
				for(int i=0;i<channels.size();i++)
					CMLib.commands().postChannel(channels.get(i),null,L("@x1 has been accused of @x2.",W.criminal().name(),fixCharge(W)),true);
			}
			return true;
		}
		return false;
	}

	@Override
	public boolean addWarrant(Area myArea, LegalWarrant W)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		return addWarrant(laws,W);
	}

	@Override
	public boolean addWarrant(Area myArea, MOB accused, MOB victim, String crimeLocs, String crimeFlags, String crime, String sentence, String warnMsg)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if(laws!=null)
			return fillOutWarrant(accused,laws,myArea,victim,crimeLocs,crimeFlags,crime,sentence,warnMsg);
		return false;
	}

	@Override
	public boolean deleteWarrant(Area myArea, LegalWarrant W)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if((laws!=null)&&(laws.warrants().contains(W)))
		{
			laws.warrants().remove(W);
			return true;
		}
		return false;
	}

	@Override
	public boolean aquit(Area myArea, MOB accused, String[] acquittableLaws)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if(laws!=null)
		{
			final boolean debugging=CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST);
			String[] info=null;
			if(acquittableLaws!=null)
			{
				for (final String brokenLaw : acquittableLaws)
				{
					if((laws.basicCrimes().containsKey(brokenLaw))&&(laws.basicCrimes().get(brokenLaw) !=null))
					{
						info=laws.basicCrimes().get(brokenLaw);
						break; 
					}
					else
					if((laws.taxLaws().containsKey(brokenLaw))&&(laws.taxLaws().get(brokenLaw) instanceof String[]))
					{
						info=(String[])laws.taxLaws().get(brokenLaw);
						break; 
					}
					else
					if((laws.abilityCrimes().containsKey(brokenLaw))&&(laws.abilityCrimes().get(brokenLaw) !=null))
					{
						info=laws.abilityCrimes().get(brokenLaw);
						break; 
					}
				}
				if(info==null)
					return false;
			}
			for(final LegalWarrant W : laws.warrants())
			{
				if((isStillACrime(W,debugging))
				&&(W.criminal()==accused)
				&&((info==null)||(W.crime().equalsIgnoreCase(info[Law.BIT_CRIMENAME]))))
				{
					laws.warrants().remove(W);
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean isJailRoom(Area myArea, List<Room> jails)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if(laws!=null)
		{
			final List<Room> rooms=getRooms(myArea,laws.jailRooms());
			boolean answer=false;
			for(int i=0;i<jails.size();i++)
				answer=answer||rooms.contains(jails.get(i));
			return answer;
		}
		return false;
	}
	@Override
	public boolean accuse(Area myArea, MOB accused, MOB victim, String[] accusableLaws)
	{
		if(!theLawIsEnabled())
			return false;
		final Law laws=getLaws(myArea,false);
		if(laws!=null)
		{
			for (final String brokenLaw : accusableLaws)
			{
				String[] info=null;
				if((laws.basicCrimes().containsKey(brokenLaw))&&(laws.basicCrimes().get(brokenLaw) !=null))
					info=laws.basicCrimes().get(brokenLaw);
				else
				if((laws.taxLaws().containsKey(brokenLaw))&&(laws.taxLaws().get(brokenLaw) instanceof String[]))
					info=(String[])laws.taxLaws().get(brokenLaw);
				else
				if((laws.abilityCrimes().containsKey(brokenLaw))&&(laws.abilityCrimes().get(brokenLaw) !=null))
					info=laws.abilityCrimes().get(brokenLaw);
				if(info!=null)
				{
					if((info[Law.BIT_CRIMENAME]!=null)
					&&(info[Law.BIT_CRIMENAME].length()>0))
					{
						final boolean kaplah=
							fillOutWarrant(accused,
									laws,
									myArea,
									(victim==accused)?null:victim,
									info[Law.BIT_CRIMELOCS],
									info[Law.BIT_CRIMEFLAGS],
									info[Law.BIT_CRIMENAME],
									info[Law.BIT_SENTENCE],
									info[Law.BIT_WARNMSG]);
						if(kaplah)
							return true;
					}
				}
			}
		}
		return false;
	}

	@Override
	public void setParms(String newParms)
	{
		super.setParms(newParms);
		loadAttempt=false;
	}

	protected boolean defaultModifiableNames()
	{
		return true;
	}

	@Override
	public List<String> externalFiles()
	{
		String lawName=getLawParms();
		if(lawName.length()==0)
			lawName="laws.ini";
		if(lawName.equalsIgnoreCase("custom"))
			return super.externalFiles();
		if(lawName.equalsIgnoreCase("laws.ini"))
			return super.externalFiles();
		if(new CMFile(Resources.makeFileResourceName(lawName),null).exists())
			return new XVector(lawName);
		return super.externalFiles();
	}

	public final String getResourceKey(String lawName)
	{
		return "LEGAL-"+lawName+Long.toString(getExtraLawString().hashCode());
	}

	protected Law getLaws(Environmental what, boolean cleanOnly)
	{
		String lawName=getLawParms();

		boolean modifiableLaw=false;
		boolean modifiableNames=defaultModifiableNames();

		Law laws=null;
		if((lawName.equalsIgnoreCase("custom"))&&(what!=null))
		{
			modifiableLaw=true;
			laws=(Law)Resources.getResource(getResourceKey(what.Name()));
		}
		else
		{
			if(lawName.length()==0)
				lawName="laws.ini";
			laws=(Law)Resources.getResource(getResourceKey(lawName));
			modifiableNames=false;
		}

		if((laws==null)&&(cleanOnly))
			return null;

		if(laws==null)
		{
			final Properties lawprops=new Properties();
			try
			{
				if((lawName.equalsIgnoreCase("custom"))&&(what!=null))
				{
					final List<PlayerData> data=CMLib.database().DBReadData(what.Name(),"ARREST",what.Name()+"/ARREST");
					if((data!=null)&&(data.size()>0))
					{
						final DatabaseEngine.PlayerData pdata=data.get(0);
						String s=CMStrings.replaceAll(pdata.xml(),"~","\n");
						s=CMStrings.replaceAll(s,"`","'");
						lawprops.load(new ByteArrayInputStream(CMStrings.strToBytes(s)));
					}
					else
					{
						String s=Law.defaultLaw;
						lawprops.load(new ByteArrayInputStream(CMStrings.strToBytes(s)));
						s=CMStrings.replaceAll(s,"\n","~");
						s=CMStrings.replaceAll(s,"\r","~");
						s=CMStrings.replaceAll(s,"'","`");
						CMLib.database().DBCreateData(what.Name(),"ARREST",what.Name()+"/ARREST",s);
					}
				}
				if(lawprops.isEmpty())
					lawprops.load(new ByteArrayInputStream(new CMFile(Resources.makeFileResourceName(lawName),null).raw()));
			}
			catch(final IOException e)
			{
				if(!loadAttempt)
				{
					Log.errOut("Arrest","Unable to load: "+lawName+", legal system inoperable.");
					loadAttempt=true;
				}
				return (Law)CMClass.getCommon("DefaultLawSet");
			}
			lawprops.putAll(getExtraLawParms());
			loadAttempt=true;
			laws=(Law)CMClass.getCommon("DefaultLawSet");
			laws.initialize(this,lawprops,modifiableNames,modifiableLaw);
			if(lawName.equalsIgnoreCase("custom")&&(what!=null))
				Resources.submitResource(getResourceKey(what.name()),laws);
			else
				Resources.submitResource(getResourceKey(lawName),laws);
		}
		return laws;
	}

	public void unCuff(MOB mob)
	{
		final Ability A=mob.fetchEffect("Skill_HandCuff");
		if(A!=null)
			A.unInvoke();
	}


	public void dismissOfficer(MOB officer)
	{
		if(officer==null)
			return;
		if((officer.getStartRoom()!=null)
		&&(officer.location()!=null)
		&&(officer.getStartRoom()==officer.location()))
			return;
		if(officer.isMonster())
			CMLib.tracking().wanderAway(officer,true,true);
	}

	public MOB getAWitnessHere(Room R, MOB accused)
	{
		if(R!=null)
		for(int i=0;i<R.numInhabitants();i++)
		{
			final MOB M=R.fetchInhabitant(i);
			if((M!=null)
			&&M.isMonster()
			&&(M!=accused)
			&&(M.charStats().getStat(CharStats.STAT_INTELLIGENCE)>3)
			&&(CMLib.dice().rollPercentage()<=(CMLib.flags().isEvil(accused)?25:(CMLib.flags().isGood(accused)?95:50))))
				return M;
		}
		return null;
	}

	public MOB getWitness(Area A, MOB accused)
	{
		final Room R=accused.location();

		if((A!=null)&&(!A.inMyMetroArea(R.getArea())))
			return null;
		MOB M=getAWitnessHere(R,accused);
		if(M!=null)
			return M;

		if(R!=null)
		{
			for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)
			{
				final Room R2=R.getRoomInDir(d);
				M=getAWitnessHere(R2,accused);
				if(M!=null)
					return M;
			}
		}
		return null;
	}

	public boolean isAnyKindOfOfficer(Law laws, MOB M)
	{
		if((M.isMonster())
		&&(M.location()!=null)
		&&(CMLib.flags().isMobile(M)))
		{
			if((laws.officerNames().size()<=0)
			||(laws.officerNames().get(0).equals("@")))
				return false;
			for(int i=0;i<laws.officerNames().size();i++)
			{
				if((CMLib.english().containsString(M.displayText(),laws.officerNames().get(i))
				||(CMLib.english().containsString(M.Name(),laws.officerNames().get(i)))))
					return true;
			}
		}
		return false;
	}

	public boolean isElligibleOfficer(Law laws, MOB M, Area myArea)
	{
		if((M!=null)&&(M.isMonster())&&(M.location()!=null))
		{
			if((myArea!=null)&&(!myArea.inMyMetroArea(M.location().getArea())))
				return false;

			if(isAnyKindOfOfficer(laws,M)
			&&(!isBusyWithJustice(laws,M))
			&&(CMLib.flags().isAliveAwakeMobile(M,true))
			&&(!M.isInCombat()))
				return true;
		}
		return false;
	}

	public MOB getElligibleOfficerHere(Law laws,
									   Area myArea,
									   Room R,
									   MOB criminal,
									   MOB victim)
	{
		if(R==null)
			return null;
		for(int i=0;i<R.numInhabitants();i++)
		{
			final MOB M=R.fetchInhabitant(i);
			if((M!=null)
			&&(M!=criminal)
			&&(M.location()!=null)
			&&(myArea.inMyMetroArea(M.location().getArea()))
			&&((victim==null)||(M!=victim))
			&&(isElligibleOfficer(laws,M,myArea))
			&&(CMLib.flags().canBeSeenBy(criminal,M)))
				return M;
		}
		return null;
	}

	public MOB getAnyElligibleOfficer(Law laws,
									  Area myArea,
									  MOB criminal,
									  MOB victim)
	{
		final Room R=criminal.location();
		if(R==null)
			return null;
		if((myArea!=null)&&(!myArea.inMyMetroArea(R.getArea())))
			return null;
		MOB M=getElligibleOfficerHere(laws,myArea,R,criminal,victim);
		if((M==null)&&(myArea!=null))
		{
			for(final Enumeration e=myArea.getMetroMap();e.hasMoreElements();)
			{
				final Room R2=(Room)e.nextElement();
				M=getElligibleOfficerHere(laws,myArea,R2,criminal,victim);
				if(M!=null)
					break;
			}
		}
		return M;
	}

	public MOB getElligibleOfficer(Law laws,
								   Area myArea,
								   MOB criminal,
								   MOB victim)
	{
		final Room R=criminal.location();
		if(R==null)
			return null;
		if((myArea!=null)&&(!myArea.inMyMetroArea(R.getArea())))
			return null;
		MOB M=getElligibleOfficerHere(laws,myArea,R,criminal,victim);
		if(M!=null)
			return M;
		for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)
		{
			final Room R2=R.getRoomInDir(d);
			if(R2!=null)
			{
				M=getElligibleOfficerHere(laws,myArea,R2,criminal,victim);
				if(M!=null)
				{
					final int direction=Directions.getOpDirectionCode(d);
					CMLib.tracking().walk(M,direction,false,false);
					if(M.location()==R)
						return M;
				}
			}
		}
		return null;
	}

	public boolean canFocusOn(MOB officer, MOB criminal)
	{
		final CMMsg msg=CMClass.getMsg(officer,criminal,CMMsg.MSG_LOOK,L("<S-NAME> look(s) closely at <T-NAME>."));
		if((officer!=null)&&(officer.location()!=null)&&(criminal.location()==officer.location()))
		{
			if(!officer.location().okMessage(officer,msg))
				return false;
			if(msg.sourceMessage().indexOf("<T-NAME>")<0)
				return false;
			if((criminal.name().toUpperCase().equals(criminal.Name().toUpperCase()))
			||(criminal.name().toUpperCase().startsWith(criminal.Name().toUpperCase()+" "))
			||(criminal.name().toUpperCase().endsWith(" "+criminal.Name().toUpperCase())))
				return true;
		}
		return true;
	}

	@Override
	public boolean isStillACrime(LegalWarrant W, boolean debugging)
	{
		// will witness talk, or victim press charges?
		final Set<MOB> H=W.criminal().getGroupMembers(new HashSet<MOB>());
		if((W.witness()!=null)&&W.witness().amDead())
		{
			if(debugging)
				Log.debugOut("ARREST", "("+lastAreaName+"): "+W.crime()+" Witness ("+W.witness().Name()+") is DEAD!");
			return false;
		}
		if(W.arrestingOfficer()!=null)
		{
			if(W.witness()==W.arrestingOfficer())
				return true;
			if((W.victim()!=null)&&(W.victim()==W.arrestingOfficer()))
				return true;
		}

		if((W.witness()!=null)&&H.contains(W.witness()))
		{
			if(debugging)
				Log.debugOut("ARREST", "("+lastAreaName+"): "+W.crime()+" Witness ("+W.witness().Name()+") is a friend of the accused!");
			return false;
		}
		if((W.victim()!=null)&&(H.contains(W.victim())))
		{
			if(debugging)
				Log.debugOut("ARREST", "("+lastAreaName+"): "+W.crime()+" Victim ("+W.victim().Name()+") is a friend of the accused!");
			return false;
		}
		// crimes expire after three real days
		if((W.lastOffense()>0)&&((System.currentTimeMillis()-W.lastOffense())>EXPIRATION_MILLIS))
		{
			if(debugging)
				Log.debugOut("ARREST","("+lastAreaName+"): "+W.crime()+" Crime has expired: "+W.lastOffense());
			return false;
		}
		return true;
	}

	public Vector<LegalWarrant> getRelevantWarrants(List<LegalWarrant> warrants, LegalWarrant W, MOB criminal)
	{
		final Vector<LegalWarrant> V=new Vector<LegalWarrant>();
		if(W!=null)
			V.addElement(W);
		for(final LegalWarrant W2 : warrants)
		{
			if((W2.criminal()==criminal)
			&&(W2!=W)
			&&((W==null)
				||(W2.crime()==null)
				||(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE))
				||(W2.crime().equalsIgnoreCase(W.crime()))))
					V.addElement(W2);
		}
		return V;
	}

	public double getFine(Law laws, LegalWarrant W, MOB criminal)
	{
		String s=null;
		if(CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE))
		{
			s=W.getPunishmentParm(Law.PUNISHMENTMASK_FINE);
			if((s==null)||(s.length()==0)||(!CMath.isNumber(s)))
				return 0;
			return CMath.s_double(s);
		}
		double fine=0.0;
		final Vector<LegalWarrant> V=getRelevantWarrants(laws.warrants(),W,criminal);
		for(int w2=0;w2<V.size();w2++)
		{
			final LegalWarrant W2=V.elementAt(w2);
			if(!CMath.bset(W2.punishment(),Law.PUNISHMENTMASK_SEPARATE))
			{
				s=W.getPunishmentParm(Law.PUNISHMENTMASK_FINE);
				if((s!=null)&&(s.length()>0)&&(CMath.isNumber(s)))
					fine+=CMath.s_double(s);
			}
		}
		return fine;
	}
	protected String getDetainParm(Law laws, LegalWarrant W, MOB criminal)
	{
		String s=null;
		if(CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE))
		{
			s=W.getPunishmentParm(Law.PUNISHMENTMASK_DETAIN);
			if((s==null)||(s.length()==0))
				return "";
		}
		s=W.getPunishmentParm(Law.PUNISHMENTMASK_DETAIN);
		if((s==null)||(s.length()==0))
		{
			final Vector<LegalWarrant> V=getRelevantWarrants(laws.warrants(),W,criminal);
			for(int w2=0;w2<V.size();w2++)
			{
				final LegalWarrant W2=V.elementAt(w2);
				if(!CMath.bset(W2.punishment(),Law.PUNISHMENTMASK_SEPARATE))
				{
					s=W.getPunishmentParm(Law.PUNISHMENTMASK_DETAIN);
					if((s!=null)&&(s.length()>0))
						break;
				}
			}
		}
		if(s!=null)
		{
			return s;
		}
		return "";
	}

	protected String getDetainRoom(Law laws, LegalWarrant W, MOB criminal)
	{
		final String s=getDetainParm(laws,W,criminal);
		if((s==null)||(s.length()==0))
			return "";
		final int x=s.indexOf(',');
		if((x<0)||(!CMath.isInteger(s.substring(x+1))))
			return s;
		return s.substring(0,x);
	}

	protected int getDetainTime(Law laws, LegalWarrant W, MOB criminal)
	{
		final String s=getDetainParm(laws,W,criminal);
		if((s==null)||(s.length()==0))
			return -1;
		final int x=s.indexOf(',');
		if((x<0)||(!CMath.isInteger(s.substring(x+1))))
			return laws.jailTimes()[0].intValue();
		return CMath.s_int(s.substring(x+1));
	}

	public int highestCrimeAction(Law laws, LegalWarrant W, MOB criminal)
	{
		int highest=0;
		if(CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE))
			return W.punishment();
		final Vector<LegalWarrant> V=getRelevantWarrants(laws.warrants(),W,criminal);
		for(int w2=0;w2<V.size();w2++)
		{
			final LegalWarrant W2=V.elementAt(w2);
			if(!CMath.bset(W2.punishment(),Law.PUNISHMENTMASK_SEPARATE))
			{
				if(((W2.punishment()&Law.PUNISHMENT_MASK)+W2.offenses())>(highest&Law.PUNISHMENT_MASK))
					highest=(W2.punishment()&Law.PUNISHMENT_MASK)+((W2.offenses()<4)?W2.offenses():3);
			}
		}
		for(int w2=0;w2<V.size();w2++)
		{
			final LegalWarrant W2=V.elementAt(w2);
			if((!CMath.bset(W2.punishment(),Law.PUNISHMENTMASK_SEPARATE))
			&&(highest<((W2.punishment()&Law.PUNISHMENT_MASK)+4)))
				highest++;
		}
		if(highest>Law.PUNISHMENT_HIGHEST)
			highest=Law.PUNISHMENT_HIGHEST;
		String cap = W.getPunishmentParm(Law.PUNISHMENTMASK_PUNISHCAP);
		if((cap != null)&&(cap.trim().length()>0))
		{
			cap=cap.trim();
			if(CMath.isInteger(cap))
			{
				if(highest>CMath.s_int(cap))
					highest=CMath.s_int(cap);
			}
			else
			{
				int x=CMParms.indexOf(Law.PUNISHMENT_DESCS,cap.toUpperCase());
				if(highest>x)
					highest=x;
			}
		}
			
		int adjusted=highest;
		if((CMLib.flags().isGood(criminal))&&(adjusted>0))
			adjusted--;
		return adjusted;
	}

	public boolean isBusyWithJustice(Law laws, MOB M)
	{
		for(final LegalWarrant W : laws.warrants())
		{
			if(W.arrestingOfficer()!=null)
			{
				if(W.criminal()==M)
					return true;
				else
				if(W.arrestingOfficer()==M)
					return true;
			}
		}
		return false;
	}

	public String fixCharge(LegalWarrant W)
	{
		if(W==null)
			return "";
		final String charge=W.crime();
		if(W.victim()==null)
			return charge;
		if(charge.indexOf("<T-NAME>")<0)
			return charge;
		return charge.replaceFirst("<T-NAME>",W.victim().name());
	}

	public String restOfCharges(Law laws, MOB mob)
	{
		final StringBuffer msg=new StringBuffer("");
		for(int w=0;(laws.getWarrant(mob,w)!=null);w++)
		{
			final LegalWarrant W=laws.getWarrant(mob,w);
			if(W!=null)
			{
				if(w==0)
					msg.append(L("for @x1",fixCharge(W)));
				else
				if(laws.getWarrant(mob,w+1)==null)
					msg.append(L(", and for @x1",fixCharge(W)));
				else
					msg.append(L(", for @x1",fixCharge(W)));
			}
		}
		return msg.toString();
	}

	public void makePeace(Room R)
	{
		if(R==null)
			return;
		for(int i=0;i<R.numInhabitants();i++)
		{
			final MOB inhab=R.fetchInhabitant(i);
			if((inhab!=null)&&(inhab.isInCombat()))
				inhab.makePeace(true);
		}
	}

	public boolean isTheJudge(Law laws, MOB M)
	{
		if((M!=null)
		&&((M.isMonster()||M.soulMate()!=null))
		&&(!CMLib.flags().isMobile(M))
		&&(M.location()!=null))
		{
			if((laws.judgeNames().size()<=0)||(laws.judgeNames().get(0).equals("@")))
				return false;
			for(int i=0;i<laws.judgeNames().size();i++)
			{
				if((CMLib.english().containsString(M.displayText(),laws.judgeNames().get(i)))
				||(CMLib.english().containsString(M.Name(),laws.judgeNames().get(i))))
					return true;
			}
		}
		return false;
	}

	public MOB getTheJudgeHere(Law laws, Room R)
	{
		for(int i=0;i<R.numInhabitants();i++)
		{
			final MOB M=R.fetchInhabitant(i);
			if(isTheJudge(laws,M))
				return M;
		}
		return null;
	}

	public Room findTheJudge(Law laws, Area myArea)
	{
		for(final Enumeration r=myArea.getMetroMap();r.hasMoreElements();)
		{
			final Room R=(Room)r.nextElement();
			for(int i=0;i<R.numInhabitants();i++)
			{
				final MOB M=R.fetchInhabitant(i);
				if(isTheJudge(laws,M))
					return R;
			}
		}
		return null;
	}

	protected String trackingModifiers(MOB officer)
	{
		final StringBuilder modifiers=new StringBuilder("");
		if(officer!=null)
		{
			if((!CMLib.flags().isFlying(officer))&&(!CMLib.flags().isSwimming(officer)))
				modifiers.append(" LANDONLY");
			else
			{
				if(!CMLib.flags().isFlying(officer))
					modifiers.append(" NOAIR");
				if(!CMLib.flags().isSwimming(officer))
					modifiers.append(" NOWATER");
			}
		}
		return modifiers.toString();
	}
	
	public boolean trackTheJudge(MOB officer, Area myArea, Law laws)
	{
		CMLib.tracking().stopTracking(officer);
		final Ability A=CMClass.getAbility("Skill_Track");
		if(A!=null)
		{
			final Room R=findTheJudge(laws,myArea);
			if(R!=null)
			{
				A.invoke(officer,CMParms.parse("\""+CMLib.map().getExtendedRoomID(R)+"\" "+trackingModifiers(officer)),R,true,0);
				return true;
			}
		}
		return false;
	}

	public Room getReleaseRoom(Law laws, Area myArea, MOB criminal, LegalWarrant W)
	{
		Room room=null;
		if((criminal.isMonster())&&(criminal.getStartRoom()!=null))
			room=criminal.getStartRoom();
		else
		{
			if((laws.releaseRooms().size()==0)||(laws.releaseRooms().get(0).equals("@")))
				return myArea.getMetroMap().nextElement();
			if(criminal.location()!=null)
				room=getRoom(criminal.location().getArea(),laws.releaseRooms());
			if(room==null)
				room=getRoom(myArea,laws.releaseRooms());
			if(room==null)
				room=findTheJudge(laws,myArea);
			if(room==null)
				room=myArea.getMetroMap().nextElement();
		}
		return room;
	}


	public boolean isTroubleMaker(MOB M)
	{
		if(M==null)
			return false;
		for(final Enumeration<Behavior> e=M.behaviors();e.hasMoreElements();)
		{
			final Behavior B=e.nextElement();
			if((B!=null)&&(CMath.bset(B.flags(),Behavior.FLAG_TROUBLEMAKING)))
				return true;
		}
		return (M.fetchEffect("QuestBound")!=null); // questing mobs are, by default, trouble makers
	}

	public List<Room> getRooms(Area A, List<String> V)
	{
		final Vector<Room> finalV=new Vector<Room>();
		Room jail=null;
		if(V.size()==0)
			return finalV;
		for(int v=0;v<V.size();v++)
		{
			final String which=V.get(v);
			jail=getRoom(A,which);
			if((jail!=null)
			&&(!finalV.contains(jail)))
				finalV.addElement(jail);
		}
		return finalV;
	}

	public Room getRoom(Area A, String which)
	{
		Room jail=null;
		jail=CMLib.map().getRoom(which);
		if(jail==null)
		{
			for(final Enumeration r=A.getMetroMap();r.hasMoreElements();)
			{
				final Room R=(Room)r.nextElement();
				if(CMLib.english().containsString(R.displayText(),which))
				{
					jail = R;
					break;
				}
			}
		}
		if(jail==null)
		{
			for(final Enumeration r=A.getMetroMap();r.hasMoreElements();)
			{
				final Room R=(Room)r.nextElement();
				if(CMLib.english().containsString(R.description(),which))
				{
					jail = R;
					break;
				}
			}
		}
		return jail;
	}

	public Room getRoom(Area A, List<String> V)
	{
		if(V.size()==0)
			return null;
		final String which=V.get(CMLib.dice().roll(1,V.size(),-1));
		return getRoom(A,which);
	}

	public void fileAllWarrants(Law laws, LegalWarrant W1, MOB mob)
	{

		final Vector<LegalWarrant> V=new Vector<LegalWarrant>();
		{
			LegalWarrant W=null;
			if((W1!=null)&&(CMath.bset(W1.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
			{
				for(int i=0;(W=laws.getWarrant(mob,i))!=null;i++)
				{
					if((W.criminal()==mob)&&(W1.crime().equalsIgnoreCase(W.crime())))
						V.addElement(W);
				}
			}
			else
			{
				for(int i=0;(W=laws.getWarrant(mob,i))!=null;i++)
				{
					if(W.criminal()==mob)
						V.addElement(W);
				}
			}
		}
		for(final LegalWarrant W : V)
		{
			laws.warrants().remove(W);
			if(W.crime()!=null)
			{
				boolean found=false;
				for(final LegalWarrant oW : laws.oldWarrants())
				{
					if((oW.criminal()==mob)
					&&(oW.crime()!=null)
					&&(oW.crime().equals(W.crime())))
						found=true;
				}
				if(!found)
				{
					W.setOffenses(W.offenses()+1);
					laws.oldWarrants().add(W);
				}
			}
		}
	}

	public Room findTheJail(MOB mob, Area myArea, Law laws)
	{
		Room jail=null;
		if((laws.jailRooms().size()==0)||(laws.jailRooms().get(0).equals("@")))
			return null;
		jail=getRoom(mob.location().getArea(),laws.jailRooms());
		if(jail==null)
			jail=getRoom(myArea,laws.jailRooms());
		return jail;
	}

	public Room findTheDetentionCenter(MOB mob, Area myArea, Law laws, LegalWarrant W)
	{
		final String detentionCenter=getDetainRoom(laws,W,W.criminal());
		if(detentionCenter.length()==0)
			return null;
		Room detainer=getRoom(mob.location().getArea(),detentionCenter);
		if(detainer==null)
			detainer=getRoom(myArea,detentionCenter);
		return detainer;
	}

	public boolean judgeMe(Law laws, MOB judge, MOB officer, MOB criminal, LegalWarrant W, Area A, boolean debugging)
	{
		final Vector<LegalWarrant> relevantCrimes=getRelevantWarrants(laws.warrants(),W,criminal);
		if(CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SKIPTRIAL))
			judge=officer;
		if(debugging)
			Log.debugOut("Arrest","("+lastAreaName+"): "+W.crime()+" "+criminal.Name()+" judged for "+W.crime()+" has base action "+W.punishment()+", and final judgement "+highestCrimeAction(laws,W,W.criminal()));
		boolean totallyDone=false;
		switch(highestCrimeAction(laws,W,W.criminal())&Law.PUNISHMENT_MASK)
		{
		case Law.PUNISHMENT_WARN:
		{
			if((judge==null)&&(officer!=null))
				judge=officer;
			final StringBuffer str=new StringBuffer("");
			str.append(L("@x1, you are in trouble for @x2.  ",criminal.name(),restOfCharges(laws,criminal)));
			for(int w2=0;w2<relevantCrimes.size();w2++)
			{
				final LegalWarrant W2=relevantCrimes.elementAt(w2);
				if(W2.criminal()==criminal)
				{
					if(W2.witness()!=null)
						str.append(L("The charge of @x1 was witnessed by @x2.  ",fixCharge(W2),W2.witness().name()));
					if((W2.warnMsg()!=null)&&(W2.warnMsg().length()>0))
						str.append(W2.warnMsg()+"  ");
					if((W2.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
						str.append(laws.getMessage(Law.MSG_PREVOFF)+"  ");
				}
			}
			if((laws.getMessage(Law.MSG_WARNING).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_DETAIN)))
				str.append(laws.getMessage(Law.MSG_WARNING)+"  ");
			CMLib.commands().postSay(judge,criminal,str.toString(),false,false);
			totallyDone=true;
			break;
		}
		case Law.PUNISHMENT_THREATEN:
		{
			if((judge==null)&&(officer!=null))
				judge=officer;
			final StringBuffer str=new StringBuffer("");
			str.append(L("@x1, you are in trouble for @x2.  ",criminal.name(),restOfCharges(laws,criminal)));
			for(int w2=0;w2<relevantCrimes.size();w2++)
			{
				final LegalWarrant W2=relevantCrimes.elementAt(w2);
				if(W2.criminal()==criminal)
				{
					if(W2.witness()!=null)
						str.append(L("The charge of @x1 was witnessed by @x2.  ",fixCharge(W2),W2.witness().name()));
					if((W2.warnMsg()!=null)&&(W2.warnMsg().length()>0))
						str.append(W2.warnMsg()+"  ");
					if((W2.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
						str.append(laws.getMessage(Law.MSG_PREVOFF)+"  ");
				}
			}
			if((laws.getMessage(Law.MSG_THREAT).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_DETAIN)))
				str.append(laws.getMessage(Law.MSG_THREAT)+"  ");
			CMLib.commands().postSay(judge,criminal,str.toString(),false,false);
			totallyDone=true;
			break;
		}
		case Law.PUNISHMENT_PAROLE1:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.paroleMessages(0).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.paroleMessages(0),false,false);
				W.setJailTime(laws.paroleTimes(0));
				W.setState(Law.STATE_PAROLING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_PAROLE2:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.paroleMessages(1).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.paroleMessages(1),false,false);
				W.setJailTime(laws.paroleTimes(1));
				W.setState(Law.STATE_PAROLING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_PAROLE3:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.paroleMessages(2).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.paroleMessages(2),false,false);
				W.setJailTime(laws.paroleTimes(2));
				W.setState(Law.STATE_PAROLING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_PAROLE4:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.paroleMessages(3).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.paroleMessages(3),false,false);
				W.setJailTime(laws.paroleTimes(3));
				W.setState(Law.STATE_PAROLING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_JAIL1:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.jailMessages(0).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.jailMessages(0),false,false);
				W.setJailTime(laws.jailTimes(0));
				W.setState(Law.STATE_JAILING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_JAIL2:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.jailMessages(1).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.jailMessages(1),false,false);
				W.setJailTime(laws.jailTimes(1));
				W.setState(Law.STATE_JAILING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_JAIL3:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.jailMessages(2).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.jailMessages(2),false,false);
				W.setJailTime(laws.jailTimes(2));
				W.setState(Law.STATE_JAILING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_JAIL4:
			if(judge!=null)
			{
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.jailMessages(3).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.jailMessages(3),false,false);
				W.setJailTime(laws.jailTimes(3));
				W.setState(Law.STATE_JAILING);
			}
			totallyDone=false;
			break;
		case Law.PUNISHMENT_EXECUTE:
			if(judge!=null)
			{
				criminal.setFollowing(null);
				if((W.offenses()>0)&&(laws.getMessage(Law.MSG_PREVOFF).length()>0)&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
					CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PREVOFF),false,false);
				if(laws.getMessage(Law.MSG_EXECUTE).length()>0)
					CMLib.commands().postSay(judge,criminal,laws.getMessage(Law.MSG_EXECUTE),false,false);
				W.setState(Law.STATE_EXECUTING);
			}
			totallyDone=false;
			break;
		}
		if((totallyDone)&&(CMath.bset(W.punishment(),Law.PUNISHMENTMASK_FINE)))
		{
			final double fines=getFine(laws,W,criminal);
			if((judge==null)&&(officer!=null))
				judge=officer;
			if((fines>0.0)&&(judge!=null))
			{
				CMLib.commands().postSay(judge,criminal,L("You are hereby fined @x1, payable to the local tax assessor.",CMLib.beanCounter().nameCurrencyShort(judge,fines)),false,false);
				Double D=finesAssessed.get(criminal);
				if(D==null)
					D=Double.valueOf(0.0);
				else
					finesAssessed.remove(criminal);
				finesAssessed.put(criminal,Double.valueOf(D.doubleValue()+fines));
			}
		}
		if((totallyDone)&&(CMath.bset(W.punishment(),Law.PUNISHMENTMASK_DETAIN)))
		{
			W.setState(Law.STATE_DETAINING);
			if(officer!=null)
				W.setArrestingOfficer(A,officer);
			if(debugging)
				Log.debugOut("Arrest","("+lastAreaName+"): "+W.crime()+" Putting the above crime into a detain state, officer="+(W.arrestingOfficer()!=null)+".");
			return false;
		}
		return totallyDone;
	}

	@Override
	public boolean fillOutWarrant(MOB criminalM,
								  Law laws,
								  Area myArea,
								  Environmental victimM,
								  String crimeLocs,
								  String crimeFlags,
								  String crime,
								  String sentence,
								  String warnMsg)
	{
		if(criminalM.amDead())
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST)) 
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+" * IS DEAD!");
			return false;
		}
		if(criminalM.location()==null)
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST)) 
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Accused is not here.");
			return false;
		}
		if((myArea!=null)&&(!myArea.inMyMetroArea(criminalM.location().getArea())))
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST)) 
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Accused is not in the area.");
			return false;
		}

		if(isAnyKindOfOfficer(laws,criminalM)
		||(isTheJudge(laws,criminalM))
		||CMSecurity.isAllowed(criminalM,criminalM.location(),CMSecurity.SecFlag.ABOVELAW))
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST)) 
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Accused is an officer ("+isAnyKindOfOfficer(laws,criminalM)+"), judge ("+isTheJudge(laws,criminalM)+"), or above the law ("+CMSecurity.isAllowed(criminalM,criminalM.location(),CMSecurity.SecFlag.ABOVELAW)+").");
			return false;
		}

		// is there a witness
		final MOB witness=getWitness(myArea,criminalM);
		boolean requiresWitness=true;

		// is there a victim (if necessary)
		MOB victim=null;
		if((victimM!=null)&&(victimM instanceof MOB))
			victim=(MOB)victimM;
		if(criminalM==victim)
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST)) 
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Accused and victim are the same.");
			return false;
		}

		// any special circumstances?
		if(crimeFlags.trim().length()>0)
		{
			final Vector<String> V=CMParms.parse(crimeFlags.toUpperCase());
			for(int v=0;v<V.size();v++)
			{
				final String str=V.elementAt(v);
				if(str.endsWith("WITNESS")&&(str.length()<9))
				{
					if(str.startsWith("!"))
						requiresWitness=false;
					else
					if((witness!=null)&&(witness.location()!=criminalM.location()))
					{
						if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
							Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Witness required, but not present.");
						return false;
					}
				}
				else
				if(str.endsWith("COMBAT")&&(str.length()<8))
				{
					if(criminalM.isInCombat())
					{
						if(str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* In combat, but shouldn't be!");
							return false;
						}
					}
					else
					{
						if(!str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Not in combat, but should be!");
							return false;
						}
					}
				}
				else
				if(str.endsWith("RECENTLY")&&(str.length()<10))
				{
					final LegalWarrant W=laws.getOldWarrant(criminalM,crime,false);
					final long thisTime=System.currentTimeMillis();
					if((W!=null)&&((thisTime-W.lastOffense())<600000))
					{
						if(str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Not recently, but is!");
							return false;
						}
					}
					else
					{
						if(!str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Recently required, but it isn't!");
							return false;
						}
					}
				}
			}
		}
		if((requiresWitness)&&(witness==null))
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Witness required, and none present: .");
			return false;
		}

		// is the location significant to this crime?
		if(crimeLocs.trim().length()>0)
		{
			boolean aCrime=false;
			final Vector<String> V=CMParms.parse(crimeLocs);
			final String display=criminalM.location().displayText().toUpperCase().trim();
			for(int v=0;v<V.size();v++)
			{
				final String str=V.elementAt(v).toUpperCase();
				if(str.endsWith("INDOORS")&&(str.length()<9))
				{
					if((criminalM.location().domainType()&Room.INDOORS)>0)
					{
						if(str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Shouldn't be indoors, but is!");
							return false;
						}
					}
					else
					{
						if(!str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Should be indoors, but isn't!");
							return false;
						}
					}
					aCrime=true;
				}
				else
				if(str.endsWith("HOME")&&(str.length()<6))
				{
					if(CMLib.law().doesHavePriviledgesHere(criminalM,criminalM.location()))
					{
						if(str.startsWith("!"))
						{
							if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
								Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Should not be home, but is!");
							return false;
						}
					}
					if(!str.startsWith("!"))
					{
						if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
							Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Should be home, but is not!");
						return false;
					}
					aCrime=true;
				}
				else
				if(str.startsWith("!")&&(CMLib.english().containsString(display,str.substring(1))))
				{
					if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
						Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Should not be at '"+str.substring(1)+"', but is!");
					return false;
				}
				else
				if(CMLib.english().containsString(display,str))
				{
					aCrime = true;
					break;
				}
			}
			if(!aCrime)
			{
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
					Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Crime flag failure!");
				return false;
			}
		}

		// is the victim a protected race?
		if((victim!=null)&&(!(victim instanceof Deity)))
		{
			if(!CMLib.masking().maskCheck(laws.getMessage(Law.MSG_PROTECTEDMASK),victim,false))
			{
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
					Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Victim is not a protected race!");
				return false;
			}
		}

		// does a warrant already exist?
		LegalWarrant W=null;
		for(int i=0;(W=laws.getWarrant(criminalM,i))!=null;i++)
		{
			if((W.criminal()==criminalM)
			&&(W.victim()==victim)
			&&(W.crime().equals(crime)))
			{
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
					Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Warrant already exists.");
				return false;
			}
		}
		W=laws.getOldWarrant(criminalM,crime,true);
		if(W==null)
			W=(LegalWarrant)CMClass.getCommon("DefaultArrestWarrant");

		// fill out the warrant!
		W.setCriminal(criminalM);
		W.setVictim(victim);
		W.setCrime(crime);
		W.setState(Law.STATE_SEEKING);
		W.setWitness(requiresWitness?witness:null);
		W.setLastOffense(System.currentTimeMillis());
		W.setWarnMsg(warnMsg);
		sentence=sentence.trim();
		final Vector<String> sentences=CMParms.parse(sentence);
		W.setPunishment(0);
		for(int v=0;v<sentences.size();v++)
		{
			String s=sentences.elementAt(v);
			final int x=s.indexOf('=');
			String parm=null;
			if(x>0)
			{
				parm=s.substring(x+1);
				s=s.substring(0,x+1);
			}
			boolean actionCodeSet=false;
			for(int i=0;i<Law.PUNISHMENT_DESCS.length;i++)
			{
				if(s.equalsIgnoreCase(Law.PUNISHMENT_DESCS[i]))
				{
					actionCodeSet=true;
					W.setPunishment(W.punishment());
					if(parm!=null)
						W.addPunishmentParm(i,parm);
				}
			}
			for(int i=0;i<Law.PUNISHMENTMASK_DESCS.length;i++)
			{
				if(s.equalsIgnoreCase(Law.PUNISHMENTMASK_DESCS[i]))
				{
					actionCodeSet=true;
					W.setPunishment(W.punishment()|Law.PUNISHMENTMASK_CODES[i]);
					if(parm!=null)
						W.addPunishmentParm(Law.PUNISHMENTMASK_CODES[i],parm);
				}
			}
			if(!actionCodeSet)
			{
				Log.errOut("Arrest","Unknown sentence: "+s+" for crime "+crime);
				return false;
			}
		}

		if((W.victim()!=null)&&(isTroubleMaker(W.victim()))&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE)))
			W.setPunishment((W.punishment()&Law.PUNISHMENT_MASK)/2);

		if((isStillACrime(W,CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST)))
		&&((W.witness()==null)||CMLib.flags().canBeSeenBy(W.criminal(),W.witness())))
		{
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
				Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Warrant filled out.");
			if(!addWarrant(laws,W))
				return false;
		}
		else
		if(CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST))
			Log.debugOut("ARREST","("+lastAreaName+"): "+criminalM.name()+", data: "+crimeLocs+"->"+crimeFlags+"->"+crime+"->"+sentence+"* Warrant fails the is a crime check.");
		return true;
	}

	protected boolean isAnUltimateAuthorityHere(MOB M, Law laws)
	{
		if(CMSecurity.isAllowed(M,M.location(),CMSecurity.SecFlag.ABOVELAW)||(isTheJudge(laws,M)))
			return true;
		return false;
	}

	protected boolean theLawIsEnabled()
	{
		return ((CMProps.getBoolVar(CMProps.Bool.MUDSTARTED))
				&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.ARREST)));
	}

	public void testEntryLaw(Law laws, Area myArea, MOB testMOB, Room R)
	{
		if((laws.basicCrimes().containsKey("NUDITY"))
		&&(!testMOB.isMonster())
		&&(testMOB.fetchFirstWornItem(Wearable.WORN_LEGS)==null)
		&&(testMOB.getWearPositions(Wearable.WORN_LEGS)>0)
		&&(testMOB.fetchFirstWornItem(Wearable.WORN_WAIST)==null)
		&&(testMOB.getWearPositions(Wearable.WORN_WAIST)>0)
		&&(testMOB.fetchFirstWornItem(Wearable.WORN_ABOUT_BODY)==null)
		&&(testMOB.getWearPositions(Wearable.WORN_ABOUT_BODY)>0))
		{
			final String info[]=laws.basicCrimes().get("NUDITY");
			fillOutWarrant(testMOB,
							laws,
						   myArea,
						   null,
						   info[Law.BIT_CRIMELOCS],
						   info[Law.BIT_CRIMEFLAGS],
						   info[Law.BIT_CRIMENAME],
						   info[Law.BIT_SENTENCE],
						   info[Law.BIT_WARNMSG]);
		}


		Item w=null;
		if((laws.basicCrimes().containsKey("ARMED"))
		&&((!testMOB.isMonster())||(!myArea.inMyMetroArea(CMLib.map().getStartArea(testMOB))))
		&&((w=testMOB.fetchWieldedItem())!=null)
		&&(w instanceof Weapon)
		&&(((Weapon)w).weaponClassification()!=Weapon.CLASS_NATURAL)
		&&(((Weapon)w).weaponClassification()!=Weapon.CLASS_HAMMER)
		&&(((Weapon)w).weaponClassification()!=Weapon.CLASS_STAFF)
		&&(CMLib.flags().isSeeable(w))
		&&(!CMLib.flags().isHidden(w))
		&&(!CMLib.flags().isInvisible(w)))
		{
			final String info[]=laws.basicCrimes().get("ARMED");
			if((testMOB.session()==null)
			||(((System.currentTimeMillis()-testMOB.session().getLastNPCFight())>30000)
				&&((System.currentTimeMillis()-testMOB.session().getLastPKFight())>30000)))
			{
				fillOutWarrant(testMOB,
							   laws,
							   myArea,
							   null,
							   info[Law.BIT_CRIMELOCS],
							   info[Law.BIT_CRIMEFLAGS],
							   info[Law.BIT_CRIMENAME],
							   info[Law.BIT_SENTENCE],
							   info[Law.BIT_WARNMSG]);
			}
		}

		if((laws.basicCrimes().containsKey("TRESPASSING"))
		&&((CMLib.masking().maskCheck(laws.getMessage(Law.MSG_TRESPASSERMASK),testMOB,false))
			||(testMOB.isMonster()
				&&(testMOB.getStartRoom()!=null)
				&&(testMOB.getStartRoom().getArea()!=R.getArea())
				&&(CMLib.flags().isPossiblyAggressive(testMOB))
				&&((testMOB.amFollowing()==null)
						||((!testMOB.amFollowing().isMonster())&&(testMOB.amFollowing().location()==testMOB.location())))
				&&(!CMLib.masking().maskCheck(laws.getMessage(Law.MSG_PROTECTEDMASK),testMOB,false)))))
		{
			final String[] info=laws.basicCrimes().get("TRESPASSING");
			fillOutWarrant(testMOB,
							laws,
						   myArea,
						   null,
						   info[Law.BIT_CRIMELOCS],
						   info[Law.BIT_CRIMEFLAGS],
						   info[Law.BIT_CRIMENAME],
						   info[Law.BIT_SENTENCE],
						   info[Law.BIT_WARNMSG]);
		}
	}

	private void fillOutMurderWarrant(Law laws, Area myArea, MOB criminal, MOB victim)
	{
		for(final LegalWarrant W : laws.warrants())
		{
			if((W.victim()!=null)
			&&(W.criminal()!=null)
			&&(W.victim()==victim)
			&&(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE))
			&&(W.criminal()==criminal))
				laws.warrants().remove(W);
		}
		final String[] bits=laws.basicCrimes().get("MURDER");
		fillOutWarrant(criminal,
					   laws,
					   myArea,
					   victim,
					   bits[Law.BIT_CRIMELOCS],
					   bits[Law.BIT_CRIMEFLAGS],
					   bits[Law.BIT_CRIMENAME],
					   bits[Law.BIT_SENTENCE],
					   bits[Law.BIT_WARNMSG]);
	}

	@Override
	public void executeMsg(Environmental affecting, CMMsg msg)
	{
		super.executeMsg(affecting, msg);
		if(!(affecting instanceof Area))
			return;
		if(!theLawIsEnabled())
			return;

		final Area myArea=(Area)affecting;
		final Law laws=getLaws(affecting,false);
		if(!laws.lawIsActivated())
			return;
		if(msg.source()==null)
			return;
		if (lastAreaName == null) lastAreaName = myArea.Name();

		// the archons pardon
		if((msg.sourceMinor()==CMMsg.TYP_SPEAK)
		&&(msg.sourceMessage()!=null)
		&&(isAnUltimateAuthorityHere(msg.source(),laws)))
		{
			final int x=msg.sourceMessage().toUpperCase().indexOf("I HEREBY PARDON ");
			if(x>0)
			{
				int y=msg.sourceMessage().lastIndexOf('\'');
				if(y<x)
					y=msg.sourceMessage().lastIndexOf('`');
				String name=null;
				if(y>x)
					name=msg.sourceMessage().substring(x+16,y).trim();
				else
					name=msg.sourceMessage().substring(x+16).trim();
				if(name.length()>0)
				for(final LegalWarrant W : laws.warrants())
				{
					if((W.criminal()!=null)&&(CMLib.english().containsString(W.criminal().Name(),name)))
					{
						final Ability A=W.criminal().fetchEffect("Prisoner");
						if(A!=null)
							A.unInvoke();
						if(W.jail()!=W.criminal().location())
						{
							if(W.arrestingOfficer()!=null)
								dismissOfficer(W.arrestingOfficer());
							laws.warrants().remove(W);
						}
						else
						{
							W.setCrime("pardoned");
							W.setOffenses(0);
						}
					}
				}
			}
		}

		if((msg.sourceMinor()==CMMsg.TYP_DEATH)
		&&(msg.tool() instanceof MOB)
		&&(laws.basicCrimes().containsKey("MURDER"))
		&&((!msg.source().isMonster())
			||(!isTroubleMaker(msg.source()))
			||(this.isAnyKindOfOfficer(laws, msg.source()))))
		{
			final MOB criminal=(MOB)msg.tool();
			this.fillOutMurderWarrant(laws, myArea, criminal, msg.source());
			if(criminal.isMonster() && (isAnyKindOfOfficer(laws, msg.source())))
			{
				MOB leaderM = criminal.amUltimatelyFollowing();
				if((leaderM != null) && (leaderM != criminal) && (!leaderM.isMonster()))
					this.fillOutMurderWarrant(laws, myArea, leaderM, msg.source());
			}
			return;
		}

		if(isAnyKindOfOfficer(laws,msg.source())||(isTheJudge(laws,msg.source())))
		{
			if((msg.sourceMinor()==CMMsg.TYP_ENTER)
			&&(msg.target() instanceof Room))
			{
				final Room R=(Room)msg.target();
				MOB M=null;
				for(int m=0;m<R.numInhabitants();m++)
				{
					M=R.fetchInhabitant(m);
					if((M!=null)
					&&(M!=msg.source())
					&&(!isAnyKindOfOfficer(laws,M))
					&&(!isTheJudge(laws,M)))
						testEntryLaw(laws,myArea,M,R);
				}
			}
			return;
		}

		if((msg.source().isMonster())&&(!laws.arrestMobs()))
			return;

		if(!CMLib.flags().isAliveAwakeMobile(msg.source(),true))
			return;

		final Room R=msg.source().location();
		if(R==null)
			return;

		if((msg.tool() instanceof Ability)
		&&(msg.othersMessage()!=null)
		&&((laws.abilityCrimes().containsKey(msg.tool().ID().toUpperCase()))
			||(laws.abilityCrimes().containsKey(CMLib.flags().getAbilityType_((Ability)msg.tool())))
			||(laws.abilityCrimes().containsKey(CMLib.flags().getAbilityDomain((Ability)msg.tool())))))
		{
			String[] info=laws.abilityCrimes().get(msg.tool().ID().toUpperCase());
			if(info==null)
				info=laws.abilityCrimes().get(CMLib.flags().getAbilityType_((Ability)msg.tool()));
			if(info==null)
				info=laws.abilityCrimes().get(CMLib.flags().getAbilityDomain((Ability)msg.tool()));
			fillOutWarrant(msg.source(),
							laws,
							myArea,
							msg.target(),
							info[Law.BIT_CRIMELOCS],
							info[Law.BIT_CRIMEFLAGS],
							info[Law.BIT_CRIMENAME],
							info[Law.BIT_SENTENCE],
							info[Law.BIT_WARNMSG]);
		}

		for(final Enumeration<Ability> a=msg.source().effects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)
			&&(!A.isAutoInvoked())
			&&((A.canBeUninvoked()||(!msg.source().isMonster())))
			&&((laws.abilityCrimes().containsKey("$"+A.ID().toUpperCase()))
				||(laws.abilityCrimes().containsKey("$"+CMLib.flags().getAbilityType_(A)))
				||(laws.abilityCrimes().containsKey("$"+CMLib.flags().getAbilityDomain(A)))))
			{
				String[] info=laws.abilityCrimes().get("$"+A.ID().toUpperCase());
				if(info==null)
					info=laws.abilityCrimes().get("$"+CMLib.flags().getAbilityType_(A));
				if(info==null)
					info=laws.abilityCrimes().get("$"+CMLib.flags().getAbilityDomain(A));
				fillOutWarrant(msg.source(),
								laws,
								myArea,
								null,
								info[Law.BIT_CRIMELOCS],
								info[Law.BIT_CRIMEFLAGS],
								info[Law.BIT_CRIMENAME],
								info[Law.BIT_SENTENCE],
								info[Law.BIT_WARNMSG]);
			}
		}

		if((CMath.bset(msg.targetMajor(),CMMsg.MASK_MALICIOUS))
		&&(msg.target() instanceof MOB)
		&&(!CMath.bset(msg.sourceMajor(),CMMsg.MASK_ALWAYS))
		&&((msg.tool()==null)||(msg.source().isMine(msg.tool())))
		&&(msg.target()!=msg.source())
		&&(!msg.target().name().equals(msg.source().name())))
		{
			if(isTheJudge(laws,(MOB)msg.target()))
			{
				if(!msg.source().isMonster())
				{
					for(int i=0;i<R.numInhabitants();i++)
					{
						final MOB M=R.fetchInhabitant(i);
						if((M!=null)
						&&(M!=msg.target())
						&&(M!=msg.source())
						&&(M.getVictim()!=msg.source())
						&&(isAnyKindOfOfficer(laws,M)))
						{
							if(msg.source().amFollowing()==M)
								msg.source().setFollowing(null);
							CMLib.commands().postSay(M,null,L("Ack! Treason! Die!"),false,false);
							M.setVictim(msg.source());
						}
					}
				}
			}
			else
			{
				boolean justResisting=false;
				boolean turnAbout=false;
				final boolean targetIsOfficer=isAnyKindOfOfficer(laws,(MOB)msg.target());
				final String[] assaultInfo=laws.basicCrimes().get("ASSAULT");
				final String[] murderInfo=laws.basicCrimes().get("MURDER");
				if((assaultInfo!=null)&&(murderInfo!=null))
				{
					for(final LegalWarrant W : laws.warrants())
					{
						if(targetIsOfficer
						&&(W.criminal()==msg.source())
						&&(W.arrestingOfficer()!=null)
						&&(W.criminal().location()!=null)
						&&(W.criminal().location().isInhabitant(W.arrestingOfficer())))
							justResisting=true;
						else
						if((!targetIsOfficer)
						&&(W.criminal()==msg.target())
						&&((W.victim()==msg.source())||((msg.source().amFollowing()!=null)&&(W.victim()==msg.source().amFollowing())))
						&&(W.crime().equals(assaultInfo[Law.BIT_CRIMENAME])||W.crime().equals(murderInfo[Law.BIT_CRIMENAME]))
						&&(isStillACrime(W,false)))
							turnAbout=true;
						else
						if((!targetIsOfficer)
						&&(W.victim()==msg.target())
						&&(W.criminal()==msg.source())
						&&(W.crime().equals(murderInfo[Law.BIT_CRIMENAME]))
						&&(isStillACrime(W,false)))
							turnAbout=true;
					}
				}
				if(justResisting)
				{
					if(laws.basicCrimes().containsKey("RESISTINGARREST"))
					{
						final String[] info=laws.basicCrimes().get("RESISTINGARREST");
						fillOutWarrant(msg.source(),
										laws,
										myArea,
										null,
										info[Law.BIT_CRIMELOCS],
										info[Law.BIT_CRIMEFLAGS],
										info[Law.BIT_CRIMENAME],
										info[Law.BIT_SENTENCE],
										info[Law.BIT_WARNMSG]);
					}
				}
				else
				if((!turnAbout)
				&&(assaultInfo!=null)
				&&((msg.source().isMonster())||(!isTroubleMaker((MOB)msg.target()))))
				{
					fillOutWarrant(msg.source(),
									laws,
									myArea,
									msg.target(),
									assaultInfo[Law.BIT_CRIMELOCS],
									assaultInfo[Law.BIT_CRIMEFLAGS],
									assaultInfo[Law.BIT_CRIMENAME],
									assaultInfo[Law.BIT_SENTENCE],
									assaultInfo[Law.BIT_WARNMSG]);
				}
			}
		}

		if((msg.othersCode()!=CMMsg.NO_EFFECT)
		   &&(msg.othersMessage()!=null))
		{
			if((msg.targetMinor()==CMMsg.TYP_GET)
			&&(msg.target() instanceof Item)
			&&(laws.bannedSubstances().size()>0))
			{
				final String rsc=RawMaterial.CODES.NAME(((Item)msg.target()).material()).toUpperCase();
				for(int i=0;i<laws.bannedSubstances().size();i++)
				{
					final List<String> V=laws.bannedSubstances().get(i);
					for(int v=0;v<V.size();v++)
					{
						if((CMLib.english().containsString(msg.target().name(),V.get(v)))
						||rsc.equalsIgnoreCase(V.get(v)))
						{
							final String[] info=laws.bannedBits().get(i);
							fillOutWarrant(msg.source(),
											laws,
											myArea,
											msg.target(),
											info[Law.BIT_CRIMELOCS],
											info[Law.BIT_CRIMEFLAGS],
											info[Law.BIT_CRIMENAME],
											info[Law.BIT_SENTENCE],
											info[Law.BIT_WARNMSG]);
						}
					}
				}

			}
			if(msg.sourceMinor()==CMMsg.TYP_ENTER)
				testEntryLaw(laws,myArea,msg.source(),R);

			for(int i=0;i<laws.otherCrimes().size();i++)
			{
				final List<String> V=laws.otherCrimes().get(i);
				for(int v=0;v<V.size();v++)
				{
					if(CMLib.english().containsString(msg.othersMessage(),V.get(v)))
					{
						final String[] info=laws.otherBits().get(i);
						fillOutWarrant(msg.source(),
										laws,
										myArea,
										msg.target(),
										info[Law.BIT_CRIMELOCS],
										info[Law.BIT_CRIMEFLAGS],
										info[Law.BIT_CRIMENAME],
										info[Law.BIT_SENTENCE],
										info[Law.BIT_WARNMSG]);
					}
				}
			}
		}
	}

	public void haveMobReactToLaw(MOB mob, MOB officer)
	{
		if((mob.isMonster())
		&&(!CMLib.flags().isSitting(mob))
		&&(mob.amFollowing()==null)
		&&(!mob.isInCombat()))
		{
			final boolean good=CMLib.flags().isGood(mob);
			final boolean evil=CMLib.flags().isEvil(mob);
			final boolean neutral=(!good)&&(!evil);
			if(evil
			||(neutral&&(CMLib.dice().rollPercentage()>50))
			||(CMLib.flags().flaggedBehaviors(mob,Behavior.FLAG_POTENTIALLYAGGRESSIVE).size()>0))
			{
				if(mob.phyStats().level()>(officer.phyStats().level()/2))
					mob.setVictim(officer);
				else
				if(!CMLib.flags().isAnimalIntelligence(mob))
					mob.enqueCommand(CMParms.parse("FLEE"),MUDCmdProcessor.METAFLAG_FORCED|MUDCmdProcessor.METAFLAG_ORDER,1);
			}
			else
			if((good||neutral)
			&&(!CMLib.flags().isAnimalIntelligence(mob)))
			{
				mob.makePeace(true);
				mob.doCommand(CMParms.parse("SIT"),MUDCmdProcessor.METAFLAG_FORCED|MUDCmdProcessor.METAFLAG_ORDER);
			}
			else
			if((CMLib.flags().isAnimalIntelligence(mob))&&(CMLib.dice().rollPercentage()>50))
			{
				mob.makePeace(true);
				mob.doCommand(CMParms.parse("SIT"),MUDCmdProcessor.METAFLAG_FORCED|MUDCmdProcessor.METAFLAG_ORDER);
			}
		}
	}

	@Override
	public boolean tick(Tickable ticking, int tickID)
	{
		super.tick(ticking,tickID);

		if(!(ticking instanceof Area))
			return true;
		if(tickID!=Tickable.TICKID_AREA)
			return true;
		final Area myArea=(Area)ticking;

		if(!theLawIsEnabled())
			return true;

		final Law laws=getLaws(myArea,false);
		if(!laws.lawIsActivated())
		{
			laws.warrants().clear();
			laws.oldWarrants().clear();
			return true;
		}
		final boolean debugging=CMSecurity.isDebugging(CMSecurity.DbgFlag.ARREST);

		laws.propertyTaxTick(myArea,debugging);

		final HashSet<String> handled=new HashSet<String>();
		for(final LegalWarrant W : laws.warrants())
		{
			if((W.criminal()==null)||(W.criminal().location()==null))
			{
				if(debugging)
					Log.debugOut("Arrest","("+lastAreaName+"): Tick: "+W.crime()+": Criminal or Location is null. Skipping.");
				continue;
			}

			if(!isStillACrime(W,debugging))
			{
				if(getWarrantsOf(myArea,W.criminal()).size()== 0)
				{
					unCuff(W.criminal());
					if(W.arrestingOfficer()!=null)
					{
						dismissOfficer(W.arrestingOfficer());
					}
					W.setArrestingOfficer(myArea,null);
				}
				W.setOffenses(W.offenses()+1);
				laws.oldWarrants().add(W);
				laws.warrants().remove(W);
				if(debugging)
					Log.debugOut("Arrest","("+lastAreaName+"): Tick: "+W.crime()+": No longer a crime.");
				continue;
			}


			if(!CMath.bset(W.punishment(),Law.PUNISHMENTMASK_SEPARATE))
			{
				if(handled.contains(W.criminal().Name()))
					continue;
				handled.add(W.criminal().Name());
			}
			else
			{
				if(handled.contains(W.criminal().Name()+"/"+W.crime()))
					continue;
				handled.add(W.criminal().Name()+"/"+W.crime());
			}
			if(debugging)
				Log.debugOut("Arrest","("+lastAreaName+"): Tick: Handling "+W.crime()+" for "+W.criminal().Name()+": State "+W.state());

			processWarrant(myArea, laws, W, debugging);
		}
		return true;
	}

	protected void fileArrestResister(Law laws, Area myArea, LegalWarrant W)
	{
		if((W.criminal()!=null)
		&&(W.arrestingOfficer()!=null)
		&&(!W.arrestingOfficer().amDead())
		&&(!W.crime().equalsIgnoreCase("pardoned"))
		&&(!CMLib.flags().isInTheGame(W.criminal(),true))
		&&(isStillACrime(W,false)))
		{
			if(laws.basicCrimes().containsKey("RESISTINGARREST"))
			{
				final String[] info=laws.basicCrimes().get("RESISTINGARREST");
				fillOutWarrant(W.criminal(),
								laws,
								myArea,
								null,
								info[Law.BIT_CRIMELOCS],
								info[Law.BIT_CRIMEFLAGS],
								info[Law.BIT_CRIMENAME],
								info[Law.BIT_SENTENCE],
								info[Law.BIT_WARNMSG]);
			}
		}
	}

	protected void processWarrant(Area myArea, Law laws, LegalWarrant W, boolean debugging)
	{
		MOB officer=W.arrestingOfficer();
		if((officer!=null) && (W.state()!=Law.STATE_SEEKING))
		{
			for(int b=0;b<officer.numBehaviors();b++)
			{
				final Behavior B=officer.fetchBehavior(b);
				if(B instanceof MobileBehavior)
					((MobileBehavior)B).suspendMobility(1);
			}
		}
		switch(W.state())
		{
		case Law.STATE_SEEKING:
			{
				if((officer==null)||(!W.criminal().location().isInhabitant(officer)))
				   officer=null;
				if(officer==null)
					officer=getElligibleOfficer(laws,myArea,W.criminal(),W.victim());
				W.setTravelAttemptTime(0);
				if((officer!=null)
				&&(W.criminal().location()!=null)
				&&(W.criminal().location().isInhabitant(officer))
				&&(!W.criminal().amDead())
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(CMLib.flags().canBeSeenBy(W.criminal(),officer))
				&&(canFocusOn(officer,W.criminal())))
				{
					if(CMSecurity.isAllowed(W.criminal(),W.criminal().location(),CMSecurity.SecFlag.ABOVELAW))
					{
						CMLib.commands().postSay(officer,W.criminal(),L("Damn, I can't arrest you."),false,false);
						if(CMSecurity.isAllowedEverywhere(W.criminal(),CMSecurity.SecFlag.ABOVELAW))
						{
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							W.setArrestingOfficer(myArea,null);
						}
					}
					else
					if(W.crime().equalsIgnoreCase("pardoned"))
					{
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
					}
					else
					if(judgeMe(laws,null,officer,W.criminal(),W,myArea,debugging))
					{
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						dismissOfficer(officer);
						W.setArrestingOfficer(myArea,null);
					}
					else
					if(W.state()!=Law.STATE_DETAINING)
					{
						if(!CMLib.flags().isAnimalIntelligence(W.criminal()))
						{
							W.setArrestingOfficer(myArea,officer);
							final LegalWarrant copKillerW=laws.getCopkiller(myArea,this,W.criminal());
							if(copKillerW!=null)
							{
								CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),laws.getMessage(Law.MSG_COPKILLER),false,false);
								W.setState(Law.STATE_SUBDUEING);
								W.arrestingOfficer().setVictim(W.criminal());
								processWarrant(myArea, laws, W, debugging);
								return;
							}
							final LegalWarrant lawResistW=laws.getLawResister(myArea,this,W.criminal());
							if(lawResistW!=null)
							{
								CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),laws.getMessage(Law.MSG_RESISTFIGHT),false,false);
								W.setState(Law.STATE_SUBDUEING);
								W.arrestingOfficer().setVictim(W.criminal());
								processWarrant(myArea, laws, W, debugging);
								return;
							}

							CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),L("You are under arrest @x1! Sit down on the ground immediately!",restOfCharges(laws,W.criminal())),false,false);
							W.setState(Law.STATE_ARRESTING);
						}
						else
						{
							W.setArrestingOfficer(myArea,officer);
							CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),L("You are headed to the pound for @x1!",restOfCharges(laws,W.criminal())),false,false);
							W.setState(Law.STATE_ARRESTING);
						}
					}
				}
				else
				if(W.crime().equalsIgnoreCase("pardoned"))
				{
					fileAllWarrants(laws,W,W.criminal());
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
				}
				break;
			}
		case Law.STATE_ARRESTING:
			{
				W.setTravelAttemptTime(0);
				if((officer!=null)
				&&(W.criminal().location()!=null)
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(!W.criminal().amDead())
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(CMLib.flags().canBeSeenBy(W.criminal(),officer)))
				{
					if(officer.isInCombat())
					{
						if(officer.getVictim()==W.criminal())
						{
							CMLib.commands().postSay(officer,W.criminal(),laws.getMessage(Law.MSG_RESISTFIGHT),false,false);
							W.setState(Law.STATE_SUBDUEING);
						}
						else
						{
							W.setArrestingOfficer(myArea,null);
							W.setState(Law.STATE_SEEKING);
						}
					}
					else
					if(W.crime().equalsIgnoreCase("pardoned"))
					{
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
					}
					else
					{
						haveMobReactToLaw(W.criminal(),officer);
						if(CMLib.flags().isSitting(W.criminal())||CMLib.flags().isSleeping(W.criminal()))
						{
							if(!CMLib.flags().isAnimalIntelligence(W.criminal()))
								CMLib.commands().postSay(officer,W.criminal(),laws.getMessage(Law.MSG_NORESIST),false,false);
							W.setState(Law.STATE_SUBDUEING);
						}
						else
						if((System.currentTimeMillis() - W.getLastStateChangeTime())>(CMath.mul(CMProps.getTickMillis(),1.5))) // only leave this state after they've had enough time.
						{
							W.setState(Law.STATE_SUBDUEING);
							CMLib.commands().postSay(officer,W.criminal(),laws.getMessage(Law.MSG_RESISTWARN),false,false);
						}
						if(W.criminal().isMonster())
							haveMobReactToLaw(W.criminal(),officer);
					}
				}
				else
				{
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_SUBDUEING:
			{
				if((officer!=null)
				&&(W.criminal().location()!=null)
				&&(W.criminal().location().isInhabitant(officer))
				&&(!W.criminal().amDead())
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(CMLib.flags().canBeSeenBy(W.criminal(),officer)))
				{
					W.setTravelAttemptTime(0);
					haveMobReactToLaw(W.criminal(),officer);
					if(W.crime().equalsIgnoreCase("pardoned"))
					{
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
					}
					else
					if(CMLib.flags().isStanding(W.criminal()))
					{
						if(!W.arrestingOfficer().isInCombat())
							CMLib.commands().postSay(officer,W.criminal(),laws.getMessage(Law.MSG_RESIST),false,false);

						Ability A=CMClass.getAbility("Skill_ArrestingSap");
						if(A!=null)
						{
							final int curPoints=(int)Math.round(CMath.div(W.criminal().curState().getHitPoints(),W.criminal().maxState().getHitPoints())*100.0);
							A.setProficiency(100);
							A.setAbilityCode(10);
							if(!A.invoke(officer,W.criminal(),(curPoints<=25),0))
							{
								A=CMClass.getAbility("Skill_Trip");
								if(A!=null)
								{
									A.setProficiency(100);
									A.setAbilityCode(30);
									if(!A.invoke(officer,W.criminal(),(curPoints<=50),0))
										CMLib.combat().postAttack(officer,W.criminal(),officer.fetchWieldedItem());
								}
							}
						}
					}
					final Ability cuff=W.criminal().fetchEffect("Skill_HandCuff");
					if((CMLib.flags().isSitting(W.criminal())||(cuff!=null)||(CMLib.flags().isSleeping(W.criminal())))
					&&(!W.criminal().amDead())
					&&(CMLib.flags().isInTheGame(W.criminal(),true)))
					{
						makePeace(officer.location());
						// cuff him!
						if(CMLib.flags().isAnimalIntelligence(W.criminal()))
							W.setState(Law.STATE_JAILING);
						else
							W.setState(Law.STATE_MOVING);
						if(cuff!=null){ cuff.unInvoke(); W.criminal().delEffect(cuff);}
						Ability A=CMClass.getAbility("Skill_HandCuff");
						if(A!=null)
							A.invoke(officer,W.criminal(),true,0);
						W.criminal().makePeace(true);
						makePeace(officer.location());
						A=W.criminal().fetchEffect("Skill_ArrestingSap");
						if(A!=null)
							A.unInvoke();
						A=W.criminal().fetchEffect("Fighter_Whomp");
						if(A!=null)
							A.unInvoke();
						A=W.criminal().fetchEffect("Skill_Trip");
						if(A!=null)
							A.unInvoke();
						makePeace(officer.location());
						CMLib.commands().postStand(W.criminal(),true);
						W.setTravelAttemptTime(System.currentTimeMillis());
						if(trackTheJudge(officer,myArea,laws))
							makePeace(officer.location());
						else
						{
							makePeace(officer.location());
							CMLib.commands().postSay(officer,W.criminal(),L("Since there is no judge, you may go."),false,false);
							W.setTravelAttemptTime(0);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							if(W.arrestingOfficer()!=null)
								dismissOfficer(W.arrestingOfficer());
						}
					}
					else
					{
						CMLib.commands().postSay(officer,null,L("Hmph."),false,false);
						fileArrestResister(laws,myArea,W);
						W.setTravelAttemptTime(0);
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
						W.setState(Law.STATE_SEEKING);
					}
				}
				else
				{
					if(officer!=null)
					{
						CMLib.commands().postSay(officer,null,L("Darn."),false,false);
						fileArrestResister(laws,myArea,W);
					}
					W.setTravelAttemptTime(0);
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_MOVING:
			{
				if((officer!=null)
				&&(W.criminal().location().isInhabitant(officer))
				&&(!W.criminal().amDead())
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&((W.travelAttemptTime()==0)||((System.currentTimeMillis()-W.travelAttemptTime())<(5*60*1000)))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true)))
				{
					if(W.criminal().curState().getMovement()<50)
						W.criminal().curState().setMovement(50);
					if(officer.curState().getMovement()<50)
						officer.curState().setMovement(50);
					makePeace(officer.location());
					if(officer.isMonster())
						CMLib.commands().postLook(officer,true);
					if(getTheJudgeHere(laws,officer.location())!=null)
						W.setState(Law.STATE_REPORTING);
					else
					if(!CMLib.flags().isTracking(officer))
					{
						if(!trackTheJudge(officer,myArea,laws))
						{
							CMLib.commands().postSay(officer,null,L("Now where was that court?."),false,false);
							W.setTravelAttemptTime(0);
							unCuff(W.criminal());
							W.setArrestingOfficer(myArea,null);
							W.setState(Law.STATE_SEEKING);
						}
					}
					else
					if((CMLib.dice().rollPercentage()>75)&&(laws.chitChat().size()>0))
						CMLib.commands().postSay(officer,W.criminal(),laws.chitChat().get(CMLib.dice().roll(1,laws.chitChat().size(),-1)),false,false);
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer lost criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("Drat! Lost another one!"),false,false);
						fileArrestResister(laws,myArea,W);
					}
					W.setTravelAttemptTime(0);
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_REPORTING:
			{
				if((officer!=null)
				&&(W.criminal().location().isInhabitant(officer))
				&&(!W.criminal().amDead())
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true)))
				{
					final MOB judge=getTheJudgeHere(laws,officer.location());
					if(judge==null)
					{
						W.setState(Law.STATE_MOVING);
						if(!trackTheJudge(officer,myArea,laws))
						{
							CMLib.commands().postSay(officer,null,L("Where was that darn court!"),false,false);
							W.setTravelAttemptTime(0);
							unCuff(W.criminal());
							W.setArrestingOfficer(myArea,null);
							W.setState(Law.STATE_SEEKING);
						}

					}
					else
					if(CMLib.flags().isAliveAwakeMobile(judge,true))
					{
						CMLib.tracking().stopTracking(officer);
						W.setTravelAttemptTime(0);
						String sirmaam="Sir";
						if(Character.toString((char)judge.charStats().getStat(CharStats.STAT_GENDER)).equalsIgnoreCase("F"))
							sirmaam="Ma'am";
						CMLib.commands().postSay(officer,judge,L("@x1, @x2 has been arrested @x3.",sirmaam,W.criminal().name(),restOfCharges(laws,W.criminal())),false,false);
						final Vector<LegalWarrant> warrants=getRelevantWarrants(laws.warrants(),W,W.criminal());
						for(int w2=0;w2<warrants.size();w2++)
						{
							final LegalWarrant W2=warrants.elementAt(w2);
							if(W2.witness()!=null)
								CMLib.commands().postSay(officer,judge,L("The charge of @x1 was witnessed by @x2.",fixCharge(W2),W2.witness().name()),false,false);
						}
						W.setState(Law.STATE_WAITING);
						if((highestCrimeAction(laws,W,W.criminal())==Law.PUNISHMENT_EXECUTE)
						&&(judge.location()!=null))
						{
							final List<String> channels=CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.EXECUTIONS);
							for(int i=0;i<channels.size();i++)
								CMLib.commands().postChannel(judge,channels.get(i),L("@x1 is being executed at @x2 for @x3 crimes.",W.criminal().Name(),judge.location().displayText(judge),W.criminal().charStats().hisher()),true);
						}
					}
					else
					{
						CMLib.commands().postSay(officer,W.criminal(),L("I guess court is not in session today."),false,false);
						W.setTravelAttemptTime(0);
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
						W.setState(Law.STATE_SEEKING);
					}
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer can't report criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("Wha? Where'd he go?"),false,false);
						fileArrestResister(laws,myArea,W);
					}
					W.setTravelAttemptTime(0);
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_WAITING:
			{
				if((officer!=null)
				&&(!W.criminal().amDead())
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true)))
				{
					final MOB judge=getTheJudgeHere(laws,officer.location());
					if(judge==null)
					{
						W.setState(Law.STATE_MOVING);
						if(!trackTheJudge(officer,myArea,laws))
						{
							CMLib.commands().postSay(officer,null,L("Where was that darn court?!"),false,false);
							W.setTravelAttemptTime(0);
							unCuff(W.criminal());
							W.setArrestingOfficer(myArea,null);
							W.setState(Law.STATE_SEEKING);
						}
					}
					else
					if(CMLib.flags().isAliveAwakeMobile(judge,true))
					{
						if(judgeMe(laws,judge,officer,W.criminal(),W,myArea,debugging))
						{
							W.setTravelAttemptTime(0);
							unCuff(W.criminal());
							dismissOfficer(officer);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							W.setArrestingOfficer(myArea,null);
						}
						// else, still stuff to do
					}
					else
					{
						CMLib.commands().postSay(officer,W.criminal(),L("Court is not in session today."),false,false);
						W.setTravelAttemptTime(0);
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
						W.setState(Law.STATE_SEEKING);
					}
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer can't await criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("Wha? Huh?"),false,false);
						fileArrestResister(laws,myArea,W);
					}
					W.setTravelAttemptTime(0);
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_PAROLING:
			{
				W.setTravelAttemptTime(0);
				if((officer!=null)
				&&(!W.criminal().amDead())
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&(CMLib.flags().canBeSeenBy(W.criminal(),officer)))
				{
					final MOB judge=getTheJudgeHere(laws,officer.location());
					fileAllWarrants(laws,W,W.criminal());
					unCuff(W.criminal());
					if((judge!=null)
					&&(CMLib.flags().isAliveAwakeMobile(judge,true)))
					{
						judge.location().show(judge,W.criminal(),CMMsg.MSG_OK_VISUAL,L("<S-NAME> put(s) <T-NAME> on parole!"));
						final Ability A=CMClass.getAbility("Prisoner");
						A.startTickDown(judge,W.criminal(),W.jailTime());
						W.criminal().recoverPhyStats();
						W.criminal().recoverCharStats();
						CMLib.commands().postSay(judge,W.criminal(),laws.getMessage(Law.MSG_PAROLEDISMISS),false,false);
						dismissOfficer(officer);
						W.setArrestingOfficer(myArea,null);
						W.criminal().tell(L("\n\r\n\r"));
						if(W.criminal().isMonster())
							CMLib.tracking().wanderAway(W.criminal(),true,true);
					}
					else
					{
						CMLib.commands().postSay(officer,null,L("No court today."),false,false);
						unCuff(W.criminal());
						if(W.arrestingOfficer()!=null)
							dismissOfficer(W.arrestingOfficer());
						W.setArrestingOfficer(myArea,null);
						W.setState(Law.STATE_SEEKING);
					}
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer can't parole criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("That was wierd."),false,false);
					}
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_JAILING:
			{
				if((officer!=null)
				&&(!W.criminal().amDead())
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&(CMLib.flags().canBeSeenBy(W.criminal(),officer)))
				{
					final Room jail=findTheJail(W.criminal(),myArea,laws);
					if(jail!=null)
					{

						Ability A=W.criminal().fetchEffect("Prisoner");
						if(A!=null){ A.unInvoke(); W.criminal().delEffect(A);}

						makePeace(officer.location());
						W.setJail(jail);
						// cuff him!
						W.setState(Law.STATE_MOVING2);
						A=CMClass.getAbility("Skill_HandCuff");
						if((A!=null)&&(!CMLib.flags().isBoundOrHeld(W.criminal())))
							A.invoke(officer,W.criminal(),true,0);
						W.criminal().makePeace(true);
						makePeace(officer.location());
						CMLib.commands().postStand(W.criminal(),true);
						CMLib.tracking().stopTracking(officer);
						A=CMClass.getAbility("Skill_Track");
						if(A!=null)
						{
							W.setTravelAttemptTime(System.currentTimeMillis());
							A.setAbilityCode(1);
							A.invoke(officer,CMParms.parse(CMLib.map().getExtendedRoomID(jail)+trackingModifiers(officer)),jail,true,0);
						}
						if(officer.fetchEffect("Skill_Track")==null)
						{
							W.setTravelAttemptTime(0);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							CMLib.commands().postSay(officer,W.criminal(),L("I can't find the jail, you are free to go."),false,false);
							dismissOfficer(officer);
							W.setArrestingOfficer(myArea,null);
						}
						makePeace(officer.location());
					}
					else
					{
						W.setTravelAttemptTime(0);
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),L("But since there IS no jail, I will let you go."),false,false);
						dismissOfficer(officer);
						W.setArrestingOfficer(myArea,null);
					}
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer can't jail criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("Crazy."),false,false);
						fileArrestResister(laws,myArea,W);
					}
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
					W.setTravelAttemptTime(0);
				}
			}
			break;
			case Law.STATE_DETAINING:
			{
				if((officer!=null)
				&&(!W.criminal().amDead())
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&(CMLib.flags().canBeSeenBy(W.criminal(),officer)))
				{
					final Room jail=findTheDetentionCenter(W.criminal(),myArea,laws,W);
					final int time=getDetainTime(laws,W,W.criminal());
					if((jail!=null)&&(time>=0))
					{
						Ability A=W.criminal().fetchEffect("Prisoner");
						if(A!=null){ A.unInvoke(); W.criminal().delEffect(A);}

						makePeace(officer.location());
						W.setJail(jail);
						W.setJailTime(time);
						// cuff him!
						W.setState(Law.STATE_MOVING3);
						A=CMClass.getAbility("Skill_HandCuff");
						W.criminal().basePhyStats().setDisposition(W.criminal().basePhyStats().disposition()|PhyStats.IS_SITTING);
						W.criminal().phyStats().setDisposition(W.criminal().phyStats().disposition()|PhyStats.IS_SITTING);
						if((A!=null)&&(!CMLib.flags().isBoundOrHeld(W.criminal())))
							A.invoke(officer,W.criminal(),true,0);
						W.criminal().makePeace(true);
						makePeace(officer.location());
						CMLib.commands().postStand(W.criminal(),true);
						CMLib.tracking().stopTracking(officer);
						A=CMClass.getAbility("Skill_Track");
						if(A!=null)
						{
							W.setTravelAttemptTime(System.currentTimeMillis());
							A.setAbilityCode(1);
							A.invoke(officer,CMParms.parse(CMLib.map().getExtendedRoomID(jail)+trackingModifiers(officer)),jail,true,0);
						}
						if(officer.fetchEffect("Skill_Track")==null)
						{
							W.setTravelAttemptTime(0);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							CMLib.commands().postSay(officer,W.criminal(),L("I can't find the detention center, you are free to go."),false,false);
							dismissOfficer(officer);
							W.setArrestingOfficer(myArea,null);
						}
						makePeace(officer.location());
					}
					else
					{
						W.setTravelAttemptTime(0);
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),L("But since there IS no detention center, I will let you go."),false,false);
						dismissOfficer(officer);
						W.setArrestingOfficer(myArea,null);
					}
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer can't detain criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("Sad."),false,false);
						fileArrestResister(laws,myArea,W);
						dismissOfficer(officer);
					}
					W.setTravelAttemptTime(0);
					fileAllWarrants(laws,W,W.criminal());
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_EXECUTING:
			{
				final MOB criminalM=W.criminal();
				if((officer!=null)
				&&(criminalM!=null)
				&&(CMLib.flags().isInTheGame(criminalM,true))
				&&(!criminalM.amDead())
				&&(criminalM.location().isInhabitant(officer))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(!W.crime().equalsIgnoreCase("pardoned"))
				&&(CMLib.flags().canBeSeenBy(criminalM,officer))
				&&(canFocusOn(officer,criminalM)))
				{
					final MOB judgeM=getTheJudgeHere(laws,officer.location());
					if((judgeM!=null)
					&&(CMLib.flags().isAliveAwakeMobile(judgeM,true))
					&&(judgeM.location()==criminalM.location()))
					{
						dismissOfficer(officer);
						CMLib.commands().postFollow(criminalM, null, true);
						for(final Enumeration<Pair<MOB,Short>> f=criminalM.followers();f.hasMoreElements();)
						{
							final Pair<MOB,Short> F=f.nextElement();
							CMLib.commands().postFollow(F.first, null, true);
						}
						Ability A=CMClass.getAbility("Prisoner");
						A.startTickDown(judgeM,criminalM,100);
						A=judgeM.fetchAbility("Fighter_Behead");
						if(A==null)
							A=judgeM.fetchAbility("Prayer_Stoning");
						boolean served=false;
						if(A!=null)
						{
							A.setProficiency(100);
							served=A.invoke(judgeM,criminalM,false,0);
						}
						fileAllWarrants(laws,null,criminalM);
						criminalM.recoverPhyStats();
						criminalM.recoverCharStats();
						if(!served)
							CMLib.combat().postAttack(judgeM,criminalM,judgeM.fetchWieldedItem());
						W.setArrestingOfficer(myArea,null);
						W.setTravelAttemptTime(0);
						unCuff(criminalM);
					}
					else
					{
						CMLib.commands().postSay(officer,null,L("Looks like court is not in session."),false,false);
						W.setTravelAttemptTime(0);
						unCuff(criminalM);
						if(W.arrestingOfficer()!=null)
							dismissOfficer(W.arrestingOfficer());
						W.setArrestingOfficer(myArea,null);
						W.setState(Law.STATE_SEEKING);
					}
				}
				else
				{
					if(officer!=null)
					{
						DebugLogLostConvicts("Officer can't execute criminal: ",W,officer);
						CMLib.commands().postSay(officer,null,L("Didn't see that coming."),false,false);
						fileArrestResister(laws,myArea,W);
					}
					W.setTravelAttemptTime(0);
					unCuff(criminalM);
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
				}
			}
			break;
		case Law.STATE_MOVING2:
			{
				if((officer!=null)
				&&(!W.criminal().amDead())
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&((W.travelAttemptTime()==0)||((System.currentTimeMillis()-W.travelAttemptTime())<(5*60*1000)))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(W.jail()!=null))
				{
					if(W.criminal().curState().getMovement()<50)
						W.criminal().curState().setMovement(50);
					if(officer.curState().getMovement()<50)
						officer.curState().setMovement(50);
					makePeace(officer.location());
					if(officer.isMonster())
						CMLib.commands().postLook(officer,true);
					if(W.jail()==W.criminal().location())
					{
						unCuff(W.criminal());
						final Ability A=CMClass.getAbility("Prisoner");
						if(A!=null)
							A.startTickDown(officer,W.criminal(),W.jailTime()+5);
						W.criminal().recoverPhyStats();
						W.criminal().recoverCharStats();
						dismissOfficer(officer);
						if(W.criminal().fetchEffect("Prisoner")==null)
						{
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
						}
						else
							W.setState(Law.STATE_RELEASE);
					}
					else
					if(!CMLib.flags().isTracking(officer))
					{
						final Ability A=CMClass.getAbility("Skill_Track");
						if(A!=null)
						{
							CMLib.tracking().stopTracking(officer);
							A.setAbilityCode(1); // tells track to cache the path
							A.invoke(officer,CMParms.parse(CMLib.map().getExtendedRoomID(W.jail())+trackingModifiers(officer)),W.jail(),true,0);
						}
						if(officer.fetchEffect("Skill_Track")==null)
						{
							W.setTravelAttemptTime(0);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							CMLib.commands().postSay(officer,W.criminal(),L("I lost the jail, so you are free to go."),false,false);
							dismissOfficer(officer);
							W.setArrestingOfficer(myArea,null);
						}
					}
					else
					if((CMLib.dice().rollPercentage()>75)&&(laws.chitChat2().size()>0))
						CMLib.commands().postSay(officer,W.criminal(),laws.chitChat2().get(CMLib.dice().roll(1,laws.chitChat2().size(),-1)),false,false);
				}
				else
				{
					DebugLogLostConvicts("Officer can't move2 criminal: ",W,officer);
					fileArrestResister(laws,myArea,W);
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
					W.setTravelAttemptTime(0);
				}
			}
			break;
			case Law.STATE_MOVING3:
			{
				if((officer!=null)
				&&(!W.criminal().amDead())
				&&(W.criminal().location().isInhabitant(officer))
				&&(CMLib.flags().isInTheGame(W.criminal(),true))
				&&((W.travelAttemptTime()==0)||((System.currentTimeMillis()-W.travelAttemptTime())<(5*60*1000)))
				&&(CMLib.flags().isAliveAwakeMobile(officer,true))
				&&(W.jail()!=null))
				{
					if(W.criminal().curState().getMovement()<50)
						W.criminal().curState().setMovement(50);
					if(officer.curState().getMovement()<50)
						officer.curState().setMovement(50);
					makePeace(officer.location());
					if(officer.isMonster())
						CMLib.commands().postLook(officer,true);
					if(W.jail()==W.criminal().location())
					{
						unCuff(W.criminal());
						final Ability A=CMClass.getAbility("Prisoner");
						if(A!=null)
							A.startTickDown(officer,W.criminal(),W.jailTime()+5);
						W.criminal().recoverPhyStats();
						W.criminal().recoverCharStats();
						dismissOfficer(officer);
						if(W.criminal().fetchEffect("Prisoner")==null)
						{
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
						}
						else
							W.setState(Law.STATE_RELEASE);
					}
					else
					if(CMLib.flags().flaggedAffects(officer,Ability.FLAG_TRACKING).size()==0)
					{
						final Ability A=CMClass.getAbility("Skill_Track");
						if(A!=null)
						{
							CMLib.tracking().stopTracking(officer);
							A.setAbilityCode(1); // tells track to cache the path
							A.invoke(officer,CMParms.parse(CMLib.map().getExtendedRoomID(W.jail())+trackingModifiers(officer)),W.jail(),true,0);
						}
						if(officer.fetchEffect("Skill_Track")==null)
						{
							W.setTravelAttemptTime(0);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							CMLib.commands().postSay(officer,W.criminal(),L("I lost the detention center, so you are free to go."),false,false);
							dismissOfficer(officer);
							W.setArrestingOfficer(myArea,null);
						}
					}
					else
					if((CMLib.dice().rollPercentage()>75)&&(laws.chitChat3().size()>0))
						CMLib.commands().postSay(officer,W.criminal(),laws.chitChat3().get(CMLib.dice().roll(1,laws.chitChat3().size(),-1)),false,false);
				}
				else
				{
					DebugLogLostConvicts("Officer can't move3 criminal: ",W,officer);
					fileArrestResister(laws,myArea,W);
					unCuff(W.criminal());
					W.setArrestingOfficer(myArea,null);
					W.setState(Law.STATE_SEEKING);
					W.setTravelAttemptTime(0);
					fileAllWarrants(laws,W,W.criminal());
				}
			}
			break;
		case Law.STATE_RELEASE:
			{
				if(((W.criminal().fetchEffect("Prisoner")==null)||(W.crime().equalsIgnoreCase("pardoned")))
				&&(!W.criminal().amDead())
				&&(W.jail()!=null))
				{
					final Ability P=W.criminal().fetchEffect("Prisoner");
					if(P!=null)
						P.unInvoke();
					if(CMath.bset(highestCrimeAction(laws,W,W.criminal()),Law.PUNISHMENTMASK_NORELEASE))
					{
						W.setTravelAttemptTime(0);
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						W.setArrestingOfficer(myArea,null);
					}
					else
					if(W.criminal().location()==W.jail())
					{
						if((officer==null)
						||(!CMLib.flags().isAliveAwakeMobile(officer,true))
						||(W.criminal().amDead())
						||(!CMLib.flags().isInTheGame(W.criminal(),true))
						||(!W.criminal().location().isInhabitant(officer)))
						{
							W.setArrestingOfficer(myArea,getAnyElligibleOfficer(laws,W.jail().getArea(),W.criminal(),W.victim()));
							if(W.arrestingOfficer()==null)
								W.setArrestingOfficer(myArea,getAnyElligibleOfficer(laws,myArea,W.criminal(),W.victim()));
							if(W.arrestingOfficer()==null)
								break;
							officer=W.arrestingOfficer();
							W.jail().bringMobHere(officer,false);
							if(!canFocusOn(officer,W.criminal()))
							{
								W.jail().show(officer,W.criminal(),CMMsg.MSG_QUIETMOVEMENT,L("<S-NAME> arrive(s) to release <T-NAME>, but can't find <T-HIM-HER>."));
								dismissOfficer(officer);
								W.setArrestingOfficer(myArea,null);
							}
							else
								W.jail().show(officer,W.criminal(),CMMsg.MSG_QUIETMOVEMENT,L("<S-NAME> arrive(s) to release <T-NAME>."));
							final Ability A=CMClass.getAbility("Skill_HandCuff");
							if((A!=null)&&(!CMLib.flags().isBoundOrHeld(W.criminal())))
								A.invoke(officer,W.criminal(),true,0);
						}
						W.setReleaseRoom(getReleaseRoom(laws,myArea,W.criminal(),W));
						W.criminal().makePeace(true);
						makePeace(officer.location());
						CMLib.tracking().stopTracking(officer);
						final Ability A=CMClass.getAbility("Skill_Track");
						if(A!=null)
						{
							W.setTravelAttemptTime(System.currentTimeMillis());
							A.invoke(officer,CMParms.parse(CMLib.map().getExtendedRoomID(W.releaseRoom())+trackingModifiers(officer)),W.releaseRoom(),true,0);
						}
						if(officer.fetchEffect("Skill_Track")==null)
						{
							W.setTravelAttemptTime(0);
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());
							CMLib.commands().postSay(officer,W.criminal(),L("Well, you can always recall."),false,false);
							dismissOfficer(officer);
							W.setArrestingOfficer(myArea,null);
						}
					}
					else
					if(W.releaseRoom()!=null)
					{
						if(W.criminal().location()==W.releaseRoom())
						{
							fileAllWarrants(laws,W,W.criminal());
							unCuff(W.criminal());

							if(officer!=null)
							{
								if((CMLib.flags().isAliveAwakeMobile(officer,true))
								&&(W.criminal().location().isInhabitant(officer)))
									CMLib.commands().postSay(officer,null,laws.getMessage(Law.MSG_LAWFREE),false,false);
								dismissOfficer(officer);
							}
							W.setTravelAttemptTime(0);
						}
						else
						{
							if((officer!=null)
							&&(CMLib.flags().isAliveAwakeMobile(officer,true))
							&&(W.criminal().location().isInhabitant(officer))
							&&((W.travelAttemptTime()==0)||((System.currentTimeMillis()-W.travelAttemptTime())<(5*60*1000))))
							{
								if(officer.isMonster())
									CMLib.commands().postLook(officer,true);
								if(W.criminal().curState().getMovement()<20)
									W.criminal().curState().setMovement(20);
								if(officer.curState().getMovement()<20)
									officer.curState().setMovement(20);
								if(W.arrestingOfficer().fetchEffect("Skill_Track")==null)
								{
									CMLib.tracking().stopTracking(officer);
									final Ability A=CMClass.getAbility("Skill_Track");
									if(A!=null)
										A.invoke(officer,CMParms.parse(CMLib.map().getExtendedRoomID(W.releaseRoom())+trackingModifiers(officer)),W.releaseRoom(),true,0);
									if(W.arrestingOfficer().fetchEffect("Skill_Track")==null)
									{
										W.setTravelAttemptTime(0);
										fileAllWarrants(laws,W,W.criminal());
										unCuff(W.criminal());
										CMLib.commands().postSay(W.arrestingOfficer(),W.criminal(),L("Don't worry, you can always recall."),false,false);
										dismissOfficer(W.arrestingOfficer());
										W.setArrestingOfficer(myArea,null);
									}
								}
							}
							else
							{
								if(officer!=null)
								{
									DebugLogLostConvicts("Officer can't release criminal: ",W,officer);
									CMLib.commands().postSay(officer,null,L("There's always recall."),false,false);
								}
								W.setTravelAttemptTime(0);
								fileAllWarrants(laws,W,W.criminal());
								unCuff(W.criminal());
								if(officer!=null)
									dismissOfficer(officer);
							}
						}
					}
					else
					{
						if(W.arrestingOfficer()!=null)
						{
							DebugLogLostConvicts("Officer can't release2 criminal: ",W,W.arrestingOfficer());
							CMLib.commands().postSay(W.arrestingOfficer(),null,L("Well, he can always recall."),false,false);
						}
						W.setTravelAttemptTime(0);
						fileAllWarrants(laws,W,W.criminal());
						unCuff(W.criminal());
						if(W.arrestingOfficer()!=null)
							dismissOfficer(W.arrestingOfficer());
					}
				}
			}
			break;
		default:
			Log.warnOut("Unknown Arrest state: "+W.state());
			break;
		}
	}
}