<NME> StdAbility.java
<BEF> package com.planet_ink.coffee_mud.Abilities;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMSecurity.DisFlag;
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.Basic.StdItem;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.AbilityMapper.AbilityMapping;
import com.planet_ink.coffee_mud.core.interfaces.CostDef;
import com.planet_ink.coffee_mud.core.interfaces.CostDef.CostType;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.MOB.Attrib;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/*
   Copyright 2001-2022 Bo Zimmerman

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

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

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

	protected boolean			isAnAutoEffect	= false;
	protected int				proficiency		= 0;
	protected boolean			savable			= true;
	protected String			miscText		= "";
	protected MOB				invoker			= null;
	protected Physical			affected		= null;
	protected boolean 			canBeUninvoked	= true;
	protected volatile boolean	unInvoked		= false;
	protected volatile int 		tickDown		= -1;
	protected long 				lastCastHelp	= 0;
	protected boolean 			amDestroyed		= false;

	private static final int[] STATIC_USAGE_NADA= new int[Ability.USAGEINDEX_TOTAL];

	public StdAbility()
	{
		super();
		//CMClass.bumpCounter(this,CMClass.CMObjectType.ABILITY);//removed for mem & perf
	}

	/*
	protected void finalize()
	{
		CMClass.unbumpCounter(this, CMClass.CMObjectType.ABILITY);
	}// removed for mem & perf
	*/

	@Override
	public CMObject newInstance()
	{
		try
		{
			return this.getClass().newInstance();
		}
		catch(final Exception e)
		{
			Log.errOut(ID(),e);
		}
		return new StdAbility();
	}

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

	@Override
	public String name()
	{
		return "an ability";
	}

	@Override
	public String description()
	{
		return "&";
	}

	@Override
	public String displayText()
	{
		return "Affected list display for " + ID();
	}

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

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

	@Override
	public void setImage(final String newImage)
	{
	}

	public static final String[]	empty	= {};

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

	@Override
	public int maxRange()
	{
		return adjustedMaxInvokerRange(0);
	}

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

	@Override
	public double castingTime(final MOB mob, final List<String> cmds)
	{
		return CMProps.getSkillActionCost(ID());
	}

	@Override
	public double combatCastingTime(final MOB mob, final List<String> cmds)
	{
		return CMProps.getSkillCombatActionCost(ID());
	}

	@Override
	public double checkedCastingCost(final MOB mob, final List<String> commands)
	{
		if(mob!=null)
		{
			if(mob.isInCombat())
				return combatCastingTime(mob,commands);
			if(abstractQuality()==Ability.QUALITY_MALICIOUS)
				return combatCastingTime(mob,commands);
		}
		return castingTime(mob,commands);
	}

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

	@Override
	public boolean isAutoInvoked()
	{
		return false;
	}

	@Override
	public boolean bubbleAffect()
	{
		return false;
	}

	protected boolean ignoreCompounding()
	{
		return false;
	}

	protected int getTicksBetweenCasts()
	{
		return 0;
	}

	protected long getTimeOfNextCast()
	{
		return 0;
	}

	protected void setTimeOfNextCast(final long absoluteTime)
	{
	}

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

	@Override
	public CostManager getTrainingCost(final MOB mob)
	{
		int qualifyingLevel;
		int playerLevel=1;
		if(mob!=null)
		{
			final Integer[] O=CMLib.ableMapper().getCostOverrides(mob,ID());
			if(O!=null)
			{
				Integer val=O[AbilityMapper.AbilCostType.TRAIN.ordinal()];
				if(val!=null)
					return CMLib.utensils().createCostManager(CostType.TRAIN,Double.valueOf(val.intValue()));
				val=O[AbilityMapper.AbilCostType.PRAC.ordinal()];
				if(val!=null)
					return CMLib.utensils().createCostManager(CostType.PRACTICE,Double.valueOf(val.intValue()));
			}
			qualifyingLevel=CMLib.ableMapper().qualifyingLevel(mob, this);
			playerLevel=mob.basePhyStats().level();
		}
		else
		{
			qualifyingLevel=CMLib.ableMapper().lowestQualifyingLevel(ID());
			playerLevel=1;
		}
		if(qualifyingLevel<=0)
			qualifyingLevel=1;
		final CostDef rawCost=getRawTrainingCost();
		if(rawCost==null)
			return CMLib.utensils().createCostManager(CostType.TRAIN,Double.valueOf(1.0));
		final double[] vars=new double[]{ qualifyingLevel,playerLevel};
		final double value=CMath.parseMathExpression(rawCost.costDefinition(),vars);
		return CMLib.utensils().createCostManager(new CostDef.Cost(value, rawCost.type(), rawCost.typeCurrency()));
	}

	protected int practicesToPractice(final MOB mob)
	{
		if(mob!=null)
		{
			final Integer[] O=CMLib.ableMapper().getCostOverrides(mob,ID());
			if((O!=null)&&(O[AbilityMapper.AbilCostType.PRACPRAC.ordinal()]!=null))
				return O[AbilityMapper.AbilCostType.PRACPRAC.ordinal()].intValue();
		}
		return iniPracticesToPractice();
	}

	protected int iniPracticesToPractice()
	{
		return 1;
	}

	protected void setTimeOfNextCast(final MOB caster)
	{
		long newTime=(getTicksBetweenCasts()*CMProps.getTickMillis());
		double mul=1.0;
		mul -= (0.05 * getXLEVELLevel(caster));
		mul -= (0.1 * getXTIMELevel(caster));
		newTime=Math.round(CMath.mul(newTime,mul));
		setTimeOfNextCast(System.currentTimeMillis() +newTime);
	}

	@Override
	public String miscTextFormat()
	{
		return CMParms.FORMAT_UNDEFINED;
	}

	@Override
	public long flags()
	{
		return 0;
	}

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

	/**
	 *  amount of mana/move used by this ability, overriding ini file
	 *  -1=normal, Ability.COST_ALL=all, Ability.COST_PCT
	 * @return amount of mana/move used by this ability, overriding ini file
	 */
	protected int overrideMana()
	{
		return Ability.COST_NORMAL;
	}

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

	@Override
	public int enchantQuality()
	{
		return abstractQuality();
	}

	@Override
	public void initializeClass()
	{
	}

	@Override
	public boolean mayBeEnchanted()
	{
		if((CMLib.ableMapper().lowestQualifyingLevel(ID())>POWER_LEVEL_THRESHOLD)
		||(usageCost(null,true)[0]>POWER_COST_THRESHOLD)
		||(overrideMana()>POWER_OVERRIDE_THRESHOLD)
		||(CMath.bset(flags(), FLAG_CLANMAGIC)))
			return false;
		return true;
	}

	@Override
	public String L(final String str, final String ... xs)
	{
		return CMLib.lang().fullSessionTranslation(str, xs);
	}

	protected static String[] I(final String[] str)
	{
		for(int i=0;i<str.length;i++)
			str[i]=CMLib.lang().commandWordTranslation(str[i]);
		return str;
	}

	protected int castingQuality(final MOB mob, final Physical target, final int abstractQuality)
	{
		if((target!=null)&&(target.fetchEffect(ID())!=null))
			return Ability.QUALITY_INDIFFERENT;
		if(isAutoInvoked())
			return Ability.QUALITY_INDIFFERENT;
		if(mob != null)
		{
			if((target!=null)
			&&(mob.getVictim()==target))
			{
				if((minRange()>0)&&(mob.rangeToTarget()<minRange()))
					return Ability.QUALITY_INDIFFERENT;
				if(mob.rangeToTarget()>maxRange())
					return Ability.QUALITY_INDIFFERENT;
			}
			final Room R=mob.location();
			if((R!=null)
			&&(this.canAffect(CAN_ROOMS)||this.canTarget(CAN_ROOMS))
			&&(R.fetchEffect(ID())!=null))
				return Ability.QUALITY_INDIFFERENT;
		}
		switch(abstractQuality)
		{
		case Ability.QUALITY_BENEFICIAL_OTHERS:
			if(mob==target)
				return Ability.QUALITY_BENEFICIAL_SELF;
			return Ability.QUALITY_BENEFICIAL_OTHERS;
		case Ability.QUALITY_MALICIOUS:
			return Ability.QUALITY_MALICIOUS;
		case Ability.QUALITY_BENEFICIAL_SELF:
			if((target instanceof MOB)&&(mob!=target))
				return Ability.QUALITY_INDIFFERENT;
			return Ability.QUALITY_BENEFICIAL_SELF;
		default:
			return Ability.QUALITY_INDIFFERENT;
		}
	}

	@Override
	public int castingQuality(final MOB mob, final Physical target)
	{
		return castingQuality(mob,target,abstractQuality());
	}

	protected synchronized int expertise(final MOB mob, final Ability A, final ExpertiseLibrary.XType code)
	{
		if((mob!=null)
		&&(A.isNowAnAutoEffect()
			||(A.canBeUninvoked())
			||A.isAutoInvoked()))
		{
			int xlevel = CMLib.expertises().getExpertiseLevelCached(mob, A.ID(), code);
			final CharStats charStats=mob.charStats(); // circumstantial bonuses
			final String codeStr="X"+code.name()+"+";
			xlevel += charStats.getAbilityAdjustment(codeStr+ID().toUpperCase());
			xlevel += charStats.getAbilityAdjustment(codeStr+Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES]);
			xlevel += charStats.getAbilityAdjustment(codeStr+Ability.DOMAIN_DESCS[(classificationCode()&Ability.ALL_DOMAINS)>> 5]);
			xlevel += charStats.getAbilityAdjustment(codeStr+"*");
			return xlevel;
		}
		return 0;
	}

	protected int getX1Level(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.X1);
	}

	protected int getX2Level(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.X2);
	}

	protected int getX3Level(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.X3);
	}

	protected int getX4Level(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.X4);
	}

	protected int getX5Level(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.X5);
	}

	protected int getXLEVELLevel(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.LEVEL);
	}

	protected int getXLOWCOSTLevel(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.LOWCOST);
	}

	protected int getXLOWFREECOSTLevel(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.LOWFREECOST);
	}

	protected int getXMAXRANGELevel(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.MAXRANGE);
	}

	protected int getXTIMELevel(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.TIME);
	}

	protected int getXPCOSTLevel(final MOB mob)
	{
		return expertise(mob, this, ExpertiseLibrary.XType.XPCOST);
	}

	protected int getXPCOSTAdjustment(final MOB mob, final int xpLoss)
	{
		final int xLevel=getXPCOSTLevel(mob);
		if(xLevel<=0)
			return xpLoss;
		return xpLoss-(int)Math.round(CMath.mul(xpLoss,CMath.mul(.05,xLevel)));
	}

	protected int adjustedMaxInvokerRange(final int max)
	{
		if(invoker==null)
			return max;
		final int level=getXMAXRANGELevel(invoker);
		if(level<=0)
			return  max;
		return max+(int)Math.round(Math.ceil(CMath.mul(max,CMath.mul(level,0.2))));
	}

	/**
	 * Designates whether, when used as a property/effect, what sort of objects this
	 * ability can affect. Uses the Ability.CAN_* constants.
	 * @see com.planet_ink.coffee_mud.Abilities.interfaces.Ability
	 * @return a mask showing the type of objects this ability can affect
	 */
	protected int canAffectCode()
	{
		return Ability.CAN_AREAS|
				 Ability.CAN_ITEMS|
				 Ability.CAN_MOBS|
				 Ability.CAN_ROOMS|
				 Ability.CAN_EXITS;
	}

	/**
	 * Designates whether, when invoked as a skill, what sort of objects this
	 * ability can effectively target. Uses the Ability.CAN_* constants.
	 * @see com.planet_ink.coffee_mud.Abilities.interfaces.Ability
	 * @return a mask showing the type of objects this ability can target
	 */
	protected int canTargetCode()
	{
		return Ability.CAN_AREAS|
				 Ability.CAN_ITEMS|
				 Ability.CAN_MOBS|
				 Ability.CAN_ROOMS|
				 Ability.CAN_EXITS;
	}

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

	@Override
	public long expirationDate()
	{
		return (tickDown) * CMProps.getTickMillis();
	}

	@Override
	public void setExpirationDate(final long time)
	{
		if(time>System.currentTimeMillis())
			tickDown=(int)((time-System.currentTimeMillis())/CMProps.getTickMillis());
		else
			tickDown=(int)Math.round(CMath.div(time,CMProps.getTickMillis()));
	}

	@Override
	public boolean isNowAnAutoEffect()
	{
		return isAnAutoEffect;
	}

	@Override
	public boolean isSavable()
	{
		return savable;
	}

	@Override
	public void setSavable(final boolean truefalse)
	{
		savable=truefalse;
	}

	@Override
	public void destroy()
	{
		amDestroyed=true;
		affected=null;
		invoker=null;
		miscText=null;
	}

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

	@Override
	public void setName(final String newName)
	{
	}

	@Override
	public void setDisplayText(final String newDisplayText)
	{
	}

	@Override
	public void setDescription(final String newDescription)
	{
	}

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

	@Override
	public void setAbilityCode(final int newCode)
	{
	}

	@Override
	public List<String> externalFiles()
	{
		return null;
	}

	protected long minCastWaitTime()
	{
		return 0;
	}

	// ** For most abilities, the following stuff actually matters */
	@Override
	public void setMiscText(final String newMiscText)
	{
		miscText=newMiscText;
	}

	@Override
	public String text()
	{
		return miscText;
	}

	@Override
	public int proficiency()
	{
		return proficiency;
	}

	@Override
	public void setProficiency(final int newProficiency)
	{
		proficiency=newProficiency;
		if(proficiency>100)
			proficiency=100;
	}

	protected int addedTickTime(final MOB invokerMOB, final int baseTickTime)
	{
		return (int)Math.round(CMath.mul(baseTickTime,CMath.mul(getXTIMELevel(invokerMOB),0.20)));
	}

	@Override
	public void startTickDown(final MOB invokerMOB, final Physical affected, int tickTime)
	{
		if(invokerMOB!=null)
			invoker=invokerMOB;

		savable=false; // makes it so that the effect does not save!

		if((classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_CRAFTINGSKILL))
		&&(classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_GATHERINGSKILL))
		&&(classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_EPICUREAN))
		&&(classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_BUILDINGSKILL)))
			tickTime+=addedTickTime(invokerMOB,tickTime);
		if(invoker()!=null)
		{
			for(int c=0;c<invoker().charStats().numClasses();c++)
				tickTime=invoker().charStats().getMyClass(c).classDurationModifier(invoker(),this,tickTime);
		}
		if(affected instanceof MOB)
		{
			final MOB mob=(MOB)affected;
			final Room room=mob.location();
			if(room==null)
				return;
			if(affected.fetchEffect(ID())==null)
			{
				affected.addEffect(this);
				final int ecap=CMProps.getIntVar(CMProps.Int.EFFECTCAP);
				if(ecap>0)
				{
					final List<Ability> candidates = new ArrayList<Ability>(mob.numEffects());
					for(final Enumeration<Ability> e = mob.effects();e.hasMoreElements();)
					{
						final Ability eA=e.nextElement();
						if((eA!=null)
						&&(eA.canBeUninvoked())
						&&(eA.invoker()!=null))
							candidates.add(eA);
					}
					if(candidates.size()>ecap)
					{
						final Ability deadA=candidates.get(CMLib.dice().roll(1, candidates.size(), -1));
						if(deadA!=null)
							deadA.unInvoke();
					}
				}
			}
			room.recoverRoomStats();
			if(invoker()!=affected)
			{
				for(int c=0;c<mob.charStats().numClasses();c++)
					tickTime=mob.charStats().getMyClass(c).classDurationModifier(mob,this,tickTime);
			}
		}
		else
		{
			if(affected.fetchEffect(this.ID())==null)
				affected.addEffect(this);

			if(affected instanceof Room)
				((Room)affected).recoverRoomStats();
			else
				affected.recoverPhyStats();
			CMLib.threads().startTickDown(this,Tickable.TICKID_MOB,1);
		}
		tickDown=tickTime;
	}

	public boolean disregardsArmorCheck(final MOB mob)
	{
		// armor checks are mostly handled by classes.
		// this is here for cases when someone gets a skill without the class to keep it in check.
		// that's why you disregard the armor check with you DO qualify
		return ((mob==null)
				||(mob.isMonster())
				||(CMLib.ableMapper().qualifiesByLevel(mob,this)));
	}

	protected int getPersonalLevelAdjustments(final MOB caster)
	{
		final CharStats charStats = caster.charStats();
		return  charStats.getAbilityAdjustment("LEVEL+"+ID().toUpperCase())
			+ charStats.getAbilityAdjustment("LEVEL+"+Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES])
			+ charStats.getAbilityAdjustment("LEVEL+"+Ability.DOMAIN_DESCS[(classificationCode()&Ability.ALL_DOMAINS)>> 5])
			+ charStats.getAbilityAdjustment("LEVEL+*");
	}

	@Override
	public int adjustedLevel(final MOB caster, final int asLevel)
	{
		if(caster==null)
			return 1;
		final int lowestQualifyingLevel=CMLib.ableMapper().lowestQualifyingLevel(this.ID());
		int adjLevel=lowestQualifyingLevel;
		if(asLevel<=0)
		{
			final int qualifyingLevel=CMLib.ableMapper().qualifyingLevel(caster,this);
			if((caster.isMonster())||(qualifyingLevel>=0))
				adjLevel+=(CMLib.ableMapper().qualifyingClassLevel(caster,this)-qualifyingLevel);
			else
				adjLevel=caster.phyStats().level()-lowestQualifyingLevel-25;
		}
		else
			adjLevel=asLevel;
		if(adjLevel<lowestQualifyingLevel)
			adjLevel=lowestQualifyingLevel;
		adjLevel += getPersonalLevelAdjustments(caster);
		if(adjLevel<1)
			return 1;
		int level=adjLevel+getXLEVELLevel(caster);
		final CharStats CS=caster.charStats();
		for(int c=0;c<CS.numClasses();c++)
			level=CS.getMyClass(c).classDurationModifier(invoker(),this,level);
		return level;
	}

	@Override
	public boolean canTarget(final int can_code)
	{
		final int canTarget=canTargetCode();
		if((can_code==0)||(canTarget==0))
			return can_code == canTarget;
		return CMath.bset(canTargetCode(),can_code);
	}

	@Override
	public boolean canAffect(final int can_code)
	{
		final int canAffect=canAffectCode();
		if((can_code==0)||(canAffect==0))
			return can_code == canAffect;
		return CMath.bset(canAffectCode(),can_code);
	}

	@Override
	public boolean canAffect(final Physical P)
	{
		if((P==null)&&(canAffectCode()==0))
			return true;
		if(P==null)
			return false;
		if((P instanceof MOB)&&((canAffectCode()&Ability.CAN_MOBS)>0))
			return true;
		if((P instanceof Item)&&((canAffectCode()&Ability.CAN_ITEMS)>0))
			return true;
		if((P instanceof Exit)&&((canAffectCode()&Ability.CAN_EXITS)>0))
			return true;
		if((P instanceof Room)&&((canAffectCode()&Ability.CAN_ROOMS)>0))
			return true;
		if((P instanceof Area)&&((canAffectCode()&Ability.CAN_AREAS)>0))
			return true;
		return false;
	}

	@Override
	public boolean canTarget(final Physical P)
	{
		if((P==null)&&(canTargetCode()==0))
			return true;
		if(P==null)
			return false;
		if((P instanceof MOB)&&((canTargetCode()&Ability.CAN_MOBS)>0))
			return true;
		if((P instanceof Item)&&((canTargetCode()&Ability.CAN_ITEMS)>0))
			return true;
		if((P instanceof Room)&&((canTargetCode()&Ability.CAN_ROOMS)>0))
			return true;
		if((P instanceof Area)&&((canTargetCode()&Ability.CAN_AREAS)>0))
			return true;
		return false;
	}

	protected MOB getTarget(final MOB mob, final List<String> commands, final Environmental givenTarget)
	{
		return getTarget(mob,commands,givenTarget,false,false);
	}

	//BZ: The magical pre-cast mana-saving range check, pt 1/2
	protected boolean checkTargetRange(final MOB mob, final MOB target)
	{
		if((target != null)
		&&(target!=mob)
		&&((minRange()>0)||((maxRange()>0)&&(maxRange()<99999)))
		&&(abstractQuality()==Ability.QUALITY_MALICIOUS))
		{
			if((minRange()>0)
			&&(((mob.getVictim()==target)&&(mob.rangeToTarget()<minRange()))
			  ||((target.getVictim()==mob)&&(target.rangeToTarget()<minRange())))
			)
			{
				failureTell(mob,mob,false,L("You are too close to @x1 to do that.",target.name()));
				return false;
			}
			else
			if((maxRange()>0)
			&&(maxRange()<99999)
			&&(((mob.getVictim()==target)&&(mob.rangeToTarget()>maxRange()))
			  ||((target.getVictim()==mob)&&(target.rangeToTarget()>maxRange())))
			)
			{
				failureTell(mob,mob,false,L("You are too far away from @x1 to do that.",target.name()));
				return false;
			}
		}
		return true;
	}

	protected MOB getTarget(final MOB mob, final List<String> commands, final Environmental givenTarget, final boolean quiet, final boolean alreadyAffOk)
	{
		String targetName=CMParms.combine(commands,0);
		MOB target=null;
		if((givenTarget!=null)
		&&(givenTarget instanceof MOB))
			target=(MOB)givenTarget;
		else
		if(targetName.length()==0)
		{
			final boolean inCombat = mob.isInCombat();
			if(inCombat
			&&(castingQuality(mob,mob.getVictim())==Ability.QUALITY_MALICIOUS)
			&&(mob.getVictim()!=null))
				target=mob.getVictim();
			else
			if(castingQuality(mob,mob)==Ability.QUALITY_BENEFICIAL_SELF)
				target=mob;
			else
			if(inCombat
			&&(abstractQuality()==Ability.QUALITY_MALICIOUS)
			&&(mob.getVictim()!=null))
				target=mob.getVictim();
			else
			if(abstractQuality()!=Ability.QUALITY_MALICIOUS)
				target=mob;
			if(target == null)
			{
				failureTell(mob,mob,false,L("You need to specify a target."));
				return null;
			}
		}
		else
		if(targetName.equalsIgnoreCase("self")||targetName.equalsIgnoreCase("me"))
			target=mob;
		else
		if(mob.location()!=null)
		{
			target=mob.location().fetchInhabitant(targetName);
			if(target==null)
			{
				final Environmental t=mob.location().fetchFromRoomFavorItems(null,targetName);
				if((t!=null)&&(!(t instanceof MOB)))
				{
					if(!quiet)
						mob.tell(mob,t,null,L("You can't do that to <T-NAMESELF>."));
					return null;
				}
			}
		}

		if(target!=null)
			targetName=target.name();

		if((target==null)
		||((givenTarget==null)
			&&(!CMLib.flags().canBeSeenBy(target,mob))
			&&((!CMLib.flags().canBeHeardMovingBy(target,mob))||(!target.isInCombat()))))
		{
			if(!quiet)
			{
				if(targetName.trim().length()==0)
					failureTell(mob,mob,false,L("You don't see them here."));
				else
					failureTell(mob,mob,false,L("You don't see anyone called '@x1' here.",targetName));
			}
			return null;
		}

		if((!alreadyAffOk)&&(!isAutoInvoked())&&(target.fetchEffect(this.ID())!=null))
		{
			if((givenTarget==null)&&(!quiet))
			{
				if(target==mob)
					failureTell(mob,mob,false,L("You are already affected by @x1.",name()));
				else
					failureTell(mob,target,false,L("<S-NAME> is already affected by @x1.",name()));
			}
			return null;
		}

		if(!checkTargetRange(mob,target))
			target=null;

		return target;
	}

	protected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget, final Filterer<Environmental> filter)
	{
		return getAnyTarget(mob,commands,givenTarget,filter,false,false);
	}

	protected Physical getAnyTarget(final MOB mob, final Room location, final boolean anyContainer, final List<String> commands,
									final Physical givenTarget, final Filterer<Environmental> filter)
	{
		final Physical P=getAnyTarget(mob,commands,givenTarget,filter,false,false, true);
		if(P!=null)
			return P;
		return getTarget(mob, location, givenTarget, anyContainer, commands, filter);
	}

	protected Physical getAnyTarget(final MOB mob, final Room location, final boolean anyContainer, final List<String> commands,
			final Physical givenTarget, final Filterer<Environmental> filter, final boolean quiet)
	{
		final Physical P=getAnyTarget(mob,commands,givenTarget,filter,false,false, true);
		if(P!=null)
			return P;
		return getTarget(mob, location, givenTarget, anyContainer, commands, filter, quiet);
	}

	protected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget, final Filterer<Environmental> filter, final boolean checkOthersInventory)
	{
		return getAnyTarget(mob,commands,givenTarget,filter,checkOthersInventory,false);
	}

	protected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget,
			final Filterer<Environmental> filter,
			final boolean checkOthersInventory, final boolean alreadyAffOk)
	{
		return getAnyTarget(mob,commands,givenTarget,filter,checkOthersInventory,alreadyAffOk,false);
	}

	protected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget,
			final Filterer<Environmental> filter,
			final boolean checkOthersInventory, final boolean alreadyAffOk,
			final boolean quiet)
	{
		final Room R=mob.location();
		String targetName=CMParms.combine(commands,0);
		Physical target=null;
		if(givenTarget != null)
			target=givenTarget;
		else
		if((targetName.length()==0)&&(mob.isInCombat())&&(castingQuality(mob,mob.getVictim())==Ability.QUALITY_MALICIOUS))
			target=mob.getVictim();
		else
		if(targetName.equalsIgnoreCase("self")||targetName.equalsIgnoreCase("me"))
			target=mob;
		else
		if((targetName.length()==0)&&(mob.isInCombat())&&(abstractQuality()==Ability.QUALITY_MALICIOUS))
			target=mob.getVictim();
		else
		if(R!=null)
		{
			if((target==null)
			&&(targetName.equalsIgnoreCase("here")))
				target=R;
			if(target == null)
				target=R.fetchFromRoomFavorMOBs(null,targetName);
			if(target==null)
				target=R.fetchFromMOBRoomFavorsItems(mob,null,targetName,filter);
			if((target==null)
			&&(targetName.equalsIgnoreCase("room")
				||targetName.equalsIgnoreCase("place")))
				target=R;
			int dir=-1;
			if((target==null)&&((dir=CMLib.directions().getGoodDirectionCode(targetName))>=0))
				target=R.getExitInDir(dir);
			if((target==null)&&(checkOthersInventory))
			{
				for(int i=0;i<R.numInhabitants();i++)
				{
					final MOB M=R.fetchInhabitant(i);
					target=M.fetchItem(null,filter,targetName);
					if(target!=null)
						break;
				}
			}
		}
		if(target!=null)
			targetName=target.name();

		if((target==null)
		||((givenTarget==null)
		   &&(!CMLib.flags().canBeSeenBy(target,mob))
		   &&((!CMLib.flags().canBeHeardMovingBy(target,mob))
				||((target instanceof MOB)&&(!((MOB)target).isInCombat())))))
		{
			if(!quiet)
			{
				if(targetName.trim().length()==0)
					failureTell(mob,mob,false,L("You don't see that here."));
				else
				if(!CMLib.flags().isSleeping(mob))
					failureTell(mob,mob,false,L("You don't see '@x1' here.",targetName));
			}
			return null;
		}

		if((!alreadyAffOk)&&(target.fetchEffect(this.ID())!=null))
		{
			if(givenTarget==null)
			{
				if(!quiet)
				{
					if(target==mob)
						failureTell(mob,mob,false,L("You are already affected by @x1.",name()));
					else
						mob.tell(mob,target,null,L("<T-NAME> is already affected by @x1.",name()));
				}
			}
			return null;
		}
		return target;
	}

	protected static Item possibleContainer(final MOB mob, final List<String> commands, final boolean withStuff, final Filterer<Environmental> filter)
	{
		if((commands==null)||(commands.size()<2))
			return null;

		final String possibleContainerID=commands.get(commands.size()-1);
		final Environmental thisThang=mob.location().fetchFromMOBRoomFavorsItems(mob,null,possibleContainerID,filter);
		if((thisThang!=null)
		&&(thisThang instanceof Item)
		&&(((Item)thisThang) instanceof Container)
		&&((!withStuff)||(((Container)thisThang).hasContent())))
		{
			commands.remove(commands.size()-1);
			return (Item)thisThang;
		}
		return null;
	}

	protected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget, final List<String> commands, final Filterer<Environmental> filter)
	{
		return getTarget(mob,location,givenTarget,null,commands,filter);
	}

	protected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget,
			final boolean anyContainer, final List<String> commands, final Filterer<Environmental> filter)
	{
		return getTarget(mob, location, givenTarget, anyContainer, commands, filter, false);
	}

	protected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget,
			final boolean anyContainer, final List<String> commands, final Filterer<Environmental> filter,
			final boolean quiet)
	{
		Item I=this.getTarget(mob, location, givenTarget, null, commands, filter, anyContainer);
		if(I!=null)
			return I;
		if(!anyContainer)
			return I;
		final List<Item> containers=new ArrayList<Item>();
		if(location!=null)
		{
			for(final Enumeration<Item> i=location.items();i.hasMoreElements();)
			{
				final Item C=i.nextElement();
				if((C instanceof Container)
				&&(((Container)C).isOpen()))
					containers.add(C);
			}
		}
		else
		{
			for(final Enumeration<Item> i=mob.items();i.hasMoreElements();)
			{
				final Item C=i.nextElement();
				if((C instanceof Container)
				&&(((Container)C).isOpen()))
					containers.add(C);
			}
		}
		if(containers.size()==0)
			return this.getTarget(mob, location, givenTarget, null, commands, filter, quiet);
		else
		{
			for(int c=0;c<containers.size();c++)
			{
				final Item C=containers.get(c);
				I=this.getTarget(mob, location, givenTarget, C, commands, filter, quiet || (c<containers.size()-1));
				if(I!=null)
					return I;
			}
		}
		return null;
	}

	protected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget, final Item container,
			final List<String> commands, final Filterer<Environmental> filter)
	{
		return getTarget(mob, location, givenTarget, container, commands, filter, false);
	}

	protected Item evalTargetItem(final MOB mob, final Room location, final Environmental givenTarget, final Environmental target, final String targetName, final boolean quiet)
	{
		if((target==null)
		||(!(target instanceof Item))
		||((givenTarget==null)&&(!CMLib.flags().canBeSeenBy(target,mob))))
		{
			if(!quiet)
			{
				if(targetName.length()==0)
					failureTell(mob,mob,false,L("You need to be more specific."));
				else
				if((target==null)||(target instanceof Item))
				{
					if(targetName.trim().length()==0)
						failureTell(mob,mob,false,L("You don't see that here."));
					else
					if(!CMLib.flags().isSleeping(mob)) // no idea why this is here :(
					{
						if(location != null)
							failureTell(mob,mob,false,L("You don't see anything called '@x1' here.",targetName));
						else
							failureTell(mob,mob,false,L("You don't have anything called '@x1'.",targetName));
					}
					else // this was added for clan donate (and other things I'm sure) while sleeping.
						failureTell(mob,mob,false,L("You don't see '@x1' in your dreams.",targetName));
				}
				else
					mob.tell(mob,target,null,L("You can't do that to <T-NAMESELF>."));
			}
			return null;
		}
		return (Item)target;
	}

	protected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget, final Item container,
			final List<String> commands, final Filterer<Environmental> filter, final boolean quiet)
	{
		String targetName=CMParms.combine(commands,0);

		Environmental target=null;
		if((givenTarget!=null)&&(givenTarget instanceof Item))
			target=givenTarget;

		if((location!=null)&&(target==null)&&(targetName.length()>0))
			target=location.fetchFromRoomFavorItems(container,targetName);
		if((target==null)&&(targetName.length()>0))
		{
			if(location!=null)
				target=location.fetchFromMOBRoomFavorsItems(mob,container,targetName,filter);
			else
			if(mob != null)
				target=mob.fetchItem(container, filter, targetName);
		}
		if(target!=null)
			targetName=target.name();

		return evalTargetItem(mob, location, givenTarget, target, targetName, quiet);

	}

	protected Item getTargetItemFavorMOB(final MOB mob, final Room location, final Physical givenTarget, final Item container,
			final List<String> commands, final Filterer<Environmental> filter)
	{
		return getTargetItemFavorMOB(mob, location, givenTarget, container, commands, filter, false);
	}

	protected Item getTargetItemFavorMOB(final MOB mob, final Room location, final Physical givenTarget,
			final List<String> commands, final Filterer<Environmental> filter)
	{
		return getTargetItemFavorMOB(mob, location, givenTarget, null, commands, filter, false);
	}

	protected Item getTargetItemFavorMOB(final MOB mob, final Room location, final Physical givenTarget, final Item container,
			final List<String> commands, final Filterer<Environmental> filter, final boolean quiet)
	{
		String targetName=CMParms.combine(commands,0);

		Environmental target=null;
		if((givenTarget!=null)&&(givenTarget instanceof Item))
			target=givenTarget;

		if((target==null)&&(targetName.length()>0))
		{
			if(location!=null)
				target=location.fetchFromMOBRoomFavorsItems(mob,container,targetName,filter);
			else
				target=mob.fetchItem(container, filter, targetName);
		}
		if(target!=null)
			targetName=target.name();

		return evalTargetItem(mob, location, givenTarget, target, targetName, quiet);
	}

	@Override
	public int compareTo(final CMObject o)
	{
		return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o));
	}

	protected void cloneFix(final Ability E)
	{
	}

	@Override
	public CMObject copyOf()
	{
		try
		{
			final StdAbility E=(StdAbility)this.clone();
			//CMClass.bumpCounter(E,CMClass.CMObjectType.ABILITY);//removed for mem & perf
			E.cloneFix(this);
			return E;

		}
		catch(final CloneNotSupportedException e)
		{
			return this.newInstance();
		}
	}

	@Override
	public boolean proficiencyCheck(final MOB mob, final int adjustment, final boolean auto)
	{
		if(auto)
		{
			isAnAutoEffect=true;
			if((mob!=null)&&(!mob.isMine(this)))
				setProficiency(100);
			return true;
		}

		isAnAutoEffect=false;
		int pctChance=proficiency();
		if(mob != null)
		{
			if(CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.SUPERSKILL))
				return true;
			final CharStats charStats = mob.charStats();
			pctChance += charStats.getAbilityAdjustment("PROF+"+ID().toUpperCase());
			pctChance += charStats.getAbilityAdjustment("PROF+"+Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES]);
			pctChance += charStats.getAbilityAdjustment("PROF+"+Ability.DOMAIN_DESCS[(classificationCode()&Ability.ALL_DOMAINS)>> 5]);
			pctChance += charStats.getAbilityAdjustment("PROF+*");
		}

		if(!CMSecurity.isDisabled(DisFlag.DIS955RULE))
		{
			if(pctChance>95)
				pctChance=95;
			if(pctChance<5)
				pctChance=5;
		}

		if(adjustment>=0)
			pctChance+=adjustment;
		else
		if(CMLib.dice().rollPercentage()>(100+adjustment))
			return false;
		return (CMLib.dice().rollPercentage()<pctChance);
	}

	@Override
	public Physical affecting()
	{
		return affected;
	}

	@Override
	public void setAffectedOne(final Physical P)
	{
		affected=P;
	}

	@Override
	public void unInvoke()
	{
		unInvoked=true;

		final Physical being=affected;
		if(being==null)
			return;

		if(canBeUninvoked())
		{
			being.delEffect(this);
			if(being instanceof Room)
				((Room)being).recoverRoomStats();
			else
			if(being instanceof MOB)
			{
				final MOB M=(MOB)being;
				final Room R=M.location();
				if((R!=null)&&(R.isInhabitant(M)))
					R.recoverRoomStats();
				else
				{
					M.recoverPhyStats();
					M.recoverCharStats();
					M.recoverMaxState();
				}
			}
			else
				being.recoverPhyStats();
		}
	}

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

	@Override
	public void affectPhyStats(final Physical affected, final PhyStats affectableStats)
	{
	}

	@Override
	public void affectCharStats(final MOB affectedMob, final CharStats affectableStats)
	{
	}

	@Override
	public void affectCharState(final MOB affectedMob, final CharState affectableMaxState)
	{
	}

	@Override
	public MOB invoker()
			{
				int maxAmt;
				int baseAmt;
				switch(costType)
				{
				case 1:
					maxAmt=mob.maxState().getMovement();
					baseAmt=mob.baseState().getMovement();
					break;
				case 2:
					maxAmt=mob.maxState().getHitPoints();
					baseAmt=mob.baseState().getHitPoints();
					break;
				default:
				case 0:
					maxAmt=mob.maxState().getMana();
					baseAmt=mob.baseState().getMana();
					break;
				}
				int costDown=0;
				final int rawCost;
				else
				{
					switch(costType)
					{
					rawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));
				else
					rawCost = consumed / divider;
				if(rawCost>2)
				{
					costDown=getXLOWCOSTLevel(mob);
					final int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));
						break;
					default:
					case 0:
						maxAmt=mob.maxState().getMana();
						baseAmt=mob.baseState().getMana();
						break;
					}
				}
				int costDown=0;
				final int rawCost;
				if(consumed==COST_ALL)
					rawCost = (baseAmt>maxAmt) ? baseAmt : maxAmt;
				else
				if(consumed>COST_PCT)
					rawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));
				else
					rawCost = consumed / divider;
				if((rawCost>2)&&(mob!=null))
				{
					costDown=getXLOWCOSTLevel(mob);
					final int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));
					if(fivePercent > 1)
						costDown *= fivePercent;
					if((costDown>=rawCost)&&(rawCost<Integer.MAX_VALUE/2))
						costDown=rawCost/2;
					minimum=(minimum-costDown);
					if(minimum<5)
						minimum=5;
					final int freeDown=getXLOWFREECOSTLevel(mob) / 2;
					costDown += freeDown;
					minimum=(minimum-freeDown);
					if(minimum<0)
						minimum=0;
	@Override
	public int[] usageCost(final MOB mob, final boolean ignoreClassOverride)
	{
		if(mob==null)
		{
			final Map<String,int[]> overrideCache=getHardOverrideManaCache();
			if(!overrideCache.containsKey(ID()))
			{
				final int[] usage=new int[Ability.USAGEINDEX_TOTAL];
				Arrays.fill(usage,overrideMana());
				overrideCache.put(ID(), usage);
			}
			return overrideCache.get(ID());
		}
		if(usageType()==Ability.USAGE_NADA)
			return STATIC_USAGE_NADA;

		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);
		}
		return hardOverrideCache;
	}

	@Override
	public int[] usageCost(final MOB mob, final boolean ignoreClassOverride)
	{
		final Map<String,int[]> overrideCache=getHardOverrideManaCache();
		if(mob==null)
		{
		{
			int diff=0;
			int lowest=Integer.MAX_VALUE;
			for(int c=0;c<mob.charStats().numClasses();c++)
			{
				final CharClass C=mob.charStats().getMyClass(c);
				final int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());
				final int classLevel=mob.charStats().getClassLevel(C.ID());
				if((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))
				{
					diff+=(classLevel-qualifyingLevel);
					if(qualifyingLevel<lowest)
						lowest=qualifyingLevel;
				}
			}
			if(lowest==Integer.MAX_VALUE)
		}
		else
		{
			int diff=0;
			}

			Integer[] costOverrides=null;
			if(!ignoreClassOverride)
				costOverrides=CMLib.ableMapper().getCostOverrides(mob,ID());
			minimum=CMProps.getMinManaException(ID());
			if(minimum==Integer.MIN_VALUE)
					final int classLevel=mob.charStats().getClassLevel(C.ID());
					if((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))
					{
						diff+=(classLevel-qualifyingLevel);
						if(qualifyingLevel<lowest)
							lowest=qualifyingLevel;
					}
				}
			}
			if(lowest==Integer.MAX_VALUE)
			{
				lowest=CMLib.ableMapper().lowestQualifyingLevel(ID());
			{
				final double[] vars = new double[] {
					lowest,
					mob.phyStats().level(),
					minimum,
					adjustedLevel(mob,0),
					diff
				};
				consumed=(int)CMath.parseMathExpression((CMath.CompiledFormula)cost, vars, 0.0);
				minimum=CMProps.getIntVar(CMProps.Int.MANAMINCOST);
			if(minimum<0)
			{
				minimum=(lowest*CMath.abs(minimum));
				int actualMinimum=5;
				final int exception = CMProps.getMinManaException("_DEFAULT");
				if(exception > 0 )
					actualMinimum = exception;
				if(minimum<actualMinimum)
					minimum=actualMinimum;
			}
			final Object cost = CMProps.getManaCostObject(ID());
			if(cost instanceof Integer)
				consumed=((Integer)cost).intValue();
			else
			if(cost instanceof CMath.CompiledFormula)
			{
				final double[] vars = new double[] {
					lowest,
					(mob==null)?lowest:mob.phyStats().level(),
					minimum,
					(mob==null)?lowest:adjustedLevel(mob,0),
					diff
				};
			else
				abilityUsageCache[myCacheIndex]=usageCost;
		}
		return usageCost;
	}

				consumed=(consumed - (consumed/30 * diff));
			if(consumed<minimum)
				consumed=minimum;
			if((overrideMana()>=0) && (CMProps.getManaCostExceptionObject(ID()) == null))
				consumed=overrideMana();
			if((costOverrides!=null)&&(costOverrides[AbilityMapper.AbilCostType.MANA.ordinal()]!=null))
			{
				consumed=costOverrides[AbilityMapper.AbilCostType.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;
		}
		if(mob == null)
			overrideCache.put(ID(), usageCost);
		return usageCost;
	}

	@Override
	public void helpProficiency(final MOB mob, final int adjustment)
	{
		if(mob==null)
			return;
		final Ability A=mob.fetchAbility(ID());
		if(A==null)
			return;

		if(mob.isPlayer())
		{
			CMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_SKILLUSE);
			CMLib.achievements().possiblyBumpAchievement(mob, AchievementLibrary.Event.SKILLUSE, 1, this);
		}

		if(!A.isSavable())
			return;

		if((System.currentTimeMillis()-((StdAbility)A).lastCastHelp)<300000)
			return;

		if(!A.appropriateToMyFactions(mob))
			return;

		final int maxProficiency = CMLib.ableMapper().getMaxProficiency(mob,true,ID());
		if(A.proficiency()< maxProficiency)
		{
			final int currentProficiency=A.proficiency()+adjustment;
			if(((int)Math.round(Math.sqrt((mob.charStats().getStat(CharStats.STAT_INTELLIGENCE)))*34.0*Math.random()))>=currentProficiency)
			{
				final int qualLevel=CMLib.ableMapper().qualifyingLevel(mob,A);
				final double adjustedChance;
				if(qualLevel<0)
					adjustedChance=100.1;
				else
				if(qualLevel>30)
				{
					final float fatigueFactor=(mob.curState().getFatigue() > CharState.FATIGUED_MILLIS ? 50.0f : 100.0f);
					final int maxLevel=CMProps.get(mob.session()).getInt(CMProps.Int.LASTPLAYERLEVEL);
					adjustedChance=fatigueFactor * CMath.div((maxLevel+1-qualLevel),((2*maxLevel)+(10*qualLevel)));
				}
				else
				{
					final float fatigueFactor=(mob.curState().getFatigue() > CharState.FATIGUED_MILLIS ? 50.0f : 100.0f);
					final int maxLevel=CMProps.get(mob.session()).getInt(CMProps.Int.LASTPLAYERLEVEL);
					adjustedChance=fatigueFactor * CMath.div((maxLevel+1-qualLevel),((2*maxLevel)+(10*qualLevel)));
				}
				if(CMLib.dice().rollPercentage()<Math.round(adjustedChance))
				{
					// very important, since these can be autoinvoked affects (copies)!
					A.setProficiency(A.proficiency()+1);
					if((this!=A)&&(proficiency()<maxProficiency))
						setProficiency(A.proficiency());
					final Ability effA=mob.fetchEffect(ID());
					if((effA!=null) && (effA!=A) && (effA!=this)
					&&(effA.invoker()==mob)
					&&(effA.proficiency()<maxProficiency))
						effA.setProficiency(A.proficiency());
					if(mob.isAttributeSet(MOB.Attrib.AUTOIMPROVE))
						mob.tell(L("You become better at @x1.",A.name()));
					((StdAbility)A).lastCastHelp=System.currentTimeMillis();
				}
			}
			if((A.proficiency() >= maxProficiency)
			&&(mob.isPlayer()))
			{
				final List<String> channels = CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.PROFICIENT, mob);
				for (int i = 0; i < channels.size(); i++)
					CMLib.commands().postChannel(channels.get(i), mob.clans(), L("@x1 is now proficient at @x2.", mob.name(), A.Name()), true);
				CMLib.achievements().possiblyBumpAchievement(mob, AchievementLibrary.Event.SKILLPROF, 1, A);
			}
		}
		else
			A.setProficiency(maxProficiency);
	}

	protected boolean failureTell(final MOB mob, final MOB targetM, final boolean auto, final String msg)
	{
		if(mob==null)
			return false;
		if(auto
		&&(mob.isMonster())
		&&(targetM!=null)
		&&(targetM.isPlayer()))
			targetM.tell(targetM,null,null,msg);
		else
			mob.tell(targetM,null,null,msg);
		return false;
	}

	@Override
	public boolean preInvoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel, final int secondsElapsed, final double actionsRemaining)
	{
		if(mob.commandQueSize() > 1)
		{
			mob.tell(L("You cancel @x1.",name()));
			return false;
		}
		return true;
	}

	@Override
	public boolean invoke(final MOB mob, final Physical target, final boolean auto, final int asLevel)
	{
		final Vector<String> V=new Vector<String>(1);
		if(target!=null)
			V.addElement(target.name());
		return invoke(mob,V,target,auto,asLevel);
	}

	protected boolean testUsageCost(final MOB mob, final boolean auto, final int[] consumed, final boolean quiet)
	{
		if(mob.curState().getMana()<consumed[Ability.USAGEINDEX_MANA])
		{
			if(!quiet)
			{
				if(mob.maxState().getMana()==consumed[Ability.USAGEINDEX_MANA])
					failureTell(mob,mob,auto,L("You must be at full mana to do that."));
				else
					failureTell(mob,mob,auto,L("You don't have enough mana to do that."));
			}
			return false;
		}
		if(mob.curState().getMovement()<consumed[Ability.USAGEINDEX_MOVEMENT])
		{
			if(!quiet)
			{
				if(mob.maxState().getMovement()==consumed[Ability.USAGEINDEX_MOVEMENT])
					failureTell(mob,mob,auto,L("You must be at full movement to do that."));
				else
					failureTell(mob,mob,auto,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(!quiet)
			{
				if(mob.maxState().getHitPoints()==consumed[Ability.USAGEINDEX_HITPOINTS])
					failureTell(mob,mob,auto,L("You must be at full health to do that."));
				else
					failureTell(mob,mob,auto,L("You don't have enough hit points to do that."));
			}
			return false;
		}
		return true;
	}

	@Override
	public boolean invoke(final MOB mob, final List<String> commands, final Physical target, final boolean auto, final int asLevel)
	{
		//expertiseCache=null; // this was insane!
		if((mob!=null)&&(getXMAXRANGELevel(mob)>0)) // wut? this must be preventing an npe in some edge case...
			invoker=mob;
		if((!auto)&&(mob!=null))
		{
			isAnAutoEffect=false;

			// if you can't move, you can't cast! Not even verbal!
			if(!CMLib.flags().isAliveAwakeMobile(mob,false))
				return false;

			final Room room=mob.location();
			if((getTicksBetweenCasts()>0)
			&&(getTimeOfNextCast()>0)
			&&(System.currentTimeMillis()<getTimeOfNextCast())
			&&(room!=null)
			&&(room.getArea()!=null)
			&&(!CMSecurity.isAllowed(mob, room, CMSecurity.SecFlag.ALLSKILLS)))
			{
				final TimeClock C=room.getArea().getTimeObj();
				if(C!=null)
					failureTell(mob,mob,auto,L("You must wait @x1 before you can do that again.",C.deriveEllapsedTimeString(getTimeOfNextCast()-System.currentTimeMillis())));
				return false;
			}

			if(CMath.bset(usageType(),Ability.USAGE_MOVEMENT)
			&&(CMLib.flags().isBound(mob)))
			{
				failureTell(mob,mob,auto,L("You are bound!"));
				return false;
			}

			int[] consumed=usageCost(mob,false);
			final int[] timeCache;
			final int nowLSW = (int)(System.currentTimeMillis()&0x7FFFFFFF);
			final AbilityMapper.CompoundingRule rule = CMLib.ableMapper().getCompoundingRule(mob, this);
			if((rule!=null)
			&&(rule.compoundingTicks() > 0)
			&&(consumed != STATIC_USAGE_NADA)
			&&(overrideMana()<Integer.MAX_VALUE-51))
			{
				final int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());
				if(abilityUsageCache[Ability.CACHEINDEX_LASTTIME] == null)
					abilityUsageCache[Ability.CACHEINDEX_LASTTIME] = new int[USAGEINDEX_TOTAL];
				timeCache = abilityUsageCache[Ability.CACHEINDEX_LASTTIME];
				if(timeCache[USAGEINDEX_TIMELSW]>nowLSW)
					timeCache[USAGEINDEX_TIMELSW]=0;
				final int numTicksSinceLastCast=(int)((nowLSW-timeCache[USAGEINDEX_TIMELSW]) / CMProps.getTickMillis());
				if((numTicksSinceLastCast >= rule.compoundingTicks())||(ignoreCompounding()))
					timeCache[USAGEINDEX_COUNT]=0;
				else
				{
					consumed=Arrays.copyOf(consumed, consumed.length);
					final double pctPenalty = rule.pctPenalty();
					final double amtPenalty = rule.amtPenalty();
					if(amtPenalty < 0)
					{
						failureTell(mob,mob,auto,L("You can't do that again just yet."));
						return false;
					}
					else
					{
						for(int usageIndex = 0 ; usageIndex < Ability.USAGEINDEX_TOTAL; usageIndex++)
						{
							if(consumed[usageIndex]>0)
							{
								double newAmt=consumed[usageIndex];
								for(int ct=0;ct<timeCache[USAGEINDEX_COUNT];ct++)
								{
									if(newAmt<Short.MAX_VALUE)
									{
										newAmt+=amtPenalty;
										newAmt+=CMath.mul(newAmt, pctPenalty);
									}
								}
								consumed[usageIndex]=(int)Math.round(Math.ceil(newAmt));
							}
						}
					}
				}
			}
			else
				timeCache=null;

			if(!testUsageCost(mob,false,consumed,auto))
				return false;

			if((minCastWaitTime()>0)&&(lastCastHelp>0))
			{
				if((System.currentTimeMillis()-lastCastHelp)<minCastWaitTime())
				{
					if(minCastWaitTime()<=1000)
						failureTell(mob,mob,auto,L("You need a second to recover before doing that again."));
					else
					if(minCastWaitTime()<=5000)
						failureTell(mob,mob,auto,L("You need a few seconds to recover before doing that again."));
					else
						failureTell(mob,mob,auto,L("You need awhile to recover before doing that again."));
					return false;
				}
			}
			if(!checkComponents(mob))
				return false;
			if(timeCache!=null)
			{
				timeCache[USAGEINDEX_COUNT]++;
				timeCache[USAGEINDEX_TIMELSW]=nowLSW;
			}
			mob.curState().adjMana(-consumed[0],mob.maxState());
			mob.curState().adjMovement(-consumed[1],mob.maxState());
			mob.curState().adjHitPoints(-consumed[2],mob.maxState());
			helpProficiency(mob, 0);
			final CMMsg msg=CMClass.getMsg(mob,null,this,CMMsg.MSG_PREINVOKE, null);
			if(!mob.okMessage(mob, msg))
				return false;
			mob.executeMsg(mob, msg);
		}
		else
			isAnAutoEffect=true;
		return true;
	}

	protected boolean checkComponents(final MOB mob)
	{
		if((mob!=null)
		&&(!CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.COMPONENTS))
		)
		{
			final Vector<AbilityComponent> componentsRequirements=(Vector<AbilityComponent>)CMLib.ableComponents().getAbilityComponentMap().get(ID().toUpperCase());
			if(componentsRequirements!=null)
			{
				final List<Object> components=CMLib.ableComponents().componentCheck(mob,componentsRequirements, false);
				if(components==null)
				{
					failureTell(mob,mob,false,L("The requirements to use this @x1 are: @x2.",
							Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES].toLowerCase(),
							CMLib.ableComponents().getAbilityComponentDesc(mob,ID())));
					if(!mob.isPlayer())
						CMLib.ableComponents().startAbilityComponentTrigger(mob, this);
					return false;
				}
				CMLib.ableComponents().destroyAbilityComponents(components);
			}
		}
		return true;
	}

	protected Set<MOB> properTargets(final MOB mob, final Environmental givenTarget, final boolean auto)
	{
		Set<MOB> h=CMLib.combat().properTargets(this,mob,auto);
		if((givenTarget instanceof MOB)
		&&(CMLib.flags().isInTheGame((MOB)givenTarget,true)))
		{
			if(h==null)
				h=new SHashSet<MOB>();
			if(!h.contains(givenTarget))
				h.add((MOB)givenTarget);
		}
		return h;
	}

	protected List<MOB> properTargetList(final MOB mob, final Environmental givenTarget, final boolean auto)
	{
		final Set<MOB> h=properTargets(mob,givenTarget,auto);
		final List<MOB> list=new ArrayList<MOB>(h.size());
		if(h.contains(mob))
		{
			h.remove(mob);
			list.add(mob);
		}
		list.addAll(h);
		return list;
	}

	protected int adjustMaliciousTickdownTime(final MOB mob, final Physical target, final int baseTicks, final int asLevel)
	{
		int tickDown = baseTicks;
		if((target!=null)
		&&(asLevel<=0)
		&&(mob!=null)
		&&(!(target instanceof Room)))
		{
			int levelDiff = target.phyStats().level()-adjustedLevel(mob,asLevel);
			final int expRate=CMProps.getIntVar(CMProps.Int.EXPRATE);
			if(baseTicks > Integer.MAX_VALUE/4)
				levelDiff=0;
			else
			if(levelDiff > expRate)
				levelDiff = expRate;
			else
			if(levelDiff < -expRate)
				levelDiff = -expRate;
			final double levelTimeFudge = CMath.div(levelDiff, expRate+1);
			tickDown-=(int)Math.round(CMath.mul(tickDown,levelTimeFudge));
			if((tickDown>(CMProps.getTicksPerHour()/3))
			||(mob instanceof Deity))
				tickDown=(int)(CMProps.getTicksPerHour()/3);
		}

		if((tickDown>(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)))
		||(mob instanceof Deity))
			tickDown=(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY));

		if(tickDown<2)
			tickDown=2;
		return tickDown;
	}

	protected int getMaliciousTickdownTime(final MOB mob, final Physical target, final int tickAdjustmentFromStandard, final int asLevel)
	{
		if(tickAdjustmentFromStandard>0)
			return tickAdjustmentFromStandard;
		final int tickTime = adjustMaliciousTickdownTime(mob,target,((int)Math.round(CMath.mul(adjustedLevel(mob,asLevel),1.1)))+25,asLevel);
		if(tickAdjustmentFromStandard == 0)
			return tickTime;
		else
			return (int)Math.round(CMath.mul(CMath.div(-tickAdjustmentFromStandard, 100.0) , (double)tickTime));
	}

	public Ability maliciousAffect(final MOB mob, final Physical target, final int asLevel, int tickAdjustmentFromStandard, final int additionAffectCheckCode)
	{
		final Room room=mob.location();
		if(room==null)
			return null;
		if(additionAffectCheckCode>=0)
		{
			final CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.NO_EFFECT,additionAffectCheckCode,CMMsg.NO_EFFECT,null);
			if(room.okMessage(mob,msg))
			{
				room.send(mob,msg);
				if(msg.value()>0)
					return null;
			}
			else
				return null;
		}
		invoker=mob;
		final Ability newOne=(Ability)copyOf();
		((StdAbility)newOne).canBeUninvoked=true;
		tickAdjustmentFromStandard=getMaliciousTickdownTime(mob,target,tickAdjustmentFromStandard,asLevel);
		newOne.startTickDown(invoker,target,tickAdjustmentFromStandard);
		return newOne;
	}

	protected boolean beneficialWordsFizzle(final MOB mob, final Environmental target, final String message)
	{
		// it didn't work, but tell everyone you tried.
		final CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MSG_SPEAK,"^T"+message+"^?");
		final Room room=mob.location();
		if(room==null)
			return false;
		if(room.okMessage(mob,msg))
			room.send(mob,msg);
		return false;
	}

	protected boolean beneficialVisualFizzle(final MOB mob, final Environmental target, final String message)
	{
		// it didn't work, but tell everyone you tried.
		final CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MSG_OK_VISUAL,message);
		final Room room=mob.location();
		if(room==null)
			return false;
		if(room.okMessage(mob,msg))
			room.send(mob,msg);

		return false;
	}

	protected boolean maliciousFizzle(final MOB mob, final Environmental target, final String message)
	{
		// it didn't work, but tell everyone you tried.
		final String targetMessage;
		if((target instanceof MOB)
		&&(mob!=target)
		&&(((MOB)target).isAttributeSet(Attrib.NOBATTLESPAM)))
			targetMessage=null;
		else
			targetMessage=message;
		final CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MSG_OK_VISUAL|CMMsg.MASK_MALICIOUS,message,targetMessage,message);
		final Room room=mob.location();
		if(room==null)
			return false;
		CMLib.color().fixSourceFightColor(msg);
		if(room.okMessage(mob,msg))
			room.send(mob,msg);
		return false;
	}

	protected int adjustBeneficialTickdownTime(final MOB mob, final Environmental target, final int baseTicks)
	{
		int tickDown = baseTicks;
		if((tickDown>(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)))
		||(mob instanceof Deity))
			tickDown=(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY));
		if(tickDown<5)
			tickDown=5;
		return tickDown;
	}

	protected boolean aPossibleAbuserOfCasterLevel(final MOB casterM)
	{
		if(casterM.isPlayer())
			return true;
		final Room R=casterM.location();
		if(R==null)
			return false;
		if(casterM instanceof Deity)
			return false;
		final MOB folM=casterM.amUltimatelyFollowing();
		if((folM!=null)&&(folM.isPlayer()))
			return true;
		/* too much
		for(final Enumeration<MOB> m=R.inhabitants();m.hasMoreElements();)
		{
			final MOB M=m.nextElement();
			if((M!=null)
			&&(M!=casterM)
			&&(M.isPlayer())
			&&(casterM.willFollowOrdersOf(M)))
				return true;
		}
		*/
		return false;
	}

	protected int getBeneficialTickdownTime(final MOB mob, final Environmental target, final int tickAdjustmentFromStandard, final int asLevel)
	{
		if(tickAdjustmentFromStandard>0)
			return tickAdjustmentFromStandard;
		int casterLevel = adjustedLevel(mob,asLevel);
		if((mob != target)
		&&(target instanceof MOB)
		&&(aPossibleAbuserOfCasterLevel(mob)))
		{
			final int levelCap = ((MOB)target).phyStats().level() + CMProps.getIntVar(CMProps.Int.EXPRATE);
			if(casterLevel >  levelCap)
				casterLevel = levelCap;
		}
		final int beneficialTicks = (casterLevel*4)+40;
		final int tickTime = adjustBeneficialTickdownTime(mob,target,beneficialTicks);
		if(tickAdjustmentFromStandard==0)
			return tickTime;
		else
			return (int)Math.round(CMath.mul(CMath.div(-tickAdjustmentFromStandard, 100.0) , (double)tickTime));
	}

	public Ability beneficialAffect(final MOB mob, final Physical target, final int asLevel, int tickAdjustmentFromStandard)
	{
		invoker=mob;
		final Ability newOne=(Ability)this.copyOf();
		((StdAbility)newOne).canBeUninvoked=true;
		tickAdjustmentFromStandard=getBeneficialTickdownTime(mob,target,tickAdjustmentFromStandard,asLevel);
		newOne.startTickDown(invoker,target,tickAdjustmentFromStandard);
		return newOne;
	}

	protected void spreadImmunity(final MOB mob)
	{
		if((mob==null)||(mob.fetchEffect(ID())!=null))
			return;
		Ability A=mob.fetchEffect("TemporaryImmunity");
		if(A==null)
		{
			A=CMClass.getAbility("TemporaryImmunity");
			A.setSavable(false);
			A.makeLongLasting();
			mob.addEffect(A);
			A.makeLongLasting();
		}
		A.setMiscText("+"+ID());
	}

	@Override
	public boolean autoInvocation(final MOB mob, final boolean force)
	{
		if(isAutoInvoked())
		{
			if(!force)
			{
				final PlayerStats pStats = mob.playerStats();
				if((pStats != null) && (pStats.isOnAutoInvokeList(ID())))
					return false;
			}
			final Ability thisAbility=mob.fetchEffect(ID());
			if(thisAbility!=null)
				return false;
			final StdAbility thatAbility=(StdAbility)copyOf();
			thatAbility.canBeUninvoked=true;
			thatAbility.setSavable(false);
			thatAbility.setInvoker(mob);
			thatAbility.isAnAutoEffect=true;
			mob.addEffect(thatAbility);
			return true;
		}
		return false;
	}

	@Override
	public void makeNonUninvokable()
	{
		unInvoked=false;
		canBeUninvoked=false;
		savable=true;
	}

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

	public int getTickDownRemaining()
	{
		return tickDown;
	}

	public void setTickDownRemaining(final int newTick)
	{
		tickDown=newTick;
	}

	@Override
	public int getTickStatus()
	{
		return Tickable.STATUS_NOT;
	}

	@Override
	public boolean canBeTaughtBy(final MOB teacher, final MOB student)
	{
		if(teacher.isAttributeSet(MOB.Attrib.NOTEACH))
		{
			teacher.tell(L("You are refusing to teach right now."));
			student.tell(L("@x1 is refusing to teach right now.",teacher.name()));
			return false;
		}
		if(CMLib.flags().isSleeping(teacher)||CMLib.flags().isSitting(teacher))
		{
			teacher.tell(L("You need to stand up to teach."));
			student.tell(L("@x1 needs to stand up to teach.",teacher.name()));
			return false;
		}
		if(teacher.isInCombat())
		{
			student.tell(L("@x1 seems busy right now.",teacher.name()));
			teacher.tell(L("Not while you are fighting!"));
			return false;
		}
		final Ability yourAbility=teacher.fetchAbility(ID());
		if(yourAbility!=null)
		{
			final int prof25=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,yourAbility.ID()), .25));
			if(yourAbility.proficiency()<prof25)
			{
				teacher.tell(L("You are not proficient enough to teach '@x1'",name()));
				student.tell(L("@x1 is not proficient enough to teach '@x2'.",teacher.name(),name()));
				return false;
			}
			return true;
		}
		teacher.tell(L("You don't know '@x1'.",name()));
		student.tell(L("@x1 doesn't know '@x2'.",teacher.name(),name()));
		return false;
	}

	@Override
	public String requirements(final MOB mob)
	{
		final CostManager cost=getTrainingCost(mob);
		return cost.requirements(mob);
	}

	@Override
	public boolean canBeLearnedBy(final MOB teacher, final MOB student)
	{
		final CostManager cost=getTrainingCost(student);
		if(!cost.doesMeetCostRequirements(student))
		{
			final String ofWhat=cost.costType(student);
			if(teacher != null)
				teacher.tell(L("@x1 does not have enough @x2 to learn '@x3'.",student.name(),ofWhat,name()));
			student.tell(L("You do not have enough @x1.",ofWhat));
			return false;
		}
		if((student.isAttributeSet(MOB.Attrib.NOTEACH))
		&&((!student.isMonster())||(!student.willFollowOrdersOf(teacher))))
		{
			if(teacher != null)
				teacher.tell(L("@x1 is refusing training at this time.",student.name()));
			student.tell(L("You are refusing training at this time."));
			return false;
		}
		final int qLevel=CMLib.ableMapper().qualifyingLevel(student,this);
		if(qLevel<0)
		{
			if(teacher != null)
				teacher.tell(L("@x1 is not the right class to learn '@x2'.",student.name(),name()));
			student.tell(L("You are not the right class to learn '@x1'.",name()));
			return false;
		}
		if((!student.charStats().getCurrentClass().leveless())
		&&(!CMLib.ableMapper().qualifiesByLevel(student,this))
		&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.LEVELS)))
		{
			if(teacher != null)
				teacher.tell(L("@x1 is not high enough level to learn '@x2'.",student.name(),name()));
			student.tell(L("You are not high enough level to learn '@x1'.",name()));
			return false;
		}
		if(student.charStats().getStat(CharStats.STAT_INTELLIGENCE)<2)
		{
			if(teacher != null)
				teacher.tell(L("@x1 is too stupid to learn new skills.",student.name()));
			student.tell(L("You are too stupid to learn new skills."));
			return false;
		}

		final String qualClassID = CMLib.ableMapper().qualifyingID(student, this);
		if((qualClassID != null) && (qualClassID.equalsIgnoreCase("All")||(CMClass.getCharClass(qualClassID) != null)))
		{
			int highestSkillLevel=-1;
			for(final AbilityMapping mA : CMLib.ableMapper().getAbleMapping(qualClassID).values())
			{
				if(mA.qualLevel() > highestSkillLevel)
					highestSkillLevel = mA.qualLevel();
			}
			if(highestSkillLevel > 0)
			{
				final int baseInt = CMProps.getIntVar(CMProps.Int.BASEMAXSTAT); // 18
				final int normalizedReqInt = (int)Math.round(Math.ceil(CMath.mul(baseInt, CMath.div(qLevel, highestSkillLevel))));
				final int studentInt=student.charStats().getStat(CharStats.STAT_INTELLIGENCE);
				if(studentInt + 6 < normalizedReqInt)
				{
					if(teacher != null)
						teacher.tell(L("@x1 is not smart enough to learn level @x2 skills.",student.name(),qLevel+""));
					student.tell(L("You are not of high enough intelligence to learn level @x1 skills.",qLevel+""));
					return false;
				}
			}
		}

		final Ability yourAbility=student.fetchAbility(ID());
		final Ability teacherAbility=(teacher != null) ? teacher.fetchAbility(ID()) : null;
		if(yourAbility!=null)
		{
			if(teacher != null)
				teacher.tell(L("@x1 already knows '@x2'.",student.name(),name()));
			student.tell(L("You already know '@x1'.",name()));
			return false;
		}

		if(teacher != null)
		{
			if(teacherAbility!=null)
			{
				final int prof25=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,teacherAbility.ID()), .25));
				if(teacherAbility.proficiency()<prof25)
				{
					teacher.tell(L("You aren't proficient enough to teach '@x1'.",name()));
					student.tell(L("@x1 isn't proficient enough to teach you '@x2'.",teacher.name(),name()));
					return false;
				}
			}
			else
			{
				student.tell(L("@x1 does not know anything about that.",teacher.name()));
				teacher.tell(L("You don't know that."));
				return false;
			}
		}
		if(student.isInCombat())
		{
			if(teacher != null)
				teacher.tell(L("@x1 seems busy right now.",student.name()));
			student.tell(L("Not while you are fighting!"));
			return false;
		}

		if(CMLib.flags().isSleeping(student)||CMLib.flags().isSitting(student))
		{
			student.tell(L("You need to stand up and be alert to learn."));
			if(teacher != null)
				teacher.tell(L("@x1 needs to stand up to be taught about that.",student.name()));
			return false;
		}

		final String extraMask=CMLib.ableMapper().getApplicableMask(student,this);
		if((extraMask.length()>0)&&(!CMLib.masking().maskCheck(extraMask,student,true)))
		{
			final String reason="requirements: "+CMLib.masking().maskDesc(extraMask);
			student.tell(L("You may not learn '@x1' at this time due to the @x2",name(),reason));
			if(teacher != null)
				teacher.tell(L("@x1 does not fit the '@x2' @x3",student.name(),name(),reason));
			return false;
		}

		final DVector prereqs=CMLib.ableMapper().getUnmetPreRequisites(student,this);
		if((prereqs!=null)&&(prereqs.size()>0))
		{
			final String names=CMLib.ableMapper().formatPreRequisites(prereqs);
			student.tell(L("You must learn @x1 before you can gain @x2.",names,name()));
			if(teacher != null)
				teacher.tell(L("@x1 has not learned the pre-requisites to @x2 yet.",student.name(),name()));
			return false;
		}

		return true;
	}

	protected Map<MOB,MOB> saveCombatState(final MOB mob, final boolean andFollowers)
	{
		final Map<MOB,MOB> map = new TreeMap<MOB,MOB>();
		final HashSet<MOB> fols = new HashSet<MOB>();
		fols.add(mob);
		if(andFollowers)
			mob.getGroupMembers(fols);
		for(final MOB M : fols)
			map.put(M,M.getVictim());
		return map;
	}

	protected void restoreCombatState(final Map<MOB,MOB> map)
	{
		for(final MOB M : map.keySet())
		{
			M.setVictim(map.get(M));
		}
	}

	protected int verbalCastCode(final MOB mob, final Physical target, final boolean auto)
	{
		int affectType=CMMsg.MSG_CAST_VERBAL_SPELL;
		if(castingQuality(mob,target)==Ability.QUALITY_MALICIOUS)
			affectType=CMMsg.MSG_CAST_ATTACK_VERBAL_SPELL;
		if(auto)
			affectType=affectType|CMMsg.MASK_ALWAYS;
		return affectType;
	}

	protected int verbalSpeakCode(final MOB mob, final Physical target, final boolean auto)
	{
		int affectType=CMMsg.MSG_NOISE|CMMsg.MASK_MOUTH;
		if(castingQuality(mob,target)==Ability.QUALITY_MALICIOUS)
			affectType=CMMsg.MSG_NOISE|CMMsg.MASK_MOUTH|CMMsg.MASK_MALICIOUS;
		if(auto)
			affectType=affectType|CMMsg.MASK_ALWAYS;
		return affectType;
	}

	protected int verbalCastMask(final MOB mob,final Physical target, final boolean auto)
	{
		return verbalCastCode(mob,target,auto)&CMMsg.MAJOR_MASK;
	}

	protected int somanticCastCode(final MOB mob, final Physical target, final boolean auto)
	{
		int affectType=CMMsg.MSG_CAST_SOMANTIC_SPELL;
		if(castingQuality(mob,target)==Ability.QUALITY_MALICIOUS)
			affectType=CMMsg.MSG_CAST_ATTACK_SOMANTIC_SPELL;
		if(auto)
			affectType=affectType|CMMsg.MASK_ALWAYS;
		return affectType;
	}

	protected int somanticCastMask(final MOB mob, final Physical target, final boolean auto)
	{
		return somanticCastCode(mob,target,auto)&CMMsg.MAJOR_MASK;
	}

	@Override
	public boolean canBePracticedBy(final MOB teacher, final MOB student)
	{
		if((practicesToPractice(student)>0)&&(student.getPractices()<practicesToPractice(student)))
		{
			teacher.tell(L("@x1 does not have enough practices to practice '@x2'.",student.name(),name()));
			student.tell(L("You do not have enough practices."));
			return false;
		}

		if(teacher.isAttributeSet(MOB.Attrib.NOTEACH))
		{
			teacher.tell(L("You are refusing to teach right now."));
			student.tell(L("@x1 is refusing to teach right now.",teacher.name()));
			return false;
		}
		if((student.isAttributeSet(MOB.Attrib.NOTEACH))
		&&((!student.isMonster())||(!student.willFollowOrdersOf(teacher))))
		{
			teacher.tell(L("@x1 is refusing training at this time.",student.name()));
			student.tell(L("You are refusing training at this time."));
			return false;
		}

		final Ability yourAbility=student.fetchAbility(ID());
		final Ability teacherAbility=teacher.fetchAbility(ID());
		if(yourAbility==null)
		{
			teacher.tell(L("@x1 has not gained '@x2' yet.",student.name(),name()));
			student.tell(L("You havn't gained '@x1' yet.",name()));
			return false;
		}

		if(teacherAbility==null)
		{
			student.tell(L("@x1 does not know anything about '@x2'.",teacher.name(),name()));
			teacher.tell(L("You don't know '@x1'.",name()));
			return false;
		}

		if(yourAbility.proficiency()>=teacherAbility.proficiency())
		{
			teacher.tell(L("You aren't proficient enough to teach any more about '@x1'.",name()));
			student.tell(L("@x1 isn't proficient enough to teach any more about '@x2'.",teacher.name(),name()));
			return false;
		}
		else
		{
			final int prof75=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,yourAbility.ID()), .75));
			if(yourAbility.proficiency()>prof75-1)
			{
				teacher.tell(L("You can't teach @x1 any more about '@x2'.",student.charStats().himher(),name()));
				student.tell(L("You can't learn any more about '@x1' except through dilligence.",name()));
				return false;
			}
		}

		final int prof25=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,teacherAbility.ID()), .25));
		if(teacherAbility.proficiency()<prof25)
		{
			teacher.tell(L("You aren't proficient enough to teach '@x1'.",name()));
			student.tell(L("@x1 isn't proficient enough to teach you '@x2'.",teacher.name(),name()));
			return false;
		}
		if(CMLib.flags().isSleeping(student)||CMLib.flags().isSitting(student))
		{
			student.tell(L("You need to stand up to practice."));
			teacher.tell(L("@x1 needs to stand up to practice that.",student.name()));
			return false;
		}
		if(student.isInCombat())
		{
			teacher.tell(L("@x1 seems busy right now.",student.name()));
			student.tell(L("Not while you are fighting!"));
			return false;
		}

		return true;
	}

	@Override
	public void teach(final MOB teacher, final MOB student)
	{
		if(student.fetchAbility(ID())==null)
		{
			final CostManager cost=getTrainingCost(student);
			if(!cost.doesMeetCostRequirements(student))
				return;
			cost.doSpend(student);
			final Ability newAbility=(Ability)newInstance();
			final int prof75=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,newAbility.ID()), .75));
			newAbility.setProficiency((int)Math.round(CMath.mul(proficiency(),((CMath.div(teacher.charStats().getStat(CharStats.STAT_WISDOM)+student.charStats().getStat(CharStats.STAT_INTELLIGENCE),100.0))))));
			if(newAbility.proficiency()>prof75)
				newAbility.setProficiency(prof75);
			final int defProficiency=CMLib.ableMapper().getDefaultProficiency(student.charStats().getCurrentClass().ID(),true,ID());
			if((defProficiency>0)&&(defProficiency>newAbility.proficiency()))
				newAbility.setProficiency(defProficiency);
			final String defParms=CMLib.ableMapper().getDefaultParm(student.charStats().getCurrentClass().ID(), true, ID());
			newAbility.setMiscText(defParms);
			student.addAbility(newAbility);
			newAbility.autoInvocation(student, false);
		}
		student.recoverCharStats();
		student.recoverPhyStats();
		student.recoverMaxState();
	}

	@Override
	public void unlearn(final MOB student)
	{
		if(student == null)
			return;
		final Ability A=student.fetchAbility(ID());
		if(A==this)
			student.delAbility(this);
		final Ability eA=student.fetchEffect(ID());
		if(eA!=null)
		{
			eA.unInvoke();
			student.delEffect(eA);
		}
	}

	@Override
	public void practice(final MOB teacher, final MOB student)
	{
		if(student.getPractices()<practicesToPractice(student))
			return;

		final Ability yourAbility=student.fetchAbility(ID());
		if(yourAbility!=null)
		{
			final Ability teachAbility=teacher.fetchAbility(ID());
			final int prof75=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,yourAbility.ID()), .75));
			if(yourAbility.proficiency()<prof75)
			{
				student.setPractices(student.getPractices()-practicesToPractice(student));
				final int wisint = teacher.charStats().getStat(CharStats.STAT_WISDOM)
								 + student.charStats().getStat(CharStats.STAT_INTELLIGENCE);
				int newProf = yourAbility.proficiency()+(int)Math.round(25.0*(CMath.div(wisint,36.0)));
				if(newProf > prof75)
					newProf=prof75;
				if((teachAbility!=null)&&(newProf > teachAbility.proficiency()))
					newProf = teachAbility.proficiency();
				yourAbility.setProficiency(newProf);
				final Ability yourEffect=student.fetchEffect(ID());
				if((yourEffect!=null)&&(yourEffect.invoker()==student))
					yourEffect.setProficiency(yourAbility.proficiency());
			}
		}
	}

	@Override
	public void makeLongLasting()
	{
		tickDown=Integer.MAX_VALUE;
	}

	@Override
	public void executeMsg(final Environmental myHost, final CMMsg msg)
	{
		return;
	}

	@Override
	public boolean okMessage(final Environmental myHost, final CMMsg msg)
	{
		return true;
	}

	@Override
	public boolean tick(final Tickable ticking, final int tickID)
	{
		if((unInvoked)&&(canBeUninvoked()))
			return false;

		if((canBeUninvoked())
		&&(tickID==Tickable.TICKID_MOB)
		&&(tickDown!=Integer.MAX_VALUE))
		{
			if(tickDown<0)
				return !unInvoked;
			if((--tickDown)<=0)
			{
				tickDown=-1;
				unInvoke();
				return false;
			}
		}
		return true;
	}

	@Override
	public boolean appropriateToMyFactions(final MOB mob)
	{
		if(mob == null)
			return true;
		for(final Enumeration<String> e=mob.factions();e.hasMoreElements();)
		{
			final String factionID=e.nextElement();
			final Faction F=CMLib.factions().getFaction(factionID);
			if((F!=null)&&F.hasUsage(this))
				return F.canUse(mob,this);
		}
		return true;
	}

	@Override
	public boolean isGeneric()
	{
		return false;
	}

	@Override
	public int getSaveStatIndex()
	{
		return getStatCodes().length;
	}

	private static final String[]	CODES			= { "CLASS", "TEXT" };
	private static final String[]	INTERNAL_CODES	= { "TICKDOWN","LEVEL","ISANAUTOEFFECT","NAME","NEXTCAST","CANUNINVOKE" };

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

	@Override
	public boolean isStat(final String code)
	{
		return CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0;
	}

	protected int getCodeNum(final String code)
	{
		for(int i=0;i<CODES.length;i++)
		{
			if(code.equalsIgnoreCase(CODES[i]))
				return i;
		}
		return -1;
	}

	protected int getInternalCodeNum(final String code)
	{
		for(int i=0;i<INTERNAL_CODES.length;i++)
		{
			if(code.equalsIgnoreCase(INTERNAL_CODES[i]))
				return i;
		}
		return -1;
	}

	@Override
	public String getStat(final String code)
	{
		switch(getCodeNum(code))
		{
		case 0:
			return ID();
		case 1:
			return text();
		default:
			switch(getInternalCodeNum(code))
			{
			case 0:
				return Integer.toString(tickDown);
			case 1:
				return "0";
			case 2:
				return Boolean.toString(isAnAutoEffect);
			case 3:
				return name();
			case 4:
				return ""+this.getTimeOfNextCast();
			case 5:
				return ""+this.canBeUninvoked();
			default:
				break;
			}
			break;
		}
		return "";
	}

	@Override
	public void setStat(final String code, final String val)
	{
		switch(getCodeNum(code))
		{
		case 0:
			return;
		case 1:
			setMiscText(val);
			break;
		default:
			switch(getInternalCodeNum(code))
			{
			case 0:
				tickDown = CMath.s_int(val);
				break;
			case 1:
				break;
			case 2:
				isAnAutoEffect = CMath.s_bool(val);
				break;
			case 3:
				break;
			case 4:
				if(CMath.isLong(val))
					setTimeOfNextCast(CMath.s_long(val));
				break;
			case 5:
				this.canBeUninvoked = CMath.s_bool(val);
				this.unInvoked=false;
				break;
			default:
				break;
			}
			break;
		}
	}

	@Override
	public boolean sameAs(final Environmental E)
	{
		if(!(E instanceof StdAbility))
			return false;
		final String[] codes=getStatCodes();
		for(int i=0;i<codes.length;i++)
		{
			if(!E.getStat(codes[i]).equals(getStat(codes[i])))
				return false;
		}
		return true;
	}
}

