@Override
	@SuppressWarnings("unchecked")
	public Quest questMaker(MOB mob)
	{
		if(mob.isMonster())
			return null;
		final DVector questTemplates=getQuestTemplate(mob,null);
		try
		{
			if((questTemplates==null)||(questTemplates.size()==0))
			{
				mob.tell(L("No valid quest templates found in resources/quests/templates!"));
				return null;
			}
			int questIndex=-1;
			while((questIndex<0)&&(mob.session()!=null)&&(!mob.session().isStopped()))
			{
				final String choice=mob.session().prompt(L("Select a quest template (?): "),"");
				if(choice.equals("?"))
				{
					final StringBuffer fullList=new StringBuffer("\n\r^HCANCEL^N -- to cancel.\n\r");
					for(int t=0;t<questTemplates.size();t++)
						fullList.append("^H"+(String)questTemplates.elementAt(t,1)+"^N\n\r"+(String)questTemplates.elementAt(t,2)+"\n\r");
					mob.tell(fullList.toString());
				}
				else
				if((choice.length()==0)||(choice.equalsIgnoreCase("CANCEL")))
					return null;
				else
				{
					final StringBuffer list=new StringBuffer("");
					for(int t=0;t<questTemplates.size();t++)
						if(choice.equalsIgnoreCase((String)questTemplates.elementAt(t,1)))
							questIndex=t;
						else
							list.append(((String)questTemplates.elementAt(t,1))+", ");
					if(questIndex<0)
						mob.tell(L("'@x1' is not a valid quest name, use ? for a list, or select from the following: @x2",choice,list.toString().substring(0,list.length()-2)));
				}
			}
			if((mob.session()==null)||(mob.session().isStopped())||(questIndex<0))
				return null;
			final DVector qFDV=getQuestTemplate(mob,(String)questTemplates.elementAt(questIndex,3));
			if((qFDV==null)||(qFDV.size()==0))
				return null;
			final String questTemplateName=(String)qFDV.elementAt(0,1);
			final Vector<Object> qPages=(Vector<Object>)qFDV.elementAt(0,4);
			mob.tell("^Z"+questTemplateName+"^.^N");
			mob.tell((String)qFDV.elementAt(0,2));
			for(int page=0;page<qPages.size();page++)
			{
				final DVector pageDV=(DVector)qPages.elementAt(page);
				final String pageName=(String)pageDV.elementAt(0,2);
				final String pageInstructions=(String)pageDV.elementAt(0,3);
				mob.tell(L("\n\r\n\r^HPage #@x1: ^N@x2",""+(page+1),pageName));
				mob.tell("^N"+pageInstructions);
				boolean ok=false;
				int showFlag=-999;
				while((mob.session()!=null)&&(!mob.session().isStopped())&&(!ok))
				{
					int showNumber=0;
					String lastLabel=null;
					for(int step=1;step<pageDV.size();step++)
					{
						final Integer stepType=(Integer)pageDV.elementAt(step,1);
						final String keyName=(String)pageDV.elementAt(step,2);
						final String defValue=(String)pageDV.elementAt(step,3);
						String parm1Fixed=CMStrings.capitalizeAndLower(keyName.replace('_',' '));
						if(parm1Fixed.startsWith("$"))
							parm1Fixed=parm1Fixed.substring(1);

						final boolean optionalEntry=CMath.bset(stepType.intValue(),QuestManager.QM_COMMAND_OPTIONAL);
						final int inputCode=stepType.intValue()&QuestManager.QM_COMMAND_MASK;
						final QMCommand inputCommand = QMCommand.values()[inputCode];
						switch(inputCommand)
						{
						case $TITLE: break;
						case $HIDDEN:
							pageDV.setElementAt(step,4,defValue==null?"":defValue);
							break;
						case $LABEL: lastLabel=defValue; break;
						case $EXPRESSION:
						case $TIMEEXPRESSION:
						case $UNIQUE_QUEST_NAME:
						case $STRING:
						case $LONG_STRING:
						case $NAME:
						case $ZAPPERMASK:
						case $ROOMID:
						{
							final String showValue=(showFlag<-900)?defValue:(String)pageDV.elementAt(step,4);
							final GenericEditor.CMEval evaler = getQuestCommandEval(inputCommand);
							if(inputCommand==QMCommand.$ZAPPERMASK)
								lastLabel=(lastLabel==null?"":lastLabel)+"\n\r"+CMLib.masking().maskHelp("\n\r","disallows");
							final String s=CMLib.genEd().prompt(mob,showValue,++showNumber,showFlag,parm1Fixed,optionalEntry,false,lastLabel,
															evaler,null);
							pageDV.setElementAt(step,4,s);
							break;
						}
						case $AREA:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final StringBuffer label=new StringBuffer(((lastLabel==null)?"":lastLabel)+"\n\rChoices: ");
							for(final Enumeration<Area> e=CMLib.map().areas();e.hasMoreElements();)
								label.append("\""+e.nextElement().name()+"\" ");
							final GenericEditor.CMEval evaler = getQuestCommandEval(inputCommand);
							final String s=CMLib.genEd().prompt(mob,showValue,++showNumber,showFlag,parm1Fixed,optionalEntry,false,label.toString(),
															evaler,
															null);
							pageDV.setElementAt(step,4,s);
							break;
						}
						case $EXISTING_QUEST_NAME:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final StringBuffer label=new StringBuffer(((lastLabel==null)?"":lastLabel)+"\n\rChoices: ");
							for(int q=0;q<CMLib.quests().numQuests();q++)
								label.append("\""+CMLib.quests().fetchQuest(q).name()+"\" ");
							final GenericEditor.CMEval evaler = getQuestCommandEval(inputCommand);
							final String s=CMLib.genEd().prompt(mob,showValue,++showNumber,showFlag,parm1Fixed,optionalEntry,false,label.toString(),
															evaler, null);
							pageDV.setElementAt(step,4,s);
							break;
						}
						case $ABILITY:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final StringBuffer label=new StringBuffer(((lastLabel==null)?"":lastLabel)+"\n\rChoices: ");
							for(final Enumeration<Ability> e=CMClass.abilities();e.hasMoreElements();)
								label.append(e.nextElement().ID()+" ");
							final GenericEditor.CMEval evaler = getQuestCommandEval(inputCommand);
							final String s=CMLib.genEd().prompt(mob,showValue,++showNumber,showFlag,parm1Fixed,optionalEntry,false,label.toString(),
															evaler, null);
							pageDV.setElementAt(step,4,s);
							break;
						}
						case $CHOOSE:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final String label=((lastLabel==null)?"":lastLabel)+"\n\rChoices: "+defValue;
							final GenericEditor.CMEval evaler = getQuestCommandEval(inputCommand);
							final String s=CMLib.genEd().prompt(mob,showValue,++showNumber,showFlag,parm1Fixed,optionalEntry,false,label,
															evaler,
															CMParms.toStringArray(CMParms.parseCommas(defValue.toUpperCase(),true)));
							pageDV.setElementAt(step,4,s);
							break;
						}
						case $ITEMXML:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final String newValue=addXMLQuestItem(mob, showFlag, pageDV, showValue, parm1Fixed, lastLabel, optionalEntry, step, ++showNumber);
							if(newValue!=null)
								pageDV.setElementAt(step,4,newValue);
							break;
						}
						case $ITEMXML_ONEORMORE:
						{
							String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final Vector<String> itemXMLs=new Vector<String>();
							int x=showValue.indexOf("</ITEM><ITEM>");
							while(x>=0)
							{
								final String xml=showValue.substring(0,x+7).trim();
								if(xml.length()>0)
									itemXMLs.addElement(xml);
								showValue=showValue.substring(x+7);
								x=showValue.indexOf("</ITEM><ITEM>");
							}
							if(showValue.trim().length()>0)
								itemXMLs.addElement(showValue.trim());
							String newValue=null;
							for(int i=0;i<=itemXMLs.size();i++)
							{
								showValue=(i<itemXMLs.size())?(String)itemXMLs.elementAt(i):"";
								final boolean optional=(i==0)?optionalEntry:true;
								final String thisValue=addXMLQuestItem(mob, showFlag, pageDV, showValue, parm1Fixed, lastLabel, optional, step, ++showNumber);
								if(thisValue!=null)
								{
									if(newValue==null)
										newValue="";
									newValue+=thisValue;
									if((thisValue.length()>0)
									&&(i==itemXMLs.size()))
										itemXMLs.addElement(thisValue);
								}
							}
							if(newValue!=null)
								pageDV.setElementAt(step,4,newValue);
							break;
						}
						case $MOBXML:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final String newValue=addXMLQuestMob(mob, showFlag, pageDV, showValue, parm1Fixed, lastLabel, optionalEntry, step, ++showNumber);
							if(newValue!=null)
								pageDV.setElementAt(step,4,newValue);
							break;
						}
						case $MOBXML_ONEORMORE:
						{
							String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final Vector<String> mobXMLs=new Vector<String>();
							int x=showValue.indexOf("</MOB><MOB>");
							while(x>=0)
							{
								final String xml=showValue.substring(0,x+6).trim();
								if(xml.length()>0)
									mobXMLs.addElement(xml);
								showValue=showValue.substring(x+6);
								x=showValue.indexOf("</MOB><MOB>");
							}
							if(showValue.trim().length()>0)
								mobXMLs.addElement(showValue.trim());

							String newValue=null;
							for(int i=0;i<=mobXMLs.size();i++)
							{
								showValue=(i<mobXMLs.size())?(String)mobXMLs.elementAt(i):"";
								final boolean optional=(i==0)?optionalEntry:true;
								final String thisValue=addXMLQuestMob(mob, showFlag, pageDV, showValue, parm1Fixed, lastLabel, optional, step, ++showNumber);
								if(thisValue!=null)
								{
									if(newValue==null)
										newValue="";
									newValue+=thisValue;
									if((thisValue.length()>0)
									&&(i==mobXMLs.size()))
										mobXMLs.addElement(thisValue);
								}
							}
							if(newValue!=null)
								pageDV.setElementAt(step,4,newValue);
							break;
						}
						case $FACTION:
						{
							final String showValue=(showFlag<-900)?"":(String)pageDV.elementAt(step,4);
							final StringBuffer label=new StringBuffer(((lastLabel==null)?"":lastLabel)+"\n\rChoices: ");
							for(final Enumeration<Faction> f=CMLib.factions().factions();f.hasMoreElements();)
								label.append("\""+f.nextElement().name()+"\" ");
							final GenericEditor.CMEval evaler = getQuestCommandEval(inputCommand);
							final String s=CMLib.genEd().prompt(mob,showValue,++showNumber,showFlag,parm1Fixed,optionalEntry,false,label.toString(),
															evaler, null);
							pageDV.setElementAt(step,4,s);
							break;
						}
						}
					}
					if(showFlag<-900)
					{
						ok=false;
						showFlag=0;
						mob.tell(L("\n\r^HNow verify this page's selections:^.^N"));
						continue;
					}
					if(showFlag>0)
					{
						showFlag=-1;
						continue;
					}
					final String what=mob.session().prompt(L("Edit which (enter 0 to cancel)? "),"");
					if(what.trim().equals("0"))
					{
						if(mob.session().confirm(L("Are you sure you want to abort (y/N)? "),"N"))
						{
							mob.tell(L("Aborted."));
							return null;
						}
					}
					showFlag=CMath.s_int(what);
					if(showFlag<=0)
					{
						showFlag=-1;
						ok=true;
						// all done
					}
				}
				if((mob.session()==null)||(mob.session().isStopped()))
					return null;
			}
			String name="";
			String script=((StringBuffer)qFDV.elementAt(0,5)).toString();
			String var=null;
			String val=null;
			for(int page=0;page<qPages.size();page++)
			{
				final DVector pageDV=(DVector)qPages.elementAt(page);
				for(int v=0;v<pageDV.size();v++)
				{
					var=(String)pageDV.elementAt(v,2);
					val=(String)pageDV.elementAt(v,4);
					if(((Integer)pageDV.elementAt(v,1)).intValue()==QMCommand.$UNIQUE_QUEST_NAME.ordinal())
						name=val;
					script=CMStrings.replaceAll(script,var,val);
				}
			}
			script=CMStrings.replaceAll(script,"$#AUTHOR",mob.Name());
			if((mob.session()!=null)&&(!mob.session().isStopped())
			&&(mob.session().confirm(L("Create the new quest: @x1 (y/N)? ",name),"N")))
			{
				final Quest Q=(Quest)CMClass.getCommon("DefaultQuest");
				final CMFile newQF=new CMFile(Resources.makeFileResourceName("quests/"+name+".quest"),mob,CMFile.FLAG_LOGERRORS);
				newQF.saveText(script);
				Q.setScript("LOAD=quests/"+name+".quest",true);
				if((Q.name().trim().length()==0)||(Q.duration()<0))
				{
					mob.tell(L("You must specify a VALID quest string.  This one contained errors.  Try AHELP QUESTS."));
					return null;
				}
				CMLib.quests().addQuest(Q);
				CMLib.quests().save();
				return Q;
			}
			return null;
		}
		catch(final java.io.IOException e)
		{
			return null;
		}
	}