protected void genDynamicAbilities(MOB mob, Modifiable E, String typeName, String levelName, int showNumber, int showFlag)
		throws IOException
	{
		if((showFlag>0)&&(showFlag!=showNumber))
			return;
		while((mob.session()!=null)&&(!mob.session().isStopped())&&(true))
		{
			final StringBuffer parts=new StringBuffer("");
			final int numResources=CMath.s_int(E.getStat("NUMRABLE"));
			final Vector<Ability> ables=new Vector<Ability>();
			final Vector<String> data=new Vector<String>();
			for(int v=0;v<numResources;v++)
			{
				final Ability A=CMClass.getAbility(E.getStat("GETRABLE"+v));
				if(A!=null)
				{
					parts.append("("+A.ID()+"/"+E.getStat("GETRABLELVL"+v)+"/"+E.getStat("GETRABLEQUAL"+v)+"/"+E.getStat("GETRABLEPROF"+v)+"/"+E.getStat("GETRABLEPARM"+v));
					if(CMParms.contains(E.getStatCodes(),"GETRABLEROLE") && (E instanceof ClanGovernment))
					{
						final List<String> roleIdsList=CMParms.parseCommas(E.getStat("GETRABLEROLE"+v), true);
						final List<String> roleNamesList=new ArrayList<String>(roleIdsList.size());
						for(String roleId : roleIdsList)
						{
							ClanPosition P=((ClanGovernment)E).findPositionRole(roleId);
							if(P!=null)
								roleNamesList.add(P.getID());
						}
						parts.append("/"+CMParms.toListString(roleNamesList)+"), ");
					}
					else
						parts.append("), ");
					ables.addElement(A);
					data.addElement(A.ID()+";"+E.getStat("GETRABLELVL"+v)+";"+E.getStat("GETRABLEQUAL"+v)+";"+E.getStat("GETRABLEPROF"+v)+";"+E.getStat("GETRABLEPARM"+v)+";"+E.getStat("GETRABLEROLE"+v));
				}
			}
			if(parts.toString().endsWith(", "))
			{
				parts.deleteCharAt(parts.length()-1);
				parts.deleteCharAt(parts.length()-1);
			}
			mob.tell(L("@x1. @x2 Abilities: @x3.",""+showNumber,typeName,parts.toString()));
			if((showFlag!=showNumber)&&(showFlag>-999))
				return;
			final String newName=mob.session().prompt(L("Enter an ability name to add or remove (?)\n\r:"),"");
			if(newName.equalsIgnoreCase("?"))
				mob.tell(CMLib.lister().reallyList(mob,CMClass.abilities(),-1).toString());
			else
			if(newName.length()>0)
			{
				int partNum=-1;
				for(int i=0;i<ables.size();i++)
				{
					if(CMLib.english().containsString(ables.elementAt(i).ID(),newName))
					{ 
						partNum=i; 
						break;
					}
				}
				boolean updateList=false;
				if(partNum<0)
				{
					final Ability A=CMClass.getAbility(newName);
					if(A==null)
						mob.tell(L("That is neither an existing ability name, nor a valid one to add.  Use ? for a list."));
					else
					if(A.isAutoInvoked())
						mob.tell(L("'@x1' cannot be named, as it is autoinvoked.",A.name()));
					else
					if((A.triggerStrings()==null)||(A.triggerStrings().length==0))
						mob.tell(L("'@x1' cannot be named, as it has no trigger/command words.",A.name()));
					else
					{
						final StringBuffer str=new StringBuffer(A.ID()+";");
						final String level=mob.session().prompt(L("Enter the level of this skill (1): "),"1");
						str.append((""+CMath.s_int(level))+";");
						if(mob.session().confirm(L("Is this skill automatically gained (Y/n)?"),"Y"))
							str.append("false;");
						else
							str.append("true;");
						final String prof=mob.session().prompt(L("Enter the (perm) proficiency level (100): "),"100");
						str.append((""+CMath.s_int(prof))+";");
						final String parm=mob.session().prompt(L("Enter any default parameters: "),"");
						str.append(""+parm+";");
						String roles="";
						if((CMParms.contains(E.getStatCodes(),"GETRABLEROLE"))&&(E instanceof ClanGovernment))
						{
							boolean repeat=true;
							while(repeat && (mob.session()!=null)&&(!mob.session().isStopped()))
							{
								final String s=mob.session().prompt(L("Enter one or more roles to restrict this to (?): "),"");
								if(s.trim().equalsIgnoreCase("?"))
									mob.tell(L("Roles: ")+CMParms.toCMObjectListString(((ClanGovernment)E).getPositions()));
								else
								if(s.trim().length()==0)
									break;
								else
								{
									StringBuilder finalListBuilder=new StringBuilder("");
									List<String> newRoleList=CMParms.parseCommas(s,true);
									for(String nr : newRoleList)
									{
										ClanPosition P=((ClanGovernment)E).findPositionRole(nr);
										if(P==null)
										{
											mob.tell(L("'@x1' is not a valid position. The list is comma-delimited.  Try ?"));
											finalListBuilder.setLength(0);
											break;
										}
										else
											finalListBuilder.append(", ").append(P.getRoleID());
									}
									if(finalListBuilder.length()>2)
									{
										roles=finalListBuilder.substring(2);
										break;
									}
								}
							}
						}
						str.append(roles+";");
						data.addElement(str.toString());
						ables.addElement(A);
						mob.tell(L("@x1 added.",A.name()));
						updateList=true;
					}
				}
				else
				{
					final Ability A=ables.elementAt(partNum);
					ables.removeElementAt(partNum);
					data.removeElementAt(partNum);
					updateList=true;
					mob.tell(L("@x1 removed.",A.name()));
				}
				if(updateList)
				{
					if(data.size()>0)
						E.setStat("NUMRABLE",""+data.size());
					else
						E.setStat("NUMRABLE","");
					for(int i=0;i<data.size();i++)
					{
						final List<String> V=CMParms.parseSemicolons(data.elementAt(i),false);
						E.setStat("GETRABLE"+i,(V.get(0)));
						E.setStat("GETRABLELVL"+i,(V.get(1)));
						E.setStat("GETRABLEQUAL"+i,(V.get(2)));
						E.setStat("GETRABLEPROF"+i,(V.get(3)));
						E.setStat("GETRABLEPARM"+i,(V.get(4)));
						if(CMParms.contains(E.getStatCodes(),"GETRABLEROLE")&&(V.size()>5))
							E.setStat("GETRABLEROLE"+i,(V.get(5)));
					}
				}
			}
			else
			{
				mob.tell(L("(no change)"));
				return;
			}
		}
	}