<MSG> Added the ability to get reasonable mana costs w/o a mob.

git-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@20528 0d6f1817-ed0e-0410-87c9-987e46238f29

<DFF> @@ -1360,21 +1360,29 @@ public class StdAbility implements Ability
 			{
 				int maxAmt;
 				int baseAmt;
-				switch(costType)
+				if(mob == null)
 				{
-				case 1:
-					maxAmt=mob.maxState().getMovement();
-					baseAmt=mob.baseState().getMovement();
-					break;
-				case 2:
-					maxAmt=mob.maxState().getHitPoints();
-					baseAmt=mob.baseState().getHitPoints();
-					break;
-				default:
-				case 0:
-					maxAmt=mob.maxState().getMana();
-					baseAmt=mob.baseState().getMana();
-					break;
+					maxAmt=1000;
+					baseAmt=1000;
+				}
+				else
+				{
+					switch(costType)
+					{
+					case 1:
+						maxAmt=mob.maxState().getMovement();
+						baseAmt=mob.baseState().getMovement();
+						break;
+					case 2:
+						maxAmt=mob.maxState().getHitPoints();
+						baseAmt=mob.baseState().getHitPoints();
+						break;
+					default:
+					case 0:
+						maxAmt=mob.maxState().getMana();
+						baseAmt=mob.baseState().getMana();
+						break;
+					}
 				}
 				int costDown=0;
 				final int rawCost;
@@ -1385,7 +1393,7 @@ public class StdAbility implements Ability
 					rawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));
 				else
 					rawCost = consumed / divider;
