/
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/Behaviors/interfaces/
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/
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/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/smtp/
com/planet_ink/coffee_mud/core/threads/
com/planet_ink/siplet/applet/
lib/
resources/fakedb/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
package com.planet_ink.coffee_mud.Common;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.exceptions.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;

/*
   Copyright 2000-2010 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.
*/
@SuppressWarnings("unchecked")
public class DefaultQuest implements Quest, Tickable, CMObject
{

    public String ID(){return "DefaultQuest";}

    protected String name="";
    protected String author="";
    protected String displayName="";
    protected String startDate="";
    protected int duration=450; // about 30 minutes
    protected String rawScriptParameter="";
    protected Vector winners=new Vector();
    protected boolean durable=false;
    protected int minWait=-1;
    protected int minPlayers=-1;
    protected String playerMask="";
    protected int runLevel=-1;
    protected int maxWait=-1;
    protected int waitRemaining=-1;
    protected int ticksRemaining=-1;
    protected long lastStartDateTime=System.currentTimeMillis();
    private boolean stoppingQuest=false;
    protected int spawn=SPAWN_NO;
    private QuestState questState=new QuestState();
    private boolean copy=false;
    private boolean suspended=false;
    private Hashtable stepEllapsedTimes=new Hashtable();
    public DVector internalFiles=null;
    private int[] resetData=null;

    // the unique name of the quest
    public String name(){return name;}
    public void setName(String newName){name=newName;}

    // the author of the quest
    public String author(){return author;}
    public void setAuthor(String newName){author=newName;}
    
    // the display name of the quest
    public String displayName(){ return displayName;}
    public void setDisplayName(String newName){ displayName=newName;}

    public CMObject copyOf()
    {
        try
        {
            Object O=this.clone();
            return (CMObject)O;
        }
        catch(CloneNotSupportedException e)
        {
            return newInstance();
        }
    }

    public boolean suspended(){ return suspended;}
    public void setSuspended(boolean truefalse){suspended=truefalse;}

    public CMObject newInstance(){try{return (CMObject)getClass().newInstance();}catch(Exception e){return new DefaultQuest();}}
    public void initializeClass(){}

	public Object getDesignatedObject(String named)
	{
		int code=-1;
		for(int i=0;i<QCODES.length;i++)
			if(named.equalsIgnoreCase(QCODES[i]))
			{ code=i; break;}
		switch(code)
		{
		case 0: return ID();
		case 1: return name();
		case 2: return ""+duration();
		case 3: return ""+minWait();
		case 4: return ""+minPlayers();
		case 5: return ""+playerMask();
		case 6: return ""+runLevel();
		case 7: return ""+startDate();
		case 8: return ""+startDate();
		case 9: return ""+waitInterval();
        case 10: return SPAWN_DESCS[getSpawn()];
        case 11: return displayName();
        case 13: return Boolean.toString(durable);
        case 12: break; // instructions should fall through
		}
		return questState.getStat(named);
	}

    public void internalQuestDelete()
    {
        if(isCopy()) return;
        if((internalFiles!=null)&&(internalFiles.size()>0))
        {
            for(int i=0;i<internalFiles.size();i++)
            {
                String filename=((String)internalFiles.elementAt(i,1)).toUpperCase();
                Vector delThese=new Vector();
                boolean foundKey=false;
                Vector V=Resources.findResourceKeys(filename);
                for(Enumeration e=V.elements();e.hasMoreElements();)
                {
                    String key=(String)e.nextElement();
                    if(key.startsWith("PARSEDPRG: ")&&(key.toUpperCase().endsWith(filename)))
                    { foundKey=true; delThese.addElement(key);}
                }
                if(foundKey)
                    for(int d=0;d<delThese.size();d++)
                        Resources.removeResource((String)delThese.elementAt(d));
            }
            internalFiles.clear();
            internalFiles=null;
        }
    }

    // the unique name of the quest
    public String startDate(){return startDate;}
    public void setStartDate(String newDate){
        int x=newDate.indexOf("-");
        if((x>0)
        &&(CMath.isMathExpression(newDate.substring(0,x)))
        &&(CMath.isMathExpression(newDate.substring(x+1))))
	    	startDate=newDate;
    }
    public void setStartMudDate(String newDate){
        setStartDate(newDate);
        if(startDate.equals(newDate))
            startDate="MUDDAY "+startDate;
    }

    // the duration, in ticks
    public int duration(){return duration;}
    public void setDuration(int newTicks){duration=newTicks;}

    public void setCopy(boolean truefalse){copy=truefalse;}
    public boolean isCopy(){return copy;}

    public void setSpawn(int spawnFlag){spawn=(spawnFlag<0)?0:spawnFlag;}
    public int getSpawn(){return spawn;}

	public int minPlayers(){return minPlayers;}
	public void setMinPlayers(int players){minPlayers=players;}
	public int runLevel(){return runLevel;}
	public void setRunLevel(int level){runLevel=level;}
	public String playerMask(){return playerMask;}
	public void setPlayerMask(String mask){playerMask=mask;}

    // the rest of the script.  This may be semicolon-separated instructions,
    // or a LOAD command followed by the quest script path.
    public void setScript(String parm){
        rawScriptParameter=parm;
        name="";
        author="";
        displayName="";
        startDate="";
        duration=-1;
        minWait=-1;
        maxWait=-1;
        minPlayers=-1;
        spawn=SPAWN_NO;
        playerMask="";
        runLevel=-1;
        internalFiles=null;
        durable=false;
        setVars(parseLoadScripts(parm,new Vector(),new Vector()),0);
        if(isCopy()) spawn=SPAWN_NO;
    }
    public String script(){return rawScriptParameter;}

    public void autostartup()
    {
        if(!resetWaitRemaining(0))
            CMLib.threads().deleteTick(this,Tickable.TICKID_QUEST);
        else
        if(!running())
            CMLib.threads().startTickDown(this,Tickable.TICKID_QUEST,1);
    }

    public void setVars(Vector script, int startAtLine)
    {
        Vector parsedLine=null;
        String var=null;
        String val=null;
        Vector setScripts=CMLib.quests().parseQuestCommandLines(script,"SET",startAtLine);
        for(int v=0;v<setScripts.size();v++)
        {
            parsedLine=(Vector)setScripts.elementAt(v);
            if(parsedLine.size()>1)
            {
                var=((String)parsedLine.elementAt(1)).toUpperCase();
                val=CMParms.combine(parsedLine,2);
                if(isStat(var))
                    setStat(var,val);
            }
        }
    }

    public StringBuffer getResourceFileData(String named)
    {
        int index=-1;
        if(internalFiles!=null){
            index=internalFiles.indexOf(named.toUpperCase().trim());
            if(index>=0) return (StringBuffer)internalFiles.elementAt(index,2);
        }
        StringBuffer buf=new CMFile(Resources.makeFileResourceName(named),null,true).text();
        return buf;
    }

    private void questifyScriptableBehavs(Environmental E)
    {
    	if(E==null) return;
    	Behavior B=null;
    	for(int b=0;b<E.numBehaviors();b++)
    	{
    		B=E.fetchBehavior(b);
    		if(B instanceof ScriptingEngine)
    			((ScriptingEngine)B).registerDefaultQuest(this.name());
    	}
    }

    private Vector sortSelect(Environmental E, String str,
                             Vector choices,
                             Vector choices0,
                             Vector choices1,
                             Vector choices2,
                             Vector choices3)
    {
        String mname=E.name().toUpperCase();
        String mdisp=E.displayText().toUpperCase();
        String mdesc=E.description().toUpperCase();
        if(str.equalsIgnoreCase("any"))
        {
            choices=choices0;
            choices0.addElement(E);
        }
        else
        if(mname.equalsIgnoreCase(str))
        {
            choices=choices0;
            choices0.addElement(E);
        }
        else
        if(CMLib.english().containsString(mname,str))
        {
            if((choices==null)||(choices==choices2)||(choices==choices3))
                choices=choices1;
            choices1.addElement(E);
        }
        else
        if(CMLib.english().containsString(mdisp,str))
        {
            if((choices==null)||(choices==choices3))
                choices=choices2;
            choices2.addElement(E);
        }
        else
        if(CMLib.english().containsString(mdesc,str))
        {
            if(choices==null) choices=choices3;
            choices3.addElement(E);
        }
        return choices;
    }

    private TimeClock getMysteryTimeNowFromState()
    {
    	TimeClock NOW=null;
    	if(questState.mysteryData==null) return (TimeClock)CMLib.time().globalClock().copyOf();
        if((questState.mysteryData.whereAt!=null)&&(questState.mysteryData.whereAt.getArea()!=null))
        	NOW=(TimeClock)questState.mysteryData.whereAt.getArea().getTimeObj().copyOf();
        else
        if((questState.mysteryData.whereHappened!=null)&&(questState.mysteryData.whereHappened.getArea()!=null))
        	NOW=(TimeClock)questState.mysteryData.whereHappened.getArea().getTimeObj().copyOf();
        else
        if((questState.room!=null)&&(questState.room.getArea()!=null))
        	NOW=(TimeClock)questState.room.getArea().getTimeObj().copyOf();
        else
        if(questState.area!=null)
        	NOW=(TimeClock)questState.area.getTimeObj().copyOf();
        else
        	NOW=(TimeClock)CMLib.time().globalClock().copyOf();
        return NOW;
    }

    public void parseQuestScriptWArgs(Vector script, Vector args)
    {
    	if(args==null) args=new Vector();
    	if(args.size()==0)
	        parseQuestScript(script, args, -1);
    	else
    	{
	        Vector allArgs=new Vector();
	        for(int i=0;i<args.size();i++)
	        {
	        	Object O=args.elementAt(i);
	        	if(O instanceof Vector)
	        	{
	        		Vector V=(Vector)O;
		    		if(allArgs.size()==0)
		        		for(int v=0;v<V.size();v++)
		        			allArgs.addElement(CMParms.makeVector(V.elementAt(v)));
		    		else
		    		{
		    			Vector allArgsCopy=(Vector)allArgs.clone();
		    			allArgs.clear();
		        		for(int aa=0;aa<allArgsCopy.size();aa++)
		        		{
		        			Vector argSet=(Vector)allArgsCopy.elementAt(aa);
			        		for(int v=0;v<V.size();v++)
				        	{
				        		Vector V2=(Vector)argSet.clone();
				        		V2.addElement(V.elementAt(v));
				        		allArgs.addElement(V2);
			        		}
			        	}
		        	}
	        	}
	    		else
	    		if(allArgs.size()==0)
        			allArgs.addElement(CMParms.makeVector(O));
	    		else
        		for(int aa=0;aa<allArgs.size();aa++)
	        		((Vector)allArgs.elementAt(aa)).addElement(O);
	        }
	        for(int a=0;a<allArgs.size();a++)
		        parseQuestScript(script, (Vector)allArgs.elementAt(a),-1);
        }
    }

    protected void errorOccurred(QuestState q, boolean quietFlag, String msg)
    {
    	if(!quietFlag) Log.errOut("Quest",msg);
        q.error=true;
    }

    private Enumeration getAppropriateRoomSet(QuestState q)
    {
        if(q.roomGroup!=null)
            return q.roomGroup.elements();
        else
        if(q.area!=null)
            return q.area.getMetroMap();
        return CMLib.map().rooms();
    }

    private void sizeDownTo(Vector V, int num)
    {
    	if(num<0) return;
    	if(num==0) V.clear();
    	else
    	while(V.size()>num)
    		V.removeElementAt(CMLib.dice().roll(1,V.size(),-1));
    }

