/
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/Specializations/
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/
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/BasicTech/
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.interfaces.ItemPossessor.Expire;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.TrackingLibrary;
import com.planet_ink.coffee_mud.Libraries.interfaces.TrackingLibrary.TrackingFlag;
import com.planet_ink.coffee_mud.Libraries.interfaces.TrackingLibrary.TrackingFlags;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/*
   Copyright 2006-2019 Bo Zimmerman

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

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

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

	@Override
	protected int canImproveCode()
	{
		return Behavior.CAN_ITEMS | Behavior.CAN_MOBS | Behavior.CAN_ROOMS | Behavior.CAN_EXITS | Behavior.CAN_AREAS;
	}

	protected PairVector<Object, Double>		rates		= new PairVector<Object, Double>();
	protected List<Room>						ratesVec	= null;
	protected PairVector<MOB, String>			thingsToSay	= new PairVector<MOB, String>();

	protected QuadVector<MOB, Room, Double, TrackingFlags>	destinations= new QuadVector<MOB, Room, Double, TrackingFlags>();

	protected static final String defaultGreeting = "Need directions? Just name the place and I'll name the price! Append words like noswim, noclimb, nofly, nolocks, noprivate, and nocrawl to narrow the focus.";

	protected final static TrackingLibrary.TrackingFlags defaultTrackingFlags	= CMLib.tracking().newFlags()
																				.plus(TrackingLibrary.TrackingFlag.NOEMPTYGRIDS)
																				.plus(TrackingLibrary.TrackingFlag.NOHOMES)
																				.plus(TrackingLibrary.TrackingFlag.NOHIDDENAREAS);
	protected final static TrackingLibrary.TrackingFlags defaultRoomRadiusFlags	= CMLib.tracking().newFlags();

	protected double	basePrice		= 0.0;
	protected double	perRoomPrice	= 0.0;
	protected String	talkerName		= "";
	protected MOB		fakeTalker		= null;
	protected Room		startRoom		= null;
	protected String	greeting		= defaultGreeting;
	protected String	mountStr		= "";
	protected boolean	portal			= false;
	protected int		maxRange		= 100;

	protected TrackingLibrary.TrackingFlags trackingFlags	= CMLib.tracking().newFlags().plus(defaultTrackingFlags);
	protected TrackingLibrary.TrackingFlags roomRadiusFlags = CMLib.tracking().newFlags().plus(defaultRoomRadiusFlags);

	@Override
	public String accountForYourself()
	{
		return "direction giving and selling";
	}

	protected boolean disableComingsAndGoings()
	{
		return false;
	}

	protected String getGiveMoneyMessage(final MOB mob, final Environmental observer, final Environmental destination, final String moneyName)
	{
		if(observer instanceof MOB)
			return L("I can help you find @x1, but you'll need to give me @x2 first.",getDestinationName(mob,destination),moneyName);
		else
		if(observer instanceof Container)
			return L("I can help you find @x1, but you'll need to put @x2 into @x3 first.",getDestinationName(mob,destination),moneyName,observer.name());
		else
			return L("I can help you find @x1, but you'll need to drop @x2 first.",getDestinationName(mob,destination),moneyName);
	}

	protected MOB getTalker(final Environmental o, final Room room)
	{
		if(o==null)
			return null;
		if(startRoom==null)
			startRoom=room;
		if((talkerName==null)||(talkerName.length()==0))
		{
			if(o instanceof MOB)
				return (MOB)o;
			if(fakeTalker==null)
			{
				fakeTalker=CMClass.getFactoryMOB();
				fakeTalker.setName(o.name());
			}
		}
		else
		if(o instanceof Rideable)
		{
			final Environmental E=CMLib.english().fetchEnvironmental(((Rideable)o).riders(),talkerName,true);
			if(E instanceof MOB)
				return (MOB)E;
		}
		if(fakeTalker==null)
		{
			fakeTalker=CMClass.getFactoryMOB();
			fakeTalker.setName(talkerName);
		}
		fakeTalker.setStartRoom(startRoom);
		if(room != null)
			fakeTalker.setLocation(room);
		else
			fakeTalker.setLocation(CMLib.map().roomLocation(o));
		return fakeTalker;
	}

	protected Environmental getReceiver(final Environmental o, final Room room)
	{
		return o;
	}

	protected void resetDefaults()
	{
		basePrice=0.0;
		talkerName="";
		fakeTalker=null;
		startRoom=null;
		greeting=defaultGreeting;
		mountStr="";
		maxRange = 100;
		perRoomPrice = 0.0;
		rates.clear();
		ratesVec=null;
		destinations.clear();
		thingsToSay.clear();
		portal=false;
	}

	protected void resetFlags()
	{
		trackingFlags	= CMLib.tracking().newFlags().plus(defaultTrackingFlags);
		roomRadiusFlags = CMLib.tracking().newFlags().plus(defaultRoomRadiusFlags);
	}

	@Override
	public void setParms(final String newParm)
	{
		super.setParms(newParm);
		resetDefaults();
		if((CMath.isInteger(newParm))
		||(CMath.isDouble(newParm)))
		{
			basePrice=CMath.s_double(newParm);
			return;
		}
		final List<String> V=CMParms.parseSemicolons(newParm,true);
		String s=null;
		int x=0;
		double price=0;
		Room R=null;
		Area A=null;
		resetFlags();
		for(int v=0;v<V.size();v++)
		{
			s=V.get(v);
			x=s.indexOf('=');
			if(x>=0)
			{
				String numStr=s.substring(x+1).trim();
				if(numStr.startsWith("\"")&&(numStr.endsWith("\"")))
					numStr=numStr.substring(1,numStr.length()-1).trim();
				s=s.substring(0,x).trim().toUpperCase();
				final boolean isTrue=numStr.toLowerCase().startsWith("t");
				if(s.equals("PORTAL"))
				{
					portal=isTrue;
					continue;
				}
				else
				if(s.equals("AREAONLY"))
				{
					if(isTrue)
						roomRadiusFlags.add(TrackingFlag.AREAONLY);
					continue;
				}
				else
				if(s.equals("NOCLIMB"))
				{
					if(isTrue)
						trackingFlags.add(TrackingFlag.NOCLIMB);
					continue;
				}
				else
				if(s.equals("NOWATER"))
				{
					if(isTrue)
						trackingFlags.add(TrackingFlag.NOWATER);
					continue;
				}
				else
				if(s.equals("INDOOROK"))
				{
					if(isTrue)
					{
						trackingFlags.remove(TrackingFlag.OUTDOORONLY);
						roomRadiusFlags.remove(TrackingFlag.OUTDOORONLY);
					}
					continue;
				}
				else
				if(s.equals("NOAIR"))
				{
					if(isTrue)
						trackingFlags.add(TrackingFlag.NOAIR);
					continue;
				}
				else
				if(s.equals("NOLOCKS"))
				{
					if(isTrue)
						trackingFlags.add(TrackingFlag.UNLOCKEDONLY);
					continue;
				}
				else
				if(s.equals("NOHOMES"))
				{
					if(isTrue)
						trackingFlags.add(TrackingFlag.NOHOMES);
					continue;
				}
				else
				if(s.equals("NOINDOOR"))
				{
					if(isTrue)
					{
						trackingFlags.add(TrackingFlag.OUTDOORONLY);
						roomRadiusFlags.add(TrackingFlag.OUTDOORONLY);
					}
					continue;
				}
				else
				if(s.equals("MAXRANGE"))
				{
					maxRange=CMath.s_int(numStr);
					continue;
				}
				else
				if(s.equals("GREETING"))
				{
					greeting=numStr;
					continue;
				}
				else
				if(s.equals("ENTERMSG"))
				{
					mountStr=numStr;
					continue;
				}
				else
				if(s.equals("TALKERNAME"))
				{
					talkerName=numStr;
					continue;
				}
				else
				if(s.equals("PERROOM"))
				{
					perRoomPrice=CMath.s_double(numStr);
					continue;
				}
				else
					price=CMath.s_double(numStr);
			}
			A=null;
			R=CMLib.map().getRoom(s);
			if(R==null)
				A=CMLib.map().findArea(s);
			if(A!=null)
				rates.add(A,Double.valueOf(price));
			else
			if((R!=null)&&(!rates.containsFirst(R)))
				rates.add(R,Double.valueOf(price));
			else
				rates.add(s,Double.valueOf(price));
		}
		basePrice=price;
	}

	protected double getPrice(final Room centerRoom, final Room  destR)
	{
		if(destR==null)
			return basePrice;
		final int rateIndex=rates.indexOfFirst(destR);
		if(rateIndex<0)
		{
			double price=basePrice;
			if(this.perRoomPrice > 0.0)
			{
				final List<Room> trail=CMLib.tracking().findTrailToRoom(centerRoom, destR, trackingFlags, maxRange);
				if(trail != null)
					price = price + (perRoomPrice * trail.size());
			}
			return price;
		}
		return rates.get(rateIndex).second.doubleValue();
	}

	protected final List<Room> getRoomsInRange(final Room centerRoom, final List<Room> roomsInRange, final TrackingFlags roomRadiusFlags)
	{
		if(roomsInRange != null)
			return roomsInRange;
		return CMLib.tracking().getRadiantRooms(centerRoom,roomRadiusFlags,maxRange);
	}

	protected Room findNearestAreaRoom(final Area A, final List<Room> roomsInRange)
	{
		for(final Room R : roomsInRange)
		{
			if(A.inMyMetroArea(R.getArea()))
				return R;
		}
		return A.getRandomMetroRoom();
	}

	protected Room findDestination(final Environmental observer, final MOB mob, final Room centerRoom, final String where, final TrackingFlags roomRadiusFlags)
	{
		PairVector<String,Double> stringsToDo=null;
		Room roomR=null;
		List<Room> roomsInRange=null;
		if(rates.size()==0)
		{
			final Area A=CMLib.map().findArea(where);
			if(A!=null)
			{
				roomsInRange=getRoomsInRange(centerRoom,roomsInRange,roomRadiusFlags);
				roomR=findNearestAreaRoom(A,roomsInRange);
			}
		}
		else
		if(rates.size()>0)
		{
			for(int r=rates.size()-1;r>=0;r--)
			{
				if(rates.get(r).first instanceof String)
				{
					final String place=(String)rates.get(r).first;
					if((observer!=null)&&(centerRoom!=null))
					{
						if(stringsToDo==null)
							stringsToDo=new PairVector<String,Double>();
						stringsToDo.addElement(place,rates.get(r).second);
					}
					rates.removeElementAt(r);
				}
			}
			if((stringsToDo!=null)&&(observer!=null))
			{
				Room R=null;
				String place=null;
				for(int r=0;r<stringsToDo.size();r++)
				{
					roomsInRange=getRoomsInRange(centerRoom,roomsInRange,roomRadiusFlags);
					place=stringsToDo.get(r).first;
					R=(Room)CMLib.english().fetchEnvironmental(roomsInRange,place,false);
					if(R!=null)
						rates.add(R,stringsToDo.get(r).second);
				}
				stringsToDo.clear();
				stringsToDo=null;
			}
			if(ratesVec==null)
			{
				ratesVec=new ArrayList<Room>();
				for(final Pair<Object,Double> p : rates)
				{
					if(p.first instanceof Area)
						ratesVec.add(((Area)p.first).getRandomMetroRoom());
					else
					if(p.first instanceof Room)
						ratesVec.add((Room)p.first);
				}
			}
			roomR=(Room)CMLib.english().fetchEnvironmental(ratesVec,where,true);
			if(roomR==null)
				roomR=(Room)CMLib.english().fetchEnvironmental(ratesVec,where,false);
			if(roomR==null)
			{
				final Area A=CMLib.map().findArea(where);
				if(A!=null)
				{
					roomsInRange=getRoomsInRange(centerRoom,roomsInRange,roomRadiusFlags);
					roomR=findNearestAreaRoom(A,roomsInRange);
				}
			}
		}
		if(roomR==null)
		{
			roomsInRange=getRoomsInRange(centerRoom,roomsInRange,roomRadiusFlags);
			roomR=CMLib.map().findFirstRoom(new IteratorEnumeration<Room>(roomsInRange.iterator()), mob, where, false, 5);
		}
		return roomR;
	}

	protected boolean mayGiveThisMoney(final MOB source, final MOB conceirgeM, final Room room, final Environmental possibleCoins)
	{
		if(possibleCoins!=null)
		{
			if(!(possibleCoins instanceof Coins))
			{
				if(room.isInhabitant(conceirgeM))
				{
					CMLib.commands().postSay(conceirgeM,source,L("I'm sorry, I can only accept money."),true,false);
					return false;
				}
				return true;
			}

			final int destIndex=destinations.indexOfFirst(source);
			if(destIndex<0)
			{
				CMLib.commands().postSay(conceirgeM,source,L("What's this for?  Please tell me where you'd like to go first."),true,false);
				return false;
			}
			else
			if(!((Coins)possibleCoins).getCurrency().equalsIgnoreCase(CMLib.beanCounter().getCurrency(conceirgeM)))
			{
				CMLib.commands().postSay(conceirgeM,source,L("I'm sorry, I don't accept that kind of currency."),true,false);
				return false;
			}
			final Double owed=destinations.get(destinations.indexOfFirst(source)).third;
			if(owed.doubleValue()<=0.0)
			{
				CMLib.commands().postSay(conceirgeM,source,L("Hey, you've already paid me!"),true,false);
				return false;
			}
		}
		return true;
	}

	@Override
	public boolean okMessage(final Environmental host, final CMMsg msg)
	{
		if(!super.okMessage(host,msg))
			return false;
		final MOB source=msg.source();
		if(startRoom==null)
			startRoom=source.location();
		if((!canFreelyBehaveNormal(host))&&(host instanceof MOB))
			return true;
		final Environmental observer=host;
		final Room room=source.location();
		if(source != observer)
		{
			if((msg.amITarget(getReceiver(observer,room)))
			&&(msg.targetMinor()==CMMsg.TYP_GIVE)
			&&(!(observer instanceof Container))
			&&(!(observer instanceof MOB)))
				return this.mayGiveThisMoney(source, getTalker(observer,room), room, msg.tool());
			else
			if((msg.amITarget(observer))
			&&(msg.targetMinor()==CMMsg.TYP_PUT)
			&&(observer instanceof Container))
				return this.mayGiveThisMoney(source, getTalker(observer,room), room, msg.tool());
			else
			if((msg.targetMinor()==CMMsg.TYP_DROP)
			&&(!(observer instanceof MOB)))
				return this.mayGiveThisMoney(source, getTalker(observer,room), room, msg.target());
		}
		return true;
	}

	@Override
	public boolean tick(final Tickable ticking, final int tickID)
	{
		if((ticking instanceof Environmental)
		&&(tickID==Tickable.TICKID_MOB)
		&&(thingsToSay.size()>0)
		&&(canFreelyBehaveNormal(ticking))||(!(ticking instanceof MOB)))
		{
			final Room R=CMLib.map().roomLocation((Environmental)ticking);
			synchronized(thingsToSay)
			{
				while(thingsToSay.size()>0)
				{
					final MOB source=thingsToSay.get(0).first;
					final MOB observer=getTalker((Environmental)ticking,source.location());
					final String msg=thingsToSay.get(0).second;
					thingsToSay.removeElementAt(0);
					if((R!=null)&&(R.isHere(source)))
						CMLib.commands().postSay(observer,source,msg,true,false);
				}
			}
		}
		return super.tick(ticking,tickID);
	}

	protected void executeMoneyDrop(final MOB source, final MOB conciergeM, final Environmental possibleCoins, final CMMsg addToMsg)
	{
		if(possibleCoins instanceof Coins)
		{
			final int destIndex=destinations.indexOfFirst(source);
			if(destIndex>=0)
			{
				final Quad<MOB,Room,Double,TrackingFlags> destT=destinations.get(destIndex);
				final Room destR=destT.second;
				final Double owed=Double.valueOf(destT.third.doubleValue() - ((Coins)possibleCoins).getTotalValue());
				if(owed.doubleValue()>0.0)
				{
					destT.third=owed;
					CMLib.commands().postSay(conciergeM,source,L("Ok, you still owe @x1.",CMLib.beanCounter().nameCurrencyLong(conciergeM,owed.doubleValue())),true,false);
					return;
				}
				else
				if(owed.doubleValue()<0.0)
				{
					final double change=-owed.doubleValue();
					final Coins C=CMLib.beanCounter().makeBestCurrency(conciergeM,change);
					if((change>0.0)&&(C!=null))
					{
						// this message will actually end up triggering the hand-over.
						final CMMsg newMsg=CMClass.getMsg(conciergeM,source,C,CMMsg.MSG_SPEAK,L("^T<S-NAME> say(s) 'Heres your change.' to <T-NAMESELF>.^?"));
						C.setOwner(conciergeM);
						final long num=C.getNumberOfCoins();
						final String curr=C.getCurrency();
						final double denom=C.getDenomination();
						C.destroy();
						C.setNumberOfCoins(num);
						C.setCurrency(curr);
						C.setDenomination(denom);
						destT.third=Double.valueOf(0.0);
						addToMsg.addTrailerMsg(newMsg);
					}
					else
						CMLib.commands().postSay(conciergeM,source,L("Gee, thanks. :)"),true,false);
				}
				((Coins)possibleCoins).destroy();
				giveMerchandise(source, destR, conciergeM, source.location(), destT.fourth);
				destinations.removeElementFirst(source);
			}
			else
			if(!CMLib.flags().canBeSeenBy(source,conciergeM))
				CMLib.commands().postSay(conciergeM,null,L("Wha?  Where did this come from?  Cool!"),false,false);
		}
	}

	protected String getDestinationName(final MOB mob, final Environmental destination)
	{
		return (destination instanceof Room)?((Room)destination).displayText(mob):destination.name();
	}

	protected void giveMerchandise(final MOB whoM, Room destination, final Environmental observer, final Room room, final TrackingFlags trackingFlags)
	{

		if(this.portal)
		{
			final Room R=whoM.location();
			final Ability spell=CMClass.getAbility("Spell_Portal");
			final CMMsg msg=CMClass.getMsg(whoM,R,spell,CMMsg.MSG_CAST_VERBAL_SPELL,L("A blinding, swirling portal appears here."));
			final CMMsg msg2=CMClass.getMsg(whoM,destination,spell,CMMsg.MSG_CAST_VERBAL_SPELL,L("A blinding, swirling portal appears here."));
			if((R.okMessage(whoM,msg))&&(destination.okMessage(whoM,msg2)))
			{
				R.send(whoM,msg);
				destination=(Room)msg2.target();
				destination.sendOthers(whoM,msg2);
				final Item I=CMClass.getItem("GenPortal");
				I.setName(L("A portal to @x1",getDestinationName(whoM,destination)));
				I.setDisplayText(L("A portal to @x1 swirls here",getDestinationName(whoM,destination)));
				I.setReadableText(CMLib.map().getExtendedRoomID(destination));
				R.addItem(I, Expire.Monster_EQ);
				final Behavior B=CMClass.getBehavior("Decay");
				B.setParms("minticks=8 maxticks=12");
				I.addBehavior(B);
				thingsToSay.addElement(whoM,L("Enter this portal to @x1.",getDestinationName(whoM,destination)));
			}
		}
		else
		{
			final MOB fromM=getTalker(observer,room);
			String name=CMLib.map().getExtendedRoomID(destination);
			if(name.length()==0)
				name=destination.displayText();
			final List<Room> set=new ArrayList<Room>();
			CMLib.tracking().getRadiantRooms(fromM.location(),set,trackingFlags,null,maxRange,null);
			String trailStr;
			if(CMLib.tracking().canValidTrail(fromM.location(), set, name, maxRange, null, 1))
				trailStr=CMLib.tracking().getTrailToDescription(fromM.location(),set,name,false,false,maxRange,null,1);
			else
			{
				//set.clear();
				final TrackingFlags noAirFlags = trackingFlags.copyOf();
				noAirFlags.add(TrackingFlag.NOAIR);
				CMLib.tracking().getRadiantRooms(fromM.location(),set,noAirFlags,null,maxRange,null);
				trailStr=CMLib.tracking().getTrailToDescription(fromM.location(),set,name,false,false,maxRange,null,1);
			}
			thingsToSay.addElement(whoM,L("The way to @x1 from here is: @x2",getDestinationName(whoM,destination),trailStr));
		}
	}

	@Override
	public void executeMsg(final Environmental affecting, final CMMsg msg)
	{
		super.executeMsg(affecting,msg);

		if((!canFreelyBehaveNormal(affecting))&&(affecting instanceof MOB))
			return;

		final MOB source=msg.source();
		final Environmental observer=affecting;
		final Room room=source.location();
		if(source!=observer)
		{
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_GIVE:
				if(msg.amITarget(getReceiver(observer,room)))
					executeMoneyDrop(source,getTalker(observer,room),msg.tool(),msg);
				break;
			case CMMsg.TYP_DROP:
				if((!(observer instanceof Container))
				&&((!(observer instanceof Rideable))||(msg.source().riding()==observer))
				&&(!(observer instanceof MOB)))
					executeMoneyDrop(source,getTalker(observer,room),msg.target(),msg);
				break;
			case CMMsg.TYP_PUT:
				if((msg.amITarget(observer))
				&&((!(observer instanceof Rideable))||(msg.source().riding()==observer))
				&&(observer instanceof Container))
					executeMoneyDrop(source,getTalker(observer,room),msg.tool(),msg);
				break;
			case CMMsg.TYP_SPEAK:
				if((msg.source()==getTalker(observer,room))
				&&(msg.target() instanceof MOB)
				&&(msg.tool() instanceof Coins)
				&&(((Coins)msg.tool()).amDestroyed())
				&&(!msg.source().isMine(msg.tool()))
				&&(!((MOB)msg.target()).isMine(msg.tool())))
					CMLib.beanCounter().giveSomeoneMoney(msg.source(),(MOB)msg.target(),((Coins)msg.tool()).getTotalValue());
				else
				if((!msg.source().isMonster())
				&&((msg.target()==observer)||(source.location().numPCInhabitants()==1)||(!(observer instanceof MOB)))
				&&((!(observer instanceof Rideable))||(msg.source().riding()==observer))
				&&(msg.sourceMessage()!=null))
				{
					String say=CMStrings.getSayFromMessage(msg.sourceMessage());
					if((say!=null)&&(say.length()>0))
					{
						final List<String> parsedSay = CMParms.parse(say);
						boolean didLoop=true;
						final TrackingFlags myRoomRadiusFlags=CMLib.tracking().newFlags();
						myRoomRadiusFlags.addAll(roomRadiusFlags);
						boolean didAnything=false;
						while(didLoop && (parsedSay.size()>0))
						{
							didLoop=false;
							final String s=parsedSay.get(parsedSay.size()-1);
							if(s.equalsIgnoreCase("noclimb"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.NOCLIMB);
							else
							if(s.equalsIgnoreCase("nodoors"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.OPENONLY);
							else
							if(s.equalsIgnoreCase("noswim"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.NOWATER);
							else
							if(s.equalsIgnoreCase("noprivate"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.NOPRIVATEPROPERTY);
							else
							if(s.equalsIgnoreCase("nofly"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.NOAIR);
							else
							if(s.equalsIgnoreCase("nolocks"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.UNLOCKEDONLY);
							else
							if(s.equalsIgnoreCase("nocrawl"))
								didLoop=myRoomRadiusFlags.add(TrackingFlag.NOCRAWL);
							if(didLoop)
								parsedSay.remove(parsedSay.size()-1);
							didAnything = didAnything || didLoop;
						}
						Room roomR;
						if(didAnything)
						{
							say=CMParms.combine(parsedSay);
							roomR=findDestination(observer,msg.source(),room,say,myRoomRadiusFlags);
							if((roomR==null)||(roomR.roomID().length()==0))
							{
								final Room roomR2=findDestination(observer,msg.source(),room,say,trackingFlags);
								if(roomR2!=null)
									roomR=roomR2;
							}
						}
						else
						{
							roomR=findDestination(observer,msg.source(),room,say,trackingFlags);
							if((roomR==null)||(roomR.roomID().length()==0))
							{
								final Room roomR2=findDestination(observer,msg.source(),room,say,myRoomRadiusFlags);
								if(roomR2!=null)
									roomR=roomR2;
							}
						}
						if(roomR==null)
						{
							synchronized(thingsToSay)
							{
								thingsToSay.addElement(msg.source(),L("I'm sorry, I don't know where '@x1' is.",say));
								return;
							}
						}
						final double rate=getPrice(room,roomR);
						final Double owed=Double.valueOf(rate);
						trackingFlags.addAll(myRoomRadiusFlags);
						destinations.removeElementFirst(msg.source());
						if(owed.doubleValue()<=0.0)
							giveMerchandise(msg.source(), roomR, observer, room, trackingFlags);
						else
						{
							destinations.addElement(msg.source(),roomR,owed, trackingFlags);
							final String moneyName=CMLib.beanCounter().nameCurrencyLong(getTalker(observer,room),rate);
							thingsToSay.addElement(msg.source(),getGiveMoneyMessage(msg.source(),observer,roomR,moneyName));
						}
					}
				}
				break;
			case CMMsg.TYP_LEAVE:
				if((msg.target()==room)
				&&(!disableComingsAndGoings())
				&&(destinations.containsFirst(msg.source())))
					destinations.removeElementFirst(msg.source());
				break;
			case CMMsg.TYP_ENTER:
				if((!msg.source().isMonster())
				&&((greeting!=null)&&(greeting.length()>0))
				&&(!disableComingsAndGoings())
				&&(!destinations.containsFirst(msg.source()))
				&&(msg.target()==CMLib.map().roomLocation(observer))
				&&(CMLib.flags().canBeSeenBy(msg.source(), getTalker(observer,room))))
					thingsToSay.addElement(msg.source(),greeting);
				//$FALL-THROUGH$
			case CMMsg.TYP_MOUNT:
				if((!msg.source().isMonster())
				&&(msg.target()==observer)
				&&((mountStr!=null)&&(mountStr.length()>0))
				&&(!destinations.containsFirst(msg.source()))
				&&(CMLib.flags().canBeSeenBy(msg.source(), getTalker(observer,room))))
					thingsToSay.addElement(msg.source(),mountStr);
			}
		}
	}
}