/
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.Abilities.Common;
import com.planet_ink.coffee_mud.Abilities.StdAbility;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/*
   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.
*/


public class CommonSkill extends StdAbility
{
	@Override
	public String ID()
	{
		return "CommonSkill";
	}

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

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

	private static final String[] triggerStrings = empty;

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

	public String supportedResourceString()
	{
		return "";
	}

	public static final Map<String, Integer[]>	resourcesMap	= new Hashtable<String, Integer[]>();
	protected static Item						fakeFire		= null;
	protected static final List<String>			uninvokeEmpties	= new ReadOnlyList<String>(new ArrayList<String>(0));

	protected volatile Room	activityRoom	= null;
	protected boolean		aborted			= false;
	protected boolean		helping			= false;
	protected boolean		bundling		= false;
	public Ability			helpingAbility	= null;
	protected volatile int	tickUp			= 0;
	protected String		verb			= L("working");
	protected String		playSound		= null;
	protected int			yield			= baseYield();
	protected volatile int	lastBaseDuration= 0;

	protected int baseYield()
	{
		return 1;
	}

	@Override
	public int abstractQuality()
	{
		return Ability.QUALITY_INDIFFERENT;
	}

	protected String displayText = L("(Doing something productive)");

	@Override
	public String displayText()
	{
		return displayText;
	}

	@Override
	protected ExpertiseLibrary.SkillCostDefinition getRawTrainingCost()
	{
		return CMProps.getCommonSkillGainCost(ID());
	}

	@Override
	protected int iniPracticesToPractice()
	{
		return 1;
	}

	protected boolean allowedWhileMounted()
	{
		return true;
	}

	@Override
	public int usageType()
	{
		return USAGE_MOVEMENT;
	}

	protected boolean allowedInTheDark()
	{
		return false;
	}

	@Override
	protected int canAffectCode()
	{
		return Ability.CAN_MOBS;
	}

	@Override
	protected int canTargetCode()
	{
		return Ability.CAN_ITEMS;
	}

	protected List<String> getUninvokeException()
	{
		return uninvokeEmpties;
	}

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

	protected boolean canBeDoneSittingDown()
	{
		return false;
	}

	protected int getActivityMessageType()
	{
		return canBeDoneSittingDown() ? CMMsg.MSG_HANDS | CMMsg.MASK_SOUND : CMMsg.MSG_NOISYMOVEMENT;
	}

	@Override
	public int abilityCode()
	{
		return yield;
	}

	@Override
	public void setAbilityCode(int newCode)
	{
		yield = newCode;
	}

	@Override
	public boolean okMessage(final Environmental myHost, final CMMsg msg)
	{
		if(!super.okMessage(myHost, msg))
			return false;
		if((myHost instanceof MOB)&&(myHost == this.affected)&&(((MOB)myHost).location()!=null))
		{
			if((msg.sourceMinor()==CMMsg.TYP_SHUTDOWN)
			||((msg.sourceMinor()==CMMsg.TYP_QUIT)&&(msg.amISource((MOB)myHost))))
			{
				aborted=true;
				unInvoke();
			}
		}
		return true;
	}

