package com.planet_ink.coffee_mud.Common; import com.planet_ink.coffee_mud.core.interfaces.*; import com.planet_ink.coffee_mud.core.threads.ServiceEngine; import com.planet_ink.coffee_mud.core.*; import com.planet_ink.coffee_mud.core.collections.*; import com.planet_ink.coffee_mud.core.database.DBConnections; 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.Common.interfaces.AccountStats.Agent; 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.ClanVote; import com.planet_ink.coffee_mud.Common.interfaces.Clan.MemberRecord; import com.planet_ink.coffee_mud.Common.interfaces.PlayerAccount.AccountFlag; 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.Libraries.interfaces.AbilityMapper.AbilityMapping; import com.planet_ink.coffee_mud.Libraries.interfaces.AchievementLibrary.Achievement; import com.planet_ink.coffee_mud.Libraries.interfaces.AchievementLibrary.AchievementLoadFlag; import com.planet_ink.coffee_mud.Libraries.interfaces.AchievementLibrary.Award; import com.planet_ink.coffee_mud.Libraries.interfaces.AchievementLibrary.Tracker; import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine.PlayerData; import com.planet_ink.coffee_mud.Libraries.interfaces.JournalsLibrary.ForumJournal; import com.planet_ink.coffee_mud.Libraries.interfaces.PlayerLibrary.ThinPlayer; import com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag; 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.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; /** * Portions Copyright (c) 2003 Jeremy Vyska * Portions Copyright (c) 2004-2019 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 DefaultClan implements Clan { @Override public String ID() { return "DefaultClan"; } private final int tickStatus = Tickable.STATUS_NOT; @Override public int getTickStatus() { return tickStatus; } protected String clanName = ""; protected String clanCategory = null; protected String clanPremise = ""; protected String clanRecall = ""; protected String clanMorgue = ""; protected String clanClass = ""; protected int clanLevel = 0; protected String clanDonationRoom = ""; protected int clanTrophies = 0; protected Boolean isRivalrous = null; protected int autoPosition = -1; protected String acceptanceSettings = ""; protected int clanStatus = 0; protected long lastStatusChange = 0; protected String lastClanKillLog = null; protected double taxRate = 0.0; protected volatile long exp = 0; protected volatile long lastClanTickMs = System.currentTimeMillis(); protected Object expSync = new Object(); protected List<ClanVote> voteList = null; protected List<Long> clanKills = new Vector<Long>(); protected Integer overrideMinClanMembers = null; protected long lastPropsReload = System.currentTimeMillis(); protected ItemCollection extItems = (ItemCollection) CMClass.getCommon("WeakItemCollection"); protected Map<String, long[]> relations = new Hashtable<String, long[]>(); protected int government = 0; protected long lastGovernmentLoadTime = -1; protected ClanGovernment govt = null; protected long totalOnlineMins = 0; protected long totalLevelsGained = 0; protected int monthOnlineMins = 0; protected int monthPlayerXP = 0; protected int monthClanXP = 0; protected int monthConquered = 0; protected int monthClanLevels = 0; protected int monthControlPoints = 0; protected int monthNewMembers = 0; protected volatile int tickUp = 0; protected volatile int transientSize = -1; protected Set<ClanFlag> clanFlags = new SHashSet<ClanFlag>(); protected final static List<Ability> empty=new XVector<Ability>(1,true); protected final List<Pair<Clan, Integer>> channelSet = new XVector<Pair<Clan, Integer>>(1, true); protected CMUniqNameSortSVec<Tattoo> tattoos = new CMUniqNameSortSVec<Tattoo>(1); protected Map<String, Tracker> achievementers = new STreeMap<String, Tracker>(); /** return a new instance of the object*/ @Override public CMObject newInstance() { try { return getClass().newInstance(); } catch (final Exception e) { return new DefaultClan(); } } @Override public void initializeClass() { } @Override public int compareTo(final CMObject o) { return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o)); } @Override public CMObject copyOf() { try { final DefaultClan C=(DefaultClan)this.clone(); C.extItems=(ItemCollection)extItems.copyOf(); C.clanFlags = new SHashSet<ClanFlag>(clanFlags); return C; } catch(final CloneNotSupportedException e) { return new DefaultClan(); } } @Override public ClanGovernment getGovernment() { return govt(); } protected ClanGovernment govt() { if((govt != null) && ((government < 0) || (lastGovernmentLoadTime == CMLib.clans().getLastGovernmentLoad()))) return govt; else { ClanGovernment govt = CMLib.clans().getStockGovernment(government); if(govt == null) { govt = CMLib.clans().getDefaultGovernment(); government = govt.getID(); } lastGovernmentLoadTime = CMLib.clans().getLastGovernmentLoad(); return govt; } } private synchronized void clanKills() { if(lastClanKillLog==null) { final List<PlayerData> V=CMLib.database().DBReadPlayerData(clanID(),"CLANKILLS",clanID()+"/CLANKILLS"); clanKills.clear(); if(V.size()==0) lastClanKillLog=""; else { lastClanKillLog=V.get(0).xml(); final List<String> V2=CMParms.parseSemicolons(lastClanKillLog,true); for(int v=0;v<V2.size();v++) clanKills.add(Long.valueOf(CMath.s_long(V2.get(v)))); } } } private void updateClanKills() { Long date=null; final StringBuffer str=new StringBuffer(""); final long now=System.currentTimeMillis(); for(int i=clanKills.size()-1;i>=0;i--) { date=clanKills.get(i); if(date.longValue()<now) clanKills.remove(i); else str.append(date.longValue()).append(";"); } if((lastClanKillLog==null)||(!lastClanKillLog.equals(str.toString()))) { lastClanKillLog=str.toString(); CMLib.database().DBReCreatePlayerData(clanID(),"CLANKILLS",clanID()+"/CLANKILLS",str.toString()); } } @Override public void resetMonthlyTrophyData() { monthOnlineMins = 0; monthPlayerXP = 0; monthClanXP = 0; monthConquered = 0; monthClanLevels = 0; monthControlPoints = 0; monthNewMembers = 0; } @Override public long getTrophyData(final Trophy trophy) { switch(trophy) { case MonthlyPlayerMinutes: return monthOnlineMins; case MonthlyPlayerXP: return monthPlayerXP; case MonthlyClanXP: return monthClanXP; case MonthlyConquests: return monthConquered; case MonthlyClanLevels: return monthClanLevels; case MonthlyControlPoints: return monthControlPoints; case MonthlyNewMembers: return monthNewMembers; case Points: return calculateMapPoints(); case Experience: return exp; case Areas: return getControlledAreas().size(); case ClanKills: return getCurrentClanKills(null); case Members: return getSize(); case MemberLevel: return filterMedianLevel(getFullMemberList()); case PlayerMinutes: return totalOnlineMins; case PlayerLevelsGained: return totalLevelsGained; default: return 0; } } @Override public void bumpTrophyData(final Trophy trophy, final int amt) { switch(trophy) { case MonthlyPlayerMinutes: monthOnlineMins += amt; break; case MonthlyPlayerXP: monthPlayerXP += amt; break; case MonthlyClanXP: monthClanXP += amt; break; case MonthlyConquests: monthConquered += amt; break; case MonthlyClanLevels: monthClanLevels += amt; break; case MonthlyControlPoints: monthControlPoints += amt; break; case MonthlyNewMembers: monthNewMembers += amt; break; case Points: // derived break; case Experience: this.exp += amt; break; case Areas: // derived break; case ClanKills: // derived from member records break; case Members: // derived from member records break; case MemberLevel: // derived from mob records of member records break; case PlayerMinutes: totalOnlineMins += amt; break; case PlayerLevelsGained: totalLevelsGained += amt; break; } } @Override public void updateVotes() { final XMLLibrary xml=CMLib.xml(); final StringBuilder str=new StringBuilder(""); for(final Enumeration<ClanVote> e=votes();e.hasMoreElements();) { final ClanVote CV=e.nextElement(); str.append(xml.convertXMLtoTag("BY",CV.voteStarter)); str.append(xml.convertXMLtoTag("FUNC",CV.function)); str.append(xml.convertXMLtoTag("ON",""+CV.voteStarted)); str.append(xml.convertXMLtoTag("STATUS",""+CV.voteStatus)); str.append(xml.convertXMLtoTag("CMD",CV.matter)); if((CV.votes!=null)&&(CV.votes.size()>0)) { str.append("<VOTES>"); for(int v=0;v<CV.votes.size();v++) { str.append("<VOTE>"); str.append(xml.convertXMLtoTag("BY",CV.votes.getFirst(v))); str.append(xml.convertXMLtoTag("YN",CV.votes.getSecond(v).toString())); str.append("</VOTE>"); } str.append("</VOTES>"); } } if(str.length()>0) CMLib.database().DBReCreatePlayerData(clanID(),"CLANVOTES",clanID()+"/CLANVOTES","<BALLOTS>"+str.toString()+"</BALLOTS>"); else CMLib.database().DBDeletePlayerData(clanID(),"CLANVOTES",clanID()+"/CLANVOTES"); } @Override public void addVote(final ClanVote CV) { if(CV==null) return; votes(); voteList.add(CV); } @Override public void delVote(final ClanVote CV) { votes(); voteList.remove(CV); } @Override public void recordClanKill(final MOB killer, final MOB killed) { clanKills(); final Area A=CMLib.map().areaLocation(killer); if(A!=null) clanKills.add(Long.valueOf(System.currentTimeMillis() + (365L * 24L * 60L * 60L * 1000))); updateClanKills(); if((killer != null) &&(killed != null)) { if(killed.isMonster()) CMLib.database().DBUpdateClanKills(this.clanID(), killer.Name(), 1, 0); else CMLib.database().DBUpdateClanKills(this.clanID(), killer.Name(), 0, 1); } } @Override public int getCurrentClanKills(final MOB killer) { if(killer==null) { clanKills(); return clanKills.size(); } else { final MemberRecord M = CMLib.database().DBGetClanMember(this.clanID(), killer.Name()); return M.playerpvps; } } @Override public double getCurrentClanGoldDonations(final MOB killer) { if(killer==null) { return 0; } else { final MemberRecord M = CMLib.database().DBGetClanMember(this.clanID(), killer.Name()); return M.donatedGold; } } @Override public long getCurrentClanXPDonations(final MOB killer) { if(killer==null) { return this.exp; } else { final MemberRecord M = CMLib.database().DBGetClanMember(this.clanID(), killer.Name()); return M.donatedXP; } } @Override public boolean isSet(final ClanFlag flag) { return clanFlags.contains(flag); } @Override public void setFlag(final ClanFlag flag, final boolean setOrUnset) { if(setOrUnset) clanFlags.add(flag); else clanFlags.remove(flag); } @Override public boolean isOnlyFamilyApplicants() { return govt().isFamilyOnly(); } @Override public boolean isLoyaltyThroughItems() { return govt().isConquestItemLoyalty(); } @Override public boolean isWorshipConquest() { return govt().isConquestByWorship(); } @Override public long calculateMapPoints() { return calculateMapPoints(getControlledAreas()); } @Override public long calculateMapPoints(final List<Area> controlledAreas) { long points=0; if(controlledAreas!=null) { for(final Area A : controlledAreas) { final LegalBehavior B=CMLib.law().getLegalBehavior(A); if(B!=null) points+=B.controlPoints(); } } return points; } @Override public List<Area> getControlledAreas() { final Vector<Area> done=new Vector<Area>(); for(final Enumeration<Area> e=CMLib.map().areas();e.hasMoreElements();) { final Area A=e.nextElement(); final LegalBehavior B=CMLib.law().getLegalBehavior(A); if(B!=null) { final String controller=B.rulingOrganization(); final Area A2=CMLib.law().getLegalObject(A); if(controller.equals(clanID())&&(!done.contains(A2))) done.addElement(A2); } } return done; } @Override public Enumeration<ClanVote> votes() { if(voteList==null) { final List<PlayerData> V=CMLib.database().DBReadPlayerData(clanID(),"CLANVOTES",clanID()+"/CLANVOTES"); final XMLLibrary xmlLib=CMLib.xml(); voteList=new Vector<ClanVote>(); for(int v=0;v<V.size();v++) { final ClanVote CV=new ClanVote(); final String rawxml=V.get(v).xml(); if(rawxml.trim().length()==0) return new IteratorEnumeration<Clan.ClanVote>(voteList.iterator()); final List<XMLLibrary.XMLTag> xml=xmlLib.parseAllXML(rawxml); if(xml==null) { Log.errOut("Clans","Unable to parse: "+rawxml); return new IteratorEnumeration<Clan.ClanVote>(voteList.iterator()); } final List<XMLLibrary.XMLTag> voteData=xmlLib.getContentsFromPieces(xml,"BALLOTS"); if(voteData==null) { Log.errOut("Clans","Unable to get BALLOTS data."); return new IteratorEnumeration<Clan.ClanVote>(voteList.iterator()); } CV.voteStarter=xmlLib.getValFromPieces(voteData,"BY"); CV.voteStarted=xmlLib.getLongFromPieces(voteData,"ON"); CV.function=xmlLib.getIntFromPieces(voteData,"FUNC"); CV.voteStatus=xmlLib.getIntFromPieces(voteData,"STATUS"); CV.matter=xmlLib.getValFromPieces(voteData,"CMD"); CV.votes=new PairVector<String,Boolean>(); final List<XMLLibrary.XMLTag> xV=xmlLib.getContentsFromPieces(voteData,"VOTES"); if((xV!=null)&&(xV.size()>0)) { for(int x=0;x<xV.size();x++) { final XMLTag iblk=xV.get(x); if((!iblk.tag().equalsIgnoreCase("VOTE"))||(iblk.contents()==null)) continue; final String userID=iblk.getValFromPieces("BY"); final boolean yn=iblk.getBoolFromPieces("YN"); CV.votes.addElement(userID,Boolean.valueOf(yn)); } } voteList.add(CV); } } return new IteratorEnumeration<Clan.ClanVote>(voteList.iterator()); } @Override public int getAutoPosition() { return autoPosition<0?govt().getAutoRole():autoPosition; } @Override public void setAutoPosition(final int pos) { if(pos == govt().getAutoRole()) autoPosition=-1; else autoPosition=pos; } @Override public void setExp(final long newexp) { synchronized(expSync) { final long oldxp=exp; exp=newexp; if(exp<0) exp=0; final CMath.CompiledFormula form = govt().getXPCalculationFormula(); if(oldxp < exp) // we gained { double nextLevelXP = CMath.parseMathExpression(form, new double[]{getClanLevel()}, 0.0); while(exp > nextLevelXP) { setClanLevel(getClanLevel()+1); bumpTrophyData(Trophy.MonthlyClanLevels, 1); clanAnnounce(""+getGovernmentName()+" "+name()+" has attained clan level "+getClanLevel()+"!"); CMLib.achievements().possiblyBumpAchievement(getResponsibleMember(), AchievementLibrary.Event.CLANLEVELSGAINED, 1, this); update(); nextLevelXP = CMath.parseMathExpression(form, new double[]{getClanLevel()}, 0.0); } } else if((oldxp > exp) && (getClanLevel()>1)) { double prevLevelXP = CMath.parseMathExpression(form, new double[]{getClanLevel()-1}, 0.0); while(exp < prevLevelXP) { setClanLevel(getClanLevel()-1); CMLib.achievements().possiblyBumpAchievement(getResponsibleMember(), AchievementLibrary.Event.CLANLEVELSGAINED, -1, this); clanAnnounce(""+getGovernmentName()+" "+name()+" has reverted to clan level "+getClanLevel()+"!"); update(); prevLevelXP = CMath.parseMathExpression(form, new double[]{getClanLevel()-1}, 0.0); } } } } @Override public void adjExp(final MOB memberM, final int howMuch) { if (howMuch != 0) { setExp(getExp() + howMuch); if(howMuch > 0) bumpTrophyData(Trophy.MonthlyClanXP, howMuch); if(memberM != null) CMLib.database().DBUpdateClanDonates(this.clanID(), memberM.Name(), 0, howMuch); } } @Override public void adjDeposit(final MOB memberM, final double howMuch) { if(memberM != null) CMLib.database().DBUpdateClanDonates(this.clanID(), memberM.Name(), howMuch, 0); } @Override public long getExp() { return exp; } @Override public int getTrophies() { return clanTrophies; } @Override public void setTrophies(final int trophyFlag) { clanTrophies = trophyFlag; } @Override public void setTaxes(final double rate) { taxRate=rate; } @Override public double getTaxes() { return taxRate; } @Override public int getClanRelations(final String id) { final long i[]=relations.get(id.toUpperCase()); if(i!=null) return (int)i[0]; return REL_NEUTRAL; } @Override public long getLastRelationChange(final String id) { final long i[]=relations.get(id.toUpperCase()); if(i!=null) return i[1]; return 0; } @Override public void setClanRelations(final String id, final int rel, final long time) { relations.remove(id.toUpperCase()); final long[] i=new long[2]; i[0]=rel; i[1]=time; relations.put(id.toUpperCase(),i); } @Override public int getGovernmentID() { return government; } @Override public void setGovernmentID(final int type) { government = type; lastGovernmentLoadTime = -1; } @Override public String getCategory() { if(clanCategory!=null) return clanCategory; return govt().getCategory(); } @Override public int getMinClanMembers() { if(overrideMinClanMembers!=null) return overrideMinClanMembers.intValue(); if(govt().getOverrideMinMembers()!=null) return govt().getOverrideMinMembers().intValue(); return CMProps.getIntVar(CMProps.Int.MINCLANMEMBERS); } @Override public void setMinClanMembers(final int amt) { overrideMinClanMembers=null; if(govt().getOverrideMinMembers()!=null) { if(govt().getOverrideMinMembers().intValue()==amt) return; overrideMinClanMembers=Integer.valueOf(amt); } else { if(CMProps.getIntVar(CMProps.Int.MINCLANMEMBERS)==amt) return; overrideMinClanMembers=Integer.valueOf(amt); } } @Override public void setCategory(final String newCategory) { if(govt().getCategory().equalsIgnoreCase(newCategory)) clanCategory=null; else clanCategory=newCategory; } @Override public boolean isRivalrous() { if(isRivalrous==null) return govt().isRivalrous(); return isRivalrous.booleanValue(); } @Override public void setRivalrous(final boolean isRivalrous) { if(govt().isRivalrous()==isRivalrous) this.isRivalrous=null; else this.isRivalrous=Boolean.valueOf(isRivalrous); } @Override public void create() { CMLib.database().DBCreateClan(this); CMLib.clans().addClan(this); } @Override public void update() { CMLib.database().DBUpdateClan(this); } @Override public void addMember(final MOB M, final int role) { transientSize=-1; M.setClan(clanID(),role); CMLib.database().DBUpdateClanMembership(M.Name(), clanID(), role); updateClanPrivileges(M); bumpTrophyData(Trophy.MonthlyNewMembers, 1); } @Override public void delMember(final MOB M) { transientSize=-1; CMLib.database().DBUpdateClanMembership(M.Name(), clanID(), -1); M.setClan(clanID(),-1); updateClanPrivileges(M); } @Override public boolean updateClanPrivileges(final MOB M) { boolean didUpdatePlayer=false; if(M==null) return false; final Pair<Clan,Integer> p=M.getClanRole(clanID()); if((p!=null) && (getAuthority(p.second.intValue(),Function.CLAN_BENEFITS)!=Clan.Authority.CAN_NOT_DO)) { final CharClass CC=getClanClassC(); if((CC!=null) &&(CC.availabilityCode()!=0) &&(M.baseCharStats().getCurrentClass()!=CC)) { M.baseCharStats().setCurrentClass(CC); didUpdatePlayer=true; M.recoverCharStats(); } CMLib.achievements().loadClanAchievements(M,AchievementLoadFlag.NORMAL); } else { final String removeMsg =CMLib.achievements().removeClanAchievementAwards(M, this); if((removeMsg != null)&&(removeMsg.length()>0)) M.tell(removeMsg); } // Get a list of all possible spell grants for this clan, check against class qualifications. // Form a list of forbidden spells, and then remove THOSE. final Map<String, AbilityMapping> allAbles = CMLib.ableMapper().getAbleMapping(getGovernment().getName()); final Set<String> qualAbleIDS = new TreeSet<String>(); for(final Ability A : getGovernment().getClanLevelAbilities(M, this, Integer.valueOf(getClanLevel()))) qualAbleIDS.add(A.ID()); for(final String ableID : allAbles.keySet()) { final Ability A=M.fetchAbility(ableID); if((A != null) &&(!qualAbleIDS.contains(ableID)) &&(!CMLib.ableMapper().qualifiesByLevel(M, ableID)) // this will check ALL clans, as well as other sources. &&(allAbles.get(ableID).autoGain())) M.delAbility(A); } final PlayerStats pStats = M.playerStats(); if(pStats!=null) { final Set<String> myAllowedTitles = new TreeSet<String>(); if(p != null) { final ClanPosition myPos = govt().findPositionRole(p.second); final String myNicePosName = CMStrings.capitalizeAllFirstLettersAndLower(myPos.getName()); for(final String baseTitle : govt().getTitleAwards()) myAllowedTitles.add(L(baseTitle,name(),myNicePosName)); for(final String posTitle : myPos.getTitleAwards()) myAllowedTitles.add(L(posTitle,name(),myNicePosName)); if(getAuthority(p.second.intValue(),Function.CLAN_TITLES)!=Clan.Authority.CAN_NOT_DO) { for(final String title : myAllowedTitles) { if(!pStats.getTitles().contains(title)) pStats.getTitles().add(title); } } } for(final ClanPosition pos : govt().getPositions()) { if((p==null)||(p.second.intValue()!=pos.getRoleID())) { final String nicePosName = CMStrings.capitalizeAllFirstLettersAndLower(pos.getName()); for(final String baseTitle : govt().getTitleAwards()) { final String badTitle = L(baseTitle,name(),nicePosName); if(!myAllowedTitles.contains(badTitle)) { for(final String titleCheck : pStats.getTitles()) { if(titleCheck.equalsIgnoreCase(badTitle)) pStats.getTitles().remove(titleCheck); } } } for(final String posTitle : pos.getTitleAwards()) { final String badTitle = L(posTitle,name(),nicePosName); if(!myAllowedTitles.contains(badTitle)) { for(final String titleCheck : pStats.getTitles()) { if(titleCheck.equalsIgnoreCase(badTitle)) pStats.getTitles().remove(titleCheck); } } } } } } if(p==null) { Item I=null; final List<Item> itemsToMove=new ArrayList<Item>(); for(int i=0;i<M.numItems();i++) { I=M.getItem(i); if(I instanceof ClanItem) itemsToMove.add(I); } for(int i=0;i<itemsToMove.size();i++) { I=itemsToMove.get(i); if(I!=null) { Room R=null; if((getDonation()!=null) &&(getDonation().length()>0)) R=CMLib.map().getRoom(getDonation()); if((R==null) &&(getRecall()!=null) &&(getRecall().length()>0)) R=CMLib.map().getRoom(getRecall()); if(I instanceof Container) { final List<Item> V=((Container)I).getDeepContents(); for(int v=0;v<V.size();v++) V.get(v).setContainer(null); } I.setContainer(null); I.wearAt(Wearable.IN_INVENTORY); if(R!=null) R.moveItemTo(I); else if(M.isMine(I)) I.destroy(); didUpdatePlayer=true; } } } if((didUpdatePlayer)&&(!CMSecurity.isSaveFlag(CMSecurity.SaveFlag.NOPLAYERS))) CMLib.database().DBUpdatePlayer(M); return didUpdatePlayer; } @Override public void destroyClan() { final List<MemberRecord> members=getMemberList(); for(final MemberRecord member : members) { final MOB M=CMLib.players().getLoadPlayer(member.name); if(M!=null) { M.setClan(clanID(),-1); updateClanPrivileges(M); CMLib.database().DBUpdateClanMembership(M.Name(), clanID(), -1); } } CMLib.database().DBDeleteJournal("a Journal of "+getGovernmentName()+" "+getName(), null); CMLib.database().DBDeleteJournal("CLAN_MOTD"+clanID(), null); CMLib.database().DBDeleteClan(this); CMLib.clans().removeClan(this); } protected CharClass getClanClassC() { if(clanClass.length()==0) return null; CharClass C=CMClass.getCharClass(clanClass); if(C==null) C=CMClass.findCharClass(clanClass); return C; } @Override public String getDetail(final MOB mob) { final int COLBL_WIDTH=CMLib.lister().fixColWidth(16.0,mob); final StringBuilder msg=new StringBuilder(""); final Pair<Clan,Integer> mobClanRole=(mob!=null)?(mob.getClanRole(clanID())):null; final boolean member=(mob!=null) &&(mobClanRole!=null) &&(getAuthority(mobClanRole.second.intValue(),Function.LIST_MEMBERS)!=Authority.CAN_NOT_DO); final boolean sysmsgs=(mob!=null)&&mob.isAttributeSet(MOB.Attrib.SYSOPMSGS); final CMath.CompiledFormula form = govt().getXPCalculationFormula(); final double nextLevelXP = CMath.parseMathExpression(form, new double[]{getClanLevel()}, 0.0); msg.append("^x"+CMStrings.padRight(L(getGovernmentName()+" Profile"),COLBL_WIDTH)+":^.^N "+clanID()+"\n\r"); msg.append("-----------------------------------------------------------------\n\r"); msg.append(getPremise()+"\n\r"); msg.append("-----------------------------------------------------------------\n\r"); msg.append("^x"+CMStrings.padRight(L("Level"),COLBL_WIDTH)+":^.^N "+getClanLevel()); if(member||sysmsgs) msg.append(L(" (Next at ^w@x1^Nxp)",""+nextLevelXP)); msg.append("\n\r"); msg.append("^x"+CMStrings.padRight(L("Type"),COLBL_WIDTH)+":^.^N "+CMStrings.capitalizeAndLower(govt().getName())+"\n\r"); if(getAcceptanceSettings().length()>0) { msg.append("^x"+CMStrings.padRight(L("Qualifications"),COLBL_WIDTH)+":^.^N "+CMLib.masking().maskDesc(getAcceptanceSettings())+"\n\r"); if(getBasicRequirementMask().length()>0) msg.append("^x"+CMStrings.padLeft(L("Plus "),COLBL_WIDTH)+":^.^N "+CMLib.masking().maskDesc(getBasicRequirementMask())+"\n\r"); } else if(getBasicRequirementMask().length()>0) msg.append("^x"+CMStrings.padRight(L("Qualifications"),COLBL_WIDTH)+":^.^N "+CMLib.masking().maskDesc(getBasicRequirementMask())+"\n\r"); else msg.append("^x"+CMStrings.padRight(L("Qualifications"),COLBL_WIDTH)+":^.^N "+L("Anyone may apply")+"\n\r"); final CharClass clanC=getClanClassC(); if(clanC!=null) msg.append("^x"+CMStrings.padRight(L("Class"),COLBL_WIDTH)+":^.^N "+clanC.name()+"\n\r"); msg.append("^x"+CMStrings.padRight(L("Exp. Tax Rate"),COLBL_WIDTH)+":^.^N "+((int)Math.round(getTaxes()*100))+"%\n\r"); if(member||sysmsgs) { msg.append("^x"+CMStrings.padRight(L("Experience Pts."),COLBL_WIDTH)+":^.^N "+getExp()+"\n\r"); if(getMorgue().length()>0) { final Room R=CMLib.map().getRoom(getMorgue()); if(R!=null) msg.append("^x"+CMStrings.padRight(L("Morgue"),COLBL_WIDTH)+":^.^N "+R.displayText(mob)+"\n\r"); } if(getDonation().length()>0) { final Room R=CMLib.map().getRoom(getDonation()); if(R!=null) msg.append("^x"+CMStrings.padRight(L("Donations"),COLBL_WIDTH)+":^.^N "+R.displayText(mob)+"\n\r"); } if(getRecall().length()>0) { final Room R=CMLib.map().getRoom(getRecall()); if(R!=null) msg.append("^x"+CMStrings.padRight(L("Recall"),COLBL_WIDTH)+":^.^N "+R.displayText(mob)+"\n\r"); } } final List<MemberRecord> members=getMemberList(); final Set<ClanPosition> sortedPositions=new HashSet<ClanPosition>(); for(int i=0;i<govt().getPositions().length;i++) { ClanPosition topRankedPos=null; for(final ClanPosition pos : govt().getPositions()) { if((pos.isPublic()) &&(!sortedPositions.contains(pos)) &&((topRankedPos==null)||(pos.getRank() < topRankedPos.getRank()))) topRankedPos = pos; } if(topRankedPos != null) { msg.append("^x"+CMStrings.padRight(CMStrings.capitalizeAllFirstLettersAndLower(topRankedPos.getPluralName()),COLBL_WIDTH)+":^.^N "+crewList(members, topRankedPos.getRoleID())+"\n\r"); sortedPositions.add(topRankedPos); } } msg.append("^x"+CMStrings.padRight(L("Total Members"),COLBL_WIDTH)+":^.^N "+members.size()+"\n\r"); if(CMLib.clans().numClans()>1) { msg.append("-----------------------------------------------------------------\n\r"); msg.append("^x"+CMStrings.padRight(L("Clan Relations"),COLBL_WIDTH)+":^.^N \n\r"); boolean others = false; final int COLCL_WIDTH=CMLib.lister().fixColWidth(26.0,mob); for(final Enumeration<Clan> e=CMLib.clans().clans();e.hasMoreElements();) { final Clan C=e.nextElement(); if((C!=this)&&(C.isRivalrous())) { final int rel=getClanRelations(C.clanID()); final int orel=C.getClanRelations(clanID()); if((rel!=REL_NEUTRAL) || (orel != REL_NEUTRAL)) { msg.append("^H"+CMStrings.padRight(C.name(),COLCL_WIDTH)+":^.^N "); msg.append(REL_COLORS[rel]).append(CMStrings.capitalizeAndLower(REL_DESCS[rel])); if((rel!=REL_NEUTRAL) || (orel != REL_NEUTRAL)) msg.append("^N (^W<-").append(REL_COLORS[orel]).append(CMStrings.capitalizeAndLower(REL_DESCS[orel])).append("^N)"); else msg.append("^N"); msg.append("\n\r"); } else others=true; } } if(others) { msg.append("^H"+CMStrings.padRight("All Others",COLCL_WIDTH)+":^.^N "); msg.append(REL_COLORS[REL_NEUTRAL]).append(CMStrings.capitalizeAndLower(REL_DESCS[REL_NEUTRAL])); msg.append("^N").append("\n\r"); } } if(member||sysmsgs) { updateClanPrivileges(mob); for(final ClanPosition pos : govt().getPositions()) { if((!pos.isPublic())&&(member) &&((pos.getRoleID()!=govt().getAutoRole())||(pos.getRoleID()==govt().getAcceptPos()))) { msg.append("-----------------------------------------------------------------\n\r"); msg.append("^x"+CMStrings.padRight(CMStrings.capitalizeAndLower(pos.getPluralName()),COLBL_WIDTH) +":^.^N "+crewList(members, pos.getRoleID())+"\n\r"); } } if((mobClanRole!=null) &&(govt().getAutoRole()!=govt().getAcceptPos()) &&((getAuthority(mobClanRole.second.intValue(),Function.ACCEPT)!=Clan.Authority.CAN_NOT_DO)||sysmsgs)) { final ClanPosition pos=govt().getPositions()[getAutoPosition()]; msg.append("-----------------------------------------------------------------\n\r"); msg.append("^x"+CMStrings.padRight(CMStrings.capitalizeAndLower(pos.getPluralName()),COLBL_WIDTH) +":^.^N "+crewList(members, pos.getRoleID())+"\n\r"); } } final List<String> control=new ArrayList<String>(); final List<Area> controlledAreas=getControlledAreas(); for(final Area A : controlledAreas) control.add(A.name()); if(control.size()>0) { msg.append("-----------------------------------------------------------------\n\r"); msg.append(L("^xClan Controlled Areas (% revolt):^.^N\n\r")); Collections.sort(control); int col=0; final int COL_LEN=CMLib.lister().fixColWidth(25.0,mob); for(int i=0;i<control.size();i++) { if((++col)>3) { msg.append("\n\r"); col=1; } final Area A=CMLib.map().getArea(control.get(i)); if(A!=null) { final LegalBehavior B=CMLib.law().getLegalBehavior(A); final Area legalA=CMLib.law().getLegalObject(A); int pctRevolt=0; if((B!=null)&&(legalA!=null)) pctRevolt=B.revoltChance(); msg.append("^c"+CMStrings.padRight(A.name()+"^N ("+pctRevolt+"%)",COL_LEN)+"^N"); } } msg.append("\n\r"); } if((CMLib.clans().trophySystemActive()) &&(getTrophies()!=0) &&(!isSet(ClanFlag.NOTROPHY))) { msg.append("-----------------------------------------------------------------\n\r"); msg.append(L("^xTrophies awarded:^.^N\n\r")); for(final Trophy t : Trophy.values()) { if(CMath.bset(getTrophies(),t.flagNum())) { msg.append(t.codeString+" "); if(t.name().toUpperCase().indexOf("MONTHLY")<0) msg.append("(").append(this.getTrophyData(t)).append(") "); else msg.append(":"); msg.append(L(" Prize: @x1\n\r",CMLib.clans().translatePrize(t))); } } } final List<Achievement> achievements = new ArrayList<Achievement>(); // current users achievements rechecked above for(final Tattoo tatt : this.tattoos) { final Achievement A=CMLib.achievements().getAchievement(tatt.getTattooName()); if(A!=null) // let's just trust this one. achievements.add(A); } if(achievements.size()>0) { msg.append("-----------------------------------------------------------------\n\r"); msg.append(L("^xClan Achievements:^.^N\n\r")); for(final Achievement A : achievements) msg.append("^N"+A.getDisplayStr()+"\n\r"); } if(((mobClanRole!=null)&&(getAuthority(mobClanRole.second.intValue(),Function.CLAN_BENEFITS)!=Clan.Authority.CAN_NOT_DO))||sysmsgs) { msg.append("-----------------------------------------------------------------\n\r"); msg.append(L("^xClan Level Benefits:^.^N\n\r")); final List<AbilityMapper.AbilityMapping> abilities=CMLib.ableMapper().getUpToLevelListings(govt().getName(),getClanLevel(),true,false); if(abilities.size()>0) { final List<String> names = new Vector<String>(); for(final AbilityMapper.AbilityMapping aMap : abilities) { final Ability A=CMClass.getAbility(aMap.abilityID()); if(A!=null) { if((aMap.extFields().size()==0) ||(mobClanRole==null) ||(sysmsgs) ||(aMap.extFields().containsKey(mobClanRole.second.toString()))) names.add(A.name()+(aMap.autoGain()?"":"(q)")+((aMap.extFields().size()>0)?"*":"")); } } for(final Achievement A : achievements) { final Award[] awards = A.getRewards(); for(final Award award : awards) names.add(CMLib.achievements().fixAwardDescription(A, award, mob, mob)); } msg.append(CMLib.lister().makeColumns(mob,names,null,3)); msg.append("\n\r"); } final int numReff=CMath.s_int(govt().getStat("NUMREFF")); for(int i=0;i<numReff;i++) { final String ableName=govt().getStat("GETREFF"+i); final String ableText=govt().getStat("GETREFFPARM"+i); final int ableLvl=CMath.s_int(govt().getStat("GETREFFLVL"+i)); final List<String> ableRoles=CMParms.parseCommas(govt().getStat("GETREFFROLE"+i),true); final Ability A=CMClass.getAbility(ableName); if((A!=null) &&(ableLvl<=this.clanLevel) &&((ableRoles.size()==0) ||(mobClanRole==null) ||(sysmsgs) ||(ableRoles.contains(mobClanRole.second.toString())))) { A.setMiscText(ableText); msg.append(A.accountForYourself()).append("\n\r"); } } } return msg.toString(); } public String L(final String str, final String ... xs) { return CMLib.lang().fullSessionTranslation(str, xs); } @Override public String getGovernmentName() { return CMStrings.capitalizeAndLower(govt().getName()); } @Override public boolean canBeAssigned(final MOB mob, final int role) { if(mob==null) return false; if((role<0)||(role>govt().getPositions().length)) return false; final ClanPosition pos = govt().getPositions()[role]; return CMLib.masking().maskCheck(fixRequirementMask(pos.getInnerMaskStr()), mob, true); } private boolean canBeAssigned(final ThinPlayer mob, final int role) { if(mob==null) return false; if((role<0)||(role>govt().getPositions().length)) return false; final ClanPosition pos = govt().getPositions()[role]; if((pos.getInnerMaskStr() == null) ||(pos.getInnerMaskStr().trim().length()==0)) return true; final String mask = this.fixRequirementMask(pos.getInnerMaskStr()); return CMLib.masking().maskCheck(CMLib.masking().getPreCompiledMask(mask), mob); } @Override public Authority getAuthority(final int roleID, final Function function) { if((roleID<0)||(roleID>=govt().getPositions().length)) return Authority.CAN_NOT_DO; return govt().getPositions()[roleID].getFunctionChart()[function.ordinal()]; } public String fixRequirementMask(final String oldMask) { if((oldMask==null)||(oldMask.trim().length()==0)) return ""; final StringBuilder mask=new StringBuilder(oldMask.trim()); if(mask.length()==0) return ""; final MOB M=getResponsibleMember(); int x=mask.indexOf("%["); while(x>=0) { final int y=mask.indexOf("]%",x+1); if(y>x) { final String tag=mask.substring(x+2,y); String value="Unknown"; if(isStat(tag)) value=getStat(tag); else if(M!=null) { if(tag.equalsIgnoreCase("WORSHIPCHARID")) { value=M.getWorshipCharID(); if(value.length()==0) value="ANY"; } else if(CMLib.coffeeMaker().isAnyGenStat(M, tag)) value=CMLib.coffeeMaker().getAnyGenStat(M, tag); } else if(tag.equalsIgnoreCase("WORSHIPCHARID")) value="ANY"; mask.replace(x, y+2, value); } if(x>=mask.length()-1) break; x=mask.indexOf("%[",x+1); } return mask.toString(); } @Override public String getBasicRequirementMask() { return fixRequirementMask(govt().getRequiredMaskStr()); } protected List<MemberRecord> getRealMemberList(final int PosFilter) { final List<MemberRecord> members=getMemberList(PosFilter); if(members==null) return null; final List<MemberRecord> realMembers=new Vector<MemberRecord>(); for(final MemberRecord member : members) { if(CMLib.players().playerExists(member.name)) realMembers.add(member); } return members; } @Override public int getSize() { if(transientSize < 0) transientSize = CMLib.database().DBReadClanMembers(clanID()).size(); return transientSize; } @Override public String name() { return clanName; } @Override public String getName() { return clanName; } @Override public String clanID() { return clanName; } @Override public void setName(final String newName) { clanName = newName; } @Override public String getPremise() { return clanPremise; } @Override public void setPremise(final String newPremise) { clanPremise = newPremise; } @Override public int getClanLevel() { return clanLevel; } @Override public void setClanLevel(final int newClanLevel) { if(newClanLevel<=0) clanLevel=1; else clanLevel = newClanLevel; } @Override public String getAcceptanceSettings() { return acceptanceSettings; } @Override public void setAcceptanceSettings(final String newSettings) { acceptanceSettings = newSettings; } @Override public String getClanClass() { return clanClass; } @Override public void setClanClass(final String newClass) { clanClass = newClass; } @Override public String getDataXML() { final StringBuilder str=new StringBuilder(""); final XMLLibrary xmlLib=CMLib.xml(); str.append("<POLITICS>"); str.append(xmlLib.convertXMLtoTag("GOVERNMENT",""+getGovernmentID())); str.append(xmlLib.convertXMLtoTag("TAXRATE",""+getTaxes())); str.append(xmlLib.convertXMLtoTag("EXP",""+getExp())); str.append(xmlLib.convertXMLtoTag("ONLINEMINS",""+totalOnlineMins)); str.append(xmlLib.convertXMLtoTag("LVLSGAINED",""+totalLevelsGained)); str.append(xmlLib.convertXMLtoTag("LEVEL",""+getClanLevel())); str.append(xmlLib.convertXMLtoTag("CCLASS",""+getClanClass())); str.append(xmlLib.convertXMLtoTag("AUTOPOS",""+getAutoPosition())); str.append(xmlLib.convertXMLtoTag("FLAGS",""+getStat("FLAGS"))); str.append(xmlLib.convertXMLtoTag("LASTSTATUSCHANGE",""+this.lastStatusChange)); if(clanCategory!=null) str.append(xmlLib.convertXMLtoTag("CATE",clanCategory)); if(overrideMinClanMembers!=null) str.append(xmlLib.convertXMLtoTag("MINM",overrideMinClanMembers.toString())); if(isRivalrous!=null) str.append(xmlLib.convertXMLtoTag("RIVAL",isRivalrous.toString())); str.append("<ACHIEVEMENTS"); for(final Iterator<Tracker> i=achievementers.values().iterator();i.hasNext();) { final Tracker T = i.next(); if(T.getAchievement().isSavableTracker() && (T.getCount(null) != 0)) str.append(" ").append(T.getAchievement().getTattoo()).append("=").append(T.getCount(null)); // getCount(null) should be ok, because it's only the un-savable trackers that need the mob obj } str.append(" />"); str.append("<TATTOOS>").append(CMParms.toListString(tattoos)).append("</TATTOOS>"); if(relations.size()==0) str.append("<RELATIONS/>"); else { str.append("<RELATIONS>"); for(final Iterator<String> e=relations.keySet().iterator();e.hasNext();) { final String key=e.next(); str.append("<RELATION>"); str.append(xmlLib.convertXMLtoTag("CLAN",key)); final long[] i=relations.get(key); str.append(xmlLib.convertXMLtoTag("STATUS",""+i[0])); str.append("</RELATION>"); } str.append("</RELATIONS>"); } str.append("</POLITICS>"); str.append(xmlLib.convertXMLtoTag("ONLINEMINS",""+totalOnlineMins)); str.append(xmlLib.convertXMLtoTag("LVLSGAINED",""+totalLevelsGained)); final StringBuilder monthlies = new StringBuilder(); monthlies.append(monthOnlineMins).append(","); monthlies.append(monthPlayerXP).append(","); monthlies.append(monthClanXP).append(","); monthlies.append(monthConquered).append(","); monthlies.append(monthClanLevels).append(","); monthlies.append(monthControlPoints).append(","); monthlies.append(monthNewMembers); str.append(xmlLib.convertXMLtoTag("MONTHLYSTATS",monthlies.toString())); return str.toString(); } @Override public void setDataXML(final String politics) { final XMLLibrary xmlLib=CMLib.xml(); XMLTag piece; relations.clear(); government=0; if(politics.trim().length()==0) return; final List<XMLLibrary.XMLTag> xml=xmlLib.parseAllXML(politics); if(xml==null) { Log.errOut("Clans","Unable to parse: "+politics); return; } final List<XMLLibrary.XMLTag> poliData=xmlLib.getContentsFromPieces(xml,"POLITICS"); if(poliData==null) { Log.errOut("Clans","Unable to get POLITICS data."); return; } government=xmlLib.getIntFromPieces(poliData,"GOVERNMENT"); exp=xmlLib.getLongFromPieces(poliData,"EXP"); setClanLevel(xmlLib.getIntFromPieces(poliData,"LEVEL")); setExp(exp); // may change the level taxRate=xmlLib.getDoubleFromPieces(poliData,"TAXRATE"); clanClass=xmlLib.getValFromPieces(poliData,"CCLASS"); lastStatusChange=xmlLib.getLongFromPieces(poliData,"LASTSTATUSCHANGE"); autoPosition=xmlLib.getIntFromPieces(poliData,"AUTOPOS"); clanCategory=null; piece=xmlLib.getPieceFromPieces(poliData, "CATE"); if(piece!=null) setCategory(piece.value()); overrideMinClanMembers=null; piece=xmlLib.getPieceFromPieces(poliData, "MINM"); if(piece!=null) this.setMinClanMembers(CMath.s_int(piece.value())); isRivalrous=null; piece=xmlLib.getPieceFromPieces(poliData, "RIVAL"); if(piece!=null) setRivalrous(CMath.s_bool(piece.value())); final String monthlyData = xmlLib.getValFromPieces(poliData, "MONTHLYSTATS"); final int[] data=CMParms.parseIntList(monthlyData, ','); if((data != null)&&(data.length>6)) { monthOnlineMins = data[0]; monthPlayerXP = data[1]; monthClanXP = data[2]; monthConquered = data[3]; monthClanLevels = data[4]; monthControlPoints = data[5]; monthNewMembers = data[6]; } totalOnlineMins=xmlLib.getIntFromPieces(poliData,"ONLINEMINS"); totalLevelsGained=xmlLib.getIntFromPieces(poliData,"LVLSGAINED"); setStat("FLAGS",xmlLib.getValFromPieces(poliData,"FLAGS")); final XMLTag achievePiece = xmlLib.getPieceFromPieces(poliData, "ACHIEVEMENTS"); achievementers.clear(); for(final Enumeration<Achievement> a=CMLib.achievements().achievements(Agent.CLAN);a.hasMoreElements();) { final Achievement A=a.nextElement(); if((achievePiece != null) && achievePiece.parms().containsKey(A.getTattoo())) achievementers.put(A.getTattoo(), A.getTracker(CMath.s_int(achievePiece.parms().get(A.getTattoo()).trim()))); else achievementers.put(A.getTattoo(), A.getTracker(0)); } final String[] allTattoos=xmlLib.getValFromPieces(poliData, "TATTOOS").split(","); this.tattoos.clear(); for(final String tattoo : allTattoos) this.addTattoo(tattoo); // now RESOURCES! final List<XMLLibrary.XMLTag> xV=xmlLib.getContentsFromPieces(poliData,"RELATIONS"); if((xV!=null)&&(xV.size()>0)) { for(int x=0;x<xV.size();x++) { final XMLTag iblk=xV.get(x); if((!iblk.tag().equalsIgnoreCase("RELATION"))||(iblk.contents()==null)) continue; final String relClanID=iblk.getValFromPieces("CLAN"); final int rel=iblk.getIntFromPieces("STATUS"); setClanRelations(relClanID,rel,0); } } } @Override public int getStatus() { return clanStatus; } @Override public void setStatus(final int newStatus) { if(newStatus != clanStatus) this.lastStatusChange=System.currentTimeMillis(); clanStatus = newStatus; } @Override public String getRecall() { return clanRecall; } @Override public void setRecall(final String newRecall) { clanRecall = newRecall; } @Override public String getMorgue() { return clanMorgue; } @Override public void setMorgue(final String newMorgue) { clanMorgue = newMorgue; } @Override public String getDonation() { return clanDonationRoom; } @Override public void setDonation(final String newDonation) { clanDonationRoom = newDonation; } @Override public List<MemberRecord> getMemberList() { return getMemberList(-1); } public int filterMedianLevel(final List<FullMemberRecord> members) { final List<Integer> lvls=new SortedListWrap<Integer>(new XVector<Integer>()); for(final FullMemberRecord r : members) lvls.add(Integer.valueOf(r.level)); if(lvls.size()>0) return lvls.get(lvls.size()/2).intValue(); return 0; } public List<MemberRecord> filterMemberList(final List<? extends MemberRecord> members, final int posFilter) { final Vector<MemberRecord> filteredMembers=new Vector<MemberRecord>(); for(final MemberRecord member : members) { if(((member.role==posFilter)||(posFilter<0)) &&(member.name.length()>0)) filteredMembers.add(member); } return filteredMembers; } @Override public List<MemberRecord> getMemberList(final int posFilter) { final List<MemberRecord> members=CMLib.database().DBReadClanMembers(clanID()); transientSize = members.size(); return filterMemberList(members, posFilter); } @Override public MemberRecord findMemberRecord(final String name) { final List<MemberRecord> members=CMLib.database().DBReadClanMembers(clanID()); transientSize = members.size(); for(final MemberRecord member : members) { if(member.name.equalsIgnoreCase(name)) return member; } for(final MemberRecord member : members) { if(member.name.startsWith(name)) return member; } return null; } @Override public MOB findMember(final String name) { final MemberRecord M=findMemberRecord(name); if(M != null) return CMLib.players().getPlayerAllHosts(M.name); return null; } @Override public MemberRecord getMember(final String name) { return CMLib.database().DBGetClanMember(clanID(),name); } @Override public List<FullMemberRecord> getFullMemberList() { final List<FullMemberRecord> members=new Vector<FullMemberRecord>(); final List<MemberRecord> fullMembers = CMLib.database().DBReadClanMembers(clanID()); final List<MemberRecord> subMembers=filterMemberList(fullMembers, -1); transientSize = fullMembers.size(); for(final MemberRecord member : subMembers) { if(member!=null) { final MOB M=CMLib.players().getPlayer(member.name); if((M!=null) &&(M.playerStats()!=null)) { final boolean isAdmin=CMSecurity.isASysOp(M) || M.phyStats().level() > CMProps.get(M.session()).getInt(CMProps.Int.LASTPLAYERLEVEL); if(M.lastTickedDateTime()>0) members.add(new FullMemberRecord(member,M.basePhyStats().level(),M.lastTickedDateTime(),isAdmin)); else members.add(new FullMemberRecord(member,M.basePhyStats().level(),M.playerStats().getLastDateTime(),isAdmin)); } else { final PlayerLibrary.ThinPlayer tP = CMLib.database().getThinUser(member.name); if(tP != null) { final boolean isAdmin=CMSecurity.isASysOp(tP) || tP.level() > CMProps.getIntVar(CMProps.Int.LASTPLAYERLEVEL); members.add(new FullMemberRecord(member,tP.level(),tP.last(),isAdmin)); } else { Log.warnOut("Clan "+clanID()+" removed member '"+member.name+"' due to being nonexistant!"); CMLib.database().DBUpdateClanMembership(member.name, clanID(), -1); } } } } return members; } private String crewList(List<? extends MemberRecord> members, final int posFilter) { final StringBuffer list=new StringBuffer(""); members = filterMemberList(members, posFilter); if(members.size()>1) { for(int j=0;j<(members.size() - 1);j++) list.append(members.get(j).name+", "); list.append("and "+members.get(members.size()-1).name); } else if(members.size()>0) list.append(members.get(0).name); return list.toString(); } @Override public int getNumVoters(final Function function) { int voters=0; final List<MemberRecord> members=getMemberList(); final Function voteFunc = (function == Function.ASSIGN) ? Function.VOTE_ASSIGN : Function.VOTE_OTHER; for(final MemberRecord member : members) { if(getAuthority(member.role, voteFunc)==Authority.CAN_DO) voters++; } return voters; } @Override public List<Integer> getTopRankedRoles(final Function func) { final List<ClanPosition> allRoles=new LinkedList<ClanPosition>(); for(final ClanPosition pos : govt().getPositions()) { if((func==null)||(pos.getFunctionChart()[func.ordinal()]!=Authority.CAN_NOT_DO)) allRoles.add(pos); } final List<Integer> roleIDs=new LinkedList<Integer>(); int topRank=Integer.MAX_VALUE; for(final ClanPosition pos : allRoles) { if(pos.getRank() < topRank) topRank=pos.getRank(); } for(final ClanPosition pos : allRoles) { if(pos.getRank() == topRank) roleIDs.add(Integer.valueOf(pos.getRoleID())); } return roleIDs; } @Override public int getNumberRoles() { return govt().getPositions().length; } @Override public int getTopQualifiedRoleID(final Function func, final MOB mob) { if(mob==null) return govt().getAutoRole(); ClanPosition topPos = null; for(final ClanPosition pos : govt().getPositions()) { if(canBeAssigned(mob,pos.getRoleID()) &&((topPos==null)||(pos.getRank() < topPos.getRank())) &&((func==null)||(getAuthority(pos.getRoleID(),func)!=Authority.CAN_NOT_DO))) topPos = pos; } if(topPos == null) return govt().getAutoRole(); return topPos.getRoleID(); } @Override public int getRoleFromName(String position) { position=position.toUpperCase().trim(); for(final ClanPosition pos : govt().getPositions()) { if(pos.getID().equalsIgnoreCase(position) ||pos.getName().equalsIgnoreCase(position) ||pos.getPluralName().equalsIgnoreCase(position)) return pos.getRoleID(); } for(final ClanPosition pos : govt().getPositions()) { if(pos.getID().toUpperCase().startsWith(position) ||pos.getName().toUpperCase().equalsIgnoreCase(position)) return pos.getRoleID(); } for(final ClanPosition pos : govt().getPositions()) { if((pos.getID().toUpperCase().indexOf(position)>0) ||(pos.getName().toUpperCase().indexOf(position)>0)) return pos.getRoleID(); } return -1; } @Override public boolean isPubliclyListedFor(final MOB mob) { if((!govt().isPublic())&&(mob.getClanRole(clanID())==null)) return false; return true; } @Override public String[] getRolesList() { final List<String> roleNames=new LinkedList<String>(); for(final ClanPosition pos : govt().getPositions()) roleNames.add(pos.getName()); return roleNames.toArray(new String[0]); } @Override public int getMostInRole(final int roleID) { if((roleID<0)||(roleID>=govt().getPositions().length)) return 0; final double most = govt().getPositions()[roleID].getMax(); if(most >= 1.0) return (int)Math.round(most); if(most <= 0) return 1; final double rawMost = CMath.mul(getSize(), most); if(rawMost < 1.0) return 1; return (int)Math.round(rawMost); } @Override public String getRoleName(final int roleID, final boolean titleCase, final boolean plural) { if((roleID<0)||(roleID>=govt().getPositions().length)) return ""; final ClanPosition pos=govt().getPositions()[roleID]; if(plural) { if(!titleCase) return pos.getPluralName().toLowerCase(); else return CMStrings.capitalizeAndLower(pos.getPluralName()); } if(!titleCase) return pos.getName().toLowerCase(); else return CMStrings.capitalizeAllFirstLettersAndLower(pos.getName()); } protected boolean isSafeFromPurge() { if(getMinClanMembers()<=0) return true; final List<String> protectedOnes=Resources.getFileLineVector(Resources.getFileResource("protectedplayers.ini",false)); if((protectedOnes!=null)&&(protectedOnes.size()>0)) { for(int b=0;b<protectedOnes.size();b++) { final String B=protectedOnes.get(b); if(B.equalsIgnoreCase(clanID())) return true; } } return false; } @Override public boolean tick(final Tickable ticking, final int tickID) { if(tickID!=Tickable.TICKID_CLAN) return true; if(CMSecurity.isDisabled(CMSecurity.DisFlag.CLANTICKS)) return true; synchronized(this) { tickUp++; } if(lastPropsReload < CMProps.getLastResetTime()) { lastPropsReload=CMProps.getLastResetTime(); this.overrideMinClanMembers=null; } try { if((tickUp % CMProps.getTicksPerMudHour())==0) { int onlineMembers=0; for(final Iterator<Session> s=CMLib.sessions().sessions();s.hasNext();) { final Session S=s.next(); if(S!=null) { final MOB M=S.mob(); if((M!=null) &&(M.getClanRole(clanID())!=null)) onlineMembers++; } } final long ellapsedMs = System.currentTimeMillis() - this.lastClanTickMs; final int playerMinutes = (int)((onlineMembers * ellapsedMs) / (1000 * 60)); bumpTrophyData(Trophy.MonthlyPlayerMinutes, playerMinutes); bumpTrophyData(Trophy.PlayerMinutes, playerMinutes); this.lastClanTickMs = System.currentTimeMillis(); } // only do the following once per rl day if(tickUp < CMProps.getTicksPerDay()) { return true; } tickUp = 0; final List<FullMemberRecord> members=getFullMemberList(); int activeMembers=0; final long deathMilis=CMProps.getIntVar(CMProps.Int.DAYSCLANDEATH)*CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)*CMProps.getTickMillis(); final long overthrowMilis=CMProps.getIntVar(CMProps.Int.DAYSCLANOVERTHROW)*CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)*CMProps.getTickMillis(); for(final FullMemberRecord member : members) { final long lastLogin=member.lastActiveTimeMs; if(((System.currentTimeMillis()-lastLogin)<deathMilis)||(deathMilis==0)) activeMembers++; } final int minimumMembers = getMinClanMembers(); if(CMSecurity.isDebugging(CMSecurity.DbgFlag.CLANS)) Log.debugOut("DefaultClan","("+clanID()+"): "+activeMembers+"/"+minimumMembers+" active members."); if(activeMembers<minimumMembers) { if(!isSafeFromPurge()) { final long duration=(3L * 24L * 60L * 60L * 1000L); if((System.currentTimeMillis() - this.lastStatusChange)>duration) { if(getStatus()==CLANSTATUS_FADING) { Log.sysOut("Clans","Clan '"+getName()+" deleted with only "+activeMembers+" having logged on lately."); destroyClan(); final StringBuffer buf=new StringBuffer(""); for(final FullMemberRecord member : members) buf.append(member.name+" on "+CMLib.time().date2String(member.lastActiveTimeMs)+" "); Log.sysOut("Clans","Clan '"+getName()+" had the following membership: "+buf.toString()); return true; } setStatus(CLANSTATUS_FADING); final List<Integer> topRoles=getTopRankedRoles(Function.ASSIGN); for(final MemberRecord member : members) { final String name = member.name; final int role=member.role; //long lastLogin=((Long)members.elementAt(j,3)).longValue(); if(topRoles.contains(Integer.valueOf(role))) { if(CMLib.players().playerExists(name)) { CMLib.smtp().emailIfPossible("AutoPurge",CMStrings.capitalizeAndLower(name),"AutoPurge: "+name(), ""+getGovernmentName()+" "+name()+" is in danger of being deleted if at least "+(minimumMembers-activeMembers) +" members do not log on within 24 hours."); } } } Log.sysOut("Clans","Clan '"+getName()+"' fading with only "+activeMembers+" having logged on lately. Will purge on "+CMLib.time().date2String(this.lastStatusChange)+duration); clanAnnounce(""+getGovernmentName()+" "+name()+" is in danger of being deleted if more members do not log on within 24 hours."); update(); } } else if(getStatus()!=CLANSTATUS_ACTIVE) { setStatus(CLANSTATUS_ACTIVE); update(); } } else switch(getStatus()) { case CLANSTATUS_FADING: setStatus(CLANSTATUS_ACTIVE); clanAnnounce(""+getGovernmentName()+" "+name()+" is no longer in danger of being deleted. Be aware that there is required activity level."); update(); break; case CLANSTATUS_PENDING: setStatus(CLANSTATUS_ACTIVE); Log.sysOut("Clans",""+getGovernmentName()+" '"+getName()+" now active with "+activeMembers+"."); clanAnnounce(""+getGovernmentName()+" "+name()+" now has sufficient members. The "+getGovernmentName()+" is now fully approved."); update(); break; default: break; } // handle any necessary promotions if(govt().getAutoPromoteBy() != AutoPromoteFlag.NONE) { // first step is to figure out which positions need filling // 1. if at least one position can assign others, then the highest of those is the answer. // 2. if none can assign others, than each over the applicants is the answer // Algorithm: // Get all the members who qualify by their last login time // Sort them by the qualifying criteria // for each available position, either fill holes (no overwrite), or move them (overwrite) final List<FullMemberRecord> highestQualifiedMembers = new LinkedList<FullMemberRecord>(); for(final FullMemberRecord member : members) { if((member.role<0)||(member.role>=govt().getPositions().length)) continue; // checking if they are only an applicant if((member.role == govt().getAutoRole()) &&(govt().getAcceptPos() != govt().getAutoRole()) &&(govt().getAutoRole() >= 0) &&(govt().getAcceptPos() >= 0) &&(getTopRankedRoles(Function.ACCEPT).size()>0)) continue; final ThinPlayer M = CMLib.database().getThinUser(member.name); if(M==null) continue; if((((System.currentTimeMillis()-member.lastActiveTimeMs)<overthrowMilis)||(overthrowMilis==0)) &&(!highestQualifiedMembers.contains(member))) highestQualifiedMembers.add(member); } if(highestQualifiedMembers.size()==0) { for(final FullMemberRecord member : members) { if((member.role<0)||(member.role>=govt().getPositions().length)) member.role=0; final ThinPlayer M = CMLib.database().getThinUser(member.name); if(M==null) continue; if((((System.currentTimeMillis()-member.lastActiveTimeMs)<overthrowMilis)||(overthrowMilis==0)) &&(!highestQualifiedMembers.contains(member))) highestQualifiedMembers.add(member); } } if(highestQualifiedMembers.size()==0) { for(final FullMemberRecord member : members) { if((member.role<0)||(member.role>=govt().getPositions().length)) member.role=0; final ThinPlayer M = CMLib.database().getThinUser(member.name); if(M==null) continue; highestQualifiedMembers.add(member); } } // now sort them. AutoPromoteFlag basePromoteBy = govt().getAutoPromoteBy(); boolean overWrite = false; // Now, sort the members by the qualifying criteria switch(basePromoteBy) { case LEVEL_OVERWRITE: overWrite=true; basePromoteBy=AutoPromoteFlag.LEVEL; //$FALL-THROUGH$ case LEVEL: Collections.sort(highestQualifiedMembers,new Comparator<FullMemberRecord>() { @Override public int compare(final FullMemberRecord o1, final FullMemberRecord o2) { if(o2.level==o1.level) return 0; if(o2.level<o1.level) return -1; return 1; } }); break; case RANK_OVERWRITE: overWrite=true; basePromoteBy=AutoPromoteFlag.RANK; //$FALL-THROUGH$ case RANK: Collections.sort(highestQualifiedMembers,new Comparator<MemberRecord>() { @Override public int compare(final MemberRecord o1, final MemberRecord o2) { final ClanPosition cp1 = govt().getPositions()[o1.role]; final ClanPosition cp2 = govt().getPositions()[o2.role]; if(cp1.getRank()==cp2.getRank()) return 0; if(cp1.getRank()<cp2.getRank()) return -1; return 1; } }); break; case GOLD_OVERWRITE: overWrite=true; basePromoteBy=AutoPromoteFlag.GOLD; //$FALL-THROUGH$ case GOLD: Collections.sort(highestQualifiedMembers,new Comparator<MemberRecord>() { @Override public int compare(final MemberRecord o1, final MemberRecord o2) { if(o2.donatedGold==o1.donatedGold) return 0; if(o2.donatedGold<o1.donatedGold) return -1; return 1; } }); break; case XP_OVERWRITE: overWrite=true; basePromoteBy=AutoPromoteFlag.XP; //$FALL-THROUGH$ case XP: Collections.sort(highestQualifiedMembers,new Comparator<MemberRecord>() { @Override public int compare(final MemberRecord o1, final MemberRecord o2) { if(o2.donatedXP==o1.donatedXP) return 0; if(o2.donatedXP<o1.donatedXP) return -1; return 1; } }); break; case JOINDATE_OVERWRITE: overWrite=true; basePromoteBy=AutoPromoteFlag.JOINDATE; //$FALL-THROUGH$ case JOINDATE: Collections.sort(highestQualifiedMembers,new Comparator<MemberRecord>() { @Override public int compare(final MemberRecord o1, final MemberRecord o2) { if(o1.joinDate==o2.joinDate) return 0; if(o1.joinDate<o2.joinDate) return -1; return 1; } }); break; case NONE: break; default: break; } // now get all the positions we need to fill // if a position can assign, then that's the winner(s) final List<Integer> highPositionList = getTopRankedRoles(Function.ASSIGN); if(highPositionList.size()==0) { // otherwise, every position that's not an applicant is the winner for(final ClanPosition pos : govt().getPositions()) { if(((pos.getRoleID() == govt().getAutoRole())||(pos.getRoleID() == govt().getAcceptPos())) &&(govt().getAcceptPos() != govt().getAutoRole()) &&(govt().getAutoRole() >= 0) &&(govt().getAcceptPos() >= 0) &&(getTopRankedRoles(Function.ACCEPT).size()>0)) continue; highPositionList.add(Integer.valueOf(pos.getRoleID())); } } Collections.sort(highPositionList, new Comparator<Integer>() { @Override public int compare(final Integer o1, final Integer o2) { final ClanPosition pos1=govt().getPositions()[o1.intValue()]; final ClanPosition pos2=govt().getPositions()[o2.intValue()]; if(pos1.getRank()==pos2.getRank()) return 0; if(pos1.getRank()<pos2.getRank()) return -1; return 1; } }); final HashMap<String,Reference<ThinPlayer>> thinCache = new HashMap<String,Reference<ThinPlayer>>(); final boolean fillAll = getTopRankedRoles(Function.ASSIGN).size()==0; // finally we fill the positions // if we are overwriting, then we always pick the best people who fit and // kick out the rest. If we are NOT overwriting, then we are only filling // holes for when inactivity drops one out. final Map<FullMemberRecord,Integer> finalHighRollers = new HashMap<FullMemberRecord,Integer>(); for(final Integer highPosI : highPositionList) { final int highRoleID = highPosI.intValue(); int most=getMostInRole(highRoleID); if(most>getSize()) most=getSize(); int numToAdd = 0; if(!overWrite) { int current = 0; for(final Iterator<FullMemberRecord> i=highestQualifiedMembers.iterator();i.hasNext();) { final FullMemberRecord M=i.next(); if(M.role == highRoleID) { i.remove(); finalHighRollers.put(M, highPosI); current++; } } numToAdd = most-current; if(!fillAll) { if(current>0) numToAdd=0; else if(numToAdd>0) numToAdd=1; } } else { numToAdd = most; if(!fillAll) { for(final Iterator<FullMemberRecord> i=highestQualifiedMembers.iterator();i.hasNext();) { final FullMemberRecord M=i.next(); if(M.role == highRoleID) { i.remove(); finalHighRollers.put(M, highPosI); numToAdd=0; } } if(numToAdd>0) numToAdd=1; } } for(int i=0;i<numToAdd;i++) { for(int s=0;s<highestQualifiedMembers.size();s++) { final FullMemberRecord M=highestQualifiedMembers.get(s); if(!thinCache.containsKey(M.name)) thinCache.put(M.name, new WeakReference<ThinPlayer>(CMLib.database().getThinUser(M.name))); final ThinPlayer tP=thinCache.get(M.name).get(); if((tP!=null) &&(canBeAssigned(tP, highRoleID))) { highestQualifiedMembers.remove(s); finalHighRollers.put(M, highPosI); break; } } } } final ClanPosition acceptRole=govt().getPositions()[govt().getAcceptPos()]; final Map<FullMemberRecord,Integer> finalRollers = finalHighRollers; for(final FullMemberRecord member : members) { if(highPositionList.contains(Integer.valueOf(member.role)) &&(!finalRollers.containsKey(member))) { boolean someoneIsTakingMyPlace = false; for(final FullMemberRecord R : finalRollers.keySet()) { if((finalRollers.get(R).intValue() == member.role) &&(R.role != member.role)) someoneIsTakingMyPlace=true; } if(someoneIsTakingMyPlace) finalRollers.put(member, Integer.valueOf(acceptRole.getRoleID())); } } for(final FullMemberRecord member : members) { if(finalRollers.containsKey(member)) { final Integer newRoleI = finalRollers.get(member); if(newRoleI.intValue() != member.role) { final ClanPosition oldPos = govt().getPositions()[member.role]; final ClanPosition newPos = govt().getPositions()[newRoleI.intValue()]; final MOB mob=CMLib.players().getPlayerAllHosts(member.name); clanAnnounce(member.name+" is now a "+newPos.getName()+" of the "+getGovernmentName()+" "+name()+"."); if(oldPos.getRank() < newPos.getRank()) Log.sysOut("Clans",member.name+" of "+getGovernmentName()+" "+name()+" was auto-demoted to "+newPos.getName()+"."); else if(oldPos.getRank() > newPos.getRank()) Log.sysOut("Clans",member.name+" of "+getGovernmentName()+" "+name()+" was auto-promoted to "+newPos.getName()+"."); else Log.sysOut("Clans",member.name+" of "+getGovernmentName()+" "+name()+" was auto-assigned to "+newPos.getName()+"."); if((mob!=null) &&(mob.getClanRole(clanID())!=null)) mob.setClan(clanID(),newPos.getRoleID()); member.role=newPos.getRoleID(); CMLib.database().DBUpdateClanMembership(member.name, name(), newPos.getRoleID()); } } } final List<MemberRecord> highMembers=new LinkedList<MemberRecord>(); for(final FullMemberRecord member : members) { if((((System.currentTimeMillis()-member.lastActiveTimeMs)<deathMilis)||(deathMilis==0)) &&(highPositionList.contains(Integer.valueOf(member.role)))) highMembers.add(member); } if(highMembers.size()==0) { if(!isSafeFromPurge()) { Log.sysOut("Clans","Clan '"+getName()+" deleted for lack of leadership."); destroyClan(); final StringBuffer buf=new StringBuffer(""); for(final FullMemberRecord member : members) buf.append(member.name+" on "+CMLib.time().date2String(member.lastActiveTimeMs)+" "); Log.sysOut("Clans","Clan '"+getName()+" had the following membership: "+buf.toString()); return true; } } } boolean anyVoters = false; for(final ClanPosition pos : govt().getPositions()) { if((pos.getFunctionChart()[Function.VOTE_ASSIGN.ordinal()]==Clan.Authority.CAN_DO) ||(pos.getFunctionChart()[Function.VOTE_OTHER.ordinal()]==Clan.Authority.CAN_DO)) anyVoters=true; } // now do votes if(anyVoters&&(votes()!=null)) { boolean updateVotes=false; final Vector<ClanVote> votesToRemove=new Vector<ClanVote>(); long duration=govt().getMaxVoteDays(); if(duration<=0) duration=54; duration=duration*CMProps.getIntVar(CMProps.Int.TICKSPERMUDDAY)*CMProps.getTickMillis(); for(final Enumeration<ClanVote> e=votes();e.hasMoreElements();) { final ClanVote CV=e.nextElement(); final int numVotes=getNumVoters(Function.values()[CV.function]); int quorum=govt().getVoteQuorumPct(); quorum=(int)Math.round(CMath.mul(CMath.div(quorum,100.0),numVotes)); if(quorum<2) quorum=2; if(numVotes==1) quorum=1; final long endsOn=CV.voteStarted+duration; if(CV.voteStatus==VSTAT_STARTED) { if(CV.votes==null) CV.votes=new PairVector<String,Boolean>(); boolean voteIsOver=false; if(System.currentTimeMillis()>endsOn) voteIsOver=true; else if(CV.votes.size()==numVotes) voteIsOver=true; if(voteIsOver) { CV.voteStarted=System.currentTimeMillis(); updateVotes=true; if(CV.votes.size()<quorum) CV.voteStatus=VSTAT_FAILED; else { int yeas=0; int nays=0; for(int i=0;i<CV.votes.size();i++) { if(CV.votes.getSecond(i).booleanValue()) yeas++; else nays++; } if(yeas<=nays) CV.voteStatus=VSTAT_FAILED; else { CV.voteStatus=VSTAT_PASSED; final MOB mob=CMClass.getFactoryMOB(); mob.setName(clanID()); mob.setClan(clanID(),getTopRankedRoles(Function.values()[CV.function]).get(0).intValue()); mob.basePhyStats().setLevel(1000); if(mob.location()==null) { mob.setLocation(mob.getStartRoom()); if(mob.location()==null) mob.setLocation(CMLib.map().getRandomRoom()); } final Vector<String> V=CMParms.parse(CV.matter); mob.doCommand(V,MUDCmdProcessor.METAFLAG_FORCED); mob.destroy(); } } } } else if(System.currentTimeMillis()>endsOn) { updateVotes=true; votesToRemove.addElement(CV); } } for(int v=0;v<votesToRemove.size();v++) delVote(votesToRemove.elementAt(v)); if(updateVotes) updateVotes(); } update(); // also saves exp, and trophies CMLib.database().DBUpdateClanItems(this); } catch(final Exception x2) { Log.errOut("Clans",x2); } return true; } @Override public boolean doesOutRank(final int highRoleID, final int lowRoleID) { final ClanGovernment govt=govt(); if((highRoleID == lowRoleID) ||(highRoleID < 0) ||(highRoleID >= govt.getPositions().length)) return false; if((lowRoleID<0) ||(lowRoleID >= govt.getPositions().length)) return true; return govt.getPositions()[highRoleID].getRank() < govt.getPositions()[lowRoleID].getRank(); } @Override public void clanAnnounce(final String msg) { if(channelSet.size()==0) { synchronized(channelSet) { if(channelSet.size()==0) channelSet.add(new Pair<Clan,Integer>(this,Integer.valueOf(getGovernment().getAcceptPos()))); } } final List<String> channels=CMLib.channels().getFlaggedChannelNames(ChannelsLibrary.ChannelFlag.CLANINFO, null); for(int i=0;i<channels.size();i++) CMLib.commands().postChannel(channels.get(i),channelSet,msg,true); } private static final SearchIDList<Ability> emptyAbles =new CMUniqSortSVec<Ability>(1); @Override public SearchIDList<Ability> clanAbilities(final MOB mob) { final Pair<Clan,Integer> p=(mob!=null)?mob.getClanRole(clanID()):null; if((mob==null)||((p!=null)&&(getAuthority(p.second.intValue(),Function.CLAN_BENEFITS)!=Clan.Authority.CAN_NOT_DO))) return govt().getClanLevelAbilities(mob,this,Integer.valueOf(getClanLevel())); return emptyAbles; } @Override public int numClanEffects(final MOB mob) { return govt().getClanLevelEffects(mob, this, Integer.valueOf(getClanLevel())).size(); } @Override public ChameleonList<Ability> clanEffects(final MOB mob) { return govt().getClanLevelEffects(mob,this, Integer.valueOf(getClanLevel())); } @Override public int applyExpMods(final MOB memberM, int exp) { boolean changed=false; if((getTaxes()>0.0)&&(exp>1)) { final int clanshare=(int)Math.round(CMath.mul(exp,getTaxes())); if(clanshare>0) { exp-=clanshare; adjExp(memberM, clanshare); changed=true; } } // player xp punished for taxes, but not awarded for trophies bumpTrophyData(Trophy.MonthlyPlayerXP, exp); if(getTrophies() != 0) { for(final Trophy t : Trophy.values()) { if(CMath.bset(getTrophies(),t.flagNum())) exp = CMLib.clans().adjustXPAward(t, exp); } } if(changed) update(); return exp; } @Override public MOB getResponsibleMember() { final List<MemberRecord> members=getMemberList(); final List<Integer> topRoles=getTopRankedRoles(null); MOB respMember = null; final int level = -1; for(final MemberRecord member : members) { if(topRoles.contains(Integer.valueOf(member.role))) { final MOB M=CMLib.players().getLoadPlayer(member.name); if((M!=null)&&(M.basePhyStats().level() > level)) respMember = M; } } if(respMember != null) return respMember; String memberName = null; ClanPosition newPos=null; for(final MemberRecord member : members) { if((member.role<govt().getPositions().length) &&(member.role>=0) &&((newPos==null)||(govt().getPositions()[member.role].getRank()<newPos.getRank()))) { newPos = govt().getPositions()[member.role]; memberName = member.name; } } if(memberName != null) return CMLib.players().getLoadPlayer(memberName); return null; } @Override public ItemCollection getExtItems() { return extItems; } /** Manipulation of the tatoo list */ @Override public void addTattoo(final String of) { final Tattoo T=(Tattoo)CMClass.getCommon("DefaultTattoo"); addTattoo(T.set(of)); } @Override public void addTattoo(final String of, final int tickDown) { final Tattoo T=(Tattoo)CMClass.getCommon("DefaultTattoo"); addTattoo(T.set(of,tickDown)); } @Override public void delTattoo(final String of) { final Tattoo T=findTattoo(of); if(T!=null) tattoos.remove(T); } @Override public void addTattoo(final Tattoo of) { if ((of == null) || (of.getTattooName() == null) || (of.getTattooName().length() == 0) || findTattoo(of.getTattooName()) != null) return; tattoos.addElement(of); } @Override public void delTattoo(final Tattoo of) { if ((of == null) || (of.getTattooName() == null) || (of.getTattooName().length() == 0)) return; final Tattoo tat = findTattoo(of.getTattooName()); if (tat == null) return; tattoos.remove(tat); } @Override public Enumeration<Tattoo> tattoos() { return tattoos.elements(); } @Override public Tattoo findTattoo(final String of) { if ((of == null) || (of.length() == 0)) return null; return tattoos.find(of.trim()); } @Override public Tattoo findTattooStartsWith(final String of) { if ((of == null) || (of.length() == 0)) return null; return tattoos.findStartsWith(of.trim()); } @Override public void killAchievementTracker(final Achievement A, final Tattooable C, final MOB mob) { if(achievementers.containsKey(A.getTattoo())) { achievementers.remove(A.getTattoo()); } } @Override public Tracker getAchievementTracker(final Achievement A, final Tattooable C, final MOB mob) { final Tracker T; if(achievementers.containsKey(A.getTattoo())) { T=achievementers.get(A.getTattoo()); } else { T=A.getTracker(0); achievementers.put(A.getTattoo(), T); } return T; } @Override public void rebuildAchievementTracker(final Tattooable C, final MOB mob, final String achievementTattoo) { final Achievement A=CMLib.achievements().getAchievement(achievementTattoo); if(A!=null) { if(achievementers.containsKey(A.getTattoo())) achievementers.put(A.getTattoo(), A.getTracker(achievementers.get(A.getTattoo()).getCount(C))); else achievementers.put(A.getTattoo(), A.getTracker(0)); } else achievementers.remove(achievementTattoo); } /** Stat variables associated with clan objects. */ private final static String[] CLAN_STATS={ "ACCEPTANCE", // 0 "DETAIL", // 1 "DONATEROOM", // 2 "EXP", // 3 "GOVT", // 4 "MORGUE", // 5 "POLITICS", // 6 "PREMISE", // 7 "RECALL", // 8 "SIZE", // 9 "STATUS", // 10 "TAXES", // 11 "TROPHIES", // 12 "TYPE", // 13 "AREAS", // 14 "MEMBERLIST", // 15 "TOPMEMBER", // 16 "CLANLEVEL", // 17 "CATEGORY", // 18 "RIVALROUS",//19 "MINMEMBERS", //20 "CLANCHARCLASS", // 21 "NAME", // 22 "FLAGS" // 23 }; @Override public String[] getStatCodes() { return CLAN_STATS; } @Override public int getSaveStatIndex() { return CLAN_STATS.length; } @Override public boolean isStat(final String code) { return CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0; } @Override public String getStat(final String code) { final int dex=CMParms.indexOf(getStatCodes(),code.toUpperCase().trim()); if(dex<0) return ""; switch(dex) { case 0: return getAcceptanceSettings(); case 1: return getDetail(null); case 2: return getDonation(); case 3: return "" + getExp(); case 4: return "" + getGovernmentName(); case 5: return getMorgue(); case 6: return getDataXML(); case 7: return getPremise(); case 8: return getRecall(); case 9: return "" + getSize(); case 10: return Clan.CLANSTATUS_DESC[getStatus()]; case 11: return "" + getTaxes(); case 12: return "" + getTrophies(); case 13: return "0"; case 14: { final List<Area> areas = getControlledAreas(); final StringBuffer list = new StringBuffer(""); for (int i = 0; i < areas.size(); i++) list.append("\"" + areas.get(i).name() + "\" "); return list.toString().trim(); } case 15: { final List<MemberRecord> members = getMemberList(); final StringBuffer list = new StringBuffer(""); for (final MemberRecord member : members) list.append("\"" + member.name + "\" "); return list.toString().trim(); } case 16: { final MOB M = getResponsibleMember(); if (M != null) return M.Name(); return ""; } case 17: return Integer.toString(getClanLevel()); case 18: return "" + getCategory(); case 19: return "" + isRivalrous(); case 20: return "" + getMinClanMembers(); case 21: return "" + getClanClass(); case 22: return "" + getName(); case 23: return CMParms.toListString(clanFlags); } return ""; } @Override public void setStat(final String code, final String val) { final int dex=CMParms.indexOf(getStatCodes(),code.toUpperCase().trim()); if(dex<0) return; switch(dex) { case 0: setAcceptanceSettings(val); break; case 1: break; // detail case 2: setDonation(val); break; case 3: setExp(CMath.s_long(val.trim())); break; case 4: setGovernmentID(CMath.s_int(val.trim())); break; case 5: setMorgue(val); break; case 6: setDataXML(val); break; case 7: setPremise(val); break; case 8: setRecall(val); break; case 9: break; // size case 10: if(CMath.s_int(val.trim())!=getStatus()) setStatus(CMath.s_int(val.trim())); break; case 11: setTaxes(CMath.s_double(val.trim())); break; case 12: setTrophies(CMath.s_int(val.trim())); break; case 13: break; // type case 14: break; // areas case 15: break; // memberlist case 16: break; // topmember case 17: setClanLevel(CMath.s_int(val.trim())); break; // clanlevel case 18: setCategory(val.trim()); break; // clancategory case 19: setRivalrous(CMath.s_bool(val.trim())); break; // isrivalrous case 20: setMinClanMembers(CMath.s_int(val.trim())); break; // minmembers case 21: setClanClass(val.trim()); break; // clancharclass case 22: this.setName(val.trim()); break; // name case 23: { clanFlags = new SHashSet<ClanFlag>(); for(final String s : CMParms.parseCommas(val.toUpperCase(),true)) { final ClanFlag flag = (ClanFlag)CMath.s_valueOf(ClanFlag.class, s); if(flag != null) clanFlags.add(flag); } break; } } } }