package com.planet_ink.coffee_mud.CharClasses; 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.CharClasses.interfaces.CharClass.SubClassRule; import com.planet_ink.coffee_mud.Commands.interfaces.*; import com.planet_ink.coffee_mud.Common.interfaces.*; 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.Locales.interfaces.*; import com.planet_ink.coffee_mud.MOBS.interfaces.*; import com.planet_ink.coffee_mud.Libraries.interfaces.*; import com.planet_ink.coffee_mud.Races.interfaces.*; import java.util.*; /* Copyright 2001-2016 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ @SuppressWarnings({"unchecked","rawtypes"}) public class StdCharClass implements CharClass { @Override public String ID() { return "StdCharClass"; } private final static String localizedStaticName = CMLib.lang().L("mob"); @Override public String name() { return localizedStaticName; } @Override public String name(int classLevel) { return name(); } @Override public String baseClass() { return ID(); } @Override public int getLevelCap() { return -1; } @Override public int getBonusPracLevel() { return 0; } @Override public int getBonusAttackLevel() { return 0; } @Override public int getAttackAttribute() { return CharStats.STAT_STRENGTH; } @Override public int getPracsFirstLevel() { return 5; } @Override public int getTrainsFirstLevel() { return 3; } @Override public int getLevelsPerBonusDamage() { return 1; } @Override public String getMovementFormula() { return "10*((@x2<@x3)/18)"; } public String movementDesc = null; @Override public String getHitPointsFormula() { return "((@x6<@x7)/3)+(1*(1?6))"; } public String hitPointsDesc = null; @Override public String getManaFormula() { return "((@x4<@x5)/3)+(1*(1?6))"; } public String manaDesc = null; protected String[] names = null; protected int maxStatAdj[] = new int[CharStats.CODES.TOTAL()]; protected List<Item>outfitChoices = null; @Override public int allowedArmorLevel() { return CharClass.ARMOR_ANY; } @Override public int allowedWeaponLevel() { return CharClass.WEAPONS_ANY; } protected Set<Integer> disallowedWeaponClasses(MOB mob) { return null; } protected Set<Integer> requiredWeaponMaterials() { return null; } protected int requiredArmorSourceMinor() { return -1; } protected String armorFailMessage() { return L("<S-NAME> fumble(s) <S-HIS-HER> <SKILL> due to <S-HIS-HER> armor!"); } @Override public boolean raceless() { return false; } @Override public boolean leveless() { return false; } @Override public boolean expless() { return false; } @Override public SubClassRule getSubClassRule() { return SubClassRule.BASEONLY; } @Override public boolean showThinQualifyList() { return false; } @Override public int maxNonCraftingSkills() { return CMProps.getIntVar(CMProps.Int.MAXNONCRAFTINGSKILLS); } @Override public int maxCraftingSkills() { return CMProps.getIntVar(CMProps.Int.MAXCRAFTINGSKILLS); } @Override public int maxCommonSkills() { return CMProps.getIntVar(CMProps.Int.MAXCOMMONSKILLS); } @Override public int maxLanguages() { return CMProps.getIntVar(CMProps.Int.MAXLANGUAGES); } private static final CMSecurity.SecGroup empty = new CMSecurity.SecGroup(new CMSecurity.SecFlag[] {}); @Override public CMSecurity.SecGroup getSecurityFlags(int classLevel) { return empty; } private final String[] raceRequiredList = new String[0]; @Override public String[] getRequiredRaceList() { return raceRequiredList; } private final Pair<String, Integer>[] minimumStatRequirements = new Pair[0]; @Override public Pair<String, Integer>[] getMinimumStatRequirements() { return minimumStatRequirements; } @Override public CMObject newInstance() { return this; } @Override public String getStartingMoney() { return ""; } protected String getShortAttackAttribute() { return CharStats.CODES.SHORTNAME(getAttackAttribute()); } protected final static String[][] hitPointDescReplacePairs={ {"@x1","Lvl"},{"(@x2<@x3)","Str"},{"@x2<@x3","Str"},{"(@x4<@x5)","Dex"},{"@x4<@x5","Dex"}, {"(@x6<@x7)","Con"},{"@x6<@x7","Con"},{"@x2","Str"},{"@x3","Str"},{"@x4","Dex"},{"@x5","Dex"}, {"@x6", "Con"},{"@x7", "Con"},{"@x8", "Int"},{"@x9", "Wis"},{"1?", "d"},{"*", "X"} }; protected final String[][] manaDescReplacePairs={ {"@x1","Lvl"},{"(@x2<@x3)","Wis"},{"@x2<@x3","Wis"},{"(@x4<@x5)","Int"},{"@x4<@x5","Int"}, {"(@x6<@x7)",getShortAttackAttribute()},{"@x6<@x7",getShortAttackAttribute()},{"@x2","Wis"}, {"@x3","Wis"},{"@x4","Int"},{"@x5","Int"},{"@x6", "Con"},{"@x7", "Con"},{"@x8", "Cha"}, {"@x9", "Dex"},{"1?", "d"},{"*", "X"} }; protected final static String[][] movementDescReplacePairs={ {"@x1","Lvl"},{"(@x2<@x3)","Str"},{"@x2<@x3","Str"},{"(@x4<@x5)","Dex"},{"@x4<@x5","Dex"}, {"(@x6<@x7)","Con"},{"@x6<@x7","Con"},{"@x2","Str"},{"@x3","Str"},{"@x4","Dex"},{"@x5","Dex"}, {"@x6", "Con"},{"@x7", "Con"},{"@x8", "Int"},{"@x9", "Wis"},{"1?", "d"},{"*", "X"} }; @Override public String[] nameSet() { if(names!=null) return names; names=new String[1]; names[0]=name(); return names; } @Override public void initializeClass() { } @Override public boolean isGeneric() { return false; } @Override public int availabilityCode() { return 0; } public void cloneFix(CharClass C) { } @Override public CMObject copyOf() { try { final StdCharClass E=(StdCharClass)this.clone(); E.cloneFix(this); return E; } catch(final CloneNotSupportedException e) { return this; } } @Override public int classDurationModifier(MOB myChar, Ability skill, int duration) { return duration; } @Override public int classLevelModifier(MOB myChar, Ability skill, int level) { return level; } @Override public int getTickStatus() { return Tickable.STATUS_NOT; } @Override public boolean tick(Tickable myChar, int tickID) { return true; } @Override public boolean qualifiesForThisClass(MOB mob, boolean quiet) { if(CMSecurity.isDisabled(CMSecurity.DisFlag.STDCLASSES) && (!isGeneric())) return false; final String multiClassRule=CMProps.getVar(CMProps.Str.MULTICLASS); String multiClassFirstRule=multiClassRule; String multiClassSecondRule=""; final int x=multiClassRule.indexOf('-'); if(x>0) { multiClassFirstRule=multiClassRule.substring(0, x); multiClassSecondRule=multiClassRule.substring(x+1); } final String changeToBaseClassID=baseClass(); final String changeToClassID=ID(); final SubClassRule changeToSubClassRule = getSubClassRule(); String canOnlyBeClassID=""; String canOnlyBeBaseClassID=""; if(multiClassSecondRule.length()>0) { if(multiClassSecondRule.startsWith("GRP-")) { final CharClass possibleClass=CMClass.findCharClass(multiClassSecondRule.substring(4)); if(possibleClass != null) canOnlyBeBaseClassID=possibleClass.ID(); multiClassSecondRule="NO"; } else { final CharClass possibleClass=CMClass.findCharClass(multiClassSecondRule); if(possibleClass != null) canOnlyBeClassID=possibleClass.ID(); multiClassSecondRule="NO"; } } if(mob == null) { if(multiClassFirstRule.equals("SUB")||multiClassSecondRule.equals("SUB")) { if((changeToBaseClassID.equals(changeToClassID))||(changeToSubClassRule==SubClassRule.ANY)) return true; return false; } else return true; } final CharClass curClass = mob.baseCharStats().getCurrentClass(); final String currentClassID=curClass.ID(); final String currentBaseClassID=curClass.baseClass(); for(final Pair<String,Integer> minReq : getMinimumStatRequirements()) { final int statCode=CharStats.CODES.findWhole(minReq.first, true); if(statCode >= 0) { if(mob.baseCharStats().getStat(statCode) < minReq.second.intValue()) { if(!quiet) mob.tell(L("You need at least a @x1 @x2 to become a @x3.",minReq.second.toString(),CMStrings.capitalizeAndLower(CharStats.CODES.NAME(statCode)),name())); return false; } } } final Race R=mob.baseCharStats().getMyRace(); final String[] raceList=getRequiredRaceList(); boolean foundOne=raceList.length==0; for(final String raceName : raceList) { if(raceName.equalsIgnoreCase("any") || raceName.equalsIgnoreCase("all") || R.ID().equalsIgnoreCase(raceName) || R.name().equalsIgnoreCase(raceName) || R.racialCategory().equalsIgnoreCase(raceName)) { foundOne=true; break; } } if(!foundOne) { if(!quiet) { mob.tell(L("You need to be a @x1 to be a @x2.",getRaceList(raceList).toString(),name())); } return false; } if((!mob.isMonster())&&(mob.basePhyStats().level()>0)) { if(currentClassID.equals(changeToClassID)) { if(!quiet) mob.tell(L("But you are already a @x1!",name())); return false; } if(currentClassID.equalsIgnoreCase("StdCharClass")) // this is the starting character rule { if((canOnlyBeClassID.length()>0)&&(!changeToClassID.equals(canOnlyBeClassID))) return false; if((canOnlyBeBaseClassID.length()>0)&&(!changeToBaseClassID.equals(canOnlyBeBaseClassID))) return false; if((multiClassRule.equals("NO"))||(multiClassRule.equals("MULTI"))) return true; if((multiClassRule.equals("SUB")||multiClassSecondRule.equals("BASE")) &&(changeToBaseClassID.equals(changeToClassID)||(changeToSubClassRule==SubClassRule.ANY))) return true; if((multiClassSecondRule.equals("SUBONLY")) &&((!changeToBaseClassID.equals(changeToClassID))||(changeToSubClassRule==SubClassRule.ANY))) return true; if(multiClassFirstRule.equals("APP")&&(getSubClassRule()==SubClassRule.ANY)) return true; if(!quiet) mob.tell(L("You can't train to be a @x1!",name())); return false; } else if(curClass.getSubClassRule()==SubClassRule.NONE) { if(!quiet) mob.tell(L("You can't train to be a @x1!",name())); return false; } else if(curClass.getSubClassRule()==SubClassRule.ANY) // if you are an apprentice { if(multiClassFirstRule.equals("NO") ||multiClassSecondRule.equals("NO") ||multiClassFirstRule.equals("MULTI") ||multiClassSecondRule.equals("MULTI")) return true; if(multiClassFirstRule.equals("SUB")||multiClassSecondRule.equals("SUB")) { if((changeToBaseClassID.equals(changeToClassID))||(changeToBaseClassID.equals(currentBaseClassID))) return true; if(!quiet) mob.tell(L("You must be a @x1 type to become a @x2.",changeToBaseClassID,name())); } return false; } else { if(multiClassFirstRule.equals("MULTI")||multiClassSecondRule.equals("MULTI")) return true; else if(multiClassFirstRule.equals("NO")||(multiClassSecondRule.equals("NO"))) mob.tell(L("You should be happy to be a @x1.",curClass.name())); else if(multiClassFirstRule.equals("SUB")|| multiClassSecondRule.equals("SUB")) { if(currentBaseClassID.equals(changeToBaseClassID)||(curClass.getSubClassRule()==SubClassRule.ANY)) return true; boolean doesBaseHaveAnAny=false; for(final Enumeration<CharClass> c=CMClass.charClasses();c.hasMoreElements();) { final CharClass C=c.nextElement(); if((C.baseClass().equals(currentBaseClassID))&&(C.getSubClassRule()==SubClassRule.ANY)) { doesBaseHaveAnAny=true; break; } } if(doesBaseHaveAnAny) return true; if(!quiet) mob.tell(L("You must be a @x1 type to become a @x2.",changeToBaseClassID,name())); } } return false; } return true; } private StringBuilder getRaceList(String[] raceList) { final StringBuilder str=new StringBuilder(); if(raceList.length==1) str.append(CMStrings.capitalizeAndLower(raceList[0])); else if(raceList.length==2) str.append(CMStrings.capitalizeAndLower(raceList[0])).append(" or ").append(CMStrings.capitalizeAndLower(raceList[1])); else for(int i=0;i<raceList.length;i++) { if(i>0) str.append(", "); if(i==raceList.length-1) str.append(L("or ")); str.append(CMStrings.capitalizeAndLower(raceList[i])); } return str; } @Override public String getWeaponLimitDesc() { return WEAPONS_LONGDESC[allowedWeaponLevel()]; } @Override public String getArmorLimitDesc() { return ARMOR_LONGDESC[allowedArmorLevel()]; } @Override public String getOtherLimitsDesc() { return ""; } @Override public String getOtherBonusDesc() { return ""; } @Override public String getStatQualDesc() { final Pair<String,Integer>[] reqs=getMinimumStatRequirements(); if(reqs.length==0) return "None"; final StringBuilder str=new StringBuilder(""); for(int x=0;x<reqs.length;x++) { final Pair<String,Integer> req=reqs[x]; if(x>0) str.append(", "); str.append(CMStrings.capitalizeAndLower(req.first)).append(" ").append(req.second.toString()).append("+"); } return str.toString(); } @Override public String getRaceQualDesc() { final String[] raceList=getRequiredRaceList(); if(raceList.length==0) return "All"; return getRaceList(raceList).toString(); } @Override public String getMaxStatDesc() { final StringBuilder str=new StringBuilder(""); for(final int i : CharStats.CODES.BASECODES()) if(maxStatAdjustments()[i]!=0) str.append(CMStrings.capitalizeAndLower(CharStats.CODES.DESC(i))+" ("+(CMProps.getIntVar(CMProps.Int.BASEMAXSTAT)+maxStatAdjustments()[i])+"), "); str.append(L("Others (@x1)",""+CMProps.getIntVar(CMProps.Int.BASEMAXSTAT))); return str.toString(); } @Override public String getPracticeDesc() { final StringBuilder str=new StringBuilder(""); str.append(L("@x1 +(Wisdom/6)",""+getPracsFirstLevel())); if(getBonusPracLevel()>0) str.append("+"+getBonusPracLevel()); else if(getBonusPracLevel()<0) str.append(""+getBonusPracLevel()); return str.toString()+L(" per level"); } @Override public String getTrainDesc() { return getTrainsFirstLevel()+L(" +1 per level"); } @Override public String getDamageDesc() { return L("+1 damage per @x1 level(s)",""+getLevelsPerBonusDamage()); } @Override public String getHitPointDesc() { if(hitPointsDesc==null) { String formula=getHitPointsFormula(); final int x=formula.indexOf("*(1?"); if(x>0) { final int y=formula.indexOf(')',x+1); if(y>x) formula=formula.substring(0, x)+"d"+formula.substring(x+4,y)+formula.substring(y+1); } formula=CMStrings.replaceAlls(formula, hitPointDescReplacePairs); hitPointsDesc=CMProps.getIntVar(CMProps.Int.STARTHP)+" +"+formula+" per lvl"; } return hitPointsDesc; } @Override public String getManaDesc() { if(manaDesc==null) { String formula=getManaFormula(); final int x=formula.indexOf("*(1?"); if(x>0) { final int y=formula.indexOf(')',x+1); if(y>x) formula=formula.substring(0, x)+"d"+formula.substring(x+4,y)+formula.substring(y+1); } formula=CMStrings.replaceAlls(formula, manaDescReplacePairs); manaDesc=CMProps.getIntVar(CMProps.Int.STARTMANA)+" +"+formula+" /lvl"; } return manaDesc; } @Override public String getMovementDesc() { if(movementDesc==null) { String formula=getMovementFormula(); final int x=formula.indexOf("*(1?"); if(x>0) { final int y=formula.indexOf(')',x+1); if(y>x) formula=formula.substring(0, x)+"d"+formula.substring(x+4,y)+formula.substring(y+1); } formula=CMStrings.replaceAlls(formula, movementDescReplacePairs); movementDesc=CMProps.getIntVar(CMProps.Int.STARTMOVE)+" +"+formula+" per lvl"; } return movementDesc; } @Override public String getPrimeStatDesc() { return CMStrings.capitalizeAndLower(CharStats.CODES.DESC(getAttackAttribute())); } @Override public String getAttackDesc() { final StringBuilder str=new StringBuilder(""); str.append("+("+getPrimeStatDesc().substring(0,3)+"/18)"); if(getBonusAttackLevel()>0) str.append("+"+getBonusAttackLevel()); else if(getBonusAttackLevel()<0) str.append(""+getBonusAttackLevel()); str.append(L(" per level")); return str.toString(); } protected Set<Integer> buildDisallowedWeaponClasses() { return buildDisallowedWeaponClasses(allowedWeaponLevel()); } protected Set<Integer> buildDisallowedWeaponClasses(int lvl) { if(lvl==CharClass.WEAPONS_ANY) return null; final int[] set=CharClass.WEAPONS_SETS[lvl]; final Set<Integer> H=new HashSet<Integer>(); if(set[0]>Weapon.CLASS_DESCS.length) return null; for(int i=0;i<Weapon.CLASS_DESCS.length;i++) { boolean found=false; for (final int element : set) { if(element==i) found=true; } if(!found) H.add(Integer.valueOf(i)); } return H; } protected HashSet<Integer> buildRequiredWeaponMaterials() { if(allowedWeaponLevel()==CharClass.WEAPONS_ANY) return null; final int[] set=CharClass.WEAPONS_SETS[allowedWeaponLevel()]; if(set[0]>Weapon.CLASS_DESCS.length) { final HashSet<Integer> H=new HashSet<Integer>(); for (final int element : set) H.add(Integer.valueOf(element)); return H; } return null; } protected boolean isQualifyingAuthority(MOB mob, Ability A) { CharClass C=null; int ql=0; for(int i=(mob.charStats().numClasses()-1);i>=0;i--) // last one is current { C=mob.charStats().getMyClass(i); if( C != null ) { ql=CMLib.ableMapper().getQualifyingLevel(C.ID(),true,A.ID()); if((ql>0) &&(ql<=mob.charStats().getClassLevel(C))) return (C.ID().equals(ID())); } } return false; } protected boolean armorCheck(MOB mob, int sourceCode, Environmental E) { if(!(E instanceof Ability)) return true; if((allowedArmorLevel()!=CharClass.ARMOR_ANY) &&((requiredArmorSourceMinor()<0)||(sourceCode&CMMsg.MINOR_MASK)==requiredArmorSourceMinor()) &&(isQualifyingAuthority(mob,(Ability)E)) &&(mob.isMine(E)) &&(!E.ID().equals("Skill_Recall")) &&((((Ability)E).classificationCode()&Ability.ALL_ACODES)!=Ability.ACODE_COMMON_SKILL) &&((((Ability)E).classificationCode()&Ability.ALL_ACODES)!=Ability.ACODE_LANGUAGE) &&(!CMLib.utensils().armorCheck(mob,allowedArmorLevel())) &&(CMLib.dice().rollPercentage()>(mob.charStats().getStat(getAttackAttribute())*2))) return false; return true; } protected boolean weaponCheck(MOB mob, int sourceCode, Environmental E) { if((((sourceCode&CMMsg.MINOR_MASK)==CMMsg.TYP_WEAPONATTACK)||((sourceCode&CMMsg.MINOR_MASK)==CMMsg.TYP_THROW)) &&(E instanceof Weapon) &&(mob.charStats().getCurrentClass().ID().equals(ID())) &&(((requiredWeaponMaterials()!=null)&&(!requiredWeaponMaterials().contains(Integer.valueOf(((Weapon)E).material()&RawMaterial.MATERIAL_MASK)))) ||((disallowedWeaponClasses(mob)!=null)&&(disallowedWeaponClasses(mob).contains(Integer.valueOf(((Weapon)E).weaponClassification()))))) &&(CMLib.dice().rollPercentage()>(mob.charStats().getStat(getAttackAttribute())*2)) &&(mob.fetchWieldedItem()!=null)) { mob.location().show(mob,null,CMMsg.MSG_OK_ACTION,L("<S-NAME> fumble(s) horribly with @x1.",E.name())); return false; } return true; } protected boolean giveMobAbility(MOB mob, Ability A, int proficiency, String defaultParm, boolean isBorrowedClass) { return giveMobAbility(mob,A,proficiency,defaultParm,isBorrowedClass,true); } protected boolean giveMobAbility(MOB mob, Ability A, int proficiency, String defaultParm, boolean isBorrowedClass, boolean autoInvoke) { if(mob.fetchAbility(A.ID())==null) { A=(Ability)A.copyOf(); A.setSavable(!isBorrowedClass); A.setProficiency(proficiency); A.setMiscText(defaultParm); mob.addAbility(A); if(autoInvoke) A.autoInvocation(mob, false); return true; } return false; } @Override public int[] maxStatAdjustments() { return maxStatAdj; } @Override public void grantAbilities(MOB mob, boolean isBorrowedClass) { if(CMSecurity.isAllowedEverywhere(mob,CMSecurity.SecFlag.ALLSKILLS)) { // the most efficient way of doing this -- just hash em! final Hashtable<String,Ability> alreadyAble=new Hashtable<String,Ability>(); final Hashtable<String,Ability> alreadyAff=new Hashtable<String,Ability>(); for(final Enumeration<Ability> a=mob.effects();a.hasMoreElements();) { final Ability A=a.nextElement(); if(A!=null) alreadyAff.put(A.ID(),A); } for(int a=0;a<mob.numAbilities();a++) { final Ability A=mob.fetchAbility(a); if(A!=null) { A.setProficiency(CMLib.ableMapper().getMaxProficiency(mob,true,A.ID())); A.setSavable(false); final Ability A2=alreadyAff.get(A.ID()); if(A2!=null) A2.setProficiency(CMLib.ableMapper().getMaxProficiency(mob,true,A.ID())); else A.autoInvocation(mob, false); alreadyAble.put(A.ID(),A); } } for(final Enumeration<Ability> a=CMClass.abilities();a.hasMoreElements();) { final Ability A=a.nextElement(); final int lvl=CMLib.ableMapper().lowestQualifyingLevel(A.ID()); if((lvl>=0) &&(CMLib.ableMapper().qualifiesByAnyCharClass(A.ID())) &&(!alreadyAble.containsKey(A.ID()))) giveMobAbility(mob,A,100,"",true,false); } for(final Enumeration e=CMLib.expertises().definitions();e.hasMoreElements();) mob.addExpertise(((ExpertiseLibrary.ExpertiseDefinition)e.nextElement()).ID()); alreadyAble.clear(); alreadyAff.clear(); } else { final List<Ability> onesToAdd=new ArrayList<Ability>(); for(final Enumeration<Ability> a=CMClass.abilities();a.hasMoreElements();) { final Ability A=a.nextElement(); if((CMLib.ableMapper().getQualifyingLevel(ID(),true,A.ID())>0) &&(CMLib.ableMapper().getQualifyingLevel(ID(),true,A.ID())<=mob.baseCharStats().getClassLevel(this)) &&(CMLib.ableMapper().getDefaultGain(ID(),true,A.ID()))) { final String extraMask=CMLib.ableMapper().getExtraMask(A.ID(),true,A.ID()); if((extraMask==null) ||(extraMask.length()==0) ||(CMLib.masking().maskCheck(extraMask,mob,true))) onesToAdd.add(A); } } for(int v=0;v<onesToAdd.size();v++) { final Ability A=onesToAdd.get(v); giveMobAbility(mob,A,CMLib.ableMapper().getDefaultProficiency(ID(),true,A.ID()),CMLib.ableMapper().getDefaultParm(ID(),true,A.ID()),isBorrowedClass); } } } @Override public CharClass makeGenCharClass() { if(isGeneric()) return this; final CharClass CR=(CharClass)CMClass.getCharClass("GenCharClass").copyOf(); CR.setClassParms("<CCLASS><ID>"+ID()+"</ID><NAME>"+name()+"</NAME></CCLASS>"); CR.setStat("BASE",baseClass()); CR.setStat("HITPOINTSFORMULA",""+getHitPointsFormula()); CR.setStat("MANAFORMULA",""+getManaFormula()); CR.setStat("LVLPRAC",""+getBonusPracLevel()); CR.setStat("MOVEMENTFORMULA",""+getMovementFormula()); CR.setStat("RACQUAL", CMParms.toListString(getRequiredRaceList())); CR.setStat("LVLATT",""+getBonusAttackLevel()); CR.setStat("ATTATT",""+getAttackAttribute()); CR.setStat("FSTTRAN",""+getTrainsFirstLevel()); CR.setStat("FSTPRAC",""+getPracsFirstLevel()); CR.setStat("LVLDAM",""+getLevelsPerBonusDamage()); CR.setStat("ARMOR",""+allowedArmorLevel()); //CR.setStat("STRWEAP",""+this.allowedArmorLevel()); //CR.setStat("STRARM",""+this.allowedArmorLevel()); CR.setStat("STRLMT",""+getOtherLimitsDesc()); CR.setStat("STRBON",""+getOtherBonusDesc()); CR.setStat("PLAYER",""+availabilityCode()); CR.setStat("HELP",""+CMLib.help().getHelpText(name(),null,false)); CR.setStat("MAXNCS",""+maxNonCraftingSkills()); CR.setStat("MAXCRS",""+maxCraftingSkills()); CR.setStat("MONEY", getStartingMoney()); CR.setStat("MAXCMS",""+maxCommonSkills()); CR.setStat("SUBRUL", ""+getSubClassRule().toString()); CR.setStat("MAXLGS",""+maxLanguages()); CR.setStat("NUMMINSTATS", ""+getMinimumStatRequirements().length); for(int p=0;p<getMinimumStatRequirements().length;p++) { final Pair<String,Integer> P=getMinimumStatRequirements()[p]; CR.setStat("GETMINSTAT"+p,P.first); CR.setStat("GETSTATMIN"+p,P.second.toString()); } CR.setStat("QUAL",""); final MOB fakeMOB=CMClass.getFactoryMOB(); fakeMOB.baseCharStats().setMyClasses(ID()); fakeMOB.baseCharStats().setMyLevels("0"); fakeMOB.recoverCharStats(); final PhyStats RS=(PhyStats)CMClass.getCommon("DefaultPhyStats"); RS.setAllValues(0); affectPhyStats(fakeMOB,RS); RS.setRejuv(PhyStats.NO_REJUV); CR.setStat("ESTATS",CMLib.coffeeMaker().getPhyStatsStr(RS)); final CharStats S1=(CharStats)CMClass.getCommon("DefaultCharStats"); S1.setMyClasses(ID()); S1.setMyLevels("0"); S1.setAllValues(0); final CharStats S2=(CharStats)CMClass.getCommon("DefaultCharStats"); S2.setAllValues(10); S2.setMyClasses(ID()); S2.setMyLevels("0"); final CharStats S3=(CharStats)CMClass.getCommon("DefaultCharStats"); S3.setAllValues(11); S3.setMyClasses(ID()); S3.setMyLevels("0"); final CharStats SETSTAT=(CharStats)CMClass.getCommon("DefaultCharStats"); SETSTAT.setAllValues(0); final CharStats ADJSTAT=(CharStats)CMClass.getCommon("DefaultCharStats"); ADJSTAT.setAllValues(0); affectCharStats(fakeMOB,S1); affectCharStats(fakeMOB,S2); affectCharStats(fakeMOB,S3); for(final int i: CharStats.CODES.ALLCODES()) { if(i!=CharStats.STAT_AGE) { if(CharStats.CODES.isBASE(i)) { if((S2.getStat(i)==S3.getStat(i)) &&(S1.getStat(CharStats.CODES.toMAXBASE(i))!=0)) { SETSTAT.setStat(i,S2.getStat(i)); S1.setStat(CharStats.CODES.toMAXBASE(i),0); S2.setStat(CharStats.CODES.toMAXBASE(i),0); S3.setStat(CharStats.CODES.toMAXBASE(i),0); } else ADJSTAT.setStat(i,S1.getStat(i)); } else ADJSTAT.setStat(i,S1.getStat(i)); } } CR.setStat("ASTATS",CMLib.coffeeMaker().getCharStatsStr(ADJSTAT)); CR.setStat("CSTATS",CMLib.coffeeMaker().getCharStatsStr(SETSTAT)); final CharState CS=(CharState)CMClass.getCommon("DefaultCharState"); CS.setAllValues(0); affectCharState(fakeMOB,CS); CR.setStat("ASTATE",CMLib.coffeeMaker().getCharStateStr(CS)); final List<AbilityMapper.AbilityMapping> data1=CMLib.ableMapper().getUpToLevelListings(ID(),Integer.MAX_VALUE,true,false); if(data1.size()>0) CR.setStat("NUMCABLE",""+data1.size()); else CR.setStat("NUMCABLE",""); for(int i=0;i<data1.size();i++) { final AbilityMapper.AbilityMapping able = data1.get(i); CR.setStat("GETCABLELVL"+i,Integer.toString(able.qualLevel())); CR.setStat("GETCABLEPROF"+i,Integer.toString(able.defaultProficiency())); CR.setStat("GETCABLEGAIN"+i,Boolean.toString(able.autoGain())); CR.setStat("GETCABLESECR"+i,Boolean.toString(able.isSecret())); CR.setStat("GETCABLEPARM"+i,able.defaultParm()); CR.setStat("GETCABLEPREQ"+i,able.originalSkillPreReqList()); CR.setStat("GETCABLEMASK"+i,able.extraMask()==null?"":able.extraMask()); CR.setStat("GETCABLEMAXP"+i,Integer.toString(able.maxProficiency())); // GETCABLE -- MUST BE LAST -- CR.setStat("GETCABLE"+i,able.abilityID()); } Set<Integer> H=disallowedWeaponClasses(null); if((H==null)||(H.size()==0)) CR.setStat("NUMWEAP",""); else { CR.setStat("NUMWEAP",""+H.size()); CR.setStat("GETWEAP",""+CMParms.toListString(H)); } List<Item> outfit=outfit(null); if(outfit==null) outfit=new Vector<Item>(); CR.setStat("NUMOFT",""+outfit.size()); for(int i=0;i<outfit.size();i++) CR.setStat("GETOFTID"+i,outfit.get(i).ID()); for(int i=0;i<outfit.size();i++) CR.setStat("GETOFTPARM"+i,outfit.get(i).text()); CR.setStat("HITPOINTSFORMULA",""+getHitPointsFormula()); CR.setStat("MANAFORMULA",""+getManaFormula()); CR.setStat("MOVEMENTFORMULA",""+getMovementFormula()); CR.setStat("LEVELCAP",""+getLevelCap()); CR.setStat("DISFLAGS",""+((raceless()?CharClass.GENFLAG_NORACE:0) |(leveless()?CharClass.GENFLAG_NOLEVELS:0) |(expless()?CharClass.GENFLAG_NOEXP:0) |(showThinQualifyList()?CharClass.GENFLAG_THINQUALLIST:0))); //CharState STARTCS=(CharState)CMClass.getCommon("DefaultCharState"); STARTCS.setAllValues(0); //this.startCharacter(mob,isBorrowedClass,verifyOnly) //CR.setStat("STARTASTATE",CMLib.coffeeMaker().getCharStateStr(STARTCS)); final String[] names=nameSet(); final List<List<String>> securitySets=new Vector<List<String>>(); final List<Integer> securityLvls=new Vector<Integer>(); CR.setStat("NUMNAME",""+names.length); for(int n=0;n<names.length;n++) CR.nameSet()[n]=names[n]; final int[] lvls=new int[names.length]; int nameDex=0; final List<String> firstSet=CMParms.parseSemicolons(getSecurityFlags(0).toString(';'),true); final Vector<String> cumulativeSet=new Vector<String>(); cumulativeSet.addAll(firstSet); securitySets.add(firstSet); securityLvls.add(Integer.valueOf(0)); for(int x=1;x<20000;x++) { if(!this.name(x).equals(names[nameDex])) { nameDex++; if(nameDex>=names.length) break; lvls[nameDex]=x; } if(getSecurityFlags(x).size()!=cumulativeSet.size()) { final List<String> V=new Vector<String>(); V.addAll(CMParms.parseSemicolons(getSecurityFlags(x).toString(';'),true)); for(int i=0;i<cumulativeSet.size();i++) V.remove(cumulativeSet.elementAt(i)); securitySets.add(V); securityLvls.add(Integer.valueOf(x)); cumulativeSet.addAll(V); } } for(int l=0;l<lvls.length;l++) CR.setStat("NAMELEVEL"+l,""+lvls[l]); if((securitySets.size()==1) &&(securitySets.get(0).size()==0)) { securitySets.clear(); securityLvls.clear(); } CR.setStat("NUMSSET",""+securitySets.size()); for(int s=0;s<securitySets.size();s++) { CR.setStat("SSET"+s,CMParms.combine(securitySets.get(s),0)); CR.setStat("SSETLEVEL"+s,""+securityLvls.get(s).intValue()); } H=requiredWeaponMaterials(); if((H==null)||(H.size()==0)) CR.setStat("NUMWMAT",""); else { CR.setStat("NUMWMAT",""+H.size()); CR.setStat("GETWMAT",""+CMParms.toListString(H)); } H=disallowedWeaponClasses(fakeMOB); if((H==null)||(H.size()==0)) CR.setStat("NUMWEP",""); else { CR.setStat("NUMWEP",""+H.size()); CR.setStat("GETWEP",""+CMParms.toListString(H)); } CR.setStat("ARMORMINOR",""+requiredArmorSourceMinor()); CR.setStat("STATCLASS",this.getClass().getName()); CR.setStat("EVENTCLASS",this.getClass().getName()); fakeMOB.destroy(); return CR; } @Override public void endCharacter(MOB mob) { } @Override public void startCharacter(MOB mob, boolean isBorrowedClass, boolean verifyOnly) { if(!verifyOnly) { mob.setPractices(mob.getPractices()+getPracsFirstLevel()); mob.setTrains(mob.getTrains()+getTrainsFirstLevel()); grantAbilities(mob,isBorrowedClass); CMLib.achievements().grantAbilitiesAndExpertises(mob); } if((mob!=null) && (CMSecurity.isASysOp(mob) || CMSecurity.isAllowedEverywhere(mob, CMSecurity.SecFlag.ALLSKILLS))) { final PlayerStats pStats = mob.playerStats(); if((pStats != null)&&(!pStats.isOnAutoInvokeList("ANYTHING"))) { for(String s : CMProps.getListFileVarSet(CMProps.ListFile.WIZ_NOAUTOINVOKE)) pStats.addAutoInvokeList(s); } } } @Override public List<Item> outfit(MOB myChar) { return outfitChoices; } @Override public void affectPhyStats(Physical affected, PhyStats affectableStats) { } @Override public void affectCharStats(MOB affectedMob, CharStats affectableStats) { if(affectableStats.getCurrentClass().ID().equals(ID())) for(final int i: CharStats.CODES.MAXCODES()) affectableStats.setStat(i,affectableStats.getStat(i)+maxStatAdjustments()[i]+maxStatAdjustments()[CharStats.CODES.toMAXBASE(i)]); } @Override public void affectCharState(MOB affectedMob, CharState affectableMaxState) { } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { if((msg.source()==myHost) &&(!msg.source().isMonster()) &&(msg.source().charStats().getCurrentClass()==this)) // this is important because of event buddies and dup checks { if(!armorCheck(msg.source(),msg.sourceCode(),msg.tool())) { if(msg.tool()==null) msg.source().location().show(msg.source(),null,CMMsg.MSG_OK_VISUAL,CMStrings.replaceAll(armorFailMessage(),"<SKILL>",L("maneuver"))); else msg.source().location().show(msg.source(),null,CMMsg.MSG_OK_VISUAL,CMStrings.replaceAll(armorFailMessage(),"<SKILL>",msg.tool().name()+L(" attempt"))); return false; } if(!weaponCheck(msg.source(),msg.sourceCode(),msg.tool())) return false; } return true; } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { if((msg.source()==myHost) &&(msg.target() instanceof Item) &&(msg.source().charStats().getCurrentClass()==this) // this is important because of event buddies and dup checks &&(!msg.source().isMonster())) { switch(msg.targetMinor()) { case CMMsg.TYP_WIELD: { if((msg.target() instanceof Weapon) &&(((requiredWeaponMaterials()!=null)&&(!requiredWeaponMaterials().contains(Integer.valueOf(((Weapon)msg.target()).material()&RawMaterial.MATERIAL_MASK)))) ||((disallowedWeaponClasses(msg.source())!=null)&&(disallowedWeaponClasses(msg.source()).contains(Integer.valueOf(((Weapon)msg.target()).weaponClassification())))))) msg.addTrailerMsg(CMClass.getMsg(msg.source(),msg.target(),null,CMMsg.TYP_OK_VISUAL,L("<T-NAME> feel(s) a bit strange in your hands."),CMMsg.NO_EFFECT,null,CMMsg.NO_EFFECT,null)); break; } case CMMsg.TYP_WEAR: case CMMsg.TYP_HOLD: { if(!CMLib.utensils().armorCheck(msg.source(),(Item)msg.target(),allowedArmorLevel())) { final String choice=CMProps.getAnyListFileValue(CMProps.ListFile.ARMOR_MISFITS); msg.addTrailerMsg(CMClass.getMsg(msg.source(),msg.target(),null,CMMsg.TYP_OK_VISUAL,choice,CMMsg.NO_EFFECT,null,CMMsg.NO_EFFECT,null)); } break; } default: break; } } } @Override public int compareTo(CMObject o) { return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o)); } @Override public void unLevel(MOB mob) { } @Override public void level(MOB mob, List<String> gainedAbilityIDs) { } @Override public int adjustExperienceGain(MOB host, MOB mob, MOB victim, int amount) { return amount; } /** * Localize an internal string -- shortcut. Same as calling: * @see com.planet_ink.coffee_mud.Libraries.interfaces.LanguageLibrary#fullSessionTranslation(String, String...) * Call with the string to translate, which may contain variables of the form @x1, @x2, etc. The array in xs * is then used to replace the variables AFTER the string is translated. * @param str the string to translate * @param xs the array of variables to replace * @return the translated string, with all variables in place */ public String L(final String str, final String ... xs) { return CMLib.lang().fullSessionTranslation(str, xs); } @Override public boolean isValidClassDivider(MOB killer, MOB killed, MOB mob, Set<MOB> followers) { return isValidClassBeneficiary(killer,killed,mob,followers); } @Override public boolean isValidClassBeneficiary(MOB killer, MOB killed, MOB mob, Set<MOB> followers) { if((mob!=null) &&(mob!=killed) &&(!mob.amDead()) &&((mob.getVictim()==killed) ||(followers.contains(mob)) ||(mob==killer))) return true; return false; } @Override public String classParms() { return ""; } @Override public void setClassParms(String parms) { } protected static String[] CODES = { "CLASS", "PARMS" }; @Override public int getSaveStatIndex() { return getStatCodes().length; } @Override public String getStat(String code) { switch (getCodeNum(code)) { case 0: return ID(); case 1: return "" + classParms(); } return ""; } @Override public void setStat(String code, String val) { switch (getCodeNum(code)) { case 0: return; case 1: setClassParms(val); break; } } @Override public String[] getStatCodes() { return CODES; } @Override public boolean isStat(String code) { return CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0; } protected int getCodeNum(String code) { for (int i = 0; i < CODES.length; i++) { if (code.equalsIgnoreCase(CODES[i])) return i; } return -1; } public boolean sameAs(CharClass E) { if (!(E instanceof StdCharClass)) return false; for (int i = 0; i < CODES.length; i++) { if (!E.getStat(CODES[i]).equals(getStat(CODES[i]))) return false; } return true; } }