	@Override
	public boolean tick(Tickable ticking, int tickID)
	{
		if((affected instanceof MOB)&&(tickID==Tickable.TICKID_MOB))
		{
			final MOB mob=(MOB)affected;
			if((mob.isInCombat())
			||(mob.location()!=activityRoom)
			||(!CMLib.flags().isAliveAwakeMobileUnbound(mob,true)))
			{
				aborted=true;
				unInvoke();
				return false;
			}
			final String sound=(playSound!=null)?CMLib.protocol().msp(playSound,10):"";
			if(tickDown==4)
				mob.location().show(mob,null,getActivityMessageType(),L("<S-NAME> <S-IS-ARE> almost done @x1.@x2",verb,sound));
			else
			if((tickUp%4)==0)
			{
				final int total=tickUp+tickDown;
				final int pct=(int)Math.round(CMath.div(tickUp,total)*100.0);
				mob.location().show(mob,null,this,getActivityMessageType(),L("<S-NAME> continue(s) @x1 (@x2% completed).@x3",verb,""+pct,sound),null,L("<S-NAME> continue(s) @x1.@x2",verb,sound));
			}
			if((helping)
			&&(helpingAbility!=null)
			&&(helpingAbility.affecting() instanceof MOB)
			&&(((MOB)helpingAbility.affecting()).isMine(helpingAbility)))
				helpingAbility.tick(helpingAbility.affecting(),tickID);
			if((mob.soulMate()==null)
			&&(mob.playerStats()!=null)
			&&(mob.location()!=null)
			&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.HYGIENE)))
				mob.playerStats().adjHygiene(PlayerStats.HYGIENE_COMMONDIRTY);
		}

		final int preTickDown=tickDown;
		if(!super.tick(ticking,tickID))
			return false;
		tickUp+=(preTickDown-tickDown);
		return true;
	}

	@Override
	public void unInvoke()
	{
		if(canBeUninvoked() && (!super.unInvoked))
		{
			if((affected!=null)
			&&(affected instanceof MOB)
			&&(((MOB)affected).location()!=null))
			{
				final MOB mob=(MOB)affected;
				if(aborted)
					mob.location().show(mob,null,getActivityMessageType(),L("<S-NAME> stop(s) @x1.",verb));
				else
					mob.location().show(mob,null,getActivityMessageType(),L("<S-NAME> <S-IS-ARE> done @x1.",verb));
				helping=false;
				helpingAbility=null;
			}
		}
		super.unInvoke();
	}

	protected int getDuration(int baseTicks, MOB mob, int itemLevel, int minDuration)
	{
		int ticks=baseTicks;
		final int level=mob.phyStats().level() - itemLevel;
		final double pct=CMath.div(level,CMProps.getIntVar(CMProps.Int.LASTPLAYERLEVEL))*.5;
		ticks-=(int)Math.round(CMath.mul(ticks, pct));

		lastBaseDuration=ticks;
		if(lastBaseDuration<minDuration)
			lastBaseDuration=minDuration;
		
		final double quickPct = getXTIMELevel(mob) * 0.05;
		ticks-=(int)Math.round(CMath.mul(ticks, quickPct));
		if(ticks<minDuration)
			ticks=minDuration;
		return ticks;
	}

	@Override
	protected int addedTickTime(MOB invokerMOB, int baseTickTime)
	{
		// common skills tend to SUBTRACT time -- not add to it!
		return 0;
	}

	protected String getBrand(MOB mob)
	{
		if(mob==null)
			return "This is the work of an anonymous craftsman.";
		else
			return "This is the work of "+mob.Name()+".";
	}

	protected void commonTell(MOB mob, Environmental target, Environmental tool, String str)
	{
		if(mob.isMonster()&&(mob.amFollowing()!=null))
		{
			if(str.startsWith("You"))
				str=L("I@x1",str.substring(3));
			if(target!=null)
				str=CMStrings.replaceAll(str,"<T-NAME>",target.name());
			if(tool!=null)
				str=CMStrings.replaceAll(str,"<O-NAME>",tool.name());
			CMLib.commands().postSay(mob,null,str,false,false);
		}
		else
			mob.tell(mob,target,tool,str);
	}

	protected void commonTell(MOB mob, String str)
	{
		if(mob.isMonster()&&(mob.amFollowing()!=null))
		{
			if(str.startsWith("You"))
				str=L("I@x1",str.substring(3));
			CMLib.commands().postSay(mob,null,str,false,false);
		}
		else
			mob.tell(str);
	}

	protected void commonEmote(MOB mob, String str)
	{
		if(mob.isMonster()&&(mob.amFollowing()!=null))
			mob.location().show(mob,null,getActivityMessageType()|CMMsg.MASK_ALWAYS,str);
		else
			mob.tell(mob,null,null,str);
	}

	protected int lookingFor(int material, Room fromHere)
	{
		final Vector<Integer> V=new Vector<Integer>();
		V.addElement(Integer.valueOf(material));
		return lookingFor(V,fromHere);
	}

	protected int lookingFor(Vector<Integer> materials, Room fromHere)
	{
		final Vector<Integer> possibilities=new Vector<Integer>();
		for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)
		{
			final Room room=fromHere.getRoomInDir(d);
			final Exit exit=fromHere.getExitInDir(d);
			if((room!=null)&&(exit!=null)&&(exit.isOpen()))
			{
				final int material=room.myResource();
				if(materials.contains(Integer.valueOf(material&RawMaterial.MATERIAL_MASK)))
				{possibilities.addElement(Integer.valueOf(d));}
			}
		}
		if(possibilities.size()==0)
			return -1;
		return (possibilities.elementAt(CMLib.dice().roll(1,possibilities.size(),-1))).intValue();
	}

	public Item getRequiredFire(MOB mob,int autoGenerate)
	{
		if((autoGenerate>0)
		||((this instanceof CraftingSkill)&&(!((CraftingSkill)this).fireRequired)))
		{
			if(fakeFire != null)
				return fakeFire;
			fakeFire =CMClass.getBasicItem( "StdItem" );
			fakeFire.basePhyStats().setDisposition( fakeFire.basePhyStats().disposition() | PhyStats.IS_GLOWING | PhyStats.IS_LIGHTSOURCE );
			fakeFire.addNonUninvokableEffect( CMClass.getAbility( "Burning" ) );
			fakeFire.recoverPhyStats();
			return fakeFire;
		}
		Item fire=null;
		for(int i=0;i<mob.location().numItems();i++)
		{
			final Item I2=mob.location().getItem(i);
			if((I2!=null)&&(I2.container()==null)&&(CMLib.flags().isOnFire(I2)))
			{
				fire=I2;
				break;
			}
		}
		if((fire==null)||(!mob.location().isContent(fire)))
		{
			commonTell(mob,L("A fire will need to be built first."));
			return null;
		}
		return fire;
	}

	@Override
	public int[] usageCost(MOB mob, boolean ignoreClassOverride)
	{
		if(mob==null)
			return super.usageCost(null, ignoreClassOverride);
		if(usageType()==Ability.USAGE_NADA)
			return super.usageCost(mob, ignoreClassOverride);

		final int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());
		final int myCacheIndex=ignoreClassOverride?Ability.CACHEINDEX_CLASSLESS:Ability.CACHEINDEX_NORMAL;
		final int[] myCache=abilityUsageCache[myCacheIndex];
		final boolean rebuildCache=(myCache==null);
		int consumed;
		int minimum;
		if(!rebuildCache && (myCache!=null ))
		{
			if(myCache.length==3)
				return myCache;
			consumed=myCache[0];
			minimum=myCache[1];
		}
		else
		{
			consumed=25;
			final int lvl=CMLib.ableMapper().qualifyingClassLevel(mob,this)+super.getXLOWCOSTLevel(mob);
			final int lowest=CMLib.ableMapper().qualifyingLevel(mob,this);
			final int diff=lvl-lowest;
			Integer[] costOverrides=null;
			if(!ignoreClassOverride)
				costOverrides=CMLib.ableMapper().getCostOverrides(mob,ID());
			if(diff>0)
			switch(diff)
			{
			case 1:
				consumed = 20;
				break;
			case 2:
				consumed = 16;
				break;
			case 3:
				consumed = 13;
				break;
			case 4:
				consumed = 11;
				break;
			case 5:
				consumed = 8;
				break;
			default:
				consumed = 5;
				break;
			}
			final int maxOverride=CMProps.getMaxManaException(ID());
			if(maxOverride!=Short.MIN_VALUE)
			{
				if(maxOverride<0) 
					consumed=consumed+lowest;
				else
				if(consumed > maxOverride)
					consumed=maxOverride;
			}
			final int minOverride=CMProps.getMinManaException(ID());
			if(minOverride!=Short.MIN_VALUE)
			{
				if(minOverride<0)
					consumed=(lowest<5)?5:lowest; 
				else
				if(consumed<minOverride)
					consumed=minOverride;
			}
			if(overrideMana()>=0) 
				consumed=overrideMana();
			minimum=5;
			if((costOverrides!=null)&&(costOverrides[AbilityMapper.Cost.MANA.ordinal()]!=null))
			{
				consumed=costOverrides[AbilityMapper.Cost.MANA.ordinal()].intValue();
				if((consumed<minimum)&&(consumed>=0)) 
					minimum=consumed;
			}
		}
		final int[] usageCost=buildCostArray(mob,consumed,minimum);
		if(rebuildCache)
		{
			if(consumed > COST_PCT-1)
				abilityUsageCache[myCacheIndex]=new int[]{consumed,minimum};
			else
				abilityUsageCache[myCacheIndex]=usageCost;
		}
		return usageCost;
	}

	public int xlevel(MOB mob)
	{
		return mob.phyStats().level()+(2*getXLEVELLevel(mob));
	}

	public boolean confirmPossibleMaterialLocation(int resource, Room room)
	{
		if(room==null)
			return false;
		final Integer I=Integer.valueOf(resource);
		final boolean isMaterial=(resource&RawMaterial.RESOURCE_MASK)==0;
		final int roomResourceType=room.myResource();
		if(((isMaterial&&(resource==(roomResourceType&RawMaterial.MATERIAL_MASK))))
		||(I.intValue()==roomResourceType))
			return true;
		final List<Integer> resources=room.resourceChoices();
		if(resources!=null)
		for(int i=0;i<resources.size();i++)
		{
			if(isMaterial&&(resource==(resources.get(i).intValue()&RawMaterial.MATERIAL_MASK)))
				return true;
			else
			if(resources.get(i).equals(I))
				return true;
		}
		return false;
	}

	public Integer[] supportedResourcesMap()
	{
		final String rscs=supportedResourceString().toUpperCase();
		if(resourcesMap.containsKey(rscs))
		{
			return resourcesMap.get(rscs);
		}
		else
		{
			final List<String> set=CMParms.parseAny(supportedResourceString(),"|",true);
			final List<Integer> finalSet=new ArrayList<Integer>();
			for(int i=0;i<set.size();i++)
			{
				int x=-1;
				String setMat=set.get(i);
				if(setMat.startsWith("_"))
					x=RawMaterial.CODES.FIND_IgnoreCase(setMat.substring(1));
				else
				{
					final int y=setMat.indexOf('-');
					List<String> restV=null;
					if(y>0)
					{
						restV=CMParms.parseAny(setMat.substring(y+1),"-", true);
						setMat=setMat.substring(0, y);
					}
					final RawMaterial.Material m=RawMaterial.Material.findIgnoreCase(setMat);
					if(m!=null)
					{
						x=m.mask();
						if((restV!=null)&&(restV.size()>0))
						{
							final List<Integer> rscsV=new XVector<Integer>(RawMaterial.CODES.COMPOSE_RESOURCES(x));
							for(final String sv : restV)
							{
								final int code = RawMaterial.CODES.FIND_CaseSensitive(sv);
								if(code >=0)
									rscsV.remove(Integer.valueOf(code));
							}
							for(int codeDex=0;codeDex<rscsV.size()-1;codeDex++)
								finalSet.add(rscsV.get(codeDex));
							x=rscsV.get(rscsV.size()-1).intValue();
						}
					}
				}
				if(x<0)
					x=RawMaterial.CODES.FIND_IgnoreCase(setMat);
				if(x>=0)
					finalSet.add(Integer.valueOf(x));
			}
			final Integer[] finalArray=finalSet.toArray(new Integer[0]);
			resourcesMap.put(rscs, finalArray);
			return finalArray;
		}
	}

	public boolean isMadeOfSupportedResource(Item I)
	{
		if(I==null)
			return false;
		for(final Integer R : supportedResourcesMap())
		{
			if((R.intValue() & RawMaterial.MATERIAL_MASK)==0)
			{
				if((I.material()& RawMaterial.MATERIAL_MASK)==R.intValue())
					return true;
			}
			else
			if(I.material()==R.intValue())
				return true;
		}
		return false;
	}

	@Override
	public boolean canBeLearnedBy(MOB teacherM, MOB studentM)
	{
		if(!super.canBeLearnedBy(teacherM,studentM))
			return false;
		if(studentM==null)
			return true;
		final CharClass C=studentM.charStats().getCurrentClass();
		if(CMLib.ableMapper().getQualifyingLevel(C.ID(), false, ID())>=0)
			return true;
		final boolean crafting = ((classificationCode()&Ability.ALL_DOMAINS)==Ability.DOMAIN_CRAFTINGSKILL);
		final AbilityComponents.AbilityLimits remainders = CMLib.ableComponents().getCommonSkillRemainder(studentM, this);
		if(remainders.commonSkills()<=0)
		{
			teacherM.tell(L("@x1 can not learn any more common skills.",studentM.name(teacherM)));
			studentM.tell(L("You have learned the maximum @x1 common skills, and may not learn any more.",""+remainders.maxCommonSkills()));
			return false;
		}
		if(remainders.specificSkillLimit()<=0)
		{
			if(crafting)
				teacherM.tell(L("@x1 can not learn any more crafting common skills.",studentM.name(teacherM)));
			else
				teacherM.tell(L("@x1 can not learn any more non-crafting common skills.",studentM.name(teacherM)));
			final int max = crafting ? remainders.maxCraftingSkills() : remainders.maxNonCraftingSkills();
			if(crafting)
				studentM.tell(L("You have learned the maximum @x1 crafting skills, and may not learn any more.",""+max));
			else
				studentM.tell(L("You have learned the maximum @x1 non-crafting skills, and may not learn any more.",""+max));
			return false;
		}
		return true;
	}

	@Override
	public void teach(MOB teacher, MOB student)
	{
		super.teach(teacher, student);
		if((student!=null)&&(student.fetchAbility(ID())!=null))
		{
			final CharClass C=student.charStats().getCurrentClass();
			if(CMLib.ableMapper().getQualifyingLevel(C.ID(), false, ID())>=0)
				return;
			final boolean crafting = ((classificationCode()&Ability.ALL_DOMAINS)==Ability.DOMAIN_CRAFTINGSKILL);
			final AbilityComponents.AbilityLimits remainders = CMLib.ableComponents().getCommonSkillRemainder(student, this);
			if(remainders.commonSkills()<=0)
				student.tell(L("@x1 may not learn any more common skills.",student.name()));
			else
			if(remainders.commonSkills()<=Integer.MAX_VALUE/2)
				student.tell(L("@x1 may learn @x2 more common skills.",student.name(),""+remainders.commonSkills()));
			if(remainders.specificSkillLimit()<=0)
				student.tell(L("@x1 may not learn any more @x2crafting common skills.",student.name(),(crafting?"":"non-")));
			else
			if(remainders.specificSkillLimit()<=Integer.MAX_VALUE/2)
				student.tell(L("@x1 may learn @x2 more @x3crafting common skills.",student.name(),""+remainders.specificSkillLimit(),(crafting?"":"non-")));
		}
	}

	public void bumpTickDown(long byThisMuch)
	{
		tickDown+=byThisMuch;
		if(byThisMuch > 0)
			this.lastBaseDuration+=byThisMuch;
	}

	@Override
	public void startTickDown(MOB invokerMOB, Physical affected, int tickTime)
	{
		super.startTickDown(invokerMOB, affected, tickTime);
		tickUp=0;
	}

	public boolean checkStop(MOB mob, List<String> commands)
	{
		if((commands!=null)
		&&(commands.size()==1)
		&&(commands.get(0).equalsIgnoreCase("stop")))
		{
			final Ability A=mob.fetchEffect(ID());
			if((A!=null)&&(!A.isNowAnAutoEffect())&&(A.canBeUninvoked()))
			{
				if(A instanceof CommonSkill)
					((CommonSkill)A).aborted=true;
				A.unInvoke();
				return true;
			}
			mob.tell(L("You are not doing that right now."));
		}
		return false;
	}

	@Override
	public void setMiscText(String newMiscText)
	{
		if("abort".equalsIgnoreCase(newMiscText))
		{
			this.aborted=true;
			this.tickDown=1;
			this.tick(affected, Tickable.TICKID_MOB);
		}
		else
			super.setMiscText(newMiscText);
	}
	
	@Override
	public boolean invoke(MOB mob, List<String> commands, Physical givenTarget, boolean auto, int asLevel)
	{
		aborted=false;
		if(mob.isInCombat())
		{
			commonEmote(mob,L("<S-NAME> <S-IS-ARE> in combat!"));
			return false;
		}
		if((!allowedWhileMounted())&&(mob.riding()!=null))
		{
			commonEmote(mob,L("You can't do that while @x1 @x2.",mob.riding().stateString(mob),mob.riding().name()));
			return false;
		}

		if((!allowedInTheDark())&&(!CMLib.flags().canBeSeenBy(mob.location(),mob)))
		{
			commonTell(mob,L("<S-NAME> can't see to do that!"));
			return false;
		}
		if((CMLib.flags().isSitting(mob)&&(!canBeDoneSittingDown()))||CMLib.flags().isSleeping(mob))
		{
			commonTell(mob,L("You need to stand up!"));
			return false;
		}
		for(final Enumeration<Ability> a=mob.personalEffects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)
			&&(((A.classificationCode()&Ability.ALL_ACODES)==Ability.ACODE_COMMON_SKILL)||(A.ID().equalsIgnoreCase("AstroEngineering")))
			&&(!getUninvokeException().contains(A.ID())))
			{
				if(A instanceof CommonSkill)
					((CommonSkill)A).aborted=true;
				A.unInvoke();
			}
		}
		isAnAutoEffect=false;

		// if you can't move, you can't do anything!
		if(!CMLib.flags().isAliveAwakeMobileUnbound(mob,false))
			return false;
		final int[] consumed=usageCost(mob,false);
		if(mob.curState().getMana()<consumed[Ability.USAGEINDEX_MANA])
		{
			if(mob.maxState().getMana()==consumed[Ability.USAGEINDEX_MANA])
				mob.tell(L("You must be at full mana to do that."));
			else
				mob.tell(L("You don't have enough mana to do that."));
			return false;
		}
		if(mob.curState().getMovement()<consumed[Ability.USAGEINDEX_MOVEMENT])
		{
			if(mob.maxState().getMovement()==consumed[Ability.USAGEINDEX_MOVEMENT])
				mob.tell(L("You must be at full movement to do that."));
			else
				mob.tell(L("You don't have enough movement to do that.  You are too tired."));
			return false;
		}
		if(mob.curState().getHitPoints()<consumed[Ability.USAGEINDEX_HITPOINTS])
		{
			if(mob.maxState().getHitPoints()==consumed[Ability.USAGEINDEX_HITPOINTS])
				mob.tell(L("You must be at full health to do that."));
			else
				mob.tell(L("You don't have enough hit points to do that."));
			return false;
		}
		if(!checkComponents(mob))
			return false;
		mob.curState().adjMana(-consumed[0],mob.maxState());
		mob.curState().adjMovement(-consumed[1],mob.maxState());
		mob.curState().adjHitPoints(-consumed[2],mob.maxState());
		setAbilityCode(baseYield());
		activityRoom=mob.location();
		if(!bundling)
			helpProficiency(mob, 0);

		return true;
	}
}