-				if(rawCost>2)
+				if((rawCost>2)&&(mob!=null))
 				{
 					costDown=getXLOWCOSTLevel(mob);
 					final int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));
@@ -1425,21 +1433,17 @@ public class StdAbility implements Ability
 	@Override
 	public int[] usageCost(final MOB mob, final boolean ignoreClassOverride)
 	{
+		final Map<String,int[]> overrideCache=getHardOverrideManaCache();
 		if(mob==null)
 		{
-			final Map<String,int[]> overrideCache=getHardOverrideManaCache();
-			if(!overrideCache.containsKey(ID()))
-			{
-				final int[] usage=new int[Ability.USAGEINDEX_TOTAL];
-				Arrays.fill(usage,overrideMana());
-				overrideCache.put(ID(), usage);
-			}
-			return overrideCache.get(ID());
+			if(overrideCache.containsKey(ID()))
+				return overrideCache.get(ID());
+
 		}
 		if(usageType()==Ability.USAGE_NADA)
 			return STATIC_USAGE_NADA;
 
-		final int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());
+		final int[][] abilityUsageCache=(mob==null)?new int[Ability.CACHEINDEX_TOTAL][]:mob.getAbilityUsageCache(ID());
 		final int myCacheIndex=ignoreClassOverride?Ability.CACHEINDEX_CLASSLESS:Ability.CACHEINDEX_NORMAL;
 		final int[] myCache=abilityUsageCache[myCacheIndex];
 		final boolean rebuildCache=(myCache==null);
