/
com/planet_ink/coffee_mud/Abilities/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Areas/interfaces/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Software/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/Locales/interfaces/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/MOBS/interfaces/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/application/
com/planet_ink/coffee_mud/core/smtp/
com/planet_ink/siplet/applet/
lib/
resources/examples/
resources/fakedb/
resources/quests/delivery/
resources/quests/diseased/
resources/quests/drowning/
resources/quests/gobwar/
resources/quests/holidays/
resources/quests/robbed/
resources/quests/smurfocide/
resources/quests/stolen/
resources/quests/templates/
resources/quests/treasurehunt/
resources/quests/vengeance/
web/
web/admin.templates/
web/admin/images/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
package com.planet_ink.coffee_mud.Libraries;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.AbilityMapper.AbilityMapping;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/* 
   Copyright 2000-2006 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 CMAble extends StdLibrary implements AbilityMapper
{
    public String ID(){return "CMAble";}
	public Hashtable completeAbleMap=new Hashtable();
	public Hashtable lowestQualifyingLevelMap=new Hashtable();
	public Hashtable allows=new Hashtable();
    public Hashtable completeDomainMap=new Hashtable();
    public Hashtable reverseAbilityMap=new Hashtable();

	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  boolean autoGain)
	{ addCharAbilityMapping(ID,qualLevel,ability,0,"",autoGain,false,new Vector(),""); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  boolean autoGain,
									  String extraMasks)
	{ addCharAbilityMapping(ID,qualLevel,ability,0,"",autoGain,false,new Vector(),extraMasks); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  boolean autoGain,
									  Vector skillPreReqs)
	{ addCharAbilityMapping(ID,qualLevel,ability,0,"",autoGain,false,skillPreReqs,""); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  boolean autoGain,
									  Vector skillPreReqs,
									  String extraMasks)
	{ addCharAbilityMapping(ID,qualLevel,ability,0,"",autoGain,false,skillPreReqs,extraMasks); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  String defParm,
									  boolean autoGain)
	{ addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,defParm,autoGain,false,new Vector(),""); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  String defParm,
									  boolean autoGain,
									  String extraMasks)
	{ addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,defParm,autoGain,false,new Vector(),extraMasks); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  boolean autoGain)
	{ addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,"",autoGain,false,new Vector(),""); }
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  boolean autoGain,
									  String extraMasks)
	{ addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,"",autoGain,false,new Vector(),extraMasks); }
	
	public void delCharAbilityMapping(String ID, String ability)
	{
		if(!completeAbleMap.containsKey(ID))
			completeAbleMap.put(ID,new Hashtable());
		Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
		if(ableMap.containsKey(ability))
			ableMap.remove(ability);
		DVector reV=(DVector)reverseAbilityMap.get(ability);
		if(reV!=null) reV.removeElement(ID);
	}
	public void delCharMappings(String ID)
	{
		if(completeAbleMap.containsKey(ID))
			completeAbleMap.remove(ID);
		for(Enumeration e=reverseAbilityMap.keys();e.hasMoreElements();)
		{
			String ability=(String)e.nextElement();
			DVector reV=(DVector)reverseAbilityMap.get(ability);
			reV.removeElement(ID);
		}
	}
	
	public Enumeration getClassAbles(String ID)
	{
		if(!completeAbleMap.containsKey(ID))
			completeAbleMap.put(ID,new Hashtable());
		Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
		return ableMap.elements();
	}
	
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  String defaultParam,
									  boolean autoGain,
									  boolean secret)
	{ addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,defaultParam,autoGain,secret,new Vector(),"");}
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  String defaultParam,
									  boolean autoGain,
									  boolean secret,
									  String extraMasks)
	{ addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,defaultParam,autoGain,secret,new Vector(),extraMasks);}
    
    public void addCharAbilityMapping(String ID, 
                                      int qualLevel,
                                      String ability, 
                                      int defaultProficiency,
                                      String defaultParam,
                                      boolean autoGain,
                                      boolean secret,
                                      Vector preReqSkillsList,
                                      String extraMask)
    { addCharAbilityMapping(ID,qualLevel,ability,defaultProficiency,defaultParam,autoGain,secret,preReqSkillsList,extraMask,null);}
	
	public void addPreRequisites(String ID, Vector preReqSkillsList, String extraMask)
	{
		if(preReqSkillsList==null) return;
		for(int v=0;v<preReqSkillsList.size();v++)
		{
			String s=(String)preReqSkillsList.elementAt(v);
			int x=s.indexOf("(");
			if((x>=0)&&(s.endsWith(")")))
				s=s.substring(0,x);
			if((s.indexOf("*")>=0)||(s.indexOf(",")>=0))
			{
				String ID2=ID;
				while(allows.contains("*"+ID2))
					ID2="*"+ID2;
				allows.put("*"+ID2,s);
			}
			else
			{
				Vector V=(Vector)allows.get(s);
				if(V==null){ V=new Vector(); allows.put(s,V);}
				if(!V.contains(ID))V.addElement(ID);
			}
		}
		if((extraMask!=null)&&(extraMask.trim().length()>0))
		{
			Vector preReqsOf=CMLib.masking().getAbilityEduReqs(extraMask);
			for(int v=0;v<preReqsOf.size();v++)
			{
				String s=(String)preReqsOf.elementAt(v);
				if((s.indexOf("*")>=0)||(s.indexOf(",")>=0))
				{
					String ID2=ID;
					while(allows.contains("*"+ID2))
						ID2="*"+ID2;
					allows.put("*"+ID2,s);
				}
				else
				{
					Vector V=(Vector)allows.get(s);
					if(V==null){ V=new Vector(); allows.put(s,V);}
					if(!V.contains(ID))V.addElement(ID);
				}
			}
		}
	}
	
    public boolean isDomainIncludedInAnyAbility(int domain, int acode)
    {
        Vector V=(Vector)completeDomainMap.get(new Integer(domain));
        if(V==null)
        {
            Ability A=null;
            V=new Vector();
            completeDomainMap.put(new Integer(domain),V);
            for(Enumeration e=CMClass.abilities();e.hasMoreElements();)
            {
                A=(Ability)e.nextElement();
                if(((A.classificationCode()&Ability.ALL_DOMAINS)==domain)
                &&(!V.contains(new Integer((A.classificationCode()&Ability.ALL_ACODES)))))
                    V.addElement(new Integer((A.classificationCode()&Ability.ALL_ACODES)));
            }
        }
        return V.contains(new Integer(acode));
    }
    
    public DVector getClassAllowsList(String classID)
    {
        DVector ABLES=getUpToLevelListings(classID,CMProps.getIntVar(CMProps.SYSTEMI_LASTPLAYERLEVEL),false,false);
        Hashtable alreadyDone=new Hashtable();
        DVector DV=new DVector(2);
    	AbilityMapping able=null;
        Vector V2=null;
        Integer Ix=null;
        for(int a=0;a<ABLES.size();a++)
        {
            V2=getAbilityAllowsList((String)ABLES.elementAt(a,1));
            if((V2==null)||(V2.size()==0)) continue;
            able=(AbilityMapping)ABLES.elementAt(a,2);
            for(int v2=0;v2<V2.size();v2++)
            {
            	Ix=(Integer)alreadyDone.get(V2.elementAt(v2));
            	if(Ix==null)
            	{
	                alreadyDone.put(V2.elementAt(v2), new Integer(DV.size()));
	                DV.addElement(V2.elementAt(v2),new Integer(able.qualLevel));
            	}
            	else
            	if(((Integer)DV.elementAt(Ix.intValue(),2)).intValue()>able.qualLevel)
            		DV.setElementAt(Ix.intValue(),2,new Integer(able.qualLevel));
            }
        }
        return DV;
    }
    
	public Vector getAbilityAllowsList(String ableID)
	{
		String KEYID=null;
		String abilityID=null;
		Vector remove=null;
		for(Enumeration e=allows.keys();e.hasMoreElements();) 
		{
			KEYID=(String)e.nextElement();
			if(KEYID.startsWith("*"))
			{
				abilityID=(String)allows.get(KEYID);
				if(abilityID.startsWith("*")||abilityID.endsWith("*")||(abilityID.indexOf(",")>0))
				{
					Vector orset=getOrSet(ableID,abilityID);
					if(orset.size()!=0)
					{
						String KEYID2=KEYID;
						while(KEYID2.startsWith("*")) KEYID2=KEYID2.substring(1);
						addPreRequisites(KEYID2,orset,"");
						if(remove==null) remove=new Vector();
						remove.addElement(KEYID);
					}
				}
			}
		}
		if(remove!=null)
			for(int r=0;r<remove.size();r++)
				allows.remove(remove.elementAt(r));
		return (Vector)allows.get(ableID);
	}
	
	public void addCharAbilityMapping(String ID, 
									  int qualLevel,
									  String ability, 
									  int defaultProficiency,
									  String defaultParam,
									  boolean autoGain,
									  boolean secret,
									  Vector preReqSkillsList,
									  String extraMask,
                                      Integer[] costOverrides)
	{
		delCharAbilityMapping(ID,ability);
		Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
		AbilityMapping able=new AbilityMapping();
		able.abilityName=ability;
		able.qualLevel=qualLevel;
		able.autoGain=autoGain;
		able.isSecret=secret;
		able.defaultParm=defaultParam;
		able.defaultProficiency=defaultProficiency;
		able.extraMask=extraMask;
        able.costOverrides=costOverrides;
		
		able.skillPreReqs=new DVector(2);
		addPreRequisites(ability,preReqSkillsList,extraMask);
		if(preReqSkillsList!=null)
		for(int v=0;v<preReqSkillsList.size();v++)
		{
			String s=(String)preReqSkillsList.elementAt(v);
			int prof=0;
			int x=s.indexOf("(");
			if((x>=0)&&(s.endsWith(")")))
			{
				prof=CMath.s_int(s.substring(x+1,s.length()-1));
				s=s.substring(0,x);
			}
			able.skillPreReqs.addElement(s,new Integer(prof));
		}
		ableMap.put(ability,able);
		int arc_level=getQualifyingLevel("Archon",true,ability);
		if((arc_level<0)||((qualLevel>=0)&&(qualLevel<arc_level)))
			addCharAbilityMapping("Archon",qualLevel,ability,true);
		Integer lowLevel=(Integer)lowestQualifyingLevelMap.get(ability);
		if((lowLevel==null)
		||(qualLevel<lowLevel.intValue()))
			lowestQualifyingLevelMap.put(ability,new Integer(qualLevel));
		DVector reV=(DVector)reverseAbilityMap.get(ability);
		if(reV==null){ reV=new DVector(2); reverseAbilityMap.put(ability,reV);}
		if(!reV.contains(ID)) reV.addElement(ID, able);
	}
	
	public boolean qualifiesByAnyCharClass(String abilityID)
	{
		if(completeAbleMap.containsKey("All"))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(abilityID)) 
				return true;
		}
		for(Enumeration e=CMClass.charClasses();e.hasMoreElements();)
		{
			CharClass C=(CharClass)e.nextElement();
			if(completeAbleMap.containsKey(C.ID()))
			{
				Hashtable ableMap=(Hashtable)completeAbleMap.get(C.ID());
				if(ableMap.containsKey(abilityID)) 
					return true;
			}
		}
		return false;
	}
	
	public int lowestQualifyingLevel(String ability)
	{
		Integer lowLevel=(Integer)lowestQualifyingLevelMap.get(ability);
		if(lowLevel==null) return 0;
		return lowLevel.intValue();
	}
	
	public boolean classOnly(String classID, String abilityID)
	{
		if(completeAbleMap.containsKey(classID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(classID);
			if(!ableMap.containsKey(abilityID)) 
				return false;
		}
		else
			return false;
		for(Enumeration e=completeAbleMap.keys();e.hasMoreElements();)
		{
			String key=(String)e.nextElement();
			if((!key.equalsIgnoreCase(classID))
			&&(((Hashtable)completeAbleMap.get(classID)).containsKey(abilityID)))
				return false;
		}
		return true;
	}

	
	public boolean classOnly(MOB mob, String classID, String abilityID)
	{
		if(completeAbleMap.containsKey(classID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(classID);
			if(!ableMap.containsKey(abilityID)) 
				return false;
		}
		else
			return false;
		for(int c=0;c<mob.charStats().numClasses();c++)
		{
			CharClass C=mob.charStats().getMyClass(c);
			if((!C.ID().equals(classID))
			&&(completeAbleMap.containsKey(classID))
			&&(((Hashtable)completeAbleMap.get(classID)).containsKey(abilityID)))
				return false;
		}
		return true;
	}
	
	
	public boolean availableToTheme(String abilityID, int theme, boolean publicly)
	{
		for(Enumeration e=completeAbleMap.keys();e.hasMoreElements();)
		{
			String key=(String)e.nextElement();
			if(((Hashtable)completeAbleMap.get(key)).containsKey(abilityID))
			{
				if(key.equalsIgnoreCase("All")) return true;
				CharClass C=CMClass.getCharClass(key);
				if((C!=null)
				&&((C.availabilityCode()&theme)==theme)
				&&((!publicly)||((C.availabilityCode()&Area.THEME_SKILLONLYMASK)==0)))
					return true;
			}
		}
		return false;
	}
	
	public Vector getLevelListings(String ID, boolean checkAll, int level)
	{
		Vector V=new Vector();
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			for(Enumeration e=ableMap.keys();e.hasMoreElements();)
			{
				String key=(String)e.nextElement();
				AbilityMapping able=(AbilityMapping)ableMap.get(key);
				if(able.qualLevel==level)
					V.addElement(key);
			}
		}
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			for(Enumeration e=ableMap.keys();e.hasMoreElements();)
			{
				String key=(String)e.nextElement();
				AbilityMapping able=(AbilityMapping)ableMap.get(key);
				if((able.qualLevel==level)
				&&(!V.contains(key)))
					V.addElement(key);
			}
		}
		return V;
	}
	public DVector getUpToLevelListings(String ID, int level, boolean ignoreAll, boolean gainedOnly)
	{
		DVector DV=new DVector(2);
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			for(Enumeration e=ableMap.keys();e.hasMoreElements();)
			{
				String key=(String)e.nextElement();
				AbilityMapping able=(AbilityMapping)ableMap.get(key);
				if((able.qualLevel<=level)
				&&((!gainedOnly)||(able.autoGain)))
					DV.addElement(key,able);
			}
		}
		if((completeAbleMap.containsKey("All"))&&(!ignoreAll))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			for(Enumeration e=ableMap.keys();e.hasMoreElements();)
			{
				String key=(String)e.nextElement();
				AbilityMapping able=(AbilityMapping)ableMap.get(key);
				if((able.qualLevel<=level)
				&&(!DV.contains(key))
				&&((!gainedOnly)||(able.autoGain)))
					DV.addElement(key,able);
			}
		}
		return DV;
	}
	
	public int getQualifyingLevel(String ID, boolean checkAll, String ability)
	{
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).qualLevel;
		}
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).qualLevel;
		}
		return -1;
	}

	public Vector getOrSet(String errStr, String abilityID)
	{
		Ability preA=null;
		Vector orset=new Vector();
		Vector preorset=CMParms.parseCommas(abilityID,true);
		for(int p=0;p<preorset.size();p++)
		{
			abilityID=(String)preorset.elementAt(p);
			if(abilityID.startsWith("*"))
			{
				String a=abilityID.substring(1).toUpperCase();
				for(Enumeration e=CMClass.abilities();e.hasMoreElements();)
				{ 
					preA=(Ability)e.nextElement();
					if(preA.ID().toUpperCase().endsWith(a))
						orset.addElement(preA.ID());
				}
			}
			else
			if(abilityID.endsWith("*"))
			{
				String a=abilityID.substring(0,abilityID.length()-1).toUpperCase();
				for(Enumeration e=CMClass.abilities();e.hasMoreElements();)
				{ 
					preA=(Ability)e.nextElement();
					if(preA.ID().toUpperCase().startsWith(a))
						orset.addElement(preA.ID());
				}
			}
			else
				orset.addElement(abilityID);
		}
		for(int o=orset.size()-1;o>=0;o--)
		{
			abilityID=(String)orset.elementAt(o);
			preA=CMClass.getAbility(abilityID);
			if(preA==null) 
			{
				preA=CMClass.findAbility(abilityID);
				if(preA!=null)
					orset.setElementAt(preA.ID(),o);
				else
				{
					Log.errOut("CMAble","Skill "+errStr+" requires nonexistant skill "+abilityID+".");
					orset.clear();
					break;
				}
			}
		}
		return orset;
	}
	
	public void fillPreRequisites(Ability A, DVector rawPreReqs)
	{
		for(int v=0;v<rawPreReqs.size();v++)
		{
			String abilityID=(String)rawPreReqs.elementAt(v,1);
			if(abilityID.startsWith("*")||abilityID.endsWith("*")||(abilityID.indexOf(",")>0))
			{
				Vector orset=getOrSet(A.ID(),abilityID);
				if(orset.size()!=0)
					rawPreReqs.setElementAt(v,1,orset);
			}
			else
			{
				Ability otherAbility=CMClass.getAbility(abilityID);
				if(otherAbility==null) 
				{
					otherAbility=CMClass.findAbility(abilityID);
					if(otherAbility!=null)
						rawPreReqs.setElementAt(v,1,otherAbility.ID());
					else
					{
						Log.errOut("CMAble","Skill "+A.ID()+" requires nonexistant skill "+abilityID+".");
						break;
					}
				}
			}
		}
	}
	
	public DVector getApplicablePreRequisites(MOB mob, Ability A)
	{
		DVector V=getRawPreRequisites(mob,A);
		if((V==null)||(V.size()==0)) return new DVector(2);
		fillPreRequisites(A,V);
		return V;
	}
	
	public DVector getCommonPreRequisites(Ability A)
	{
		DVector preReqs=null;
		Hashtable ableMap=null;
		if(completeAbleMap.containsKey("All"))
		{
			ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(A.ID()))
				preReqs=((AbilityMapping)ableMap.get(A.ID())).skillPreReqs;
		}
		if(preReqs==null)
		for(Enumeration e=completeAbleMap.elements();e.hasMoreElements();)
		{
			ableMap=(Hashtable)e.nextElement();
			if(ableMap.containsKey(A.ID()))
			{
				preReqs=((AbilityMapping)ableMap.get(A.ID())).skillPreReqs;
				if((preReqs!=null)&&(preReqs.size()>0)) break;
			}
		}
		if((preReqs==null)||(preReqs.size()==0)) return new DVector(2);
		DVector reqs=preReqs.copyOf();
		fillPreRequisites(A,reqs);
		return reqs;
		
	}
	
	public String getCommonExtraMask(Ability A)
	{
		String mask=null;
		Hashtable ableMap=null;
		if(completeAbleMap.containsKey("All"))
		{
			ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(A.ID()))
				mask=((AbilityMapping)ableMap.get(A.ID())).extraMask;
		}
		if((mask==null)||(mask.length()==0))
		for(Enumeration e=completeAbleMap.elements();e.hasMoreElements();)
		{
			ableMap=(Hashtable)e.nextElement();
			if(ableMap.containsKey(A.ID()))
			{
				mask=((AbilityMapping)ableMap.get(A.ID())).extraMask;
				if((mask!=null)&&(mask.length()>0)) break;
			}
		}
		if((mask==null)||(mask.length()==0)) return "";
		return mask;
	}
	
	public DVector getUnmetPreRequisites(MOB student, Ability A)
	{
		DVector V=getRawPreRequisites(student,A);
		if((V==null)||(V.size()==0)) return new DVector(2);
		fillPreRequisites(A,V);
		String abilityID=null;
		Integer prof=null;
		Ability A2=null;
		for(int v=V.size()-1;v>=0;v--)
		{
			prof=(Integer)V.elementAt(v,2);
			if(V.elementAt(v,1) instanceof String)
			{
				abilityID=(String)V.elementAt(v,1);
				A2=student.fetchAbility(abilityID);
				if((A2!=null)&&(A2.proficiency()>=prof.intValue()))
					V.removeElementAt(v);
				else
				if(!qualifiesByLevel(student,abilityID))
					V.removeElementAt(v);
			}
			else
			{
				Vector orset=(Vector)V.elementAt(v,1);
				for(int o=orset.size()-1;o>=0;o--)
				{
					abilityID=(String)orset.elementAt(o);
					A2=student.fetchAbility(abilityID);
					if((A2!=null)&&(A2.proficiency()>=prof.intValue()))
					{
						orset.clear();
						break;
					}
					if(!qualifiesByLevel(student,abilityID))
						orset.removeElementAt(o);
				}
				if(orset.size()==0)
					V.removeElementAt(v);
			}
		}
		return V;
	}
	
	public DVector getPreReqs(String ID, boolean checkAll, String ability)
	{
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).skillPreReqs;
		}
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).skillPreReqs;
		}
		return null;
	}
	
	public String formatPreRequisites(DVector preReqs)
	{
		StringBuffer names=new StringBuffer("");
		if((preReqs!=null)&&(preReqs.size()>0))
		{
			Integer prof=null;
			for(int p=0;p<preReqs.size();p++)
			{
				prof=(Integer)preReqs.elementAt(p,2);
				if(preReqs.elementAt(p,1) instanceof Vector)
				{
					Vector V=(Vector)preReqs.elementAt(p,1);
					names.append("(One of: ");
					for(int v=0;v<V.size();v++)
					{
						Ability A=CMClass.getAbility((String)V.elementAt(v));
						if(A!=null) 
						{
							names.append("'"+A.name()+"'");
							if(V.size()>1)
							{
								if(v==(V.size()-2))
									names.append(", or ");
								else
								if(v<V.size()-2)
									names.append(", ");
							}
						}
					}
					if(prof.intValue()>0)
						names.append(" at "+prof+"%)");
					else
						names.append(")");
				}
				else
				{
					Ability A=CMClass.getAbility((String)preReqs.elementAt(p,1));
					if(A!=null) 
					{
						names.append("'"+A.name()+"'");
						if(prof.intValue()>0)
							names.append(" at "+prof+"%");
					}
				}
				if(preReqs.size()>1)
				{
					if(p==(preReqs.size()-2))
						names.append(", and ");
					else
					if(p<preReqs.size()-2)
						names.append(", ");
				}
			}
		}
		return names.toString();
	}
	
	public DVector getRawPreRequisites(MOB student, Ability A)
	{
		if(student==null) return new DVector(2);
		DVector reqs=null;
		for(int c=student.charStats().numClasses()-1;c>=0;c--)
		{
			CharClass C=student.charStats().getMyClass(c);
			int level=getQualifyingLevel(C.ID(),true,A.ID());
			int classLevel=student.charStats().getClassLevel(C);
			if((level>=0)&&(classLevel>=level))
			{
				reqs=getPreReqs(C.ID(),true,A.ID());
				if(reqs!=null) return reqs.copyOf();
			}
		}
		int level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,A.ID());
		int classLevel=student.baseEnvStats().level();
		if((level>=0)&&(classLevel>=level))
		{
			reqs=getPreReqs(student.charStats().getMyRace().ID(),false,A.ID());
			if(reqs!=null) return reqs.copyOf();
		}
		reqs=getPreReqs(student.charStats().getCurrentClass().ID(),true,A.ID());
		return (reqs==null)?new DVector(2):reqs.copyOf();
	}

	public String getExtraMask(String ID, boolean checkAll, String ability)
	{
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).extraMask;
		}
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).extraMask;
		}
		return null;
	}
	
	public String getApplicableMask(MOB student, Ability A)
	{
		if(student==null) return "";
		String mask=null;
		for(int c=student.charStats().numClasses()-1;c>=0;c--)
		{
			CharClass C=student.charStats().getMyClass(c);
			int level=getQualifyingLevel(C.ID(),true,A.ID());
			int classLevel=student.charStats().getClassLevel(C);
			if((level>=0)&&(classLevel>=level))
			{
				mask=getExtraMask(C.ID(),true,A.ID());
				if(mask!=null) return mask;
			}
		}
		int level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,A.ID());
		int classLevel=student.baseEnvStats().level();
		if((level>=0)&&(classLevel>=level))
		{
			mask=getExtraMask(student.charStats().getMyRace().ID(),false,A.ID());
			if(mask!=null) return mask;
		}
		mask=getExtraMask(student.charStats().getCurrentClass().ID(),true,A.ID());
		return mask==null?"":mask;
	}
	
	public int qualifyingLevel(MOB student, Ability A)
	{
		if(student==null) return -1;
		int theLevel=-1;
		int greatestDiff=-1;
		for(int c=student.charStats().numClasses()-1;c>=0;c--)
		{
			CharClass C=student.charStats().getMyClass(c);
			int level=getQualifyingLevel(C.ID(),true,A.ID());
			int classLevel=student.charStats().getClassLevel(C);
			if((level>=0)
			&&(classLevel>=level)
			&&((classLevel-level)>greatestDiff))
			{
				greatestDiff=classLevel-level;
				theLevel=level;
			}
		}
		int level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,A.ID());
		int classLevel=student.baseEnvStats().level();
		if((level>=0)
		&&(classLevel>=level)
		&&((classLevel-level)>greatestDiff))
		{
			greatestDiff=classLevel-level;
			theLevel=level;
		}
		if(theLevel<0) 
			return getQualifyingLevel(student.charStats().getCurrentClass().ID(),true,A.ID());
		return theLevel;
	}

	public int qualifyingClassLevel(MOB student, Ability A)
	{
		if(student==null) return -1;
		int greatestDiff=-1;
		CharClass theClass=null;
		for(int c=student.charStats().numClasses()-1;c>=0;c--)
		{
			CharClass C=student.charStats().getMyClass(c);
			int level=getQualifyingLevel(C.ID(),true,A.ID());
			int classLevel=student.charStats().getClassLevel(C);
			if((level>=0)
			&&(classLevel>=level)
			&&((classLevel-level)>greatestDiff))
			{
				greatestDiff=classLevel-level;
				theClass=C;
			}
		}
		int level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,A.ID());
		int classLevel=student.baseEnvStats().level();
		if((level>=0)
		&&(classLevel>=level)
		&&((classLevel-level)>greatestDiff))
			greatestDiff=classLevel-level;
		if(theClass==null) 
			return student.charStats().getClassLevel(student.charStats().getCurrentClass());
		return student.charStats().getClassLevel(theClass);
	}

	public Object lowestQualifyingClassRace(MOB student, Ability A)
	{
		if(student==null) return null;
		int theLevel=-1;
		CharClass theClass=null;
		for(int c=student.charStats().numClasses()-1;c>=0;c--)
		{
			CharClass C=student.charStats().getMyClass(c);
			int level=getQualifyingLevel(C.ID(),true,A.ID());
			int classLevel=student.charStats().getClassLevel(C);
			if((level>=0)
			&&(classLevel>=level)
			&&((theLevel<0)||(theLevel>=level)))
			{
				theLevel=level;
				theClass=C;
			}
		}
		int level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,A.ID());
		if((level>=0)
		&&((theClass==null)||((student.baseEnvStats().level()>=level)&&(theLevel>level))))
			return student.charStats().getMyRace();
		return theClass;
	}

	
	public boolean qualifiesByCurrentClassAndLevel(MOB student, Ability A)
	{
		if(student==null) return false;
		CharClass C=student.charStats().getCurrentClass();
		int level=getQualifyingLevel(C.ID(),true,A.ID());
		if((level>=0)
		&&(student.charStats().getClassLevel(C)>=level))
			return true;
		level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,A.ID());
		if((level>=0)&&(student.envStats().level()>=level))
			return true;
		return false;
	}

	public boolean qualifiesByLevel(MOB student, Ability A){return (A==null)?false:qualifiesByLevel(student,A.ID());}
	public boolean qualifiesByLevel(MOB student, String ability)
	{
		if(student==null) return false;
		for(int c=student.charStats().numClasses()-1;c>=0;c--)
		{
			CharClass C=student.charStats().getMyClass(c);
			int level=getQualifyingLevel(C.ID(),true,ability);
			if((level>=0)
			&&(student.charStats().getClassLevel(C)>=level))
				return true;
		}
		int level=getQualifyingLevel(student.charStats().getMyRace().ID(),false,ability);
		if((level>=0)&&(student.envStats().level()>=level))
			return true;
		return false;
	}

	public boolean getDefaultGain(String ID, boolean checkAll, String ability)
	{
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).autoGain;
		}
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).autoGain;
		}
		return false;
	}
	
	
    /**
     * @param ID
     * @param ability
     * @return
     */
    public AbilityMapping getAbleMap(String ID, String ability)
    {
        if(completeAbleMap.containsKey(ID))
        {
            Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
            if(ableMap.containsKey(ability))
                return (AbilityMapping)ableMap.get(ability);
        }
        return null;
    }
	public AbilityMapping getAllAbleMap(String ability){ return getAbleMap("All",ability);}
	
	public boolean getSecretSkill(String ID, boolean checkAll, String ability)
	{
		boolean secretFound=false;
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
            {
				if(!((AbilityMapping)ableMap.get(ability)).isSecret)
					return false;
				secretFound=true;
            }
		}
		if(checkAll)
		{
			AbilityMapping AB=getAllAbleMap(ability);
			if(AB!=null) return AB.isSecret;
		}
		return secretFound;
	}
	
	public boolean getAllSecretSkill(String ability)
	{
		AbilityMapping AB=getAllAbleMap(ability);
		if(AB!=null) return AB.isSecret;
		return false;
	}
	
	public boolean getSecretSkill(MOB mob, String ability)
	{
		boolean secretFound=false;
		for(int c=0;c<mob.charStats().numClasses();c++)
		{
			String charClass=mob.charStats().getMyClass(c).ID();
			if(completeAbleMap.containsKey(charClass))
			{
				Hashtable ableMap=(Hashtable)completeAbleMap.get(charClass);
				if(ableMap.containsKey(ability))
                {
					if(!((AbilityMapping)ableMap.get(ability)).isSecret)
						return false;
					secretFound=true;
                }
			}
		}
		if(completeAbleMap.containsKey(mob.charStats().getMyRace().ID()))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(mob.charStats().getMyRace().ID());
			if(ableMap.containsKey(ability))
            {
				if(!((AbilityMapping)ableMap.get(ability)).isSecret)
					return false;
				secretFound=true;
            }
		}
		AbilityMapping AB=getAllAbleMap(ability);
		if(AB!=null) return AB.isSecret;
		return secretFound;
	}
	
	public boolean getSecretSkill(String ability)
	{
		boolean secretFound=false;
		for(Enumeration e=CMClass.charClasses();e.hasMoreElements();)
		{
			String charClass=((CharClass)e.nextElement()).ID();
			if(completeAbleMap.containsKey(charClass)&&(!charClass.equals("Archon")))
			{
				Hashtable ableMap=(Hashtable)completeAbleMap.get(charClass);
				if(ableMap.containsKey(ability))
                {
					if(!((AbilityMapping)ableMap.get(ability)).isSecret)
						return false;
					secretFound=true;
                }
			}
		}
		for(Enumeration e=CMClass.races();e.hasMoreElements();)
		{
			String ID=((Race)e.nextElement()).ID();
			if(completeAbleMap.containsKey(ID))
			{
				Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
				if(ableMap.containsKey(ability))
                {
					if(!((AbilityMapping)ableMap.get(ability)).isSecret)
						return false;
					secretFound=true;
                }
			}
		}
		AbilityMapping AB=getAllAbleMap(ability);
		if(AB!=null) return AB.isSecret;
		return secretFound;
	}
	
    public Integer[] getCostOverrides(String ID, boolean checkAll, String ability)
    {
    	Integer[] found=null;
        if(completeAbleMap.containsKey(ID))
        {
            Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
            if(ableMap.containsKey(ability))
                found=((AbilityMapping)ableMap.get(ability)).costOverrides;
        }
        if((checkAll)&&(found==null))
        {
            AbilityMapping AB=getAllAbleMap(ability);
            if(AB!=null) found=AB.costOverrides;
        }
        return found;
    }
    
    public Integer[] getAllCostOverrides(String ability)
    {
        AbilityMapping AB=getAllAbleMap(ability);
        if(AB!=null) return AB.costOverrides;
        return null;
    }
    
    public Integer[] getCostOverrides(MOB mob, String ability)
    {
    	Integer[] found=null;
        for(int c=0;c<mob.charStats().numClasses();c++)
        {
            String charClass=mob.charStats().getMyClass(c).ID();
            if(completeAbleMap.containsKey(charClass))
            {
                Hashtable ableMap=(Hashtable)completeAbleMap.get(charClass);
                if((ableMap.containsKey(ability))&&(found==null))
                    found=((AbilityMapping)ableMap.get(ability)).costOverrides;
            }
        }
        if(completeAbleMap.containsKey(mob.charStats().getMyRace().ID()))
        {
            Hashtable ableMap=(Hashtable)completeAbleMap.get(mob.charStats().getMyRace().ID());
            if((ableMap.containsKey(ability))&&(found==null))
                found=((AbilityMapping)ableMap.get(ability)).costOverrides;
        }
        AbilityMapping AB=getAllAbleMap(ability);
        if((AB!=null)&&(found==null))
            return found=AB.costOverrides;
        return found;
    }
    
    public Integer[] getCostOverrides(String ability)
    {
    	Integer[] found=null;
        for(Enumeration e=CMClass.charClasses();e.hasMoreElements();)
        {
            String charClass=((CharClass)e.nextElement()).ID();
            if(completeAbleMap.containsKey(charClass)&&(!charClass.equals("Archon")))
            {
                Hashtable ableMap=(Hashtable)completeAbleMap.get(charClass);
                if((ableMap.containsKey(ability))&&(found==null))
                    found=((AbilityMapping)ableMap.get(ability)).costOverrides;
            }
        }
        for(Enumeration e=CMClass.races();e.hasMoreElements();)
        {
            String ID=((Race)e.nextElement()).ID();
            if(completeAbleMap.containsKey(ID))
            {
                Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
                if((ableMap.containsKey(ability))&&(found==null))
                    found=((AbilityMapping)ableMap.get(ability)).costOverrides;
            }
        }
        AbilityMapping AB=getAllAbleMap(ability);
        if((AB!=null)&&(found==null))
            return found=AB.costOverrides;
        return found;
    }
    
	public String getDefaultParm(String ID, boolean checkAll, String ability)
	{
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).defaultParm;
		}
		
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).defaultParm;
		}
		return "";
	}
	
	public int getDefaultProficiency(String ID, boolean checkAll, String ability)
	{
		if(completeAbleMap.containsKey(ID))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get(ID);
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).defaultProficiency;
		}
		if((checkAll)&&(completeAbleMap.containsKey("All")))
		{
			Hashtable ableMap=(Hashtable)completeAbleMap.get("All");
			if(ableMap.containsKey(ability))
				return ((AbilityMapping)ableMap.get(ability)).defaultProficiency;
		}
		return 0;
	}
	
	// returns Vector of components found if all good, returns Integer of bad row if not.
	public Vector componentCheck(MOB mob, DVector req)
	{
		if((mob==null)||(req==null)||(req.size()==0)) 
			return new Vector();
		boolean currentAND=false;
		boolean previousValue=true;
		int amt=0;
		Object O=null;
		Integer disp=null;
		Vector passes=new Vector();
		Item I=null;
		Item container=null;
		Vector thisSet=new Vector();
		for(int i=0;i<req.size();i++)
		{
			currentAND=((Boolean)req.elementAt(i,1)).booleanValue();
			if(previousValue&&(!currentAND)) return passes;
			
			// if they fail the zappermask, its like the req is NOT even there...
			if((((String)req.elementAt(i,6)).length()>0)
			&&(!CMLib.masking().maskCheck((String)req.elementAt(i,6),mob,true)))
				continue;
			amt=((Integer)req.elementAt(i,4)).intValue();
			O=req.elementAt(i,5);
			disp=(Integer)req.elementAt(i,2);
			thisSet.clear();
			for(int ii=0;ii<mob.inventorySize();ii++)
			{
				I=mob.fetchInventory(ii);
				if(I==null) continue;
				if((O instanceof String)&&(!CMLib.english().containsString(I.name(),(String)O)))
					continue;
				else
				if((O instanceof Integer)&&((!(I instanceof RawMaterial))||(I.material()!=((Integer)O).intValue())))
					continue;
				else
				if((O instanceof Long)&&((!(I instanceof RawMaterial))||((I.material()&RawMaterial.MATERIAL_MASK)!=((Long)O).intValue())))
					continue;
				container=I.ultimateContainer();
				if(container==null) container=I;
				switch(disp.intValue())
				{
				case 0: if(!container.amWearingAt(Item.IN_INVENTORY))continue; break;
				case 1: if(!container.amWearingAt(Item.WORN_HELD))continue; break;
				case 2: if(container.amWearingAt(Item.IN_INVENTORY))continue; break;
				}
                if((!(O instanceof String))
                &&(CMLib.flags().isOnFire(I)||CMLib.flags().enchanted(I)))
                    continue;
				if(O instanceof String)
                {
                    if(I instanceof PackagedItems)
                        I=(Item)CMLib.materials().unbundle(I,amt);
					amt-=I.numberOfItems();
                }
				else
                if(I.envStats().weight()>amt)
                {
                    I=(Item)CMLib.materials().unbundle(I,amt);
					amt=amt-I.envStats().weight();
                }
                else
                    amt=amt-I.envStats().weight();
                thisSet.addElement(I);
                
				if(amt<=0)
				{
					if(thisSet.size()>0) 
                        thisSet.addElement(req.elementAt(i,3));
					break;
				}
			}
			if((amt>0)&&(currentAND)&&(i>0)) return null;
			previousValue=amt<=0;
			if(previousValue) CMParms.addToVector(thisSet,passes);
		}
		if(passes.size()==0) return null;
		return passes;
	}

    public DVector getAbilityComponentDVector(String AID){ return (DVector)getAbilityComponentMap().get(AID.toUpperCase().trim());}
    public Vector getAbilityComponentDecodedDVectors(String AID){ return getAbilityComponentDecodedDVectors(getAbilityComponentDVector(AID));}
    public DVector getAbilityComponentDecodedDVector(DVector codedDV, int r)
    {
        DVector curr=new DVector(2);
        Object O=null;
        Integer disp=null;
        String itemDesc=null;
        curr.addElement("ANDOR",(((Boolean)codedDV.elementAt(r,1)).booleanValue()?"&&":"||"));
        disp=(Integer)codedDV.elementAt(r,2);
        switch(disp.intValue())
        {
        case 1: curr.addElement("DISPOSITION","held"); break;
        case 2: curr.addElement("DISPOSITION","worn"); break;
        default: curr.addElement("DISPOSITION","inventory"); break;
        }
        if(((Boolean)codedDV.elementAt(r,3)).booleanValue())
            curr.addElement("FATE","consumed");
        else
            curr.addElement("FATE","kept");
        curr.addElement("AMOUNT",""+((Integer)codedDV.elementAt(r,4)).toString());
        O=codedDV.elementAt(r,5);
        if(O instanceof String)
            itemDesc=(String)O;
        else
        if(O instanceof Long)
            itemDesc=RawMaterial.MATERIAL_DESCS[((Long)O).intValue()>>8].toUpperCase();
        else
        if(O instanceof Integer)
            itemDesc=RawMaterial.RESOURCE_DESCS[((Integer)O).intValue()&RawMaterial.RESOURCE_MASK].toUpperCase();
        curr.addElement("COMPONENTID",itemDesc);
        curr.addElement("MASK",(String)codedDV.elementAt(r,6));
        return curr;
    }
    
    public void setAbilityComponentCodedFromDecodedDVector(DVector decodedDV, DVector codedDV, int row)
    {
        String[] s=new String[6];
        for(int i=0;i<6;i++)
            s[i]=(String)decodedDV.elementAt(i,2);
        if(s[0].equalsIgnoreCase("||"))
            codedDV.setElementAt(row,1,new Boolean(false));
        else
            codedDV.setElementAt(row,1,new Boolean(true));
        if(s[1].equalsIgnoreCase("held"))
            codedDV.setElementAt(row,2,new Integer(1));
        else
        if(s[1].equalsIgnoreCase("worn"))
            codedDV.setElementAt(row,2,new Integer(2));
        else
            codedDV.setElementAt(row,2,new Integer(0));
        if(s[2].equalsIgnoreCase("consumed"))
            codedDV.setElementAt(row,3,new Boolean(true));
        else
            codedDV.setElementAt(row,3,new Boolean(false));
        codedDV.setElementAt(row,4,new Integer(CMath.s_int(s[3])));
        int depth=CMLib.materials().getResourceCode(s[4],false);
        if(depth>=0) 
            codedDV.setElementAt(row,5,new Integer(depth));
        else
        {
            depth=CMLib.materials().getMaterialCode(s[4],false);
            if(depth>=0) 
                codedDV.setElementAt(row,5,new Long(depth));
            else
                codedDV.setElementAt(row,5,s[4].toUpperCase().trim());
        }
        codedDV.setElementAt(row,6,s[5]);
    }
    
    public Vector getAbilityComponentDecodedDVectors(DVector req)
    {
        if(req==null) return null;
        Vector V=new Vector();
        for(int r=0;r<req.size();r++)
        {
            DVector curr=getAbilityComponentDecodedDVector(req,r);
            V.addElement(curr);
        }
        return V;
    }
    
    public void addBlankAbilityComponent(DVector codedDV)
    {
        codedDV.addElement(new Boolean(true),new Integer(0),new Boolean(false),new Integer(1),new String("resource-material-item name"),"");
    }
    
    public String getAbilityComponentCodedString(String AID)
    {
        StringBuffer buf=new StringBuffer("");
        Vector comps=getAbilityComponentDecodedDVectors(AID);
        DVector curr=null;
        for(int c=0;c<comps.size();c++)
        {
            curr=(DVector)comps.elementAt(c);
            if(c>0) buf.append((String)curr.elementAt(0,2));
            buf.append("(");
            buf.append((String)curr.elementAt(1,2));
            buf.append(":");
            buf.append((String)curr.elementAt(2,2));
            buf.append(":");
            buf.append((String)curr.elementAt(3,2));
            buf.append(":");
            buf.append((String)curr.elementAt(4,2));
            buf.append(":");
            buf.append((String)curr.elementAt(5,2));
            buf.append(")");
        }
        return AID+"="+buf.toString();
    }
    
    public String getAbilityComponentDesc(MOB mob, DVector req, int r)
    {
        int amt=0;
        Object O=null;
        Integer disp=null;
        String itemDesc=null;
        StringBuffer buf=new StringBuffer("");
        if(r>0) buf.append((((Boolean)req.elementAt(r,1)).booleanValue()?", and ":", or "));
        if((mob!=null)
        &&(((String)req.elementAt(r,6)).length()>0)
        &&(!CMLib.masking().maskCheck((String)req.elementAt(r,6),mob,true)))
            return "";
        if(mob==null)
        {
            if(((String)req.elementAt(r,6)).length()>0)
                buf.append("MASK: "+(String)req.elementAt(r,6)+": ");
        }
        amt=((Integer)req.elementAt(r,4)).intValue();
        O=req.elementAt(r,5);
        disp=(Integer)req.elementAt(r,2);
        if(O instanceof String)
            itemDesc=((amt>1)?(amt+" "+((String)O)+"s"):CMStrings.startWithAorAn((String)O));
        else
        if(O instanceof Long)
            itemDesc=amt+((amt>1)?" pounds":" pound")+" of "+RawMaterial.MATERIAL_DESCS[((Long)O).intValue()>>8].toLowerCase();
        else
        if(O instanceof Integer)
            itemDesc=amt+((amt>1)?" pounds":" pound")+" of "+RawMaterial.RESOURCE_DESCS[((Integer)O).intValue()&RawMaterial.RESOURCE_MASK].toLowerCase();
        switch(disp.intValue())
        {
        case 0: buf.append(itemDesc); break;
        case 1: buf.append(itemDesc+" held"); break;
        case 2: buf.append(itemDesc+" worn or wielded"); break;
        }
        return buf.toString();
    }
    
	public String getAbilityComponentDesc(MOB mob, String AID)
	{
		DVector req=getAbilityComponentDVector(AID);
		if(req==null) return null;
		StringBuffer buf=new StringBuffer("");
		for(int r=0;r<req.size();r++){ buf.append(getAbilityComponentDesc(mob,req,r));}
		return buf.toString();
	}

	public String addAbilityComponent(String s, Hashtable H)
	{
		int x=s.indexOf("=");
		if(x<0) return "Malformed component line (code 0): "+s;
		String id=s.substring(0,x).toUpperCase().trim();
		String parms=s.substring(x+1);
		
		String parmS=null;
		String rsc=null;
		DVector parm=null;
		Object[] build=null;
		int depth=0;
		parm=new DVector(6);
		String error=null;
		while(parms.length()>0)
		{
			build=new Object[6];
			build[0]=new Boolean(true);
			if(parms.startsWith("||"))
			{ build[0]=new Boolean(false); parms=parms.substring(2).trim();}
			else
			if(parms.startsWith("&&"))
			{ parms=parms.substring(2).trim();}
			
			if(!parms.startsWith("("))
			{error="Malformed component line (code 1): "+parms; break;}
			
			depth=0;
			x=1;
			for(;x<parms.length();x++)
				if((parms.charAt(x)==')')&&(depth==0))
					break;
				else
				if(parms.charAt(x)=='(')
					depth++;
				else
				if(parms.charAt(x)==')')
					depth--;
			if(x==parms.length()){error="Malformed component line (code 2): "+parms; break;}
			parmS=parms.substring(1,x).trim();
			parms=parms.substring(x+1).trim();
			
			build[1]=new Integer(0);
			x=parmS.indexOf(":");
			if(x<0){error="Malformed component line (code 0-1): "+parmS; continue;}
			if(parmS.substring(0,x).equalsIgnoreCase("held")) build[1]=new Integer(1);
			else
			if(parmS.substring(0,x).equalsIgnoreCase("worn")) build[1]=new Integer(2);
			else
			if((x>0)&&(!parmS.substring(0,x).equalsIgnoreCase("inventory")))
			{error="Malformed component line (code 0-2): "+parmS; continue;}
			parmS=parmS.substring(x+1);
			
			build[2]=new Boolean(true);
			x=parmS.indexOf(":");
			if(x<0){error="Malformed component line (code 1-1): "+parmS; continue;}
			if(parmS.substring(0,x).equalsIgnoreCase("kept")) build[2]=new Boolean(false);
			else
			if((x>0)&&(!parmS.substring(0,x).equalsIgnoreCase("consumed")))
			{error="Malformed component line (code 1-2): "+parmS; continue;}
			parmS=parmS.substring(x+1);
			
			build[3]=new Integer(1);
			x=parmS.indexOf(":");
			if(x<0){error="Malformed component line (code 2-1): "+parmS; continue;}
			if((x>0)&&(!CMath.isInteger(parmS.substring(0,x))))
			{error="Malformed component line (code 2-2): "+parmS; continue;}
			if(x>0) build[3]=new Integer(CMath.s_int(parmS.substring(0,x)));
			parmS=parmS.substring(x+1);
			
			build[4]=new String("");
			x=parmS.indexOf(":");
			if(x<=0){error="Malformed component line (code 3-1): "+parmS; continue;}
			rsc=parmS.substring(0,x);
			depth=CMLib.materials().getResourceCode(rsc,false);
			if(depth>=0) 
				build[4]=new Integer(depth);
			else
			{
				depth=CMLib.materials().getMaterialCode(rsc,false);
				if(depth>=0) 
					build[4]=new Long(depth);
				else
					build[4]=rsc.toUpperCase().trim();
			}
			parmS=parmS.substring(x+1);
			
			build[5]=parmS;
			
			parm.addElement(build[0],build[1],build[2],build[3],build[4],build[5]);
		}
		parm.trimToSize();
		if(error!=null) return error;
		if(parm.size()>0) H.put(id.toUpperCase(),parm);
		return null;
	}
	
	// format of each data entry is 1=ANDOR(B), 2=DISPO(I), 3=CONSUMED(B), 4=AMT(I), 5=MATERIAL(L)RESOURCE(I)NAME(S), 6=MASK(S) 
	public Hashtable getAbilityComponentMap()
	{
		Hashtable H=(Hashtable)Resources.getResource("COMPONENT_MAP");
		if(H==null)
		{
			H=new Hashtable();
			StringBuffer buf=new CMFile(Resources.makeFileResourceName("skills/components.txt"),null,true).text();
			Vector V=new Vector();
			if(buf!=null)
				V=Resources.getFileLineVector(buf);
			String s=null;
			String error=null;
			if(V!=null)
			for(int v=0;v<V.size();v++)
			{
				s=((String)V.elementAt(v)).trim();
				if(s.startsWith("#")||(s.length()==0)||s.startsWith(";")||s.startsWith(":")) continue;
				error=addAbilityComponent(s,H);
				if(error!=null) Log.errOut("CMAble",error);
			}
			Resources.submitResource("COMPONENT_MAP",H);
		}
		return H;
	}
	
}