/
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/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
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/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/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/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
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/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Libraries;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.AbilityMapper.AbilityMapping;
import com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag;
import com.planet_ink.coffee_mud.core.threads.ServiceEngine;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMSecurity.DbgFlag;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.DefaultClan;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.Clan.AutoPromoteFlag;
import com.planet_ink.coffee_mud.Common.interfaces.Clan.FullMemberRecord;
import com.planet_ink.coffee_mud.Common.interfaces.Clan.Function;
import com.planet_ink.coffee_mud.Common.interfaces.Clan.Authority;
import com.planet_ink.coffee_mud.Common.interfaces.Clan.MemberRecord;
import com.planet_ink.coffee_mud.Common.interfaces.Clan.Trophy;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;
/**
 * Portions Copyright (c) 2003 Jeremy Vyska
 * Portions Copyright (c) 2004-2016 Bo Zimmerman
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   	 http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class Clans extends StdLibrary implements ClanManager
{
	protected SHashtable<String,Clan>	all					= new SHashtable<String,Clan>();
	protected List<Pair<Clan,Integer>>	all2				= new Vector<Pair<Clan,Integer>>();
	protected long	 		  		  	lastGovernmentLoad  = 0;
	protected Map<String,Clan>  	  	webPathClanMappings = new SHashtable<String,Clan>();
	protected Map<String,String>		clanWebPathMappings = new SHashtable<String,String>();

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

	public boolean isCommonClanRelations(Clan C1, Clan C2, int relation)
	{
		if((C1==null)||(C2==null))
			return relation==Clan.REL_NEUTRAL;
		int i1=C1.getClanRelations(C2.clanID());
		int i2=C2.getClanRelations(C1.clanID());
		if((i1==i2)
		&&((i1==Clan.REL_WAR)
		   ||(i1==Clan.REL_ALLY)))
		   return i1==relation;
		for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
		{
			final Clan C=e.nextElement();
			if((C!=C1)&&(C!=C2))
			{
				if((i1!=Clan.REL_WAR)
				&&(C1.getClanRelations(C.clanID())==Clan.REL_ALLY)
				&&(C.getClanRelations(C2.clanID())==Clan.REL_WAR))
					i1=Clan.REL_WAR;
				if((i2!=Clan.REL_WAR)
				&&(C2.getClanRelations(C.clanID())==Clan.REL_ALLY)
				&&(C.getClanRelations(C1.clanID())==Clan.REL_WAR))
					i2=Clan.REL_WAR;
			}
		}
		if(i1==i2)
			return relation==i1;

		if(Clan.REL_NEUTRALITYGAUGE[i1]<Clan.REL_NEUTRALITYGAUGE[i2])
			return relation==i1;
		return relation==i2;
	}

	@Override
	public boolean isCommonClanRelations(String clanID1, String clanID2, int relation)
	{
		if((clanID1==null)||(clanID2==null)||(clanID1.length()==0)||(clanID2.length()==0))
			return Clan.REL_NEUTRAL==relation;
		return isCommonClanRelations(getClan(clanID1),getClan(clanID1), relation);
	}

	@Override
	public boolean isAtClanWar(MOB M1, MOB M2)
	{
		final List<Pair<Clan,Clan>> pairs=findUncommonRivalrousClans(M1, M2);
		for(final Pair<Clan,Clan> p : pairs)
			if(isCommonClanRelations(p.first,p.second, Clan.REL_WAR))
				return true;
		return false;
	}

	@Override
	public boolean checkClanPrivilege(MOB mob, Clan.Function func)
	{
		return findPrivilegedClan(mob,func)!=null;
	}

	@Override
	public boolean checkClanPrivilege(MOB mob, String clanID, Clan.Function func)
	{
		if(mob==null)
			return false;
		final Pair<Clan,Integer> c=mob.getClanRole(clanID);
		if(c==null)
			return false;
		if(c.first.getAuthority(c.second.intValue(), func) != Clan.Authority.CAN_NOT_DO)
			return true;
		return false;
	}

	@Override
	public Pair<Clan,Integer> findPrivilegedClan(MOB mob, Clan.Function func)
	{
		for(final Pair<Clan,Integer> c : mob.clans())
		{
			if(c.first.getAuthority(c.second.intValue(), func) != Clan.Authority.CAN_NOT_DO)
				return c;
		}
		return null;
	}

	@Override
	public List<Pair<Clan,Integer>> findPrivilegedClans(MOB mob, Clan.Function func)
	{
		final List<Pair<Clan,Integer>> set=new Vector<Pair<Clan,Integer>>();
		for(final Pair<Clan,Integer> c : mob.clans())
		{
			if(c.first.getAuthority(c.second.intValue(), func) != Clan.Authority.CAN_NOT_DO)
				set.add(c);
		}
		return set;
	}

	@Override
	public Clan findRivalrousClan(MOB mob)
	{
		for(final Pair<Clan,Integer> c : mob.clans())
		{
			if(c.first.isRivalrous())
			{
				return c.first;
			}
		}
		return null;
	}

	@Override
	public Clan findConquerableClan(MOB mob)
	{
		for(final Pair<Clan,Integer> c : mob.clans())
		{
			if(c.first.getGovernment().isConquestEnabled())
			{
				return c.first;
			}
		}
		return null;
	}

	@Override
	public List<Triad<Clan,Integer,Integer>> findCommonRivalrousClans(MOB mob1, MOB mob2)
	{
		final List<Triad<Clan,Integer,Integer>> list=new XVector<Triad<Clan,Integer,Integer>>(1,true);
		if((mob1==null)||(mob2==null))
			return list;
		for(final Pair<Clan,Integer> c : mob1.clans())
		{
			if(c.first.isRivalrous())
			{
				final Pair<Clan,Integer> c2=mob2.getClanRole(c.first.clanID());
				if(c2!=null)
					list.add(new Triad<Clan,Integer,Integer>(c.first,c.second,c2.second));
			}
		}
		return list;
	}

	@Override
	public List<Pair<Clan,Integer>> findRivalrousClans(MOB mob)
	{
		final List<Pair<Clan,Integer>> list=new XVector<Pair<Clan,Integer>>(1,true);
		for(final Pair<Clan,Integer> c : mob.clans())
			if(c.first.isRivalrous())
				list.add(c);
		return list;
	}

	@Override
	public List<Pair<Clan,Integer>> findRivalrousClans(MOB clanSourceMob, MOB filterMob)
	{
		final List<Pair<Clan,Integer>> list=new XVector<Pair<Clan,Integer>>(1,true);
		if((clanSourceMob==null)||(filterMob==null))
			return list;
		for(final Pair<Clan,Integer> c : clanSourceMob.clans())
		{
			if(c.first.isRivalrous())
			{
				final Pair<Clan,Integer> c2=filterMob.getClanRole(c.first.clanID());
				if(c2==null)
					list.add(c);
			}
		}
		return list;
	}

	public List<Pair<Clan,Clan>> findUncommonRivalrousClans(MOB M1, MOB M2)
	{
		final List<Pair<Clan,Clan>> list=new XVector<Pair<Clan,Clan>>(1,true);
		if((M1==null)||(M2==null))
			return list;
		// i need the disunion here (what's the word for that?), as a order-irrelevant set of pairs
		for(final Pair<Clan,Integer> c : M1.clans())
		{
			if(c.first.isRivalrous())
			{
				final Pair<Clan,Integer> c2=M2.getClanRole(c.first.clanID());
				if(c2==null)
					list.add(new Pair<Clan,Clan>(c.first,null));
			}
		}
		final List<Pair<Clan,Clan>> finalList=new XVector<Pair<Clan,Clan>>(1,true);
		for(final Pair<Clan,Clan> p : list)
			for(final Pair<Clan,Integer> c : M2.clans())
				if(c.first.isRivalrous())
					finalList.add(new Pair<Clan,Clan>(p.first,c.first));
		return finalList;
	}

	public List<Pair<Clan,Clan>> getAllClanPairs(MOB M1, MOB M2)
	{
		final List<Pair<Clan,Clan>> list=new XVector<Pair<Clan,Clan>>(1,true);
		if((M1==null)||(M2==null))
			return list;
		// i need the disunion here (what's the word for that?), as a order-irrelevant set of pairs
		for(final Pair<Clan,Integer> c : M1.clans())
			list.add(new Pair<Clan,Clan>(c.first,null));
		final List<Pair<Clan,Clan>> finalList=new XVector<Pair<Clan,Clan>>(1,true);
		for(final Pair<Clan,Clan> p : list)
			for(final Pair<Clan,Integer> c : M2.clans())
				if(p.first!=c.first)
					finalList.add(new Pair<Clan,Clan>(p.first,c.first));
		return finalList;
	}

	public int getClanRelations(Clan C1, Clan C2)
	{
		if((C1==null)||(C2==null))
			return Clan.REL_NEUTRAL;
		final int i1=C1.getClanRelations(C2.clanID());
		final int i2=C2.getClanRelations(C1.clanID());
		final int rel=Clan.RELATIONSHIP_VECTOR[i1][i2];
		if(rel==Clan.REL_WAR)
			return Clan.REL_WAR;
		if(rel==Clan.REL_ALLY)
			return Clan.REL_ALLY;
		for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
		{
			final Clan C=e.nextElement();
			if((C!=C1)
			&&(C!=C2)
			&&(((C1.getClanRelations(C.clanID())==Clan.REL_ALLY)&&(C.getClanRelations(C2.clanID())==Clan.REL_WAR)))
				||((C2.getClanRelations(C.clanID())==Clan.REL_ALLY)&&(C.getClanRelations(C1.clanID())==Clan.REL_WAR)))
					return Clan.REL_WAR;
		}
		return rel;
	}

	@Override
	public int getClanRelations(String clanID1, String clanID2)
	{
		if((clanID1==null)||(clanID2==null)||(clanID1.length()==0)||(clanID2.length()==0))
			return Clan.REL_NEUTRAL;
		return getClanRelations(getClan(clanID1),getClan(clanID2));
	}

	@Override
	public boolean findAnyClanRelations(MOB M1, MOB M2, int relation)
	{
		for(final Pair<Clan,Clan> c : getAllClanPairs(M1, M2))
			if(getClanRelations(c.first, c.second)==relation)
				return true;
		return false;
	}

	@Override
	public boolean isAnyCommonClan(MOB M1, MOB M2)
	{
		if((M1==null)||(M2==null))
			return false;
		for(final Pair<Clan,Integer> p : M1.clans())
			if(M2.getClanRole(p.first.clanID())!=null)
				return true;
		return false;
	}

	@Override
	public Clan getClan(String id)
	{
		if((id==null)||(id.length()==0))
			return null;
		Clan C=all.get(id.toUpperCase());
		if(C!=null)
			return C;
		for(final Enumeration<Clan> e=all.elements();e.hasMoreElements();)
		{
			C=e.nextElement();
			if(CMLib.english().containsString(CMStrings.removeColors(C.name()),id))
				return C;
		}
		return null;
	}

	@Override
	public Clan findClan(String id)
	{
		Clan C=getClan(id);
		if(C!=null)
			return C;
		for(final Enumeration<Clan> e=all.elements();e.hasMoreElements();)
		{
			C=e.nextElement();
			if(CMLib.english().containsString(CMStrings.removeColors(C.name()),id))
				return C;
		}
		return null;
	}

	@Override
	public boolean isFamilyOfMembership(MOB M, List<MemberRecord> members)
	{
		if(M == null)
			return false;
		if(members.contains(M.Name()))
			return true;
		if((M.getLiegeID().length()>0)
		&&(M.isMarriedToLiege())
		&&(members.contains(M.getLiegeID())))
			return true;
		for(final Enumeration<Tattoo> e=M.tattoos();e.hasMoreElements();)
		{
			final Tattoo T=e.nextElement();
			if(T.getTattooName().startsWith("PARENT:"))
			{
				final String name=T.getTattooName().substring("PARENT:".length());
				final MOB M2=CMLib.players().getLoadPlayer(name.toLowerCase());
				if((M2 != null)&&isFamilyOfMembership(M2,members))
					return true;
			}
		}
		return false;
	}

	@Override
	public Enumeration<Clan> clans()
	{
		return all.elements();
	}

	@Override
	public int numClans()
	{
		return all.size();
	}

	@Override
	public void addClan(Clan C)
	{
		synchronized(all)
		{
			if(!CMSecurity.isDisabled(CMSecurity.DisFlag.CLANTICKS))
				CMLib.threads().startTickDown(C,Tickable.TICKID_CLAN,(int)CMProps.getTicksPerDay());
			all.put(C.clanID().toUpperCase(),C);
			all2.add(new Pair<Clan,Integer>(C,Integer.valueOf(C.getGovernment().getAcceptPos())));
			setClanWebSiteMappings(C,CMProps.getVar(CMProps.Str.CLANWEBSITES));
			CMLib.journals().registerClanForum(C,CMProps.getVar(CMProps.Str.CLANFORUMDATA));
			CMLib.map().sendGlobalMessage(CMLib.map().deity(), CMMsg.TYP_CLANEVENT,
				CMClass.getMsg(CMLib.map().deity(), CMMsg.MSG_CLANEVENT, "+"+C.name()));
		}
	}

	@Override
	public void removeClan(Clan C)
	{
		synchronized(all)
		{
			CMLib.threads().deleteTick(C,Tickable.TICKID_CLAN);
			all.remove(C.clanID().toUpperCase());
			for(final Pair<Clan,Integer> p : all2)
				if(p.first==C)
				{
					all2.remove(p);
					break;
				}
			setClanWebSiteMappings(C,null); // will delete mapping
			CMLib.journals().registerClanForum(C,null); // will remove mapping
			CMLib.map().sendGlobalMessage(CMLib.map().deity(), CMMsg.TYP_CLANEVENT,
					CMClass.getMsg(CMLib.map().deity(), CMMsg.MSG_CLANEVENT, "-"+C.name()));
		}
	}

	@Override
	public void tickAllClans()
	{
		for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
		{
			final Clan C=e.nextElement();
			C.tick(C,Tickable.TICKID_CLAN);
		}
	}

	@Override
	public void clanAnnounceAll(String msg)
	{
		final List<String> channels=CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.CLANINFO);
		for(int i=0;i<channels.size();i++)
			CMLib.commands().postChannel(channels.get(i),clanRoles(),msg,true);
	}

	@Override public Enumeration<String> clansNames(){return all.keys();}

	@Override public Iterable<Pair<Clan,Integer>> clanRoles(){return all2;}

	@Override
	public String translatePrize(Trophy trophy)
	{
		String prizeStr="";
		switch(trophy)
		{
			case Areas: prizeStr=CMProps.getVar(CMProps.Str.CLANTROPAREA); break;
			case Points: prizeStr=CMProps.getVar(CMProps.Str.CLANTROPCP); break;
			case Experience: prizeStr=CMProps.getVar(CMProps.Str.CLANTROPEXP); break;
			case PlayerKills: prizeStr=CMProps.getVar(CMProps.Str.CLANTROPPK); break;
			case Members: prizeStr=CMProps.getVar(CMProps.Str.CLANTROPMB); break;
			case MemberLevel: prizeStr=CMProps.getVar(CMProps.Str.CLANTROPLVL); break;
		}
		if(prizeStr.length()==0)
			return "None";
		if(prizeStr.length()>0)
		{
			final Vector<String> V=CMParms.parse(prizeStr);
			if(V.size()>=2)
			{
				final String type=V.lastElement().toUpperCase();
				final String amt=V.firstElement();
				if("EXPERIENCE".startsWith(type))
					return amt+" experience point bonus.";
			}
		}
		return prizeStr;
	}

	@Override
	public boolean trophySystemActive()
	{
		return (CMProps.getVar(CMProps.Str.CLANTROPAREA).length()>0)
			|| (CMProps.getVar(CMProps.Str.CLANTROPCP).length()>0)
			|| (CMProps.getVar(CMProps.Str.CLANTROPEXP).length()>0)
			|| (CMProps.getVar(CMProps.Str.CLANTROPMB).length()>0)
			|| (CMProps.getVar(CMProps.Str.CLANTROPLVL).length()>0)
			|| (CMProps.getVar(CMProps.Str.CLANTROPPK).length()>0);

	}

	@Override
	public boolean goForward(MOB mob, Clan C, List<String> commands, Clan.Function function, boolean voteIfNecessary)
	{
		if((mob==null)||(C==null))
			return false;
		final Pair<Clan,Integer> clanRole=mob.getClanRole(C.clanID());
		if(clanRole==null)
			return false;
		final int role=clanRole.second.intValue();
		final Clan.Authority allowed=C.getAuthority(role,function);
		if(allowed==Clan.Authority.CAN_DO)
			return true;
		if(allowed==Clan.Authority.CAN_NOT_DO)
			return false;
		if(function==Clan.Function.ASSIGN)
		{
			if(C.getAuthority(role,Clan.Function.VOTE_ASSIGN)!=Clan.Authority.CAN_DO)
				return false;
		}
		else
		if(C.getAuthority(role,Clan.Function.VOTE_OTHER)!=Clan.Authority.CAN_DO)
			return false;
		if(!voteIfNecessary)
			return true;
		final String matter=CMParms.combine(commands,0);
		for(final Enumeration<Clan.ClanVote> e=C.votes();e.hasMoreElements();)
		{
			final Clan.ClanVote CV=e.nextElement();
			if((CV.voteStarter.equalsIgnoreCase(mob.Name()))
			&&(CV.voteStatus==Clan.VSTAT_STARTED))
			{
				mob.tell(L("This matter must be voted upon, but you already have a vote underway."));
				return false;
			}
			if(CV.matter.equalsIgnoreCase(matter))
			{
				mob.tell(L("This matter must be voted upon, and is already BEING voted upon.  Use CLANVOTE to see."));
				return false;
			}
		}
		if(mob.session()==null)
			return false;
		try
		{
			final int numVotes=C.getNumVoters(function);
			if(numVotes==1)
				return true;

			if(mob.session().confirm(L("This matter must be voted upon.  Would you like to start the vote now (y/N)?"),"N"))
			{
				final Clan.ClanVote CV=new Clan.ClanVote();
				CV.matter=matter;
				CV.voteStarter=mob.Name();
				CV.function=function.ordinal();
				CV.voteStarted=System.currentTimeMillis();
				CV.votes=new PairVector<String,Boolean>();
				CV.voteStatus=Clan.VSTAT_STARTED;
				C.addVote(CV);
				C.updateVotes();
				final Clan.Function voteFunctionType = (function == Clan.Function.ASSIGN) ? Clan.Function.VOTE_ASSIGN : Clan.Function.VOTE_OTHER;
				final List<Integer> votingRoles = new Vector<Integer>();
				for(int i=0;i<C.getRolesList().length;i++)
					if(C.getAuthority(i, voteFunctionType)==Clan.Authority.CAN_DO)
						votingRoles.add(Integer.valueOf(i));
				if(votingRoles.size()>0)
				{
					final String firstRoleName = C.getRoleName(votingRoles.iterator().next().intValue(), true, true);
					final String rest = " "+firstRoleName+" should use CLANVOTE to participate.";
					if(votingRoles.size() >= (C.getRolesList().length-2))
					{
						if(function == Clan.Function.ASSIGN)
							clanAnnounce(mob,"The "+C.getGovernmentName()+" "+C.clanID()+" has a new election to vote upon. "+rest);
						else
							clanAnnounce(mob,"The "+C.getGovernmentName()+" "+C.clanID()+" has a new matter to vote upon. "+rest);
					}
					else
					if(votingRoles.size()==1)
						clanAnnounce(mob,"The "+C.getGovernmentName()+" "+C.clanID()+" has a new matter to vote upon. "+rest);
					else
					{
						final String[] roleNames = new String[votingRoles.size()];
						for(int i=0;i<votingRoles.size();i++)
						{
							final Integer roleID=votingRoles.get(i);
							roleNames[i]=C.getRoleName(roleID.intValue(), true, true);
						}
						final String list = CMLib.english().toEnglishStringList(roleNames);
						clanAnnounce(mob,"The "+C.getGovernmentName()+" "+C.clanID()+" has a new matter to vote upon. "
								+list+" should use CLANVOTE to participate.");
					}
				}
				mob.tell(L("Your vote has started.  Use CLANVOTE to cast your vote."));
				return false;
			}
		}
		catch(final java.io.IOException e){}
		mob.tell(L("Without a vote, this command can not be executed."));
		return false;
	}

	protected String indt(int x)
	{
		return CMStrings.SPACES.substring(0,x*4);
	}

	@Override
	public long getLastGovernmentLoad()
	{
		return lastGovernmentLoad;
	}

	@Override
	public String getGovernmentHelp(MOB mob, String named, boolean exact)
	{
		ClanGovernment helpG=null;
		for(final ClanGovernment G : getStockGovernments())
			if(G.getName().equalsIgnoreCase(named))
				helpG=G;
		if((helpG==null)&&(exact))
			return null;
		if(helpG==null)
			for(final ClanGovernment G : getStockGovernments())
				if(G.getName().toUpperCase().startsWith(named.toUpperCase()))
					helpG=G;
		if(helpG==null)
		{
			final List<ClanGovernment> gtypes=new Vector<ClanGovernment>();
			String name=null;
			for(final ClanGovernment G : getStockGovernments())
				for(final ClanPosition P : G.getPositions())
					if(P.getName().equalsIgnoreCase(named)||P.getPluralName().equalsIgnoreCase(named))
					{
						gtypes.add(G);
						name=P.getName();
					}
			if(gtypes.size()==0)
			{
				if(exact)
					return null;
				for(final ClanGovernment G : getStockGovernments())
					for(final ClanPosition P : G.getPositions())
						if(P.getName().toUpperCase().startsWith(named.toUpperCase())
							||P.getPluralName().toUpperCase().startsWith(named.toUpperCase()))
						{
							gtypes.add(G);
							name=P.getName();
						}
			}
			if(gtypes.size()==0)
				return null;
			final String[] typeNames=new String[gtypes.size()];
			for(int g=0;g<gtypes.size();g++)
				typeNames[g]=CMStrings.capitalizeAndLower(gtypes.get(g).getName());
			return "The "+name+" is a rank or position within the following clan types: "
				   +CMLib.english().toEnglishStringList(typeNames)
				   +".  Please see help on CLAN or on one of the listed clan types for more information. ";
		}
		return helpG.getHelpStr();
	}

	@Override
	public ClanGovernment createSampleGovernment()
	{
		final Authority[] pows1=new Authority[Function.values().length];
		for(int i=0;i<pows1.length;i++) pows1[i]=Authority.CAN_NOT_DO;
		final Authority[] pows2=new Authority[Function.values().length];
		for(int i=0;i<pows2.length;i++) pows2[i]=Authority.CAN_DO;
		final ClanPosition P1=(ClanPosition)CMClass.getCommon("DefaultClanPosition");
		P1.setID("APPLICANT");
		P1.setRoleID(0);
		P1.setRank(0);
		P1.setName(L("Applicant"));
		P1.setPluralName("Applicants");
		P1.setMax(Integer.MAX_VALUE);
		P1.setInnerMaskStr("");
		P1.setFunctionChart(pows1);
		P1.setPublic(false);
		final ClanPosition P2=(ClanPosition)CMClass.getCommon("DefaultClanPosition");
		P2.setID("MEMBER");
		P2.setRoleID(1);
		P2.setRank(1);
		P2.setName(L("Member"));
		P2.setPluralName("Members");
		P2.setMax(Integer.MAX_VALUE);
		P2.setInnerMaskStr("");
		P2.setFunctionChart(pows2);
		P2.setPublic(false);
		final Set<Integer> usedTypeIDs=new HashSet<Integer>();
		final ClanGovernment[] gvts=(ClanGovernment[])Resources.getResource("parsed_clangovernments");
		int id=0;
		if(gvts!=null)
		{
			for(final ClanGovernment G2 : gvts)
				usedTypeIDs.add(Integer.valueOf(G2.getID()));
			for(int i=0;i<gvts.length;i++)
				if(!usedTypeIDs.contains(Integer.valueOf(i)))
				{
					id=i; break;
				}
		}

		final ClanGovernment G=(ClanGovernment)CMClass.getCommon("DefaultClanGovernment");
		G.setID(id);
		G.setName(L("Sample Govt"));
		G.setCategory("");
		G.setPositions(new ClanPosition[]{P1,P2});
		G.setAutoRole(0);
		G.setAcceptPos(1);
		G.setRequiredMaskStr("");
		G.setEntryScript("");
		G.setExitScript("");
		G.setAutoPromoteBy(AutoPromoteFlag.NONE);
		G.setPublic(true);
		G.setFamilyOnly(false);
		G.setOverrideMinMembers(Integer.valueOf(1));
		G.setConquestEnabled(true);
		G.setConquestItemLoyalty(true);
		G.setConquestByWorship(false);
		G.setShortDesc("Change Me!");
		G.setLongDesc("");
		G.setMaxVoteDays(10);
		G.setRivalrous(true);
		G.setVoteQuorumPct(66);
		G.setDefault(true);
		return G;
	}

	@Override
	public void reSaveGovernmentsXML()
	{
		final ClanGovernment[] govt = getStockGovernments();
		final String xml = makeGovernmentXML(govt);
		if(!Resources.updateFileResource("clangovernments.xml", xml))
		{
			Log.errOut("Clans","Can't save clangovernments.xml");
		}
		Resources.removeResource("parsed_clangovernments");
		getStockGovernments();
	}

	@Override
	public ClanGovernment createGovernment(String name)
	{
		final ClanGovernment[] gvts=getStockGovernments();
		final List<ClanGovernment> govts = new SVector<ClanGovernment>(gvts);
		for(final ClanGovernment G : gvts)
			if(G.getName().equalsIgnoreCase(name))
				return null;
		final ClanGovernment newG=createSampleGovernment();
		final Set<Integer> takenIDs=new HashSet<Integer>();
		for(final ClanGovernment g : gvts)
			takenIDs.add(Integer.valueOf(g.getID()));
		int newID=CMLib.dice().roll(1, Integer.MAX_VALUE, 0);
		for(int i=0;i<gvts.length+1;i++)
			if(!takenIDs.contains(Integer.valueOf(i)))
				newID=i;
		newG.setID(newID);
		newG.setName(name);
		govts.add(newG);
		Resources.submitResource("parsed_clangovernments", govts.toArray(new ClanGovernment[0]));
		return newG;
	}

	@Override
	public boolean removeGovernment(ClanGovernment government)
	{
		final ClanGovernment[] gvts=getStockGovernments();
		if(gvts.length==1)
			return false;
		final List<ClanGovernment> govts = new SVector<ClanGovernment>(gvts);
		govts.remove(government);
		if(govts.size()==gvts.length)
			return false;
		Resources.submitResource("parsed_clangovernments", govts.toArray(new ClanGovernment[0]));
		return true;
	}

	@Override
	public ClanGovernment[] getStockGovernments()
	{
		ClanGovernment[] gvts=(ClanGovernment[])Resources.getResource("parsed_clangovernments");
		if(gvts==null)
		{
			synchronized(this)
			{
				gvts=(ClanGovernment[])Resources.getResource("parsed_clangovernments");
				if(gvts==null)
				{
					final StringBuffer str=Resources.getFileResource("clangovernments.xml", true);
					if(str==null)
						gvts=new ClanGovernment[0];
					else
					{
						gvts=parseGovernmentXML(str);
					}
					if((gvts==null)||(gvts.length==0))
					{
						final ClanGovernment gvt=createSampleGovernment();
						gvt.setDefault(true);
						gvts=new ClanGovernment[]{gvt};
					}
					lastGovernmentLoad=System.currentTimeMillis();
					Resources.submitResource("parsed_clangovernments",gvts);
				}
			}
		}
		return gvts;
	}

	@Override
	public ClanGovernment getDefaultGovernment()
	{
		final ClanGovernment[] gvts=getStockGovernments();
		for(final ClanGovernment gvt : gvts)
		{
			if(gvt.isDefault())
				return gvt;
		}
		return gvts[0];
	}

	@Override
	public ClanGovernment getStockGovernment(int typeid)
	{
		final ClanGovernment[] gvts=getStockGovernments();
		if(gvts.length <= typeid)
		{
			Log.errOut("Clans","Someone mistakenly requested stock government typeid "+typeid);
			return gvts[0];
		}
		return gvts[typeid];
	}

	@Override
	public String makeGovernmentXML(ClanGovernment gvt)
	{
		final StringBuilder str=new StringBuilder("");
		str.append("<CLANTYPE ").append("TYPEID="+gvt.getID()+" ").append("NAME=\""+gvt.getName()+"\" ").append("CATEGORY=\""+gvt.getCategory()+"\"").append(">\n");
		if(gvt.isDefault())
			str.append(indt(1)).append("<ISDEFAULT>true</ISDEFAULT>\n");
		str.append(indt(1)).append("<SHORTDESC>").append(CMLib.xml().parseOutAngleBrackets(gvt.getShortDesc())).append("</SHORTDESC>\n");
		str.append(indt(1)).append("<LONGDESC>").append(CMLib.xml().parseOutAngleBrackets(gvt.getLongDesc())).append("</LONGDESC>\n");
		str.append(indt(1)).append("<POSITIONS>\n");
		final Set<Clan.Function> voteSet = new HashSet<Clan.Function>();
		for(int p=gvt.getPositions().length-1;p>=0;p--)
		{
			final ClanPosition pos = gvt.getPositions()[p];
			str.append(indt(2)).append("<POSITION ").append("ID=\""+pos.getID()+"\" ").append("ROLEID="+pos.getRoleID()+" ")
								.append("RANK="+pos.getRank()+" ").append("NAME=\""+pos.getName()+"\" ").append("PLURAL=\""+pos.getPluralName()+"\" ")
								.append("MAX="+pos.getMax()+" ").append("INNERMASK=\""+CMLib.xml().parseOutAngleBrackets(pos.getInnerMaskStr())+"\" ")
								.append("PUBLIC=\""+pos.isPublic()+"\">\n");
			for(final Clan.Function func : Clan.Function.values())
				if(pos.getFunctionChart()[func.ordinal()]==Clan.Authority.CAN_DO)
					str.append(indt(3)).append("<POWER>").append(func.toString()).append("</POWER>\n");
				else
				if(pos.getFunctionChart()[func.ordinal()]==Clan.Authority.MUST_VOTE_ON)
					voteSet.add(func);
			str.append(indt(2)).append("</POSITION>\n");
		}
		str.append(indt(1)).append("</POSITIONS>\n");
		if(voteSet.size()==0)
			str.append(indt(1)).append("<VOTING />\n");
		else
		{
			str.append(indt(1)).append("<VOTING ").append("MAXDAYS="+gvt.getMaxVoteDays()+" QUORUMPCT="+gvt.getVoteQuorumPct()+">\n");
			for(final Clan.Function func : voteSet)
				str.append(indt(2)).append("<POWER>").append(func.toString()).append("</POWER>\n");
			str.append(indt(1)).append("</VOTING>\n");
		}
		str.append(indt(1)).append("<AUTOPOSITION>").append(gvt.getPositions()[gvt.getAutoRole()].getID()).append("</AUTOPOSITION>\n");
		str.append(indt(1)).append("<ACCEPTPOSITION>").append(gvt.getPositions()[gvt.getAcceptPos()].getID()).append("</ACCEPTPOSITION>\n");
		str.append(indt(1)).append("<REQUIREDMASK>").append(CMLib.xml().parseOutAngleBrackets(gvt.getRequiredMaskStr())).append("</REQUIREDMASK>\n");
		str.append(indt(1)).append("<ENTRYSCRIPT>").append(CMLib.xml().parseOutAngleBrackets(gvt.getEntryScript())).append("</ENTRYSCRIPT>\n");
		str.append(indt(1)).append("<EXITSCRIPT>").append(CMLib.xml().parseOutAngleBrackets(gvt.getExitScript())).append("</EXITSCRIPT>\n");
		str.append(indt(1)).append("<AUTOPROMOTEBY>").append(gvt.getAutoPromoteBy().toString()).append("</AUTOPROMOTEBY>\n");
		str.append(indt(1)).append("<PUBLIC>").append(gvt.isPublic()).append("</PUBLIC>\n");
		str.append(indt(1)).append("<FAMILYONLY>").append(gvt.isFamilyOnly()).append("</FAMILYONLY>\n");
		str.append(indt(1)).append("<RIVALROUS>").append(gvt.isRivalrous()).append("</RIVALROUS>\n");
		str.append(indt(1)).append("<XPPERLEVELFORMULA>").append(gvt.getXpCalculationFormulaStr()).append("</XPPERLEVELFORMULA>\n");
		if(gvt.getOverrideMinMembers() == null)
			str.append(indt(1)).append("<OVERRIDEMINMEMBERS />\n");
		else
			str.append(indt(1)).append("<OVERRIDEMINMEMBERS>").append(gvt.getOverrideMinMembers().toString()).append("</OVERRIDEMINMEMBERS>\n");
		str.append(indt(1)).append("<CONQUEST>\n");
		{
			str.append(indt(2)).append("<ENABLED>").append(gvt.isConquestEnabled()).append("</ENABLED>\n");
			str.append(indt(2)).append("<ITEMLOYALTY>").append(gvt.isConquestItemLoyalty()).append("</ITEMLOYALTY>\n");
			str.append(indt(2)).append("<DEITYBASIS>").append(gvt.isConquestByWorship()).append("</DEITYBASIS>\n");
		}
		str.append(indt(1)).append("</CONQUEST>\n");
		gvt.getClanLevelAbilities(null,null,Integer.valueOf(Integer.MAX_VALUE));
		final Enumeration<AbilityMapping> m= CMLib.ableMapper().getClassAbles(gvt.getName(), false);
		if(!m.hasMoreElements())
			str.append(indt(1)).append("<ABILITIES />\n");
		else
		{
			str.append(indt(1)).append("<ABILITIES>\n");
			for(;m.hasMoreElements();)
			{
				final AbilityMapping map=m.nextElement();
				final String addExt;
				if(map.extFields().size()>0)
				{
					final List<String> posNames=new ArrayList<String>();
					for(String I : map.extFields().keySet())
					{
						final ClanPosition P=gvt.findPositionRole(I);
						if(P!=null)
							posNames.add(P.getID());
					}
					addExt="ROLES=\""+CMParms.toListString(posNames)+"\" ";
				}
				else
					addExt="";
				str.append(indt(2)).append("<ABILITY ID=\""+map.abilityID()+"\" PROFF="+map.defaultProficiency()+" LEVEL="+map.qualLevel()+" QUALIFYONLY="+(!map.autoGain())+" "+addExt+"/>\n");
			}
			str.append(indt(1)).append("</ABILITIES>\n");
		}
		final int numEffects=CMath.s_int(gvt.getStat("NUMREFF"));
		if(numEffects==0)
			str.append(indt(1)).append("<EFFECTS />\n");
		else
		{
			str.append(indt(1)).append("<EFFECTS>\n");
			for(int a=0;a<numEffects;a++)
			{
				final String ableID=gvt.getStat("GETREFF"+a);
				final String ableParm=gvt.getStat("GETREFFPARM"+a);
				final int lvl = CMath.s_int(gvt.getStat("GETREFFLVL"+a));
				final String roleList=gvt.getStat("GETREFFROLE"+a);
				final String addExt;
				if(roleList.trim().length()>0)
				{
					final List<String> posNames=new ArrayList<String>();
					for(String I : CMParms.parseCommas(roleList,true))
					{
						final ClanPosition P=gvt.findPositionRole(I);
						if(P!=null)
							posNames.add(P.getID());
					}
					addExt="ROLES=\""+CMParms.toListString(posNames)+"\" ";
				}
				else
					addExt="";
				str.append(indt(2)).append("<EFFECT ID=\""+ableID+"\" LEVEL="+lvl+" PARMS=\""+CMLib.xml().parseOutAngleBrackets(ableParm)+"\" "+addExt+"/>\n");
			}
			str.append(indt(1)).append("</EFFECTS>\n");
		}

		str.append("</CLANTYPE>\n");
		return str.toString();
	}

	@Override
	public String makeGovernmentXML(ClanGovernment gvts[])
	{
		final StringBuilder str=new StringBuilder("");
		str.append("<CLANTYPES>\n");
		for(final ClanGovernment gvt : gvts)
			str.append(makeGovernmentXML(gvt));
		str.append("</CLANTYPES>\n");
		return str.toString();
	}

	@Override
	public List<Pair<Clan,Integer>> getClansByCategory(MOB M, String category)
	{
		final List<Pair<Clan,Integer>> list=new Vector<Pair<Clan,Integer>>(1);
		if(M==null)
			return list;
		if(category==null)
			category="";
		for(final Pair<Clan,Integer> p : M.clans())
			if(p.first.getCategory().equalsIgnoreCase(category))
				list.add(p);
		return list;
	}

	@Override
	public ClanGovernment[] parseGovernmentXML(StringBuffer xml)
	{
		final List<XMLLibrary.XMLTag> xmlV = CMLib.xml().parseAllXML(xml);
		final XMLTag clanTypesTag = CMLib.xml().getPieceFromPieces(xmlV, "CLANTYPES");
		List<XMLLibrary.XMLTag> clanTypes = null;
		if(clanTypesTag != null)
			clanTypes = clanTypesTag.contents();
		else
		{
			final XMLLibrary.XMLTag clanType = CMLib.xml().getPieceFromPieces(xmlV, "CLANTYPE");
			if(clanType != null)
			{
				clanTypes = new SVector<XMLLibrary.XMLTag>();
				clanTypes.add(clanType);
			}
			else
			{
				Log.errOut("Clans","No CLANTYPES found in xml");
				return null;
			}
		}

		final List<ClanGovernment> governments=new SVector<ClanGovernment>();
		for(final XMLTag clanTypePieceTag : clanTypes)
		{
			final String typeName=clanTypePieceTag.parms().get("NAME");
			final int typeID=CMath.s_int(clanTypePieceTag.parms().get("TYPEID"));
			final boolean isDefault=CMath.s_bool(clanTypePieceTag.parms().get("ISDEFAULT"));
			String category=clanTypePieceTag.parms().get("CATEGORY");
			if(category==null)
				category="";

			final Authority[]	baseFunctionChart = new Authority[Function.values().length];
			for(int i=0;i<Function.values().length;i++)
				baseFunctionChart[i]=Authority.CAN_NOT_DO;
			final XMLTag votingTag = clanTypePieceTag.getPieceFromPieces( "VOTING");
			final int maxVotingDays = CMath.s_int(votingTag.parms().get("MAXDAYS"));
			final int minVotingPct = CMath.s_int(votingTag.parms().get("QUORUMPCT"));
			for(final XMLTag piece : votingTag.contents())
			{
				if(piece.tag().equalsIgnoreCase("POWER"))
				{
					final Function power = (Function)CMath.s_valueOf(Clan.Function.values(),piece.value());
					if(power == null)
						Log.errOut("Clans","Illegal power found in xml: "+piece.value());
					else
						baseFunctionChart[power.ordinal()] = Authority.MUST_VOTE_ON;
				}
			}

			final List<ClanPosition> positions=new SVector<ClanPosition>();
			final XMLTag positionsTag = clanTypePieceTag.getPieceFromPieces( "POSITIONS");
			for(final XMLTag posPiece : positionsTag.contents())
			{
				if(posPiece.tag().equalsIgnoreCase("POSITION"))
				{
					final Authority[]	functionChart = baseFunctionChart.clone();
					final String ID=posPiece.parms().get("ID");
					final int roleID=CMath.s_int(posPiece.parms().get("ROLEID"));
					final int rank=CMath.s_int(posPiece.parms().get("RANK"));
					final String name=posPiece.parms().get("NAME");
					final String pluralName=posPiece.parms().get("PLURAL");
					final int max=CMath.s_int(posPiece.parms().get("MAX"));
					final boolean isPublic=CMath.s_bool(posPiece.parms().get("PUBLIC"));
					final String innerMaskStr=CMLib.xml().restoreAngleBrackets(posPiece.parms().get("INNERMASK"));
					for(final XMLTag powerPiece : posPiece.contents())
					{
						if(powerPiece.tag().equalsIgnoreCase("POWER"))
						{
							final Function power = (Function)CMath.s_valueOf(Clan.Function.values(),powerPiece.value());
							if(power == null)
								Log.errOut("Clans","Illegal power found in xml: "+powerPiece.value());
							else
								functionChart[power.ordinal()] = Authority.CAN_DO;
						}
					}
					final ClanPosition P=(ClanPosition)CMClass.getCommon("DefaultClanPosition");
					P.setID(ID);
					P.setRoleID(roleID);
					P.setRank(rank);
					P.setName(name);
					P.setPluralName(pluralName);
					P.setMax(max);
					P.setInnerMaskStr(innerMaskStr);
					P.setFunctionChart(functionChart);
					P.setPublic(isPublic);
					positions.add(P);
				}
			}
			ClanPosition[] posArray = new ClanPosition[positions.size()];
			for(final ClanPosition pos : positions)
				if((pos.getRoleID()>=0)&&(pos.getRoleID()<positions.size()))
					if(posArray[pos.getRoleID()]!=null)
					{
						Log.errOut("Clans","Bad ROLEID "+pos.getRoleID()+" in positions list in "+typeName);
						posArray=new ClanPosition[0];
						break;
					}
					else
					{
						posArray[pos.getRoleID()]=pos;
					}
			if(posArray.length==0)
			{
				Log.errOut("Clans","Missing positions in "+typeName);
				continue;
			}
			final String	autoRoleStr=clanTypePieceTag.getValFromPieces( "AUTOPOSITION");
			ClanPosition autoRole=null;
			for(final ClanPosition pos : positions)
				if(pos.getID().equalsIgnoreCase(autoRoleStr) )
					autoRole=pos;
			if(autoRole==null)
			{
				Log.errOut("Clans","Illegal role found in xml: "+autoRoleStr);
				continue;
			}
			final String	acceptRoleStr=clanTypePieceTag.getValFromPieces( "ACCEPTPOSITION");
			ClanPosition acceptRole=null;
			for(final ClanPosition pos : positions)
				if(pos.getID().equalsIgnoreCase(acceptRoleStr) )
					acceptRole=pos;
			if(acceptRole==null)
			{
				Log.errOut("Clans","Illegal acceptRole found in xml: "+acceptRoleStr);
				continue;
			}
			final String requiredMaskStr=CMLib.xml().restoreAngleBrackets(clanTypePieceTag.getValFromPieces( "REQUIREDMASK"));
			final String entryScript=CMLib.xml().restoreAngleBrackets(clanTypePieceTag.getValFromPieces( "ENTRYSCRIPT"));
			final String exitScript=CMLib.xml().restoreAngleBrackets(clanTypePieceTag.getValFromPieces( "EXITSCRIPT"));
			final String shortDesc=CMLib.xml().restoreAngleBrackets(clanTypePieceTag.getValFromPieces( "SHORTDESC"));
			final String longDesc=CMLib.xml().restoreAngleBrackets(clanTypePieceTag.getValFromPieces( "LONGDESC"));
			final String autoPromoteStr=clanTypePieceTag.getValFromPieces( "AUTOPROMOTEBY");
			final Clan.AutoPromoteFlag autoPromote = AutoPromoteFlag.valueOf(autoPromoteStr);
			if(autoPromote==null)
			{
				Log.errOut("Clans","Illegal AUTOPROMOTEBY found in xml: "+autoPromoteStr);
				continue;
			}
			final boolean isPublic=CMath.s_bool(clanTypePieceTag.getValFromPieces( "PUBLIC"));
			final boolean isFamilyOnly=CMath.s_bool(clanTypePieceTag.getValFromPieces( "FAMILYONLY"));
			final String	overrideMinMembersStr=clanTypePieceTag.getValFromPieces( "OVERRIDEMINMEMBERS");
			Integer overrideMinMembers = null;
			if((overrideMinMembersStr!=null)&&CMath.isInteger(overrideMinMembersStr))
				overrideMinMembers=Integer.valueOf(CMath.s_int(overrideMinMembersStr));
			boolean isRivalrous=true;
			final String rivalrousStr=clanTypePieceTag.getValFromPieces( "RIVALROUS");
			if(CMath.isBool(rivalrousStr))
				isRivalrous=CMath.s_bool(rivalrousStr);
			final String xpPerLevelFormulaStr=clanTypePieceTag.getValFromPieces( "XPPERLEVELFORMULA");
			final XMLTag conquestTag = clanTypePieceTag.getPieceFromPieces( "CONQUEST");
			boolean conquestEnabled=true;
			boolean conquestItemLoyalty=true;
			boolean conquestDeityBasis=false;
			if(conquestTag!=null)
			{
				conquestEnabled=CMath.s_bool(conquestTag.getValFromPieces( "ENABLED"));
				conquestItemLoyalty=CMath.s_bool(conquestTag.getValFromPieces( "ITEMLOYALTY"));
				conquestDeityBasis=CMath.s_bool(conquestTag.getValFromPieces( "DEITYBASIS"));
			}
			final ClanGovernment G=(ClanGovernment)CMClass.getCommon("DefaultClanGovernment");
			G.setID(typeID);
			G.setName(typeName);
			G.setCategory(category);
			G.setPositions(posArray);
			G.setAutoRole(autoRole.getRoleID());
			G.setAcceptPos(acceptRole.getRoleID());
			G.setRequiredMaskStr(requiredMaskStr);
			G.setEntryScript(entryScript);
			G.setExitScript(exitScript);
			G.setAutoPromoteBy(autoPromote);
			G.setPublic(isPublic);
			G.setFamilyOnly(isFamilyOnly);
			G.setOverrideMinMembers(overrideMinMembers);
			G.setConquestEnabled(conquestEnabled);
			G.setConquestItemLoyalty(conquestItemLoyalty);
			G.setConquestByWorship(conquestDeityBasis);
			G.setShortDesc(shortDesc);
			G.setLongDesc(longDesc);
			G.setXpCalculationFormulaStr(xpPerLevelFormulaStr);
			G.setMaxVoteDays(maxVotingDays);
			G.setRivalrous(isRivalrous);
			G.setVoteQuorumPct(minVotingPct);
			G.setDefault(isDefault);

			final XMLTag abilitiesTag = clanTypePieceTag.getPieceFromPieces( "ABILITIES");
			if((abilitiesTag!=null)&&(abilitiesTag.contents()!=null)&&(abilitiesTag.contents().size()>0))
			{
				G.setStat("NUMRABLE", Integer.toString(abilitiesTag.contents().size()));
				for(int x=0;x<abilitiesTag.contents().size();x++)
				{
					final XMLTag able = abilitiesTag.contents().get(x);
					G.setStat("GETRABLE"+x, able.parms().get("ID"));
					G.setStat("GETRABLEPROF"+x, able.parms().get("PROFF"));
					G.setStat("GETRABLEQUAL"+x, able.parms().get("QUALIFYONLY"));
					G.setStat("GETRABLELVL"+x, able.parms().get("LEVEL"));
					G.setStat("GETRABLEROLE"+x, able.parms().get("ROLES"));
				}
			}
			final XMLTag effectsTag = clanTypePieceTag.getPieceFromPieces( "EFFECTS");
			if((effectsTag!=null)&&(effectsTag.contents()!=null)&&(effectsTag.contents().size()>0))
			{
				G.setStat("NUMREFF", Integer.toString(effectsTag.contents().size()));
				for(int x=0;x<effectsTag.contents().size();x++)
				{
					final XMLTag able = effectsTag.contents().get(x);
					G.setStat("GETREFF"+x, able.parms().get("ID"));
					G.setStat("GETREFFPARM"+x, CMLib.xml().restoreAngleBrackets(able.parms().get("PARMS")));
					G.setStat("GETREFFLVL"+x, able.parms().get("LEVEL"));
					G.setStat("GETREFFROLE"+x, able.parms().get("ROLES"));
				}
			}
			governments.add(G);
		}
		final ClanGovernment[] govts=new ClanGovernment[governments.size()];
		for(final ClanGovernment govt : governments)
			if((govt.getID() < 0)||(govt.getID() >=governments.size()) || (govts[govt.getID()]!=null))
			{
				Log.errOut("Clans","Bad TYPEID "+govt.getID());
				return new ClanGovernment[0];
			}
			else
				govts[govt.getID()]=govt;

		if(governments.size()>0)
		{
			for(int i=0;i<govts.length;i++)
				if(govts[i]==null)
					govts[i]=governments.get(0);
			return govts;
		}
		else
		{
			return null;
		}
	}

	@Override
	public Clan getWebPathClanMapping(String webPath)
	{
		if(webPath==null)
			return null;
		return this.webPathClanMappings.get(webPath.toLowerCase().trim());
	}

	@Override
	public String getClanWebTemplateDir(String webPath)
	{
		if(webPath==null)
			return null;
		return this.clanWebPathMappings.get(webPath.toLowerCase().trim());
	}

	private void setClanWebSiteMappings(Clan clan, String allMappingsStr)
	{
		for(final Iterator<String> keyIter = this.webPathClanMappings.keySet().iterator();keyIter.hasNext();)
		{
			final String key=keyIter.next();
			final Clan foundClan = this.webPathClanMappings.get(key);
			if(foundClan == clan)
			{
				keyIter.remove();
				clanWebPathMappings.remove(key);
			}
		}
		if(allMappingsStr==null)
			return;
		final List<String> set=CMParms.parseCommas(allMappingsStr,true);
		for(String s : set)
		{
			final String originalS=s;
			s=s.trim();
			if(s.startsWith("["))
			{
				int x=s.indexOf(']');
				final String cat=s.substring(1,x).trim();
				if(clan.getGovernment().getCategory().equalsIgnoreCase(cat))
				{
					s=s.substring(x+1).trim();
					x=s.lastIndexOf(' ');
					if(x>0)
					{
						String siteFilesPathStr=CMStrings.replaceAll(s.substring(0,x),"<CLANNAME>",clan.clanID());
						siteFilesPathStr=CMStrings.replaceAll(siteFilesPathStr,"<CLANTYPE>",clan.getGovernmentName());
						final String siteFilesPath = new CMFile(siteFilesPathStr.trim(),null).getAbsolutePath();
						final String siteTemplatePath = new CMFile(s.substring(x+1).trim(),null).getAbsolutePath();
						if(webPathClanMappings.containsKey(siteFilesPath.toLowerCase()))
						{
							Log.errOut("Clans","Multiple clans at same webclansites path: in coffeemud.ini: "+originalS);
						}
						else
						{
							webPathClanMappings.put(siteFilesPath.toLowerCase(), clan);
							clanWebPathMappings.put(siteFilesPath.toLowerCase(), siteTemplatePath);
						}
						return;
					}
					else
					{
						Log.errOut("Clans","Unparseable webclansites bit in coffeemud.ini: "+originalS);
					}
				}
			}
		}
	}

	@Override
	public Clan getWebPathClan(String sitePath)
	{
		return webPathClanMappings.get(sitePath.toLowerCase());
	}


	@Override
	public void clanAnnounce(MOB mob, String msg)
	{
		final List<String> channels=CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.CLANINFO);
		for(int i=0;i<channels.size();i++)
		{
			CMLib.commands().postChannel(mob,channels.get(i),msg,true);
		}
	}

	protected int filterMedianLevel(List<FullMemberRecord> members)
	{
		final List<Integer> lvls=new SortedListWrap<Integer>(new XVector<Integer>());
		for(final FullMemberRecord r : members)
		{
			if(!r.isAdmin)
			{
				lvls.add(Integer.valueOf(r.level));
			}
		}
		if(lvls.size()>0)
		{
			return lvls.get(lvls.size()/2).intValue();
		}
		return 0;
	}

	protected Clan getTrophyWinner(Trophy trophy)
	{
		for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
		{
			final Clan C=e.nextElement();
			if(CMath.bset(C.getTrophies(),trophy.flagNum()))
			{
				return C;
			}
		}
		return null;
	}

	public void clanTrophyScan()
	{
		if(trophySystemActive())
		{
			// calculate winner of the members count contest
			if(CMProps.getVar(CMProps.Str.CLANTROPMB).length()>0)
			{
				Clan winnerC=getTrophyWinner(Trophy.Members);
				int winnerMembers=(winnerC==null)?0:winnerC.getSize();
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement(); if(C==winnerC) continue;
					final int numMembers=C.getSize();
					if(numMembers>winnerMembers)
					{
						winnerC=C;
						winnerMembers=numMembers;
					}
				}
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS))
					Log.debugOut("Clans","MBTrophy: "+((winnerC==null)?"Noone":winnerC.clanID())+" won with "+winnerMembers);
				if((winnerC!=null)&&(!CMath.bset(winnerC.getTrophies(),Trophy.Members.flagNum()))&&(winnerC.getExp()>0))
				{
					winnerC.setTrophies(winnerC.getTrophies()|Trophy.Members.flagNum());
					clanAnnounceAll("The "+winnerC.getGovernmentName()+" "+winnerC.name()+" has been awarded the trophy for "+Trophy.Members.description+".");
				}
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((winnerC!=C)&&(CMath.bset(C.getTrophies(),Trophy.Members.flagNum())))
					{
						C.setTrophies(C.getTrophies()-Trophy.Members.flagNum());
						C.clanAnnounce(L("The @x1 @x2 has lost control of the trophy for @x3.",C.getGovernmentName(),C.name(),Trophy.Members.description));
					}
				}
			}

			// calculate winner of the member level contest
			if(CMProps.getVar(CMProps.Str.CLANTROPLVL).length()>0)
			{
				Clan winnerC=getTrophyWinner(Trophy.MemberLevel);
				int winnerLevel=(winnerC==null)?0:filterMedianLevel(winnerC.getFullMemberList());
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement(); if(C==winnerC) continue;
					final int highestLevel=filterMedianLevel(C.getFullMemberList());
					if(highestLevel>winnerLevel)
					{
						winnerC=C;
						winnerLevel=highestLevel;
					}
				}
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS))
					Log.debugOut("DefaultClan","LVLTrophy: "+((winnerC==null)?"Noone":winnerC.clanID())+" won with "+winnerLevel);
				if((winnerC!=null)&&(!CMath.bset(winnerC.getTrophies(),Trophy.MemberLevel.flagNum()))&&(winnerC.getExp()>0))
				{
					winnerC.setTrophies(winnerC.getTrophies()|Trophy.MemberLevel.flagNum());
					clanAnnounceAll("The "+winnerC.getGovernmentName()+" "+winnerC.name()+" has been awarded the trophy for "+Trophy.MemberLevel.description+".");
				}
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((winnerC!=C)&&(CMath.bset(C.getTrophies(),Trophy.MemberLevel.flagNum())))
					{
						C.setTrophies(C.getTrophies()-Trophy.MemberLevel.flagNum());
						C.clanAnnounce(L("The @x1 @x2 has lost control of the trophy for @x3.",C.getGovernmentName(),C.name(),Trophy.MemberLevel.description));
					}
				}
			}

			// calculate winner of the exp contest
			if(CMProps.getVar(CMProps.Str.CLANTROPEXP).length()>0)
			{
				Clan winnerC=getTrophyWinner(Trophy.Experience);
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement(); 
					if(C==winnerC) 
						continue;
					if((winnerC==null)||(C.getExp()>winnerC.getExp()))
						winnerC=C;
				}
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS))
					Log.debugOut("DefaultClan","EXPTrophy: "+((winnerC==null)?"Noone":winnerC.clanID())+" won with "+((winnerC==null)?"0":""+winnerC.getExp()));
				if((winnerC!=null)&&(!CMath.bset(winnerC.getTrophies(),Trophy.Experience.flagNum()))&&(winnerC.getExp()>0))
				{
					winnerC.setTrophies(winnerC.getTrophies()|Trophy.Experience.flagNum());
					clanAnnounceAll("The "+winnerC.getGovernmentName()+" "+winnerC.name()+" has been awarded the trophy for "+Trophy.Experience.description+".");
				}
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((winnerC!=C)&&(CMath.bset(C.getTrophies(),Trophy.Experience.flagNum())))
					{
						C.setTrophies(C.getTrophies()-Trophy.Experience.flagNum());
						C.clanAnnounce(L("The @x1 @x2 has lost control of the trophy for @x3.",C.getGovernmentName(),C.name(),Trophy.Experience.description));
					}
				}
			}

			// calculate winner of the pk contest
			if(CMProps.getVar(CMProps.Str.CLANTROPPK).length()>0)
			{
				Clan winnerC=getTrophyWinner(Trophy.PlayerKills);
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement(); 
					if(C==winnerC) 
						continue;
					if((winnerC==null)||(C.getCurrentClanKills(null)>winnerC.getCurrentClanKills(null)))
						winnerC=C;
				}
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS))
					Log.debugOut("DefaultClan","PKTrophy: "+((winnerC==null)?"Noone":winnerC.clanID())+" won with "+((winnerC==null)?"0":""+winnerC.getCurrentClanKills(null)));
				if((winnerC!=null)
				&&(!CMath.bset(winnerC.getTrophies(),Trophy.PlayerKills.flagNum()))
				&&(winnerC.getCurrentClanKills(null)>0))
				{
					winnerC.setTrophies(winnerC.getTrophies()|Trophy.PlayerKills.flagNum());
					clanAnnounceAll("The "+winnerC.getGovernmentName()+" "+winnerC.name()+" has been awarded the trophy for "+Trophy.PlayerKills.description+".");
				}
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((winnerC!=C)&&(CMath.bset(C.getTrophies(),Trophy.PlayerKills.flagNum())))
					{
						C.setTrophies(C.getTrophies()-Trophy.PlayerKills.flagNum());
						C.clanAnnounce(L("The @x1 @x2 has lost control of the trophy for @x3.",C.getGovernmentName(),C.name(),Trophy.PlayerKills.description));
					}
				}
			}

			// calculate winner of the conquest contests
			if((CMProps.getVar(CMProps.Str.CLANTROPAREA).length()>0)
			||(CMProps.getVar(CMProps.Str.CLANTROPCP).length()>0))
			{
				Clan winnerMostClansControlledC=getTrophyWinner(Trophy.Areas);
				long mostClansControlled=(winnerMostClansControlledC==null)?-1:winnerMostClansControlledC.getControlledAreas().size();
				Clan winnerMostControlPointsC=getTrophyWinner(Trophy.Points);
				long mostControlPoints=(winnerMostControlPointsC==null)?-1:winnerMostControlPointsC.calculateMapPoints();
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((C!=winnerMostClansControlledC)&&(CMProps.getVar(CMProps.Str.CLANTROPAREA).length()>0))
					{
						final int controlledAreas=C.getControlledAreas().size();
						if(controlledAreas>mostClansControlled)
						{
							winnerMostClansControlledC=C;
							mostClansControlled=controlledAreas;
						}
					}
					if((C!=winnerMostControlPointsC)&&(CMProps.getVar(CMProps.Str.CLANTROPCP).length()>0))
					{
						final long mapPoints=C.calculateMapPoints();
						if(mapPoints>mostControlPoints)
						{
							winnerMostControlPointsC=C;
							mostControlPoints=mapPoints;
						}
					}
				}
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS))
					Log.debugOut("DefaultClan","AREATrophy: "+((winnerMostClansControlledC==null)?"Noone":winnerMostClansControlledC.clanID())+" won with "+mostClansControlled);
				if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS))
					Log.debugOut("DefaultClan","CPTrophy: "+((winnerMostControlPointsC==null)?"Noone":winnerMostControlPointsC.clanID())+" won with "+mostControlPoints);
				if((winnerMostClansControlledC!=null)
				&&(CMProps.getVar(CMProps.Str.CLANTROPAREA).length()>0)
				&&(mostClansControlled>0))
				{
					if(!CMath.bset(winnerMostClansControlledC.getTrophies(),Trophy.Areas.flagNum()))
					{
						winnerMostClansControlledC.setTrophies(winnerMostClansControlledC.getTrophies()|Trophy.Areas.flagNum());
						clanAnnounceAll("The "+winnerMostClansControlledC.getGovernmentName()+" "+winnerMostClansControlledC.name()+" has been awarded the trophy for "+Trophy.Areas.description+".");
					}
				}
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((winnerMostClansControlledC!=C)
					&&(CMath.bset(C.getTrophies(),Trophy.Areas.flagNum())))
					{
						C.setTrophies(C.getTrophies()-Trophy.Areas.flagNum());
						C.clanAnnounce(L("The @x1 @x2 has lost control of the trophy for @x3.",C.getGovernmentName(),C.name(),Trophy.Areas.description));
					}
				}
				if((winnerMostControlPointsC!=null)
				&&(CMProps.getVar(CMProps.Str.CLANTROPCP).length()>0)
				&&(mostControlPoints>0))
				{
					if(!CMath.bset(winnerMostControlPointsC.getTrophies(),Trophy.Points.flagNum()))
					{
						winnerMostControlPointsC.setTrophies(winnerMostControlPointsC.getTrophies()|Trophy.Points.flagNum());
						clanAnnounceAll("The "+winnerMostControlPointsC.getGovernmentName()+" "+winnerMostControlPointsC.name()+" has been awarded the trophy for "+Trophy.Points.description+".");
					}
				}
				for(final Enumeration<Clan> e=clans();e.hasMoreElements();)
				{
					final Clan C=e.nextElement();
					if((winnerMostControlPointsC!=C)
					&&(CMath.bset(C.getTrophies(),Trophy.Points.flagNum())))
					{
						C.setTrophies(C.getTrophies()-Trophy.Points.flagNum());
						C.clanAnnounce(L("The @x1 @x2 has lost control of the trophy for @x3.",C.getGovernmentName(),C.name(),Trophy.Points.description));
					}
				}
			}
		}
	}

	@Override
	public boolean activate()
	{
		if(serviceClient==null)
		{
			name="THClans"+Thread.currentThread().getThreadGroup().getName().charAt(0);
			serviceClient=CMLib.threads().startTickDown(this, Tickable.TICKID_SUPPORT|Tickable.TICKID_SOLITARYMASK, CMProps.getTickMillis()*CMProps.getTicksPerDay(), 1);
		}
		return true;
	}

	@Override public boolean tick(Tickable ticking, int tickID)
	{
		try
		{
			if(!CMSecurity.isDisabled(CMSecurity.DisFlag.CLANTICKS))
			{
				tickStatus=Tickable.STATUS_ALIVE;
				isDebugging=CMSecurity.isDebugging(DbgFlag.CLANS);
				setThreadStatus(serviceClient,"clan trophy scan");
				clanTrophyScan();
				setThreadStatus(serviceClient,"sleeping");
			}
		}
		finally
		{
			tickStatus=Tickable.STATUS_NOT;
			setThreadStatus(serviceClient,"sleeping");
		}
		return true;
	}

	@Override
	public boolean shutdown()
	{
		for(final Enumeration<Clan> e=all.elements();e.hasMoreElements();)
		{
			final Clan C=e.nextElement();
			CMLib.threads().deleteTick(C,Tickable.TICKID_CLAN);
			CMLib.database().DBUpdateClan(C);
			CMLib.database().DBUpdateClanItems(C);
		}
		all.clear();
		all2.clear();
		if(CMLib.threads().isTicking(this, TICKID_SUPPORT|Tickable.TICKID_SOLITARYMASK))
		{
			CMLib.threads().deleteTick(this, TICKID_SUPPORT|Tickable.TICKID_SOLITARYMASK);
			serviceClient=null;
		}
		return true;
	}

	@Override
	public void forceTick()
	{
		serviceClient.tickTicker(false);
	}

	@Override
	public void propertiesLoaded()
	{
		super.propertiesLoaded();
		for(final String clanID : all.keySet())
		{
			final Clan C=all.get(clanID);
			setClanWebSiteMappings(C,null);
			CMLib.journals().registerClanForum(C,null);
		}
		for(final String clanID : all.keySet())
		{
			final Clan C=all.get(clanID);
			setClanWebSiteMappings(C,CMProps.getVar(CMProps.Str.CLANWEBSITES));
			CMLib.journals().registerClanForum(C,CMProps.getVar(CMProps.Str.CLANFORUMDATA));
		}
	}

}