    public void parseQuestScript(Vector script, Vector args, int startLine)
    {
    	Vector finalScript=new Vector();
    	for(int v=0;v<script.size();v++)
    	{
    		if(script.elementAt(v) instanceof String)
    			finalScript.addElement(script.elementAt(v));
    		else
    		if(script.elementAt(v) instanceof Vector)
    		{
    			int vs=v;
    			while((v<script.size())&&(script.elementAt(v) instanceof Vector))
    				v++;
    			int rnum=vs+CMLib.dice().roll(1,v-vs,-1);
    			if(rnum<script.size())
    			{
    				Vector V=(Vector)script.elementAt(rnum);
    				for(int v2=0;v2<V.size();v2++)
    					if(V.elementAt(v2) instanceof String)
	    					finalScript.addElement(V.elementAt(v2));
    			}
    		}
    	}

    	script=finalScript;
    	QuestState q=questState;
        int vStart=startLine;
        if(vStart<0) vStart=0;
        q.done=false;
        if(vStart>=script.size())
            return;
        q.startLine=vStart;
        for(int v=vStart;v<script.size();v++)
        {
            if(startLine>=0) q.lastLine=v;
            String s=modifyStringFromArgs((String)script.elementAt(v),args);
            Vector p=CMParms.parse(s);
            boolean isQuiet=q.beQuiet;
            if(p.size()>0)
            {
                String cmd=((String)p.elementAt(0)).toUpperCase();
                if(cmd.equals("<SCRIPT>"))
                {
                    StringBuffer jscript=new StringBuffer("");
                    while(((++v)<script.size())
                    &&(!((String)script.elementAt(v)).trim().toUpperCase().startsWith("</SCRIPT>")))
                        jscript.append(((String)script.elementAt(v))+"\n");
                    if(v>=script.size())
                    {
                    	errorOccurred(q,false,"Quest '"+name()+"', <SCRIPT> command without </SCRIPT> found.");
                        break;
                    }
                    if(!CMSecurity.isApprovedJScript(jscript))
                    {
                    	errorOccurred(q,false,"Quest '"+name()+"', <SCRIPT> not approved.  Use MODIFY JSCRIPT to approve.");
                        break;
                    }
                    Context cx = Context.enter();
                    try
                    {
                        JScriptQuest scope = new JScriptQuest(this,q);
                        cx.initStandardObjects(scope);
                        scope.defineFunctionProperties(JScriptQuest.functions,
                                                       JScriptQuest.class,
                                                       ScriptableObject.DONTENUM);
                        cx.evaluateString(scope, jscript.toString(),"<cmd>", 1, null);
                    }
                    catch(Exception e)
                    {
                       	errorOccurred(q,false,"Quest '"+name()+"', JScript q.error: "+e.getMessage()+".");
                        Context.exit();
                        break;
                    }
                    Context.exit();
                    continue;
                }
                if(cmd.equals("QUIET"))
                {
                    if(p.size()<2)
                    {
                        q.beQuiet=true;
                        continue;
                    }
                    isQuiet=true;
                    p.removeElementAt(0);
                    cmd=((String)p.elementAt(0)).toUpperCase();
                }
                if(cmd.equals("STEP"))
                {
                    if((p.size()>1)&&(((String)p.elementAt(1)).equalsIgnoreCase("BREAK")))
                    {
                        q.lastLine=script.size();
                        q.done=true;
                    }
                    else
                    if((p.size()>1)&&(((String)p.elementAt(1)).equalsIgnoreCase("BACK")))
                    {
                        if(startLine>=0) q.lastLine=q.startLine;
                    }
                    else
                        if(startLine>=0) q.lastLine=v+1;
                    return;
                }
                if(cmd.equals("RESET"))
                {
                    if(q.room!=null)
                        CMLib.map().resetRoom(q.room, true);
                    else
                    if(q.roomGroup!=null)
                    {
	                    for(int r=0;r<q.roomGroup.size();r++)
	                        CMLib.map().resetRoom((Room)q.roomGroup.elementAt(r), true);
                    }
                    else
                    if(q.area!=null)
                        CMLib.map().resetArea(q.area);
                    else
                    {
                    	errorOccurred(q,false,"Quest '"+name()+"', no resettable room, roomgroup, area, or areagroup set.");
                        break;
                    }
                }
                else
                if(cmd.equals("SET"))
                {
                    if(p.size()<2)
                    {
                    	errorOccurred(q,isQuiet,"Quest '"+name()+"', unfound variable on set.");
                    	break;
                    }
                    cmd=((String)p.elementAt(1)).toUpperCase();
                    if(cmd.equals("AREA"))
                    {
                        if(p.size()<3){
                            q.area=null;
                            continue;
                        }
                        try{
                            q.area=(Area)getObjectIfSpecified(p,args,2,0); q.envObject=q.area; continue;
                        }catch(CMException ex){
                            q.area=null;
                        }
                        Vector names=new Vector();
                        Vector areas=new Vector();
                        if((p.size()>3)&&(((String)p.elementAt(2)).equalsIgnoreCase("any")))
                            for(int ip=3;ip<p.size();ip++)
                                names.addElement(p.elementAt(ip));
                        else
                            names.addElement(CMParms.combine(p,2));
                        for(int n=0;n<names.size();n++)
                        {
                            String areaName=(String)names.elementAt(n);
                            int oldSize=areas.size();
                            if(areaName.equalsIgnoreCase("any"))
                                areas.addElement(CMLib.map().getRandomArea());
                            if(oldSize==areas.size())
                            for (Enumeration e = CMLib.map().areas(); e.hasMoreElements(); )
                            {
                                Area A2 = (Area) e.nextElement();
                                if (A2.Name().equalsIgnoreCase(areaName))
                                {
                                    areas.addElement(A2);
                                    break;
                                }
                            }
                            if(oldSize==areas.size())
                            for(Enumeration e=CMLib.map().areas();e.hasMoreElements();)
                            {
                                Area A2=(Area)e.nextElement();
                                if(CMLib.english().containsString(A2.Name(),areaName))
                                {
                                    areas.addElement(A2);
                                    break;
                                }
                            }
                        }
                        if(areas.size()>0)
                            q.area=(Area)areas.elementAt(CMLib.dice().roll(1,areas.size(),-1));
                        if(q.area==null)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown area '"+CMParms.combine(p,2)+"'.");
                        	break;
                        }
                    }
                    else
                    if(cmd.equals("AREAGROUP"))
                    {
                        q.area=null;
                        q.roomGroup=null;
                        if(p.size()<3) continue;
                        Vector names=new Vector();
                        Vector areas=new Vector();
                        for(int ip=2;ip<p.size();ip++)
                            names.addElement(p.elementAt(ip));
                        for(int n=0;n<names.size();n++)
                        {
                            String areaName=(String)names.elementAt(n);
                            int oldSize=areas.size();
                            if(areaName.equalsIgnoreCase("any"))
                                areas.addElement(CMLib.map().getRandomArea());
                            boolean addAll=areaName.equalsIgnoreCase("all");
                            if(oldSize==areas.size())
                            {
                                if(addAll)
                                {
                                    for (Enumeration e = CMLib.map().areas(); e.hasMoreElements(); )
                                    {
                                        Area A2=(Area)e.nextElement();
                                        if(!areas.contains(A2))
                                            areas.addElement(e.nextElement());
                                    }
                                }
                                else
                                {
                                    Area A2=CMLib.map().findArea(areaName);
                                    if((A2!=null)&&(!areas.contains(A2)))
                                        areas.addElement(A2);
                                }
                            }
                        }
                        if(areas.size()>0)
                        {
                            q.roomGroup=new Vector();
                            Area A=null;
                            Room R=null;
                            for(Enumeration e=areas.elements();e.hasMoreElements();)
                            {
                                A=(Area)e.nextElement();
                                for(Enumeration e2=A.getMetroMap();e2.hasMoreElements();)
                                {
                                    R=(Room)e2.nextElement();
                                    if(!q.roomGroup.contains(R))
                                        q.roomGroup.add(R);
                                }
                            }
                            q.envObject=q.roomGroup;
                        }
                        else
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown areas '"+CMParms.combine(p,2)+"'.");
                            break;
                        }
                    }
                    else
                    if(cmd.equals("MOBTYPE"))
                    {
                        boolean reselect=false;
                        if((p.size()>2)&&(((String)p.elementAt(2)).equalsIgnoreCase("reselect")))
                        {
                        	p.removeElementAt(2);
                        	reselect=true;
                        }
                        if(p.size()<3){
                            q.mob=null;
                            continue;
                        }
                        try{
                        	q.mob=(MOB)getObjectIfSpecified(p,args,2,0);
                        }catch(CMException ex){
                            q.mob=null;
	                        Vector choices=new Vector();
	                        Vector mobTypes=CMParms.parse(CMParms.combine(p,2).toUpperCase());
	                        for(int t=0;t<mobTypes.size();t++)
	                        {
	                            String mobType=(String)mobTypes.elementAt(t);
	                            if(mobType.startsWith("-")) continue;
	                            if(q.mobGroup==null)
	                            {
	                                try
	                                {
	                                    for(Enumeration e=getAppropriateRoomSet(q);e.hasMoreElements();)
	                                    {
	                                        Room R2=(Room)e.nextElement();
	                                        for(int i=0;i<R2.numInhabitants();i++)
	                                        {
	                                            MOB M2=R2.fetchInhabitant(i);
	                                            if((M2!=null)
	                                            &&(M2.isMonster())
	                                            &&((M2.amUltimatelyFollowing()==null)||(M2.amUltimatelyFollowing().isMonster())))
	                                            {
	                                                if(mobType.equalsIgnoreCase("any"))
	                                                    choices.addElement(M2);
	                                                else
	                                                if((CMClass.classID(M2).toUpperCase().indexOf(mobType)>=0)
	                                                ||(M2.charStats().getMyRace().racialCategory().toUpperCase().indexOf(mobType)>=0)
	                                                ||(M2.charStats().getMyRace().name().toUpperCase().indexOf(mobType)>=0)
	                                                ||(M2.charStats().getCurrentClass().name(M2.charStats().getCurrentClassLevel()).toUpperCase().indexOf(mobType)>=0))
	                                                    choices.addElement(M2);
	                                            }
	                                        }
	                                    }
	                                }catch(NoSuchElementException e){}
	                            }
	                            else
	                            {
	                                try
	                                {
	                                    for(Enumeration e=q.mobGroup.elements();e.hasMoreElements();)
	                                    {
	                                        MOB M2=(MOB)e.nextElement();
	                                        if((M2!=null)
	                                        &&(M2.isMonster())
                                            &&((M2.amUltimatelyFollowing()==null)||(M2.amUltimatelyFollowing().isMonster())))
	                                        {
	                                            if(mobType.equalsIgnoreCase("any"))
	                                                choices.addElement(M2);
	                                            else
	                                            if((CMClass.classID(M2).toUpperCase().indexOf(mobType)>=0)
	                                            ||(M2.charStats().getMyRace().racialCategory().toUpperCase().indexOf(mobType)>=0)
	                                            ||(M2.charStats().getMyRace().name().toUpperCase().indexOf(mobType)>=0)
	                                            ||(M2.charStats().getCurrentClass().name(M2.charStats().getCurrentClassLevel()).toUpperCase().indexOf(mobType)>=0))
	                                                choices.addElement(M2);
	                                        }
	                                    }
	                                }catch(NoSuchElementException e){}
	                            }
	                        }

	                        for(int t=0;t<mobTypes.size();t++)
	                        {
	                            String mobType=(String)mobTypes.elementAt(t);
	                            if(!mobType.startsWith("-")) continue;
	                            mobType=mobType.substring(1);
	                            for(int i=choices.size()-1;i>=0;i--)
	                            {
	                                MOB M2=(MOB)choices.elementAt(i);
	                                if((M2!=null)
	                                &&(M2.isMonster())
                                    &&((M2.amUltimatelyFollowing()==null)||(M2.amUltimatelyFollowing().isMonster())))
	                                {
	                                    if((CMClass.classID(M2).toUpperCase().indexOf(mobType)>=0)
	                                    ||(M2.charStats().getMyRace().racialCategory().toUpperCase().indexOf(mobType)>=0)
	                                    ||(M2.charStats().getMyRace().name().toUpperCase().indexOf(mobType)>=0)
	                                    ||(M2.charStats().getCurrentClass().name(M2.charStats().getCurrentClassLevel()).toUpperCase().indexOf(mobType)>=0)
	                                    ||(M2.name().toUpperCase().indexOf(mobType)>=0)
	                                    ||(M2.displayText().toUpperCase().indexOf(mobType)>=0))
	                                        choices.removeElement(M2);
	                                }
	                            }
	                        }
	                        if(choices.size()>0)
	                        {
	                            for(int c=choices.size()-1;c>=0;c--)
	                            	if(((!reselect)||(!q.reselectable.contains(choices.elementAt(c))))
	                                &&(CMLib.quests().objectInUse((Environmental)choices.elementAt(c))!=null))
	                                    choices.removeElementAt(c);
	                            if((choices.size()==0)&&(!isQuiet))
	                                errorOccurred(q,isQuiet,"Quest '"+name()+"', all choices were taken: '"+p+"'.");
	                        }
	                        if(choices.size()>0)
	                            q.mob=(MOB)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
                        }
                        if(q.mob==null)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"', !mobtype '"+p+"'.");
                        	break;
                        }
                        if(reselect) q.reselectable.add(q.mob);

                        // why is this being done -- atm, this is a simple map mob, minding his own business.
                        questifyScriptableBehavs(q.mob);

                        if(q.room!=null)
                            q.room.bringMobHere(q.mob,false);
                        else
                            q.room=q.mob.location();
                        q.envObject=q.mob;
                        runtimeRegisterObject(q.mob);
                        if(q.room!=null)
                        {
                            q.area=q.room.getArea();
                            q.room.recoverRoomStats();
                            q.room.showHappens(CMMsg.MSG_OK_ACTION,null);
                        }
                    }
                    else
                    if(cmd.equals("MOBGROUP"))
                    {
                        q.mobGroup=null;
                        boolean reselect=false;
                        if((p.size()>2)&&(((String)p.elementAt(2)).equalsIgnoreCase("reselect")))
                        {
                        	p.removeElementAt(2);
                        	reselect=true;
                        }
                        if(p.size()<3) continue;
                        Vector choices=null;
                        String mobName=CMParms.combine(p,2).toUpperCase();
                        String maskStr=CMLib.quests().breakOutMaskString(s,p);
                        Vector mask=(maskStr.trim().length()==0)?null:CMLib.masking().maskCompile(maskStr);
                        if(mask!=null) mobName=CMParms.combine(p,2).toUpperCase();
                        try{
                        	choices=(Vector)getObjectIfSpecified(p,args,2,1);
                        }catch(CMException ex){
                            if(mobName.length()==0) mobName="ANY";
                            boolean addAll=mobName.equalsIgnoreCase("all");
	                        Vector choices0=new Vector();
	                        Vector choices1=new Vector();
	                        Vector choices2=new Vector();
	                        Vector choices3=new Vector();
	                        try
	                        {
	                            for(Enumeration e=getAppropriateRoomSet(q);e.hasMoreElements();)
	                            {
	                                Room R2=(Room)e.nextElement();
	                                for(int i=0;i<R2.numInhabitants();i++)
	                                {
	                                    MOB M2=R2.fetchInhabitant(i);
	                                    if((M2!=null)
	                                    &&(M2.isMonster())
                                        &&((M2.amUltimatelyFollowing()==null)||(M2.amUltimatelyFollowing().isMonster())))
	                                    {
                                            if(CMLib.masking().maskCheck(mask,M2,true))
                                            {
    	                                        if(addAll)
    	                                        {
    	                                            choices = choices0;
    	                                            choices.addElement(M2);
    	                                        }
    	                                        else
        	                                        choices=sortSelect(M2,mobName,choices,choices0,choices1,choices2,choices3);
                                            }
	                                    }
	                                }
	                            }
	                        }catch(NoSuchElementException e){}

	                        if((choices!=null)&&(choices.size()>0))
	                        {
	                            for(int c=choices.size()-1;c>=0;c--)
	                            	if(((!reselect)||(!q.reselectable.contains(choices.elementAt(c))))
	                                &&(CMLib.quests().objectInUse((Environmental)choices.elementAt(c))!=null))
	                                    choices.removeElementAt(c);
	                            if((choices.size()==0)&&(!isQuiet))
	                                errorOccurred(q,isQuiet,"Quest '"+name()+"', all choices were taken: '"+p+"'.");
	                        }
                        }
                        if((choices!=null)&&(choices.size()>0))
                        {
                            q.mobGroup=choices;
                            if(reselect) q.reselectable.addAll(choices);
                        }
                        else
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"', !mobgroup '"+mobName+":"+mask+"'.");
                        	break;
                        }
                        q.envObject=q.mobGroup;
                    }
                    else
                    if(cmd.equals("ITEMGROUP"))
                    {
                        q.itemGroup=null;
                        boolean reselect=false;
                        if((p.size()>2)&&(((String)p.elementAt(2)).equalsIgnoreCase("reselect")))
                        {
                        	p.removeElementAt(2);
                        	reselect=true;
                        }
                        if(p.size()<3) continue;
                        Vector choices=null;
                        String itemName=CMParms.combine(p,2).toUpperCase();
                        String maskStr=CMLib.quests().breakOutMaskString(s,p);
                        Vector mask=(maskStr.trim().length()==0)?null:CMLib.masking().maskCompile(maskStr);
                        if(mask!=null) itemName=CMParms.combine(p,2).toUpperCase();
                        try{
                        	choices=(Vector)getObjectIfSpecified(p,args,2,1);
                        }catch(CMException ex){
	                        Vector choices0=new Vector();
	                        Vector choices1=new Vector();
	                        Vector choices2=new Vector();
	                        Vector choices3=new Vector();
	                        if(itemName.length()==0) itemName="ANY";
                            boolean addAll=itemName.equalsIgnoreCase("all");
	                        try
	                        {
	                            for(Enumeration e=getAppropriateRoomSet(q);e.hasMoreElements();)
	                            {
	                                Room R2=(Room)e.nextElement();
	                                for(int i=0;i<R2.numItems();i++)
	                                {
	                                    Item I2=R2.fetchItem(i);
	                                    if(I2!=null)
	                                    {
                                            if(CMLib.masking().maskCheck(mask,I2,true))
                                            {
    	                                        if(addAll)
    	                                        {
    	                                            choices = choices0;
    	                                            choices.addElement(I2);
    	                                        }
    	                                        else
        	                                        choices=sortSelect(I2,itemName,choices,choices0,choices1,choices2,choices3);
                                            }
	                                    }
	                                }
	                            }
	                        }catch(NoSuchElementException e){}
	                        if((choices!=null)&&(choices.size()>0))
	                        {
	                            for(int c=choices.size()-1;c>=0;c--)
	                            	if(((!reselect)||(!q.reselectable.contains(choices.elementAt(c))))
	                                &&(CMLib.quests().objectInUse((Environmental)choices.elementAt(c))!=null))
	                                    choices.removeElementAt(c);
	                            if((choices.size()==0)&&(!isQuiet))
	                                errorOccurred(q,isQuiet,"Quest '"+name()+"', all choices were taken: '"+p+"'.");
	                        }
                        }
                        if((choices!=null)&&(choices.size()>0))
                        {
                            if(reselect) q.reselectable.addAll(choices);
                            q.itemGroup=choices;
                        }
                        else
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"', !itemgroup '"+itemName+":"+mask+"'.");
                        	break;
                        }
                        q.envObject=q.itemGroup;
                    }
                    else
                    if(cmd.equals("ITEMTYPE"))
                    {
                        boolean reselect=false;
                        if((p.size()>2)&&(((String)p.elementAt(2)).equalsIgnoreCase("reselect")))
                        {
                        	p.removeElementAt(2);
                        	reselect=true;
                        }
                        if(p.size()<3){
                            q.item=null;
                            continue;
                        }
                        try{
                        	q.item=(Item)getObjectIfSpecified(p,args,2,0);
                        }catch(CMException ex){
                            q.item=null;
	                        Vector choices=new Vector();
	                        Vector itemTypes=new Vector();
	                        for(int i=2;i<p.size();i++)
	                            itemTypes.addElement(p.elementAt(i));
	                        for(int t=0;t<itemTypes.size();t++)
	                        {
	                            String itemType=((String)itemTypes.elementAt(t)).toUpperCase();
	                            if(itemType.startsWith("-")) continue;
	                            try
	                            {
	                                if(q.itemGroup==null)
	                                {
		                                for(Enumeration e=getAppropriateRoomSet(q);e.hasMoreElements();)
		                                {
		                                    Room R2=(Room)e.nextElement();
		                                    for(int i=0;i<R2.numItems();i++)
		                                    {
		                                        Item I2=R2.fetchItem(i);
		                                        if((I2!=null))
		                                        {
		                                            if(itemType.equalsIgnoreCase("any"))
		                                                choices.addElement(I2);
		                                            else
		                                            if(CMClass.classID(I2).toUpperCase().indexOf(itemType)>=0)
		                                                choices.addElement(I2);
		                                        }
		                                    }
		                                }
	                                }
	                                else
	                                {
	                                    for(Enumeration e=q.itemGroup.elements();e.hasMoreElements();)
	                                    {
	                                        Item I2=(Item)e.nextElement();
	                                        if((I2!=null))
	                                        {
	                                            if(itemType.equalsIgnoreCase("any"))
	                                                choices.addElement(I2);
	                                            else
	                                            if(CMClass.classID(I2).toUpperCase().indexOf(itemType)>=0)
	                                                choices.addElement(I2);
	                                        }
	                                    }
	                                }
	                            }catch(NoSuchElementException e){}
	                        }

	                        for(int t=0;t<itemTypes.size();t++)
	                        {
	                            String itemType=(String)itemTypes.elementAt(t);
	                            if(!itemType.startsWith("-")) continue;
	                            itemType=itemType.substring(1);
	                            for(int i=choices.size()-1;i>=0;i--)
	                            {
	                                Item I2=(Item)choices.elementAt(i);
	                                if((CMClass.classID(I2).toUpperCase().indexOf(itemType)>=0)
	                                ||(I2.name().toUpperCase().indexOf(itemType)>=0)
	                                ||(I2.displayText().toUpperCase().indexOf(itemType)>=0))
	                                    choices.removeElement(I2);
	                            }
	                        }
	                        if(choices.size()>0)
	                        {
	                            for(int c=choices.size()-1;c>=0;c--)
	                            	if(((!reselect)||(!q.reselectable.contains(choices.elementAt(c))))
	                                &&(CMLib.quests().objectInUse((Environmental)choices.elementAt(c))!=null))
	                                    choices.removeElementAt(c);
	                            if((choices.size()==0)&&(!isQuiet))
	                                errorOccurred(q,isQuiet,"Quest '"+name()+"', all choices were taken: '"+p+"'.");
	                        }
	                        if(choices.size()>0)
	                            q.item=(Item)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
                        }
                        if(q.item==null)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"', !itemtype '"+p+"'.");
                        	break;
                        }
                        questifyScriptableBehavs(q.item); // this really makes little sense, though is harmless
                        if(reselect) q.reselectable.add(q.item);
                        if(q.room!=null)
                            q.room.bringItemHere(q.item,-1,true);
                        else
                        if(q.item.owner() instanceof Room)
                            q.room=(Room)q.item.owner();
                        q.envObject=q.item;
                        if(q.room!=null)
                        {
                            q.area=q.room.getArea();
                            q.room.recoverRoomStats();
                            q.room.showHappens(CMMsg.MSG_OK_ACTION,null);
                        }
                    }
                    else
                    if(cmd.equals("PRESERVE"))
                        q.preserveState=CMath.parseIntExpression((String)p.elementAt(2));
                    else
                    if(cmd.equals("LOCALE")||cmd.equals("LOCALEGROUP")||cmd.equals("LOCALEGROUPAROUND"))
                    {
                    	int range=0;
                    	if(cmd.equals("LOCALE"))
                    	{
	                        try{
	                        	q.room=(Room)getObjectIfSpecified(p,args,2,0);
	                        	if(q.room!=null)
	                        	{
	                        		q.area=q.room.getArea();
		                        	q.envObject=q.room;
	                        	}
	                        	continue;
	                        }catch(CMException ex){
	                            q.room=null;
	                        }
                    	}
                    	else
                    	if(cmd.equals("LOCALEGROUPAROUND"))
                    	{
                    		q.roomGroup=null;
                        	if(p.size()<3) continue;
                        	range=CMath.parseIntExpression((String)p.elementAt(2));
                        	if(range<=0)
                        	{
                            	errorOccurred(q,isQuiet,"Quest '"+name()+"', !localegrouparound #'"+((String)p.elementAt(2)+"'."));
                            	break;
                        	}
                        	p.removeElementAt(2);
                        	if(q.room==null)
                        	{
                            	errorOccurred(q,isQuiet,"Quest '"+name()+"', localegrouparound !room.");
                            	break;
                        	}
                    	}
                    	else
                    	{
	                        try{
	                            q.roomGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                            q.envObject=q.roomGroup;
	                            continue;
	                        }catch(CMException ex){
	                            q.roomGroup=null;
	                        }
                    	}
                        if(p.size()<3) continue;
                        Vector names=new Vector();
                        if((p.size()>3)&&(((String)p.elementAt(2)).equalsIgnoreCase("any")))
                            for(int ip=3;ip<p.size();ip++)
                                names.addElement(p.elementAt(ip));
                        else
                            names.addElement(CMParms.combine(p,2));
                        Vector choices=new Vector();
                        Vector useThese=null;
                        if(range>0)
                        {
            				TrackingLibrary.TrackingFlags flags;
            				flags = new TrackingLibrary.TrackingFlags()
            						.add(TrackingLibrary.TrackingFlag.AREAONLY);
                        	useThese=CMLib.tracking().getRadiantRooms(q.room,flags,range);
                        }
                        for(int n=0;n<names.size();n++)
                        {
                            String localeName=((String)names.elementAt(n)).toUpperCase();
                            try
                            {
                                Enumeration e=null;
                                if(useThese!=null)
                                	e=useThese.elements();
                                else
                                if(q.area!=null)
                                	e=q.area.getMetroMap();
                                else
                                	e=CMLib.map().rooms();
                                boolean addAll=(localeName.equalsIgnoreCase("any")
                                                ||localeName.equalsIgnoreCase("all"));
                                for(;e.hasMoreElements();)
                                {
                                    Room R2=(Room)e.nextElement();
                                    if(addAll||CMClass.classID(R2).toUpperCase().indexOf(localeName)>=0)
                                        choices.addElement(R2);
                                    else
                                    {
                                        int dom=R2.domainType();
                                        if((dom&Room.INDOORS)>0)
                                        {
                                            if(Room.indoorDomainDescs[dom-Room.INDOORS].indexOf(localeName)>=0)
                                                choices.addElement(R2);
                                        }
                                        else
                                        if(Room.outdoorDomainDescs[dom].indexOf(localeName)>=0)
                                            choices.addElement(R2);
                                    }
                                }
                            }catch(NoSuchElementException e){}
                        }
                        if(cmd.equalsIgnoreCase("LOCALEGROUP")||cmd.equalsIgnoreCase("LOCALEGROUPAROUND"))
                        {
                        	if(choices.size()>0)
	                        	q.roomGroup=(Vector)choices.clone();
                        	else
	                        {
                            	errorOccurred(q,isQuiet,"Quest '"+name()+"', !localegroup '"+CMParms.combine(p,2)+"'.");
                            	break;
	                        }
                        	q.envObject=q.roomGroup;
                        }
                        else
                        {
	                        if(choices.size()>0)
	                            q.room=(Room)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
	                        if(q.room==null)
	                        {
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', !locale '"+CMParms.combine(p,2)+"'.");
	                            break;
	                        }
	                        q.area=q.room.getArea();
	                    	q.envObject=q.room;
                        }
                    }
                    else
                    if(cmd.equals("ROOM")||cmd.equals("ROOMGROUP")||cmd.equals("ROOMGROUPAROUND"))
                    {
                        int range=0;
                    	if(cmd.equals("ROOM"))
                    	{
	                        try{
	                        	q.room=(Room)getObjectIfSpecified(p,args,2,0);
	                        	if(q.room!=null)
	                        	{
	                        		q.area=q.room.getArea();
		                        	q.envObject=q.room;
	                        	}
	                        	continue;
	                        }catch(CMException ex){
	                            q.room=null;
	                        }
                    	}
                    	else
                    	if(cmd.equals("ROOMGROUPAROUND"))
                    	{
                    		q.roomGroup=null;
                        	if(p.size()<3) continue;
                        	range=CMath.s_parseIntExpression((String)p.elementAt(2));
                        	if(range<=0)
                        	{
                                errorOccurred(q,isQuiet,"Quest '"+name()+"' roomgrouparound #'"+((String)p.elementAt(2)+"'."));
	                            break;
                        	}
                        	p.removeElementAt(2);
                        	if(q.room==null)
                        	{
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', roomgrouparound !room.");
	                            break;
                        	}
                    	}
                    	else
                    	{
	                        try{
	                            q.roomGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                            q.envObject=q.roomGroup;
	                            continue;
	                        }catch(CMException ex){
	                            q.roomGroup=null;
	                        }
                    	}
                        if(p.size()<3) continue;
                        Vector choices=null;
                        Vector choices0=new Vector();
                        Vector choices1=new Vector();
                        Vector choices2=new Vector();
                        Vector choices3=new Vector();
                        Vector names=new Vector();
                        String maskStr=CMLib.quests().breakOutMaskString(s,p);
                        Vector mask=(maskStr.trim().length()==0)?null:CMLib.masking().maskCompile(maskStr);
                        if((p.size()>3)&&(((String)p.elementAt(2)).equalsIgnoreCase("any")))
                            for(int ip=3;ip<p.size();ip++)
                                names.addElement(p.elementAt(ip));
                        else
                            names.addElement(CMParms.combine(p,2));
                        Vector useThese=null;
                        if(range>0)
                        {
            				TrackingLibrary.TrackingFlags flags;
            				flags = new TrackingLibrary.TrackingFlags()
            						.add(TrackingLibrary.TrackingFlag.AREAONLY);
                        	useThese=CMLib.tracking().getRadiantRooms(q.room,flags,range);
                        }
                        for(int n=0;n<names.size();n++)
                        {
                            String localeName=((String)names.elementAt(n)).toUpperCase();
                            try
                            {
                                Enumeration e=null;
                                if(useThese!=null)
                                	e=useThese.elements();
                                else
                                if(q.area!=null)
                                	e=q.area.getMetroMap();
                                else
                                	e=CMLib.map().rooms();
                                boolean addAll=localeName.equalsIgnoreCase("any")
                                             ||localeName.equalsIgnoreCase("all");
                                for(;e.hasMoreElements();)
                                {
                                    Room R2=(Room)e.nextElement();
                                    String display=R2.displayText().toUpperCase();
                                    String desc=R2.description().toUpperCase();
                                    if((mask!=null)&&(!CMLib.masking().maskCheck(mask,R2,true)))
                                        continue;
                                    if(addAll)
                                    {
                                        choices=choices0;
                                        choices0.addElement(R2);
                                    }
                                    else
                                    if(CMLib.map().getExtendedRoomID(R2).equalsIgnoreCase(localeName))
                                    {
                                        choices=choices0;
                                        choices0.addElement(R2);
                                    }
                                    else
                                    if(display.equalsIgnoreCase(localeName))
                                    {
                                        if((choices==null)||(choices==choices2)||(choices==choices3))
                                            choices=choices1;
                                        choices1.addElement(R2);
                                    }
                                    else
                                    if(CMLib.english().containsString(display,localeName))
                                    {
                                        if((choices==null)||(choices==choices3))
                                            choices=choices2;
                                        choices2.addElement(R2);
                                    }
                                    else
                                    if(CMLib.english().containsString(desc,localeName))
                                    {
                                        if(choices==null) choices=choices3;
                                        choices3.addElement(R2);
                                    }
                                }
                            }catch(NoSuchElementException e){}
                        }
                        if(cmd.equalsIgnoreCase("ROOMGROUP")||cmd.equalsIgnoreCase("ROOMGROUPAROUND"))
                        {
                        	if((choices!=null)&&(choices.size()>0))
	                        	q.roomGroup=(Vector)choices.clone();
                        	else
	                        {
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', !roomgroup '"+CMParms.combine(p,2)+"'.");
	                            break;
	                        }
                        	q.envObject=q.roomGroup;
                        }
                        else
                        {
	                        if((choices!=null)&&(choices.size()>0))
	                            q.room=(Room)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
	                        if(q.room==null)
	                        {
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', !room '"+CMParms.combine(p,2)+"'.");
	                            break;
	                        }
	                        q.area=q.room.getArea();
	                    	q.envObject=q.room;
                        }
                    }
                    else
                    if(cmd.equals("MOB"))
                    {
                        boolean reselect=false;
                        if((p.size()>2)&&(((String)p.elementAt(2)).equalsIgnoreCase("reselect")))
                        {
                        	p.removeElementAt(2);
                        	reselect=true;
                        }
                        if(p.size()<3){
                            q.mob=null;
                            continue;
                        }
                        String mobName=CMParms.combine(p,2).toUpperCase();
                        String maskStr=CMLib.quests().breakOutMaskString(s,p);
                        Vector mask=(maskStr.trim().length()==0)?null:CMLib.masking().maskCompile(maskStr);
                        if(mask!=null) mobName=CMParms.combine(p,2).toUpperCase();
                        try{
                        	q.mob=(MOB)getObjectIfSpecified(p,args,2,0);
                        }catch(CMException ex){
                            q.mob=null;
	                        Vector choices=null;
	                        Vector choices0=new Vector();
	                        Vector choices1=new Vector();
	                        Vector choices2=new Vector();
	                        Vector choices3=new Vector();
	                        if(mobName.length()==0) mobName="ANY";
	                        if(q.mobGroup!=null)
	                        {
	                            for(Enumeration e=q.mobGroup.elements();e.hasMoreElements();)
	                            {
	                                MOB M2=(MOB)e.nextElement();
	                                if((M2!=null)
	                                &&(M2.isMonster())
                                    &&((M2.amUltimatelyFollowing()==null)||(M2.amUltimatelyFollowing().isMonster())))
	                                {
	                                    if(!CMLib.masking().maskCheck(mask,M2,true))
	                                        continue;
	                                    choices=sortSelect(M2,mobName,choices,choices0,choices1,choices2,choices3);
	                                }
	                            }
	                        }
	                        else
	                        {
	                            try
	                            {
	                                for(Enumeration e=getAppropriateRoomSet(q);e.hasMoreElements();)
	                                {
	                                    Room R2=(Room)e.nextElement();
	                                    for(int i=0;i<R2.numInhabitants();i++)
	                                    {
	                                        MOB M2=R2.fetchInhabitant(i);
	                                        if((M2!=null)
	                                        &&(M2.isMonster())
                                            &&((M2.amUltimatelyFollowing()==null)||(M2.amUltimatelyFollowing().isMonster())))
	                                        {
	                                            if(!CMLib.masking().maskCheck(mask,M2,true))
	                                                continue;
	                                            choices=sortSelect(M2,mobName,choices,choices0,choices1,choices2,choices3);
	                                        }
	                                    }
	                                }
	                            }catch(NoSuchElementException e){}
	                        }
	                        if((choices!=null)
	                        &&(choices.size()>0))
	                        {
	                            for(int c=choices.size()-1;c>=0;c--)
	                            	if(((!reselect)||(!q.reselectable.contains(choices.elementAt(c))))
	                                &&(CMLib.quests().objectInUse((Environmental)choices.elementAt(c))!=null))
	                                    choices.removeElementAt(c);
	                            if((choices.size()==0)&&(!isQuiet))
	                                errorOccurred(q,isQuiet,"Quest '"+name()+"', all choices were taken: '"+p+"'.");
	                        }
	                        if((choices!=null)&&(choices.size()>0))
	                            q.mob=(MOB)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
                        }
                        if(q.mob==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !mob '"+mobName+"'.");
                            break;
                        }
                        if(reselect) q.reselectable.addElement(q.mob);
                        questifyScriptableBehavs(q.mob);  // just wierd
                        if(q.room!=null)
                            q.room.bringMobHere(q.mob,false);
                        else
                            q.room=q.mob.location();
                        if(q.room!=null)
                        {
	                        q.area=q.room.getArea();
	                        q.envObject=q.mob;
	                        runtimeRegisterObject(q.mob);
	                        q.room.recoverRoomStats();
	                        q.room.showHappens(CMMsg.MSG_OK_ACTION,null);
                        }
                    }
                    else
                    if(cmd.equals("ITEM"))
                    {
                        boolean reselect=false;
                        if((p.size()>2)&&(((String)p.elementAt(2)).equalsIgnoreCase("reselect")))
                        {
                        	p.removeElementAt(2);
                        	reselect=true;
                        }
                        if(p.size()<3){
                            q.item=null;
                            continue;
                        }
                        String itemName=CMParms.combine(p,2).toUpperCase();
                        String maskStr=CMLib.quests().breakOutMaskString(s,p);
                        Vector mask=(maskStr.trim().length()==0)?null:CMLib.masking().maskCompile(maskStr);
                        if(mask!=null) itemName=CMParms.combine(p,2).toUpperCase();
                        try{
                        	q.item=(Item)getObjectIfSpecified(p,args,2,0);
                        }catch(CMException ex){
                            q.item=null;
	                        Vector choices=null;
	                        Vector choices0=new Vector();
	                        Vector choices1=new Vector();
	                        Vector choices2=new Vector();
	                        Vector choices3=new Vector();
	                        if(itemName.trim().length()==0) itemName="ANY";
	                        try
	                        {
	                        if(q.itemGroup!=null)
	                        {
	                            for(Enumeration e=q.itemGroup.elements();e.hasMoreElements();)
	                            {
	                                Item I2=(Item)e.nextElement();
	                                if(I2!=null)
	                                {
	                                    if(!CMLib.masking().maskCheck(mask,I2,true))
	                                        continue;
	                                    choices=sortSelect(I2,itemName,choices,choices0,choices1,choices2,choices3);
	                                }
	                            }
	                        }
	                        else
	                        {
	                            for(Enumeration e=getAppropriateRoomSet(q);e.hasMoreElements();)
	                            {
	                                Room R2=(Room)e.nextElement();
	                                for(int i=0;i<R2.numItems();i++)
	                                {
	                                    Item I2=R2.fetchItem(i);
	                                    if(I2!=null)
	                                    {
	                                        if(!CMLib.masking().maskCheck(mask,I2,true))
	                                            continue;
	                                        choices=sortSelect(I2,itemName,choices,choices0,choices1,choices2,choices3);
	                                    }
	                                }
	                            }
	                        }
	                        }catch(NoSuchElementException e){}
	                        if((choices!=null)&&(choices.size()>0))
	                        {
	                            for(int c=choices.size()-1;c>=0;c--)
	                            	if(((!reselect)||(!q.reselectable.contains(choices.elementAt(c))))
	                                &&(CMLib.quests().objectInUse((Environmental)choices.elementAt(c))!=null))
	                                    choices.removeElementAt(c);
	                            if((choices.size()==0)&&(!isQuiet))
	                                errorOccurred(q,isQuiet,"Quest '"+name()+"', all choices were taken: '"+p+"'.");
	                        }
	                        if((choices!=null)&&(choices.size()>0))
	                            q.item=(Item)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
                        }
                        if(q.item==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !item '"+itemName+"'.");
                            break;
                        }
                        if(reselect) q.reselectable.add(q.item);
                        questifyScriptableBehavs(q.item); // here we go again
                        if(q.room!=null)
                            q.room.bringItemHere(q.item,-1,true);
                        else
                        if(q.item.owner() instanceof Room)
                            q.room=(Room)q.item.owner();
                        q.envObject=q.item;
                        if(q.room!=null)
                        {
                            q.area=q.room.getArea();
                            q.room.recoverRoomStats();
                            q.room.showHappens(CMMsg.MSG_OK_ACTION,null);
                        }
                    }
                    else
                    if(cmd.equals("AGENT"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                        try{
                        	q.mob=(MOB)getObjectIfSpecified(p,args,2,0);
                        }catch(CMException ex){
	                    	if(p.size()>2)
	                        {
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', agent syntax '"+CMParms.combine(p,2)+"'.");
	                            break;
	                        }
                        }
                    	if(q.mob==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', agent !mob.");
                            break;
                        }
                        questifyScriptableBehavs(q.mob);  // should be done to loaded or q-scripted mobs only
                    	q.mysteryData.agent=q.mob;
                        q.mob=q.mysteryData.agent;
                        q.envObject=q.mob;
                    }
                    else
                    if(cmd.equals("FACTION"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                        if(p.size()<3) continue;
                        String numStr=CMParms.combine(p,2);
                        Faction F=null;
                        try{
                        	F=(Faction)getObjectIfSpecified(p,args,2,0);
                        }catch(CMException ex){
	                        if(numStr.equalsIgnoreCase("ANY"))
	                        {
	                        	int numFactions=CMLib.factions().factionSet().size();
	                        	int whichFaction=CMLib.dice().roll(1,numFactions,-1);
	                        	int curFaction=0;
	                        	Hashtable factions=(Hashtable)CMLib.factions().factionSet().clone();
	                        	for(Enumeration e=factions.elements();e.hasMoreElements();)
	                        	{
	                        		F=(Faction)e.nextElement();
	                        		if(curFaction==whichFaction)
	                        			break;
	                        		curFaction++;
	                        	}
	                        }
	                        else
	                        {
		                        F=CMLib.factions().getFaction(numStr);
		                        if(F==null) F=CMLib.factions().getFactionByName(numStr);
	                        }
                        }
                        if(F==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !faction #'"+numStr+"'.");
                            break;
                        }
                        q.mysteryData.faction=F;
                    }
                    else
                    if(cmd.equals("FACTIONGROUP"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                        if(p.size()<3){
                            q.mysteryData.factionGroup=null;
                            continue;
                        }
                        try{
                        	q.mysteryData.factionGroup=(Vector)getObjectIfSpecified(p,args,2,1);
                        }catch(CMException ex){
                            q.mysteryData.factionGroup=null;
	                        String numStr=CMParms.combine(p,2);
	                        Faction F=null;
	                    	if(q.mysteryData.faction!=null)
	                    		q.mysteryData.factionGroup.addElement(q.mysteryData.faction);
	                        if(CMath.isMathExpression(numStr)||numStr.equalsIgnoreCase("ALL"))
	                        {
	                        	int numFactions=CMLib.factions().factionSet().size();
	                        	if(numStr.equalsIgnoreCase("ALL")) numStr=""+numFactions;
	                        	int num=CMath.s_parseIntExpression(numStr);
	                        	if(num>=numFactions) num=numFactions;
	                        	int tries=500;
	                        	while((q.mysteryData.factionGroup.size()<num)&&(--tries>0))
	                        	{
		                        	int whichFaction=CMLib.dice().roll(1,numFactions,-1);
		                        	int curFaction=0;
		                        	Hashtable factions=(Hashtable)CMLib.factions().factionSet().clone();
		                        	for(Enumeration e=factions.elements();e.hasMoreElements();)
		                        	{
		                        		F=(Faction)e.nextElement();
		                        		if(curFaction==whichFaction)
		                        			break;
		                        		curFaction++;
		                        	}
		                        	if(!q.mysteryData.factionGroup.contains(F))
		                        		q.mysteryData.factionGroup.addElement(F);
	                        	}
	                        }
	                        else
	                        {
		                        for(int pi=2;pi<p.size();pi++)
		                        {
			                        F=CMLib.factions().getFaction((String)p.elementAt(pi));
			                        if(F==null) F=CMLib.factions().getFactionByName((String)p.elementAt(pi));
			                        if(F==null)
			                        {
		                                errorOccurred(q,isQuiet,"Quest '"+name()+"', !factiongroup '"+(String)p.elementAt(pi)+"'.");
			                            break;
			                        }
		                        	if(!q.mysteryData.factionGroup.contains(F))
		                        		q.mysteryData.factionGroup.addElement(F);
		                        }
		                        if(q.error) break;
	                        }
                        }
                        if((q.mysteryData.factionGroup!=null)
                        &&(q.mysteryData.factionGroup.size()>0)
                        &&(q.mysteryData.faction==null))
	                        q.mysteryData.faction=(Faction)q.mysteryData.factionGroup.elementAt(CMLib.dice().roll(1,q.mysteryData.factionGroup.size(),-1));
                    }
                    else
                    if(cmd.equals("AGENTGROUP"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                        if(p.size()<3){
                            q.mysteryData.agentGroup=null;
                            continue;
                        }
                        try{
                        	q.mysteryData.agentGroup=(Vector)getObjectIfSpecified(p,args,2,1);
                            if((q.mysteryData.agentGroup!=null)
                            &&(q.mysteryData.agentGroup.size()>0)
                            &&(q.mysteryData.agent==null))
    	                        q.mysteryData.agent=(MOB)q.mysteryData.agentGroup.elementAt(CMLib.dice().roll(1,q.mysteryData.agentGroup.size(),-1));
                        }catch(CMException ex){
                            q.mysteryData.agentGroup=null;
	                        String numStr=CMParms.combine(p,2).toUpperCase();
	                        if(!CMath.isMathExpression(numStr))
	                        {
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', !agentgroup #'"+numStr+"'.");
	                            break;
	                        }
	                        if((q.mobGroup==null)||(q.mobGroup.size()==0))
	                        {
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', !agentgroup mobgroup.");
	                            break;
	                        }
	                        Vector V=(Vector)q.mobGroup.clone();
	                        q.mysteryData.agentGroup=new Vector();
	                        if(q.mysteryData.agent!=null)
	                        	q.mysteryData.agentGroup.addElement(q.mysteryData.agent);
	                        int num=CMath.parseIntExpression(numStr);
	                        if(num>=V.size()) num=V.size();
	                        while((q.mysteryData.agentGroup.size()<num)&&(V.size()>0))
	                        {
	                        	int dex=CMLib.dice().roll(1,V.size(),-1);
	                        	Object O=V.elementAt(dex);
	                        	V.removeElementAt(dex);
	                        	q.mysteryData.agentGroup.addElement(O);
	                            if(q.mysteryData.agent==null) q.mysteryData.agent=(MOB)O;
	                        }
	                        questifyScriptableBehavs(q.mob);
                        }
                        q.mob=q.mysteryData.agent;
                        if(q.mysteryData.agentGroup!=null)
	                        q.mobGroup=(Vector)q.mysteryData.agentGroup.clone();
                        q.envObject=q.mysteryData.agentGroup;
                    }
                    else
                    if(cmd.equals("WHEREHAPPENEDGROUP")||cmd.equals("WHEREATGROUP"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	if(cmd.equals("WHEREHAPPENEDGROUP"))
                    	{
	                        try{
	                        	q.mysteryData.whereHappenedGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	q.roomGroup=q.mysteryData.whereHappenedGroup;
	                        	q.mysteryData.whereHappened=((q.roomGroup==null)||(q.roomGroup.size()==0))?null:
	                        								(Room)q.roomGroup.elementAt(CMLib.dice().roll(1,q.roomGroup.size(),-1));
	                            q.envObject=q.mysteryData.whereHappenedGroup;
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.whereHappenedGroup=null;
	                        }
                    	}
                    	else
                    	{
	                        try{
	                        	q.mysteryData.whereAtGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	q.roomGroup=q.mysteryData.whereAtGroup;
	                        	q.mysteryData.whereAt=((q.roomGroup==null)||(q.roomGroup.size()==0))?null:
	                        						  (Room)q.roomGroup.elementAt(CMLib.dice().roll(1,q.roomGroup.size(),-1));
	                            q.envObject=q.mysteryData.whereAtGroup;
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.whereAtGroup=null;
	                        }
                    	}
                        if(p.size()<3) continue;
                        String numStr=CMParms.combine(p,2).toUpperCase();
                        if(!CMath.isMathExpression(numStr))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !"+cmd.toLowerCase()+" #'"+numStr+"'.");
                            break;
                        }
                        if((q.roomGroup==null)||(q.roomGroup.size()==0))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !"+cmd.toLowerCase()+" roomGroup.");
                            break;
                        }
                        Vector V=(Vector)q.roomGroup.clone();
                        Vector V2=new Vector();
                        Room R=null;
                    	if(cmd.equals("WHEREHAPPENEDGROUP"))
                    	{
	                    	q.mysteryData.whereHappenedGroup=V2;
	                    	R=q.mysteryData.whereHappened;
                    	}
                    	else
                    	{
	                        q.mysteryData.whereAtGroup=null;
	                    	R=q.mysteryData.whereAt;
                    	}
                    	if(R!=null) V2.addElement(R);
                        int num=CMath.parseIntExpression(numStr);
                        if(num>=V.size()) num=V.size();
                        while((V2.size()<num)&&(V.size()>0))
                        {
                        	int dex=CMLib.dice().roll(1,V.size(),-1);
                        	Object O=V.elementAt(dex);
                        	V.removeElementAt(dex);
                        	if(!V2.contains(O)) V2.addElement(O);
                            if(R==null) R=(Room)O;
                        }
                        q.roomGroup=(Vector)V2.clone();
                        q.room=R;
                        q.envObject=q.roomGroup;
                    	if(cmd.equals("WHEREHAPPENEDGROUP"))
                    		q.mysteryData.whereHappened=R;
                    	else
                    		q.mysteryData.whereAt=R;
                    }
                    else
                    if(cmd.equals("WHENHAPPENEDGROUP")||cmd.equals("WHENATGROUP"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	Vector V2;
                        TimeClock TC=null;
                    	if(cmd.equals("WHENHAPPENEDGROUP"))
                    	{
	                        try{
	                        	q.mysteryData.whenHappenedGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	V2=q.mysteryData.whenHappenedGroup;
	                        	if((V2!=null)&&(V2.size()>0))
		                    		q.mysteryData.whenHappened=(TimeClock)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.whenHappenedGroup=null;
	                        }
                    	}
                    	else
                    	{
	                        try{
	                        	q.mysteryData.whenAtGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	V2=q.mysteryData.whenAtGroup;
	                        	if((V2!=null)&&(V2.size()>0))
		                    		q.mysteryData.whenAt=(TimeClock)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.whenAtGroup=null;
	                        }
                    	}
                        if(p.size()<3) continue;
                        V2=new Vector();
                        TimeClock NOW=getMysteryTimeNowFromState();
                    	if(TC!=null) V2.addElement(TC);
                    	if(cmd.equals("WHENHAPPENEDGROUP"))
                    	{
	                    	q.mysteryData.whenHappenedGroup=V2;
	                    	TC=q.mysteryData.whenHappened;
                    	}
                    	else
                    	{
	                        q.mysteryData.whenAtGroup=V2;
	                        TC=q.mysteryData.whenAt;
                    	}
                    	for(int pi=2;pi<p.size();pi++)
                    	{
                    		String numStr=(String)p.elementAt(pi);
                    		if(!CMath.isMathExpression(numStr))
                    		{
                                errorOccurred(q,isQuiet,"Quest '"+name()+"', "+cmd.toLowerCase()+" !relative hour #: "+numStr+".");
                                break;
                    		}
                    		TimeClock TC2=(TimeClock)NOW.copyOf();
                    		TC2.tickTock(CMath.parseIntExpression(numStr));
                    		V2.addElement(TC2);
                    	}
                    	if(q.error) break;
                    	if((V2.size()>0)&&(TC==null))
                    		TC=(TimeClock)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
                    	if(cmd.equals("WHENHAPPENEDGROUP"))
                    		q.mysteryData.whenHappened=TC;
                    	else
                    		q.mysteryData.whenAt=TC;
                    }
                    else
                    if(cmd.equals("WHENHAPPENED")
                    ||cmd.equals("WHENAT"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	if(cmd.equals("WHENHAPPENED"))
                    	{
	                        try{
	                            q.mysteryData.whenHappened=(TimeClock)getObjectIfSpecified(p,args,2,0);
	                            continue;
	                        }catch(CMException ex){
	                            q.mysteryData.whenHappened=null;
	                        }
                    	}
                    	else
                    	{
	                        try{
	                            q.mysteryData.whenAt=(TimeClock)getObjectIfSpecified(p,args,2,0);
	                            continue;
                            }catch(CMException ex){
                                q.mysteryData.whenAt=null;
                            }
                    	}
                        if(p.size()<3) continue;
                        TimeClock NOW=getMysteryTimeNowFromState();
                        TimeClock TC=null;
                        String numStr=CMParms.combine(p,2);
                		if(!CMath.isMathExpression(numStr))
                		{
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', "+cmd.toLowerCase()+" !relative hour #: "+numStr+".");
                            break;
                		}
                		TC=(TimeClock)NOW.copyOf();
                		TC.tickTock(CMath.parseIntExpression(numStr));
                    	if(cmd.equals("WHENHAPPENED"))
	                    	q.mysteryData.whenHappened=TC;
                    	else
	                    	q.mysteryData.whenAt=TC;
                    }
                    else
                    if(cmd.equals("MOTIVEGROUP")||cmd.equals("ACTIONGROUP"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	Vector V2=null;
                    	int num=-1;
                    	if((p.size()>2)&&(CMath.isMathExpression((String)p.elementAt(2))))
                    	{
                    		num=CMath.s_parseIntExpression((String)p.elementAt(2));
                    		p.removeElementAt(2);
                    	}
                    	if(cmd.equals("MOTIVEGROUP"))
                    	{
	                        try{
	                        	q.mysteryData.motiveGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	sizeDownTo(q.mysteryData.motiveGroup,num);
	                        	V2=q.mysteryData.motiveGroup;
	                        	if((V2!=null)&&(V2.size()>0))
		                    		q.mysteryData.motive=(String)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.motiveGroup=null;
	                        }
                    	}
                    	else
                    	{
	                        try{
	                        	q.mysteryData.actionGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	sizeDownTo(q.mysteryData.actionGroup,num);
	                        	V2=q.mysteryData.actionGroup;
	                        	if((V2!=null)&&(V2.size()>0))
		                    		q.mysteryData.action=(String)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.actionGroup=null;
	                        }
                    	}
                        if(p.size()<3) continue;
                        V2=new Vector();
                        String Mstr=null;
                    	if(cmd.equals("MOTIVEGROUP"))
                    	{
	                    	q.mysteryData.motiveGroup=V2;
	                    	Mstr=q.mysteryData.motive;
                    	}
                    	else
                    	{
	                    	q.mysteryData.actionGroup=V2;
	                    	Mstr=q.mysteryData.action;
                    	}
                    	if(Mstr!=null) V2.addElement(Mstr);
                        for(int pi=2;pi<p.size();pi++)
                        	if(!V2.contains(p.elementAt(pi)))
	                        	V2.addElement(p.elementAt(pi));
                    	sizeDownTo(V2,num);
                        if((V2.size()>0)&&(Mstr==null))
                        	Mstr=(String)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
                    	if(cmd.equals("MOTIVEGROUP"))
                    		q.mysteryData.motive=Mstr;
                    	else
                    		q.mysteryData.action=Mstr;
                    }
                    else
                    if(cmd.equals("MOTIVE"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                        if(p.size()<3) continue;
                        try{
                            q.mysteryData.motive=(String)getObjectIfSpecified(p,args,2,0);
                            continue;
                        }catch(CMException ex){
                            q.mysteryData.motive=null;
                        }
                        q.mysteryData.motive=CMParms.combine(p,2);
                    }
                    else
                    if(cmd.equals("ACTION"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                        if(p.size()<3) continue;
                        try{
                            q.mysteryData.action=(String)getObjectIfSpecified(p,args,2,0);
                            continue;
                        }catch(CMException ex){
                            q.mysteryData.action=null;
                        }
                        q.mysteryData.action=CMParms.combine(p,2);
                    }
                    else
                    if(cmd.equals("WHEREHAPPENED")
                    ||cmd.equals("WHEREAT"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	if(cmd.equals("WHEREHAPPENED"))
                    	{
                            try{
                            	q.mysteryData.whereHappened=(Room)getObjectIfSpecified(p,args,2,0);
                                q.room=q.mysteryData.whereHappened;
                                q.envObject=q.room;
                            	continue;
                            }catch(CMException ex){
                                q.mysteryData.whereHappened=null;
                            }
                    	}
                    	else
                    	{
                            try{
                            	q.mysteryData.whereAt=(Room)getObjectIfSpecified(p,args,2,0);
                                q.room=q.mysteryData.whereAt;
                                q.envObject=q.room;
                            	continue;
                            }catch(CMException ex){
                                q.mysteryData.whereAt=null;
                            }
                    	}
                    	if(p.size()>2)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', "+cmd.toLowerCase()+" syntax '"+CMParms.combine(p,2)+"'.");
                            break;
                        }
                    	if(q.room==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', "+cmd.toLowerCase()+" !room.");
                            break;
                        }
                    	if(cmd.equals("WHEREHAPPENED"))
	                    	q.mysteryData.whereHappened=q.room;
                    	else
	                    	q.mysteryData.whereAt=q.room;
                        q.envObject=q.room;
                    }
                    else
                    if(cmd.equals("TARGETGROUP")
                    ||cmd.equals("TOOLGROUP"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	Vector V2=null;
                    	if(cmd.equals("TARGETGROUP"))
                    	{
	                        try{
	                        	q.mysteryData.targetGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	V2=q.mysteryData.targetGroup;
	                        	if((V2!=null)&&(V2.size()>0))
	                        	{
	                        		if(V2.firstElement() instanceof MOB)
	                        		{
	                        			q.mobGroup=V2;
	                                    q.mob=(MOB)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                                    q.envObject=q.mobGroup;
		                        		q.mysteryData.target=q.mob;
	                        		}
	                        		if(V2.firstElement() instanceof Item)
	                        		{
	                        			q.itemGroup=V2;
	                                    q.item=(Item)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
		                        		q.mysteryData.target=q.item;
	                                    q.envObject=q.itemGroup;
	                        		}
	                        	}
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.targetGroup=null;
	                        }
                    	}
                    	else
                    	{
	                        try{
	                        	q.mysteryData.toolGroup=(Vector)getObjectIfSpecified(p,args,2,1);
	                        	V2=q.mysteryData.toolGroup;
	                        	if((V2!=null)&&(V2.size()>0))
	                        	{
	                        		if(V2.firstElement() instanceof MOB)
	                        		{
	                        			q.mobGroup=V2;
	                                    q.mob=(MOB)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                                    q.envObject=q.mobGroup;
		                        		q.mysteryData.tool=q.mob;
	                        		}
	                        		if(V2.firstElement() instanceof Item)
	                        		{
	                        			q.itemGroup=V2;
	                                    q.item=(Item)V2.elementAt(CMLib.dice().roll(1,V2.size(),-1));
	                                    q.envObject=q.itemGroup;
		                        		q.mysteryData.tool=q.item;
	                        		}
	                        	}
	                        	continue;
	                        }catch(CMException ex){
	                            q.mysteryData.toolGroup=null;
	                        }
                    	}
                        if(p.size()<3) continue;
                        String numStr=CMParms.combine(p,2).toUpperCase();
                        if(!CMath.isMathExpression(numStr))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !"+cmd.toLowerCase()+" #'"+numStr+"'.");
                            break;
                        }
                        if(((q.mobGroup==null)||(q.mobGroup.size()==0))
                        &&((q.itemGroup==null)||(q.itemGroup.size()==0)))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', !"+cmd.toLowerCase()+" mobgroup itemgroup.");
                            break;
                        }
                        Vector V;
                        if((q.mobGroup!=null)&&(q.mobGroup.size()>0))
                        	V=(Vector)q.mobGroup.clone();
                        else
                        	V=(Vector)q.itemGroup.clone();
                        V2=new Vector();
                        Environmental finalE=null;
                    	if(cmd.equals("TARGETGROUP"))
                    	{
	                    	q.mysteryData.targetGroup=V2;
	                    	finalE=q.mysteryData.target;
                    	}
                    	else
                    	{
	                    	q.mysteryData.toolGroup=V2;
	                    	finalE=q.mysteryData.tool;
                    	}
                    	if(finalE!=null)V2.addElement(finalE);
                        int num=CMath.parseIntExpression(numStr);
                        if(num>=V.size()) num=V.size();
                        Object O;
                        while((V2.size()<num)&&(V.size()>0))
                        {
                        	int dex=CMLib.dice().roll(1,V.size(),-1);
                        	O=V.elementAt(dex);
                        	V.removeElementAt(dex);
                        	if(!V2.contains(O)) V2.addElement(O);
                            if(finalE==null) finalE=(Environmental)O;
                        }
                        if(finalE instanceof MOB)
                        {
	                        q.mobGroup=(Vector)V2.clone();
	                        q.mob=(MOB)finalE;
	                        questifyScriptableBehavs(q.mob); // i just dont get it
                        }
                        else
                        if(finalE instanceof Item)
                        {
	                        q.itemGroup=(Vector)V2.clone();
	                        q.item=(Item)finalE;
	                        questifyScriptableBehavs(q.item);
                        }
                        q.envObject=V2;
                    	if(cmd.equals("TARGETGROUP"))
                    		q.mysteryData.target=finalE;
                    	else
                    		q.mysteryData.tool=finalE;
                    }
                    else
                    if(cmd.equals("TARGET")
                    ||cmd.equals("TOOL"))
                    {
                    	if(q.mysteryData==null) q.mysteryData=new MysteryData();
                    	if(cmd.equals("TARGET"))
                    	{
                            try{
                            	q.mysteryData.target=(Environmental)getObjectIfSpecified(p,args,2,0);
                            	if(q.mysteryData.target instanceof MOB)
                            		q.mob=(MOB)q.mysteryData.target;
                            	if(q.mysteryData.target instanceof Item)
                            		q.item=(Item)q.mysteryData.target;
                            	q.envObject=q.mysteryData.target;
                            	continue;
                            }catch(CMException ex){
                                q.mysteryData.target=null;
                            }
                    	}
                    	else
                    	{
                            try{
                            	q.mysteryData.tool=(Environmental)getObjectIfSpecified(p,args,2,0);
                            	if(q.mysteryData.tool instanceof MOB)
                            		q.mob=(MOB)q.mysteryData.tool;
                            	if(q.mysteryData.tool instanceof Item)
                            		q.item=(Item)q.mysteryData.tool;
                            	q.envObject=q.mysteryData.tool;
                            	continue;
                            }catch(CMException ex){
                                q.mysteryData.tool=null;
                            }
                    	}
                    	if(p.size()>2)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', "+cmd.toLowerCase()+" syntax '"+CMParms.combine(p,2)+"'.");
                            break;
                        }
                    	if((q.envObject instanceof Vector)
                    	&&(((Vector)q.envObject).size()>0)
                    	&&(((Vector)q.envObject).firstElement() instanceof Environmental))
                    		q.envObject=(Environmental)((Vector)q.envObject).elementAt(CMLib.dice().roll(1,((Vector)q.envObject).size(),-1));
                    	if(!(q.envObject instanceof Environmental))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', "+cmd.toLowerCase()+" !object.");
                            break;
                        }
                    	if(cmd.equals("TARGET"))
	                    	q.mysteryData.target=(Environmental)q.envObject;
                    	else
	                    	q.mysteryData.tool=(Environmental)q.envObject;
                    	if(q.envObject instanceof MOB)
                    	{
                    		q.mob=(MOB)q.envObject;
                            questifyScriptableBehavs(q.mob);  // useless, but harmless
                    	}
                    	else
                    	if(q.envObject instanceof Item)
                    	{
                    		q.item=(Item)q.envObject;
                            questifyScriptableBehavs(q.item);
                    	}
                    }
                    else
                    if(!isStat(cmd))
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown variable '"+cmd+"'.");
                        break;
                    }
                }
                else
                if(cmd.equals("IMPORT"))
                {
                    if(p.size()<2)
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', no IMPORT type.");
                        break;
                    }
                    cmd=((String)p.elementAt(1)).toUpperCase();
                    if(cmd.equals("MOBS"))
                    {
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', no IMPORT MOBS file.");
                            break;
                        }
                        StringBuffer buf=getResourceFileData(CMParms.combine(p,2));
                        if((buf==null)||(buf.length()<20))
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',Unknown XML file: '"+CMParms.combine(p,2)+"' for '"+name()+"'.");
                            break;
                        }
                        if(buf.substring(0,20).indexOf("<MOBS>")<0)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"', Invalid XML file: '"+CMParms.combine(p,2)+"' for '"+name()+"'.");
                            break;
                        }
                        q.loadedMobs=new Vector();
                        String errorStr=CMLib.coffeeMaker().addMOBsFromXML(buf.toString(),q.loadedMobs,null);
                        if(errorStr.length()>0)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',Error on import of: '"+CMParms.combine(p,2)+"' for '"+name()+"': "+errorStr+".");
                            break;
                        }
                        if(q.loadedMobs.size()<=0)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',No mobs loaded: '"+CMParms.combine(p,2)+"' for '"+name()+"'.");
                            break;
                        }
                    }
                    else
                    if(cmd.equals("ITEMS"))
                    {
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', no import filename!");
                            break;
                        }
                        StringBuffer buf=getResourceFileData(CMParms.combine(p,2));
                        if((buf==null)||(buf.length()<20))
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',Unknown XML file: '"+CMParms.combine(p,2)+"' for '"+name()+"'.");
                            break;
                        }
                        if(buf.substring(0,20).indexOf("<ITEMS>")<0)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',Invalid XML file: '"+CMParms.combine(p,2)+"' for '"+name()+"'.");
                            break;
                        }
                        q.loadedItems=new Vector();
                        String errorStr=CMLib.coffeeMaker().addItemsFromXML(buf.toString(),q.loadedItems,null);
                        if(errorStr.length()>0)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',Error on import of: '"+CMParms.combine(p,2)+"' for '"+name()+"': "+errorStr+".");
                            break;
                        }
                        if(q.loadedItems.size()<=0)
                        {
                        	errorOccurred(q,isQuiet,"Quest '"+name()+"',No items loaded: '"+CMParms.combine(p,2)+"' for '"+name()+"'.");
                            break;
                        }
                    }
                    else
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown import type '"+cmd+"'.");
                        break;
                    }
                }
                else
                if(cmd.startsWith("LOAD="))
                {
                	boolean error=q.error;
                	Vector args2=new Vector();
                	parseQuestScriptWArgs(parseLoadScripts(s,args,args2),args2);
                	if((!error)&&(q.error))
                		break;
                }
                else
                if(cmd.equals("LOAD"))
                {
                    if(p.size()<2)
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unfound type on load.");
                        break;
                    }
                    cmd=((String)p.elementAt(1)).toUpperCase();
                    if(cmd.equals("MOB")||cmd.equals("MOBGROUP"))
                    {
                        if(q.loadedMobs.size()==0)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot load mob, no mobs imported.");
                            break;
                        }
                        int maxToLoad=Integer.MAX_VALUE;
                        if((p.size()>2)&&(CMath.isMathExpression((String)p.elementAt(2))))
                    	{
                    		maxToLoad=CMath.parseIntExpression((String)p.elementAt(2));
                    		p.removeElementAt(2);
                    	}
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', no mob name to load!");
                            break;
                        }
                        String mobName=CMParms.combine(p,2);
                        String maskStr=CMLib.quests().breakOutMaskString(s,p);
                        Vector mask=(maskStr.trim().length()==0)?null:CMLib.masking().maskCompile(maskStr);
                        if(mask!=null) mobName=CMParms.combine(p,2).toUpperCase();
                        if(mobName.length()==0) mobName="ANY";
                        boolean addAll=mobName.equalsIgnoreCase("ALL")
                                        ||mobName.equalsIgnoreCase("ANY");
                        Vector choices=new Vector();
                        for(int i=0;i<q.loadedMobs.size();i++)
                        {
                            MOB M2=(MOB)q.loadedMobs.elementAt(i);
                            if((CMLib.masking().maskCheck(mask,M2,true))
                            &&(addAll
                                ||(CMLib.english().containsString(M2.name(),mobName))
                                ||(CMLib.english().containsString(M2.displayText(),mobName))
                                ||(CMLib.english().containsString(M2.description(),mobName))))
                                    choices.addElement(M2.copyOf());
                        }
                        if(choices.size()==0)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', no mob found to load '"+mobName+"'!");
                            break;
                        }
                        Vector mobsToDo=null;
                        if(cmd.equalsIgnoreCase("MOB"))
                        {
                        	mobsToDo=new Vector();
                        	mobsToDo.addElement(choices.elementAt(CMLib.dice().roll(1,choices.size(),-1)));
                        }
                        else
                        {
                        	mobsToDo=(Vector)choices.clone();
                        	q.mobGroup=mobsToDo;
                        }
                        while((mobsToDo.size()>maxToLoad)&&(maxToLoad>0))
                        	mobsToDo.removeElementAt(CMLib.dice().roll(1,mobsToDo.size(),-1));
                        while((mobsToDo.size()<maxToLoad)&&(maxToLoad>0)&&(maxToLoad<Integer.MAX_VALUE))
                        	mobsToDo.addElement(((CMObject)mobsToDo.elementAt(CMLib.dice().roll(1,mobsToDo.size(),-1))).copyOf());
                        Room choiceRoom=q.room;
                        for(int m=0;m<mobsToDo.size();m++)
                        {
                        	q.mob=(MOB)mobsToDo.elementAt(m);
	                        q.room=choiceRoom;
	                        if(q.room==null)
	                        {
	                            if(q.roomGroup!=null)
	                            	q.room=(Room)q.roomGroup.elementAt(CMLib.dice().roll(1,q.roomGroup.size(),-1));
	                            else
	                            if(q.area!=null)
	                                q.room=q.area.getRandomMetroRoom();
	                            else
	                                q.room=CMLib.map().getRandomRoom();
	                        }
	                        if(q.room!=null)
	                        {
	                            q.mob.setStartRoom(null);
	                            q.mob.baseEnvStats().setRejuv(0);
                                q.mob.baseEnvStats().setDisposition(q.mob.baseEnvStats().disposition()|EnvStats.IS_UNSAVABLE);
	                            q.mob.recoverEnvStats();
	                            q.mob.text();
	                            q.mob.bringToLife(q.room,true);
	                        }
                            questifyScriptableBehavs(q.mob);
	                        runtimeRegisterObject(q.mob);
	                        q.room.recoverRoomStats();
	                        q.room.showHappens(CMMsg.MSG_OK_ACTION,null);
                            if(q.mob!=null)
                                q.mob.setStartRoom(null); // necessary to tell qm to clean him UP!
                        }
                        q.envObject=mobsToDo.clone();
                        if(q.room!=null)
	                        q.area=q.room.getArea();
                    }
                    else
                    if(cmd.equals("ITEM")||cmd.equals("ITEMGROUP"))
                    {
                        if(q.loadedItems.size()==0)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot load item, no items imported.");
                            break;
                        }
                        int maxToLoad=Integer.MAX_VALUE;
                        if((p.size()>2)&&(CMath.isMathExpression((String)p.elementAt(2))))
                    	{
                    		maxToLoad=CMath.parseIntExpression((String)p.elementAt(2));
                    		p.removeElementAt(2);
                    	}
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', no item name to load!");
                            break;
                        }
                        String itemName=CMParms.combine(p,2);
                        Vector choices=new Vector();
                        for(int i=0;i<q.loadedItems.size();i++)
                        {
                            Item I2=(Item)q.loadedItems.elementAt(i);
                            if((itemName.equalsIgnoreCase("any"))
                            ||(CMLib.english().containsString(I2.name(),itemName))
                            ||(CMLib.english().containsString(I2.displayText(),itemName))
                            ||(CMLib.english().containsString(I2.description(),itemName)))
                                choices.addElement(I2.copyOf());
                        }
                        if(choices.size()==0)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', no item found to load '"+itemName+"'!");
                            break;
                        }
                        Vector itemsToDo=null;
                        if(cmd.equalsIgnoreCase("ITEM"))
                        {
                        	itemsToDo=new Vector();
                            itemsToDo.addElement(choices.elementAt(CMLib.dice().roll(1,choices.size(),-1)));
                        }
                        else
                        {
                        	itemsToDo=(Vector)choices.clone();
                        	q.itemGroup=itemsToDo;
                        }
                        while((itemsToDo.size()>maxToLoad)&&(maxToLoad>0))
                        	itemsToDo.removeElementAt(CMLib.dice().roll(1,itemsToDo.size(),-1));
                        while((itemsToDo.size()<maxToLoad)&&(maxToLoad>0)&&(maxToLoad<Integer.MAX_VALUE))
                        	itemsToDo.addElement(((CMObject)itemsToDo.elementAt(CMLib.dice().roll(1,itemsToDo.size(),-1))).copyOf());
                        Room choiceRoom=q.room;
                        for(int m=0;m<itemsToDo.size();m++)
                        {
                        	q.item=(Item)itemsToDo.elementAt(m);
	                        q.room=choiceRoom;
	                        if(q.room==null)
	                        {
	                            if(q.roomGroup!=null)
	                            	q.room=(Room)q.roomGroup.elementAt(CMLib.dice().roll(1,q.roomGroup.size(),-1));
	                            else
	                            if(q.area!=null)
	                                q.room=q.area.getRandomMetroRoom();
	                            else
	                                q.room=CMLib.map().getRandomRoom();
	                        }
	                        if(q.room!=null)
	                        {
	                            q.item.baseEnvStats().setRejuv(0);
                                q.item.baseEnvStats().setDisposition(q.item.baseEnvStats().disposition()|EnvStats.IS_UNSAVABLE);
	                            q.item.recoverEnvStats();
	                            q.item.text();
	                            q.room.addItem(q.item);
	                            q.room.recoverRoomStats();
	                            q.room.showHappens(CMMsg.MSG_OK_ACTION,null);
	                        }
                            questifyScriptableBehavs(q.item);
	                        runtimeRegisterObject(q.item);
                        }
                        if(q.room!=null)
	                        q.area=q.room.getArea();
                        q.envObject=itemsToDo.clone();
                    }
                    else
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown load type '"+cmd+"'.");
                        break;
                    }

                }
                else
                if(cmd.equals("GIVE"))
                {
                    if(p.size()<2)
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unfound type on give.");
                        break;
                    }
                    cmd=((String)p.elementAt(1)).toUpperCase();
                    if(cmd.equals("FOLLOWER"))
                    {
                        if(q.mob==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give follower, no mob set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give follower, follower name not given.");
                            break;
                        }
                        String mobName=CMParms.combine(p,2);
                        Vector choices=new Vector();
                        for(int i=q.stuff.size()-1;i>=0;i--)
                        {
                            Environmental E2=(Environmental)q.stuff.elementAt(i,1);
                            if((E2!=q.mob)&&(E2 instanceof MOB))
                            {
                                MOB M2=(MOB)E2;
                                if((mobName.equalsIgnoreCase("any"))
                                ||(CMLib.english().containsString(M2.name(),mobName))
                                ||(CMLib.english().containsString(M2.displayText(),mobName))
                                ||(CMLib.english().containsString(M2.description(),mobName)))
                                    choices.addElement(M2);
                            }
                        }
                        if(choices.size()==0)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give follower, no mobs called '"+mobName+"' previously set in script.");
                            break;
                        }
                        MOB M2=(MOB)choices.elementAt(CMLib.dice().roll(1,choices.size(),-1));
                        M2.setFollowing(q.mob);
                    }
                    else
                    if(cmd.equals("ITEM")||(cmd.equalsIgnoreCase("ITEMS")))
                    {
                        if((q.item==null)&&(q.itemGroup==null)&&(q.loadedItems==null))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give item(s), no item(s) set or loaded.");
                            break;
                        }
                        if((q.mob==null)&&(q.mobGroup==null))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give item(s), no mob set.");
                            break;
                        }
                        if(p.size()>2)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give item(s), parameter unnecessarily given: '"+CMParms.combine(p,2)+"'.");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.mob!=null)
                            toSet.addElement(q.mob);
                        else
                        if(q.mobGroup!=null)
                            toSet=q.mobGroup;
                        Vector itemSet=new Vector();
                        if(q.item!=null)
                        	itemSet.addElement(q.item);
                        else
                        if(q.itemGroup!=null)
                        	itemSet=q.itemGroup;
                        else
                        if(q.loadedItems!=null)
                            itemSet=q.loadedItems;
                        for(int i=0;i<toSet.size();i++)
                        {
                            MOB M2=(MOB)toSet.elementAt(i);
                            runtimeRegisterObject(M2);
                            if(cmd.equals("ITEMS"))
                            {
	                            for(int i3=0;i3<itemSet.size();i3++)
	                            {
	                            	Item I3=(Item)itemSet.elementAt(i3);
		                            if(q.item==I3)
		                            {
			                            M2.giveItem(I3);
			                            q.item=(Item)q.item.copyOf();
	                                    questifyScriptableBehavs(q.item);
		                            }
		                            else
		                            {
		                                I3=(Item)I3.copyOf();
	                                    questifyScriptableBehavs(I3);
			                            M2.giveItem(I3);
		                            }
	                            }
                            }
                            else
                            if(cmd.equals("ITEM"))
                            {
                            	Item I3=(Item)itemSet.elementAt(CMLib.dice().roll(1,itemSet.size(),-1));
                                questifyScriptableBehavs(I3);
	                            if(q.item==I3)
	                            {
		                            M2.giveItem(I3);
		                            q.item=(Item)q.item.copyOf();
                                    questifyScriptableBehavs(q.item);
	                            }
	                            else
                                {
                                    I3=(Item)I3.copyOf();
                                    questifyScriptableBehavs(I3);
		                            M2.giveItem(I3);
                                }
                            }
                        }
                    }
                    else
                    if(cmd.equals("ABILITY"))
                    {
                        if((q.mob==null)&&(q.mobGroup==null))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give ability, no mob set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give ability, ability name not given.");
                            break;
                        }
                        Ability A3=CMClass.findAbility((String)p.elementAt(2));
                        if(A3==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give ability, ability name unknown '"+((String)p.elementAt(2))+".");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.mob!=null)
                            toSet.addElement(q.mob);
                        else
                        if(q.mobGroup!=null)
                            toSet=q.mobGroup;
                        for(int i=0;i<toSet.size();i++)
                        {
                            MOB M2=(MOB)toSet.elementAt(i);
                            runtimeRegisterAbility(M2,A3.ID(),CMParms.combineWithQuotes(p,3),true);
                        }
                    }
                    else
                    if(cmd.equals("BEHAVIOR"))
                    {
                        if(q.envObject==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give behavior, no mob or item set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give behavior, behavior name not given.");
                            break;
                        }
                        Behavior B=CMClass.getBehavior((String)p.elementAt(2));
                        if(B==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give behavior, behavior name unknown '"+((String)p.elementAt(2))+".");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.envObject instanceof Vector)
                            toSet=(Vector)q.envObject;
                        else
                        if(q.envObject!=null)
                            toSet.addElement(q.envObject);
                        for(int i=0;i<toSet.size();i++)
                        {
                            Environmental E2=(Environmental)toSet.elementAt(i);
                            runtimeRegisterBehavior(E2,B.ID(),CMParms.combineWithQuotes(p,3),true);
                        }
                    }
                    else
                    if(cmd.equals("STAT"))
                    {
                        if(q.envObject==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give stat, no mob or item set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give stat, stat name not given.");
                            break;
                        }
                        String stat=(String)p.elementAt(2);
                        String val=CMParms.combineWithQuotes(p,3);
                        Vector toSet=new Vector();
                        if(q.envObject instanceof Vector)
                            toSet=(Vector)q.envObject;
                        else
                        if(q.envObject!=null)
                            toSet.addElement(q.envObject);
                        for(int i=0;i<toSet.size();i++)
                        {
                            Environmental E2=(Environmental)toSet.elementAt(i);
                            if(stat.equalsIgnoreCase("KEYPLAYER"))
                            {
                                Ability A=E2.fetchEffect("QuestBound");
                                if(A!=null) A.setStat("KEY",val);
                            }
                            else
                                runtimeRegisterStat(E2,stat,val,true);
                        }
                    }
                    else
                    if(cmd.equals("SCRIPT"))
                    {
                        if(q.envObject==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give script, no object set.");
                            break;
                        }
                        boolean proceed=true;
                        boolean savable=false;
                        String word=null;
                        String scope=CMStrings.replaceAll(name()," ","_").toUpperCase().trim();
                        while(proceed&&(p.size()>2))
                        {
                            word=(String)p.elementAt(2);
                            proceed=false;
                            if(word.equalsIgnoreCase("SAVABLE"))
                            {
                                savable=true;
                                proceed=true;
                            }
                            else
                            if(word.equalsIgnoreCase("GLOBAL"))
                            {
                                scope="";
                                proceed=true;
                            }
                            else
                            if(word.equalsIgnoreCase("INDIVIDUAL"))
                            {
                                scope="*";
                                proceed=true;
                            }
                            if(proceed) p.removeElementAt(2);
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give script, script not given.");
                            break;
                        }
                        String val=CMParms.combineWithQuotes(p,2);
                        Vector toSet=new Vector();
                        if(q.envObject instanceof Vector)
                            toSet=(Vector)q.envObject;
                        else
                        if(q.envObject!=null)
                            toSet.addElement(q.envObject);
                        for(int i=0;i<toSet.size();i++)
                        {
                            Environmental E2=(Environmental)toSet.elementAt(i);
                            ScriptingEngine S=(ScriptingEngine)CMClass.getCommon("DefaultScriptingEngine");
                            S.setSavable(savable);
                            S.registerDefaultQuest(name());
                            S.setVarScope(scope);
                            S.setScript(val);
                            E2.addScript(S);
                            runtimeRegisterObject(E2);
                            questState.addons.addElement(CMParms.makeVector(E2,S),Integer.valueOf(questState.preserveState));
                        }
                    }
                    else
                    if(cmd.equals("AFFECT"))
                    {
                        if(q.envObject==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give Effect, no mob, room or item set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give Effect, ability name not given.");
                            break;
                        }
                        Ability A3=CMClass.findAbility((String)p.elementAt(2));
                        if(A3==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot give Effect, ability name unknown '"+((String)p.elementAt(2))+".");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.envObject instanceof Vector)
                            toSet=(Vector)q.envObject;
                        else
                        if(q.envObject!=null)
                            toSet.addElement(q.envObject);
                        for(int i=0;i<toSet.size();i++)
                        {
                            Environmental E2=(Environmental)toSet.elementAt(i);
                            runtimeRegisterEffect(E2,A3.ID(),CMParms.combineWithQuotes(p,3),true);
                        }
                    }
                    else
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown give type '"+cmd+"'.");
                        break;
                    }
                }
                else
                if(cmd.equals("TAKE"))
                {
                    if(p.size()<2)
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unfound type on take.");
                        break;
                    }
                    cmd=((String)p.elementAt(1)).toUpperCase();
                    if(cmd.equals("ABILITY"))
                    {
                        if((q.mob==null)&&(q.mobGroup==null))
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take ability, no mob set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take ability, ability name not given.");
                            break;
                        }
                        Ability A3=CMClass.findAbility((String)p.elementAt(2));
                        if(A3==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take ability, ability name unknown '"+((String)p.elementAt(2))+".");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.mob!=null)
                            toSet.addElement(q.mob);
                        else
                        if(q.mobGroup!=null)
                            toSet=q.mobGroup;
                        for(int i=0;i<toSet.size();i++)
                        {
                            MOB M2=(MOB)toSet.elementAt(i);
                            runtimeRegisterAbility(M2,A3.ID(),CMParms.combineWithQuotes(p,3),false);
                        }
                    }
                    else
                    if(cmd.equals("BEHAVIOR"))
                    {
                        if(q.envObject==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take behavior, no mob or item set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take behavior, behavior name not given.");
                            break;
                        }
                        Behavior B=CMClass.getBehavior((String)p.elementAt(2));
                        if(B==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take behavior, behavior name unknown '"+((String)p.elementAt(2))+".");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.envObject instanceof Vector)
                            toSet=(Vector)q.envObject;
                        else
                        if(q.envObject!=null)
                            toSet.addElement(q.envObject);
                        for(int i=0;i<toSet.size();i++)
                        {
                            Environmental E2=(Environmental)toSet.elementAt(i);
                            runtimeRegisterBehavior(E2,B.ID(),CMParms.combineWithQuotes(p,3),false);
                        }
                    }
                    else
                    if(cmd.equals("AFFECT"))
                    {
                        if(q.envObject==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take Effect, no mob, room or item set.");
                            break;
                        }
                        if(p.size()<3)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take Effect, ability name not given.");
                            break;
                        }
                        Ability A3=CMClass.findAbility((String)p.elementAt(2));
                        if(A3==null)
                        {
                            errorOccurred(q,isQuiet,"Quest '"+name()+"', cannot take Effect, ability name unknown '"+((String)p.elementAt(2))+".");
                            break;
                        }
                        Vector toSet=new Vector();
                        if(q.envObject instanceof Vector)
                            toSet=(Vector)q.envObject;
                        else
                        if(q.envObject!=null)
                            toSet.addElement(q.envObject);
                        for(int i=0;i<toSet.size();i++)
                        {
                            Environmental E2=(Environmental)toSet.elementAt(i);
                            runtimeRegisterEffect(E2,A3.ID(),CMParms.combineWithQuotes(p,3),false);
                        }
                    }
                    else
                    {
                        errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown take type '"+cmd+"'.");
                        break;
                    }
                }
                else
                {
                    errorOccurred(q,isQuiet,"Quest '"+name()+"', unknown command '"+cmd+"'.");
                    break;
                }
                q.done=true;
            }
        }
    }

    public boolean spawnQuest(String script, Vector baseVars, boolean reTime)
    {
    	DefaultQuest Q2=(DefaultQuest)CMClass.getCommon("DefaultQuest");
        Q2.setCopy(true);
        Q2.setVars(baseVars,0);
        Q2.setScript(script);
        CMLib.quests().addQuest(Q2);
        if(reTime)
        {
        	Long ellapsed=(Long)stepEllapsedTimes.get(script);
        	if(ellapsed==null) ellapsed=Long.valueOf(0);
        	stepEllapsedTimes.remove(script);
        	ellapsed=Long.valueOf(ellapsed.longValue()+(System.currentTimeMillis()-lastStartDateTime));
        	stepEllapsedTimes.put(script,ellapsed);
            Q2.resetWaitRemaining(ellapsed.longValue());
            if(Q2.startQuestOnTime())
            {
            	stepEllapsedTimes.remove(script);
            	return true;
            }
        }
        else
        if(Q2.startQuestInternal())
        	return true;
    	Q2.enterDormantState();
    	return false;
    }


    public boolean startQuest() 
    {
        if((!running())&&(!isCopy()))
        	CMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_QUESTSTARTATTEMPT);
        return startQuestInternal();
    }
    
    // this will execute the quest script.  If the quest is running, it
    // will call stopQuestInternal first to shut it down.
    public boolean startQuestInternal()
    {
        if(running()) {
        	stopQuestInternal();
        	resetData=null;
        }

        Vector args=new Vector();
        questState=new QuestState();
        Vector baseScript=parseLoadScripts(script(),new Vector(),args);
        if((!isCopy())&&(getSpawn()!=SPAWN_NO))
        {
            if(getSpawn()==SPAWN_FIRST)
                spawnQuest(script(),baseScript,false);
            else
            if(getSpawn()==SPAWN_ANY)
            {
                Vector parsed=CMLib.quests().parseQuestSteps(baseScript,0,false);
                for(int p=0;p<parsed.size();p++)
                    spawnQuest((String)parsed.elementAt(p),baseScript,true);
            }
            lastStartDateTime=System.currentTimeMillis();
        	enterDormantState();
            return false; // always return false, since, per se, this quest is NOT started.
        }
        try{
	        parseQuestScript(baseScript,args,0);
        }catch(Throwable t){
        	questState.error=true;
        	Log.errOut("DefaultQuest",t);
        }
        if(questState.error)
        {
            if(!questState.beQuiet)
            {
                int retry=0;
                if((durable)&&(resetData==null))
                    retry=10;
                else
                if(resetData!=null)
                    retry=resetData[0];
                Log.errOut("Quest","Errors starting '"
                        +name()
                        +"', quest not started"
                        +((retry>0)?", retry in "+retry+".":"."));
            }
            if((durable)&&(resetData==null))
            {
                resetQuest(10);
                return false;
            }
        	CMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_QUESTFAILEDSTART);
        }
        else
        if(!questState.done)
            Log.errOut("Quest","Nothing parsed in '"+name()+"', quest not started.");
        else
        if(duration()<0)
        {
            Log.errOut("Quest","No duration, quest '"+name()+"' not started.");
            questState.error=true;
        }
        if((!questState.error)&&(questState.done))
        {
        	enterRunningState();
        	return true;
        }
        stopQuestInternal();
        return false;
    }

    public void enterRunningState()
    {
        if(duration()>=0)
        {
            waitRemaining=-1;
            if(duration()==0)
                ticksRemaining=1;
            else
            if((resetData!=null)&&(resetData[1]>0))
                ticksRemaining=resetData[1];
            else
                ticksRemaining=duration();
            CMLib.threads().startTickDown(this,Tickable.TICKID_QUEST,1);
        }
        resetData=null;
        lastStartDateTime=System.currentTimeMillis();
        stepEllapsedTimes.remove(script());
    }

    public void cleanQuestStep()
    {
        stoppingQuest=true;
        if(questState.stuff.size()>0)
        {
        	synchronized(questState)
        	{
	            for(int i=0;i<questState.stuff.size();i++)
	            {
	                Integer I=(Integer)questState.stuff.elementAt(i,2);
	                if(I.intValue()>0)
	                {
	                    questState.stuff.setElementAt(i,2,Integer.valueOf(I.intValue()-1));
	                    continue;
	                }
	                Environmental E=(Environmental)questState.stuff.elementAt(i,1);
	            	Ability A=E.fetchEffect("QuestBound");
	            	if(A!=null)E.delEffect(A);
	                questState.stuff.removeElementAt(i);
	                if(E instanceof Item)
	                {
	                	if((CMath.bset(E.baseEnvStats().disposition(),EnvStats.IS_UNSAVABLE))
	                	&&(!((Item)E).amDestroyed()))
		                    ((Item)E).destroy();
	                }
	                else
	                if(E instanceof MOB)
	                {
	                    MOB M=(MOB)E;
	                    ScriptingEngine B=(ScriptingEngine)((MOB)E).fetchBehavior("Scriptable");
	                    if(B!=null)
	                        B.endQuest(E,M,name());
	                    Room R=M.getStartRoom();
	                    if((R==null)||(CMath.bset(E.baseEnvStats().disposition(),EnvStats.IS_UNSAVABLE)))
	                    {
	                    	M.setFollowing(null);
	                        CMLib.tracking().wanderAway(M,true,false);
	                        if(M.location()!=null)
	                            M.location().delInhabitant(M);
	                        M.setLocation(null);
	                        M.destroy();
	                    }
	                    else
	                    if((!M.amDead())
	                    &&(!M.amDestroyed())
	                    &&((M.location()!=R)||(!R.isInhabitant(M))))
	                    {
	                    	M.setFollowing(null);
	                        CMLib.tracking().wanderAway(M,false,true);
	                    }
	                }
	                i--;
	            }
	        }
	        if(questState.addons.size()>0)
	        {
	        	synchronized(questState)
	        	{
	        		if(stoppingQuest)
		            for(int i=questState.addons.size()-1;i>=0;i--)
		            {
		                Integer I=(Integer)questState.addons.elementAt(i,2);
		                if(I.intValue()>0)
		                {
		                    questState.addons.setElementAt(i,2,Integer.valueOf(I.intValue()-1));
		                    continue;
		                }
		                Vector V=(Vector)questState.addons.elementAt(i,1);
		                questState.addons.removeElementAt(i);
		                if(V.size()<2) continue;
		                Environmental E=(Environmental)V.elementAt(0);
		                Object O=V.elementAt(1);
		                if(O instanceof String)
		                {
		                    String stat=(String)O;
		                    String parms=(String)V.elementAt(2);
		                    if(CMStrings.contains(E.getStatCodes(),stat.toUpperCase().trim()))
			                    E.setStat(stat,parms);
		                    else
		                    if(CMStrings.contains(E.baseEnvStats().getStatCodes(),stat.toUpperCase().trim()))
		                    {
		                    	E.baseEnvStats().setStat(stat.toUpperCase().trim(),parms);
		                    	E.recoverEnvStats();
		                    }
		                    else
		                    if((E instanceof MOB)&&(CMStrings.contains(CharStats.STAT_NAMES,stat.toUpperCase().trim())))
		                    {
		                    	((MOB)E).baseCharStats().setStat(CMParms.indexOf(CharStats.STAT_NAMES,stat.toUpperCase().trim()),CMath.s_int(parms));
		                    	((MOB)E).recoverCharStats();
		                    }
		                    else
		                    if((E instanceof MOB)&&CMStrings.contains(((MOB)E).baseState().getStatCodes(),stat))
		                    {
		                    	((MOB)E).baseState().setStat(stat,parms);
		                    	((MOB)E).recoverMaxState();
		                    	((MOB)E).resetToMaxState();
		                    }
		                }
		                else
		                if(O instanceof Behavior)
		                {
		                    Behavior B=E.fetchBehavior(((Behavior)O).ID());
		                    if((E instanceof MOB)&&(B instanceof ScriptingEngine))
		                        ((ScriptingEngine)B).endQuest(E,(MOB)E,name());
		                    if((V.size()>2)&&(V.elementAt(2) instanceof String))
		                    {
		                        if(B==null){ B=(Behavior)O; E.addBehavior(B);}
		                        B.setParms((String)V.elementAt(2));
		                    }
		                    else
		                    if(B!=null)
		                        E.delBehavior(B);
		                }
		                else
		                if(O instanceof ScriptingEngine)
		                {
		                    ScriptingEngine S=(ScriptingEngine)O;
		                    if((E instanceof MOB)&&(!S.isSavable()))
		                    {
		                        S.endQuest(E,(MOB)E,name());
		                        ((MOB)E).delScript(S);
		                    }
		                }
		                else
		                if(O instanceof Ability)
		                {
		                    if((V.size()>2)
		                    &&(V.elementAt(2) instanceof Ability)
		                    &&(E instanceof MOB))
		                    {
		                        Ability A=((MOB)E).fetchAbility(((Ability)O).ID());
		                        if((V.size()>3)&&(V.elementAt(3) instanceof String))
		                        {
		                            if(A==null){A=(Ability)O; ((MOB)E).addAbility(A);}
		                            A.setMiscText((String)V.elementAt(3));
		                        }
		                        else
		                        if(A!=null)
		                            ((MOB)E).delAbility(A);
		                    }
		                    else
		                    {
		                        Ability A=E.fetchEffect(((Ability)O).ID());
		                        if((V.size()>2)&&(V.elementAt(2) instanceof String))
		                        {
		                            if(A==null){A=(Ability)O; E.addEffect(A);}
		                            A.setMiscText((String)V.elementAt(2));
		                        }
		                        else
		                        if(A!=null)
		                        {
		                            A.unInvoke();
		                            E.delEffect(A);
		                        }
		                    }
		                }
		                else
		                if(O instanceof Item)
		                    ((Item)O).destroy();
		            }
		        }
	        }
        }
        stoppingQuest=false;
    }


    // this will cause a quest to begin parsing its next "step".
    // this will clear out unpreserved objects from previous
    // step and resume quest script processing.
    // if this is the LAST step, stopQuestInternal() is automatically called

    public boolean stepQuest()
    {
        if((questState==null)||(stoppingQuest))
        	return false;
        cleanQuestStep();
        ticksRemaining=-1;
        setDuration(-1);

        Vector args=new Vector();
        Vector script=parseLoadScripts(script(),new Vector(),args);
        try{
        	setVars(script,questState.lastLine);
            parseQuestScript(script,args,questState.lastLine);
        }catch(Throwable t){
            questState.error=true;
            Log.errOut("DefaultQuest",t);
        }
        if(questState.error)
        {
            if(!questState.beQuiet)
                Log.errOut("Quest","One or more errors in '"+name()+"', quest not started");
        }
        else
        if(!questState.done)
        {
            // valid DONE state, when stepping over the end
        }
        else
        if(duration()<0)
        {
            Log.errOut("Quest","No duration, quest '"+name()+"' not started.");
            questState.error=true;
        }

        if((!questState.error)&&(questState.done)&&(duration()>=0))
        {
        	enterRunningState();
        	return true;
        }
        stopQuestInternal();
        return false;
    }

    public void resetQuest(int firstPauseTicks) {
        if(stoppingQuest) return;
        // ticksRemaining of -1 is OK, it will grab duration
        resetData=new int[]{firstPauseTicks,ticksRemaining};
        stopQuest();
    }
    
    public void stopQuest()
    {
        if((!stoppingQuest)&&(running()))
	    	CMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_QUESTSUCCESS);
    	stopQuestInternal();
    }
    
    // this will stop executing of the quest script.  It will clean up
    // any objects or mobs which may have been loaded, restoring map
    // mobs to their previous state.
    public void stopQuestInternal()
    {
        if(stoppingQuest) return;
        // first set everything to complete!
        synchronized(questState)
        {
	        for(int q=0;q<questState.stuff.size();q++)
	            questState.stuff.setElementAt(q,2,Integer.valueOf(0));
	        for(int q=0;q<questState.addons.size();q++)
	            questState.addons.setElementAt(q,2,Integer.valueOf(0));
        }
        cleanQuestStep();
        stoppingQuest=true;
        if(!isCopy())
        	setScript(script()); // causes wait times/name to reload
        enterDormantState();
        stoppingQuest=false;
    }

    public boolean enterDormantState()
    {
    	ticksRemaining=-1;
        if((isCopy())||(!resetWaitRemaining(0)))
    	{
    		CMLib.quests().delQuest(this);
            CMLib.threads().deleteTick(this,Tickable.TICKID_QUEST);
            return false;
    	}
        return true;
    }

    public boolean resetWaitRemaining(long ellapsedTime)
    {
        
        if(resetData!=null) {
            waitRemaining=resetData[0];
            resetData[0]*=2;
            return true;
        }
        
        if(((minWait()<0)||(maxWait<0))
        &&(startDate().trim().length()==0))
            return false;
        if(running())
        	return true;
        if(startDate().length()>0)
        {
            if(startDate().toUpperCase().startsWith("MUDDAY"))
            {
                String sd2=startDate().substring("MUDDAY".length()).trim();
                int x=sd2.indexOf("-");
                if(x<0) return false;
                int mudmonth=CMath.s_parseIntExpression(sd2.substring(0,x));
                int mudday=CMath.s_parseIntExpression(sd2.substring(x+1));
                TimeClock C=(TimeClock)CMClass.getCommon("DefaultTimeClock");
                TimeClock NOW=CMLib.time().globalClock();
                C.setMonth(mudmonth);
                C.setDayOfMonth(mudday);
                C.setTimeOfDay(0);
                if((mudmonth<NOW.getMonth())
                ||((mudmonth==NOW.getMonth())&&(mudday<NOW.getDayOfMonth())))
                    C.setYear(NOW.getYear()+1);
                else
                    C.setYear(NOW.getYear());
                long distance=C.deriveMillisAfter(NOW);
                waitRemaining=(int)(distance/Tickable.TIME_TICK);
            }
            else
            {
                int x=startDate.indexOf("-");
                if(x<0) return false;
                int month=CMath.s_parseIntExpression(startDate.substring(0,x));
                int day=CMath.s_parseIntExpression(startDate.substring(x+1));
                int year=Calendar.getInstance().get(Calendar.YEAR);
                long distance=CMLib.time().string2Millis(month+"/"+day+"/"+year+" 12:00 AM");
                while(distance<System.currentTimeMillis())
                    distance=CMLib.time().string2Millis(month+"/"+day+"/"+(++year)+" 12:00 AM");
                waitRemaining=(int)((distance-System.currentTimeMillis())/Tickable.TIME_TICK);
            }
        }
        else
            waitRemaining=(minWait+(CMLib.dice().roll(1,maxWait,0)))-(int)(ellapsedTime/Tickable.TIME_TICK);
        return true;
    }

    public int minWait(){return minWait;}
    public void setMinWait(int wait){minWait=wait;}
    public int waitInterval(){return maxWait;}
    public void setWaitInterval(int wait){maxWait=wait;}
    public int waitRemaining(){return waitRemaining;}

    public Quest getMainQuestObject()
    {
        Quest Q=this;
        if(isCopy())
        {
            Quest Q2=null;
            for(int q=0;q<CMLib.quests().numQuests();q++)
            {
                Q2=CMLib.quests().fetchQuest(q);
                if((Q2!=null)&&(Q2.name().equals(name))&&(!isCopy()))
                { Q=Q2; break;}
            }
        }
        return Q;
    }

    // if the quest has a winner, this is him.
    public void declareWinner(String name)
    {
    	if(name==null) return;
        name=name.trim();
        if(name.length()==0) return;
        Quest Q=getMainQuestObject();
        if(!Q.wasWinner(name))
        {
            Q.getWinners().addElement(name);
            CMLib.database().DBUpdateQuest(Q);
        }
    }

    public String getWinnerStr()
    {
        Quest Q=getMainQuestObject();
        StringBuffer list=new StringBuffer("");
        Vector V=Q.getWinners();
        for(int i=0;i<V.size();i++)
            list.append(((String)V.elementAt(i))+";");
        return list.toString();
    }

    public void setWinners(String list)
    {
        Quest Q=getMainQuestObject();
        Vector V=Q.getWinners();
        V.clear();
        list=list.trim();
        int x=list.indexOf(";");
        while(x>0)
        {
            String s=list.substring(0,x).trim();
            list=list.substring(x+1).trim();
            if(s.length()>0)
                V.addElement(s);
            x=list.indexOf(";");
        }
        if(list.trim().length()>0)
            V.addElement(list.trim());
    }

    // retreive the list of previous winners
    public Vector getWinners()
    {
        Quest Q=getMainQuestObject();
        if(Q==this) return winners;
        return Q.getWinners();
    }

    // was a previous winner
    public boolean wasWinner(String name)
    {
    	if(name==null) return false;
        name=name.trim();
        if(name.length()==0) return false;
        Quest Q=getMainQuestObject();
        Vector V=Q.getWinners();
        for(int i=0;i<V.size();i++)
        {
            if(((String)V.elementAt(i)).equalsIgnoreCase(name))
                return true;
        }
        return false;
    }

    // informational
    public boolean running(){return ticksRemaining>=0;}
    public boolean stopping(){return stoppingQuest;}
    public boolean waiting(){return waitRemaining>=0;}
    public int ticksRemaining(){return ticksRemaining;}
    public int minsRemaining(){return (int)(ticksRemaining*Tickable.TIME_TICK/60000);}
    private long tickStatus=Tickable.STATUS_NOT;
    public long getTickStatus(){return tickStatus;}

    public boolean tick(Tickable ticking, int tickID)
    {
        if(tickID!=Tickable.TICKID_QUEST)
            return false;
        if(CMSecurity.isDisabled("QUESTS")
        ||(CMProps.getBoolVar(CMProps.SYSTEMB_MUDSHUTTINGDOWN))
        ||(!CMProps.getBoolVar(CMProps.SYSTEMB_MUDSTARTED))
        ||(suspended))
            return true;

        tickStatus=Tickable.STATUS_START;
        if(running())
        {
            tickStatus=Tickable.STATUS_ALIVE;
            if(duration()>0)
                ticksRemaining--;
            if(ticksRemaining<0)
            {
            	CMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_QUESTTIMESTOP);
                stopQuest();
            }
            tickStatus=Tickable.STATUS_END;
        }
        else
        {
        	if(startQuestOnTime())
        	{
            	CMLib.coffeeTables().bump(this,CoffeeTableRow.STAT_QUESTTIMESTART);
        	}
        }
        tickStatus=Tickable.STATUS_NOT;
        return true;
    }

    protected boolean startQuestOnTime()
    {
        if((--waitRemaining)>=0) return false;

        boolean allowedToRun=true;
        if(runLevel()>=0)
        for(int q=0;q<CMLib.quests().numQuests();q++)
        {
            Quest Q=CMLib.quests().fetchQuest(q);
            if((!Q.name().equals(name))
            &&(Q.running())
            &&(Q.duration()!=0)
            &&(Q.runLevel()<=runLevel()))
            { allowedToRun=false; break;}
        }
        if(allowedToRun)
        {
            int numElligiblePlayers=CMLib.sessions().size();
            if(playerMask.length()>0)
            {
                numElligiblePlayers=0;
                for(int s=CMLib.sessions().size()-1;s>=0;s--)
                {
                    Session S=CMLib.sessions().elementAt(s);
                    if((S.mob()!=null)&&(CMLib.masking().maskCheck(playerMask,S.mob(),true)))
                        numElligiblePlayers++;
                }
            }
        	ticksRemaining=-1;
            if((numElligiblePlayers>=minPlayers)||(duration()==0))
                return startQuestInternal();
        }
    	enterDormantState();
    	return false;
    }


    public void runtimeRegisterAbility(MOB mob, String abilityID, String parms, boolean give)
    {
        if(mob==null) return;
        runtimeRegisterObject(mob);
        Vector V=new Vector();
        V.addElement(mob);
        Ability A4=mob.fetchAbility(abilityID);
        if(A4!=null)
        {
            V.addElement(A4);
            V.addElement(A4);
            V.addElement(A4.text());
            if(give)
            {
                A4.setMiscText(parms);
                A4.setProficiency(100);
            }
            else
                mob.delAbility(A4);
        }
        else
        if(!give) return;
        else
        {
            A4=CMClass.getAbility(abilityID);
            if(A4==null) return;
            A4.setMiscText(parms);
            V.addElement(A4);
            V.addElement(A4);
            A4.setProficiency(100);
            mob.addAbility(A4);
        }
        synchronized(questState)
        {
	        questState.addons.addElement(V,Integer.valueOf(questState.preserveState));
        }
    }

    public void runtimeRegisterObject(Environmental object)
    {
        synchronized(questState)
        {
	        int x=questState.stuff.indexOf(object);
	        if(x<0)
	        {
	        	questState.stuff.addElement(object, Integer.valueOf(questState.preserveState));
	        	Ability A=CMClass.getAbility("QuestBound");
	        	A.setMiscText(""+this);
	        	object.addNonUninvokableEffect(A);
	        }
	        else
	        {
	            Integer I=(Integer)questState.stuff.elementAt(x,2);
	            if(I.intValue()<questState.preserveState)
	                questState.stuff.setElementAt(x,2, Integer.valueOf(questState.preserveState));
	        }
        }
    }

    public void runtimeRegisterEffect(Environmental affected, String abilityID, String parms, boolean give)
    {
        if(affected==null) return;
        runtimeRegisterObject(affected);
        Vector V=new Vector();
        V.addElement(affected);
        Ability A4=affected.fetchEffect(abilityID);
        if(A4!=null)
        {
            V.addElement(A4);
            V.addElement(A4.text());
            if(give)
            {
                A4.makeLongLasting();
                A4.setMiscText(parms);
            }
            else
                affected.delEffect(A4);
        }
        else
        if(!give) return;
        else
        {
            A4=CMClass.getAbility(abilityID);
            if(A4==null) return;
            V.addElement(A4);
            A4.setMiscText(parms);
            if(affected instanceof MOB)
                A4.startTickDown((MOB)affected,affected,99999);
            else
                A4.startTickDown(null,affected,99999);
            A4.makeLongLasting();
        }
        synchronized(questState)
        {
	        questState.addons.addElement(V,Integer.valueOf(questState.preserveState));
        }
    }

    public void runtimeRegisterBehavior(Environmental behaving, String behaviorID, String parms, boolean give)
    {
        if(behaving==null) return;
        runtimeRegisterObject(behaving);
        Vector V=new Vector();
        V.addElement(behaving);
        Behavior B=behaving.fetchBehavior(behaviorID);
        if(B!=null)
        {
            V.addElement(B);
            V.addElement(B.getParms());
            if(give)
                B.setParms(parms);
            else
                behaving.delBehavior(B);
        }
        else
        if(!give){
            return;
        }
        else
        {
            B=CMClass.getBehavior(behaviorID);
            if(B==null) return;
            V.addElement(B);
            B.setParms(parms);
            behaving.addBehavior(B);
        }
        B.registerDefaultQuest(name());
        synchronized(questState)
        {
	        questState.addons.addElement(V,Integer.valueOf(questState.preserveState));
        }
    }

    public void runtimeRegisterStat(Environmental E, String stat, String parms, boolean give)
    {
        if(E==null) return;
        runtimeRegisterObject(E);
        Vector V=new Vector();
        V.addElement(E);
        stat=stat.toUpperCase().trim();
        String oldVal="";
        if(CMStrings.contains(E.getStatCodes(),stat))
            oldVal=E.getStat(stat);
        else
        if(CMStrings.contains(E.baseEnvStats().getStatCodes(),stat))
        	oldVal=E.baseEnvStats().getStat(stat);
        else
        if((E instanceof MOB)&&(CMStrings.contains(CharStats.STAT_NAMES,stat)))
        	oldVal=""+((MOB)E).baseCharStats().getStat(CMParms.indexOf(CharStats.STAT_NAMES,stat));
        else
        if((E instanceof MOB)&&CMStrings.contains(((MOB)E).baseState().getStatCodes(),stat))
        	oldVal=((MOB)E).baseState().getStat(stat);
        V.addElement(stat);
        V.addElement(oldVal);
        if(!give) return;
        V.addElement(stat);
        V.addElement(oldVal);
        if(CMStrings.contains(E.getStatCodes(),stat))
        	E.setStat(stat,parms);
        else
        if(CMStrings.contains(E.baseEnvStats().getStatCodes(),stat))
        {
        	E.baseEnvStats().setStat(stat,parms);
        	E.recoverEnvStats();
        }
        else
        if((E instanceof MOB)&&(CMStrings.contains(CharStats.STAT_NAMES,stat)))
        {
        	((MOB)E).baseCharStats().setStat(CMParms.indexOf(CharStats.STAT_NAMES,stat),CMath.s_int(parms));
        	((MOB)E).recoverCharStats();
        }
        else
        if((E instanceof MOB)&&CMStrings.contains(((MOB)E).baseState().getStatCodes(),stat))
        {
        	((MOB)E).baseState().setStat(stat,parms);
        	((MOB)E).recoverMaxState();
        	((MOB)E).resetToMaxState();
        }
        synchronized(questState)
        {
	        questState.addons.addElement(V,Integer.valueOf(questState.preserveState));
        }
    }

    public int getQuestThingIndex(Vector V, String name, int type, int[] num)
    {
        if(V==null) return -1;
        for(int i=0;i<V.size();i++)
        {
            Environmental E=(Environmental)V.elementAt(i);
            if(CMClass.isType(E,type))
            {
                switch(type)
                {
                case CMClass.OBJECT_LOCALE:
                    if(CMLib.map().getExtendedRoomID((Room)E).equalsIgnoreCase(name))
                        return num[0];
                    break;
                default:
                    if(E.Name().equalsIgnoreCase(name))
                        return num[0];
                    break;
                }
                num[0]++;
            }
        }
        return -1;
    }

    public int getQuestMobIndex(String name)
    {
        int[] num={1};
        int x=-1;
        synchronized(questState)
        {
	        if(questState.stuff!=null)
	            x=getQuestThingIndex(questState.stuff.getDimensionVector(1),name,CMClass.OBJECT_MOB,num);
	        if(x<0)
	            x=getQuestThingIndex(questState.mobGroup,name,CMClass.OBJECT_MOB,num);
	        if((x<0)&&(questState.mysteryData!=null))
	            x=getQuestThingIndex(questState.mysteryData.agentGroup,name,CMClass.OBJECT_MOB,num);
        }
        return x;
    }

    public int getQuestRoomIndex(String roomID)
    {
        int[] num={1};
        int x=-1;
        synchronized(questState)
        {
	        if(questState.stuff!=null)
	            x=getQuestThingIndex(questState.stuff.getDimensionVector(1),roomID,CMClass.OBJECT_LOCALE,num);
	        if(x<0)
	            x=getQuestThingIndex(questState.roomGroup,roomID,CMClass.OBJECT_LOCALE,num);
	        if(questState.mysteryData!=null)
	        {
	            if(x<0)
	                x=getQuestThingIndex(questState.mysteryData.whereAtGroup,roomID,CMClass.OBJECT_LOCALE,num);
	            if(x<0)
	                x=getQuestThingIndex(questState.mysteryData.whereHappenedGroup,roomID,CMClass.OBJECT_LOCALE,num);
	        }
        }
        return x;
    }

    public int getQuestItemIndex(String name)
    {
        int[] num={1};
        int x=-1;
        synchronized(questState)
        {
	        if(questState.stuff!=null)
	            x=getQuestThingIndex(questState.stuff.getDimensionVector(1),name,CMClass.OBJECT_ITEM,num);
	        if(x<0)
	            x=getQuestThingIndex(questState.itemGroup,name,CMClass.OBJECT_ITEM,num);
	        if(questState.mysteryData!=null)
	        {
	            if(x<0)
	                x=getQuestThingIndex(questState.mysteryData.toolGroup,name,CMClass.OBJECT_ITEM,num);
	        }
        }
        return x;
    }

    public Environmental getQuestThing(Vector V, int dex, int type, int[] num)
    {
        if(V==null) return null;
        for(int i=0;i<V.size();i++)
        {
            Environmental E=(Environmental)V.elementAt(i);
            if(CMClass.isType(E,type))
            {
                if(dex==num[0]) return E;
                num[0]++;
            }
        }
        return null;
    }


    public MOB getQuestMob(int i)
    {
        int[] num={1};
        Environmental E=null;
        synchronized(questState)
        {
	        if(questState.stuff!=null)
	            E=getQuestThing(questState.stuff.getDimensionVector(1),i,CMClass.OBJECT_MOB,num);
	        if(E instanceof MOB) return (MOB)E;
	        E=getQuestThing(questState.mobGroup,i,CMClass.OBJECT_MOB,num);
	        if(E instanceof MOB) return (MOB)E;
	        if(questState.mysteryData!=null)
	            E=getQuestThing(questState.mysteryData.agentGroup,i,CMClass.OBJECT_MOB,num);
	        if(E instanceof MOB) return (MOB)E;
        }
        return null;
    }

    public Item getQuestItem(int i)
    {
        int[] num={1};
        Environmental E=null;
        synchronized(questState)
        {
	        if(questState.stuff!=null)
	            E=getQuestThing(questState.stuff.getDimensionVector(1),i,CMClass.OBJECT_ITEM,num);
	        if(E instanceof Item) return (Item)E;
	        E=getQuestThing(questState.itemGroup,i,CMClass.OBJECT_ITEM,num);
	        if(E instanceof Item) return (Item)E;
	        if(questState.mysteryData!=null)
	            E=getQuestThing(questState.mysteryData.toolGroup,i,CMClass.OBJECT_ITEM,num);
	        if(E instanceof Item) return (Item)E;
        }
        return null;
    }

    public Room getQuestRoom(int i)
    {
        int[] num={1};
        Environmental E=null;
        synchronized(questState)
        {
	        if(questState.stuff!=null)
	            E=getQuestThing(questState.stuff.getDimensionVector(1),i,CMClass.OBJECT_LOCALE,num);
	        if(E instanceof Room) return (Room)E;
	        E=getQuestThing(questState.roomGroup,i,CMClass.OBJECT_LOCALE,num);
	        if(E instanceof Room) return (Room)E;
	        if(questState.mysteryData!=null)
	            E=getQuestThing(questState.mysteryData.whereAtGroup,i,CMClass.OBJECT_LOCALE,num);
	        if(E instanceof Room) return (Room)E;
	        if(questState.mysteryData!=null)
	            E=getQuestThing(questState.mysteryData.whereHappenedGroup,i,CMClass.OBJECT_LOCALE,num);
	        if(E instanceof Room) return (Room)E;
        }
        return null;
    }

    public String getQuestMobName(int i)
    {
        MOB M=getQuestMob(i);
        if(M!=null) return M.name();
        return "";
    }

    public String getQuestItemName(int i)
    {
        Item I=getQuestItem(i);
        if(I!=null) return I.name();
        return "";
    }

    public String getQuestRoomID(int i)
    {
        Room R=getQuestRoom(i);
        if(R!=null) return CMLib.map().getExtendedRoomID(R);
        return "";
    }

    public int getObjectInUseIndex(String name)
    {
        synchronized(questState)
        {
	        for(int i=0;i<questState.stuff.size();i++)
	        {
	            Environmental E=(Environmental)questState.stuff.elementAt(i,1);
	            if(E.name().equalsIgnoreCase(name))
	                return (i+1);
	        }
        }
        return -1;
    }

    public boolean isObjectInUse(Environmental E)
    {
        return ((questState.stuff!=null)&&(questState.stuff.contains(E)));
    }

    public Vector parseLoadScripts(String text, Vector oldArgs, Vector args)
    {
        if(text.trim().toUpperCase().startsWith("LOAD="))
        {
        	String filename=null;
        	Vector V=CMParms.parse(text.trim().substring(5).trim());
        	if(V.size()>0)
        	{
        		filename=(String)V.firstElement();
        		Vector parms=null;
        		try{
	        		for(int v=1;v<V.size();v++)
	        		{
	        			parms=CMParms.parse((String)V.elementAt(v));
	        			Object O=getObjectIfSpecified(parms,oldArgs,0,1);
        				args.addElement((O==null)?"":O);
	        		}
		            StringBuffer buf=getResourceFileData(filename);
		            if(buf!=null) text=buf.toString();
        		}catch(CMException ex){
        			Log.errOut("DefaultQuest","'"+text+"' either has a space in the filename, or unknown parms.");
        		}
        	}
        }
        int x=text.toLowerCase().indexOf(XMLLibrary.FILE_XML_BOUNDARY.toLowerCase());
        if(x>=0)
        {
            String xml=text.substring(x+XMLLibrary.FILE_XML_BOUNDARY.length()).trim();
            text=text.substring(0,x);
            if((xml.length()>0)&&(internalFiles==null))
            {
                Vector topXMLV=CMLib.xml().parseAllXML(xml);
                for(int t=0;t<topXMLV.size();t++)
                {
                    XMLLibrary.XMLpiece filePiece=(XMLLibrary.XMLpiece)topXMLV.elementAt(t);
                    String name=null;
                    String data=null;
                    if(filePiece.tag.equalsIgnoreCase("FILE")&&(filePiece.contents!=null))
                        for(int p=0;p<filePiece.contents.size();p++)
                        {
                            XMLLibrary.XMLpiece piece=(XMLLibrary.XMLpiece)filePiece.contents.elementAt(p);
                            if(piece.tag.equalsIgnoreCase("NAME")) name=piece.value;
                            if(piece.tag.equalsIgnoreCase("DATA")) data=piece.value;
                        }
                    if((name!=null)&&(data!=null)&&(name.trim().length()>0)&&(data.trim().length()>0))
                    {
                        if(internalFiles==null) internalFiles=new DVector(2);
                        internalFiles.addElement(name.toUpperCase().trim(),new StringBuffer(data));
                    }

                }
            }
        }
        Vector script=new Vector();
        Vector V=script;
        while(text.length()>0)
        {
            int y=-1;
            int yy=0;
            while(yy<text.length())
                if((text.charAt(yy)==';')&&((yy<=0)||(text.charAt(yy-1)!='\\'))) {y=yy;break;}
                else
                if(text.charAt(yy)=='\n'){y=yy;break;}
                else
                if(text.charAt(yy)=='\r'){y=yy;break;}
                else yy++;
            String cmd="";
            if(y<0)
            {
                cmd=text.trim();
                text="";
            }
            else
            {
                cmd=text.substring(0,y).trim();
                text=text.substring(y+1).trim();
            }
            if((cmd.length()>0)&&(!cmd.startsWith("#")))
            {
            	if(cmd.toUpperCase().startsWith("<OPTION>"))
            	{
            		V=new Vector();
            		script.addElement(V);
            	}
            	else
            	if(cmd.toUpperCase().startsWith("</OPTION>"))
            		V=script;
            	else
            		V.addElement(CMStrings.replaceAll(cmd,"\\;",";").trim());
            }
        }
        return script;
    }

    private static final String VALID_ASTR_CODES="_&|";
    private String modifyStringFromArgs(String s, Vector args)
    {
    	int x=s.toUpperCase().indexOf("$");
    	while((x>=0)&&(x<s.length()-1))
    	{
    		int y=x+1;
    		if((y<s.length())&&(VALID_ASTR_CODES.indexOf(s.charAt(y))>=0)) y++;
    		while((y<s.length())&&(Character.isLetterOrDigit(s.charAt(y)))) y++;
    		try{
    			String possObjName=(x+1>=y)?null:s.substring(x+1,y);
    			if((possObjName!=null)&&(possObjName.length()>0))
    			{
    				char firstCode=possObjName.charAt(0);
    	    		if(VALID_ASTR_CODES.indexOf(firstCode)>=0)
    					possObjName=possObjName.substring(1);
	    			Object O=getObjectIfSpecified(CMParms.paramParse(possObjName),args,0,0);
	    			String replace=(O==null)?"null":O.toString();
	    			if(O instanceof Room)
	    				replace=((Room)O).roomTitle(null);
	    			else
	    			if(O instanceof Environmental)
	    				replace=((Environmental)O).Name();
	    			else
	    			if(O instanceof TimeClock)
	    				replace=((TimeClock)O).getShortTimeDescription();
    				switch(firstCode){
    				case '_':
    					replace=CMStrings.capitalizeAndLower(replace);
    					break;
    				case '&':
    					replace=CMLib.english().cleanArticles(replace);
    					break;
    				case '|':
    					replace=CMLib.english().cleanArticles(replace).trim().replace(' ','|');
    					break;
    				}
	    			s=s.substring(0,x)+replace+s.substring(y);
    			}
    		}catch(CMException ex){}
	    	x=s.toUpperCase().indexOf("$",x+1);
    	}
    	return s;
    }

    private Object getObjectIfSpecified(Vector parms, Vector args, int startp, int object0vector1)
    	throws CMException
    {
    	if(parms.size()-startp==0) throw new CMException("Not specified");
    	StringBuffer allParms=new StringBuffer((String)parms.elementAt(startp));
    	for(int p=startp+1;p<parms.size();p++)
    		allParms.append((String)parms.elementAt(p));
    	Object O=null;
    	Vector V=new Vector();
    	int lastI=0;
    	String eval=null;
    	char code=' ';
    	for(int i=0;i<=allParms.length();i++)
    	{
			eval=null;
    		if((i==allParms.length())
    		||((i<allParms.length())&&((allParms.charAt(i)=='+')||(allParms.charAt(i)=='-')||(allParms.charAt(i)=='#'))))
    		{
    			eval=allParms.substring(lastI,i).trim().toUpperCase();
    			lastI=i+1;
    		}
    		if(eval!=null)
    		{
    			if(eval.startsWith("ARG")&&CMath.isMathExpression(eval.substring(3)))
    			{
    				int num=CMath.parseIntExpression(eval.substring(3));
    				if((num<=0)||(num>args.size()))  throw new CMException ("Not specified: "+eval);
    				O=args.elementAt(num-1);
    			}
    			else
    			{
	    			if(!questState.isStat(eval)) throw new CMException ("Not specified: "+eval);
	    			O=questState.getStat(eval);
    			}
    			switch(code)
    			{
    			case '#':
    			{
    				int index=0;
    				if(CMath.isMathExpression(eval))
    					index=CMath.parseIntExpression(eval);
    				if((index>=0)&&(index<V.size()))
    				{
    					O=V.elementAt(index);
    					V.clear();
    					V.addElement(O);
    				}
    				break;
    			}
    			case '-':
    				if(O instanceof Vector)
    					CMParms.delFromVector((Vector)O,V);
    				else
    				if(O!=null)
    					V.removeElement(O);
    				break;
    			case '+': case ' ':
    				if(O instanceof Vector)
    					CMParms.addToVector((Vector)O,V);
    				else
    				if(O!=null)
    					V.addElement(O);
    				break;
    			}
    			if(i<allParms.length()) code=allParms.charAt(i);
    		}
    	}
		switch(object0vector1)
		{
		case 0:
			if(V.size()==0) return null;
			return V.elementAt(CMLib.dice().roll(1,V.size(),-1));
		case 1:
			if(V.size()==0) return null;
			return V;
		}
		return null;
    }

	public static class QuestState implements Cloneable
    {
        public MysteryData mysteryData=new MysteryData();
        public Vector loadedMobs=new Vector();
        public Vector loadedItems=new Vector();
        public Area area=null;
        public Room room=null;
        public MOB mob=null;
        public Vector mobGroup=null;
        public Vector reselectable=new Vector();
        public Vector itemGroup=null;
        public Vector roomGroup=null;
        public Item item=null;
        public Object envObject=null;
        public boolean error=false;
        public boolean done=false;
        public boolean beQuiet=false;
        protected int preserveState=0;
        protected int lastLine=0;
        protected int startLine;
        // key 1=vector, below.  key 2=preserveState
        public DVector stuff=new DVector(2);
        // contains a set of vectors, vectors are formatted as such:
        // key 1=vector, below.  key 2=preserveState
        // 0=environmental item/mob/etc
        //  1=Ability, 2=Ability (for an ability added)
        //  1=Ability, 2=Ability, 3=String (for an ability modified)
        //  1=Effect(for an Effect added)
        //  1=Effect, 2=String (for an Effect modified)
        //  1=Behavior (for an Behavior added)
        //  1=Behavior, 2=String (for an Behavior modified)
        protected DVector addons=new DVector(2);
        public DVector vars=new DVector(2);

        public boolean isStat(String statName)
        {
    		int x=statName.indexOf("#");
    		if(x>=0) statName=statName.substring(0,x);
    		for(int i=0;i<QOBJS.length;i++)
    			if(statName.equalsIgnoreCase(QOBJS[i]))
    				return true;
			if(mysteryData!=null)
				return mysteryData.isStat(statName);
			return false;
        }

    	public Object getStat(String statName)
    	{
    		int x=statName.indexOf("#");
    		String whichStr=null;
    		int whichNum=-1;
    		if(x>=0){
    			whichStr=statName.substring(x+1);
    			if(whichStr.length()>0) whichNum=CMath.s_parseIntExpression(whichStr);
    			statName=statName.substring(0,x);
    		}
    		Object O=null;
    		int code=-1;
    		for(int i=0;i<QOBJS.length;i++)
    			if(statName.equalsIgnoreCase(QOBJS[i]))
    			{ code=i; break;}
    		switch(code)
    		{
    		case 0: O=loadedMobs; break;
    		case 1: O=loadedItems; break;
    		case 2: O=area; break;
    		case 3: O=room; break;
    		case 4: O=mobGroup; break;
    		case 5: O=itemGroup; break;
    		case 6: O=roomGroup; break;
    		case 7: O=item; break;
    		case 8: O=envObject; break;
    		case 9: O=stuff; break;
    		case 10: O=mob; break;
    		default:
    			if(mysteryData!=null)
    				O=mysteryData.getStat(statName);
    			break;
    		}
    		if(O instanceof Vector)
    		{
    			Vector V=(Vector)O;
    			if((whichStr!=null)&&((whichNum<=0)||(whichNum>V.size())))
    				return ""+V.size();
    			if(whichStr!=null)
    				return V.elementAt(whichNum-1);
    		}
    		return O;
    	}
    }

    public static class MysteryData implements Cloneable
    {
    	public Vector factionGroup;
    	public Faction faction;
    	public MOB agent;
    	public Vector agentGroup;
    	public Environmental target;
    	public Vector targetGroup;
    	public Environmental tool;
    	public Vector toolGroup;
    	public Room whereHappened;
    	public Vector whereHappenedGroup;
    	public Room whereAt;
    	public Vector whereAtGroup;
    	public String action;
    	public Vector actionGroup;
    	public String motive;
    	public Vector motiveGroup;
    	public TimeClock whenHappened;
    	public Vector whenHappenedGroup;
    	public TimeClock whenAt;
    	public Vector whenAtGroup;
        public boolean isStat(String statName)
        {
    		for(int i=0;i<MYSTERY_QCODES.length;i++)
    			if(statName.equalsIgnoreCase(MYSTERY_QCODES[i]))
    				return true;
    		return false;
        }
    	public Object getStat(String statName)
    	{
    		int code=-1;
    		for(int i=0;i<MYSTERY_QCODES.length;i++)
    			if(statName.equalsIgnoreCase(MYSTERY_QCODES[i]))
    			{ code=i; break;}
    		switch(code){
	    		case 0: return faction;  case 1: return factionGroup;
	    		case 2: return agent;  case 3: return agentGroup;
	    		case 4: return action;  case 5: return actionGroup;
	    		case 6: return target;  case 7: return targetGroup;
	    		case 8: return motive;  case 9: return motiveGroup;
	    		case 10: return whereHappened;  case 11: return whereHappenedGroup;
	    		case 12: return whereAt;  case 13: return whereAtGroup;
	    		case 14: return whenHappened;  case 15: return whenHappenedGroup;
	    		case 16: return whenAt;  case 17: return whenAtGroup;
	    		case 18: return tool;  case 19: return toolGroup;
    		}
    		return null;
    	}
    }

	public String[] getStatCodes()
	{
		String[] CCODES=new String[QCODES.length+MYSTERY_QCODES.length];
		for(int i=0;i<QCODES.length;i++)
			CCODES[i]=QCODES[i];
		for(int i=0;i<MYSTERY_QCODES.length;i++)
			CCODES[QCODES.length+i]=MYSTERY_QCODES[i];
		return QCODES;
	}

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

	public boolean sameAs(DefaultQuest E)
	{
		String[] CCODES=getStatCodes();
		for(int i=0;i<CCODES.length;i++)
			if(!E.getStat(CCODES[i]).equals(getStat(CCODES[i])))
			   return false;
		return true;
	}

	public void setStat(String code, String val)
	{
		switch(getCodeNum(code)){
		case 0: break;
		case 1: setName(val); break;
		case 2: setDuration(CMLib.time().parseTickExpression(val)); break;
		case 3: setMinWait(CMLib.time().parseTickExpression(val)); break;
		case 4: setMinPlayers(CMath.s_parseIntExpression(val)); break;
		case 5: setPlayerMask(val); break;
		case 6: setRunLevel(CMath.s_parseIntExpression(val)); break;
		case 7: setStartDate(val); break;
		case 8: setStartMudDate(val); break;
		case 9: setWaitInterval(CMLib.time().parseTickExpression(val)); break;
        case 10:
            setSpawn(CMParms.indexOf(SPAWN_DESCS,val.toUpperCase().trim()));
            break;
        case 11: setDisplayName(val); break;
        case 13: durable=CMath.s_bool(val); break;
        case 14: author=val; break;
        case 12: // instructions can and should fall through the default
		default:
            if((code.toUpperCase().trim().equalsIgnoreCase("REMAINING"))&&(running()))
                ticksRemaining=CMLib.time().parseTickExpression(val);
            else
            {
    			int x=questState.vars.indexOf(code.toUpperCase().trim());
    			if(x>=0)
    				questState.vars.setElementAt(x,2,val);
    			else
    				questState.vars.addElement(code.toUpperCase().trim(),val);
            }
			break;
		}
	}

	public boolean isStat(String code)
	{
		if((getCodeNum(code)>=0)
		||(questState.vars.contains(code)))
			return true;
		return false;
	}

	public String getStat(String code)
	{
		switch(getCodeNum(code)){
		case 0: return ""+ID();
		case 1: return ""+name();
		case 2: return ""+duration();
		case 3: return ""+minWait();
		case 4: return ""+minPlayers();
		case 5: return ""+playerMask();
		case 6: return ""+runLevel();
		case 7: return ""+startDate();
		case 8: return ""+startDate();
		case 9: return ""+waitInterval();
        case 10: return SPAWN_DESCS[getSpawn()];
        case 11: return displayName();
        case 13: return Boolean.toString(durable);
        case 14: return author();
        case 12: // instructions can and should fall through the default
		default:
        {
            code=code.toUpperCase().trim();
            if((code.equalsIgnoreCase("REMAINING"))&&(running()))
                return ""+ticksRemaining;
			int x=questState.vars.indexOf(code);
			if(x>=0)
				return (String)questState.vars.elementAt(x,2);
            if(questState.isStat(code))
            {
                Object O=questState.getStat(code);
                if(O instanceof Room) return ((Room)O).roomTitle(null);
                if(O instanceof TimeClock) return ((TimeClock)O).getShortTimeDescription();
                if(O instanceof Environmental) return ((Environmental)O).Name();
                if(O instanceof Vector) return ""+((Vector)O).size();
                if(O!=null) return O.toString();
            }
            else
            if(code.endsWith("_ROOMID")&&(questState.isStat(code.substring(0,code.length()-7))))
            {
                code=code.substring(0,code.length()-7);
                Object O=questState.getStat(code);
                if(O instanceof Vector){
                    if(((Vector)O).size()>0)
                        O=((Vector)O).elementAt(CMLib.dice().roll(1,((Vector)O).size(),-1));
                }
                if(O instanceof Environmental)
                {
                    Room R=CMLib.map().roomLocation((Environmental)O);
                    if(R!=null) return CMLib.map().getExtendedRoomID(R);
                }
            }
            else
            if(code.endsWith("_CLASS")&&(questState.isStat(code.substring(0,code.length()-6))))
            {
                code=code.substring(0,code.length()-6);
                Object O=questState.getStat(code);
                if(O instanceof Vector){
                    if(((Vector)O).size()>0)
                        O=((Vector)O).elementAt(CMLib.dice().roll(1,((Vector)O).size(),-1));
                }
                if(O instanceof CMObject)
                    return ((CMObject)O).ID();
                else
                if(O!=null)
                    return O.getClass().getName();
            }
			return "";
        }
		}
	}

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

    protected static class JScriptQuest extends ScriptableObject
    {
        public String getClassName(){ return "JScriptQuest";}
        static final long serialVersionUID=44;
        Quest quest=null;
        QuestState state=null;
        public Quest quest(){return quest;}
        public QuestState setupState(){return state;}
        public JScriptQuest(Quest Q, QuestState S){quest=Q; state=S;}
        public static String[] functions = { "quest", "setupState", "toJavaString"};
        public String toJavaString(Object O){return Context.toString(O);}
    }
}