package com.planet_ink.coffee_mud.Libraries;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
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.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.Faction.FRange;
import com.planet_ink.coffee_mud.Common.interfaces.Faction.FactionChangeEvent;
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.io.IOException;
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 Factions extends StdLibrary implements FactionManager
{
@Override
public String ID()
{
return "Factions";
}
public SHashtable<String, Faction> factionMap = new SHashtable<String, Faction>();
public SHashtable<String, FRange> hashedFactionRanges = new SHashtable<String, FRange>();
public Map<Faction.Align, List<FRange>> hashedFactionAligns = new SHashtable<Faction.Align, List<FRange>>();
@Override
public Enumeration<Faction> factions()
{
return factionMap.elements();
}
@Override
public int numFactions()
{
return factionMap.size();
}
@Override
public void clearFactions()
{
factionMap.clear();
hashedFactionRanges.clear();
hashedFactionAligns.clear();
}
@Override
public Faction getFactionByNumber(final int index)
{
final Enumeration<Faction> fe=factions();
Faction F=null;
for(int facIndex=0; (facIndex<=index) && fe.hasMoreElements(); facIndex++)
F=fe.nextElement();
return F;
}
@Override
public void reloadFactions(final String factionList)
{
final List<String> preLoadFactions=CMParms.parseSemicolons(factionList,true);
clearFactions();
for(int i=0;i<preLoadFactions.size();i++)
getFaction(preLoadFactions.get(i));
}
public java.util.Map<String, FRange> rangeCodeNames()
{
return hashedFactionRanges;
}
@Override
public boolean isRangeCodeName(final String key)
{
return rangeCodeNames().containsKey(key.toUpperCase());
}
@Override
public FRange getFactionRangeByCodeName(final String rangeCodeName)
{
if(hashedFactionRanges.containsKey(rangeCodeName.toUpperCase()))
return hashedFactionRanges.get(rangeCodeName.toUpperCase());
return null;
}
@Override
public boolean isFactionedThisWay(final MOB mob, final Faction.FRange rangeCode)
{
final Faction.FRange FR=rangeCode;
if(FR==null)
return false;
final Faction F=rangeCode.getFaction();
final Faction.FRange FR2=F.fetchRange(mob.fetchFaction(F.factionID()));
if(FR2==null)
return false;
return FR2.codeName().equalsIgnoreCase(FR.codeName());
}
@Override
public String rangeDescription(final Faction.FRange FR, final String andOr)
{
if(FR==null)
return "";
final Faction F=FR.getFaction();
final Vector<FRange> relevantFactions=new Vector<FRange>();
for(final Enumeration<FRange> e=F.ranges();e.hasMoreElements();)
{
final Faction.FRange FR2=e.nextElement();
if(FR2.codeName().equalsIgnoreCase(FR.codeName()))
relevantFactions.addElement(FR2);
}
if(relevantFactions.size()==0)
return "";
if(relevantFactions.size()==1)
return F.name()+" of "+relevantFactions.firstElement().name();
final StringBuffer buf=new StringBuffer(F.name()+" of ");
for(int i=0;i<relevantFactions.size()-1;i++)
buf.append(relevantFactions.elementAt(i).name()+", ");
buf.append(andOr+relevantFactions.lastElement().name());
return buf.toString();
}
private Faction buildFactionFromXML(final StringBuffer buf, final String factionID)
{
final Faction F=(Faction)CMClass.getCommon("DefaultFaction");
F.initializeFaction(buf,factionID);
for(final Enumeration<FRange> e=F.ranges();e.hasMoreElements();)
{
final Faction.FRange FR=e.nextElement();
final String codeName=(FR.codeName().length()>0)?FR.codeName().toUpperCase():FR.name().toUpperCase();
if(!hashedFactionRanges.containsKey(codeName))
hashedFactionRanges.put(codeName,FR);
final String simpleUniqueCodeName = F.name().toUpperCase()+"."+FR.name().toUpperCase();
if(!hashedFactionRanges.containsKey(simpleUniqueCodeName))
hashedFactionRanges.put(simpleUniqueCodeName,FR);
final String uniqueCodeName = simpleUniqueCodeName.replace(' ','_');
if(!hashedFactionRanges.containsKey(uniqueCodeName))
hashedFactionRanges.put(uniqueCodeName,FR);
final String simpleUniqueIDName = F.factionID().toUpperCase()+"."+FR.name().toUpperCase();
if(!hashedFactionRanges.containsKey(simpleUniqueIDName))
hashedFactionRanges.put(simpleUniqueIDName,FR);
if(FR.alignEquiv() != null)
{
if(!hashedFactionAligns.containsKey(FR.alignEquiv()))
hashedFactionAligns.put(FR.alignEquiv(), new ArrayList<FRange>());
hashedFactionAligns.get(FR.alignEquiv()).add(FR);
}
}
return addFaction(F) ? F : null;
}
@Override
public boolean addFaction(final Faction F)
{
if(F!=null)
{
F.disable(CMSecurity.isFactionDisabled(F.factionID().toUpperCase().trim()));
factionMap.put(F.factionID().toUpperCase().trim(),F);
return !F.isDisabled();
}
return false;
}
@Override
public String makeFactionFilename(final String factionID)
{
String filename;
filename="factions/"+factionID+".ini";
if(new CMFile(Resources.makeFileResourceName(filename),null).exists())
return filename;
filename="factions/"+factionID;
if(new CMFile(Resources.makeFileResourceName(filename),null).exists())
return filename;
filename=factionID+".ini";
if(new CMFile(Resources.makeFileResourceName(filename),null).exists())
return filename;
if(new CMFile(Resources.makeFileResourceName(factionID),null).exists())
return factionID;
filename=factionID;
if(filename.indexOf('/')<0)
filename="factions/"+factionID;
if(!filename.toLowerCase().endsWith(".ini"))
filename=filename+".ini";
return filename;
}
@Override
public boolean isFactionID(String key)
{
if(key==null)
return false;
key=key.toUpperCase().trim();
if(factionMap.containsKey(key))
return true;
final CMFile f=new CMFile(Resources.makeFileResourceName(makeFactionFilename(key)),null,CMFile.FLAG_LOGERRORS);
if(f.exists())
return getFaction(key)!=null;
return false;
}
@Override
public boolean isFactionLoaded(String key)
{
if(key==null)
return false;
key=key.toUpperCase().trim();
if(factionMap.containsKey(key))
return true;
if((!key.endsWith(".INI"))
&&(factionMap.containsKey(key+".INI")))
return true;
return false;
}
@Override
public boolean isAlignmentLoaded(final Faction.Align align)
{
if(align==null)
return false;
return hashedFactionAligns.containsKey(align)
&& (hashedFactionAligns.get(align).size()>0);
}
@Override
public Faction getFaction(final String factionID)
{
if(factionID==null)
return null;
Faction F=factionMap.get(factionID.toUpperCase());
if((F==null)
&&(!factionID.toLowerCase().endsWith(".ini")))
{
F=getFaction(factionID+".ini");
}
if(F!=null)
{
if(F.isDisabled())
return null;
if(!F.amDestroyed())
return F;
factionMap.remove(F.factionID().toUpperCase());
Resources.removeResource(F.factionID());
return null;
}
if(CMSecurity.isFactionDisabled(factionID))
return null;
final CMFile f=new CMFile(Resources.makeFileResourceName(makeFactionFilename(factionID)),null,CMFile.FLAG_LOGERRORS);
if(!f.exists())
return null;
final StringBuffer buf=f.text();
if((buf!=null)&&(buf.length()>0))
{
final Faction newF=buildFactionFromXML(buf, factionID);
if(newF != null)
factionMap.put(factionID.toUpperCase(), newF);
return newF;
}
return null;
}
@Override
public Faction getFactionByRangeCodeName(final String rangeCodeName)
{
if(hashedFactionRanges.containsKey(rangeCodeName.toUpperCase()))
{
final Faction.FRange FR=hashedFactionRanges.get(rangeCodeName.toUpperCase());
if(FR!=null)
{
final Faction F=FR.getFaction();
if(!F.amDestroyed())
return F;
factionMap.remove(F.factionID().toUpperCase());
Resources.removeResource(F.factionID());
return null;
}
}
return null;
}
@Override
public Faction getFactionByName(final String factionNamed)
{
Faction F;
for(final Enumeration<String> e=factionMap.keys();e.hasMoreElements();)
{
F=factionMap.get(e.nextElement());
if(F.name().equalsIgnoreCase(factionNamed))
{
if(!F.amDestroyed())
return F;
factionMap.remove(F.factionID().toUpperCase());
Resources.removeResource(F.factionID());
return null;
}
}
return null;
}
@Override
public boolean removeFaction(final String factionID)
{
Faction F;
if(factionID==null)
{
for(final Enumeration<Faction> e=factionMap.elements();e.hasMoreElements();)
{
F=e.nextElement();
if(F!=null)
removeFaction(F.factionID());
}
return true;
}
F=getFactionByName(factionID);
if(F==null)
F=getFaction(factionID);
if(F==null)
return false;
Resources.removeResource(F.factionID());
factionMap.remove(F.factionID().toUpperCase());
return true;
}
@Override
public String listFactions()
{
final StringBuffer msg=new StringBuffer();
msg.append("\n\r^.^N");
msg.append("+--------------------------------+-----------------------------------------+\n\r");
msg.append(L("| ^HFaction Name^N | ^HFaction INI Source File (Faction ID)^N |\n\r"));
msg.append("+--------------------------------+-----------------------------------------+\n\r");
final XVector<Faction> sorted = new XVector<Faction>(factionMap.elements());
Collections.sort(sorted,new Comparator<Faction>()
{
@Override
public int compare(final Faction o1, final Faction o2)
{
return o1.name().compareTo(o2.name());
}
});
for(final Enumeration<Faction> e=sorted.elements();e.hasMoreElements();)
{
final Faction f=e.nextElement();
msg.append("| ");
msg.append(CMStrings.padRight(f.name(),30));
msg.append(" | ");
msg.append(CMStrings.padRight(f.factionID(),39));
msg.append(" |\n\r");
}
msg.append("+--------------------------------+-----------------------------------------+\n\r");
msg.append("\n\r");
return msg.toString();
}
@Override
public String name()
{
return "Factions";
}
@Override
public int getTickStatus()
{
return Tickable.STATUS_NOT;
}
@Override
public String getName(final String factionID)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.name();
return "";
}
@Override
public int getMinimum(final String factionID)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.minimum();
return 0;
}
@Override
public int getMaximum(final String factionID)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.maximum();
return 0;
}
@Override
public int getPercent(final String factionID, final int faction)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.asPercent(faction);
return 0;
}
@Override
public int getPercentFromAvg(final String factionID, final int faction)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.asPercentFromAvg(faction);
return 0;
}
@Override
public Faction.FRange getRange(final String factionID, final int faction)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.fetchRange(faction);
return null;
}
@Override
public Enumeration<Faction.FRange> getRanges(final String factionID)
{
final Faction f=getFaction(factionID);
if(f!=null)
return f.ranges();
return null;
}
@Override
public double getRangePercent(final String factionID, final int faction)
{
final Faction F=getFaction(factionID);
if(F==null)
return 0.0;
return CMath.div((int)Math.round(CMath.div((faction - F.minimum()),(F.maximum() - F.minimum())) * 10000.0),100.0);
}
@Override
public int getTotal(final String factionID)
{
final Faction f = getFaction(factionID);
if (f != null)
return (f.maximum() - f.minimum());
return 0;
}
@Override
public int getRandom(final String factionID)
{
final Faction f = getFaction(factionID);
if (f != null)
return f.randomFaction();
return 0;
}
@Override
public String getAlignmentID()
{
return "alignment.ini";
}
@Override
public String getInclinationID()
{
return "inclination.ini";
}
@Override
public void setAlignment(final MOB mob, final Faction.Align newAlignment)
{
if(factionMap.containsKey(this.getAlignmentID().toUpperCase()))
mob.addFaction(getAlignmentID(),getAlignMedianFacValue(newAlignment));
}
@Override
public void setAlignmentOldRange(final MOB mob, final int oldRange)
{
if(factionMap.containsKey(this.getAlignmentID().toUpperCase()))
{
if(oldRange>=650)
setAlignment(mob,Faction.Align.GOOD);
else
if(oldRange>=350)
setAlignment(mob,Faction.Align.NEUTRAL);
else
if(oldRange>=0)
setAlignment(mob,Faction.Align.EVIL);
else{ /* a -1 value is the new norm */}
}
}
@Override
public boolean postChangeAllFactions(final MOB mob, final MOB victim, final int amount, final boolean quiet)
{
if((mob==null))
return false;
final CMMsg msg=CMClass.getMsg(mob,victim,null,CMMsg.MASK_ALWAYS|CMMsg.TYP_FACTIONCHANGE,null,CMMsg.NO_EFFECT,null,CMMsg.NO_EFFECT,""+quiet);
msg.setValue(amount);
if(mob.location()!=null)
{
if(mob.location().okMessage(mob,msg))
mob.location().send(mob,msg);
else
return false;
}
return true;
}
@Override
public boolean postFactionChange(final MOB mob,final Environmental tool, final String factionID, final int amount)
{
if((mob==null))
return false;
final CMMsg msg=CMClass.getMsg(mob,null,tool,CMMsg.MASK_ALWAYS|CMMsg.TYP_FACTIONCHANGE,null,CMMsg.NO_EFFECT,null,CMMsg.NO_EFFECT,factionID);
msg.setValue(amount);
if(mob.location()!=null)
{
if(mob.location().okMessage(mob,msg))
mob.location().send(mob,msg);
else
return false;
}
return true;
}
protected Faction makeReactionFaction(final String prefix, final String classID, final String Name, final String code, final String baseTemplateFilename)
{
final String codedName=Name.toUpperCase().trim().replace(' ','_');
final String factionID=prefix+codedName;
Faction templateF=getFaction(this.makeFactionFilename(codedName.toLowerCase()));
if(templateF==null)
templateF=getFaction(baseTemplateFilename);
if(templateF==null)
{
Log.errOut("Factions","Could not find base template '"+baseTemplateFilename+"'");
return null;
}
StringBuffer buf = rebuildFactionProperties(templateF);
factionMap.remove(templateF.factionID().toUpperCase().trim());
String bufStr = buf.toString();
bufStr = CMStrings.replaceAll(bufStr,"<CODE>",code);
bufStr = CMStrings.replaceAll(bufStr,"<NAME>",Name);
bufStr = CMStrings.replaceAll(bufStr,"<FACTIONID>",factionID);
bufStr = CMStrings.replaceAll(bufStr,"<CLASSID>",classID);
buf=new StringBuffer(bufStr);
final Faction F=buildFactionFromXML(buf, factionID);
F.setInternalFlags(F.getInternalFlags() | Faction.IFLAG_IGNOREAUTO);
F.setInternalFlags(F.getInternalFlags() | Faction.IFLAG_NEVERSAVE);
F.setInternalFlags(F.getInternalFlags() | Faction.IFLAG_CUSTOMTICK);
return addFaction(F) ? F : null;
}
public Faction[] getSpecialFactions(final MOB mob, final Room R)
{
if((mob==null)||(R==null))
return null;
Faction F=null;
final String autoReactionTypeStr=CMProps.getVar(CMProps.Str.AUTOREACTION).toUpperCase().trim();
if((autoReactionTypeStr==null)||(autoReactionTypeStr.length()==0))
return null;
if(autoReactionTypeStr.equals("AREA"))
{
final Area A=R.getArea();
if((A!=null)&&(!CMath.bset(A.flags(), Area.FLAG_INSTANCE_CHILD)))
{
final String areaCode = A.Name().toUpperCase().trim().replace(' ','_');
F=getFaction("AREA_"+areaCode);
if(F==null)
F=makeReactionFaction("AREA_",A.ID(),A.Name(),areaCode,"examples/areareaction.ini");
if(F==null)
return null;
return new Faction[]{F};
}
}
else
if(autoReactionTypeStr.equals("NAME"))
{
final Vector<Faction> Fs=new Vector<Faction>();
for(int i=0;i<R.numInhabitants();i++)
{
final MOB M=R.fetchInhabitant(i);
if((M!=null)&&(M!=mob)&&(M.isMonster()))
{
final String nameCode = M.Name().toUpperCase().trim().replace(' ','_');
F=getFaction("NAME_"+nameCode);
if(F==null)
F=makeReactionFaction("NAME_",M.ID(),M.Name(),nameCode,"examples/namereaction.ini");
if(F!=null)
Fs.add(F);
}
}
if(Fs.size()==0)
return null;
return Fs.toArray(new Faction[0]);
}
else
if(autoReactionTypeStr.equals("RACE"))
{
final Vector<Faction> Fs=new Vector<Faction>();
final HashSet<Race> done=new HashSet<Race>(2);
for(int i=0;i<R.numInhabitants();i++)
{
final MOB M=R.fetchInhabitant(i);
if((M!=null)&&(M!=mob)&&(M.isMonster())&&(!done.contains(M.charStats().getMyRace())))
{
final Race rR=M.charStats().getMyRace();
done.add(rR);
final String nameCode = rR.name().toUpperCase().trim().replace(' ','_');
F=getFaction("RACE_"+nameCode);
if(F==null)
F=makeReactionFaction("RACE_",rR.ID(),rR.name(),nameCode,"examples/racereaction.ini");
if(F!=null)
Fs.add(F);
}
}
if(Fs.size()==0)
return null;
return Fs.toArray(new Faction[0]);
}
return null;
}
@Override
public void updatePlayerFactions(final MOB mob, final Room R, final boolean forceAutoCheck)
{
if((mob==null)||(R==null))
return;
else
{
Faction F=null;
for(final Enumeration<Faction> e=factions();e.hasMoreElements();)
{
F=e.nextElement();
if(F!=null)
{
if(((forceAutoCheck || (F.getInternalFlags()&Faction.IFLAG_IGNOREAUTO)==0))
&&(!F.hasFaction(mob))
&&(F.findAutoDefault(mob)!=Integer.MAX_VALUE))
mob.addFaction(F.factionID(),F.findAutoDefault(mob));
}
}
}
final Faction[] Fs=getSpecialFactions(mob,R);
if(Fs!=null)
{
for(final Faction F : Fs)
{
if((F!=null)&&(!F.hasFaction(mob))&&(F.findAutoDefault(mob)!=Integer.MAX_VALUE))
mob.addFaction(F.factionID(),F.findAutoDefault(mob));
}
}
}
protected void addOutsidersAndTimers(final Faction F, final Vector<Faction.FactionChangeEvent> outSiders, final Vector<Faction.FactionChangeEvent> timers)
{
Faction.FactionChangeEvent[] CEs=null;
CEs=F.getChangeEvents("ADDOUTSIDER");
if(CEs!=null)
{
for (final FactionChangeEvent ce : CEs)
outSiders.addElement(ce);
}
CEs=F.getChangeEvents("TIME");
if(CEs!=null)
{
for (final FactionChangeEvent ce : CEs)
{
if(ce.triggerParameters().length()==0)
timers.addElement(ce);
else
{
int[] ctr=(int[])ce.stateVariable(0);
if(ctr==null)
{
ctr=new int[]{CMath.s_int(ce.getTriggerParm("ROUNDS"))};
ce.setStateVariable(0,ctr);
}
if((--ctr[0])<=0)
{
ctr[0]=CMath.s_int(ce.getTriggerParm("ROUNDS"));
timers.addElement(ce);
}
}
}
}
}
@Override
public boolean tick(final Tickable ticking, final int tickID)
{
if(!CMLib.sessions().sessions().hasNext())
return true;
try
{
MOB mob=null;
Faction F=null;
Faction.FactionChangeEvent CE=null;
final Vector<Faction.FactionChangeEvent> outSiders=new Vector<Faction.FactionChangeEvent>();
final Vector<Faction.FactionChangeEvent> timers=new Vector<Faction.FactionChangeEvent>();
final HashSet<Faction> factionsDone=new HashSet<Faction>();
for(final Enumeration<Faction> e=factionMap.elements();e.hasMoreElements();)
{
F=e.nextElement();
if((F.getInternalFlags()&Faction.IFLAG_CUSTOMTICK)==0)
{
addOutsidersAndTimers(F, outSiders, timers);
factionsDone.add(F);
}
}
Room R;
Faction[] Fs;
for(final Session S : CMLib.sessions().allIterable())
{
mob=(!S.isStopped())?S.mob():null;
R=(mob==null)?null:mob.location();
if(R!=null)
{
Fs=getSpecialFactions(mob, R);
if(Fs!=null)
{
for(final Faction sF : Fs)
{
if(!factionsDone.contains(sF))
{
addOutsidersAndTimers(sF, outSiders, timers);
factionsDone.add(sF);
}
}
}
for(int o=0;o<outSiders.size();o++)
{
CE=outSiders.elementAt(o);
if((CE.applies(mob,mob))
&&(!CE.getFaction().hasFaction(mob)))
CE.getFaction().executeChange(mob,mob,CE);
}
for(int o=0;o<timers.size();o++)
{
CE=timers.elementAt(o);
if((CE.applies(mob,mob))
&&(CE.getFaction().hasFaction(mob)))
CE.getFaction().executeChange(mob,mob,CE);
}
}
}
}
catch (final Exception e)
{
Log.errOut("Factions", e);
}
return true;
}
@Override
public int getAlignPurity(final int faction, final Faction.Align eq)
{
if(!factionMap.containsKey(this.getAlignmentID().toUpperCase()))
return 0;
int bottom=Integer.MAX_VALUE;
int top=Integer.MIN_VALUE;
final int pct=getPercent(getAlignmentID(),faction);
final Enumeration<FRange> e = getRanges(getAlignmentID());
if(e!=null)
for(;e.hasMoreElements();)
{
final Faction.FRange R=e.nextElement();
if(R.alignEquiv()==eq)
{
if(R.low()<bottom)
bottom=R.low();
if(R.high()>top)
top=R.high();
}
}
switch(eq)
{
case GOOD:
return Math.abs(pct - getPercent(getAlignmentID(),top));
case EVIL:
return Math.abs(getPercent(getAlignmentID(),bottom) - pct);
case NEUTRAL:
return Math.abs(getPercent(getAlignmentID(),(int)Math.round(CMath.div((top+bottom),2))) - pct);
default:
return 0;
}
}
@Override
public int getInclinationPurity(final int faction, final Faction.Align eq)
{
if(!factionMap.containsKey(this.getInclinationID().toUpperCase()))
return 0;
int bottom=Integer.MAX_VALUE;
int top=Integer.MIN_VALUE;
final int pct=getPercent(getInclinationID(),faction);
final Enumeration<FRange> e = getRanges(getInclinationID());
if(e!=null)
for(;e.hasMoreElements();)
{
final Faction.FRange R=e.nextElement();
if(R.alignEquiv()==eq)
{
if(R.low()<bottom)
bottom=R.low();
if(R.high()>top)
top=R.high();
}
}
switch(eq)
{
case LAWFUL:
return Math.abs(pct - getPercent(getInclinationID(),top));
case CHAOTIC:
return Math.abs(getPercent(getInclinationID(),bottom) - pct);
case INDIFF:
return Math.abs(getPercent(getInclinationID(),(int)Math.round(CMath.div((top+bottom),2))) - pct);
default:
return 0;
}
}
// Please don't mock the name, I couldn't think of a better one. Sadly.
@Override
public int getAlignMedianFacValue(final Faction.Align eq)
{
if(!factionMap.containsKey(this.getAlignmentID().toUpperCase()))
return 0;
int bottom=Integer.MAX_VALUE;
int top=Integer.MIN_VALUE;
final Enumeration<FRange> e = getRanges(getAlignmentID());
if(e==null)
return 0;
for(;e.hasMoreElements();)
{
final Faction.FRange R=e.nextElement();
if(R.alignEquiv()==eq)
{
if(R.low()<bottom)
bottom=R.low();
if(R.high()>top)
top=R.high();
}
}
switch(eq)
{
case GOOD:
return top;
case EVIL:
return bottom;
case NEUTRAL:
return (int)Math.round(CMath.div((top+bottom),2));
default:
return 0;
}
}
@Override
public int isFactionTag(final String tag)
{
for(int i=0;i<Faction.TAG_NAMES.length;i++)
{
if(tag.equalsIgnoreCase(Faction.TAG_NAMES[i]))
return i;
else
if(Faction.TAG_NAMES[i].endsWith("*")&&tag.startsWith(Faction.TAG_NAMES[i].substring(0,Faction.TAG_NAMES[i].length()-1)))
return i;
}
return -1;
}
@Override
public Faction.Align getAlignEnum(final String str)
{
final Faction.Align A=(Faction.Align)CMath.s_valueOf(Faction.Align.class, str.toUpperCase().trim());
if(A!=null)
return A;
return Faction.Align.INDIFF;
}
private String getWordAffOrBehav(final String ID)
{
if(CMClass.getBehavior(ID)!=null)
return "behavior";
if(CMClass.getAbility(ID)!=null)
return "ability";
if(CMClass.getCommand(ID)!=null)
return "command";
return null;
}
@Override
public void modifyFaction(final MOB mob, final Faction me) throws IOException
{
if(mob.isMonster())
return;
boolean ok=false;
int showFlag=-1;
if(CMProps.getIntVar(CMProps.Int.EDITORTYPE)>0)
showFlag=-999;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!ok))
{
int showNumber=0;
// name
me.setName(CMLib.genEd().prompt(mob,me.name(),++showNumber,showFlag,L("Name")));
// ranges
++showNumber;
if(!me.ranges().hasMoreElements())
me.addRange("0;100;Sample Range;SAMPLE;");
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
final StringBuffer list=new StringBuffer(showNumber+". Faction Division/Ranges List:\n\r");
list.append(CMStrings.padRight(L(" Code"),16)+CMStrings.padRight(L("Name"),21)+CMStrings.padRight(L("Min"),11)+CMStrings.padRight(L("Max"),11)+CMStrings.padRight(L("Align"),6)+"\n\r");
for(final Enumeration<FRange> e=me.ranges();e.hasMoreElements();)
{
final Faction.FRange FR=e.nextElement();
list.append(CMStrings.padRight(" "+FR.codeName(),15)+" ");
list.append(CMStrings.padRight(FR.name(),20)+" ");
list.append(CMStrings.padRight(""+FR.low(),10)+" ");
list.append(CMStrings.padRight(""+FR.high(),10)+" ");
list.append(CMStrings.padRight(FR.alignEquiv().toString(),5)+"\n\r");
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
String which=mob.session().prompt(L("Enter a CODE to add, remove, or modify:"),"");
if(which.length()==0)
break;
which=which.trim().toUpperCase();
if(which.indexOf(' ')>=0)
{
mob.tell(L("Faction Range code names may not contain spaces."));
break;
}
Faction.FRange FR=me.fetchRange(which);
if(FR==null)
{
if(mob.session().confirm(L("Create a new range code named '@x1' (y/N): ",which),"N"))
{
FR=me.addRange("0;100;Change My Name;"+which+";");
}
}
else
if(mob.session().choose(L("Would you like to M)odify or D)elete this range (M/d): "),"MD","M").toUpperCase().startsWith("D"))
{
me.delRange(FR);
mob.tell(L("Range deleted."));
FR=null;
}
if(FR!=null)
{
String newName=mob.session().prompt(L("Enter a new name (@x1)\n\r: ",FR.name()),FR.name());
boolean error99=false;
if(newName.length()==0)
error99=true;
else
for(final Enumeration<FRange> e=me.ranges();e.hasMoreElements();)
{
final Faction.FRange FR3=e.nextElement();
if(FR3.name().equalsIgnoreCase(FR.name())&&(FR3!=FR))
{
mob.tell(L("A range already exists with that name!"));
error99=true;
break;
}
}
if(error99)
mob.tell(L("(no change)"));
else
FR.setName(newName);
newName=mob.session().prompt(L("Enter the low end of the range (@x1)\n\r: ",""+FR.low()),""+FR.low());
if(!CMath.isInteger(newName))
mob.tell(L("(no change)"));
else
FR.setLow(CMath.s_int(newName));
newName=mob.session().prompt(L("Enter the high end of the range (@x1)\n\r: ",""+FR.high()),""+FR.high());
if((!CMath.isInteger(newName))||(CMath.s_int(newName)<FR.low()))
mob.tell(L("(no change)"));
else
FR.setHigh(CMath.s_int(newName));
final StringBuffer prompt=new StringBuffer("Select the 'virtue' (if any) of this range:\n\r");
final StringBuffer choices=new StringBuffer("");
for(final Faction.Align i : Faction.Align.values())
{
choices.append(""+i.ordinal());
if(i==Faction.Align.INDIFF)
prompt.append(i.ordinal()+") Not applicable\n\r");
else
prompt.append(i.ordinal()+") "+i.toString().toLowerCase()+"\n\r");
}
FR.setAlignEquiv(Faction.Align.values()[CMath.s_int(mob.session().choose(L("@x1Enter alignment equivalency or 0: ",prompt.toString()),choices.toString(),""+FR.alignEquiv().ordinal()))]);
}
}
// show in score
me.setShowInScore(CMLib.genEd().prompt(mob,me.showInScore(),++showNumber,showFlag,L("Show in 'Score'")));
// show in factions
me.setShowInFactionsCommand(CMLib.genEd().prompt(mob,me.showInFactionsCommand(),++showNumber,showFlag,L("Show in 'Factions' command")));
// show in special reports
boolean alreadyReporter=false;
for(final Enumeration<Faction> e=CMLib.factions().factions();e.hasMoreElements();)
{
final Faction F2=e.nextElement();
if(F2.showInSpecialReported())
alreadyReporter=true;
}
if(!alreadyReporter)
me.setShowInSpecialReported(CMLib.genEd().prompt(mob,me.showInSpecialReported(),++showNumber,showFlag,L("Show in Reports")));
// show in editor
me.setShowInEditor(CMLib.genEd().prompt(mob,me.showInEditor(),++showNumber,showFlag,L("Show in MOB Editor")));
// auto defaults
boolean error=true;
me.setAutoDefaults(CMParms.parseSemicolons(CMLib.genEd().prompt(mob,CMParms.toSemicolonListString(me.autoDefaults()),++showNumber,showFlag,L("Optional automatic assigned values with zapper masks (semicolon delimited).\n\r ")),true));
// non-auto defaults
error=true;
if(!me.defaults().hasMoreElements())
me.setDefaults(new XVector<String>("0"));
++showNumber;
while(error&&(mob.session()!=null)&&(!mob.session().isStopped()))
{
error=false;
final String newDefaults=CMLib.genEd().prompt(mob,CMParms.toSemicolonListString(me.defaults()),showNumber,showFlag,L("Other default values with zapper masks (semicolon delimited).\n\r "));
if((showFlag!=showNumber)&&(showFlag>-999))
break;
final List<String> V=CMParms.parseSemicolons(newDefaults,true);
if(V.size()==0)
{
mob.tell(L("This field may not be empty."));
error=true;
}
me.setDefaults(CMParms.parseSemicolons(newDefaults,true));
}
// choices and choice intro
me.setChoices(CMParms.parseSemicolons(CMLib.genEd().prompt(mob,CMParms.toSemicolonListString(me.choices()),++showNumber,showFlag,L("Optional new player value choices (semicolon-delimited).\n\r ")),true));
if(me.choices().hasMoreElements())
me.setChoiceIntro(CMLib.genEd().prompt(mob,me.choiceIntro(),++showNumber,showFlag,L("Optional choices introduction text. Filename")));
// rate modifier
final String newModifier=CMLib.genEd().prompt(mob,CMath.toPct(me.rateModifier()),++showNumber,showFlag,L("Rate modifier"));
if((CMath.isNumber(newModifier))||(CMath.isPct(newModifier)))
me.setRateModifier(CMath.s_pct(newModifier));
// experience flag
boolean error2=true;
++showNumber;
while(error2&&(mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
error2=false;
final StringBuffer nextPrompt=new StringBuffer("\n\r");
int myval=-1;
for(int i=0;i<Faction.EXPAFFECT_NAMES.length;i++)
{
if(me.experienceFlag().equalsIgnoreCase(Faction.EXPAFFECT_NAMES[i]))
myval=i;
nextPrompt.append(" "+(i+1)+") "+CMStrings.capitalizeAndLower(Faction.EXPAFFECT_NAMES[i].toLowerCase())+"\n\r");
}
if(myval<0)
{
me.setExperienceFlag("NONE");
myval=0;
}
if((showFlag!=showNumber)&&(showFlag>-999))
{
mob.tell(L("@x1. Affect on experience: @x2",""+showNumber,Faction.EXPAFFECT_NAMES[myval]));
break;
}
final String prompt="Affect on experience: "+Faction.EXPAFFECT_NAMES[myval]+nextPrompt.toString()+"\n\rSelect a value: ";
final int mynewval=CMLib.genEd().prompt(mob,myval+1,showNumber,showFlag,prompt);
if((showFlag!=showNumber)&&(showFlag>-999))
break;
if((mynewval<=0)||(mynewval>Faction.EXPAFFECT_NAMES.length))
{
mob.tell(L("That value is not valid."));
error2=true;
}
else
me.setExperienceFlag(Faction.EXPAFFECT_NAMES[mynewval-1]);
}
// factors by mask
++showNumber;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
final StringBuffer list=new StringBuffer(showNumber+". Faction change adjustment Factors with Zapper Masks:\n\r");
list.append(" #) "+CMStrings.padRight(L("Zapper Mask"),31)+CMStrings.padRight(L("Gain"),6)+CMStrings.padRight(L("Loss"),6)+"\n\r");
final StringBuffer choices=new StringBuffer("");
int numFactors=0;
for(final Enumeration<Faction.FZapFactor> e=me.factors();e.hasMoreElements();)
{
final Faction.FZapFactor factor=e.nextElement();
choices.append(((char)('A'+numFactors)));
list.append(" "+(((char)('A'+numFactors))+") "));
list.append(CMStrings.padRight(factor.MOBMask(),30)+" ");
list.append(CMStrings.padRight(""+CMath.toPct(factor.gainFactor()),5)+" ");
list.append(CMStrings.padRight(""+CMath.toPct(factor.lossFactor()),5)+"\n\r");
numFactors++;
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
final String which=mob.session().choose(L("Enter a # to remove, or modify, or enter 0 to Add:"),"0"+choices.toString(),"").trim().toUpperCase();
final int factorNum=choices.toString().indexOf(which);
if((which.length()!=1)
||((!which.equalsIgnoreCase("0"))
&&((factorNum<0)||(factorNum>=numFactors))))
break;
Faction.FZapFactor factor=null;
if(!which.equalsIgnoreCase("0"))
{
factor=me.getFactor(factorNum);
if(factor!=null)
{
if(mob.session().choose(L("Would you like to M)odify or D)elete this range (M/d): "),"MD","M").toUpperCase().startsWith("D"))
{
me.delFactor(factor);
mob.tell(L("Factor deleted."));
factor=null;
}
}
}
else
factor=me.addFactor(1.0,1.0,"");
if(factor!=null)
{
final String mask=mob.session().prompt(L("Enter a new zapper mask (@x1)\n\r: ",factor.MOBMask()),factor.MOBMask());
double newHigh=factor.gainFactor();
String newName=mob.session().prompt(L("Enter gain adjustment (@x1): ",CMath.toPct(newHigh)),CMath.toPct(newHigh));
if((!CMath.isNumber(newName))&&(!CMath.isPct(newName)))
mob.tell(L("(no change)"));
else
newHigh=CMath.s_pct(newName);
double newLow=factor.lossFactor();
newName=mob.session().prompt(L("Enter loss adjustment (@x1): ",CMath.toPct(newLow)),CMath.toPct(newLow));
if((!CMath.isNumber(newName))&&(!CMath.isPct(newName)))
mob.tell(L("(no change)"));
else
newLow=CMath.s_pct(newName);
me.delFactor(factor);
factor=me.addFactor(newHigh,newLow,mask);
}
}
// relations between factions
++showNumber;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
final StringBuffer list=new StringBuffer(showNumber+". Cross-Faction Relations:\n\r");
list.append(" Faction"+CMStrings.padRight("",25)+"Percentage change\n\r");
for(final Enumeration<String> e=me.relationFactions();e.hasMoreElements();)
{
final String key=e.nextElement();
final double value=me.getRelation(key);
final Faction F=CMLib.factions().getFaction(key);
if(F!=null)
{
list.append(" "+CMStrings.padRight(F.name(),31)+" ");
list.append(CMath.toPct(value));
list.append("\n\r");
}
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
final String which=mob.session().prompt(L("Enter a faction to add, remove, or modify relations:"),"");
if(which.length()==0)
break;
Faction theF=null;
for(final Enumeration<String> e=me.relationFactions();e.hasMoreElements();)
{
final String key=e.nextElement();
final Faction F=CMLib.factions().getFaction(key);
if((F!=null)&&(F.name().equalsIgnoreCase(which)))
theF=F;
}
if(theF==null)
{
Faction possibleF=CMLib.factions().getFaction(which);
if(possibleF==null)
possibleF=CMLib.factions().getFactionByName(which);
if(possibleF==null)
mob.tell(L("'@x1' is not a valid faction.",which));
else
if(mob.session().confirm(L("Create a new relation for faction '@x1' (y/N):",possibleF.name()),"N"))
{
theF=possibleF;
me.addRelation(theF.factionID(),1.0);
}
}
else
if(mob.session().choose(L("Would you like to M)odify or D)elete this relation (M/d): "),"MD","M").toUpperCase().startsWith("D"))
{
me.delRelation(theF.factionID());
mob.tell(L("Relation deleted."));
theF=null;
}
if(theF!=null)
{
final String amount=CMath.toPct(me.getRelation(theF.factionID()));
final String newName=mob.session().prompt(L("Enter a relation amount (@x1): ",amount),""+amount);
if((!CMath.isNumber(newName))&&(!CMath.isPct(newName)))
mob.tell(L("(no change)"));
me.delRelation(theF.factionID());
me.addRelation(theF.factionID(),CMath.s_pct(newName));
}
}
// faction change triggers
++showNumber;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
final StringBuffer list=new StringBuffer(showNumber+". Faction Change Triggers:\n\r");
list.append(" "+CMStrings.padRight(L("Type"),15)
+" "+CMStrings.padRight(L("Direction"),10)
+" "+CMStrings.padRight(L("Factor"),10)
+" "+CMStrings.padRight(L("Flags"),20)
+" Mask\n\r");
int numChanges=0;
final StringBuffer choices=new StringBuffer("");
final Hashtable<Character,Faction.FactionChangeEvent> choicesHashed=new Hashtable<Character,Faction.FactionChangeEvent>();
for(final Enumeration<String> e=me.changeEventKeys();e.hasMoreElements();)
{
final Faction.FactionChangeEvent[] CEs=me.getChangeEvents(e.nextElement());
if(CEs!=null)
{
for (final FactionChangeEvent CE : CEs)
{
choices.append(((char)('A'+numChanges)));
list.append(" "+(((char)('A'+numChanges))+") "));
choicesHashed.put(Character.valueOf((char)('A'+numChanges)), CE);
if(CE.triggerParameters().trim().length()==0)
list.append(CMStrings.padRight(CE.eventID(),15)+" ");
else
list.append(CMStrings.padRight(CE.eventID()+":"+CE.triggerParameters(),15)+" ");
list.append(CMStrings.padRight(Faction.FactionChangeEvent.CHANGE_DIRECTION_DESCS[CE.direction()],10)+" ");
list.append(CMStrings.padRight(CMath.toPct(CE.factor()),10)+" ");
list.append(CMStrings.padRight(CE.flagCache(),20)+" ");
list.append(CE.targetZapper()+"\n\r");
numChanges++;
}
}
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
String which=mob.session().prompt(L("Select an ID to add, remove, or modify:"),"");
which=which.toUpperCase().trim();
if(which.length()==0)
break;
Faction.FactionChangeEvent CE=(which.length()>0)?choicesHashed.get(Character.valueOf(which.charAt(0))):null;
if(CE==null)
{
final String newID=mob.session().prompt(L("Enter a new change ID (?): ")).toUpperCase().trim();
if(newID.length()==0)
break;
if(newID.equalsIgnoreCase("?"))
{
mob.tell(L("Valid triggers: \n\r@x1",me.ALL_CHANGE_EVENT_TYPES()));
continue;
}
CE=me.createChangeEvent(newID);
if(CE==null)
{
mob.tell(L("That ID is invalid. Try '?'."));
continue;
}
else
if(!mob.session().confirm(L("Create a new trigger using ID '@x1' (y/N): ",newID),"N"))
{
me.delChangeEvent(CE);
CE=null;
break;
}
}
else
if(mob.session().choose(L("Would you like to M)odify or D)elete this trigger (M/d): "),"MD","M").toUpperCase().startsWith("D"))
{
me.delChangeEvent(CE);
mob.tell(L("Trigger deleted."));
CE=null;
}
if(CE!=null)
{
final String newFlags=mob.session().prompt(L("Trigger parms (@x1): ",CE.triggerParameters()),CE.triggerParameters());
if((newFlags.length()==0)||(newFlags.equals(CE.triggerParameters())))
mob.tell(L("(no change)"));
else
CE.setTriggerParameters(newFlags.trim());
}
if(CE!=null)
{
final StringBuffer directions=new StringBuffer("Valid directions:\n\r");
final StringBuffer cmds=new StringBuffer("");
for(int i=0;i<Faction.FactionChangeEvent.CHANGE_DIRECTION_DESCS.length;i++)
{
directions.append(((char)('A'+i))+") "+Faction.FactionChangeEvent.CHANGE_DIRECTION_DESCS[i]+"\n\r");
cmds.append((char)('A'+i));
}
final String str=mob.session().choose(L("@x1\n\rSelect a new direction (@x2): ",directions.toString(),Faction.FactionChangeEvent.CHANGE_DIRECTION_DESCS[CE.direction()]),cmds.toString()+"\n\r","");
if((str.length()==0)||str.equals("\n")||str.equals("\r")||(cmds.toString().indexOf(str.charAt(0))<0))
mob.tell(L("(no change)"));
else
CE.setDirection((cmds.toString().indexOf(str.charAt(0))));
}
if(CE!=null)
{
if(CE.factor()==0.0)
CE.setFactor(1.0);
final String amount=CMath.toPct(CE.factor());
final String newName=mob.session().prompt(L("Enter the amount factor (@x1): ",amount),""+amount);
if((!CMath.isNumber(newName))&&(!CMath.isPct(newName)))
mob.tell(L("(no change)"));
else
CE.setFactor(CMath.s_pct(newName));
}
if(CE!=null)
{
mob.tell(L("Valid flags/keys include: @x1\n\r",CMParms.toListString(CMParms.combine(Faction.FactionChangeEvent.FLAG_DESCS,Faction.FactionChangeEvent.FLAG_KEYVALS))));
final String newFlags=mob.session().prompt(L("Enter new flag(s) (@x1): @x2",CE.flagCache(),CE.flagCache()),CE.flagCache());
if((newFlags.length()==0)||(newFlags.equals(CE.flagCache())))
mob.tell(L("(no change)"));
else
CE.setFlags(newFlags);
}
if(CE!=null)
{
final String newFlags=mob.session().prompt(L("Zapper mask (@x1): @x2",CE.targetZapper(),CE.targetZapper()),CE.targetZapper());
if((newFlags.length()==0)||(newFlags.equals(CE.targetZapper())))
mob.tell(L("(no change)"));
else
CE.setTargetZapper(newFlags);
}
}
// Ability allowances
++showNumber;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
if((showFlag>0)&&(showFlag!=showNumber))
break;
final StringBuffer list=new StringBuffer(showNumber+". Ability allowances:\n\r");
list.append(" #) "
+CMStrings.padRight(L("Ability masks"),40)
+" "+CMStrings.padRight(L("Low value"),10)
+" "+CMStrings.padRight(L("High value"),10)
+"\n\r");
int numUsages=0;
final StringBuffer choices=new StringBuffer("0\n\r");
for(final Enumeration<Faction.FAbilityUsage> e=me.abilityUsages();e.hasMoreElements();)
{
final Faction.FAbilityUsage CA=e.nextElement();
if(CA!=null)
{
list.append(" "+((char)('A'+numUsages)+") "));
list.append(CMStrings.padRight(CA.abilityFlags(),40)+" ");
list.append(CMStrings.padRight(CA.low()+"",10)+" ");
list.append(CMStrings.padRight(CA.high()+"",10)+" ");
list.append("\n\r");
choices.append((char)('A'+numUsages));
numUsages++;
}
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
String which=mob.session().choose(L("Select an allowance to remove or modify, or enter 0 to Add:"),choices.toString(),"");
if(which.length()!=1)
break;
which=which.toUpperCase().trim();
Faction.FAbilityUsage CA=null;
if(!which.equalsIgnoreCase("0"))
{
final int num=(which.charAt(0)-'A');
if((num<0)||(num>=numUsages))
break;
CA=me.getAbilityUsage(num);
if(CA==null)
{
mob.tell(L("That allowance is invalid.."));
continue;
}
if(mob.session().choose(L("Would you like to M)odify or D)elete this allowance (M/d): "),"MD","M").toUpperCase().startsWith("D"))
{
me.delAbilityUsage(CA);
mob.tell(L("Allowance deleted."));
CA=null;
}
}
else
if(!mob.session().confirm(L("Create a new allowance (y/N): "),"N"))
{
continue;
}
else
CA=me.addAbilityUsage(null);
if(CA!=null)
{
boolean cont=false;
while((!cont)&&(!mob.session().isStopped()))
{
final String newFlags=mob.session().prompt(L("Ability determinate masks or ? (@x1): @x2",CA.abilityFlags(),CA.abilityFlags()),CA.abilityFlags());
if(newFlags.equalsIgnoreCase("?"))
{
final StringBuffer vals=new StringBuffer("Valid masks: \n\r");
for (final String element : Ability.ACODE_DESCS)
vals.append(element+", ");
for (final String element : Ability.DOMAIN_DESCS)
vals.append(element+", ");
for (final String element : Ability.FLAG_DESCS)
vals.append(element+", ");
vals.append(" * Any ABILITY ID (skill/prayer/spell/etc)");
mob.tell(vals.toString());
cont=false;
}
else
{
cont=true;
if((newFlags.length()==0)||(newFlags.equals(CA.abilityFlags())))
mob.tell(L("(no change)"));
else
{
final List<String> unknowns=CA.setAbilityFlag(newFlags);
if(unknowns.size()>0)
for(int i=unknowns.size()-1;i>=0;i--)
if(CMClass.getAbility(unknowns.get(i))!=null)
unknowns.remove(i);
if(unknowns.size()>0)
{
mob.tell(L("The following are unknown masks: '@x1'. Please correct them.",CMParms.toListString(unknowns)));
cont=false;
}
}
}
}
String newName=mob.session().prompt(L("Enter the minimum value to use the ability (@x1): ",""+CA.low()),""+CA.low());
if((!CMath.isInteger(newName))||(CA.low()==CMath.s_int(newName)))
mob.tell(L("(no change)"));
else
CA.setLow(CMath.s_int(newName));
newName=mob.session().prompt(L("Enter the maximum value to use the ability (@x1): ",""+CA.high()),""+CA.high());
if((!CMath.isInteger(newName))||(CA.high()==CMath.s_int(newName)))
mob.tell(L("(no change)"));
else
CA.setHigh(CMath.s_int(newName));
if(CA.high()<CA.low())
CA.setHigh(CA.low());
}
}
// Affects/Behaviors
++showNumber;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
if((showFlag>0)&&(showFlag!=showNumber))
break;
final StringBuffer list=new StringBuffer(showNumber+". Effects/Behaviors:\n\r");
list.append(" #) "
+CMStrings.padRight(L("Ability/Behavior ID"),25)
+" "+CMStrings.padRight(L("MOB Mask"),20)
+" "+CMStrings.padRight(L("Parameters"),20)
+"\n\r");
int numAffBehavs=0;
final StringBuffer choices=new StringBuffer("0\n\r");
final Vector<String> IDs=new Vector<String>();
String ID=null;
for(final Enumeration<String> e=me.affectsBehavs();e.hasMoreElements();)
{
ID=e.nextElement();
final String[] parms=me.getAffectBehav(ID);
list.append(" "+((char)('A'+numAffBehavs)+") "));
list.append(CMStrings.padRight(ID,25)+" ");
list.append(CMStrings.padRight(parms[1]+"",20)+" ");
list.append(CMStrings.padRight(parms[0]+"",20)+" ");
list.append("\n\r");
choices.append((char)('A'+numAffBehavs));
IDs.addElement(ID);
numAffBehavs++;
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
String which=mob.session().choose(L("Select an ability/behavior ID to remove or modify, or enter 0 to Add:"),choices.toString(),"");
if(which.length()!=1)
break;
which=which.toUpperCase().trim();
if(!which.equalsIgnoreCase("0"))
{
final int num=(which.charAt(0)-'A');
if((num<0)||(num>=IDs.size()))
break;
ID=IDs.elementAt(num);
final String type=getWordAffOrBehav(ID);
if(mob.session().choose(L("Would you like to M)odify or D)elete this @x1 (M/d): ",type),"MD","M").toUpperCase().startsWith("D"))
{
me.delAffectBehav(ID);
mob.tell(L("@x1 deleted.",CMStrings.capitalizeAndLower(type)));
ID=null;
}
}
else
{
boolean cont=true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
ID=mob.session().prompt(L("Enter a new Ability or Behavior ID or ?: "));
if(ID.equalsIgnoreCase("?"))
{
final StringBuffer vals=new StringBuffer("Valid IDs: \n\r");
vals.append(CMParms.toCMObjectListString(CMClass.abilities()));
vals.append(CMParms.toCMObjectListString(CMClass.behaviors()));
mob.tell(vals.toString());
cont=true;
}
else
{
if((ID.length()==0)||(me.getAffectBehav(ID)!=null))
{
mob.tell(L("(nothing done)"));
ID=null;
break;
}
final String type=getWordAffOrBehav(ID);
if(type==null)
{
mob.tell(L("'@x1 is neither a valid behavior ID or ability ID. Use ? for a list.",ID));
cont=true;
}
else
if(!mob.session().confirm(L("Create a new @x1 (y/N): ",type),"N"))
{
ID=null;
break;
}
else
me.addAffectBehav(ID,"","");
}
}
}
if(ID!=null)
{
final String type=getWordAffOrBehav(ID);
final String[] oldData=me.getAffectBehav(ID);
final String[] newData=new String[2];
boolean cont=true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
final String mask=mob.session().prompt(L("Enter a new Zapper Mask or ? (@x1)\n\r: ",oldData[1]),oldData[1]);
if(mask.equalsIgnoreCase("?"))
{
mob.tell(CMLib.masking().maskHelp("\n\r","disallow"));
cont=true;
}
else
if(!mask.equals(oldData[1]))
newData[1]=mask;
}
cont=true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
final String parms=mob.session().prompt(L("Enter new @x1 parameters for @x2 or ? (@x3)\n\r: ",type,ID,oldData[0]),oldData[0]);
if(parms.equalsIgnoreCase("?"))
{
mob.tell(CMLib.help().getHelpText(ID,mob,true).toString());
cont=true;
}
else
if(!parms.equals(oldData[0]))
newData[0]=parms;
}
if((newData[0]==null)&&(newData[1]!=null))
newData[0]=oldData[0];
else
if((newData[0]!=null)&&(newData[1]==null))
newData[1]=oldData[1];
if((newData[0]!=null)&&(newData[1]!=null))
{
me.delAffectBehav(ID);
me.addAffectBehav(ID,newData[0],newData[1]);
}
}
}
// Reaction Command/Affects/Behaviors
++showNumber;
while((mob.session()!=null)&&(!mob.session().isStopped())&&(!((showFlag>0)&&(showFlag!=showNumber))))
{
if((showFlag>0)&&(showFlag!=showNumber))
break;
final StringBuffer list=new StringBuffer(showNumber+". Reaction Commands/Effects/Behaviors:\n\r");
list.append(" #) "
+CMStrings.padRight(L("Range"),15)
+" "+CMStrings.padRight(L("MOB Mask"),18)
+" "+CMStrings.padRight(L("Able/Beh/Cmd"),15)
+" "+CMStrings.padRight(L("Parameters"),18)
+"\n\r");
int numReactions=0;
final StringBuffer choices=new StringBuffer("0\n\r");
final Vector<Faction.FReactionItem> reactions=new Vector<Faction.FReactionItem>();
Faction.FReactionItem item=null;
for(final Enumeration<Faction.FReactionItem> e=me.reactions();e.hasMoreElements();)
{
item=e.nextElement();
list.append(" "+((char)('A'+numReactions)+") "));
list.append(CMStrings.padRight(item.rangeCodeName(),15)+" ");
list.append(CMStrings.padRight(item.presentMOBMask()+"",18)+" ");
list.append(CMStrings.padRight(item.reactionObjectID()+"",15)+" ");
list.append(CMStrings.padRight(item.parameters()+"",18)+" ");
list.append("\n\r");
choices.append((char)('A'+numReactions));
reactions.addElement(item);
numReactions++;
}
mob.tell(list.toString());
if((showFlag!=showNumber)&&(showFlag>-999))
break;
String which=mob.session().choose(L("Select one to remove or modify, or enter 0 to Add:"),choices.toString(),"");
if(which.length()!=1)
break;
which=which.toUpperCase().trim();
item=null;
if(!which.equalsIgnoreCase("0"))
{
final int num=(which.charAt(0)-'A');
if((num<0)||(num>=reactions.size()))
break;
item=reactions.elementAt(num);
final String type=getWordAffOrBehav(item.reactionObjectID());
if(mob.session().choose(L("Would you like to M)odify or D)elete this @x1 (M/d): ",type),"MD","M").toUpperCase().startsWith("D"))
{
me.delReaction(item);
mob.tell(L("@x1 deleted.",CMStrings.capitalizeAndLower(type)));
item=null;
}
}
String type="";
String[] oldData=new String[]{"","","",""};
if(item != null)
{
type=getWordAffOrBehav(item.reactionObjectID());
oldData=new String[]{item.rangeCodeName(),item.presentMOBMask(),item.reactionObjectID(),item.parameters()};
}
final String[] newData=new String[4];
boolean cont=true;
cont=true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
final String rangeCode=mob.session().prompt(L("Enter a new range code or ? (@x1)\n\r: ",oldData[0]),oldData[0]).toUpperCase().trim();
if(rangeCode.equalsIgnoreCase("?"))
{
final StringBuffer str=new StringBuffer("");
for(final Enumeration<Faction.FRange> e=me.ranges();e.hasMoreElements();)
{
final Faction.FRange FR=e.nextElement();
str.append(FR.codeName()+" ");
}
mob.tell(str.toString().trim()+"\n\r");
cont=true;
}
else
if(!rangeCode.equals(oldData[0]))
{
cont=true;
for(final Enumeration<Faction.FRange> e=me.ranges();e.hasMoreElements();)
{
if(e.nextElement().codeName().equalsIgnoreCase(rangeCode))
{
newData[0]=rangeCode;
cont=false;
}
}
if(cont)
mob.tell(L("'@x1' is not a valid range code. Use ?",rangeCode));
}
}
cont = true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
final String mask=mob.session().prompt(L("Enter a new Zapper Mask or ? (@x1)\n\r: ",oldData[1]),oldData[1]);
if(mask.equalsIgnoreCase("?"))
{
mob.tell(CMLib.masking().maskHelp("\n\r","disallow"));
cont=true;
}
else
if(!mask.equals(oldData[1]))
newData[1]=mask;
}
cont=true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
final String ID=mob.session().prompt(L("Enter a new Ability, Behavior, or Command ID (@x1)\n\r: ",oldData[2]),oldData[2]);
if(ID.equalsIgnoreCase("?"))
{
final StringBuffer vals=new StringBuffer("Valid IDs: \n\r");
vals.append(CMParms.toCMObjectListString(CMClass.abilities()));
vals.append(CMParms.toCMObjectListString(CMClass.behaviors()));
vals.append(CMParms.toCMObjectListString(CMClass.commands()));
mob.tell(vals.toString());
cont=true;
}
else
{
type=getWordAffOrBehav(ID);
if(type==null)
{
mob.tell(L("'@x1 is neither a valid behavior, command, ability ID. Use ? for a list.",ID));
cont=true;
}
else
newData[2]=ID;
}
}
cont=true;
while((cont)&&(!mob.session().isStopped()))
{
cont=false;
final String parms=mob.session().prompt(L("Enter new @x1 parameters for @x2 or ? (@x3)\n\r: ",type,newData[2],oldData[3]),oldData[3]);
if(parms.equalsIgnoreCase("?"))
{
mob.tell(CMLib.help().getHelpText(newData[3],mob,true).toString());
cont=true;
}
else
if(!parms.equals(oldData[3]))
newData[3]=parms;
}
for(int n=0;n<oldData.length;n++)
{
if(newData[n]==null)
newData[n]=oldData[n];
}
if(item==null)
me.addReaction(newData[0], newData[1], newData[2], newData[3]);
else
{
item.setRangeName(newData[0]);
item.setPresentMOBMask(newData[1]);
item.setReactionObjectID(newData[2]);
item.setParameters(newData[3]);
}
}
if(me.reactions().hasMoreElements())
me.setLightReactions(CMLib.genEd().prompt(mob,me.useLightReactions(),++showNumber,showFlag,L("Use 'Light' Reactions")));
else
me.setLightReactions(false);
if(showFlag<-900)
{
ok=true;
break;
}
if(showFlag>0)
{
showFlag=-1;
continue;
}
showFlag=CMath.s_int(mob.session().prompt(L("Edit which? "),""));
if(showFlag<=0)
{
showFlag=-1;
ok=true;
}
}
final String errMsg=resaveFaction(me);
if(errMsg.length()>0)
mob.tell(errMsg);
}
private StringBuffer rebuildFactionProperties(final Faction F)
{
List<String> oldV=Resources.getFileLineVector(Resources.getFileResource(makeFactionFilename(F.factionID()),true));
if(oldV.size()<10)
{
final StringBuffer template=new CMFile(Resources.buildResourcePath("examples")+"factiontemplate.ini",null,CMFile.FLAG_LOGERRORS).text();
oldV=Resources.getFileLineVector(template);
}
final boolean[] defined=new boolean[Faction.TAG_NAMES.length];
for(int i=0;i<defined.length;i++)
defined[i]=false;
for(int v=0;v<oldV.size();v++)
{
final String s=oldV.get(v);
if(!(s.trim().startsWith("#")||s.trim().length()==0||(s.indexOf('=')<0)))
{
final String tag=s.substring(0,s.indexOf('=')).trim().toUpperCase();
final int tagRef=CMLib.factions().isFactionTag(tag);
if(tagRef>=0)
defined[tagRef]=true;
}
}
final boolean[] done=new boolean[Faction.TAG_NAMES.length];
for(int i=0;i<done.length;i++) done[i]=false;
int lastCommented=-1;
final String CR="\r\n";
final StringBuffer buf=new StringBuffer("");
for(int v=0;v<oldV.size();v++)
{
String s=oldV.get(v);
if(s.trim().length()==0)
{
if((lastCommented>=0)&&(!done[lastCommented]))
{
done[lastCommented]=true;
buf.append(F.getINIDef(Faction.TAG_NAMES[lastCommented],CR)+CR);
lastCommented=-1;
}
}
else
if(s.trim().startsWith("#")||(s.indexOf('=')<0))
{
buf.append(s+CR);
final int x=s.indexOf('=');
if(x>=0)
{
s=s.substring(0,x).trim();
int first=s.length()-1;
for(;first>=0;first--)
{
if(!Character.isLetterOrDigit(s.charAt(first)))
break;
}
first=CMLib.factions().isFactionTag(s.substring(first).trim().toUpperCase());
if(first>=0)
lastCommented=first;
}
}
else
{
final String tag=s.substring(0,s.indexOf('=')).trim().toUpperCase();
final int tagRef=CMLib.factions().isFactionTag(tag);
if(tagRef<0)
buf.append(s+CR);
else
if(!done[tagRef])
{
done[tagRef]=true;
buf.append(F.getINIDef(tag,CR)+CR);
}
}
}
if((lastCommented>=0)&&(!done[lastCommented]))
buf.append(F.getINIDef(Faction.TAG_NAMES[lastCommented],CR)+CR);
return buf;
}
@Override
public String resaveFaction(final Faction F)
{
if((F.factionID().length()>0)
&&(CMLib.factions().getFaction(F.factionID())!=null))
{
if(!CMath.bset(F.getInternalFlags(), Faction.IFLAG_NEVERSAVE))
{
final StringBuffer buf = rebuildFactionProperties(F);
String factionFilename = makeFactionFilename(F.factionID());
final CMFile file = new CMFile(makeFactionFilename(F.factionID()),null);
if(!file.exists())
factionFilename = "::"+factionFilename;
if(!Resources.updateFileResource(factionFilename,buf))
return "Faction File '"+F.factionID()+"' could not be modified. Make sure it is not READ-ONLY.";
else
if((!F.factionID().toLowerCase().endsWith(".ini"))
&&(factionFilename.toLowerCase().endsWith(".ini")))
{
CMLib.factions().removeFaction(F.factionID());
F.setFactionID(F.factionID()+".INI");
CMLib.factions().addFaction(F);
}
}
}
else
return "Can not save a blank faction";
return "";
}
@Override
public int getAbilityFlagType(String strflag)
{
for (final String element : Ability.ACODE_DESCS)
{
if(element.equalsIgnoreCase(strflag))
return 1;
}
for (final String element : Ability.DOMAIN_DESCS)
{
if(element.equalsIgnoreCase(strflag))
return 2;
}
if(strflag.startsWith("!"))
strflag=strflag.substring(1);
for (final String element : Ability.FLAG_DESCS)
{
if(element.equalsIgnoreCase(strflag))
return 3;
}
if(CMClass.getAbility(strflag)!=null)
return 0;
return -1;
}
}