<NME> ColumbiaUniv.java
<BEF> package com.planet_ink.coffee_mud.Libraries;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.core.interfaces.CostDef.CostType;
import com.planet_ink.coffee_mud.Libraries.interfaces.ExpertiseLibrary.ExpertiseDefinition;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMSecurity.SecFlag;
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.Common.interfaces.Session.InputCallback;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;
import java.util.Map.Entry;

/*
   Copyright 2006-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 ColumbiaUniv extends StdLibrary implements ExpertiseLibrary
{
	@Override
	public String ID()
	{
		return "ColumbiaUniv";
	}

	protected SHashtable<String, ExpertiseDefinition>	completeEduMap		= new SHashtable<String, ExpertiseDefinition>();
	protected SHashtable<String, List<String>>			baseEduSetLists		= new SHashtable<String, List<String>>();

	@SuppressWarnings("unchecked")
	protected Map<String, String>[]		completeUsageMap	= new Hashtable[XType.values().length];
	@SuppressWarnings("unchecked")
	protected Map<String, String[]>[]	completeUsageMaps	= new Hashtable[XType.values().length];
	protected Map<String, String>		helpMap				= new TreeMap<String, String>();
	protected DVector					rawDefinitions		= new DVector(7);

	protected ExpertiseLibrary.ExpertiseDefinition addDefinition(final String ID, final String name, final String baseName, final String listMask, final String finalMask, final String[] costs, final String[] data)
	{
		ExpertiseLibrary.ExpertiseDefinition def=getDefinition(ID);
		if(def!=null)
			return  def;
		if(CMSecurity.isExpertiseDisabled(ID.toUpperCase()))
			return null;
		if(CMSecurity.isExpertiseDisabled("*"))
			return null;
		for(int i=1;i<ID.length();i++)
		{
			if(CMSecurity.isExpertiseDisabled(ID.substring(0,i).toUpperCase()+"*"))
				return null;
		}
		def=createNewExpertiseDefinition(ID.toUpperCase(), name, baseName);
		def.addListMask(listMask);
		def.addFinalMask(finalMask);
		def.setStageNames((data==null)?new String[0]:data);
		final int practices=CMath.s_int(costs[0]);
		final int trains=CMath.s_int(costs[1]);
		final int qpCost=CMath.s_int(costs[2]);
		final int expCost=CMath.s_int(costs[3]);
		//int timeCost=CMath.s_int(costs[0]);
		if(practices>0)
			def.addCost(CostType.PRACTICE, Double.valueOf(practices));
		if(trains>0)
			def.addCost(CostType.TRAIN, Double.valueOf(trains));
		if(qpCost>0)
			def.addCost(CostType.QP, Double.valueOf(qpCost));
		if(expCost>0)
			def.addCost(CostType.XP, Double.valueOf(expCost));
		//if(timeCost>0) def.addCost(CostType.PRACTICE, Double.valueOf(practices));
		completeEduMap.put(def.ID(),def);
		baseEduSetLists.clear();
		return def;
	}

	@Override
	public boolean addModifyDefinition(final String codedLine, final boolean andSave)
	{
		int x=codedLine.indexOf('=');
		if(x<0)
			return false;
		String ID=codedLine.substring(0,x).toUpperCase().trim();
		ID=CMStrings.replaceAll(CMStrings.replaceAll(ID,"@X2",""),"@X1","").toUpperCase();
		final String resp = this.confirmExpertiseLine(codedLine, ID, false);
		if((resp!=null)&&(resp.toLowerCase().startsWith("error")))
			return false;
		if(andSave)
		{
			String baseID=null;
			if(completeEduMap.containsKey(ID.trim().toUpperCase()))
			{
				final ExpertiseDefinition def=completeEduMap.get(ID.trim().toUpperCase());
				baseID=def.getBaseName();
			}
			else
			{
				for(final String defID : completeEduMap.keySet())
				{
					final ExpertiseDefinition def = completeEduMap.get(defID);
					if(def.getBaseName().equalsIgnoreCase(ID))
						baseID = def.getBaseName();
				}
			}
			if(baseID != null)
			{
				final String expertiseFilename="skills/expertises.txt";
				String fileName=Resources.getRawFileResourceName(expertiseFilename, false);
				final List<String> lines = getExpertiseLines();
				final StringBuilder buf = new StringBuilder("");
				boolean readyToSave=false;
				for(final String l : lines)
				{
					if(l.trim().startsWith("#"))
					{
						if(l.trim().startsWith("#FILE:"))
						{
							if(readyToSave)
								break;
							fileName = l.trim().substring(6).trim();
							buf.setLength(0);
							continue;
						}
					}
					else
					{
						x=l.indexOf('=');
						if(x>0)
						{
							String baseId=l.substring(0,x).trim().toUpperCase();
							baseId=CMStrings.replaceAll(CMStrings.replaceAll(baseId,"@X2",""),"@X1","").toUpperCase();
							if(baseId.equalsIgnoreCase(baseID))
							{
								buf.append(codedLine).append("\n\r");
								readyToSave=true;
								continue;
							}
						}
					}
					buf.append(l).append("\n\r");
				}
				if(readyToSave && (buf.length()>0))
					new CMFile(fileName,null).saveText(buf);
			}
			else
			{
				final CMFile F=new CMFile(Resources.makeFileResourceName("skills/expertises.txt"),null,CMFile.FLAG_LOGERRORS);
				F.saveText("\n"+codedLine,true);
			}
			Resources.removeResource(Resources.makeFileResourceName("skills/expertises.txt"));
			CMLib.expertises().recompileExpertises();
		}
		return true;
	}

	@Override
	public String findExpertiseID(String ID, final boolean exact)
	{
		if(ID==null)
			return null;
		ID=ID.toUpperCase();
		if(helpMap.containsKey(ID))
			return ID;
		if(exact)
			return null;
		for(final String key :  helpMap.keySet())
		{
			if(key.startsWith(ID))
				return key;
		}
		for(final String key :  helpMap.keySet())
		{
			if(CMLib.english().containsString(key, ID))
				return key;
		}
		return null;
	}


	@Override
	public String getExpertiseHelp(final String ID)
	{
		if(ID==null)
			return null;
		return helpMap.get(ID.toUpperCase());
	}

	@Override
	public boolean delDefinition(final String ID, final boolean andSave)
	{
		final List<String> delThese = new LinkedList<String>();
		String baseID=null;
		if(completeEduMap.containsKey(ID.trim().toUpperCase()))
		{
			final ExpertiseDefinition def=completeEduMap.get(ID.trim().toUpperCase());
			delThese.add(ID.trim().toUpperCase());
			baseID=def.getBaseName();
		}
		else
		{
			for(final String defID : completeEduMap.keySet())
			{
				final ExpertiseDefinition def = completeEduMap.get(defID);
				if(def.getBaseName().equalsIgnoreCase(ID))
				{
					baseID = def.getBaseName();
					delThese.add(defID);
				}
			}
		}
		for(final String id : delThese)
			completeEduMap.remove(id);
		baseEduSetLists.clear();
		if(andSave && (baseID != null))
		{
			final String expertiseFilename="skills/expertises.txt";
			String fileName=Resources.getRawFileResourceName(expertiseFilename, false);
			final List<String> lines = getExpertiseLines();
			final StringBuilder buf = new StringBuilder("");
			boolean readyToSave=false;
			boolean delNextHelp=false;
			for(final String l : lines)
			{
				if(l.trim().startsWith("#"))
				{
					if(l.trim().startsWith("#FILE:"))
					{
						if(readyToSave)
							break;
						fileName = l.trim().substring(6).trim();
						buf.setLength(0);
					}
				}
				else
				if(delNextHelp && l.trim().startsWith("HELP_=<EXPERTISE>"))
				{
					delNextHelp=false;
					continue;
				}
				else
				{
					delNextHelp=false;
					final int x=l.indexOf('=');
					if(x>0)
					{
						String baseId=l.substring(0,x).trim().toUpperCase();
						baseId=CMStrings.replaceAll(CMStrings.replaceAll(baseId,"@X2",""),"@X1","").toUpperCase();
						if(baseId.equalsIgnoreCase(baseID))
						{
							delNextHelp=true;
							readyToSave=true;
							continue;
						}
					}
				}
				buf.append(l).append("\n\r");
			}
			if(readyToSave && (buf.length()>0))
				new CMFile(fileName,null).saveText(buf);
			Resources.removeResource(fileName);
			recompileExpertises();
		}
		return delThese.size()>0;
	}

	@Override
	public Enumeration<ExpertiseDefinition> definitions()
	{
		return completeEduMap.elements();
	}

	@Override
	public ExpertiseDefinition getDefinition(final String ID)
	{
		if(ID!=null)
			return completeEduMap.get(ID.trim().toUpperCase());
		return null;
	}

	@Override
	public ExpertiseDefinition findDefinition(final String ID, final boolean exactOnly)
	{
		ExpertiseDefinition D=getDefinition(ID);
		if(D!=null)
			return D;
		for(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)
		{
			D=e.nextElement();
			if(D.name().equalsIgnoreCase(ID))
				return D;
		}
		if(exactOnly)
			return null;
		for(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)
		{
			D=e.nextElement();
			if(D.ID().startsWith(ID))
				return D;
		}
		for(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)
		{
			D=e.nextElement();
			if(CMLib.english().containsString(D.name(),ID))
				return D;
		}
		return null;
	}

	@Override
	public List<ExpertiseDefinition> myQualifiedExpertises(final MOB mob)
	{
		ExpertiseDefinition D=null;
		final List<ExpertiseDefinition> V=new Vector<ExpertiseDefinition>();
		for(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)
		{
			D=e.nextElement();
			if(((D.compiledFinalMask()==null)||(CMLib.masking().maskCheck(D.compiledFinalMask(),mob,true)))
			&&((D.compiledListMask()==null)||(CMLib.masking().maskCheck(D.compiledListMask(),mob,true))))
				V.add(D);
		}
		final PlayerStats pStats = mob.playerStats();
		if(pStats != null)
		{
			for(final ExpertiseDefinition def : pStats.getExtraQualifiedExpertises().values())
			{
				if(((def.compiledFinalMask()==null)||(CMLib.masking().maskCheck(def.compiledFinalMask(),mob,true)))
				&&((def.compiledListMask()==null)||(CMLib.masking().maskCheck(def.compiledListMask(),mob,true))))
				{
					D = completeEduMap.get(def.ID());
					if(D!=null)
						V.remove(D);
					V.add(def);
				}
			}
		}
		return V;
	}

	@Override
	public List<ExpertiseDefinition> myListableExpertises(final MOB mob)
	{
		ExpertiseDefinition D=null;
		final List<ExpertiseDefinition> V=new Vector<ExpertiseDefinition>();
		for(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)
		{
			D=e.nextElement();
			if((D.compiledListMask()==null)||(CMLib.masking().maskCheck(D.compiledListMask(),mob,true)))
			{
				V.add(D);
			}
		}
		final PlayerStats pStats = mob.playerStats();
		if(pStats != null)
		{
			for(final ExpertiseDefinition def : pStats.getExtraQualifiedExpertises().values())
			{
				if((def.compiledListMask()==null)||(CMLib.masking().maskCheck(def.compiledListMask(),mob,true)))
				{
					D = completeEduMap.get(def.ID());
					if(D!=null)
						V.remove(D);
					V.add(def);
				}
			}
		}
		return V;
	}

	@Override
	public int getExpertiseLevelCached(final MOB mob, final String abilityID, final ExpertiseLibrary.XType code)
	{
		if((mob==null)||(code==null)||(abilityID==null)||(abilityID.length()==0))
			return 0;
		int expertiseLvl=0;
		final int[][] usageCache=mob.getAbilityUsageCache(abilityID);
		if(usageCache[Ability.CACHEINDEX_EXPERTISE]!=null)
			expertiseLvl=usageCache[Ability.CACHEINDEX_EXPERTISE][code.ordinal()];
		else
		{
			final int[] xFlagCache=new int[ExpertiseLibrary.XType.values().length];
			final CharClass charClass = mob.baseCharStats().getCurrentClass();
			if(charClass == null)
				return 0;
			for(final ExpertiseLibrary.XType flag : ExpertiseLibrary.XType.values())
			{
				xFlagCache[flag.ordinal()]=getExpertiseLevelCalced(mob,abilityID,flag)
											+charClass.addedExpertise(mob, flag, abilityID);
			}
			usageCache[Ability.CACHEINDEX_EXPERTISE]=xFlagCache;
			expertiseLvl = xFlagCache[code.ordinal()];
		}
		return expertiseLvl;
	}

	private int getStageNumber(final ExpertiseDefinition D)
	{
		if(D.ID().startsWith(D.getBaseName()))
		{
			final String remID=D.ID().substring(D.getBaseName().length());
			if(CMath.isInteger(remID))
				return CMath.s_int(remID);
			if(CMath.isRomanNumeral(remID))
				return CMath.convertFromRoman(remID);
		}
		return 0;
	}

	@Override
	public int getHighestListableStageBySkill(final MOB mob, final String ableID, final ExpertiseLibrary.XType flag)
	{
		final String expertiseID = completeUsageMap[flag.ordinal()].get(ableID);
		if(expertiseID == null)
			return 0;
		ExpertiseDefinition D=null;
		int max=0;
		for(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)
		{
			D=e.nextElement();
			if(expertiseID.equals(D.getBaseName()))
			{
				if((D.compiledListMask()==null)||(CMLib.masking().maskCheck(D.compiledListMask(),mob,true)))
				{
					final int stage=getStageNumber(D);
					if(stage > max)
						max=stage;
				}
			}
		}
		final PlayerStats pStats = mob.playerStats();
		if(pStats != null)
		{
			for(final ExpertiseDefinition def : pStats.getExtraQualifiedExpertises().values())
			{
				if(expertiseID.equals(def.getBaseName()))
				{
					if((def.compiledListMask()==null)||(CMLib.masking().maskCheck(def.compiledListMask(),mob,true)))
					{
						final int stage=getStageNumber(def);
						if(stage > max)
							max=stage;
					}
				}
			}
		}
		return max;
	}

	@Override
	public int numExpertises()
	{
		return completeEduMap.size();
	}

	private String expertMath(String s,final int l)
	{
		int x=s.indexOf('{');
		while(x>=0)
		{
			final int y=s.indexOf('}',x);
			if(y<0)
				break;
			s=s.substring(0,x)+CMath.parseIntExpression(s.substring(x+1,y))+s.substring(y+1);
			x=s.indexOf('{');
		}
		return s;
	}

	@Override
	public List<String> getStageCodes(String baseExpertiseCode)
	{
		String key=null;
		if(baseExpertiseCode==null)
			return new ReadOnlyVector<String>(1);
		baseExpertiseCode=baseExpertiseCode.toUpperCase();
		List<String> set=baseEduSetLists.get(baseExpertiseCode);
		if(set == null)
		{
			synchronized(CMClass.getSync(("ListedEduBuild:"+baseExpertiseCode)))
			{
				set=baseEduSetLists.get(baseExpertiseCode);
				if(set==null)
				{
					final List<String> codes=new LinkedList<String>();
					for(final Enumeration<String> e=completeEduMap.keys();e.hasMoreElements();)
					{
						key=e.nextElement();
						if(key.startsWith(baseExpertiseCode)
						&&(CMath.isInteger(key.substring(baseExpertiseCode.length()))||CMath.isRomanNumeral(key.substring(baseExpertiseCode.length()))))
							codes.add(key);
					}
					set=new ReadOnlyVector<String>(codes);
					baseEduSetLists.put(baseExpertiseCode, set);
				}
			}
		}
		return set;
	}

	@Override
	public int numStages(final String baseExpertiseCode)
	{
		return getStageCodes(baseExpertiseCode).size();
	}

	protected String getGuessedBaseExpertiseName(final String expertiseCode)
	{
		int lastBadChar=expertiseCode.length()-1;
		while( (lastBadChar>=0)
		&&(CMath.isInteger(expertiseCode.substring(lastBadChar))||CMath.isRomanNumeral(expertiseCode.substring(lastBadChar))))
			lastBadChar--;
		if(lastBadChar<expertiseCode.length()-1)
			return expertiseCode.substring(0,lastBadChar+1);
		return expertiseCode;
	}

	@Override
	public List<String> getPeerStageCodes(final String expertiseCode)
	{
		return getStageCodes(getGuessedBaseExpertiseName(expertiseCode));
	}

	@Override
	public String getApplicableExpertise(final String abilityID, final XType code)
	{
		return completeUsageMap[code.ordinal()].get(abilityID);
	}

	@Override
	public String[] getApplicableExpertises(final String abilityID, final XType code)
	{
		if(code == null)
		{
			final Set<String> all=new TreeSet<String>();
			for(final XType f : XType.values())
			{
				final String[] set=completeUsageMaps[f.ordinal()].get(abilityID);
				if(set != null)
					all.addAll(Arrays.asList(set));
			}
			return all.toArray(new String[0]);
		}
		return completeUsageMaps[code.ordinal()].get(abilityID);
	}

	@Override
	public ExpertiseDefinition getConfirmedDefinition(final MOB mob, final String ID)
	{
		if(mob == null)
				switch(costType)
				{
				case XP:
					CMLib.leveler().postExperience(student, null, "", value.intValue(), true);
					break;
				case GOLD:
					CMLib.beanCounter().subtractMoney(student, value.doubleValue());
				if((e!=null)
				&&(e.getValue()!=null))
				{
					final int level = getConfirmedExpertiseLevel(mob, def.getBaseName(), e);
					if(level == e.second.intValue())
						return def;
					if(level == 0)
						return null;
					return getDefinition(def.getBaseName()+level);
				}
			}
			return completeEduMap.get(ID.trim().toUpperCase());
		}
		return null;
	}

	protected int getConfirmedExpertiseLevel(final MOB mob, final String baseID, final Pair<String,Integer> e)
	{
		if((!mob.isMonster())
		&&(!CMSecurity.isAllowedEverywhere(mob, SecFlag.ALLSKILLS)))
		{
			final ExpertiseDefinition def=getDefinition(baseID+e.getValue().toString());
			if((def == null)
			||(!CMLib.masking().maskCheck(def.compiledListMask(), mob, true))
			||(!CMLib.masking().maskCheck(def.compiledFinalMask(), mob, true)))
			{
				final List<String> defList = getStageCodes(baseID);
				for(int i = defList.size()-e.getValue().intValue();i<defList.size();i++)
				{
					final ExpertiseDefinition def2=getDefinition(defList.get(i));
					if((def2 != null)
					&&(CMLib.masking().maskCheck(def2.compiledListMask(), mob, true))
					&&(CMLib.masking().maskCheck(def2.compiledFinalMask(), mob, true)))
						return this.getStageNumber(def2);
				}
				return 0;
			}
		}
		return e.getValue().intValue();
	}

	@Override
	public int getExpertiseLevelCalced(final MOB mob, final String abilityID, final XType code)
	{
		final String[] applicableExpIDs = getApplicableExpertises(abilityID, code);
		if((applicableExpIDs==null)||(applicableExpIDs.length<1))
			return 0;
		final Pair<String,Integer> e=mob.fetchExpertise(applicableExpIDs[0]);
		if((e!=null)
		&&(e.getValue()!=null))
			return e.getValue().intValue();
			//return getConfirmedExpertiseLevel(mob, applicableExpIDs[0], e);
		if(applicableExpIDs.length<2)
			return 0;
		for(final String expID : applicableExpIDs)
		{
			final Pair<String,Integer> e2=mob.fetchExpertise(expID);
			if((e2!=null)
			&&(e2.getValue()!=null))
				return e2.getValue().intValue();
				//return getConfirmedExpertiseLevel(mob, applicableExpIDs[0], e2);
		}
		return 0;
	}

	@Override
	public String confirmExpertiseLine(String row, String ID, final boolean addIfPossible)
	{
		int levels=0;
		final HashSet<String> flags=new HashSet<String>();
		String s=null;
		String skillMask=null;
		final String[] costs=new String[5];
		final String[] data=new String[0];
		String WKID=null;
		String name,WKname=null;
		String listMask,WKlistMask=null;
		String finalMask,WKfinalMask=null;
		List<String> skillsToRegister=null;
		ExpertiseLibrary.ExpertiseDefinition def=null;
		boolean didOne=false;
		if(row.trim().startsWith("#")||row.trim().startsWith(";")||(row.trim().length()==0))
			return null;
		int x=row.indexOf('=');
		if(x<0)
			return "Error: Invalid line! Not comment, whitespace, and does not contain an = sign!";
		if(row.trim().toUpperCase().startsWith("DATA_"))
		{
			final String lastID=ID;
			ID=row.substring(0,x).toUpperCase();
			row=row.substring(x+1);
			ID=ID.substring(5).toUpperCase();
			if(ID.length()==0)
				ID=lastID;
			if((lastID==null)||(lastID.length()==0))
				return "Error: No last expertise found for data: "+lastID+"="+row;
			else
			if(this.getDefinition(ID)!=null)
			{
				def=getDefinition(ID);
				WKID=def.name().toUpperCase().replace(' ','_');
				if(addIfPossible)
				{
					def.setStageNames(CMParms.parseCommas(row,true).toArray(new String[0]));
				}
			}
			else
			{
				final List<String> stages=getStageCodes(ID);
				if(addIfPossible)
				{
					for(int s1=0;s1<stages.size();s1++)
					{
						def=getDefinition(stages.get(s1));
						if(def==null)
							continue;
						def.setStageNames(CMParms.parseCommas(row,true).toArray(new String[0]));
					}
				}
			}
			return null;
		}
		if(row.trim().toUpperCase().startsWith("HELP_"))
		{
			final String lastID=ID;
			ID=row.substring(0,x).toUpperCase();
			row=row.substring(x+1);
			ID=ID.substring(5).toUpperCase();
			if(ID.length()==0)
				ID=lastID;
			if((lastID==null)||(lastID.length()==0))
				return "Error: No last expertise found for help: "+lastID+"="+row;
			else
			if(getDefinition(ID)!=null)
			{
				def=getDefinition(ID);
				WKID=def.name().toUpperCase().replace(' ','_');
				if(addIfPossible)
					helpMap.put(WKID,row);
			}
			else
			{
				final List<String> stages=getStageCodes(ID);
				if((stages==null)||(stages.size()==0))
					return "Error: Expertise not yet defined: "+ID+"="+row;
				def=getDefinition(stages.get(0));
				if(def!=null)
				{
					WKID=def.name().toUpperCase().replace(' ','_');
					x=WKID.lastIndexOf('_');
					if((x>=0)&&(CMath.isInteger(WKID.substring(x+1))||CMath.isRomanNumeral(WKID.substring(x+1))))
					{
						WKID=WKID.substring(0,x);
						if(addIfPossible)
						if(!helpMap.containsKey(WKID))
							helpMap.put(WKID,row+"\n\r(See help on "+def.name()+").");
					}
				}
				if(addIfPossible)
				{
					for(int s1=0;s1<stages.size();s1++)
					{
						def=getDefinition(stages.get(s1));
						if(def==null)
							continue;
						WKID=def.name().toUpperCase().replace(' ','_');
						if(!helpMap.containsKey(WKID))
							helpMap.put(WKID,row);
					}
				}
			}
			return null;
		}
		ID=row.substring(0,x).toUpperCase();
		row=row.substring(x+1);
		final List<String> parts=CMParms.parseCommas(row,false);
		if(parts.size()!=11)
			return "Error: Expertise row malformed (Requires 11 entries/10 commas): "+ID+"="+row;
		name=parts.get(0);
		if(name.length()==0)
			return "Error: Expertise name ("+name+") malformed: "+ID+"="+row;
		if(!CMath.isInteger(parts.get(1)))
			return "Error: Expertise num ("+(parts.get(1))+") malformed: "+ID+"="+row;
		levels=CMath.s_int(parts.get(1));
		flags.clear();
		flags.addAll(CMParms.parseAny(parts.get(2).toUpperCase(),'|',true));

		skillMask=parts.get(3);
		if(skillMask.length()==0)
			return "Error: Expertise skill mask ("+skillMask+") malformed: "+ID+"="+row;
		skillsToRegister=CMLib.masking().getAbilityEduReqs(skillMask);
		if(skillsToRegister.size()==0)
		{
			skillsToRegister=CMLib.masking().getAbilityEduReqs(skillMask);
			return "Error: Expertise no skills ("+skillMask+") found: "+ID+"="+row;
		}
		listMask=skillMask+" "+(parts.get(4));
		finalMask=((parts.get(5)));
		for(int i=6;i<11;i++)
			costs[i-6]=parts.get(i);
		didOne=false;
		for(int u=0;u<completeUsageMap.length;u++)
		{
			didOne=didOne||flags.contains(ExpertiseLibrary.XType.values()[u].name());
		}
		if(!didOne)
			return "Error: No flags ("+parts.get(2).toUpperCase()+") were set: "+ID+"="+row;
		if(addIfPossible)
		{
			final Set<XType> fflags = new HashSet<XType>();
			for(final String f : flags)
			{
				final XType fl = (XType)CMath.s_valueOf(XType.class, f.toUpperCase().trim());
				if(fl != null)
					fflags.add(fl);
			}
			final String baseName=CMStrings.replaceAll(CMStrings.replaceAll(ID,"@X2",""),"@X1","").toUpperCase();
			for(int l=1;l<=levels;l++)
			{
				WKID=CMStrings.replaceAll(ID,"@X1",""+l);
				WKID=CMStrings.replaceAll(WKID,"@X2",""+CMath.convertToRoman(l));
				WKname=CMStrings.replaceAll(name,"@x1",""+l);
				WKname=CMStrings.replaceAll(WKname,"@x2",""+CMath.convertToRoman(l));
				WKlistMask=CMStrings.replaceAll(listMask,"@x1",""+l);
				WKlistMask=CMStrings.replaceAll(WKlistMask,"@x2",""+CMath.convertToRoman(l));
				WKfinalMask=CMStrings.replaceAll(finalMask,"@x1",""+l);
				WKfinalMask=CMStrings.replaceAll(WKfinalMask,"@x2",""+CMath.convertToRoman(l));
				if((l>1)&&(listMask.toUpperCase().indexOf("-EXPERT")<0))
				{
					s=CMStrings.replaceAll(ID,"@X1",""+(l-1));
					s=CMStrings.replaceAll(s,"@X2",""+CMath.convertToRoman(l-1));
					WKlistMask="-EXPERTISE \"+"+s+"\" "+WKlistMask;
				}
				WKlistMask=expertMath(WKlistMask,l);
				WKfinalMask=expertMath(WKfinalMask,l);
				def=addDefinition(WKID,WKname,baseName,WKlistMask,WKfinalMask,costs,data);
				if(def!=null)
				{
					def.addRawMasks(listMask, finalMask);
					def.getFlagTypes().addAll(fflags);
					def.compiledFinalMask();
					def.compiledListMask();
				}
			}
		}
		ID=CMStrings.replaceAll(ID,"@X1","");
		ID=CMStrings.replaceAll(ID,"@X2","");
		for(int u=0;u<completeUsageMap.length;u++)
		{
			if(flags.contains(ExpertiseLibrary.XType.values()[u].name()))
			{
				for(int k=0;k<skillsToRegister.size();k++)
				{
					final String skid = skillsToRegister.get(k);
					completeUsageMap[u].put(skid, ID);
					if(!completeUsageMaps[u].containsKey(skid))
						completeUsageMaps[u].put(skid, new String[]{ID});
					else
					{
						final String[] oldSet=completeUsageMaps[u].get(skid);
						final String[] newSet=Arrays.copyOf(oldSet, oldSet.length+1);
						newSet[newSet.length-1]=ID;
						completeUsageMaps[u].put(skid, newSet);
					}
				}
			}
		}
		return addIfPossible?ID:null;
	}

	@Override
	public String getExpertiseInstructions()
	{
		final StringBuilder inst = new StringBuilder("");
		final String expertiseFilename="skills/expertises.txt";
		final List<String> V=Resources.getFileLineVector(Resources.getFileResource(expertiseFilename,true));
		for(int v=0;v<V.size();v++)
		{
			if(V.get(v).startsWith("#"))
				inst.append(V.get(v).substring(1)+"\n\r");
			else
			if(V.get(v).length()>0)
				break;
		}
		return inst.toString();
	}

	protected List<String> getExpertiseLines()
	{
		final String expertiseFilename="skills/expertises.txt";
		final List<String> V=Resources.getFileLineVector(Resources.getFileResource(expertiseFilename,true));
		Resources.removeResource(Resources.makeFileResourceName("skills/expertises.txt"));
		for(int i=2;i<99;i++)
		{
			final String fileName=Resources.getRawFileResourceName(expertiseFilename, false)+"."+i;
			final StringBuffer buf = Resources.getFileResource(expertiseFilename+"."+i,false);
			Resources.removeResource(fileName);
			if(buf.length()==0)
				break;
			V.add("#FILE:"+fileName);
			V.addAll(Resources.getFileLineVector(buf));
		}
		return V;
	}

	@Override
	public void recompileExpertises()
	{
		for(int u=0;u<completeUsageMap.length;u++)
			completeUsageMap[u]=new Hashtable<String,String>();
		for(int u=0;u<completeUsageMaps.length;u++)
			completeUsageMaps[u]=new Hashtable<String,String[]>();
		helpMap.clear();
		final List<String> V = getExpertiseLines();
		String ID=null,WKID=null;
		for(int v=0;v<V.size();v++)
		{
			final String row=V.get(v);
			WKID=this.confirmExpertiseLine(row,ID,true);
			if(WKID==null)
				continue;
			if(WKID.startsWith("Error: "))
				Log.errOut("ColumbiaUniv",WKID);
			else
				ID=WKID;
		}
	}

	@Override
	public Iterator<String> filterUniqueExpertiseIDList(final Iterator<String> i)
	{
		final Set<String> ids=new HashSet<String>();
		for(;i.hasNext();)
		{
			final String id=i.next();
			final ExpertiseDefinition def=getDefinition(id);
			if((def != null)
			&&(!(def.ID().equals(def.getBaseName()+"1")||def.ID().equals(def.getBaseName()+"I")||(def.ID().equals(def.getBaseName())))))
			{
				continue;
			}
			ids.add(id);
		}
		return ids.iterator();
	}

	protected Object parseLearnID(final String msg)
	{
		if(msg==null)
			return null;
		int learnStart=msg.indexOf("^<LEARN");
		if(learnStart>=0)
		{
			int end=-1;
			learnStart=msg.indexOf("\"",learnStart+1);
			if(learnStart>=0)
				end=msg.indexOf("\"",learnStart+1);
			if(end>learnStart)
			{
				final String ID=msg.substring(learnStart+1,end);
				final Ability A=CMClass.getAbility(ID);
				if(A!=null)
					return A;
				final ExpertiseDefinition X = this.findDefinition(ID, true);
				if(X!=null)
					return X;
				return CMClass.getObjectOrPrototype(ID);
			}
		}
		return null;
	}

	@Override
	public boolean canBeTaught(final MOB teacher, final MOB student, final Environmental item, final String msg)
	{
		if(teacher.isAttributeSet(MOB.Attrib.NOTEACH))
		{
			teacher.tell(L("You are refusing to teach right now."));
			return false;
		}

		if((student.isAttributeSet(MOB.Attrib.NOTEACH))
		&&((!student.isMonster())||(!student.willFollowOrdersOf(teacher))))
		{
			if(teacher.isMonster())
				CMLib.commands().postSay(teacher,student,L("You are refusing training at this time."),true,false);
			else
				teacher.tell(L("@x1 is refusing training at this time.",student.name()));
			return false;
		}

		Object learnThis=item;
		if(learnThis==null)
			learnThis=parseLearnID(msg);

		String teachWhat="";
		if(learnThis instanceof Ability)
		{
			final Ability theA=(Ability)learnThis;
			teachWhat=theA.name();
			if(!theA.canBeTaughtBy(teacher,student))
				return false;
			if(!theA.canBeLearnedBy(teacher,student))
				return false;
			final Ability studA=student.fetchAbility(theA.ID());
			if((studA!=null)&&(studA.isSavable()))
			{
				if(teacher.isMonster())
					CMLib.commands().postSay(teacher,student,L("You already know '@x1'.",teachWhat),true,false);
				else
					teacher.tell(L("@x1 already knows how to do that.",student.name()));
				return false;
			}
		}
		else
		if(learnThis instanceof ExpertiseDefinition)
		{
			final ExpertiseDefinition theExpertise=(ExpertiseDefinition)learnThis;
			teachWhat=theExpertise.name();
			if(student.fetchExpertise(theExpertise.ID())!=null)
			{
				if(teacher.isMonster())
					CMLib.commands().postSay(teacher,student,L("You already know @x1",theExpertise.name()),true,false);
				else
					teacher.tell(L("@x1 already knows @x2",student.name(),theExpertise.name()));
				return false;
			}

			if(!myQualifiedExpertises(student).contains(theExpertise))
			{
				if(teacher.isMonster())
				{
					CMLib.commands().postSay(teacher,student,
							L("I'm sorry, you do not yet fully qualify for the expertise '@x1'.\n\rRequirements: @x2",
							theExpertise.name(),CMLib.masking().maskDesc(theExpertise.allRequirements())),true,false);
				}
				else
				{
					teacher.tell(L("@x1 does not yet fully qualify for the expertise '@x2'.\n\rRequirements: @x3",
							student.name(),theExpertise.name(),CMLib.masking().maskDesc(theExpertise.allRequirements())));
				}
				return false;
			}
			if(!theExpertise.meetsCostRequirements(student))
			{
				if(teacher.isMonster())
				{
					CMLib.commands().postSay(teacher,student,L("I'm sorry, but to learn the expertise '@x1' requires: @x2",
						theExpertise.name(),theExpertise.costDescription()),true,false);
				}
				else
					teacher.tell(L("Training for that expertise requires @x1.",theExpertise.costDescription()));
				return false;
			}
			teachWhat=theExpertise.name();
		}
		return true;
	}

	@Override
	public void handleBeingTaught(final MOB teacher, final MOB student, final Environmental item, final String msg, final int add)
	{
		Object learnThis=item;
		if(learnThis==null)
			learnThis=parseLearnID(msg);

		if(learnThis instanceof Ability)
		{
			final Ability theA=(Ability)learnThis;
			teacher.charStats().setStat(CharStats.STAT_WISDOM, teacher.charStats().getStat(CharStats.STAT_WISDOM)+5);
			teacher.charStats().setStat(CharStats.STAT_INTELLIGENCE, teacher.charStats().getStat(CharStats.STAT_INTELLIGENCE)+5);
			final Set<String> oldSkillSet = new HashSet<String>();
			for(final Enumeration<Ability> a=student.abilities();a.hasMoreElements();)
				oldSkillSet.add(a.nextElement().ID());
			theA.teach(teacher,student);
			teacher.recoverCharStats();
			final Ability studentA=student.fetchAbility(theA.ID());
			if((studentA==null) && (!oldSkillSet.contains(theA.ID())))
				student.tell(L("You failed to understand @x1.",theA.name()));
			else
			if((!teacher.isMonster()) && (!student.isMonster()))
				CMLib.leveler().postExperience(teacher, null, null, 100, false);
			if((studentA!=null)
			&& (!oldSkillSet.contains(theA.ID()))
			&& (add!=0))
			{
				int newProficiency=studentA.proficiency() + add;
				if(newProficiency > 75)
					newProficiency = 75;
				if(newProficiency < 0)
					newProficiency = 0;
				studentA.setProficiency(newProficiency);
				final Ability studentEffA = student.fetchEffect(theA.ID());
				if(studentEffA != null)
					studentEffA.setProficiency(newProficiency);
			}
		}
		else
		if(learnThis instanceof ExpertiseDefinition)
		{
			final ExpertiseDefinition theExpertise=(ExpertiseDefinition)learnThis;
			theExpertise.spendCostRequirements(student);
			student.addExpertise(theExpertise.ID());
			if((!teacher.isMonster()) && (!student.isMonster()))
				CMLib.leveler().postExperience(teacher, null, null, 100, false);
		}
	}

	@Override
	public boolean confirmAndTeach(final MOB teacherM, final MOB studentM, final CMObject teachableO, final Runnable callBack)
	{
		if((teacherM==null)||(studentM==null))
		{
			if(callBack != null)
				callBack.run();
			return false;
		}
		final Session sess=studentM.session();
		final Room R=studentM.location();
		if((sess==null)
		||(R==null)
		||((!teacherM.isPlayer())
			&&((teacherM.amFollowing()==null)||(!teacherM.amUltimatelyFollowing().isPlayer()))))
		{
			final boolean success=postTeach(teacherM,studentM,teachableO);
			if(callBack != null)
				callBack.run();
			return success;
		}

		final String name;
		if(teachableO instanceof Ability)
			name=((Ability)teachableO).Name();
		else
		if(teachableO instanceof ExpertiseLibrary.ExpertiseDefinition)
			name=((ExpertiseLibrary.ExpertiseDefinition)teachableO).name();
		else
			name=L("Something");

		final Environmental tool=(teachableO instanceof Environmental)?(Environmental)teachableO:null;
		final String teachWhat=teachableO.name();
		final String ID=teachableO.ID();
		final CMMsg msg=CMClass.getMsg(teacherM,studentM,tool,CMMsg.MSG_TEACH,null,teachWhat,ID);
		if((!R.show(teacherM, studentM, CMMsg.MSG_SPEAK, L("<S-NAME> offer(s) to teach <T-NAME>.")))
		||(!R.okMessage(teacherM, msg)))
		{
			if(callBack != null)
				callBack.run();
			return false;
		}

		sess.prompt(new InputCallback(InputCallback.Type.CONFIRM,"N",10000)
		{
			final MOB teacher = teacherM;
			final MOB student = studentM;
			final Session session = sess;
			final CMObject teachable=teachableO;
			final String teachWhat= name;
			final Runnable postCallback=callBack;

			@Override
			public void showPrompt()
			{
				sess.promptPrint(L("\n\r@x1 wants to teach you @x2.  Is this Ok (y/N)?",teacher.Name(),teachWhat));
			}

			@Override
			public void timedOut()
			{
				teacher.tell(L("@x1 does not answer you.",student.charStats().HeShe()));
				if(postCallback != null)
					postCallback.run();
			}

			@Override
			public void callBack()
			{
				try
				{
					if(this.input.equals("Y"))
					{
						if(studentM.location()!=teacherM.location())
						{
							studentM.tell(L("@x1 vanished.",teacherM.name()));
							teacherM.tell(L("@x1 vanished.",studentM.name()));
						}
						else
							postTeach(teacher,student,teachable);
					}
					else
					{
						if(!session.isStopped())
							session.println("\n\r");
						teacher.tell(L("@x1 does not want you to.",student.charStats().HeShe()));
					}
				}
				finally
				{
					if(postCallback != null)
						postCallback.run();
				}
			}
		});
		return true;
	}

	@Override
	public boolean postTeach(final MOB teacher, final MOB student, final CMObject teachObj)
	{
		CMMsg msg=CMClass.getMsg(teacher,student,null,CMMsg.MSG_SPEAK,null);
		if(!teacher.location().okMessage(teacher,msg))
			return false;
		final Environmental tool=(teachObj instanceof Environmental)?(Environmental)teachObj:null;
		final String teachWhat=teachObj.name();
		final String ID=teachObj.ID();
		msg=CMClass.getMsg(teacher,student,tool,CMMsg.MSG_TEACH,L("<S-NAME> teach(es) <T-NAMESELF> '@x1' ^<LEARN NAME=\"@x2\" /^>.",teachWhat,ID));
		if(!teacher.location().okMessage(teacher,msg))
			return false;
		teacher.location().send(teacher,msg);
		return true;
	}

	protected ExpertiseDefinition createNewExpertiseDefinition(final String ID, final String name, final String baseName)
	{
		final ExpertiseDefinition definition = new ExpertiseDefinition()
		{
			private String								ID					= "";
			private String								name				= "";
			private String								baseName			= "";
			private String[]							data				= new String[0];
			private String								uncompiledListMask	= "";
			private String								uncompiledFinalMask	= "";
			private String								rawListMask			= "";
			private String								rawFinalMask		= "";
			private int									minLevel			= Integer.MIN_VALUE + 1;
			private MaskingLibrary.CompiledZMask		compiledListMask	= null;
			private final ExpertiseDefinition			parent				= null;
			private MaskingLibrary.CompiledZMask		compiledFinalMask	= null;
			private final List<CostManager>				costs				= new LinkedList<CostManager>();
			private final Set<XType>					xTypes				= new HashSet<XType>();

			@Override
			public String getBaseName()
			{
				return baseName;
			}

			@Override
			public void setBaseName(final String baseName)
			{
				this.baseName = baseName;
			}

			@Override
			public void setName(final String name)
			{
				this.name = name;
			}

			@Override
			public void setID(final String ID)
			{
				this.ID = ID;
			}

			@Override
			public void setStageNames(final String[] data)
			{
				this.data = data;
			}

			@Override
			public ExpertiseDefinition getParent()
			{
				return parent;
			}

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

			@Override
			public int getMinimumLevel()
			{
				if(minLevel==Integer.MIN_VALUE+1)
					minLevel=CMLib.masking().minMaskLevel(allRequirements(),0);
				return minLevel;
			}

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

			@Override
			public MaskingLibrary.CompiledZMask compiledListMask()
			{
				if((this.compiledListMask==null)&&(uncompiledListMask.length()>0))
				{
					compiledListMask=CMLib.masking().maskCompile(uncompiledListMask);
					CMLib.ableMapper().addPreRequisites(ID,new Vector<String>(),uncompiledListMask.trim());
				}
				return this.compiledListMask;
			}

			@Override
			public MaskingLibrary.CompiledZMask compiledFinalMask()
			{
				if((this.compiledFinalMask==null)&&(uncompiledFinalMask.length()>0))
				{
					this.compiledFinalMask=CMLib.masking().maskCompile(uncompiledFinalMask);
					CMLib.ableMapper().addPreRequisites(ID,new Vector<String>(),uncompiledFinalMask.trim());
				}
				return this.compiledFinalMask;
			}

			@Override
			public String allRequirements()
			{
				String req=uncompiledListMask;
				if(req==null)
					req="";
				else
					req=req.trim();
				if((uncompiledFinalMask!=null)&&(uncompiledFinalMask.length()>0))
					req=req+" "+uncompiledFinalMask;
				return req.trim();
			}

			@Override
			public String listRequirements()
			{
				return uncompiledListMask;
			}

			@Override
			public String finalRequirements()
			{
				return uncompiledFinalMask;
			}

			@Override
			public void addListMask(final String mask)
			{
				if((mask==null)||(mask.length()==0))
					return;
				if(uncompiledListMask==null)
					uncompiledListMask=mask;
				else
					uncompiledListMask+=mask;
				compiledListMask=null;
			}

			@Override
			public void addFinalMask(final String mask)
			{
				if((mask==null)||(mask.length()==0))
					return;
				if(uncompiledFinalMask==null)
					uncompiledFinalMask=mask;
				else
					uncompiledFinalMask+=mask;
				compiledFinalMask=CMLib.masking().maskCompile(uncompiledFinalMask);
				CMLib.ableMapper().addPreRequisites(ID,new Vector<String>(),uncompiledFinalMask.trim());
			}

			@Override
			public void addRawMasks(final String listMask, final String finalMask)
			{
				this.rawListMask = listMask;
				this.rawFinalMask = finalMask;
			}

			@Override
			public String rawListMask()
			{
				return this.rawListMask;
			}

			@Override
			public String rawFinalMask()
			{
				return this.rawFinalMask;
			}

			@Override
			public void addCost(final CostType type, final Double value)
			{
				costs.add(CMLib.utensils().createCostManager(type,value));
			}

			@Override
			public String costDescription()
			{
				final StringBuffer costStr=new StringBuffer("");
				for(final CostManager cost : costs)
					costStr.append(cost.requirements(null)).append(", ");
				if(costStr.length()==0)
					return "";
				return costStr.substring(0,costStr.length()-2);
			}

			@Override
			public boolean meetsCostRequirements(final MOB mob)
			{
				for(final CostManager cost : costs)
				{
					if(!cost.doesMeetCostRequirements(mob))
						return false;
				}
				return true;
			}

			@Override
			public void spendCostRequirements(final MOB mob)
			{
				for(final CostManager cost : costs)
					cost.doSpend(mob);
			}

			@Override
			public int compareTo(final CMObject o)
			{
				return (o == this) ? 0 : 1;
			}

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

			@Override
			public CMObject newInstance()
			{
				return this;
			}

			@Override
			public CMObject copyOf()
			{
				return this;
			}

			@Override
			public void initializeClass()
			{
			}

			@Override
			public Set<XType> getFlagTypes()
			{
				return xTypes;
			}
		};
		definition.setID(ID.toUpperCase());
		definition.setName(name);
		definition.setBaseName(baseName);
		return definition;
	}
}

<MSG> negating experience costs

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

<DFF> @@ -567,7 +567,7 @@ public class ColumbiaUniv extends StdLibrary implements ExpertiseLibrary
 				switch(costType)
 				{
 				case XP:
-					CMLib.leveler().postExperience(student, null, "", value.intValue(), true);
+					CMLib.leveler().postExperience(student, null, "", -value.intValue(), true);
 					break;
 				case GOLD:
 					CMLib.beanCounter().subtractMoney(student, value.doubleValue());

{'commit': 'diff --git a/com/planet_ink/coffee_mud/Libraries/ColumbiaUniv.java b/com/planet_ink/coffee_mud/Libraries/ColumbiaUniv.java\nindex a83bde80a8..3872f75d37 100644\n--- a/com/planet_ink/coffee_mud/Libraries/ColumbiaUniv.java\n+++ b/com/planet_ink/coffee_mud/Libraries/ColumbiaUniv.java\n@@ -567,7 +567,7 @@ public class ColumbiaUniv extends StdLibrary implements ExpertiseLibrary\n \t\t\t\tswitch(costType)\r\n \t\t\t\t{\r\n \t\t\t\tcase XP:\r\n-\t\t\t\t\tCMLib.leveler().postExperience(student, null, "", value.intValue(), true);\r\n+\t\t\t\t\tCMLib.leveler().postExperience(student, null, "", -value.intValue(), true);\r\n \t\t\t\t\tbreak;\r\n \t\t\t\tcase GOLD:\r\n \t\t\t\t\tCMLib.beanCounter().subtractMoney(student, value.doubleValue());\r\n', 'commit_subject': 'negating experience costs', 'repo_name': 'bozimmerman/CoffeeMud', 'committer': {'name': 'bozimmerman', 'email': 'dc45fe023482b3b890b5e574e53c1d051f45b1ef@zimmers.net', 'time_sec': '1607559688', 'tz_offset': '0', 'date': {'seconds': '1607559688'}}, 'commit_tree': '79bb59fab25d6184b7f6cfb64b643729c905db0c', 'commit_message': 'negating experience costs\n\ngit-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@20191 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'language_name': 'Shell', 'license': 'apache-2.0', 'hunks': '@@ -567,7 +567,7 @@ public class ColumbiaUniv extends StdLibrary implements ExpertiseLibrary\n \t\t\t\tswitch(costType)\r\n \t\t\t\t{\r\n \t\t\t\tcase XP:\r\n-\t\t\t\t\tCMLib.leveler().postExperience(student, null, "", value.intValue(), true);\r\n+\t\t\t\t\tCMLib.leveler().postExperience(student, null, "", -value.intValue(), true);\r\n \t\t\t\t\tbreak;\r\n \t\t\t\tcase GOLD:\r\n \t\t\t\t\tCMLib.beanCounter().subtractMoney(student, value.doubleValue());\r\n', 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/com/planet_ink/coffee_mud/Libraries/ColumbiaUniv.java', 'tgt_file': 'b/com/planet_ink/coffee_mud/Libraries/ColumbiaUniv.java', 'file_extension': '.java', 'before_file': 'package com.planet_ink.coffee_mud.Libraries;\nimport com.planet_ink.coffee_mud.Libraries.interfaces.*;\nimport com.planet_ink.coffee_mud.core.interfaces.CostDef.CostType;\nimport com.planet_ink.coffee_mud.Libraries.interfaces.ExpertiseLibrary.ExpertiseDefinition;\nimport com.planet_ink.coffee_mud.core.interfaces.*;\nimport com.planet_ink.coffee_mud.core.*;\nimport com.planet_ink.coffee_mud.core.CMSecurity.SecFlag;\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.Common.interfaces.Session.InputCallback;\nimport com.planet_ink.coffee_mud.Exits.interfaces.*;\nimport com.planet_ink.coffee_mud.Items.interfaces.*;\nimport com.planet_ink.coffee_mud.Locales.interfaces.*;\nimport com.planet_ink.coffee_mud.MOBS.interfaces.*;\nimport com.planet_ink.coffee_mud.Races.interfaces.*;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\n/*\n   Copyright 2006-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 ColumbiaUniv extends StdLibrary implements ExpertiseLibrary\n{\n\t@Override\n\tpublic String ID()\n\t{\n\t\treturn "ColumbiaUniv";\n\t}\n\n\tprotected SHashtable<String, ExpertiseDefinition>\tcompleteEduMap\t\t= new SHashtable<String, ExpertiseDefinition>();\n\tprotected SHashtable<String, List<String>>\t\t\tbaseEduSetLists\t\t= new SHashtable<String, List<String>>();\n\n\t@SuppressWarnings("unchecked")\n\tprotected Map<String, String>[]\t\tcompleteUsageMap\t= new Hashtable[XType.values().length];\n\t@SuppressWarnings("unchecked")\n\tprotected Map<String, String[]>[]\tcompleteUsageMaps\t= new Hashtable[XType.values().length];\n\tprotected Map<String, String>\t\thelpMap\t\t\t\t= new TreeMap<String, String>();\n\tprotected DVector\t\t\t\t\trawDefinitions\t\t= new DVector(7);\n\n\tprotected ExpertiseLibrary.ExpertiseDefinition addDefinition(final String ID, final String name, final String baseName, final String listMask, final String finalMask, final String[] costs, final String[] data)\n\t{\n\t\tExpertiseLibrary.ExpertiseDefinition def=getDefinition(ID);\n\t\tif(def!=null)\n\t\t\treturn  def;\n\t\tif(CMSecurity.isExpertiseDisabled(ID.toUpperCase()))\n\t\t\treturn null;\n\t\tif(CMSecurity.isExpertiseDisabled("*"))\n\t\t\treturn null;\n\t\tfor(int i=1;i<ID.length();i++)\n\t\t{\n\t\t\tif(CMSecurity.isExpertiseDisabled(ID.substring(0,i).toUpperCase()+"*"))\n\t\t\t\treturn null;\n\t\t}\n\t\tdef=createNewExpertiseDefinition(ID.toUpperCase(), name, baseName);\n\t\tdef.addListMask(listMask);\n\t\tdef.addFinalMask(finalMask);\n\t\tdef.setStageNames((data==null)?new String[0]:data);\n\t\tfinal int practices=CMath.s_int(costs[0]);\n\t\tfinal int trains=CMath.s_int(costs[1]);\n\t\tfinal int qpCost=CMath.s_int(costs[2]);\n\t\tfinal int expCost=CMath.s_int(costs[3]);\n\t\t//int timeCost=CMath.s_int(costs[0]);\n\t\tif(practices>0)\n\t\t\tdef.addCost(CostType.PRACTICE, Double.valueOf(practices));\n\t\tif(trains>0)\n\t\t\tdef.addCost(CostType.TRAIN, Double.valueOf(trains));\n\t\tif(qpCost>0)\n\t\t\tdef.addCost(CostType.QP, Double.valueOf(qpCost));\n\t\tif(expCost>0)\n\t\t\tdef.addCost(CostType.XP, Double.valueOf(expCost));\n\t\t//if(timeCost>0) def.addCost(CostType.PRACTICE, Double.valueOf(practices));\n\t\tcompleteEduMap.put(def.ID(),def);\n\t\tbaseEduSetLists.clear();\n\t\treturn def;\n\t}\n\n\t@Override\n\tpublic boolean addModifyDefinition(final String codedLine, final boolean andSave)\n\t{\n\t\tint x=codedLine.indexOf(\'=\');\n\t\tif(x<0)\n\t\t\treturn false;\n\t\tString ID=codedLine.substring(0,x).toUpperCase().trim();\n\t\tID=CMStrings.replaceAll(CMStrings.replaceAll(ID,"@X2",""),"@X1","").toUpperCase();\n\t\tfinal String resp = this.confirmExpertiseLine(codedLine, ID, false);\n\t\tif((resp!=null)&&(resp.toLowerCase().startsWith("error")))\n\t\t\treturn false;\n\t\tif(andSave)\n\t\t{\n\t\t\tString baseID=null;\n\t\t\tif(completeEduMap.containsKey(ID.trim().toUpperCase()))\n\t\t\t{\n\t\t\t\tfinal ExpertiseDefinition def=completeEduMap.get(ID.trim().toUpperCase());\n\t\t\t\tbaseID=def.getBaseName();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor(final String defID : completeEduMap.keySet())\n\t\t\t\t{\n\t\t\t\t\tfinal ExpertiseDefinition def = completeEduMap.get(defID);\n\t\t\t\t\tif(def.getBaseName().equalsIgnoreCase(ID))\n\t\t\t\t\t\tbaseID = def.getBaseName();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(baseID != null)\n\t\t\t{\n\t\t\t\tfinal String expertiseFilename="skills/expertises.txt";\n\t\t\t\tString fileName=Resources.getRawFileResourceName(expertiseFilename, false);\n\t\t\t\tfinal List<String> lines = getExpertiseLines();\n\t\t\t\tfinal StringBuilder buf = new StringBuilder("");\n\t\t\t\tboolean readyToSave=false;\n\t\t\t\tfor(final String l : lines)\n\t\t\t\t{\n\t\t\t\t\tif(l.trim().startsWith("#"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(l.trim().startsWith("#FILE:"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(readyToSave)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tfileName = l.trim().substring(6).trim();\n\t\t\t\t\t\t\tbuf.setLength(0);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tx=l.indexOf(\'=\');\n\t\t\t\t\t\tif(x>0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tString baseId=l.substring(0,x).trim().toUpperCase();\n\t\t\t\t\t\t\tbaseId=CMStrings.replaceAll(CMStrings.replaceAll(baseId,"@X2",""),"@X1","").toUpperCase();\n\t\t\t\t\t\t\tif(baseId.equalsIgnoreCase(baseID))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbuf.append(codedLine).append("\\n\\r");\n\t\t\t\t\t\t\t\treadyToSave=true;\n\t\t\t\t\t\t\t\tcontinue;\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\tbuf.append(l).append("\\n\\r");\n\t\t\t\t}\n\t\t\t\tif(readyToSave && (buf.length()>0))\n\t\t\t\t\tnew CMFile(fileName,null).saveText(buf);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfinal CMFile F=new CMFile(Resources.makeFileResourceName("skills/expertises.txt"),null,CMFile.FLAG_LOGERRORS);\n\t\t\t\tF.saveText("\\n"+codedLine,true);\n\t\t\t}\n\t\t\tResources.removeResource(Resources.makeFileResourceName("skills/expertises.txt"));\n\t\t\tCMLib.expertises().recompileExpertises();\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String findExpertiseID(String ID, final boolean exact)\n\t{\n\t\tif(ID==null)\n\t\t\treturn null;\n\t\tID=ID.toUpperCase();\n\t\tif(helpMap.containsKey(ID))\n\t\t\treturn ID;\n\t\tif(exact)\n\t\t\treturn null;\n\t\tfor(final String key :  helpMap.keySet())\n\t\t{\n\t\t\tif(key.startsWith(ID))\n\t\t\t\treturn key;\n\t\t}\n\t\tfor(final String key :  helpMap.keySet())\n\t\t{\n\t\t\tif(CMLib.english().containsString(key, ID))\n\t\t\t\treturn key;\n\t\t}\n\t\treturn null;\n\t}\n\n\n\t@Override\n\tpublic String getExpertiseHelp(final String ID)\n\t{\n\t\tif(ID==null)\n\t\t\treturn null;\n\t\treturn helpMap.get(ID.toUpperCase());\n\t}\n\n\t@Override\n\tpublic boolean delDefinition(final String ID, final boolean andSave)\n\t{\n\t\tfinal List<String> delThese = new LinkedList<String>();\n\t\tString baseID=null;\n\t\tif(completeEduMap.containsKey(ID.trim().toUpperCase()))\n\t\t{\n\t\t\tfinal ExpertiseDefinition def=completeEduMap.get(ID.trim().toUpperCase());\n\t\t\tdelThese.add(ID.trim().toUpperCase());\n\t\t\tbaseID=def.getBaseName();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(final String defID : completeEduMap.keySet())\n\t\t\t{\n\t\t\t\tfinal ExpertiseDefinition def = completeEduMap.get(defID);\n\t\t\t\tif(def.getBaseName().equalsIgnoreCase(ID))\n\t\t\t\t{\n\t\t\t\t\tbaseID = def.getBaseName();\n\t\t\t\t\tdelThese.add(defID);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor(final String id : delThese)\n\t\t\tcompleteEduMap.remove(id);\n\t\tbaseEduSetLists.clear();\n\t\tif(andSave && (baseID != null))\n\t\t{\n\t\t\tfinal String expertiseFilename="skills/expertises.txt";\n\t\t\tString fileName=Resources.getRawFileResourceName(expertiseFilename, false);\n\t\t\tfinal List<String> lines = getExpertiseLines();\n\t\t\tfinal StringBuilder buf = new StringBuilder("");\n\t\t\tboolean readyToSave=false;\n\t\t\tboolean delNextHelp=false;\n\t\t\tfor(final String l : lines)\n\t\t\t{\n\t\t\t\tif(l.trim().startsWith("#"))\n\t\t\t\t{\n\t\t\t\t\tif(l.trim().startsWith("#FILE:"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(readyToSave)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tfileName = l.trim().substring(6).trim();\n\t\t\t\t\t\tbuf.setLength(0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\tif(delNextHelp && l.trim().startsWith("HELP_=<EXPERTISE>"))\n\t\t\t\t{\n\t\t\t\t\tdelNextHelp=false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdelNextHelp=false;\n\t\t\t\t\tfinal int x=l.indexOf(\'=\');\n\t\t\t\t\tif(x>0)\n\t\t\t\t\t{\n\t\t\t\t\t\tString baseId=l.substring(0,x).trim().toUpperCase();\n\t\t\t\t\t\tbaseId=CMStrings.replaceAll(CMStrings.replaceAll(baseId,"@X2",""),"@X1","").toUpperCase();\n\t\t\t\t\t\tif(baseId.equalsIgnoreCase(baseID))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdelNextHelp=true;\n\t\t\t\t\t\t\treadyToSave=true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbuf.append(l).append("\\n\\r");\n\t\t\t}\n\t\t\tif(readyToSave && (buf.length()>0))\n\t\t\t\tnew CMFile(fileName,null).saveText(buf);\n\t\t\tResources.removeResource(fileName);\n\t\t\trecompileExpertises();\n\t\t}\n\t\treturn delThese.size()>0;\n\t}\n\n\t@Override\n\tpublic Enumeration<ExpertiseDefinition> definitions()\n\t{\n\t\treturn completeEduMap.elements();\n\t}\n\n\t@Override\n\tpublic ExpertiseDefinition getDefinition(final String ID)\n\t{\n\t\tif(ID!=null)\n\t\t\treturn completeEduMap.get(ID.trim().toUpperCase());\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ExpertiseDefinition findDefinition(final String ID, final boolean exactOnly)\n\t{\n\t\tExpertiseDefinition D=getDefinition(ID);\n\t\tif(D!=null)\n\t\t\treturn D;\n\t\tfor(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)\n\t\t{\n\t\t\tD=e.nextElement();\n\t\t\tif(D.name().equalsIgnoreCase(ID))\n\t\t\t\treturn D;\n\t\t}\n\t\tif(exactOnly)\n\t\t\treturn null;\n\t\tfor(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)\n\t\t{\n\t\t\tD=e.nextElement();\n\t\t\tif(D.ID().startsWith(ID))\n\t\t\t\treturn D;\n\t\t}\n\t\tfor(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)\n\t\t{\n\t\t\tD=e.nextElement();\n\t\t\tif(CMLib.english().containsString(D.name(),ID))\n\t\t\t\treturn D;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<ExpertiseDefinition> myQualifiedExpertises(final MOB mob)\n\t{\n\t\tExpertiseDefinition D=null;\n\t\tfinal List<ExpertiseDefinition> V=new Vector<ExpertiseDefinition>();\n\t\tfor(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)\n\t\t{\n\t\t\tD=e.nextElement();\n\t\t\tif(((D.compiledFinalMask()==null)||(CMLib.masking().maskCheck(D.compiledFinalMask(),mob,true)))\n\t\t\t&&((D.compiledListMask()==null)||(CMLib.masking().maskCheck(D.compiledListMask(),mob,true))))\n\t\t\t\tV.add(D);\n\t\t}\n\t\tfinal PlayerStats pStats = mob.playerStats();\n\t\tif(pStats != null)\n\t\t{\n\t\t\tfor(final ExpertiseDefinition def : pStats.getExtraQualifiedExpertises().values())\n\t\t\t{\n\t\t\t\tif(((def.compiledFinalMask()==null)||(CMLib.masking().maskCheck(def.compiledFinalMask(),mob,true)))\n\t\t\t\t&&((def.compiledListMask()==null)||(CMLib.masking().maskCheck(def.compiledListMask(),mob,true))))\n\t\t\t\t{\n\t\t\t\t\tD = completeEduMap.get(def.ID());\n\t\t\t\t\tif(D!=null)\n\t\t\t\t\t\tV.remove(D);\n\t\t\t\t\tV.add(def);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn V;\n\t}\n\n\t@Override\n\tpublic List<ExpertiseDefinition> myListableExpertises(final MOB mob)\n\t{\n\t\tExpertiseDefinition D=null;\n\t\tfinal List<ExpertiseDefinition> V=new Vector<ExpertiseDefinition>();\n\t\tfor(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)\n\t\t{\n\t\t\tD=e.nextElement();\n\t\t\tif((D.compiledListMask()==null)||(CMLib.masking().maskCheck(D.compiledListMask(),mob,true)))\n\t\t\t{\n\t\t\t\tV.add(D);\n\t\t\t}\n\t\t}\n\t\tfinal PlayerStats pStats = mob.playerStats();\n\t\tif(pStats != null)\n\t\t{\n\t\t\tfor(final ExpertiseDefinition def : pStats.getExtraQualifiedExpertises().values())\n\t\t\t{\n\t\t\t\tif((def.compiledListMask()==null)||(CMLib.masking().maskCheck(def.compiledListMask(),mob,true)))\n\t\t\t\t{\n\t\t\t\t\tD = completeEduMap.get(def.ID());\n\t\t\t\t\tif(D!=null)\n\t\t\t\t\t\tV.remove(D);\n\t\t\t\t\tV.add(def);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn V;\n\t}\n\n\t@Override\n\tpublic int getExpertiseLevelCached(final MOB mob, final String abilityID, final ExpertiseLibrary.XType code)\n\t{\n\t\tif((mob==null)||(code==null)||(abilityID==null)||(abilityID.length()==0))\n\t\t\treturn 0;\n\t\tint expertiseLvl=0;\n\t\tfinal int[][] usageCache=mob.getAbilityUsageCache(abilityID);\n\t\tif(usageCache[Ability.CACHEINDEX_EXPERTISE]!=null)\n\t\t\texpertiseLvl=usageCache[Ability.CACHEINDEX_EXPERTISE][code.ordinal()];\n\t\telse\n\t\t{\n\t\t\tfinal int[] xFlagCache=new int[ExpertiseLibrary.XType.values().length];\n\t\t\tfinal CharClass charClass = mob.baseCharStats().getCurrentClass();\n\t\t\tif(charClass == null)\n\t\t\t\treturn 0;\n\t\t\tfor(final ExpertiseLibrary.XType flag : ExpertiseLibrary.XType.values())\n\t\t\t{\n\t\t\t\txFlagCache[flag.ordinal()]=getExpertiseLevelCalced(mob,abilityID,flag)\n\t\t\t\t\t\t\t\t\t\t\t+charClass.addedExpertise(mob, flag, abilityID);\n\t\t\t}\n\t\t\tusageCache[Ability.CACHEINDEX_EXPERTISE]=xFlagCache;\n\t\t\texpertiseLvl = xFlagCache[code.ordinal()];\n\t\t}\n\t\treturn expertiseLvl;\n\t}\n\n\tprivate int getStageNumber(final ExpertiseDefinition D)\n\t{\n\t\tif(D.ID().startsWith(D.getBaseName()))\n\t\t{\n\t\t\tfinal String remID=D.ID().substring(D.getBaseName().length());\n\t\t\tif(CMath.isInteger(remID))\n\t\t\t\treturn CMath.s_int(remID);\n\t\t\tif(CMath.isRomanNumeral(remID))\n\t\t\t\treturn CMath.convertFromRoman(remID);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getHighestListableStageBySkill(final MOB mob, final String ableID, final ExpertiseLibrary.XType flag)\n\t{\n\t\tfinal String expertiseID = completeUsageMap[flag.ordinal()].get(ableID);\n\t\tif(expertiseID == null)\n\t\t\treturn 0;\n\t\tExpertiseDefinition D=null;\n\t\tint max=0;\n\t\tfor(final Enumeration<ExpertiseDefinition> e=definitions();e.hasMoreElements();)\n\t\t{\n\t\t\tD=e.nextElement();\n\t\t\tif(expertiseID.equals(D.getBaseName()))\n\t\t\t{\n\t\t\t\tif((D.compiledListMask()==null)||(CMLib.masking().maskCheck(D.compiledListMask(),mob,true)))\n\t\t\t\t{\n\t\t\t\t\tfinal int stage=getStageNumber(D);\n\t\t\t\t\tif(stage > max)\n\t\t\t\t\t\tmax=stage;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinal PlayerStats pStats = mob.playerStats();\n\t\tif(pStats != null)\n\t\t{\n\t\t\tfor(final ExpertiseDefinition def : pStats.getExtraQualifiedExpertises().values())\n\t\t\t{\n\t\t\t\tif(expertiseID.equals(def.getBaseName()))\n\t\t\t\t{\n\t\t\t\t\tif((def.compiledListMask()==null)||(CMLib.masking().maskCheck(def.compiledListMask(),mob,true)))\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal int stage=getStageNumber(def);\n\t\t\t\t\t\tif(stage > max)\n\t\t\t\t\t\t\tmax=stage;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn max;\n\t}\n\n\t@Override\n\tpublic int numExpertises()\n\t{\n\t\treturn completeEduMap.size();\n\t}\n\n\tprivate String expertMath(String s,final int l)\n\t{\n\t\tint x=s.indexOf(\'{\');\n\t\twhile(x>=0)\n\t\t{\n\t\t\tfinal int y=s.indexOf(\'}\',x);\n\t\t\tif(y<0)\n\t\t\t\tbreak;\n\t\t\ts=s.substring(0,x)+CMath.parseIntExpression(s.substring(x+1,y))+s.substring(y+1);\n\t\t\tx=s.indexOf(\'{\');\n\t\t}\n\t\treturn s;\n\t}\n\n\t@Override\n\tpublic List<String> getStageCodes(String baseExpertiseCode)\n\t{\n\t\tString key=null;\n\t\tif(baseExpertiseCode==null)\n\t\t\treturn new ReadOnlyVector<String>(1);\n\t\tbaseExpertiseCode=baseExpertiseCode.toUpperCase();\n\t\tList<String> set=baseEduSetLists.get(baseExpertiseCode);\n\t\tif(set == null)\n\t\t{\n\t\t\tsynchronized(CMClass.getSync(("ListedEduBuild:"+baseExpertiseCode)))\n\t\t\t{\n\t\t\t\tset=baseEduSetLists.get(baseExpertiseCode);\n\t\t\t\tif(set==null)\n\t\t\t\t{\n\t\t\t\t\tfinal List<String> codes=new LinkedList<String>();\n\t\t\t\t\tfor(final Enumeration<String> e=completeEduMap.keys();e.hasMoreElements();)\n\t\t\t\t\t{\n\t\t\t\t\t\tkey=e.nextElement();\n\t\t\t\t\t\tif(key.startsWith(baseExpertiseCode)\n\t\t\t\t\t\t&&(CMath.isInteger(key.substring(baseExpertiseCode.length()))||CMath.isRomanNumeral(key.substring(baseExpertiseCode.length()))))\n\t\t\t\t\t\t\tcodes.add(key);\n\t\t\t\t\t}\n\t\t\t\t\tset=new ReadOnlyVector<String>(codes);\n\t\t\t\t\tbaseEduSetLists.put(baseExpertiseCode, set);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn set;\n\t}\n\n\t@Override\n\tpublic int numStages(final String baseExpertiseCode)\n\t{\n\t\treturn getStageCodes(baseExpertiseCode).size();\n\t}\n\n\tprotected String getGuessedBaseExpertiseName(final String expertiseCode)\n\t{\n\t\tint lastBadChar=expertiseCode.length()-1;\n\t\twhile( (lastBadChar>=0)\n\t\t&&(CMath.isInteger(expertiseCode.substring(lastBadChar))||CMath.isRomanNumeral(expertiseCode.substring(lastBadChar))))\n\t\t\tlastBadChar--;\n\t\tif(lastBadChar<expertiseCode.length()-1)\n\t\t\treturn expertiseCode.substring(0,lastBadChar+1);\n\t\treturn expertiseCode;\n\t}\n\n\t@Override\n\tpublic List<String> getPeerStageCodes(final String expertiseCode)\n\t{\n\t\treturn getStageCodes(getGuessedBaseExpertiseName(expertiseCode));\n\t}\n\n\t@Override\n\tpublic String getApplicableExpertise(final String abilityID, final XType code)\n\t{\n\t\treturn completeUsageMap[code.ordinal()].get(abilityID);\n\t}\n\n\t@Override\n\tpublic String[] getApplicableExpertises(final String abilityID, final XType code)\n\t{\n\t\tif(code == null)\n\t\t{\n\t\t\tfinal Set<String> all=new TreeSet<String>();\n\t\t\tfor(final XType f : XType.values())\n\t\t\t{\n\t\t\t\tfinal String[] set=completeUsageMaps[f.ordinal()].get(abilityID);\n\t\t\t\tif(set != null)\n\t\t\t\t\tall.addAll(Arrays.asList(set));\n\t\t\t}\n\t\t\treturn all.toArray(new String[0]);\n\t\t}\n\t\treturn completeUsageMaps[code.ordinal()].get(abilityID);\n\t}\n\n\t@Override\n\tpublic ExpertiseDefinition getConfirmedDefinition(final MOB mob, final String ID)\n\t{\n\t\tif(mob == null)\n\t\t\t\tswitch(costType)\r\n\t\t\t\t{\r\n\t\t\t\tcase XP:\r\n\t\t\t\t\tCMLib.leveler().postExperience(student, null, "", value.intValue(), true);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase GOLD:\r\n\t\t\t\t\tCMLib.beanCounter().subtractMoney(student, value.doubleValue());\r\n\t\t\t\tif((e!=null)\n\t\t\t\t&&(e.getValue()!=null))\n\t\t\t\t{\n\t\t\t\t\tfinal int level = getConfirmedExpertiseLevel(mob, def.getBaseName(), e);\n\t\t\t\t\tif(level == e.second.intValue())\n\t\t\t\t\t\treturn def;\n\t\t\t\t\tif(level == 0)\n\t\t\t\t\t\treturn null;\n\t\t\t\t\treturn getDefinition(def.getBaseName()+level);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn completeEduMap.get(ID.trim().toUpperCase());\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected int getConfirmedExpertiseLevel(final MOB mob, final String baseID, final Pair<String,Integer> e)\n\t{\n\t\tif((!mob.isMonster())\n\t\t&&(!CMSecurity.isAllowedEverywhere(mob, SecFlag.ALLSKILLS)))\n\t\t{\n\t\t\tfinal ExpertiseDefinition def=getDefinition(baseID+e.getValue().toString());\n\t\t\tif((def == null)\n\t\t\t||(!CMLib.masking().maskCheck(def.compiledListMask(), mob, true))\n\t\t\t||(!CMLib.masking().maskCheck(def.compiledFinalMask(), mob, true)))\n\t\t\t{\n\t\t\t\tfinal List<String> defList = getStageCodes(baseID);\n\t\t\t\tfor(int i = defList.size()-e.getValue().intValue();i<defList.size();i++)\n\t\t\t\t{\n\t\t\t\t\tfinal ExpertiseDefinition def2=getDefinition(defList.get(i));\n\t\t\t\t\tif((def2 != null)\n\t\t\t\t\t&&(CMLib.masking().maskCheck(def2.compiledListMask(), mob, true))\n\t\t\t\t\t&&(CMLib.masking().maskCheck(def2.compiledFinalMask(), mob, true)))\n\t\t\t\t\t\treturn this.getStageNumber(def2);\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\treturn e.getValue().intValue();\n\t}\n\n\t@Override\n\tpublic int getExpertiseLevelCalced(final MOB mob, final String abilityID, final XType code)\n\t{\n\t\tfinal String[] applicableExpIDs = getApplicableExpertises(abilityID, code);\n\t\tif((applicableExpIDs==null)||(applicableExpIDs.length<1))\n\t\t\treturn 0;\n\t\tfinal Pair<String,Integer> e=mob.fetchExpertise(applicableExpIDs[0]);\n\t\tif((e!=null)\n\t\t&&(e.getValue()!=null))\n\t\t\treturn e.getValue().intValue();\n\t\t\t//return getConfirmedExpertiseLevel(mob, applicableExpIDs[0], e);\n\t\tif(applicableExpIDs.length<2)\n\t\t\treturn 0;\n\t\tfor(final String expID : applicableExpIDs)\n\t\t{\n\t\t\tfinal Pair<String,Integer> e2=mob.fetchExpertise(expID);\n\t\t\tif((e2!=null)\n\t\t\t&&(e2.getValue()!=null))\n\t\t\t\treturn e2.getValue().intValue();\n\t\t\t\t//return getConfirmedExpertiseLevel(mob, applicableExpIDs[0], e2);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic String confirmExpertiseLine(String row, String ID, final boolean addIfPossible)\n\t{\n\t\tint levels=0;\n\t\tfinal HashSet<String> flags=new HashSet<String>();\n\t\tString s=null;\n\t\tString skillMask=null;\n\t\tfinal String[] costs=new String[5];\n\t\tfinal String[] data=new String[0];\n\t\tString WKID=null;\n\t\tString name,WKname=null;\n\t\tString listMask,WKlistMask=null;\n\t\tString finalMask,WKfinalMask=null;\n\t\tList<String> skillsToRegister=null;\n\t\tExpertiseLibrary.ExpertiseDefinition def=null;\n\t\tboolean didOne=false;\n\t\tif(row.trim().startsWith("#")||row.trim().startsWith(";")||(row.trim().length()==0))\n\t\t\treturn null;\n\t\tint x=row.indexOf(\'=\');\n\t\tif(x<0)\n\t\t\treturn "Error: Invalid line! Not comment, whitespace, and does not contain an = sign!";\n\t\tif(row.trim().toUpperCase().startsWith("DATA_"))\n\t\t{\n\t\t\tfinal String lastID=ID;\n\t\t\tID=row.substring(0,x).toUpperCase();\n\t\t\trow=row.substring(x+1);\n\t\t\tID=ID.substring(5).toUpperCase();\n\t\t\tif(ID.length()==0)\n\t\t\t\tID=lastID;\n\t\t\tif((lastID==null)||(lastID.length()==0))\n\t\t\t\treturn "Error: No last expertise found for data: "+lastID+"="+row;\n\t\t\telse\n\t\t\tif(this.getDefinition(ID)!=null)\n\t\t\t{\n\t\t\t\tdef=getDefinition(ID);\n\t\t\t\tWKID=def.name().toUpperCase().replace(\' \',\'_\');\n\t\t\t\tif(addIfPossible)\n\t\t\t\t{\n\t\t\t\t\tdef.setStageNames(CMParms.parseCommas(row,true).toArray(new String[0]));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfinal List<String> stages=getStageCodes(ID);\n\t\t\t\tif(addIfPossible)\n\t\t\t\t{\n\t\t\t\t\tfor(int s1=0;s1<stages.size();s1++)\n\t\t\t\t\t{\n\t\t\t\t\t\tdef=getDefinition(stages.get(s1));\n\t\t\t\t\t\tif(def==null)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tdef.setStageNames(CMParms.parseCommas(row,true).toArray(new String[0]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif(row.trim().toUpperCase().startsWith("HELP_"))\n\t\t{\n\t\t\tfinal String lastID=ID;\n\t\t\tID=row.substring(0,x).toUpperCase();\n\t\t\trow=row.substring(x+1);\n\t\t\tID=ID.substring(5).toUpperCase();\n\t\t\tif(ID.length()==0)\n\t\t\t\tID=lastID;\n\t\t\tif((lastID==null)||(lastID.length()==0))\n\t\t\t\treturn "Error: No last expertise found for help: "+lastID+"="+row;\n\t\t\telse\n\t\t\tif(getDefinition(ID)!=null)\n\t\t\t{\n\t\t\t\tdef=getDefinition(ID);\n\t\t\t\tWKID=def.name().toUpperCase().replace(\' \',\'_\');\n\t\t\t\tif(addIfPossible)\n\t\t\t\t\thelpMap.put(WKID,row);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfinal List<String> stages=getStageCodes(ID);\n\t\t\t\tif((stages==null)||(stages.size()==0))\n\t\t\t\t\treturn "Error: Expertise not yet defined: "+ID+"="+row;\n\t\t\t\tdef=getDefinition(stages.get(0));\n\t\t\t\tif(def!=null)\n\t\t\t\t{\n\t\t\t\t\tWKID=def.name().toUpperCase().replace(\' \',\'_\');\n\t\t\t\t\tx=WKID.lastIndexOf(\'_\');\n\t\t\t\t\tif((x>=0)&&(CMath.isInteger(WKID.substring(x+1))||CMath.isRomanNumeral(WKID.substring(x+1))))\n\t\t\t\t\t{\n\t\t\t\t\t\tWKID=WKID.substring(0,x);\n\t\t\t\t\t\tif(addIfPossible)\n\t\t\t\t\t\tif(!helpMap.containsKey(WKID))\n\t\t\t\t\t\t\thelpMap.put(WKID,row+"\\n\\r(See help on "+def.name()+").");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(addIfPossible)\n\t\t\t\t{\n\t\t\t\t\tfor(int s1=0;s1<stages.size();s1++)\n\t\t\t\t\t{\n\t\t\t\t\t\tdef=getDefinition(stages.get(s1));\n\t\t\t\t\t\tif(def==null)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tWKID=def.name().toUpperCase().replace(\' \',\'_\');\n\t\t\t\t\t\tif(!helpMap.containsKey(WKID))\n\t\t\t\t\t\t\thelpMap.put(WKID,row);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tID=row.substring(0,x).toUpperCase();\n\t\trow=row.substring(x+1);\n\t\tfinal List<String> parts=CMParms.parseCommas(row,false);\n\t\tif(parts.size()!=11)\n\t\t\treturn "Error: Expertise row malformed (Requires 11 entries/10 commas): "+ID+"="+row;\n\t\tname=parts.get(0);\n\t\tif(name.length()==0)\n\t\t\treturn "Error: Expertise name ("+name+") malformed: "+ID+"="+row;\n\t\tif(!CMath.isInteger(parts.get(1)))\n\t\t\treturn "Error: Expertise num ("+(parts.get(1))+") malformed: "+ID+"="+row;\n\t\tlevels=CMath.s_int(parts.get(1));\n\t\tflags.clear();\n\t\tflags.addAll(CMParms.parseAny(parts.get(2).toUpperCase(),\'|\',true));\n\n\t\tskillMask=parts.get(3);\n\t\tif(skillMask.length()==0)\n\t\t\treturn "Error: Expertise skill mask ("+skillMask+") malformed: "+ID+"="+row;\n\t\tskillsToRegister=CMLib.masking().getAbilityEduReqs(skillMask);\n\t\tif(skillsToRegister.size()==0)\n\t\t{\n\t\t\tskillsToRegister=CMLib.masking().getAbilityEduReqs(skillMask);\n\t\t\treturn "Error: Expertise no skills ("+skillMask+") found: "+ID+"="+row;\n\t\t}\n\t\tlistMask=skillMask+" "+(parts.get(4));\n\t\tfinalMask=((parts.get(5)));\n\t\tfor(int i=6;i<11;i++)\n\t\t\tcosts[i-6]=parts.get(i);\n\t\tdidOne=false;\n\t\tfor(int u=0;u<completeUsageMap.length;u++)\n\t\t{\n\t\t\tdidOne=didOne||flags.contains(ExpertiseLibrary.XType.values()[u].name());\n\t\t}\n\t\tif(!didOne)\n\t\t\treturn "Error: No flags ("+parts.get(2).toUpperCase()+") were set: "+ID+"="+row;\n\t\tif(addIfPossible)\n\t\t{\n\t\t\tfinal Set<XType> fflags = new HashSet<XType>();\n\t\t\tfor(final String f : flags)\n\t\t\t{\n\t\t\t\tfinal XType fl = (XType)CMath.s_valueOf(XType.class, f.toUpperCase().trim());\n\t\t\t\tif(fl != null)\n\t\t\t\t\tfflags.add(fl);\n\t\t\t}\n\t\t\tfinal String baseName=CMStrings.replaceAll(CMStrings.replaceAll(ID,"@X2",""),"@X1","").toUpperCase();\n\t\t\tfor(int l=1;l<=levels;l++)\n\t\t\t{\n\t\t\t\tWKID=CMStrings.replaceAll(ID,"@X1",""+l);\n\t\t\t\tWKID=CMStrings.replaceAll(WKID,"@X2",""+CMath.convertToRoman(l));\n\t\t\t\tWKname=CMStrings.replaceAll(name,"@x1",""+l);\n\t\t\t\tWKname=CMStrings.replaceAll(WKname,"@x2",""+CMath.convertToRoman(l));\n\t\t\t\tWKlistMask=CMStrings.replaceAll(listMask,"@x1",""+l);\n\t\t\t\tWKlistMask=CMStrings.replaceAll(WKlistMask,"@x2",""+CMath.convertToRoman(l));\n\t\t\t\tWKfinalMask=CMStrings.replaceAll(finalMask,"@x1",""+l);\n\t\t\t\tWKfinalMask=CMStrings.replaceAll(WKfinalMask,"@x2",""+CMath.convertToRoman(l));\n\t\t\t\tif((l>1)&&(listMask.toUpperCase().indexOf("-EXPERT")<0))\n\t\t\t\t{\n\t\t\t\t\ts=CMStrings.replaceAll(ID,"@X1",""+(l-1));\n\t\t\t\t\ts=CMStrings.replaceAll(s,"@X2",""+CMath.convertToRoman(l-1));\n\t\t\t\t\tWKlistMask="-EXPERTISE \\"+"+s+"\\" "+WKlistMask;\n\t\t\t\t}\n\t\t\t\tWKlistMask=expertMath(WKlistMask,l);\n\t\t\t\tWKfinalMask=expertMath(WKfinalMask,l);\n\t\t\t\tdef=addDefinition(WKID,WKname,baseName,WKlistMask,WKfinalMask,costs,data);\n\t\t\t\tif(def!=null)\n\t\t\t\t{\n\t\t\t\t\tdef.addRawMasks(listMask, finalMask);\n\t\t\t\t\tdef.getFlagTypes().addAll(fflags);\n\t\t\t\t\tdef.compiledFinalMask();\n\t\t\t\t\tdef.compiledListMask();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tID=CMStrings.replaceAll(ID,"@X1","");\n\t\tID=CMStrings.replaceAll(ID,"@X2","");\n\t\tfor(int u=0;u<completeUsageMap.length;u++)\n\t\t{\n\t\t\tif(flags.contains(ExpertiseLibrary.XType.values()[u].name()))\n\t\t\t{\n\t\t\t\tfor(int k=0;k<skillsToRegister.size();k++)\n\t\t\t\t{\n\t\t\t\t\tfinal String skid = skillsToRegister.get(k);\n\t\t\t\t\tcompleteUsageMap[u].put(skid, ID);\n\t\t\t\t\tif(!completeUsageMaps[u].containsKey(skid))\n\t\t\t\t\t\tcompleteUsageMaps[u].put(skid, new String[]{ID});\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal String[] oldSet=completeUsageMaps[u].get(skid);\n\t\t\t\t\t\tfinal String[] newSet=Arrays.copyOf(oldSet, oldSet.length+1);\n\t\t\t\t\t\tnewSet[newSet.length-1]=ID;\n\t\t\t\t\t\tcompleteUsageMaps[u].put(skid, newSet);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn addIfPossible?ID:null;\n\t}\n\n\t@Override\n\tpublic String getExpertiseInstructions()\n\t{\n\t\tfinal StringBuilder inst = new StringBuilder("");\n\t\tfinal String expertiseFilename="skills/expertises.txt";\n\t\tfinal List<String> V=Resources.getFileLineVector(Resources.getFileResource(expertiseFilename,true));\n\t\tfor(int v=0;v<V.size();v++)\n\t\t{\n\t\t\tif(V.get(v).startsWith("#"))\n\t\t\t\tinst.append(V.get(v).substring(1)+"\\n\\r");\n\t\t\telse\n\t\t\tif(V.get(v).length()>0)\n\t\t\t\tbreak;\n\t\t}\n\t\treturn inst.toString();\n\t}\n\n\tprotected List<String> getExpertiseLines()\n\t{\n\t\tfinal String expertiseFilename="skills/expertises.txt";\n\t\tfinal List<String> V=Resources.getFileLineVector(Resources.getFileResource(expertiseFilename,true));\n\t\tResources.removeResource(Resources.makeFileResourceName("skills/expertises.txt"));\n\t\tfor(int i=2;i<99;i++)\n\t\t{\n\t\t\tfinal String fileName=Resources.getRawFileResourceName(expertiseFilename, false)+"."+i;\n\t\t\tfinal StringBuffer buf = Resources.getFileResource(expertiseFilename+"."+i,false);\n\t\t\tResources.removeResource(fileName);\n\t\t\tif(buf.length()==0)\n\t\t\t\tbreak;\n\t\t\tV.add("#FILE:"+fileName);\n\t\t\tV.addAll(Resources.getFileLineVector(buf));\n\t\t}\n\t\treturn V;\n\t}\n\n\t@Override\n\tpublic void recompileExpertises()\n\t{\n\t\tfor(int u=0;u<completeUsageMap.length;u++)\n\t\t\tcompleteUsageMap[u]=new Hashtable<String,String>();\n\t\tfor(int u=0;u<completeUsageMaps.length;u++)\n\t\t\tcompleteUsageMaps[u]=new Hashtable<String,String[]>();\n\t\thelpMap.clear();\n\t\tfinal List<String> V = getExpertiseLines();\n\t\tString ID=null,WKID=null;\n\t\tfor(int v=0;v<V.size();v++)\n\t\t{\n\t\t\tfinal String row=V.get(v);\n\t\t\tWKID=this.confirmExpertiseLine(row,ID,true);\n\t\t\tif(WKID==null)\n\t\t\t\tcontinue;\n\t\t\tif(WKID.startsWith("Error: "))\n\t\t\t\tLog.errOut("ColumbiaUniv",WKID);\n\t\t\telse\n\t\t\t\tID=WKID;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Iterator<String> filterUniqueExpertiseIDList(final Iterator<String> i)\n\t{\n\t\tfinal Set<String> ids=new HashSet<String>();\n\t\tfor(;i.hasNext();)\n\t\t{\n\t\t\tfinal String id=i.next();\n\t\t\tfinal ExpertiseDefinition def=getDefinition(id);\n\t\t\tif((def != null)\n\t\t\t&&(!(def.ID().equals(def.getBaseName()+"1")||def.ID().equals(def.getBaseName()+"I")||(def.ID().equals(def.getBaseName())))))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tids.add(id);\n\t\t}\n\t\treturn ids.iterator();\n\t}\n\n\tprotected Object parseLearnID(final String msg)\n\t{\n\t\tif(msg==null)\n\t\t\treturn null;\n\t\tint learnStart=msg.indexOf("^<LEARN");\n\t\tif(learnStart>=0)\n\t\t{\n\t\t\tint end=-1;\n\t\t\tlearnStart=msg.indexOf("\\"",learnStart+1);\n\t\t\tif(learnStart>=0)\n\t\t\t\tend=msg.indexOf("\\"",learnStart+1);\n\t\t\tif(end>learnStart)\n\t\t\t{\n\t\t\t\tfinal String ID=msg.substring(learnStart+1,end);\n\t\t\t\tfinal Ability A=CMClass.getAbility(ID);\n\t\t\t\tif(A!=null)\n\t\t\t\t\treturn A;\n\t\t\t\tfinal ExpertiseDefinition X = this.findDefinition(ID, true);\n\t\t\t\tif(X!=null)\n\t\t\t\t\treturn X;\n\t\t\t\treturn CMClass.getObjectOrPrototype(ID);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean canBeTaught(final MOB teacher, final MOB student, final Environmental item, final String msg)\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\treturn false;\n\t\t}\n\n\t\tif((student.isAttributeSet(MOB.Attrib.NOTEACH))\n\t\t&&((!student.isMonster())||(!student.willFollowOrdersOf(teacher))))\n\t\t{\n\t\t\tif(teacher.isMonster())\n\t\t\t\tCMLib.commands().postSay(teacher,student,L("You are refusing training at this time."),true,false);\n\t\t\telse\n\t\t\t\tteacher.tell(L("@x1 is refusing training at this time.",student.name()));\n\t\t\treturn false;\n\t\t}\n\n\t\tObject learnThis=item;\n\t\tif(learnThis==null)\n\t\t\tlearnThis=parseLearnID(msg);\n\n\t\tString teachWhat="";\n\t\tif(learnThis instanceof Ability)\n\t\t{\n\t\t\tfinal Ability theA=(Ability)learnThis;\n\t\t\tteachWhat=theA.name();\n\t\t\tif(!theA.canBeTaughtBy(teacher,student))\n\t\t\t\treturn false;\n\t\t\tif(!theA.canBeLearnedBy(teacher,student))\n\t\t\t\treturn false;\n\t\t\tfinal Ability studA=student.fetchAbility(theA.ID());\n\t\t\tif((studA!=null)&&(studA.isSavable()))\n\t\t\t{\n\t\t\t\tif(teacher.isMonster())\n\t\t\t\t\tCMLib.commands().postSay(teacher,student,L("You already know \'@x1\'.",teachWhat),true,false);\n\t\t\t\telse\n\t\t\t\t\tteacher.tell(L("@x1 already knows how to do that.",student.name()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse\n\t\tif(learnThis instanceof ExpertiseDefinition)\n\t\t{\n\t\t\tfinal ExpertiseDefinition theExpertise=(ExpertiseDefinition)learnThis;\n\t\t\tteachWhat=theExpertise.name();\n\t\t\tif(student.fetchExpertise(theExpertise.ID())!=null)\n\t\t\t{\n\t\t\t\tif(teacher.isMonster())\n\t\t\t\t\tCMLib.commands().postSay(teacher,student,L("You already know @x1",theExpertise.name()),true,false);\n\t\t\t\telse\n\t\t\t\t\tteacher.tell(L("@x1 already knows @x2",student.name(),theExpertise.name()));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif(!myQualifiedExpertises(student).contains(theExpertise))\n\t\t\t{\n\t\t\t\tif(teacher.isMonster())\n\t\t\t\t{\n\t\t\t\t\tCMLib.commands().postSay(teacher,student,\n\t\t\t\t\t\t\tL("I\'m sorry, you do not yet fully qualify for the expertise \'@x1\'.\\n\\rRequirements: @x2",\n\t\t\t\t\t\t\ttheExpertise.name(),CMLib.masking().maskDesc(theExpertise.allRequirements())),true,false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tteacher.tell(L("@x1 does not yet fully qualify for the expertise \'@x2\'.\\n\\rRequirements: @x3",\n\t\t\t\t\t\t\tstudent.name(),theExpertise.name(),CMLib.masking().maskDesc(theExpertise.allRequirements())));\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(!theExpertise.meetsCostRequirements(student))\n\t\t\t{\n\t\t\t\tif(teacher.isMonster())\n\t\t\t\t{\n\t\t\t\t\tCMLib.commands().postSay(teacher,student,L("I\'m sorry, but to learn the expertise \'@x1\' requires: @x2",\n\t\t\t\t\t\ttheExpertise.name(),theExpertise.costDescription()),true,false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tteacher.tell(L("Training for that expertise requires @x1.",theExpertise.costDescription()));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tteachWhat=theExpertise.name();\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void handleBeingTaught(final MOB teacher, final MOB student, final Environmental item, final String msg, final int add)\n\t{\n\t\tObject learnThis=item;\n\t\tif(learnThis==null)\n\t\t\tlearnThis=parseLearnID(msg);\n\n\t\tif(learnThis instanceof Ability)\n\t\t{\n\t\t\tfinal Ability theA=(Ability)learnThis;\n\t\t\tteacher.charStats().setStat(CharStats.STAT_WISDOM, teacher.charStats().getStat(CharStats.STAT_WISDOM)+5);\n\t\t\tteacher.charStats().setStat(CharStats.STAT_INTELLIGENCE, teacher.charStats().getStat(CharStats.STAT_INTELLIGENCE)+5);\n\t\t\tfinal Set<String> oldSkillSet = new HashSet<String>();\n\t\t\tfor(final Enumeration<Ability> a=student.abilities();a.hasMoreElements();)\n\t\t\t\toldSkillSet.add(a.nextElement().ID());\n\t\t\ttheA.teach(teacher,student);\n\t\t\tteacher.recoverCharStats();\n\t\t\tfinal Ability studentA=student.fetchAbility(theA.ID());\n\t\t\tif((studentA==null) && (!oldSkillSet.contains(theA.ID())))\n\t\t\t\tstudent.tell(L("You failed to understand @x1.",theA.name()));\n\t\t\telse\n\t\t\tif((!teacher.isMonster()) && (!student.isMonster()))\n\t\t\t\tCMLib.leveler().postExperience(teacher, null, null, 100, false);\n\t\t\tif((studentA!=null)\n\t\t\t&& (!oldSkillSet.contains(theA.ID()))\n\t\t\t&& (add!=0))\n\t\t\t{\n\t\t\t\tint newProficiency=studentA.proficiency() + add;\n\t\t\t\tif(newProficiency > 75)\n\t\t\t\t\tnewProficiency = 75;\n\t\t\t\tif(newProficiency < 0)\n\t\t\t\t\tnewProficiency = 0;\n\t\t\t\tstudentA.setProficiency(newProficiency);\n\t\t\t\tfinal Ability studentEffA = student.fetchEffect(theA.ID());\n\t\t\t\tif(studentEffA != null)\n\t\t\t\t\tstudentEffA.setProficiency(newProficiency);\n\t\t\t}\n\t\t}\n\t\telse\n\t\tif(learnThis instanceof ExpertiseDefinition)\n\t\t{\n\t\t\tfinal ExpertiseDefinition theExpertise=(ExpertiseDefinition)learnThis;\n\t\t\ttheExpertise.spendCostRequirements(student);\n\t\t\tstudent.addExpertise(theExpertise.ID());\n\t\t\tif((!teacher.isMonster()) && (!student.isMonster()))\n\t\t\t\tCMLib.leveler().postExperience(teacher, null, null, 100, false);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean confirmAndTeach(final MOB teacherM, final MOB studentM, final CMObject teachableO, final Runnable callBack)\n\t{\n\t\tif((teacherM==null)||(studentM==null))\n\t\t{\n\t\t\tif(callBack != null)\n\t\t\t\tcallBack.run();\n\t\t\treturn false;\n\t\t}\n\t\tfinal Session sess=studentM.session();\n\t\tfinal Room R=studentM.location();\n\t\tif((sess==null)\n\t\t||(R==null)\n\t\t||((!teacherM.isPlayer())\n\t\t\t&&((teacherM.amFollowing()==null)||(!teacherM.amUltimatelyFollowing().isPlayer()))))\n\t\t{\n\t\t\tfinal boolean success=postTeach(teacherM,studentM,teachableO);\n\t\t\tif(callBack != null)\n\t\t\t\tcallBack.run();\n\t\t\treturn success;\n\t\t}\n\n\t\tfinal String name;\n\t\tif(teachableO instanceof Ability)\n\t\t\tname=((Ability)teachableO).Name();\n\t\telse\n\t\tif(teachableO instanceof ExpertiseLibrary.ExpertiseDefinition)\n\t\t\tname=((ExpertiseLibrary.ExpertiseDefinition)teachableO).name();\n\t\telse\n\t\t\tname=L("Something");\n\n\t\tfinal Environmental tool=(teachableO instanceof Environmental)?(Environmental)teachableO:null;\n\t\tfinal String teachWhat=teachableO.name();\n\t\tfinal String ID=teachableO.ID();\n\t\tfinal CMMsg msg=CMClass.getMsg(teacherM,studentM,tool,CMMsg.MSG_TEACH,null,teachWhat,ID);\n\t\tif((!R.show(teacherM, studentM, CMMsg.MSG_SPEAK, L("<S-NAME> offer(s) to teach <T-NAME>.")))\n\t\t||(!R.okMessage(teacherM, msg)))\n\t\t{\n\t\t\tif(callBack != null)\n\t\t\t\tcallBack.run();\n\t\t\treturn false;\n\t\t}\n\n\t\tsess.prompt(new InputCallback(InputCallback.Type.CONFIRM,"N",10000)\n\t\t{\n\t\t\tfinal MOB teacher = teacherM;\n\t\t\tfinal MOB student = studentM;\n\t\t\tfinal Session session = sess;\n\t\t\tfinal CMObject teachable=teachableO;\n\t\t\tfinal String teachWhat= name;\n\t\t\tfinal Runnable postCallback=callBack;\n\n\t\t\t@Override\n\t\t\tpublic void showPrompt()\n\t\t\t{\n\t\t\t\tsess.promptPrint(L("\\n\\r@x1 wants to teach you @x2.  Is this Ok (y/N)?",teacher.Name(),teachWhat));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void timedOut()\n\t\t\t{\n\t\t\t\tteacher.tell(L("@x1 does not answer you.",student.charStats().HeShe()));\n\t\t\t\tif(postCallback != null)\n\t\t\t\t\tpostCallback.run();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void callBack()\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tif(this.input.equals("Y"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(studentM.location()!=teacherM.location())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstudentM.tell(L("@x1 vanished.",teacherM.name()));\n\t\t\t\t\t\t\tteacherM.tell(L("@x1 vanished.",studentM.name()));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpostTeach(teacher,student,teachable);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif(!session.isStopped())\n\t\t\t\t\t\t\tsession.println("\\n\\r");\n\t\t\t\t\t\tteacher.tell(L("@x1 does not want you to.",student.charStats().HeShe()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinally\n\t\t\t\t{\n\t\t\t\t\tif(postCallback != null)\n\t\t\t\t\t\tpostCallback.run();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean postTeach(final MOB teacher, final MOB student, final CMObject teachObj)\n\t{\n\t\tCMMsg msg=CMClass.getMsg(teacher,student,null,CMMsg.MSG_SPEAK,null);\n\t\tif(!teacher.location().okMessage(teacher,msg))\n\t\t\treturn false;\n\t\tfinal Environmental tool=(teachObj instanceof Environmental)?(Environmental)teachObj:null;\n\t\tfinal String teachWhat=teachObj.name();\n\t\tfinal String ID=teachObj.ID();\n\t\tmsg=CMClass.getMsg(teacher,student,tool,CMMsg.MSG_TEACH,L("<S-NAME> teach(es) <T-NAMESELF> \'@x1\' ^<LEARN NAME=\\"@x2\\" /^>.",teachWhat,ID));\n\t\tif(!teacher.location().okMessage(teacher,msg))\n\t\t\treturn false;\n\t\tteacher.location().send(teacher,msg);\n\t\treturn true;\n\t}\n\n\tprotected ExpertiseDefinition createNewExpertiseDefinition(final String ID, final String name, final String baseName)\n\t{\n\t\tfinal ExpertiseDefinition definition = new ExpertiseDefinition()\n\t\t{\n\t\t\tprivate String\t\t\t\t\t\t\t\tID\t\t\t\t\t= "";\n\t\t\tprivate String\t\t\t\t\t\t\t\tname\t\t\t\t= "";\n\t\t\tprivate String\t\t\t\t\t\t\t\tbaseName\t\t\t= "";\n\t\t\tprivate String[]\t\t\t\t\t\t\tdata\t\t\t\t= new String[0];\n\t\t\tprivate String\t\t\t\t\t\t\t\tuncompiledListMask\t= "";\n\t\t\tprivate String\t\t\t\t\t\t\t\tuncompiledFinalMask\t= "";\n\t\t\tprivate String\t\t\t\t\t\t\t\trawListMask\t\t\t= "";\n\t\t\tprivate String\t\t\t\t\t\t\t\trawFinalMask\t\t= "";\n\t\t\tprivate int\t\t\t\t\t\t\t\t\tminLevel\t\t\t= Integer.MIN_VALUE + 1;\n\t\t\tprivate MaskingLibrary.CompiledZMask\t\tcompiledListMask\t= null;\n\t\t\tprivate final ExpertiseDefinition\t\t\tparent\t\t\t\t= null;\n\t\t\tprivate MaskingLibrary.CompiledZMask\t\tcompiledFinalMask\t= null;\n\t\t\tprivate final List<CostManager>\t\t\t\tcosts\t\t\t\t= new LinkedList<CostManager>();\n\t\t\tprivate final Set<XType>\t\t\t\t\txTypes\t\t\t\t= new HashSet<XType>();\n\n\t\t\t@Override\n\t\t\tpublic String getBaseName()\n\t\t\t{\n\t\t\t\treturn baseName;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setBaseName(final String baseName)\n\t\t\t{\n\t\t\t\tthis.baseName = baseName;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setName(final String name)\n\t\t\t{\n\t\t\t\tthis.name = name;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setID(final String ID)\n\t\t\t{\n\t\t\t\tthis.ID = ID;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setStageNames(final String[] data)\n\t\t\t{\n\t\t\t\tthis.data = data;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ExpertiseDefinition getParent()\n\t\t\t{\n\t\t\t\treturn parent;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String name()\n\t\t\t{\n\t\t\t\treturn name;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int getMinimumLevel()\n\t\t\t{\n\t\t\t\tif(minLevel==Integer.MIN_VALUE+1)\n\t\t\t\t\tminLevel=CMLib.masking().minMaskLevel(allRequirements(),0);\n\t\t\t\treturn minLevel;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String[] getStageNames()\n\t\t\t{\n\t\t\t\treturn data;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic MaskingLibrary.CompiledZMask compiledListMask()\n\t\t\t{\n\t\t\t\tif((this.compiledListMask==null)&&(uncompiledListMask.length()>0))\n\t\t\t\t{\n\t\t\t\t\tcompiledListMask=CMLib.masking().maskCompile(uncompiledListMask);\n\t\t\t\t\tCMLib.ableMapper().addPreRequisites(ID,new Vector<String>(),uncompiledListMask.trim());\n\t\t\t\t}\n\t\t\t\treturn this.compiledListMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic MaskingLibrary.CompiledZMask compiledFinalMask()\n\t\t\t{\n\t\t\t\tif((this.compiledFinalMask==null)&&(uncompiledFinalMask.length()>0))\n\t\t\t\t{\n\t\t\t\t\tthis.compiledFinalMask=CMLib.masking().maskCompile(uncompiledFinalMask);\n\t\t\t\t\tCMLib.ableMapper().addPreRequisites(ID,new Vector<String>(),uncompiledFinalMask.trim());\n\t\t\t\t}\n\t\t\t\treturn this.compiledFinalMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String allRequirements()\n\t\t\t{\n\t\t\t\tString req=uncompiledListMask;\n\t\t\t\tif(req==null)\n\t\t\t\t\treq="";\n\t\t\t\telse\n\t\t\t\t\treq=req.trim();\n\t\t\t\tif((uncompiledFinalMask!=null)&&(uncompiledFinalMask.length()>0))\n\t\t\t\t\treq=req+" "+uncompiledFinalMask;\n\t\t\t\treturn req.trim();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String listRequirements()\n\t\t\t{\n\t\t\t\treturn uncompiledListMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String finalRequirements()\n\t\t\t{\n\t\t\t\treturn uncompiledFinalMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void addListMask(final String mask)\n\t\t\t{\n\t\t\t\tif((mask==null)||(mask.length()==0))\n\t\t\t\t\treturn;\n\t\t\t\tif(uncompiledListMask==null)\n\t\t\t\t\tuncompiledListMask=mask;\n\t\t\t\telse\n\t\t\t\t\tuncompiledListMask+=mask;\n\t\t\t\tcompiledListMask=null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void addFinalMask(final String mask)\n\t\t\t{\n\t\t\t\tif((mask==null)||(mask.length()==0))\n\t\t\t\t\treturn;\n\t\t\t\tif(uncompiledFinalMask==null)\n\t\t\t\t\tuncompiledFinalMask=mask;\n\t\t\t\telse\n\t\t\t\t\tuncompiledFinalMask+=mask;\n\t\t\t\tcompiledFinalMask=CMLib.masking().maskCompile(uncompiledFinalMask);\n\t\t\t\tCMLib.ableMapper().addPreRequisites(ID,new Vector<String>(),uncompiledFinalMask.trim());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void addRawMasks(final String listMask, final String finalMask)\n\t\t\t{\n\t\t\t\tthis.rawListMask = listMask;\n\t\t\t\tthis.rawFinalMask = finalMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String rawListMask()\n\t\t\t{\n\t\t\t\treturn this.rawListMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String rawFinalMask()\n\t\t\t{\n\t\t\t\treturn this.rawFinalMask;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void addCost(final CostType type, final Double value)\n\t\t\t{\n\t\t\t\tcosts.add(CMLib.utensils().createCostManager(type,value));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String costDescription()\n\t\t\t{\n\t\t\t\tfinal StringBuffer costStr=new StringBuffer("");\n\t\t\t\tfor(final CostManager cost : costs)\n\t\t\t\t\tcostStr.append(cost.requirements(null)).append(", ");\n\t\t\t\tif(costStr.length()==0)\n\t\t\t\t\treturn "";\n\t\t\t\treturn costStr.substring(0,costStr.length()-2);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean meetsCostRequirements(final MOB mob)\n\t\t\t{\n\t\t\t\tfor(final CostManager cost : costs)\n\t\t\t\t{\n\t\t\t\t\tif(!cost.doesMeetCostRequirements(mob))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void spendCostRequirements(final MOB mob)\n\t\t\t{\n\t\t\t\tfor(final CostManager cost : costs)\n\t\t\t\t\tcost.doSpend(mob);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int compareTo(final CMObject o)\n\t\t\t{\n\t\t\t\treturn (o == this) ? 0 : 1;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String ID()\n\t\t\t{\n\t\t\t\treturn ID;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic CMObject newInstance()\n\t\t\t{\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic CMObject copyOf()\n\t\t\t{\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void initializeClass()\n\t\t\t{\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<XType> getFlagTypes()\n\t\t\t{\n\t\t\t\treturn xTypes;\n\t\t\t}\n\t\t};\n\t\tdefinition.setID(ID.toUpperCase());\n\t\tdefinition.setName(name);\n\t\tdefinition.setBaseName(baseName);\n\t\treturn definition;\n\t}\n}\n', 'message': 'negating experience costs\n\ngit-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@20191 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'author_name': 'bozimmerman', 'lang': 'java'}