@@ -1456,16 +1460,19 @@ public class StdAbility implements Ability
 		{
 			int diff=0;
 			int lowest=Integer.MAX_VALUE;
-			for(int c=0;c<mob.charStats().numClasses();c++)
+			if(mob != null)
 			{
-				final CharClass C=mob.charStats().getMyClass(c);
-				final int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());
-				final int classLevel=mob.charStats().getClassLevel(C.ID());
-				if((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))
+				for(int c=0;c<mob.charStats().numClasses();c++)
 				{
-					diff+=(classLevel-qualifyingLevel);
-					if(qualifyingLevel<lowest)
-						lowest=qualifyingLevel;
+					final CharClass C=mob.charStats().getMyClass(c);
+					final int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());
+					final int classLevel=mob.charStats().getClassLevel(C.ID());
+					if((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))
+					{
+						diff+=(classLevel-qualifyingLevel);
+						if(qualifyingLevel<lowest)
+							lowest=qualifyingLevel;
+					}
 				}
 			}
 			if(lowest==Integer.MAX_VALUE)
@@ -1476,7 +1483,7 @@ public class StdAbility implements Ability
 			}
 
 			Integer[] costOverrides=null;
-			if(!ignoreClassOverride)
+			if((!ignoreClassOverride)&&(mob!=null))
 				costOverrides=CMLib.ableMapper().getCostOverrides(mob,ID());
 			minimum=CMProps.getMinManaException(ID());
 			if(minimum==Integer.MIN_VALUE)
@@ -1495,9 +1502,9 @@ public class StdAbility implements Ability
 			{
 				final double[] vars = new double[] {
 					lowest,
-					mob.phyStats().level(),
+					(mob==null)?lowest:mob.phyStats().level(),
 					minimum,
-					adjustedLevel(mob,0),
+					(mob==null)?lowest:adjustedLevel(mob,0),
 					diff
 				};
 				consumed=(int)CMath.parseMathExpression((CMath.CompiledFormula)cost, vars, 0.0);
@@ -1528,6 +1535,8 @@ public class StdAbility implements Ability
 			else
 				abilityUsageCache[myCacheIndex]=usageCost;
 		}
+		if(mob == null)
+			overrideCache.put(ID(), usageCost);
 		return usageCost;
 	}
 

{'commit': 'diff --git a/com/planet_ink/coffee_mud/Abilities/StdAbility.java b/com/planet_ink/coffee_mud/Abilities/StdAbility.java\nindex ce3bb42096..45e455f2fb 100644\n--- a/com/planet_ink/coffee_mud/Abilities/StdAbility.java\n+++ b/com/planet_ink/coffee_mud/Abilities/StdAbility.java\n@@ -1360,21 +1360,29 @@ public class StdAbility implements Ability\n \t\t\t{\r\n \t\t\t\tint maxAmt;\r\n \t\t\t\tint baseAmt;\r\n-\t\t\t\tswitch(costType)\r\n+\t\t\t\tif(mob == null)\r\n \t\t\t\t{\r\n-\t\t\t\tcase 1:\r\n-\t\t\t\t\tmaxAmt=mob.maxState().getMovement();\r\n-\t\t\t\t\tbaseAmt=mob.baseState().getMovement();\r\n-\t\t\t\t\tbreak;\r\n-\t\t\t\tcase 2:\r\n-\t\t\t\t\tmaxAmt=mob.maxState().getHitPoints();\r\n-\t\t\t\t\tbaseAmt=mob.baseState().getHitPoints();\r\n-\t\t\t\t\tbreak;\r\n-\t\t\t\tdefault:\r\n-\t\t\t\tcase 0:\r\n-\t\t\t\t\tmaxAmt=mob.maxState().getMana();\r\n-\t\t\t\t\tbaseAmt=mob.baseState().getMana();\r\n-\t\t\t\t\tbreak;\r\n+\t\t\t\t\tmaxAmt=1000;\r\n+\t\t\t\t\tbaseAmt=1000;\r\n+\t\t\t\t}\r\n+\t\t\t\telse\r\n+\t\t\t\t{\r\n+\t\t\t\t\tswitch(costType)\r\n+\t\t\t\t\t{\r\n+\t\t\t\t\tcase 1:\r\n+\t\t\t\t\t\tmaxAmt=mob.maxState().getMovement();\r\n+\t\t\t\t\t\tbaseAmt=mob.baseState().getMovement();\r\n+\t\t\t\t\t\tbreak;\r\n+\t\t\t\t\tcase 2:\r\n+\t\t\t\t\t\tmaxAmt=mob.maxState().getHitPoints();\r\n+\t\t\t\t\t\tbaseAmt=mob.baseState().getHitPoints();\r\n+\t\t\t\t\t\tbreak;\r\n+\t\t\t\t\tdefault:\r\n+\t\t\t\t\tcase 0:\r\n+\t\t\t\t\t\tmaxAmt=mob.maxState().getMana();\r\n+\t\t\t\t\t\tbaseAmt=mob.baseState().getMana();\r\n+\t\t\t\t\t\tbreak;\r\n+\t\t\t\t\t}\r\n \t\t\t\t}\r\n \t\t\t\tint costDown=0;\r\n \t\t\t\tfinal int rawCost;\r\n@@ -1385,7 +1393,7 @@ public class StdAbility implements Ability\n \t\t\t\t\trawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));\r\n \t\t\t\telse\r\n \t\t\t\t\trawCost = consumed / divider;\r\n-\t\t\t\tif(rawCost>2)\r\n+\t\t\t\tif((rawCost>2)&&(mob!=null))\r\n \t\t\t\t{\r\n \t\t\t\t\tcostDown=getXLOWCOSTLevel(mob);\r\n \t\t\t\t\tfinal int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));\r\n@@ -1425,21 +1433,17 @@ public class StdAbility implements Ability\n \t@Override\r\n \tpublic int[] usageCost(final MOB mob, final boolean ignoreClassOverride)\r\n \t{\r\n+\t\tfinal Map<String,int[]> overrideCache=getHardOverrideManaCache();\r\n \t\tif(mob==null)\r\n \t\t{\r\n-\t\t\tfinal Map<String,int[]> overrideCache=getHardOverrideManaCache();\r\n-\t\t\tif(!overrideCache.containsKey(ID()))\r\n-\t\t\t{\r\n-\t\t\t\tfinal int[] usage=new int[Ability.USAGEINDEX_TOTAL];\r\n-\t\t\t\tArrays.fill(usage,overrideMana());\r\n-\t\t\t\toverrideCache.put(ID(), usage);\r\n-\t\t\t}\r\n-\t\t\treturn overrideCache.get(ID());\r\n+\t\t\tif(overrideCache.containsKey(ID()))\r\n+\t\t\t\treturn overrideCache.get(ID());\r\n+\r\n \t\t}\r\n \t\tif(usageType()==Ability.USAGE_NADA)\r\n \t\t\treturn STATIC_USAGE_NADA;\r\n \r\n-\t\tfinal int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());\r\n+\t\tfinal int[][] abilityUsageCache=(mob==null)?new int[Ability.CACHEINDEX_TOTAL][]:mob.getAbilityUsageCache(ID());\r\n \t\tfinal int myCacheIndex=ignoreClassOverride?Ability.CACHEINDEX_CLASSLESS:Ability.CACHEINDEX_NORMAL;\r\n \t\tfinal int[] myCache=abilityUsageCache[myCacheIndex];\r\n \t\tfinal boolean rebuildCache=(myCache==null);\r\n@@ -1456,16 +1460,19 @@ public class StdAbility implements Ability\n \t\t{\r\n \t\t\tint diff=0;\r\n \t\t\tint lowest=Integer.MAX_VALUE;\r\n-\t\t\tfor(int c=0;c<mob.charStats().numClasses();c++)\r\n+\t\t\tif(mob != null)\r\n \t\t\t{\r\n-\t\t\t\tfinal CharClass C=mob.charStats().getMyClass(c);\r\n-\t\t\t\tfinal int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());\r\n-\t\t\t\tfinal int classLevel=mob.charStats().getClassLevel(C.ID());\r\n-\t\t\t\tif((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))\r\n+\t\t\t\tfor(int c=0;c<mob.charStats().numClasses();c++)\r\n \t\t\t\t{\r\n-\t\t\t\t\tdiff+=(classLevel-qualifyingLevel);\r\n-\t\t\t\t\tif(qualifyingLevel<lowest)\r\n-\t\t\t\t\t\tlowest=qualifyingLevel;\r\n+\t\t\t\t\tfinal CharClass C=mob.charStats().getMyClass(c);\r\n+\t\t\t\t\tfinal int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());\r\n+\t\t\t\t\tfinal int classLevel=mob.charStats().getClassLevel(C.ID());\r\n+\t\t\t\t\tif((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))\r\n+\t\t\t\t\t{\r\n+\t\t\t\t\t\tdiff+=(classLevel-qualifyingLevel);\r\n+\t\t\t\t\t\tif(qualifyingLevel<lowest)\r\n+\t\t\t\t\t\t\tlowest=qualifyingLevel;\r\n+\t\t\t\t\t}\r\n \t\t\t\t}\r\n \t\t\t}\r\n \t\t\tif(lowest==Integer.MAX_VALUE)\r\n@@ -1476,7 +1483,7 @@ public class StdAbility implements Ability\n \t\t\t}\r\n \r\n \t\t\tInteger[] costOverrides=null;\r\n-\t\t\tif(!ignoreClassOverride)\r\n+\t\t\tif((!ignoreClassOverride)&&(mob!=null))\r\n \t\t\t\tcostOverrides=CMLib.ableMapper().getCostOverrides(mob,ID());\r\n \t\t\tminimum=CMProps.getMinManaException(ID());\r\n \t\t\tif(minimum==Integer.MIN_VALUE)\r\n@@ -1495,9 +1502,9 @@ public class StdAbility implements Ability\n \t\t\t{\r\n \t\t\t\tfinal double[] vars = new double[] {\r\n \t\t\t\t\tlowest,\r\n-\t\t\t\t\tmob.phyStats().level(),\r\n+\t\t\t\t\t(mob==null)?lowest:mob.phyStats().level(),\r\n \t\t\t\t\tminimum,\r\n-\t\t\t\t\tadjustedLevel(mob,0),\r\n+\t\t\t\t\t(mob==null)?lowest:adjustedLevel(mob,0),\r\n \t\t\t\t\tdiff\r\n \t\t\t\t};\r\n \t\t\t\tconsumed=(int)CMath.parseMathExpression((CMath.CompiledFormula)cost, vars, 0.0);\r\n@@ -1528,6 +1535,8 @@ public class StdAbility implements Ability\n \t\t\telse\r\n \t\t\t\tabilityUsageCache[myCacheIndex]=usageCost;\r\n \t\t}\r\n+\t\tif(mob == null)\r\n+\t\t\toverrideCache.put(ID(), usageCost);\r\n \t\treturn usageCost;\r\n \t}\r\n \r\n', 'commit_subject': 'Added the ability to get reasonable mana costs w/o a mob.', 'repo_name': 'bozimmerman/CoffeeMud', 'committer': {'name': 'bozimmerman', 'email': 'dc45fe023482b3b890b5e574e53c1d051f45b1ef@zimmers.net', 'time_sec': '1614110085', 'tz_offset': '0', 'date': {'seconds': '1614110085'}}, 'commit_tree': '176657dc61efaf34581722c6b0e1e0266c1ec8d3', 'commit_message': 'Added the ability to get reasonable mana costs w/o a mob.\n\ngit-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@20528 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'language_name': 'JavaScript', 'license': 'apache-2.0', 'hunks': '@@ -1360,21 +1360,29 @@ public class StdAbility implements Ability\n \t\t\t{\r\n \t\t\t\tint maxAmt;\r\n \t\t\t\tint baseAmt;\r\n-\t\t\t\tswitch(costType)\r\n+\t\t\t\tif(mob == null)\r\n \t\t\t\t{\r\n-\t\t\t\tcase 1:\r\n-\t\t\t\t\tmaxAmt=mob.maxState().getMovement();\r\n-\t\t\t\t\tbaseAmt=mob.baseState().getMovement();\r\n-\t\t\t\t\tbreak;\r\n-\t\t\t\tcase 2:\r\n-\t\t\t\t\tmaxAmt=mob.maxState().getHitPoints();\r\n-\t\t\t\t\tbaseAmt=mob.baseState().getHitPoints();\r\n-\t\t\t\t\tbreak;\r\n-\t\t\t\tdefault:\r\n-\t\t\t\tcase 0:\r\n-\t\t\t\t\tmaxAmt=mob.maxState().getMana();\r\n-\t\t\t\t\tbaseAmt=mob.baseState().getMana();\r\n-\t\t\t\t\tbreak;\r\n+\t\t\t\t\tmaxAmt=1000;\r\n+\t\t\t\t\tbaseAmt=1000;\r\n+\t\t\t\t}\r\n+\t\t\t\telse\r\n+\t\t\t\t{\r\n+\t\t\t\t\tswitch(costType)\r\n+\t\t\t\t\t{\r\n+\t\t\t\t\tcase 1:\r\n+\t\t\t\t\t\tmaxAmt=mob.maxState().getMovement();\r\n+\t\t\t\t\t\tbaseAmt=mob.baseState().getMovement();\r\n+\t\t\t\t\t\tbreak;\r\n+\t\t\t\t\tcase 2:\r\n+\t\t\t\t\t\tmaxAmt=mob.maxState().getHitPoints();\r\n+\t\t\t\t\t\tbaseAmt=mob.baseState().getHitPoints();\r\n+\t\t\t\t\t\tbreak;\r\n+\t\t\t\t\tdefault:\r\n+\t\t\t\t\tcase 0:\r\n+\t\t\t\t\t\tmaxAmt=mob.maxState().getMana();\r\n+\t\t\t\t\t\tbaseAmt=mob.baseState().getMana();\r\n+\t\t\t\t\t\tbreak;\r\n+\t\t\t\t\t}\r\n \t\t\t\t}\r\n \t\t\t\tint costDown=0;\r\n \t\t\t\tfinal int rawCost;\r\n@@ -1385,7 +1393,7 @@ public class StdAbility implements Ability\n \t\t\t\t\trawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));\r\n \t\t\t\telse\r\n \t\t\t\t\trawCost = consumed / divider;\r\n-\t\t\t\tif(rawCost>2)\r\n+\t\t\t\tif((rawCost>2)&&(mob!=null))\r\n \t\t\t\t{\r\n \t\t\t\t\tcostDown=getXLOWCOSTLevel(mob);\r\n \t\t\t\t\tfinal int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));\r\n@@ -1425,21 +1433,17 @@ public class StdAbility implements Ability\n \t@Override\r\n \tpublic int[] usageCost(final MOB mob, final boolean ignoreClassOverride)\r\n \t{\r\n+\t\tfinal Map<String,int[]> overrideCache=getHardOverrideManaCache();\r\n \t\tif(mob==null)\r\n \t\t{\r\n-\t\t\tfinal Map<String,int[]> overrideCache=getHardOverrideManaCache();\r\n-\t\t\tif(!overrideCache.containsKey(ID()))\r\n-\t\t\t{\r\n-\t\t\t\tfinal int[] usage=new int[Ability.USAGEINDEX_TOTAL];\r\n-\t\t\t\tArrays.fill(usage,overrideMana());\r\n-\t\t\t\toverrideCache.put(ID(), usage);\r\n-\t\t\t}\r\n-\t\t\treturn overrideCache.get(ID());\r\n+\t\t\tif(overrideCache.containsKey(ID()))\r\n+\t\t\t\treturn overrideCache.get(ID());\r\n+\r\n \t\t}\r\n \t\tif(usageType()==Ability.USAGE_NADA)\r\n \t\t\treturn STATIC_USAGE_NADA;\r\n \r\n-\t\tfinal int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());\r\n+\t\tfinal int[][] abilityUsageCache=(mob==null)?new int[Ability.CACHEINDEX_TOTAL][]:mob.getAbilityUsageCache(ID());\r\n \t\tfinal int myCacheIndex=ignoreClassOverride?Ability.CACHEINDEX_CLASSLESS:Ability.CACHEINDEX_NORMAL;\r\n \t\tfinal int[] myCache=abilityUsageCache[myCacheIndex];\r\n \t\tfinal boolean rebuildCache=(myCache==null);\r\n@@ -1456,16 +1460,19 @@ public class StdAbility implements Ability\n \t\t{\r\n \t\t\tint diff=0;\r\n \t\t\tint lowest=Integer.MAX_VALUE;\r\n-\t\t\tfor(int c=0;c<mob.charStats().numClasses();c++)\r\n+\t\t\tif(mob != null)\r\n \t\t\t{\r\n-\t\t\t\tfinal CharClass C=mob.charStats().getMyClass(c);\r\n-\t\t\t\tfinal int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());\r\n-\t\t\t\tfinal int classLevel=mob.charStats().getClassLevel(C.ID());\r\n-\t\t\t\tif((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))\r\n+\t\t\t\tfor(int c=0;c<mob.charStats().numClasses();c++)\r\n \t\t\t\t{\r\n-\t\t\t\t\tdiff+=(classLevel-qualifyingLevel);\r\n-\t\t\t\t\tif(qualifyingLevel<lowest)\r\n-\t\t\t\t\t\tlowest=qualifyingLevel;\r\n+\t\t\t\t\tfinal CharClass C=mob.charStats().getMyClass(c);\r\n+\t\t\t\t\tfinal int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());\r\n+\t\t\t\t\tfinal int classLevel=mob.charStats().getClassLevel(C.ID());\r\n+\t\t\t\t\tif((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))\r\n+\t\t\t\t\t{\r\n+\t\t\t\t\t\tdiff+=(classLevel-qualifyingLevel);\r\n+\t\t\t\t\t\tif(qualifyingLevel<lowest)\r\n+\t\t\t\t\t\t\tlowest=qualifyingLevel;\r\n+\t\t\t\t\t}\r\n \t\t\t\t}\r\n \t\t\t}\r\n \t\t\tif(lowest==Integer.MAX_VALUE)\r\n@@ -1476,7 +1483,7 @@ public class StdAbility implements Ability\n \t\t\t}\r\n \r\n \t\t\tInteger[] costOverrides=null;\r\n-\t\t\tif(!ignoreClassOverride)\r\n+\t\t\tif((!ignoreClassOverride)&&(mob!=null))\r\n \t\t\t\tcostOverrides=CMLib.ableMapper().getCostOverrides(mob,ID());\r\n \t\t\tminimum=CMProps.getMinManaException(ID());\r\n \t\t\tif(minimum==Integer.MIN_VALUE)\r\n@@ -1495,9 +1502,9 @@ public class StdAbility implements Ability\n \t\t\t{\r\n \t\t\t\tfinal double[] vars = new double[] {\r\n \t\t\t\t\tlowest,\r\n-\t\t\t\t\tmob.phyStats().level(),\r\n+\t\t\t\t\t(mob==null)?lowest:mob.phyStats().level(),\r\n \t\t\t\t\tminimum,\r\n-\t\t\t\t\tadjustedLevel(mob,0),\r\n+\t\t\t\t\t(mob==null)?lowest:adjustedLevel(mob,0),\r\n \t\t\t\t\tdiff\r\n \t\t\t\t};\r\n \t\t\t\tconsumed=(int)CMath.parseMathExpression((CMath.CompiledFormula)cost, vars, 0.0);\r\n@@ -1528,6 +1535,8 @@ public class StdAbility implements Ability\n \t\t\telse\r\n \t\t\t\tabilityUsageCache[myCacheIndex]=usageCost;\r\n \t\t}\r\n+\t\tif(mob == null)\r\n+\t\t\toverrideCache.put(ID(), usageCost);\r\n \t\treturn usageCost;\r\n \t}\r\n \r\n', 'addition_count': 44, 'deletion_count': 35, 'src_file': 'a/com/planet_ink/coffee_mud/Abilities/StdAbility.java', 'tgt_file': 'b/com/planet_ink/coffee_mud/Abilities/StdAbility.java', 'file_extension': '.java', 'before_file': 'package com.planet_ink.coffee_mud.Abilities;\nimport com.planet_ink.coffee_mud.core.interfaces.*;\nimport com.planet_ink.coffee_mud.core.*;\nimport com.planet_ink.coffee_mud.core.CMSecurity.DisFlag;\nimport com.planet_ink.coffee_mud.core.collections.*;\nimport com.planet_ink.coffee_mud.Abilities.interfaces.*;\nimport com.planet_ink.coffee_mud.Areas.interfaces.*;\nimport com.planet_ink.coffee_mud.Behaviors.interfaces.*;\nimport com.planet_ink.coffee_mud.CharClasses.interfaces.*;\nimport com.planet_ink.coffee_mud.Commands.interfaces.*;\nimport com.planet_ink.coffee_mud.Common.interfaces.*;\nimport com.planet_ink.coffee_mud.Exits.interfaces.*;\nimport com.planet_ink.coffee_mud.Items.Basic.StdItem;\nimport com.planet_ink.coffee_mud.Items.interfaces.*;\nimport com.planet_ink.coffee_mud.Libraries.interfaces.*;\nimport com.planet_ink.coffee_mud.Libraries.interfaces.AbilityMapper.AbilityMapping;\nimport com.planet_ink.coffee_mud.core.interfaces.CostDef;\nimport com.planet_ink.coffee_mud.core.interfaces.CostDef.CostType;\nimport com.planet_ink.coffee_mud.Locales.interfaces.*;\nimport com.planet_ink.coffee_mud.MOBS.interfaces.*;\nimport com.planet_ink.coffee_mud.MOBS.interfaces.MOB.Attrib;\nimport com.planet_ink.coffee_mud.Races.interfaces.*;\n\nimport java.util.*;\n\n/*\n   Copyright 2001-2022 Bo Zimmerman\n\n   Licensed under the Apache License, Version 2.0 (the "License");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n\t   http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an "AS IS" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\npublic class StdAbility implements Ability\n{\n\t@Override\n\tpublic String ID()\n\t{\n\t\treturn "StdAbility";\n\t}\n\n\tprotected boolean\t\t\tisAnAutoEffect\t= false;\n\tprotected int\t\t\t\tproficiency\t\t= 0;\n\tprotected boolean\t\t\tsavable\t\t\t= true;\n\tprotected String\t\t\tmiscText\t\t= "";\n\tprotected MOB\t\t\t\tinvoker\t\t\t= null;\n\tprotected Physical\t\t\taffected\t\t= null;\n\tprotected boolean \t\t\tcanBeUninvoked\t= true;\n\tprotected volatile boolean\tunInvoked\t\t= false;\n\tprotected volatile int \t\ttickDown\t\t= -1;\n\tprotected long \t\t\t\tlastCastHelp\t= 0;\n\tprotected boolean \t\t\tamDestroyed\t\t= false;\n\n\tprivate static final int[] STATIC_USAGE_NADA= new int[Ability.USAGEINDEX_TOTAL];\n\n\tpublic StdAbility()\n\t{\n\t\tsuper();\n\t\t//CMClass.bumpCounter(this,CMClass.CMObjectType.ABILITY);//removed for mem & perf\n\t}\n\n\t/*\n\tprotected void finalize()\n\t{\n\t\tCMClass.unbumpCounter(this, CMClass.CMObjectType.ABILITY);\n\t}// removed for mem & perf\n\t*/\n\n\t@Override\n\tpublic CMObject newInstance()\n\t{\n\t\ttry\n\t\t{\n\t\t\treturn this.getClass().newInstance();\n\t\t}\n\t\tcatch(final Exception e)\n\t\t{\n\t\t\tLog.errOut(ID(),e);\n\t\t}\n\t\treturn new StdAbility();\n\t}\n\n\t@Override\n\tpublic String Name()\n\t{\n\t\treturn name();\n\t}\n\n\t@Override\n\tpublic String name()\n\t{\n\t\treturn "an ability";\n\t}\n\n\t@Override\n\tpublic String description()\n\t{\n\t\treturn "&";\n\t}\n\n\t@Override\n\tpublic String displayText()\n\t{\n\t\treturn "Affected list display for " + ID();\n\t}\n\n\t@Override\n\tpublic String image()\n\t{\n\t\treturn "";\n\t}\n\n\t@Override\n\tpublic String rawImage()\n\t{\n\t\treturn "";\n\t}\n\n\t@Override\n\tpublic void setImage(final String newImage)\n\t{\n\t}\n\n\tpublic static final String[]\tempty\t= {};\n\n\t@Override\n\tpublic String[] triggerStrings()\n\t{\n\t\treturn empty;\n\t}\n\n\t@Override\n\tpublic int maxRange()\n\t{\n\t\treturn adjustedMaxInvokerRange(0);\n\t}\n\n\t@Override\n\tpublic int minRange()\n\t{\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic double castingTime(final MOB mob, final List<String> cmds)\n\t{\n\t\treturn CMProps.getSkillActionCost(ID());\n\t}\n\n\t@Override\n\tpublic double combatCastingTime(final MOB mob, final List<String> cmds)\n\t{\n\t\treturn CMProps.getSkillCombatActionCost(ID());\n\t}\n\n\t@Override\n\tpublic double checkedCastingCost(final MOB mob, final List<String> commands)\n\t{\n\t\tif(mob!=null)\n\t\t{\n\t\t\tif(mob.isInCombat())\n\t\t\t\treturn combatCastingTime(mob,commands);\n\t\t\tif(abstractQuality()==Ability.QUALITY_MALICIOUS)\n\t\t\t\treturn combatCastingTime(mob,commands);\n\t\t}\n\t\treturn castingTime(mob,commands);\n\t}\n\n\t@Override\n\tpublic boolean putInCommandlist()\n\t{\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isAutoInvoked()\n\t{\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean bubbleAffect()\n\t{\n\t\treturn false;\n\t}\n\n\tprotected boolean ignoreCompounding()\n\t{\n\t\treturn false;\n\t}\n\n\tprotected int getTicksBetweenCasts()\n\t{\n\t\treturn 0;\n\t}\n\n\tprotected long getTimeOfNextCast()\n\t{\n\t\treturn 0;\n\t}\n\n\tprotected void setTimeOfNextCast(final long absoluteTime)\n\t{\n\t}\n\n\tprotected CostDef getRawTrainingCost()\n\t{\n\t\treturn CMProps.getNormalSkillGainCost(ID());\n\t}\n\n\t@Override\n\tpublic CostManager getTrainingCost(final MOB mob)\n\t{\n\t\tint qualifyingLevel;\n\t\tint playerLevel=1;\n\t\tif(mob!=null)\n\t\t{\n\t\t\tfinal Integer[] O=CMLib.ableMapper().getCostOverrides(mob,ID());\n\t\t\tif(O!=null)\n\t\t\t{\n\t\t\t\tInteger val=O[AbilityMapper.AbilCostType.TRAIN.ordinal()];\n\t\t\t\tif(val!=null)\n\t\t\t\t\treturn CMLib.utensils().createCostManager(CostType.TRAIN,Double.valueOf(val.intValue()));\n\t\t\t\tval=O[AbilityMapper.AbilCostType.PRAC.ordinal()];\n\t\t\t\tif(val!=null)\n\t\t\t\t\treturn CMLib.utensils().createCostManager(CostType.PRACTICE,Double.valueOf(val.intValue()));\n\t\t\t}\n\t\t\tqualifyingLevel=CMLib.ableMapper().qualifyingLevel(mob, this);\n\t\t\tplayerLevel=mob.basePhyStats().level();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqualifyingLevel=CMLib.ableMapper().lowestQualifyingLevel(ID());\n\t\t\tplayerLevel=1;\n\t\t}\n\t\tif(qualifyingLevel<=0)\n\t\t\tqualifyingLevel=1;\n\t\tfinal CostDef rawCost=getRawTrainingCost();\n\t\tif(rawCost==null)\n\t\t\treturn CMLib.utensils().createCostManager(CostType.TRAIN,Double.valueOf(1.0));\n\t\tfinal double[] vars=new double[]{ qualifyingLevel,playerLevel};\n\t\tfinal double value=CMath.parseMathExpression(rawCost.costDefinition(),vars);\n\t\treturn CMLib.utensils().createCostManager(new CostDef.Cost(value, rawCost.type(), rawCost.typeCurrency()));\n\t}\n\n\tprotected int practicesToPractice(final MOB mob)\n\t{\n\t\tif(mob!=null)\n\t\t{\n\t\t\tfinal Integer[] O=CMLib.ableMapper().getCostOverrides(mob,ID());\n\t\t\tif((O!=null)&&(O[AbilityMapper.AbilCostType.PRACPRAC.ordinal()]!=null))\n\t\t\t\treturn O[AbilityMapper.AbilCostType.PRACPRAC.ordinal()].intValue();\n\t\t}\n\t\treturn iniPracticesToPractice();\n\t}\n\n\tprotected int iniPracticesToPractice()\n\t{\n\t\treturn 1;\n\t}\n\n\tprotected void setTimeOfNextCast(final MOB caster)\n\t{\n\t\tlong newTime=(getTicksBetweenCasts()*CMProps.getTickMillis());\n\t\tdouble mul=1.0;\n\t\tmul -= (0.05 * getXLEVELLevel(caster));\n\t\tmul -= (0.1 * getXTIMELevel(caster));\n\t\tnewTime=Math.round(CMath.mul(newTime,mul));\n\t\tsetTimeOfNextCast(System.currentTimeMillis() +newTime);\n\t}\n\n\t@Override\n\tpublic String miscTextFormat()\n\t{\n\t\treturn CMParms.FORMAT_UNDEFINED;\n\t}\n\n\t@Override\n\tpublic long flags()\n\t{\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int usageType()\n\t{\n\t\treturn USAGE_MANA;\n\t}\n\n\t/**\n\t *  amount of mana/move used by this ability, overriding ini file\n\t *  -1=normal, Ability.COST_ALL=all, Ability.COST_PCT\n\t * @return amount of mana/move used by this ability, overriding ini file\n\t */\n\tprotected int overrideMana()\n\t{\n\t\treturn Ability.COST_NORMAL;\n\t}\n\n\t@Override\n\tpublic int abstractQuality()\n\t{\n\t\treturn Ability.QUALITY_INDIFFERENT;\n\t}\n\n\t@Override\n\tpublic int enchantQuality()\n\t{\n\t\treturn abstractQuality();\n\t}\n\n\t@Override\n\tpublic void initializeClass()\n\t{\n\t}\n\n\t@Override\n\tpublic boolean mayBeEnchanted()\n\t{\n\t\tif((CMLib.ableMapper().lowestQualifyingLevel(ID())>POWER_LEVEL_THRESHOLD)\n\t\t||(usageCost(null,true)[0]>POWER_COST_THRESHOLD)\n\t\t||(overrideMana()>POWER_OVERRIDE_THRESHOLD)\n\t\t||(CMath.bset(flags(), FLAG_CLANMAGIC)))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String L(final String str, final String ... xs)\n\t{\n\t\treturn CMLib.lang().fullSessionTranslation(str, xs);\n\t}\n\n\tprotected static String[] I(final String[] str)\n\t{\n\t\tfor(int i=0;i<str.length;i++)\n\t\t\tstr[i]=CMLib.lang().commandWordTranslation(str[i]);\n\t\treturn str;\n\t}\n\n\tprotected int castingQuality(final MOB mob, final Physical target, final int abstractQuality)\n\t{\n\t\tif((target!=null)&&(target.fetchEffect(ID())!=null))\n\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\tif(isAutoInvoked())\n\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\tif(mob != null)\n\t\t{\n\t\t\tif((target!=null)\n\t\t\t&&(mob.getVictim()==target))\n\t\t\t{\n\t\t\t\tif((minRange()>0)&&(mob.rangeToTarget()<minRange()))\n\t\t\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\t\t\tif(mob.rangeToTarget()>maxRange())\n\t\t\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\t\t}\n\t\t\tfinal Room R=mob.location();\n\t\t\tif((R!=null)\n\t\t\t&&(this.canAffect(CAN_ROOMS)||this.canTarget(CAN_ROOMS))\n\t\t\t&&(R.fetchEffect(ID())!=null))\n\t\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\t}\n\t\tswitch(abstractQuality)\n\t\t{\n\t\tcase Ability.QUALITY_BENEFICIAL_OTHERS:\n\t\t\tif(mob==target)\n\t\t\t\treturn Ability.QUALITY_BENEFICIAL_SELF;\n\t\t\treturn Ability.QUALITY_BENEFICIAL_OTHERS;\n\t\tcase Ability.QUALITY_MALICIOUS:\n\t\t\treturn Ability.QUALITY_MALICIOUS;\n\t\tcase Ability.QUALITY_BENEFICIAL_SELF:\n\t\t\tif((target instanceof MOB)&&(mob!=target))\n\t\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\t\treturn Ability.QUALITY_BENEFICIAL_SELF;\n\t\tdefault:\n\t\t\treturn Ability.QUALITY_INDIFFERENT;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int castingQuality(final MOB mob, final Physical target)\n\t{\n\t\treturn castingQuality(mob,target,abstractQuality());\n\t}\n\n\tprotected synchronized int expertise(final MOB mob, final Ability A, final ExpertiseLibrary.XType code)\n\t{\n\t\tif((mob!=null)\n\t\t&&(A.isNowAnAutoEffect()\n\t\t\t||(A.canBeUninvoked())\n\t\t\t||A.isAutoInvoked()))\n\t\t{\n\t\t\tint xlevel = CMLib.expertises().getExpertiseLevelCached(mob, A.ID(), code);\n\t\t\tfinal CharStats charStats=mob.charStats(); // circumstantial bonuses\n\t\t\tfinal String codeStr="X"+code.name()+"+";\n\t\t\txlevel += charStats.getAbilityAdjustment(codeStr+ID().toUpperCase());\n\t\t\txlevel += charStats.getAbilityAdjustment(codeStr+Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES]);\n\t\t\txlevel += charStats.getAbilityAdjustment(codeStr+Ability.DOMAIN_DESCS[(classificationCode()&Ability.ALL_DOMAINS)>> 5]);\n\t\t\txlevel += charStats.getAbilityAdjustment(codeStr+"*");\n\t\t\treturn xlevel;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tprotected int getX1Level(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.X1);\n\t}\n\n\tprotected int getX2Level(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.X2);\n\t}\n\n\tprotected int getX3Level(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.X3);\n\t}\n\n\tprotected int getX4Level(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.X4);\n\t}\n\n\tprotected int getX5Level(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.X5);\n\t}\n\n\tprotected int getXLEVELLevel(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.LEVEL);\n\t}\n\n\tprotected int getXLOWCOSTLevel(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.LOWCOST);\n\t}\n\n\tprotected int getXLOWFREECOSTLevel(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.LOWFREECOST);\n\t}\n\n\tprotected int getXMAXRANGELevel(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.MAXRANGE);\n\t}\n\n\tprotected int getXTIMELevel(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.TIME);\n\t}\n\n\tprotected int getXPCOSTLevel(final MOB mob)\n\t{\n\t\treturn expertise(mob, this, ExpertiseLibrary.XType.XPCOST);\n\t}\n\n\tprotected int getXPCOSTAdjustment(final MOB mob, final int xpLoss)\n\t{\n\t\tfinal int xLevel=getXPCOSTLevel(mob);\n\t\tif(xLevel<=0)\n\t\t\treturn xpLoss;\n\t\treturn xpLoss-(int)Math.round(CMath.mul(xpLoss,CMath.mul(.05,xLevel)));\n\t}\n\n\tprotected int adjustedMaxInvokerRange(final int max)\n\t{\n\t\tif(invoker==null)\n\t\t\treturn max;\n\t\tfinal int level=getXMAXRANGELevel(invoker);\n\t\tif(level<=0)\n\t\t\treturn  max;\n\t\treturn max+(int)Math.round(Math.ceil(CMath.mul(max,CMath.mul(level,0.2))));\n\t}\n\n\t/**\n\t * Designates whether, when used as a property/effect, what sort of objects this\n\t * ability can affect. Uses the Ability.CAN_* constants.\n\t * @see com.planet_ink.coffee_mud.Abilities.interfaces.Ability\n\t * @return a mask showing the type of objects this ability can affect\n\t */\n\tprotected int canAffectCode()\n\t{\n\t\treturn Ability.CAN_AREAS|\n\t\t\t\t Ability.CAN_ITEMS|\n\t\t\t\t Ability.CAN_MOBS|\n\t\t\t\t Ability.CAN_ROOMS|\n\t\t\t\t Ability.CAN_EXITS;\n\t}\n\n\t/**\n\t * Designates whether, when invoked as a skill, what sort of objects this\n\t * ability can effectively target. Uses the Ability.CAN_* constants.\n\t * @see com.planet_ink.coffee_mud.Abilities.interfaces.Ability\n\t * @return a mask showing the type of objects this ability can target\n\t */\n\tprotected int canTargetCode()\n\t{\n\t\treturn Ability.CAN_AREAS|\n\t\t\t\t Ability.CAN_ITEMS|\n\t\t\t\t Ability.CAN_MOBS|\n\t\t\t\t Ability.CAN_ROOMS|\n\t\t\t\t Ability.CAN_EXITS;\n\t}\n\n\t@Override\n\tpublic int classificationCode()\n\t{\n\t\treturn Ability.ACODE_SKILL;\n\t}\n\n\t@Override\n\tpublic long expirationDate()\n\t{\n\t\treturn (tickDown) * CMProps.getTickMillis();\n\t}\n\n\t@Override\n\tpublic void setExpirationDate(final long time)\n\t{\n\t\tif(time>System.currentTimeMillis())\n\t\t\ttickDown=(int)((time-System.currentTimeMillis())/CMProps.getTickMillis());\n\t\telse\n\t\t\ttickDown=(int)Math.round(CMath.div(time,CMProps.getTickMillis()));\n\t}\n\n\t@Override\n\tpublic boolean isNowAnAutoEffect()\n\t{\n\t\treturn isAnAutoEffect;\n\t}\n\n\t@Override\n\tpublic boolean isSavable()\n\t{\n\t\treturn savable;\n\t}\n\n\t@Override\n\tpublic void setSavable(final boolean truefalse)\n\t{\n\t\tsavable=truefalse;\n\t}\n\n\t@Override\n\tpublic void destroy()\n\t{\n\t\tamDestroyed=true;\n\t\taffected=null;\n\t\tinvoker=null;\n\t\tmiscText=null;\n\t}\n\n\t@Override\n\tpublic boolean amDestroyed()\n\t{\n\t\treturn amDestroyed;\n\t}\n\n\t@Override\n\tpublic void setName(final String newName)\n\t{\n\t}\n\n\t@Override\n\tpublic void setDisplayText(final String newDisplayText)\n\t{\n\t}\n\n\t@Override\n\tpublic void setDescription(final String newDescription)\n\t{\n\t}\n\n\t@Override\n\tpublic int abilityCode()\n\t{\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setAbilityCode(final int newCode)\n\t{\n\t}\n\n\t@Override\n\tpublic List<String> externalFiles()\n\t{\n\t\treturn null;\n\t}\n\n\tprotected long minCastWaitTime()\n\t{\n\t\treturn 0;\n\t}\n\n\t// ** For most abilities, the following stuff actually matters */\n\t@Override\n\tpublic void setMiscText(final String newMiscText)\n\t{\n\t\tmiscText=newMiscText;\n\t}\n\n\t@Override\n\tpublic String text()\n\t{\n\t\treturn miscText;\n\t}\n\n\t@Override\n\tpublic int proficiency()\n\t{\n\t\treturn proficiency;\n\t}\n\n\t@Override\n\tpublic void setProficiency(final int newProficiency)\n\t{\n\t\tproficiency=newProficiency;\n\t\tif(proficiency>100)\n\t\t\tproficiency=100;\n\t}\n\n\tprotected int addedTickTime(final MOB invokerMOB, final int baseTickTime)\n\t{\n\t\treturn (int)Math.round(CMath.mul(baseTickTime,CMath.mul(getXTIMELevel(invokerMOB),0.20)));\n\t}\n\n\t@Override\n\tpublic void startTickDown(final MOB invokerMOB, final Physical affected, int tickTime)\n\t{\n\t\tif(invokerMOB!=null)\n\t\t\tinvoker=invokerMOB;\n\n\t\tsavable=false; // makes it so that the effect does not save!\n\n\t\tif((classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_CRAFTINGSKILL))\n\t\t&&(classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_GATHERINGSKILL))\n\t\t&&(classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_EPICUREAN))\n\t\t&&(classificationCode() != (Ability.ACODE_COMMON_SKILL|Ability.DOMAIN_BUILDINGSKILL)))\n\t\t\ttickTime+=addedTickTime(invokerMOB,tickTime);\n\t\tif(invoker()!=null)\n\t\t{\n\t\t\tfor(int c=0;c<invoker().charStats().numClasses();c++)\n\t\t\t\ttickTime=invoker().charStats().getMyClass(c).classDurationModifier(invoker(),this,tickTime);\n\t\t}\n\t\tif(affected instanceof MOB)\n\t\t{\n\t\t\tfinal MOB mob=(MOB)affected;\n\t\t\tfinal Room room=mob.location();\n\t\t\tif(room==null)\n\t\t\t\treturn;\n\t\t\tif(affected.fetchEffect(ID())==null)\n\t\t\t{\n\t\t\t\taffected.addEffect(this);\n\t\t\t\tfinal int ecap=CMProps.getIntVar(CMProps.Int.EFFECTCAP);\n\t\t\t\tif(ecap>0)\n\t\t\t\t{\n\t\t\t\t\tfinal List<Ability> candidates = new ArrayList<Ability>(mob.numEffects());\n\t\t\t\t\tfor(final Enumeration<Ability> e = mob.effects();e.hasMoreElements();)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Ability eA=e.nextElement();\n\t\t\t\t\t\tif((eA!=null)\n\t\t\t\t\t\t&&(eA.canBeUninvoked())\n\t\t\t\t\t\t&&(eA.invoker()!=null))\n\t\t\t\t\t\t\tcandidates.add(eA);\n\t\t\t\t\t}\n\t\t\t\t\tif(candidates.size()>ecap)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Ability deadA=candidates.get(CMLib.dice().roll(1, candidates.size(), -1));\n\t\t\t\t\t\tif(deadA!=null)\n\t\t\t\t\t\t\tdeadA.unInvoke();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\troom.recoverRoomStats();\n\t\t\tif(invoker()!=affected)\n\t\t\t{\n\t\t\t\tfor(int c=0;c<mob.charStats().numClasses();c++)\n\t\t\t\t\ttickTime=mob.charStats().getMyClass(c).classDurationModifier(mob,this,tickTime);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif(affected.fetchEffect(this.ID())==null)\n\t\t\t\taffected.addEffect(this);\n\n\t\t\tif(affected instanceof Room)\n\t\t\t\t((Room)affected).recoverRoomStats();\n\t\t\telse\n\t\t\t\taffected.recoverPhyStats();\n\t\t\tCMLib.threads().startTickDown(this,Tickable.TICKID_MOB,1);\n\t\t}\n\t\ttickDown=tickTime;\n\t}\n\n\tpublic boolean disregardsArmorCheck(final MOB mob)\n\t{\n\t\t// armor checks are mostly handled by classes.\n\t\t// this is here for cases when someone gets a skill without the class to keep it in check.\n\t\t// that\'s why you disregard the armor check with you DO qualify\n\t\treturn ((mob==null)\n\t\t\t\t||(mob.isMonster())\n\t\t\t\t||(CMLib.ableMapper().qualifiesByLevel(mob,this)));\n\t}\n\n\tprotected int getPersonalLevelAdjustments(final MOB caster)\n\t{\n\t\tfinal CharStats charStats = caster.charStats();\n\t\treturn  charStats.getAbilityAdjustment("LEVEL+"+ID().toUpperCase())\n\t\t\t+ charStats.getAbilityAdjustment("LEVEL+"+Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES])\n\t\t\t+ charStats.getAbilityAdjustment("LEVEL+"+Ability.DOMAIN_DESCS[(classificationCode()&Ability.ALL_DOMAINS)>> 5])\n\t\t\t+ charStats.getAbilityAdjustment("LEVEL+*");\n\t}\n\n\t@Override\n\tpublic int adjustedLevel(final MOB caster, final int asLevel)\n\t{\n\t\tif(caster==null)\n\t\t\treturn 1;\n\t\tfinal int lowestQualifyingLevel=CMLib.ableMapper().lowestQualifyingLevel(this.ID());\n\t\tint adjLevel=lowestQualifyingLevel;\n\t\tif(asLevel<=0)\n\t\t{\n\t\t\tfinal int qualifyingLevel=CMLib.ableMapper().qualifyingLevel(caster,this);\n\t\t\tif((caster.isMonster())||(qualifyingLevel>=0))\n\t\t\t\tadjLevel+=(CMLib.ableMapper().qualifyingClassLevel(caster,this)-qualifyingLevel);\n\t\t\telse\n\t\t\t\tadjLevel=caster.phyStats().level()-lowestQualifyingLevel-25;\n\t\t}\n\t\telse\n\t\t\tadjLevel=asLevel;\n\t\tif(adjLevel<lowestQualifyingLevel)\n\t\t\tadjLevel=lowestQualifyingLevel;\n\t\tadjLevel += getPersonalLevelAdjustments(caster);\n\t\tif(adjLevel<1)\n\t\t\treturn 1;\n\t\tint level=adjLevel+getXLEVELLevel(caster);\n\t\tfinal CharStats CS=caster.charStats();\n\t\tfor(int c=0;c<CS.numClasses();c++)\n\t\t\tlevel=CS.getMyClass(c).classDurationModifier(invoker(),this,level);\n\t\treturn level;\n\t}\n\n\t@Override\n\tpublic boolean canTarget(final int can_code)\n\t{\n\t\tfinal int canTarget=canTargetCode();\n\t\tif((can_code==0)||(canTarget==0))\n\t\t\treturn can_code == canTarget;\n\t\treturn CMath.bset(canTargetCode(),can_code);\n\t}\n\n\t@Override\n\tpublic boolean canAffect(final int can_code)\n\t{\n\t\tfinal int canAffect=canAffectCode();\n\t\tif((can_code==0)||(canAffect==0))\n\t\t\treturn can_code == canAffect;\n\t\treturn CMath.bset(canAffectCode(),can_code);\n\t}\n\n\t@Override\n\tpublic boolean canAffect(final Physical P)\n\t{\n\t\tif((P==null)&&(canAffectCode()==0))\n\t\t\treturn true;\n\t\tif(P==null)\n\t\t\treturn false;\n\t\tif((P instanceof MOB)&&((canAffectCode()&Ability.CAN_MOBS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Item)&&((canAffectCode()&Ability.CAN_ITEMS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Exit)&&((canAffectCode()&Ability.CAN_EXITS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Room)&&((canAffectCode()&Ability.CAN_ROOMS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Area)&&((canAffectCode()&Ability.CAN_AREAS)>0))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean canTarget(final Physical P)\n\t{\n\t\tif((P==null)&&(canTargetCode()==0))\n\t\t\treturn true;\n\t\tif(P==null)\n\t\t\treturn false;\n\t\tif((P instanceof MOB)&&((canTargetCode()&Ability.CAN_MOBS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Item)&&((canTargetCode()&Ability.CAN_ITEMS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Room)&&((canTargetCode()&Ability.CAN_ROOMS)>0))\n\t\t\treturn true;\n\t\tif((P instanceof Area)&&((canTargetCode()&Ability.CAN_AREAS)>0))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tprotected MOB getTarget(final MOB mob, final List<String> commands, final Environmental givenTarget)\n\t{\n\t\treturn getTarget(mob,commands,givenTarget,false,false);\n\t}\n\n\t//BZ: The magical pre-cast mana-saving range check, pt 1/2\n\tprotected boolean checkTargetRange(final MOB mob, final MOB target)\n\t{\n\t\tif((target != null)\n\t\t&&(target!=mob)\n\t\t&&((minRange()>0)||((maxRange()>0)&&(maxRange()<99999)))\n\t\t&&(abstractQuality()==Ability.QUALITY_MALICIOUS))\n\t\t{\n\t\t\tif((minRange()>0)\n\t\t\t&&(((mob.getVictim()==target)&&(mob.rangeToTarget()<minRange()))\n\t\t\t  ||((target.getVictim()==mob)&&(target.rangeToTarget()<minRange())))\n\t\t\t)\n\t\t\t{\n\t\t\t\tfailureTell(mob,mob,false,L("You are too close to @x1 to do that.",target.name()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse\n\t\t\tif((maxRange()>0)\n\t\t\t&&(maxRange()<99999)\n\t\t\t&&(((mob.getVictim()==target)&&(mob.rangeToTarget()>maxRange()))\n\t\t\t  ||((target.getVictim()==mob)&&(target.rangeToTarget()>maxRange())))\n\t\t\t)\n\t\t\t{\n\t\t\t\tfailureTell(mob,mob,false,L("You are too far away from @x1 to do that.",target.name()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected MOB getTarget(final MOB mob, final List<String> commands, final Environmental givenTarget, final boolean quiet, final boolean alreadyAffOk)\n\t{\n\t\tString targetName=CMParms.combine(commands,0);\n\t\tMOB target=null;\n\t\tif((givenTarget!=null)\n\t\t&&(givenTarget instanceof MOB))\n\t\t\ttarget=(MOB)givenTarget;\n\t\telse\n\t\tif(targetName.length()==0)\n\t\t{\n\t\t\tfinal boolean inCombat = mob.isInCombat();\n\t\t\tif(inCombat\n\t\t\t&&(castingQuality(mob,mob.getVictim())==Ability.QUALITY_MALICIOUS)\n\t\t\t&&(mob.getVictim()!=null))\n\t\t\t\ttarget=mob.getVictim();\n\t\t\telse\n\t\t\tif(castingQuality(mob,mob)==Ability.QUALITY_BENEFICIAL_SELF)\n\t\t\t\ttarget=mob;\n\t\t\telse\n\t\t\tif(inCombat\n\t\t\t&&(abstractQuality()==Ability.QUALITY_MALICIOUS)\n\t\t\t&&(mob.getVictim()!=null))\n\t\t\t\ttarget=mob.getVictim();\n\t\t\telse\n\t\t\tif(abstractQuality()!=Ability.QUALITY_MALICIOUS)\n\t\t\t\ttarget=mob;\n\t\t\tif(target == null)\n\t\t\t{\n\t\t\t\tfailureTell(mob,mob,false,L("You need to specify a target."));\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\telse\n\t\tif(targetName.equalsIgnoreCase("self")||targetName.equalsIgnoreCase("me"))\n\t\t\ttarget=mob;\n\t\telse\n\t\tif(mob.location()!=null)\n\t\t{\n\t\t\ttarget=mob.location().fetchInhabitant(targetName);\n\t\t\tif(target==null)\n\t\t\t{\n\t\t\t\tfinal Environmental t=mob.location().fetchFromRoomFavorItems(null,targetName);\n\t\t\t\tif((t!=null)&&(!(t instanceof MOB)))\n\t\t\t\t{\n\t\t\t\t\tif(!quiet)\n\t\t\t\t\t\tmob.tell(mob,t,null,L("You can\'t do that to <T-NAMESELF>."));\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif(target!=null)\n\t\t\ttargetName=target.name();\n\n\t\tif((target==null)\n\t\t||((givenTarget==null)\n\t\t\t&&(!CMLib.flags().canBeSeenBy(target,mob))\n\t\t\t&&((!CMLib.flags().canBeHeardMovingBy(target,mob))||(!target.isInCombat()))))\n\t\t{\n\t\t\tif(!quiet)\n\t\t\t{\n\t\t\t\tif(targetName.trim().length()==0)\n\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see them here."));\n\t\t\t\telse\n\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see anyone called \'@x1\' here.",targetName));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tif((!alreadyAffOk)&&(!isAutoInvoked())&&(target.fetchEffect(this.ID())!=null))\n\t\t{\n\t\t\tif((givenTarget==null)&&(!quiet))\n\t\t\t{\n\t\t\t\tif(target==mob)\n\t\t\t\t\tfailureTell(mob,mob,false,L("You are already affected by @x1.",name()));\n\t\t\t\telse\n\t\t\t\t\tfailureTell(mob,target,false,L("<S-NAME> is already affected by @x1.",name()));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tif(!checkTargetRange(mob,target))\n\t\t\ttarget=null;\n\n\t\treturn target;\n\t}\n\n\tprotected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget, final Filterer<Environmental> filter)\n\t{\n\t\treturn getAnyTarget(mob,commands,givenTarget,filter,false,false);\n\t}\n\n\tprotected Physical getAnyTarget(final MOB mob, final Room location, final boolean anyContainer, final List<String> commands,\n\t\t\t\t\t\t\t\t\tfinal Physical givenTarget, final Filterer<Environmental> filter)\n\t{\n\t\tfinal Physical P=getAnyTarget(mob,commands,givenTarget,filter,false,false, true);\n\t\tif(P!=null)\n\t\t\treturn P;\n\t\treturn getTarget(mob, location, givenTarget, anyContainer, commands, filter);\n\t}\n\n\tprotected Physical getAnyTarget(final MOB mob, final Room location, final boolean anyContainer, final List<String> commands,\n\t\t\tfinal Physical givenTarget, final Filterer<Environmental> filter, final boolean quiet)\n\t{\n\t\tfinal Physical P=getAnyTarget(mob,commands,givenTarget,filter,false,false, true);\n\t\tif(P!=null)\n\t\t\treturn P;\n\t\treturn getTarget(mob, location, givenTarget, anyContainer, commands, filter, quiet);\n\t}\n\n\tprotected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget, final Filterer<Environmental> filter, final boolean checkOthersInventory)\n\t{\n\t\treturn getAnyTarget(mob,commands,givenTarget,filter,checkOthersInventory,false);\n\t}\n\n\tprotected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget,\n\t\t\tfinal Filterer<Environmental> filter,\n\t\t\tfinal boolean checkOthersInventory, final boolean alreadyAffOk)\n\t{\n\t\treturn getAnyTarget(mob,commands,givenTarget,filter,checkOthersInventory,alreadyAffOk,false);\n\t}\n\n\tprotected Physical getAnyTarget(final MOB mob, final List<String> commands, final Physical givenTarget,\n\t\t\tfinal Filterer<Environmental> filter,\n\t\t\tfinal boolean checkOthersInventory, final boolean alreadyAffOk,\n\t\t\tfinal boolean quiet)\n\t{\n\t\tfinal Room R=mob.location();\n\t\tString targetName=CMParms.combine(commands,0);\n\t\tPhysical target=null;\n\t\tif(givenTarget != null)\n\t\t\ttarget=givenTarget;\n\t\telse\n\t\tif((targetName.length()==0)&&(mob.isInCombat())&&(castingQuality(mob,mob.getVictim())==Ability.QUALITY_MALICIOUS))\n\t\t\ttarget=mob.getVictim();\n\t\telse\n\t\tif(targetName.equalsIgnoreCase("self")||targetName.equalsIgnoreCase("me"))\n\t\t\ttarget=mob;\n\t\telse\n\t\tif((targetName.length()==0)&&(mob.isInCombat())&&(abstractQuality()==Ability.QUALITY_MALICIOUS))\n\t\t\ttarget=mob.getVictim();\n\t\telse\n\t\tif(R!=null)\n\t\t{\n\t\t\tif((target==null)\n\t\t\t&&(targetName.equalsIgnoreCase("here")))\n\t\t\t\ttarget=R;\n\t\t\tif(target == null)\n\t\t\t\ttarget=R.fetchFromRoomFavorMOBs(null,targetName);\n\t\t\tif(target==null)\n\t\t\t\ttarget=R.fetchFromMOBRoomFavorsItems(mob,null,targetName,filter);\n\t\t\tif((target==null)\n\t\t\t&&(targetName.equalsIgnoreCase("room")\n\t\t\t\t||targetName.equalsIgnoreCase("place")))\n\t\t\t\ttarget=R;\n\t\t\tint dir=-1;\n\t\t\tif((target==null)&&((dir=CMLib.directions().getGoodDirectionCode(targetName))>=0))\n\t\t\t\ttarget=R.getExitInDir(dir);\n\t\t\tif((target==null)&&(checkOthersInventory))\n\t\t\t{\n\t\t\t\tfor(int i=0;i<R.numInhabitants();i++)\n\t\t\t\t{\n\t\t\t\t\tfinal MOB M=R.fetchInhabitant(i);\n\t\t\t\t\ttarget=M.fetchItem(null,filter,targetName);\n\t\t\t\t\tif(target!=null)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(target!=null)\n\t\t\ttargetName=target.name();\n\n\t\tif((target==null)\n\t\t||((givenTarget==null)\n\t\t   &&(!CMLib.flags().canBeSeenBy(target,mob))\n\t\t   &&((!CMLib.flags().canBeHeardMovingBy(target,mob))\n\t\t\t\t||((target instanceof MOB)&&(!((MOB)target).isInCombat())))))\n\t\t{\n\t\t\tif(!quiet)\n\t\t\t{\n\t\t\t\tif(targetName.trim().length()==0)\n\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see that here."));\n\t\t\t\telse\n\t\t\t\tif(!CMLib.flags().isSleeping(mob))\n\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see \'@x1\' here.",targetName));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tif((!alreadyAffOk)&&(target.fetchEffect(this.ID())!=null))\n\t\t{\n\t\t\tif(givenTarget==null)\n\t\t\t{\n\t\t\t\tif(!quiet)\n\t\t\t\t{\n\t\t\t\t\tif(target==mob)\n\t\t\t\t\t\tfailureTell(mob,mob,false,L("You are already affected by @x1.",name()));\n\t\t\t\t\telse\n\t\t\t\t\t\tmob.tell(mob,target,null,L("<T-NAME> is already affected by @x1.",name()));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn target;\n\t}\n\n\tprotected static Item possibleContainer(final MOB mob, final List<String> commands, final boolean withStuff, final Filterer<Environmental> filter)\n\t{\n\t\tif((commands==null)||(commands.size()<2))\n\t\t\treturn null;\n\n\t\tfinal String possibleContainerID=commands.get(commands.size()-1);\n\t\tfinal Environmental thisThang=mob.location().fetchFromMOBRoomFavorsItems(mob,null,possibleContainerID,filter);\n\t\tif((thisThang!=null)\n\t\t&&(thisThang instanceof Item)\n\t\t&&(((Item)thisThang) instanceof Container)\n\t\t&&((!withStuff)||(((Container)thisThang).hasContent())))\n\t\t{\n\t\t\tcommands.remove(commands.size()-1);\n\t\t\treturn (Item)thisThang;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget, final List<String> commands, final Filterer<Environmental> filter)\n\t{\n\t\treturn getTarget(mob,location,givenTarget,null,commands,filter);\n\t}\n\n\tprotected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget,\n\t\t\tfinal boolean anyContainer, final List<String> commands, final Filterer<Environmental> filter)\n\t{\n\t\treturn getTarget(mob, location, givenTarget, anyContainer, commands, filter, false);\n\t}\n\n\tprotected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget,\n\t\t\tfinal boolean anyContainer, final List<String> commands, final Filterer<Environmental> filter,\n\t\t\tfinal boolean quiet)\n\t{\n\t\tItem I=this.getTarget(mob, location, givenTarget, null, commands, filter, anyContainer);\n\t\tif(I!=null)\n\t\t\treturn I;\n\t\tif(!anyContainer)\n\t\t\treturn I;\n\t\tfinal List<Item> containers=new ArrayList<Item>();\n\t\tif(location!=null)\n\t\t{\n\t\t\tfor(final Enumeration<Item> i=location.items();i.hasMoreElements();)\n\t\t\t{\n\t\t\t\tfinal Item C=i.nextElement();\n\t\t\t\tif((C instanceof Container)\n\t\t\t\t&&(((Container)C).isOpen()))\n\t\t\t\t\tcontainers.add(C);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(final Enumeration<Item> i=mob.items();i.hasMoreElements();)\n\t\t\t{\n\t\t\t\tfinal Item C=i.nextElement();\n\t\t\t\tif((C instanceof Container)\n\t\t\t\t&&(((Container)C).isOpen()))\n\t\t\t\t\tcontainers.add(C);\n\t\t\t}\n\t\t}\n\t\tif(containers.size()==0)\n\t\t\treturn this.getTarget(mob, location, givenTarget, null, commands, filter, quiet);\n\t\telse\n\t\t{\n\t\t\tfor(int c=0;c<containers.size();c++)\n\t\t\t{\n\t\t\t\tfinal Item C=containers.get(c);\n\t\t\t\tI=this.getTarget(mob, location, givenTarget, C, commands, filter, quiet || (c<containers.size()-1));\n\t\t\t\tif(I!=null)\n\t\t\t\t\treturn I;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget, final Item container,\n\t\t\tfinal List<String> commands, final Filterer<Environmental> filter)\n\t{\n\t\treturn getTarget(mob, location, givenTarget, container, commands, filter, false);\n\t}\n\n\tprotected Item evalTargetItem(final MOB mob, final Room location, final Environmental givenTarget, final Environmental target, final String targetName, final boolean quiet)\n\t{\n\t\tif((target==null)\n\t\t||(!(target instanceof Item))\n\t\t||((givenTarget==null)&&(!CMLib.flags().canBeSeenBy(target,mob))))\n\t\t{\n\t\t\tif(!quiet)\n\t\t\t{\n\t\t\t\tif(targetName.length()==0)\n\t\t\t\t\tfailureTell(mob,mob,false,L("You need to be more specific."));\n\t\t\t\telse\n\t\t\t\tif((target==null)||(target instanceof Item))\n\t\t\t\t{\n\t\t\t\t\tif(targetName.trim().length()==0)\n\t\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see that here."));\n\t\t\t\t\telse\n\t\t\t\t\tif(!CMLib.flags().isSleeping(mob)) // no idea why this is here :(\n\t\t\t\t\t{\n\t\t\t\t\t\tif(location != null)\n\t\t\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see anything called \'@x1\' here.",targetName));\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t have anything called \'@x1\'.",targetName));\n\t\t\t\t\t}\n\t\t\t\t\telse // this was added for clan donate (and other things I\'m sure) while sleeping.\n\t\t\t\t\t\tfailureTell(mob,mob,false,L("You don\'t see \'@x1\' in your dreams.",targetName));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tmob.tell(mob,target,null,L("You can\'t do that to <T-NAMESELF>."));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn (Item)target;\n\t}\n\n\tprotected Item getTarget(final MOB mob, final Room location, final Environmental givenTarget, final Item container,\n\t\t\tfinal List<String> commands, final Filterer<Environmental> filter, final boolean quiet)\n\t{\n\t\tString targetName=CMParms.combine(commands,0);\n\n\t\tEnvironmental target=null;\n\t\tif((givenTarget!=null)&&(givenTarget instanceof Item))\n\t\t\ttarget=givenTarget;\n\n\t\tif((location!=null)&&(target==null)&&(targetName.length()>0))\n\t\t\ttarget=location.fetchFromRoomFavorItems(container,targetName);\n\t\tif((target==null)&&(targetName.length()>0))\n\t\t{\n\t\t\tif(location!=null)\n\t\t\t\ttarget=location.fetchFromMOBRoomFavorsItems(mob,container,targetName,filter);\n\t\t\telse\n\t\t\tif(mob != null)\n\t\t\t\ttarget=mob.fetchItem(container, filter, targetName);\n\t\t}\n\t\tif(target!=null)\n\t\t\ttargetName=target.name();\n\n\t\treturn evalTargetItem(mob, location, givenTarget, target, targetName, quiet);\n\n\t}\n\n\tprotected Item getTargetItemFavorMOB(final MOB mob, final Room location, final Physical givenTarget, final Item container,\n\t\t\tfinal List<String> commands, final Filterer<Environmental> filter)\n\t{\n\t\treturn getTargetItemFavorMOB(mob, location, givenTarget, container, commands, filter, false);\n\t}\n\n\tprotected Item getTargetItemFavorMOB(final MOB mob, final Room location, final Physical givenTarget,\n\t\t\tfinal List<String> commands, final Filterer<Environmental> filter)\n\t{\n\t\treturn getTargetItemFavorMOB(mob, location, givenTarget, null, commands, filter, false);\n\t}\n\n\tprotected Item getTargetItemFavorMOB(final MOB mob, final Room location, final Physical givenTarget, final Item container,\n\t\t\tfinal List<String> commands, final Filterer<Environmental> filter, final boolean quiet)\n\t{\n\t\tString targetName=CMParms.combine(commands,0);\n\n\t\tEnvironmental target=null;\n\t\tif((givenTarget!=null)&&(givenTarget instanceof Item))\n\t\t\ttarget=givenTarget;\n\n\t\tif((target==null)&&(targetName.length()>0))\n\t\t{\n\t\t\tif(location!=null)\n\t\t\t\ttarget=location.fetchFromMOBRoomFavorsItems(mob,container,targetName,filter);\n\t\t\telse\n\t\t\t\ttarget=mob.fetchItem(container, filter, targetName);\n\t\t}\n\t\tif(target!=null)\n\t\t\ttargetName=target.name();\n\n\t\treturn evalTargetItem(mob, location, givenTarget, target, targetName, quiet);\n\t}\n\n\t@Override\n\tpublic int compareTo(final CMObject o)\n\t{\n\t\treturn CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o));\n\t}\n\n\tprotected void cloneFix(final Ability E)\n\t{\n\t}\n\n\t@Override\n\tpublic CMObject copyOf()\n\t{\n\t\ttry\n\t\t{\n\t\t\tfinal StdAbility E=(StdAbility)this.clone();\n\t\t\t//CMClass.bumpCounter(E,CMClass.CMObjectType.ABILITY);//removed for mem & perf\n\t\t\tE.cloneFix(this);\n\t\t\treturn E;\n\n\t\t}\n\t\tcatch(final CloneNotSupportedException e)\n\t\t{\n\t\t\treturn this.newInstance();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean proficiencyCheck(final MOB mob, final int adjustment, final boolean auto)\n\t{\n\t\tif(auto)\n\t\t{\n\t\t\tisAnAutoEffect=true;\n\t\t\tif((mob!=null)&&(!mob.isMine(this)))\n\t\t\t\tsetProficiency(100);\n\t\t\treturn true;\n\t\t}\n\n\t\tisAnAutoEffect=false;\n\t\tint pctChance=proficiency();\n\t\tif(mob != null)\n\t\t{\n\t\t\tif(CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.SUPERSKILL))\n\t\t\t\treturn true;\n\t\t\tfinal CharStats charStats = mob.charStats();\n\t\t\tpctChance += charStats.getAbilityAdjustment("PROF+"+ID().toUpperCase());\n\t\t\tpctChance += charStats.getAbilityAdjustment("PROF+"+Ability.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES]);\n\t\t\tpctChance += charStats.getAbilityAdjustment("PROF+"+Ability.DOMAIN_DESCS[(classificationCode()&Ability.ALL_DOMAINS)>> 5]);\n\t\t\tpctChance += charStats.getAbilityAdjustment("PROF+*");\n\t\t}\n\n\t\tif(!CMSecurity.isDisabled(DisFlag.DIS955RULE))\n\t\t{\n\t\t\tif(pctChance>95)\n\t\t\t\tpctChance=95;\n\t\t\tif(pctChance<5)\n\t\t\t\tpctChance=5;\n\t\t}\n\n\t\tif(adjustment>=0)\n\t\t\tpctChance+=adjustment;\n\t\telse\n\t\tif(CMLib.dice().rollPercentage()>(100+adjustment))\n\t\t\treturn false;\n\t\treturn (CMLib.dice().rollPercentage()<pctChance);\n\t}\n\n\t@Override\n\tpublic Physical affecting()\n\t{\n\t\treturn affected;\n\t}\n\n\t@Override\n\tpublic void setAffectedOne(final Physical P)\n\t{\n\t\taffected=P;\n\t}\n\n\t@Override\n\tpublic void unInvoke()\n\t{\n\t\tunInvoked=true;\n\n\t\tfinal Physical being=affected;\n\t\tif(being==null)\n\t\t\treturn;\n\n\t\tif(canBeUninvoked())\n\t\t{\n\t\t\tbeing.delEffect(this);\n\t\t\tif(being instanceof Room)\n\t\t\t\t((Room)being).recoverRoomStats();\n\t\t\telse\n\t\t\tif(being instanceof MOB)\n\t\t\t{\n\t\t\t\tfinal MOB M=(MOB)being;\n\t\t\t\tfinal Room R=M.location();\n\t\t\t\tif((R!=null)&&(R.isInhabitant(M)))\n\t\t\t\t\tR.recoverRoomStats();\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tM.recoverPhyStats();\n\t\t\t\t\tM.recoverCharStats();\n\t\t\t\t\tM.recoverMaxState();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tbeing.recoverPhyStats();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean canBeUninvoked()\n\t{\n\t\treturn canBeUninvoked;\n\t}\n\n\t@Override\n\tpublic void affectPhyStats(final Physical affected, final PhyStats affectableStats)\n\t{\n\t}\n\n\t@Override\n\tpublic void affectCharStats(final MOB affectedMob, final CharStats affectableStats)\n\t{\n\t}\n\n\t@Override\n\tpublic void affectCharState(final MOB affectedMob, final CharState affectableMaxState)\n\t{\n\t}\n\n\t@Override\n\tpublic MOB invoker()\n\t\t\t{\r\n\t\t\t\tint maxAmt;\r\n\t\t\t\tint baseAmt;\r\n\t\t\t\tswitch(costType)\r\n\t\t\t\t{\r\n\t\t\t\tcase 1:\r\n\t\t\t\t\tmaxAmt=mob.maxState().getMovement();\r\n\t\t\t\t\tbaseAmt=mob.baseState().getMovement();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 2:\r\n\t\t\t\t\tmaxAmt=mob.maxState().getHitPoints();\r\n\t\t\t\t\tbaseAmt=mob.baseState().getHitPoints();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\tcase 0:\r\n\t\t\t\t\tmaxAmt=mob.maxState().getMana();\r\n\t\t\t\t\tbaseAmt=mob.baseState().getMana();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tint costDown=0;\r\n\t\t\t\tfinal int rawCost;\r\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tswitch(costType)\n\t\t\t\t\t{\n\t\t\t\t\trawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));\r\n\t\t\t\telse\r\n\t\t\t\t\trawCost = consumed / divider;\r\n\t\t\t\tif(rawCost>2)\r\n\t\t\t\t{\r\n\t\t\t\t\tcostDown=getXLOWCOSTLevel(mob);\r\n\t\t\t\t\tfinal int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));\r\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tmaxAmt=mob.maxState().getMana();\n\t\t\t\t\t\tbaseAmt=mob.baseState().getMana();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tint costDown=0;\n\t\t\t\tfinal int rawCost;\n\t\t\t\tif(consumed==COST_ALL)\n\t\t\t\t\trawCost = (baseAmt>maxAmt) ? baseAmt : maxAmt;\n\t\t\t\telse\n\t\t\t\tif(consumed>COST_PCT)\n\t\t\t\t\trawCost = (int)Math.round(CMath.mul(maxAmt,CMath.div((COST_ALL-consumed),100.0)));\n\t\t\t\telse\n\t\t\t\t\trawCost = consumed / divider;\n\t\t\t\tif((rawCost>2)&&(mob!=null))\n\t\t\t\t{\n\t\t\t\t\tcostDown=getXLOWCOSTLevel(mob);\n\t\t\t\t\tfinal int fivePercent=(int)Math.round(CMath.mul(rawCost,0.05));\n\t\t\t\t\tif(fivePercent > 1)\n\t\t\t\t\t\tcostDown *= fivePercent;\n\t\t\t\t\tif((costDown>=rawCost)&&(rawCost<Integer.MAX_VALUE/2))\n\t\t\t\t\t\tcostDown=rawCost/2;\n\t\t\t\t\tminimum=(minimum-costDown);\n\t\t\t\t\tif(minimum<5)\n\t\t\t\t\t\tminimum=5;\n\t\t\t\t\tfinal int freeDown=getXLOWFREECOSTLevel(mob) / 2;\n\t\t\t\t\tcostDown += freeDown;\n\t\t\t\t\tminimum=(minimum-freeDown);\n\t\t\t\t\tif(minimum<0)\n\t\t\t\t\t\tminimum=0;\n\t@Override\r\n\tpublic int[] usageCost(final MOB mob, final boolean ignoreClassOverride)\r\n\t{\r\n\t\tif(mob==null)\r\n\t\t{\r\n\t\t\tfinal Map<String,int[]> overrideCache=getHardOverrideManaCache();\r\n\t\t\tif(!overrideCache.containsKey(ID()))\r\n\t\t\t{\r\n\t\t\t\tfinal int[] usage=new int[Ability.USAGEINDEX_TOTAL];\r\n\t\t\t\tArrays.fill(usage,overrideMana());\r\n\t\t\t\toverrideCache.put(ID(), usage);\r\n\t\t\t}\r\n\t\t\treturn overrideCache.get(ID());\r\n\t\t}\r\n\t\tif(usageType()==Ability.USAGE_NADA)\r\n\t\t\treturn STATIC_USAGE_NADA;\r\n\r\n\t\tfinal int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());\r\n\t\tfinal int myCacheIndex=ignoreClassOverride?Ability.CACHEINDEX_CLASSLESS:Ability.CACHEINDEX_NORMAL;\r\n\t\tfinal int[] myCache=abilityUsageCache[myCacheIndex];\r\n\t\tfinal boolean rebuildCache=(myCache==null);\r\n\t\t}\n\t\treturn hardOverrideCache;\n\t}\n\n\t@Override\n\tpublic int[] usageCost(final MOB mob, final boolean ignoreClassOverride)\n\t{\n\t\tfinal Map<String,int[]> overrideCache=getHardOverrideManaCache();\n\t\tif(mob==null)\n\t\t{\n\t\t{\r\n\t\t\tint diff=0;\r\n\t\t\tint lowest=Integer.MAX_VALUE;\r\n\t\t\tfor(int c=0;c<mob.charStats().numClasses();c++)\r\n\t\t\t{\r\n\t\t\t\tfinal CharClass C=mob.charStats().getMyClass(c);\r\n\t\t\t\tfinal int qualifyingLevel=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,ID());\r\n\t\t\t\tfinal int classLevel=mob.charStats().getClassLevel(C.ID());\r\n\t\t\t\tif((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))\r\n\t\t\t\t{\r\n\t\t\t\t\tdiff+=(classLevel-qualifyingLevel);\r\n\t\t\t\t\tif(qualifyingLevel<lowest)\r\n\t\t\t\t\t\tlowest=qualifyingLevel;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif(lowest==Integer.MAX_VALUE)\r\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint diff=0;\n\t\t\t}\r\n\r\n\t\t\tInteger[] costOverrides=null;\r\n\t\t\tif(!ignoreClassOverride)\r\n\t\t\t\tcostOverrides=CMLib.ableMapper().getCostOverrides(mob,ID());\r\n\t\t\tminimum=CMProps.getMinManaException(ID());\r\n\t\t\tif(minimum==Integer.MIN_VALUE)\r\n\t\t\t\t\tfinal int classLevel=mob.charStats().getClassLevel(C.ID());\n\t\t\t\t\tif((qualifyingLevel>=0)&&(classLevel>=qualifyingLevel))\n\t\t\t\t\t{\n\t\t\t\t\t\tdiff+=(classLevel-qualifyingLevel);\n\t\t\t\t\t\tif(qualifyingLevel<lowest)\n\t\t\t\t\t\t\tlowest=qualifyingLevel;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(lowest==Integer.MAX_VALUE)\n\t\t\t{\n\t\t\t\tlowest=CMLib.ableMapper().lowestQualifyingLevel(ID());\n\t\t\t{\r\n\t\t\t\tfinal double[] vars = new double[] {\r\n\t\t\t\t\tlowest,\r\n\t\t\t\t\tmob.phyStats().level(),\r\n\t\t\t\t\tminimum,\r\n\t\t\t\t\tadjustedLevel(mob,0),\r\n\t\t\t\t\tdiff\r\n\t\t\t\t};\r\n\t\t\t\tconsumed=(int)CMath.parseMathExpression((CMath.CompiledFormula)cost, vars, 0.0);\r\n\t\t\t\tminimum=CMProps.getIntVar(CMProps.Int.MANAMINCOST);\n\t\t\tif(minimum<0)\n\t\t\t{\n\t\t\t\tminimum=(lowest*CMath.abs(minimum));\n\t\t\t\tint actualMinimum=5;\n\t\t\t\tfinal int exception = CMProps.getMinManaException("_DEFAULT");\n\t\t\t\tif(exception > 0 )\n\t\t\t\t\tactualMinimum = exception;\n\t\t\t\tif(minimum<actualMinimum)\n\t\t\t\t\tminimum=actualMinimum;\n\t\t\t}\n\t\t\tfinal Object cost = CMProps.getManaCostObject(ID());\n\t\t\tif(cost instanceof Integer)\n\t\t\t\tconsumed=((Integer)cost).intValue();\n\t\t\telse\n\t\t\tif(cost instanceof CMath.CompiledFormula)\n\t\t\t{\n\t\t\t\tfinal double[] vars = new double[] {\n\t\t\t\t\tlowest,\n\t\t\t\t\t(mob==null)?lowest:mob.phyStats().level(),\n\t\t\t\t\tminimum,\n\t\t\t\t\t(mob==null)?lowest:adjustedLevel(mob,0),\n\t\t\t\t\tdiff\n\t\t\t\t};\n\t\t\telse\r\n\t\t\t\tabilityUsageCache[myCacheIndex]=usageCost;\r\n\t\t}\r\n\t\treturn usageCost;\r\n\t}\r\n\r\n\t\t\t\tconsumed=(consumed - (consumed/30 * diff));\n\t\t\tif(consumed<minimum)\n\t\t\t\tconsumed=minimum;\n\t\t\tif((overrideMana()>=0) && (CMProps.getManaCostExceptionObject(ID()) == null))\n\t\t\t\tconsumed=overrideMana();\n\t\t\tif((costOverrides!=null)&&(costOverrides[AbilityMapper.AbilCostType.MANA.ordinal()]!=null))\n\t\t\t{\n\t\t\t\tconsumed=costOverrides[AbilityMapper.AbilCostType.MANA.ordinal()].intValue();\n\t\t\t\tif((consumed<minimum)&&(consumed>=0))\n\t\t\t\t\tminimum=consumed;\n\t\t\t}\n\t\t}\n\t\tfinal int[] usageCost=buildCostArray(mob,consumed,minimum);\n\t\tif(rebuildCache)\n\t\t{\n\t\t\tif(consumed > COST_PCT-1)\n\t\t\t\tabilityUsageCache[myCacheIndex]=new int[]{consumed,minimum};\n\t\t\telse\n\t\t\t\tabilityUsageCache[myCacheIndex]=usageCost;\n\t\t}\n\t\tif(mob == null)\n\t\t\toverrideCache.put(ID(), usageCost);\n\t\treturn usageCost;\n\t}\n\n\t@Override\n\tpublic void helpProficiency(final MOB mob, final int adjustment)\n\t{\n\t\tif(mob==null)\n\t\t\treturn;\n\t\tfinal Ability A=mob.fetchAbility(ID());\n\t\tif(A==null)\n\t\t\treturn;\n\n\t\tif(mob.isPlayer())\n\t\t{\n\t\t\tCMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_SKILLUSE);\n\t\t\tCMLib.achievements().possiblyBumpAchievement(mob, AchievementLibrary.Event.SKILLUSE, 1, this);\n\t\t}\n\n\t\tif(!A.isSavable())\n\t\t\treturn;\n\n\t\tif((System.currentTimeMillis()-((StdAbility)A).lastCastHelp)<300000)\n\t\t\treturn;\n\n\t\tif(!A.appropriateToMyFactions(mob))\n\t\t\treturn;\n\n\t\tfinal int maxProficiency = CMLib.ableMapper().getMaxProficiency(mob,true,ID());\n\t\tif(A.proficiency()< maxProficiency)\n\t\t{\n\t\t\tfinal int currentProficiency=A.proficiency()+adjustment;\n\t\t\tif(((int)Math.round(Math.sqrt((mob.charStats().getStat(CharStats.STAT_INTELLIGENCE)))*34.0*Math.random()))>=currentProficiency)\n\t\t\t{\n\t\t\t\tfinal int qualLevel=CMLib.ableMapper().qualifyingLevel(mob,A);\n\t\t\t\tfinal double adjustedChance;\n\t\t\t\tif(qualLevel<0)\n\t\t\t\t\tadjustedChance=100.1;\n\t\t\t\telse\n\t\t\t\tif(qualLevel>30)\n\t\t\t\t{\n\t\t\t\t\tfinal float fatigueFactor=(mob.curState().getFatigue() > CharState.FATIGUED_MILLIS ? 50.0f : 100.0f);\n\t\t\t\t\tfinal int maxLevel=CMProps.get(mob.session()).getInt(CMProps.Int.LASTPLAYERLEVEL);\n\t\t\t\t\tadjustedChance=fatigueFactor * CMath.div((maxLevel+1-qualLevel),((2*maxLevel)+(10*qualLevel)));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfinal float fatigueFactor=(mob.curState().getFatigue() > CharState.FATIGUED_MILLIS ? 50.0f : 100.0f);\n\t\t\t\t\tfinal int maxLevel=CMProps.get(mob.session()).getInt(CMProps.Int.LASTPLAYERLEVEL);\n\t\t\t\t\tadjustedChance=fatigueFactor * CMath.div((maxLevel+1-qualLevel),((2*maxLevel)+(10*qualLevel)));\n\t\t\t\t}\n\t\t\t\tif(CMLib.dice().rollPercentage()<Math.round(adjustedChance))\n\t\t\t\t{\n\t\t\t\t\t// very important, since these can be autoinvoked affects (copies)!\n\t\t\t\t\tA.setProficiency(A.proficiency()+1);\n\t\t\t\t\tif((this!=A)&&(proficiency()<maxProficiency))\n\t\t\t\t\t\tsetProficiency(A.proficiency());\n\t\t\t\t\tfinal Ability effA=mob.fetchEffect(ID());\n\t\t\t\t\tif((effA!=null) && (effA!=A) && (effA!=this)\n\t\t\t\t\t&&(effA.invoker()==mob)\n\t\t\t\t\t&&(effA.proficiency()<maxProficiency))\n\t\t\t\t\t\teffA.setProficiency(A.proficiency());\n\t\t\t\t\tif(mob.isAttributeSet(MOB.Attrib.AUTOIMPROVE))\n\t\t\t\t\t\tmob.tell(L("You become better at @x1.",A.name()));\n\t\t\t\t\t((StdAbility)A).lastCastHelp=System.currentTimeMillis();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif((A.proficiency() >= maxProficiency)\n\t\t\t&&(mob.isPlayer()))\n\t\t\t{\n\t\t\t\tfinal List<String> channels = CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.PROFICIENT, mob);\n\t\t\t\tfor (int i = 0; i < channels.size(); i++)\n\t\t\t\t\tCMLib.commands().postChannel(channels.get(i), mob.clans(), L("@x1 is now proficient at @x2.", mob.name(), A.Name()), true);\n\t\t\t\tCMLib.achievements().possiblyBumpAchievement(mob, AchievementLibrary.Event.SKILLPROF, 1, A);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tA.setProficiency(maxProficiency);\n\t}\n\n\tprotected boolean failureTell(final MOB mob, final MOB targetM, final boolean auto, final String msg)\n\t{\n\t\tif(mob==null)\n\t\t\treturn false;\n\t\tif(auto\n\t\t&&(mob.isMonster())\n\t\t&&(targetM!=null)\n\t\t&&(targetM.isPlayer()))\n\t\t\ttargetM.tell(targetM,null,null,msg);\n\t\telse\n\t\t\tmob.tell(targetM,null,null,msg);\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean preInvoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel, final int secondsElapsed, final double actionsRemaining)\n\t{\n\t\tif(mob.commandQueSize() > 1)\n\t\t{\n\t\t\tmob.tell(L("You cancel @x1.",name()));\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean invoke(final MOB mob, final Physical target, final boolean auto, final int asLevel)\n\t{\n\t\tfinal Vector<String> V=new Vector<String>(1);\n\t\tif(target!=null)\n\t\t\tV.addElement(target.name());\n\t\treturn invoke(mob,V,target,auto,asLevel);\n\t}\n\n\tprotected boolean testUsageCost(final MOB mob, final boolean auto, final int[] consumed, final boolean quiet)\n\t{\n\t\tif(mob.curState().getMana()<consumed[Ability.USAGEINDEX_MANA])\n\t\t{\n\t\t\tif(!quiet)\n\t\t\t{\n\t\t\t\tif(mob.maxState().getMana()==consumed[Ability.USAGEINDEX_MANA])\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You must be at full mana to do that."));\n\t\t\t\telse\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You don\'t have enough mana to do that."));\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif(mob.curState().getMovement()<consumed[Ability.USAGEINDEX_MOVEMENT])\n\t\t{\n\t\t\tif(!quiet)\n\t\t\t{\n\t\t\t\tif(mob.maxState().getMovement()==consumed[Ability.USAGEINDEX_MOVEMENT])\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You must be at full movement to do that."));\n\t\t\t\telse\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You don\'t have enough movement to do that.  You are too tired."));\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif(mob.curState().getHitPoints()<consumed[Ability.USAGEINDEX_HITPOINTS])\n\t\t{\n\t\t\tif(!quiet)\n\t\t\t{\n\t\t\t\tif(mob.maxState().getHitPoints()==consumed[Ability.USAGEINDEX_HITPOINTS])\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You must be at full health to do that."));\n\t\t\t\telse\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You don\'t have enough hit points to do that."));\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean invoke(final MOB mob, final List<String> commands, final Physical target, final boolean auto, final int asLevel)\n\t{\n\t\t//expertiseCache=null; // this was insane!\n\t\tif((mob!=null)&&(getXMAXRANGELevel(mob)>0)) // wut? this must be preventing an npe in some edge case...\n\t\t\tinvoker=mob;\n\t\tif((!auto)&&(mob!=null))\n\t\t{\n\t\t\tisAnAutoEffect=false;\n\n\t\t\t// if you can\'t move, you can\'t cast! Not even verbal!\n\t\t\tif(!CMLib.flags().isAliveAwakeMobile(mob,false))\n\t\t\t\treturn false;\n\n\t\t\tfinal Room room=mob.location();\n\t\t\tif((getTicksBetweenCasts()>0)\n\t\t\t&&(getTimeOfNextCast()>0)\n\t\t\t&&(System.currentTimeMillis()<getTimeOfNextCast())\n\t\t\t&&(room!=null)\n\t\t\t&&(room.getArea()!=null)\n\t\t\t&&(!CMSecurity.isAllowed(mob, room, CMSecurity.SecFlag.ALLSKILLS)))\n\t\t\t{\n\t\t\t\tfinal TimeClock C=room.getArea().getTimeObj();\n\t\t\t\tif(C!=null)\n\t\t\t\t\tfailureTell(mob,mob,auto,L("You must wait @x1 before you can do that again.",C.deriveEllapsedTimeString(getTimeOfNextCast()-System.currentTimeMillis())));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif(CMath.bset(usageType(),Ability.USAGE_MOVEMENT)\n\t\t\t&&(CMLib.flags().isBound(mob)))\n\t\t\t{\n\t\t\t\tfailureTell(mob,mob,auto,L("You are bound!"));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint[] consumed=usageCost(mob,false);\n\t\t\tfinal int[] timeCache;\n\t\t\tfinal int nowLSW = (int)(System.currentTimeMillis()&0x7FFFFFFF);\n\t\t\tfinal AbilityMapper.CompoundingRule rule = CMLib.ableMapper().getCompoundingRule(mob, this);\n\t\t\tif((rule!=null)\n\t\t\t&&(rule.compoundingTicks() > 0)\n\t\t\t&&(consumed != STATIC_USAGE_NADA)\n\t\t\t&&(overrideMana()<Integer.MAX_VALUE-51))\n\t\t\t{\n\t\t\t\tfinal int[][] abilityUsageCache=mob.getAbilityUsageCache(ID());\n\t\t\t\tif(abilityUsageCache[Ability.CACHEINDEX_LASTTIME] == null)\n\t\t\t\t\tabilityUsageCache[Ability.CACHEINDEX_LASTTIME] = new int[USAGEINDEX_TOTAL];\n\t\t\t\ttimeCache = abilityUsageCache[Ability.CACHEINDEX_LASTTIME];\n\t\t\t\tif(timeCache[USAGEINDEX_TIMELSW]>nowLSW)\n\t\t\t\t\ttimeCache[USAGEINDEX_TIMELSW]=0;\n\t\t\t\tfinal int numTicksSinceLastCast=(int)((nowLSW-timeCache[USAGEINDEX_TIMELSW]) / CMProps.getTickMillis());\n\t\t\t\tif((numTicksSinceLastCast >= rule.compoundingTicks())||(ignoreCompounding()))\n\t\t\t\t\ttimeCache[USAGEINDEX_COUNT]=0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconsumed=Arrays.copyOf(consumed, consumed.length);\n\t\t\t\t\tfinal double pctPenalty = rule.pctPenalty();\n\t\t\t\t\tfinal double amtPenalty = rule.amtPenalty();\n\t\t\t\t\tif(amtPenalty < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tfailureTell(mob,mob,auto,L("You can\'t do that again just yet."));\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor(int usageIndex = 0 ; usageIndex < Ability.USAGEINDEX_TOTAL; usageIndex++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(consumed[usageIndex]>0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble newAmt=consumed[usageIndex];\n\t\t\t\t\t\t\t\tfor(int ct=0;ct<timeCache[USAGEINDEX_COUNT];ct++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif(newAmt<Short.MAX_VALUE)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tnewAmt+=amtPenalty;\n\t\t\t\t\t\t\t\t\t\tnewAmt+=CMath.mul(newAmt, pctPenalty);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconsumed[usageIndex]=(int)Math.round(Math.ceil(newAmt));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\ttimeCache=null;\n\n\t\t\tif(!testUsageCost(mob,false,consumed,auto))\n\t\t\t\treturn false;\n\n\t\t\tif((minCastWaitTime()>0)&&(lastCastHelp>0))\n\t\t\t{\n\t\t\t\tif((System.currentTimeMillis()-lastCastHelp)<minCastWaitTime())\n\t\t\t\t{\n\t\t\t\t\tif(minCastWaitTime()<=1000)\n\t\t\t\t\t\tfailureTell(mob,mob,auto,L("You need a second to recover before doing that again."));\n\t\t\t\t\telse\n\t\t\t\t\tif(minCastWaitTime()<=5000)\n\t\t\t\t\t\tfailureTell(mob,mob,auto,L("You need a few seconds to recover before doing that again."));\n\t\t\t\t\telse\n\t\t\t\t\t\tfailureTell(mob,mob,auto,L("You need awhile to recover before doing that again."));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!checkComponents(mob))\n\t\t\t\treturn false;\n\t\t\tif(timeCache!=null)\n\t\t\t{\n\t\t\t\ttimeCache[USAGEINDEX_COUNT]++;\n\t\t\t\ttimeCache[USAGEINDEX_TIMELSW]=nowLSW;\n\t\t\t}\n\t\t\tmob.curState().adjMana(-consumed[0],mob.maxState());\n\t\t\tmob.curState().adjMovement(-consumed[1],mob.maxState());\n\t\t\tmob.curState().adjHitPoints(-consumed[2],mob.maxState());\n\t\t\thelpProficiency(mob, 0);\n\t\t\tfinal CMMsg msg=CMClass.getMsg(mob,null,this,CMMsg.MSG_PREINVOKE, null);\n\t\t\tif(!mob.okMessage(mob, msg))\n\t\t\t\treturn false;\n\t\t\tmob.executeMsg(mob, msg);\n\t\t}\n\t\telse\n\t\t\tisAnAutoEffect=true;\n\t\treturn true;\n\t}\n\n\tprotected boolean checkComponents(final MOB mob)\n\t{\n\t\tif((mob!=null)\n\t\t&&(!CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.COMPONENTS))\n\t\t)\n\t\t{\n\t\t\tfinal Vector<AbilityComponent> componentsRequirements=(Vector<AbilityComponent>)CMLib.ableComponents().getAbilityComponentMap().get(ID().toUpperCase());\n\t\t\tif(componentsRequirements!=null)\n\t\t\t{\n\t\t\t\tfinal List<Object> components=CMLib.ableComponents().componentCheck(mob,componentsRequirements, false);\n\t\t\t\tif(components==null)\n\t\t\t\t{\n\t\t\t\t\tfailureTell(mob,mob,false,L("The requirements to use this @x1 are: @x2.",\n\t\t\t\t\t\t\tAbility.ACODE_DESCS[classificationCode()&Ability.ALL_ACODES].toLowerCase(),\n\t\t\t\t\t\t\tCMLib.ableComponents().getAbilityComponentDesc(mob,ID())));\n\t\t\t\t\tif(!mob.isPlayer())\n\t\t\t\t\t\tCMLib.ableComponents().startAbilityComponentTrigger(mob, this);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tCMLib.ableComponents().destroyAbilityComponents(components);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected Set<MOB> properTargets(final MOB mob, final Environmental givenTarget, final boolean auto)\n\t{\n\t\tSet<MOB> h=CMLib.combat().properTargets(this,mob,auto);\n\t\tif((givenTarget instanceof MOB)\n\t\t&&(CMLib.flags().isInTheGame((MOB)givenTarget,true)))\n\t\t{\n\t\t\tif(h==null)\n\t\t\t\th=new SHashSet<MOB>();\n\t\t\tif(!h.contains(givenTarget))\n\t\t\t\th.add((MOB)givenTarget);\n\t\t}\n\t\treturn h;\n\t}\n\n\tprotected List<MOB> properTargetList(final MOB mob, final Environmental givenTarget, final boolean auto)\n\t{\n\t\tfinal Set<MOB> h=properTargets(mob,givenTarget,auto);\n\t\tfinal List<MOB> list=new ArrayList<MOB>(h.size());\n\t\tif(h.contains(mob))\n\t\t{\n\t\t\th.remove(mob);\n\t\t\tlist.add(mob);\n\t\t}\n\t\tlist.addAll(h);\n\t\treturn list;\n\t}\n\n\tprotected int adjustMaliciousTickdownTime(final MOB mob, final Physical target, final int baseTicks, final int asLevel)\n\t{\n\t\tint tickDown = baseTicks;\n\t\tif((target!=null)\n\t\t&&(asLevel<=0)\n\t\t&&(mob!=null)\n\t\t&&(!(target instanceof Room)))\n\t\t{\n\t\t\tint levelDiff = target.phyStats().level()-adjustedLevel(mob,asLevel);\n\t\t\tfinal int expRate=CMProps.getIntVar(CMProps.Int.EXPRATE);\n\t\t\tif(baseTicks > Integer.MAX_VALUE/4)\n\t\t\t\tlevelDiff=0;\n\t\t\telse\n\t\t\tif(levelDiff > expRate)\n\t\t\t\tlevelDiff = expRate;\n\t\t\telse\n\t\t\tif(levelDiff < -expRate)\n\t\t\t\tlevelDiff = -expRate;\n\t\t\tfinal double levelTimeFudge = CMath.div(levelDiff, expRate+1);\n\t\t\ttickDown-=(int)Math.round(CMath.mul(tickDown,levelTimeFudge));\n\t\t\tif((tickDown>(CMProps.getTicksPerHour()/3))\n\t\t\t||(mob instanceof Deity))\n\t\t\t\ttickDown=(int)(CMProps.getTicksPerHour()/3);\n\t\t}\n\n\t\tif((tickDown>(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)))\n\t\t||(mob instanceof Deity))\n\t\t\ttickDown=(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY));\n\n\t\tif(tickDown<2)\n\t\t\ttickDown=2;\n\t\treturn tickDown;\n\t}\n\n\tprotected int getMaliciousTickdownTime(final MOB mob, final Physical target, final int tickAdjustmentFromStandard, final int asLevel)\n\t{\n\t\tif(tickAdjustmentFromStandard>0)\n\t\t\treturn tickAdjustmentFromStandard;\n\t\tfinal int tickTime = adjustMaliciousTickdownTime(mob,target,((int)Math.round(CMath.mul(adjustedLevel(mob,asLevel),1.1)))+25,asLevel);\n\t\tif(tickAdjustmentFromStandard == 0)\n\t\t\treturn tickTime;\n\t\telse\n\t\t\treturn (int)Math.round(CMath.mul(CMath.div(-tickAdjustmentFromStandard, 100.0) , (double)tickTime));\n\t}\n\n\tpublic Ability maliciousAffect(final MOB mob, final Physical target, final int asLevel, int tickAdjustmentFromStandard, final int additionAffectCheckCode)\n\t{\n\t\tfinal Room room=mob.location();\n\t\tif(room==null)\n\t\t\treturn null;\n\t\tif(additionAffectCheckCode>=0)\n\t\t{\n\t\t\tfinal CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.NO_EFFECT,additionAffectCheckCode,CMMsg.NO_EFFECT,null);\n\t\t\tif(room.okMessage(mob,msg))\n\t\t\t{\n\t\t\t\troom.send(mob,msg);\n\t\t\t\tif(msg.value()>0)\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn null;\n\t\t}\n\t\tinvoker=mob;\n\t\tfinal Ability newOne=(Ability)copyOf();\n\t\t((StdAbility)newOne).canBeUninvoked=true;\n\t\ttickAdjustmentFromStandard=getMaliciousTickdownTime(mob,target,tickAdjustmentFromStandard,asLevel);\n\t\tnewOne.startTickDown(invoker,target,tickAdjustmentFromStandard);\n\t\treturn newOne;\n\t}\n\n\tprotected boolean beneficialWordsFizzle(final MOB mob, final Environmental target, final String message)\n\t{\n\t\t// it didn\'t work, but tell everyone you tried.\n\t\tfinal CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MSG_SPEAK,"^T"+message+"^?");\n\t\tfinal Room room=mob.location();\n\t\tif(room==null)\n\t\t\treturn false;\n\t\tif(room.okMessage(mob,msg))\n\t\t\troom.send(mob,msg);\n\t\treturn false;\n\t}\n\n\tprotected boolean beneficialVisualFizzle(final MOB mob, final Environmental target, final String message)\n\t{\n\t\t// it didn\'t work, but tell everyone you tried.\n\t\tfinal CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MSG_OK_VISUAL,message);\n\t\tfinal Room room=mob.location();\n\t\tif(room==null)\n\t\t\treturn false;\n\t\tif(room.okMessage(mob,msg))\n\t\t\troom.send(mob,msg);\n\n\t\treturn false;\n\t}\n\n\tprotected boolean maliciousFizzle(final MOB mob, final Environmental target, final String message)\n\t{\n\t\t// it didn\'t work, but tell everyone you tried.\n\t\tfinal String targetMessage;\n\t\tif((target instanceof MOB)\n\t\t&&(mob!=target)\n\t\t&&(((MOB)target).isAttributeSet(Attrib.NOBATTLESPAM)))\n\t\t\ttargetMessage=null;\n\t\telse\n\t\t\ttargetMessage=message;\n\t\tfinal CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MSG_OK_VISUAL|CMMsg.MASK_MALICIOUS,message,targetMessage,message);\n\t\tfinal Room room=mob.location();\n\t\tif(room==null)\n\t\t\treturn false;\n\t\tCMLib.color().fixSourceFightColor(msg);\n\t\tif(room.okMessage(mob,msg))\n\t\t\troom.send(mob,msg);\n\t\treturn false;\n\t}\n\n\tprotected int adjustBeneficialTickdownTime(final MOB mob, final Environmental target, final int baseTicks)\n\t{\n\t\tint tickDown = baseTicks;\n\t\tif((tickDown>(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)))\n\t\t||(mob instanceof Deity))\n\t\t\ttickDown=(CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY));\n\t\tif(tickDown<5)\n\t\t\ttickDown=5;\n\t\treturn tickDown;\n\t}\n\n\tprotected boolean aPossibleAbuserOfCasterLevel(final MOB casterM)\n\t{\n\t\tif(casterM.isPlayer())\n\t\t\treturn true;\n\t\tfinal Room R=casterM.location();\n\t\tif(R==null)\n\t\t\treturn false;\n\t\tif(casterM instanceof Deity)\n\t\t\treturn false;\n\t\tfinal MOB folM=casterM.amUltimatelyFollowing();\n\t\tif((folM!=null)&&(folM.isPlayer()))\n\t\t\treturn true;\n\t\t/* too much\n\t\tfor(final Enumeration<MOB> m=R.inhabitants();m.hasMoreElements();)\n\t\t{\n\t\t\tfinal MOB M=m.nextElement();\n\t\t\tif((M!=null)\n\t\t\t&&(M!=casterM)\n\t\t\t&&(M.isPlayer())\n\t\t\t&&(casterM.willFollowOrdersOf(M)))\n\t\t\t\treturn true;\n\t\t}\n\t\t*/\n\t\treturn false;\n\t}\n\n\tprotected int getBeneficialTickdownTime(final MOB mob, final Environmental target, final int tickAdjustmentFromStandard, final int asLevel)\n\t{\n\t\tif(tickAdjustmentFromStandard>0)\n\t\t\treturn tickAdjustmentFromStandard;\n\t\tint casterLevel = adjustedLevel(mob,asLevel);\n\t\tif((mob != target)\n\t\t&&(target instanceof MOB)\n\t\t&&(aPossibleAbuserOfCasterLevel(mob)))\n\t\t{\n\t\t\tfinal int levelCap = ((MOB)target).phyStats().level() + CMProps.getIntVar(CMProps.Int.EXPRATE);\n\t\t\tif(casterLevel >  levelCap)\n\t\t\t\tcasterLevel = levelCap;\n\t\t}\n\t\tfinal int beneficialTicks = (casterLevel*4)+40;\n\t\tfinal int tickTime = adjustBeneficialTickdownTime(mob,target,beneficialTicks);\n\t\tif(tickAdjustmentFromStandard==0)\n\t\t\treturn tickTime;\n\t\telse\n\t\t\treturn (int)Math.round(CMath.mul(CMath.div(-tickAdjustmentFromStandard, 100.0) , (double)tickTime));\n\t}\n\n\tpublic Ability beneficialAffect(final MOB mob, final Physical target, final int asLevel, int tickAdjustmentFromStandard)\n\t{\n\t\tinvoker=mob;\n\t\tfinal Ability newOne=(Ability)this.copyOf();\n\t\t((StdAbility)newOne).canBeUninvoked=true;\n\t\ttickAdjustmentFromStandard=getBeneficialTickdownTime(mob,target,tickAdjustmentFromStandard,asLevel);\n\t\tnewOne.startTickDown(invoker,target,tickAdjustmentFromStandard);\n\t\treturn newOne;\n\t}\n\n\tprotected void spreadImmunity(final MOB mob)\n\t{\n\t\tif((mob==null)||(mob.fetchEffect(ID())!=null))\n\t\t\treturn;\n\t\tAbility A=mob.fetchEffect("TemporaryImmunity");\n\t\tif(A==null)\n\t\t{\n\t\t\tA=CMClass.getAbility("TemporaryImmunity");\n\t\t\tA.setSavable(false);\n\t\t\tA.makeLongLasting();\n\t\t\tmob.addEffect(A);\n\t\t\tA.makeLongLasting();\n\t\t}\n\t\tA.setMiscText("+"+ID());\n\t}\n\n\t@Override\n\tpublic boolean autoInvocation(final MOB mob, final boolean force)\n\t{\n\t\tif(isAutoInvoked())\n\t\t{\n\t\t\tif(!force)\n\t\t\t{\n\t\t\t\tfinal PlayerStats pStats = mob.playerStats();\n\t\t\t\tif((pStats != null) && (pStats.isOnAutoInvokeList(ID())))\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfinal Ability thisAbility=mob.fetchEffect(ID());\n\t\t\tif(thisAbility!=null)\n\t\t\t\treturn false;\n\t\t\tfinal StdAbility thatAbility=(StdAbility)copyOf();\n\t\t\tthatAbility.canBeUninvoked=true;\n\t\t\tthatAbility.setSavable(false);\n\t\t\tthatAbility.setInvoker(mob);\n\t\t\tthatAbility.isAnAutoEffect=true;\n\t\t\tmob.addEffect(thatAbility);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void makeNonUninvokable()\n\t{\n\t\tunInvoked=false;\n\t\tcanBeUninvoked=false;\n\t\tsavable=true;\n\t}\n\n\t@Override\n\tpublic String accountForYourself()\n\t{\n\t\treturn name();\n\t}\n\n\tpublic int getTickDownRemaining()\n\t{\n\t\treturn tickDown;\n\t}\n\n\tpublic void setTickDownRemaining(final int newTick)\n\t{\n\t\ttickDown=newTick;\n\t}\n\n\t@Override\n\tpublic int getTickStatus()\n\t{\n\t\treturn Tickable.STATUS_NOT;\n\t}\n\n\t@Override\n\tpublic boolean canBeTaughtBy(final MOB teacher, final MOB student)\n\t{\n\t\tif(teacher.isAttributeSet(MOB.Attrib.NOTEACH))\n\t\t{\n\t\t\tteacher.tell(L("You are refusing to teach right now."));\n\t\t\tstudent.tell(L("@x1 is refusing to teach right now.",teacher.name()));\n\t\t\treturn false;\n\t\t}\n\t\tif(CMLib.flags().isSleeping(teacher)||CMLib.flags().isSitting(teacher))\n\t\t{\n\t\t\tteacher.tell(L("You need to stand up to teach."));\n\t\t\tstudent.tell(L("@x1 needs to stand up to teach.",teacher.name()));\n\t\t\treturn false;\n\t\t}\n\t\tif(teacher.isInCombat())\n\t\t{\n\t\t\tstudent.tell(L("@x1 seems busy right now.",teacher.name()));\n\t\t\tteacher.tell(L("Not while you are fighting!"));\n\t\t\treturn false;\n\t\t}\n\t\tfinal Ability yourAbility=teacher.fetchAbility(ID());\n\t\tif(yourAbility!=null)\n\t\t{\n\t\t\tfinal int prof25=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,yourAbility.ID()), .25));\n\t\t\tif(yourAbility.proficiency()<prof25)\n\t\t\t{\n\t\t\t\tteacher.tell(L("You are not proficient enough to teach \'@x1\'",name()));\n\t\t\t\tstudent.tell(L("@x1 is not proficient enough to teach \'@x2\'.",teacher.name(),name()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tteacher.tell(L("You don\'t know \'@x1\'.",name()));\n\t\tstudent.tell(L("@x1 doesn\'t know \'@x2\'.",teacher.name(),name()));\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String requirements(final MOB mob)\n\t{\n\t\tfinal CostManager cost=getTrainingCost(mob);\n\t\treturn cost.requirements(mob);\n\t}\n\n\t@Override\n\tpublic boolean canBeLearnedBy(final MOB teacher, final MOB student)\n\t{\n\t\tfinal CostManager cost=getTrainingCost(student);\n\t\tif(!cost.doesMeetCostRequirements(student))\n\t\t{\n\t\t\tfinal String ofWhat=cost.costType(student);\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 does not have enough @x2 to learn \'@x3\'.",student.name(),ofWhat,name()));\n\t\t\tstudent.tell(L("You do not have enough @x1.",ofWhat));\n\t\t\treturn false;\n\t\t}\n\t\tif((student.isAttributeSet(MOB.Attrib.NOTEACH))\n\t\t&&((!student.isMonster())||(!student.willFollowOrdersOf(teacher))))\n\t\t{\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 is refusing training at this time.",student.name()));\n\t\t\tstudent.tell(L("You are refusing training at this time."));\n\t\t\treturn false;\n\t\t}\n\t\tfinal int qLevel=CMLib.ableMapper().qualifyingLevel(student,this);\n\t\tif(qLevel<0)\n\t\t{\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 is not the right class to learn \'@x2\'.",student.name(),name()));\n\t\t\tstudent.tell(L("You are not the right class to learn \'@x1\'.",name()));\n\t\t\treturn false;\n\t\t}\n\t\tif((!student.charStats().getCurrentClass().leveless())\n\t\t&&(!CMLib.ableMapper().qualifiesByLevel(student,this))\n\t\t&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.LEVELS)))\n\t\t{\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 is not high enough level to learn \'@x2\'.",student.name(),name()));\n\t\t\tstudent.tell(L("You are not high enough level to learn \'@x1\'.",name()));\n\t\t\treturn false;\n\t\t}\n\t\tif(student.charStats().getStat(CharStats.STAT_INTELLIGENCE)<2)\n\t\t{\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 is too stupid to learn new skills.",student.name()));\n\t\t\tstudent.tell(L("You are too stupid to learn new skills."));\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal String qualClassID = CMLib.ableMapper().qualifyingID(student, this);\n\t\tif((qualClassID != null) && (qualClassID.equalsIgnoreCase("All")||(CMClass.getCharClass(qualClassID) != null)))\n\t\t{\n\t\t\tint highestSkillLevel=-1;\n\t\t\tfor(final AbilityMapping mA : CMLib.ableMapper().getAbleMapping(qualClassID).values())\n\t\t\t{\n\t\t\t\tif(mA.qualLevel() > highestSkillLevel)\n\t\t\t\t\thighestSkillLevel = mA.qualLevel();\n\t\t\t}\n\t\t\tif(highestSkillLevel > 0)\n\t\t\t{\n\t\t\t\tfinal int baseInt = CMProps.getIntVar(CMProps.Int.BASEMAXSTAT); // 18\n\t\t\t\tfinal int normalizedReqInt = (int)Math.round(Math.ceil(CMath.mul(baseInt, CMath.div(qLevel, highestSkillLevel))));\n\t\t\t\tfinal int studentInt=student.charStats().getStat(CharStats.STAT_INTELLIGENCE);\n\t\t\t\tif(studentInt + 6 < normalizedReqInt)\n\t\t\t\t{\n\t\t\t\t\tif(teacher != null)\n\t\t\t\t\t\tteacher.tell(L("@x1 is not smart enough to learn level @x2 skills.",student.name(),qLevel+""));\n\t\t\t\t\tstudent.tell(L("You are not of high enough intelligence to learn level @x1 skills.",qLevel+""));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfinal Ability yourAbility=student.fetchAbility(ID());\n\t\tfinal Ability teacherAbility=(teacher != null) ? teacher.fetchAbility(ID()) : null;\n\t\tif(yourAbility!=null)\n\t\t{\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 already knows \'@x2\'.",student.name(),name()));\n\t\t\tstudent.tell(L("You already know \'@x1\'.",name()));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(teacher != null)\n\t\t{\n\t\t\tif(teacherAbility!=null)\n\t\t\t{\n\t\t\t\tfinal int prof25=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,teacherAbility.ID()), .25));\n\t\t\t\tif(teacherAbility.proficiency()<prof25)\n\t\t\t\t{\n\t\t\t\t\tteacher.tell(L("You aren\'t proficient enough to teach \'@x1\'.",name()));\n\t\t\t\t\tstudent.tell(L("@x1 isn\'t proficient enough to teach you \'@x2\'.",teacher.name(),name()));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstudent.tell(L("@x1 does not know anything about that.",teacher.name()));\n\t\t\t\tteacher.tell(L("You don\'t know that."));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif(student.isInCombat())\n\t\t{\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 seems busy right now.",student.name()));\n\t\t\tstudent.tell(L("Not while you are fighting!"));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(CMLib.flags().isSleeping(student)||CMLib.flags().isSitting(student))\n\t\t{\n\t\t\tstudent.tell(L("You need to stand up and be alert to learn."));\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 needs to stand up to be taught about that.",student.name()));\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal String extraMask=CMLib.ableMapper().getApplicableMask(student,this);\n\t\tif((extraMask.length()>0)&&(!CMLib.masking().maskCheck(extraMask,student,true)))\n\t\t{\n\t\t\tfinal String reason="requirements: "+CMLib.masking().maskDesc(extraMask);\n\t\t\tstudent.tell(L("You may not learn \'@x1\' at this time due to the @x2",name(),reason));\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 does not fit the \'@x2\' @x3",student.name(),name(),reason));\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal DVector prereqs=CMLib.ableMapper().getUnmetPreRequisites(student,this);\n\t\tif((prereqs!=null)&&(prereqs.size()>0))\n\t\t{\n\t\t\tfinal String names=CMLib.ableMapper().formatPreRequisites(prereqs);\n\t\t\tstudent.tell(L("You must learn @x1 before you can gain @x2.",names,name()));\n\t\t\tif(teacher != null)\n\t\t\t\tteacher.tell(L("@x1 has not learned the pre-requisites to @x2 yet.",student.name(),name()));\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprotected Map<MOB,MOB> saveCombatState(final MOB mob, final boolean andFollowers)\n\t{\n\t\tfinal Map<MOB,MOB> map = new TreeMap<MOB,MOB>();\n\t\tfinal HashSet<MOB> fols = new HashSet<MOB>();\n\t\tfols.add(mob);\n\t\tif(andFollowers)\n\t\t\tmob.getGroupMembers(fols);\n\t\tfor(final MOB M : fols)\n\t\t\tmap.put(M,M.getVictim());\n\t\treturn map;\n\t}\n\n\tprotected void restoreCombatState(final Map<MOB,MOB> map)\n\t{\n\t\tfor(final MOB M : map.keySet())\n\t\t{\n\t\t\tM.setVictim(map.get(M));\n\t\t}\n\t}\n\n\tprotected int verbalCastCode(final MOB mob, final Physical target, final boolean auto)\n\t{\n\t\tint affectType=CMMsg.MSG_CAST_VERBAL_SPELL;\n\t\tif(castingQuality(mob,target)==Ability.QUALITY_MALICIOUS)\n\t\t\taffectType=CMMsg.MSG_CAST_ATTACK_VERBAL_SPELL;\n\t\tif(auto)\n\t\t\taffectType=affectType|CMMsg.MASK_ALWAYS;\n\t\treturn affectType;\n\t}\n\n\tprotected int verbalSpeakCode(final MOB mob, final Physical target, final boolean auto)\n\t{\n\t\tint affectType=CMMsg.MSG_NOISE|CMMsg.MASK_MOUTH;\n\t\tif(castingQuality(mob,target)==Ability.QUALITY_MALICIOUS)\n\t\t\taffectType=CMMsg.MSG_NOISE|CMMsg.MASK_MOUTH|CMMsg.MASK_MALICIOUS;\n\t\tif(auto)\n\t\t\taffectType=affectType|CMMsg.MASK_ALWAYS;\n\t\treturn affectType;\n\t}\n\n\tprotected int verbalCastMask(final MOB mob,final Physical target, final boolean auto)\n\t{\n\t\treturn verbalCastCode(mob,target,auto)&CMMsg.MAJOR_MASK;\n\t}\n\n\tprotected int somanticCastCode(final MOB mob, final Physical target, final boolean auto)\n\t{\n\t\tint affectType=CMMsg.MSG_CAST_SOMANTIC_SPELL;\n\t\tif(castingQuality(mob,target)==Ability.QUALITY_MALICIOUS)\n\t\t\taffectType=CMMsg.MSG_CAST_ATTACK_SOMANTIC_SPELL;\n\t\tif(auto)\n\t\t\taffectType=affectType|CMMsg.MASK_ALWAYS;\n\t\treturn affectType;\n\t}\n\n\tprotected int somanticCastMask(final MOB mob, final Physical target, final boolean auto)\n\t{\n\t\treturn somanticCastCode(mob,target,auto)&CMMsg.MAJOR_MASK;\n\t}\n\n\t@Override\n\tpublic boolean canBePracticedBy(final MOB teacher, final MOB student)\n\t{\n\t\tif((practicesToPractice(student)>0)&&(student.getPractices()<practicesToPractice(student)))\n\t\t{\n\t\t\tteacher.tell(L("@x1 does not have enough practices to practice \'@x2\'.",student.name(),name()));\n\t\t\tstudent.tell(L("You do not have enough practices."));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(teacher.isAttributeSet(MOB.Attrib.NOTEACH))\n\t\t{\n\t\t\tteacher.tell(L("You are refusing to teach right now."));\n\t\t\tstudent.tell(L("@x1 is refusing to teach right now.",teacher.name()));\n\t\t\treturn false;\n\t\t}\n\t\tif((student.isAttributeSet(MOB.Attrib.NOTEACH))\n\t\t&&((!student.isMonster())||(!student.willFollowOrdersOf(teacher))))\n\t\t{\n\t\t\tteacher.tell(L("@x1 is refusing training at this time.",student.name()));\n\t\t\tstudent.tell(L("You are refusing training at this time."));\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal Ability yourAbility=student.fetchAbility(ID());\n\t\tfinal Ability teacherAbility=teacher.fetchAbility(ID());\n\t\tif(yourAbility==null)\n\t\t{\n\t\t\tteacher.tell(L("@x1 has not gained \'@x2\' yet.",student.name(),name()));\n\t\t\tstudent.tell(L("You havn\'t gained \'@x1\' yet.",name()));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(teacherAbility==null)\n\t\t{\n\t\t\tstudent.tell(L("@x1 does not know anything about \'@x2\'.",teacher.name(),name()));\n\t\t\tteacher.tell(L("You don\'t know \'@x1\'.",name()));\n\t\t\treturn false;\n\t\t}\n\n\t\tif(yourAbility.proficiency()>=teacherAbility.proficiency())\n\t\t{\n\t\t\tteacher.tell(L("You aren\'t proficient enough to teach any more about \'@x1\'.",name()));\n\t\t\tstudent.tell(L("@x1 isn\'t proficient enough to teach any more about \'@x2\'.",teacher.name(),name()));\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfinal int prof75=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,yourAbility.ID()), .75));\n\t\t\tif(yourAbility.proficiency()>prof75-1)\n\t\t\t{\n\t\t\t\tteacher.tell(L("You can\'t teach @x1 any more about \'@x2\'.",student.charStats().himher(),name()));\n\t\t\t\tstudent.tell(L("You can\'t learn any more about \'@x1\' except through dilligence.",name()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfinal int prof25=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,teacherAbility.ID()), .25));\n\t\tif(teacherAbility.proficiency()<prof25)\n\t\t{\n\t\t\tteacher.tell(L("You aren\'t proficient enough to teach \'@x1\'.",name()));\n\t\t\tstudent.tell(L("@x1 isn\'t proficient enough to teach you \'@x2\'.",teacher.name(),name()));\n\t\t\treturn false;\n\t\t}\n\t\tif(CMLib.flags().isSleeping(student)||CMLib.flags().isSitting(student))\n\t\t{\n\t\t\tstudent.tell(L("You need to stand up to practice."));\n\t\t\tteacher.tell(L("@x1 needs to stand up to practice that.",student.name()));\n\t\t\treturn false;\n\t\t}\n\t\tif(student.isInCombat())\n\t\t{\n\t\t\tteacher.tell(L("@x1 seems busy right now.",student.name()));\n\t\t\tstudent.tell(L("Not while you are fighting!"));\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void teach(final MOB teacher, final MOB student)\n\t{\n\t\tif(student.fetchAbility(ID())==null)\n\t\t{\n\t\t\tfinal CostManager cost=getTrainingCost(student);\n\t\t\tif(!cost.doesMeetCostRequirements(student))\n\t\t\t\treturn;\n\t\t\tcost.doSpend(student);\n\t\t\tfinal Ability newAbility=(Ability)newInstance();\n\t\t\tfinal int prof75=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,newAbility.ID()), .75));\n\t\t\tnewAbility.setProficiency((int)Math.round(CMath.mul(proficiency(),((CMath.div(teacher.charStats().getStat(CharStats.STAT_WISDOM)+student.charStats().getStat(CharStats.STAT_INTELLIGENCE),100.0))))));\n\t\t\tif(newAbility.proficiency()>prof75)\n\t\t\t\tnewAbility.setProficiency(prof75);\n\t\t\tfinal int defProficiency=CMLib.ableMapper().getDefaultProficiency(student.charStats().getCurrentClass().ID(),true,ID());\n\t\t\tif((defProficiency>0)&&(defProficiency>newAbility.proficiency()))\n\t\t\t\tnewAbility.setProficiency(defProficiency);\n\t\t\tfinal String defParms=CMLib.ableMapper().getDefaultParm(student.charStats().getCurrentClass().ID(), true, ID());\n\t\t\tnewAbility.setMiscText(defParms);\n\t\t\tstudent.addAbility(newAbility);\n\t\t\tnewAbility.autoInvocation(student, false);\n\t\t}\n\t\tstudent.recoverCharStats();\n\t\tstudent.recoverPhyStats();\n\t\tstudent.recoverMaxState();\n\t}\n\n\t@Override\n\tpublic void unlearn(final MOB student)\n\t{\n\t\tif(student == null)\n\t\t\treturn;\n\t\tfinal Ability A=student.fetchAbility(ID());\n\t\tif(A==this)\n\t\t\tstudent.delAbility(this);\n\t\tfinal Ability eA=student.fetchEffect(ID());\n\t\tif(eA!=null)\n\t\t{\n\t\t\teA.unInvoke();\n\t\t\tstudent.delEffect(eA);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void practice(final MOB teacher, final MOB student)\n\t{\n\t\tif(student.getPractices()<practicesToPractice(student))\n\t\t\treturn;\n\n\t\tfinal Ability yourAbility=student.fetchAbility(ID());\n\t\tif(yourAbility!=null)\n\t\t{\n\t\t\tfinal Ability teachAbility=teacher.fetchAbility(ID());\n\t\t\tfinal int prof75=(int)Math.round(CMath.mul(CMLib.ableMapper().getMaxProficiency(student,true,yourAbility.ID()), .75));\n\t\t\tif(yourAbility.proficiency()<prof75)\n\t\t\t{\n\t\t\t\tstudent.setPractices(student.getPractices()-practicesToPractice(student));\n\t\t\t\tfinal int wisint = teacher.charStats().getStat(CharStats.STAT_WISDOM)\n\t\t\t\t\t\t\t\t + student.charStats().getStat(CharStats.STAT_INTELLIGENCE);\n\t\t\t\tint newProf = yourAbility.proficiency()+(int)Math.round(25.0*(CMath.div(wisint,36.0)));\n\t\t\t\tif(newProf > prof75)\n\t\t\t\t\tnewProf=prof75;\n\t\t\t\tif((teachAbility!=null)&&(newProf > teachAbility.proficiency()))\n\t\t\t\t\tnewProf = teachAbility.proficiency();\n\t\t\t\tyourAbility.setProficiency(newProf);\n\t\t\t\tfinal Ability yourEffect=student.fetchEffect(ID());\n\t\t\t\tif((yourEffect!=null)&&(yourEffect.invoker()==student))\n\t\t\t\t\tyourEffect.setProficiency(yourAbility.proficiency());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void makeLongLasting()\n\t{\n\t\ttickDown=Integer.MAX_VALUE;\n\t}\n\n\t@Override\n\tpublic void executeMsg(final Environmental myHost, final CMMsg msg)\n\t{\n\t\treturn;\n\t}\n\n\t@Override\n\tpublic boolean okMessage(final Environmental myHost, final CMMsg msg)\n\t{\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean tick(final Tickable ticking, final int tickID)\n\t{\n\t\tif((unInvoked)&&(canBeUninvoked()))\n\t\t\treturn false;\n\n\t\tif((canBeUninvoked())\n\t\t&&(tickID==Tickable.TICKID_MOB)\n\t\t&&(tickDown!=Integer.MAX_VALUE))\n\t\t{\n\t\t\tif(tickDown<0)\n\t\t\t\treturn !unInvoked;\n\t\t\tif((--tickDown)<=0)\n\t\t\t{\n\t\t\t\ttickDown=-1;\n\t\t\t\tunInvoke();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean appropriateToMyFactions(final MOB mob)\n\t{\n\t\tif(mob == null)\n\t\t\treturn true;\n\t\tfor(final Enumeration<String> e=mob.factions();e.hasMoreElements();)\n\t\t{\n\t\t\tfinal String factionID=e.nextElement();\n\t\t\tfinal Faction F=CMLib.factions().getFaction(factionID);\n\t\t\tif((F!=null)&&F.hasUsage(this))\n\t\t\t\treturn F.canUse(mob,this);\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isGeneric()\n\t{\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getSaveStatIndex()\n\t{\n\t\treturn getStatCodes().length;\n\t}\n\n\tprivate static final String[]\tCODES\t\t\t= { "CLASS", "TEXT" };\n\tprivate static final String[]\tINTERNAL_CODES\t= { "TICKDOWN","LEVEL","ISANAUTOEFFECT","NAME","NEXTCAST","CANUNINVOKE" };\n\n\t@Override\n\tpublic String[] getStatCodes()\n\t{\n\t\treturn CODES;\n\t}\n\n\t@Override\n\tpublic boolean isStat(final String code)\n\t{\n\t\treturn CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0;\n\t}\n\n\tprotected int getCodeNum(final String code)\n\t{\n\t\tfor(int i=0;i<CODES.length;i++)\n\t\t{\n\t\t\tif(code.equalsIgnoreCase(CODES[i]))\n\t\t\t\treturn i;\n\t\t}\n\t\treturn -1;\n\t}\n\n\tprotected int getInternalCodeNum(final String code)\n\t{\n\t\tfor(int i=0;i<INTERNAL_CODES.length;i++)\n\t\t{\n\t\t\tif(code.equalsIgnoreCase(INTERNAL_CODES[i]))\n\t\t\t\treturn i;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic String getStat(final String code)\n\t{\n\t\tswitch(getCodeNum(code))\n\t\t{\n\t\tcase 0:\n\t\t\treturn ID();\n\t\tcase 1:\n\t\t\treturn text();\n\t\tdefault:\n\t\t\tswitch(getInternalCodeNum(code))\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\treturn Integer.toString(tickDown);\n\t\t\tcase 1:\n\t\t\t\treturn "0";\n\t\t\tcase 2:\n\t\t\t\treturn Boolean.toString(isAnAutoEffect);\n\t\t\tcase 3:\n\t\t\t\treturn name();\n\t\t\tcase 4:\n\t\t\t\treturn ""+this.getTimeOfNextCast();\n\t\t\tcase 5:\n\t\t\t\treturn ""+this.canBeUninvoked();\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn "";\n\t}\n\n\t@Override\n\tpublic void setStat(final String code, final String val)\n\t{\n\t\tswitch(getCodeNum(code))\n\t\t{\n\t\tcase 0:\n\t\t\treturn;\n\t\tcase 1:\n\t\t\tsetMiscText(val);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tswitch(getInternalCodeNum(code))\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\ttickDown = CMath.s_int(val);\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tisAnAutoEffect = CMath.s_bool(val);\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tif(CMath.isLong(val))\n\t\t\t\t\tsetTimeOfNextCast(CMath.s_long(val));\n\t\t\t\tbreak;\n\t\t\tcase 5:\n\t\t\t\tthis.canBeUninvoked = CMath.s_bool(val);\n\t\t\t\tthis.unInvoked=false;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean sameAs(final Environmental E)\n\t{\n\t\tif(!(E instanceof StdAbility))\n\t\t\treturn false;\n\t\tfinal String[] codes=getStatCodes();\n\t\tfor(int i=0;i<codes.length;i++)\n\t\t{\n\t\t\tif(!E.getStat(codes[i]).equals(getStat(codes[i])))\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n}\n', 'message': 'Added the ability to get reasonable mana costs w/o a mob.\n\ngit-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@20528 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'author_name': 'bozimmerman', 'lang': 'java'}