/* * WeaponToken.java * Copyright 2003 (C) Devon Jones <soulcatcher@evilsoft.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on December 15, 2003, 12:21 PM * * Current Ver: $Revision$ * */ package pcgen.io.exporttoken; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import pcgen.base.lang.StringUtil; import pcgen.cdom.base.Constants; import pcgen.cdom.enumeration.CharID; import pcgen.cdom.enumeration.EquipmentLocation; import pcgen.cdom.enumeration.FactKey; import pcgen.cdom.enumeration.FormulaKey; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.MapKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.inst.EquipmentHead; import pcgen.cdom.reference.CDOMSingleRef; import pcgen.cdom.util.CControl; import pcgen.cdom.util.ControlUtilities; import pcgen.core.Equipment; import pcgen.core.Globals; import pcgen.core.PlayerCharacter; import pcgen.core.RuleConstants; import pcgen.core.SettingsHandler; import pcgen.core.SizeAdjustment; import pcgen.core.WeaponProf; import pcgen.core.analysis.BonusCalc; import pcgen.core.analysis.OutputNameFormatting; import pcgen.core.analysis.SizeUtilities; import pcgen.core.display.CharacterDisplay; import pcgen.core.display.UnarmedDamageDisplay; import pcgen.io.ExportHandler; import pcgen.util.Delta; import pcgen.util.Logging; import pcgen.util.enumeration.AttackType; /** * Deal with the WEAPON Token */ public class WeaponToken extends Token { /** Token Name */ public static final String TOKENNAME = "WEAPON"; /** PC Bonus = 0 */ public static final int WPTYPEBONUS_PC = 0; /** Equipment Bonus = 1 */ public static final int WPTYPEBONUS_EQ = 1; /** Feat Bonus = 2 */ public static final int WPTYPEBONUS_FEAT = 2; /** Template Bonus = 3 */ public static final int WPTYPEBONUS_TEMPLATE = 3; /** Damage Mode normal = 0 */ public static final int DAMAGEMODE_NORMAL = 0; /** Damage Mode basic = 1 */ public static final int DAMAGEMODE_BASIC = 1; /** Damage Mode offhand = 2 */ public static final int DAMAGEMODE_OFFHAND = 2; /** Damage Mode twohands = 3 */ public static final int DAMAGEMODE_TWOHANDS = 3; /** Damage Mode double = 4 */ public static final int DAMAGEMODE_DOUBLE = 4; // This defines if I should return the values // based on weapon's location or not. // 1,2,3 and 4 overrides the actual location // of the weapon and calculates all data // with that setting /** total hit = 0 */ public static final int HITMODE_TOTALHIT = 0; /** One weapon = 1 */ public static final int HITMODE_BASEHIT = 1; /** Two weapons, this is primary, off-hand heavy = 2 */ public static final int HITMODE_TWPHITH = 2; /** Two weapons, this is primary, off-hand light = 3 */ public static final int HITMODE_TWPHITL = 3; /** Two weapons, this is off-hand (heavy) = 4 */ public static final int HITMODE_TWOHIT = 4; /** Two weapons, this is off-hand (heavy) = 4 */ public static final int HITMODE_TWFOHH = 4; /** Two weapons, this is off-hand (light) = 5 */ public static final int HITMODE_TWFOHL = 5; /** One weapon, off-hand = 6 */ public static final int HITMODE_OHHIT = 6; /** One weapon, both-hands = 7 */ public static final int HITMODE_THHIT = 7; /** * @see pcgen.io.exporttoken.Token#getTokenName() */ @Override public String getTokenName() { return TOKENNAME; } /** * @see pcgen.io.exporttoken.Token#getToken(java.lang.String, pcgen.core.PlayerCharacter, pcgen.io.ExportHandler) */ @Override public String getToken(String tokenSource, PlayerCharacter pc, ExportHandler eh) { StringTokenizer aTok = new StringTokenizer(tokenSource, ".", false); //Weapon Token aTok.nextToken(); int merge = Constants.MERGE_ALL; int weapon = 0; Equipment eq; // First check to see if there is a MERGE token String token = aTok.nextToken(); if (token.equals("MERGENONE")) { merge = Constants.MERGE_NONE; token = aTok.nextToken(); } else if (token.equals("MERGELOC")) { merge = Constants.MERGE_LOCATION; token = aTok.nextToken(); } else if (token.equals("MERGEALL")) { merge = Constants.MERGE_ALL; token = aTok.nextToken(); } List<Equipment> weaponList = pc.getExpandedWeapons(merge); if (token.equals("ALL")) { token = aTok.nextToken(); } else if (token.equals("EQUIPPED")) { // remove all weapons which are not equipped from list for (Iterator<Equipment> it = weaponList.iterator(); it.hasNext();) { if (!it.next().isEquipped()) { it.remove(); } } token = aTok.nextToken(); } else if (token.equals("NOT_EQUIPPED")) { // remove all weapons which are equipped from list for (Iterator<Equipment> it = weaponList.iterator(); it.hasNext();) { if (it.next().isEquipped()) { it.remove(); } } token = aTok.nextToken(); } else if (token.equals("CARRIED")) { // remove all weapons which are not carried from list for (Iterator<Equipment> it = weaponList.iterator(); it.hasNext();) { if (it.next().numberCarried().intValue() == 0) { it.remove(); } } token = aTok.nextToken(); } else if (token.equals("NOT_CARRIED")) { // remove all weapons which are carried from list for (Iterator<Equipment> it = weaponList.iterator(); it.hasNext();) { if (it.next().numberCarried().intValue() > 0) { it.remove(); } } token = aTok.nextToken(); } weapon = getIntToken(token, 0); if (weapon < weaponList.size()) { eq = weaponList.get(weapon); if (weapon == weaponList.size() - 1 && eh != null && eh.getExistsOnly()) { eh.setNoMoreItems(true); } return getWeaponToken(pc, eq, aTok, tokenSource); } else if (eh != null && eh.getExistsOnly()) { eh.setNoMoreItems(true); if (eh.getCheckBefore()) { eh.setCanWrite(false); } } return ""; } /** * Get the Weapon Token output * * @param pc The character being exported * @param eq The weapon being exported * @param aTok The exporttoken split by . and up to the weapon property * @param tokenSource The original source of the export token (for error reporting.) * @return The output for the token for the weapon and character. */ public String getWeaponToken(PlayerCharacter pc, Equipment eq, StringTokenizer aTok, String tokenSource) { String token = ""; if (aTok.hasMoreTokens()) { token = aTok.nextToken(); } int range = -1; int content = -1; int ammo = -1; if (token.equals("RANGELIST")) { range = getIntToken(aTok, -1); if (aTok.hasMoreTokens()) { token = aTok.nextToken(); } else { token = "RANGELIST"; } } if (token.equals("CONTENTS")) { if (aTok.hasMoreTokens()) { content = getIntToken(aTok, -1); if (aTok.hasMoreTokens()) { token = aTok.nextToken(); } else { token = "CONTENTS"; } } else { token = "CONTENTSCOUNT"; } } if (token.equals("AMMUNITION")) { if (aTok.hasMoreTokens()) { ammo = getIntToken(aTok, -1); if (aTok.hasMoreTokens()) { token = aTok.nextToken(); } else { token = "AMMUNITION"; } } else { token = "AMMUNITIONCOUNT"; } } if (token.equals("NAME")) { boolean star = true; if (aTok.hasMoreTokens()) { if ("NOSTAR".equals(aTok.nextToken())) { star = false; } } return getNameToken(eq, pc, star); } else if (token.equals("OUTPUTNAME")) { return getOutputNameToken(eq, pc); } else if (token.equals("LONGNAME")) { return getLongNameToken(eq); } else if (token.equals("ATTACKS")) { return getAttacksToken(pc, eq) + ""; } else if (token.equals("AMMUNITIONCOUNT")) { return getAmmunitionCountToken(pc, eq) + ""; } else if (token.equals("AMMUNITION")) { return getAmmunitionToken(pc, eq, ammo); } else if (token.equals("CONTENTSCOUNT")) { return getContentsCountToken(eq) + ""; } else if (token.equals("CONTENTS")) { return getContentsToken(eq, content); } else if (token.equals("NUMATTACKS")) { return getNumAttacksToken(pc, eq) + ""; } else if (token.equals("HEFT")) { return getHeft(pc, eq); } else if (token.equals("ISTYPE")) { if (aTok.hasMoreTokens()) { return getIsTypeToken(eq, aTok.nextToken()); } return ""; } else if (token.equals("CRIT")) { return getCritToken(pc, eq); } else if (token.equals("MULT")) { return getMultToken(pc, eq); } else if (token.equals("RANGELIST")) { return getRangeListToken(eq, range, pc); } else if (token.equals("RANGE")) { boolean units = true; if (aTok.hasMoreTokens()) { if ("NOUNITS".equals(aTok.nextToken())) { units = false; } } return getRangeToken(eq, pc, units); } else if (token.equals("SIZEMOD")) { return Delta.toString(getSizeModToken(pc)); } else if (token.equals("TYPE")) { return getTypeToken(eq); } else if (token.equals("HIT") || token.equals("TOTALHIT")) { int attack = getIntToken(aTok, -1); return getTotalHitToken(pc, eq, range, content, ammo, attack); } else if (token.equals("BASEHIT")) { int attack = getIntToken(aTok, -1); return getBaseHitToken(pc, eq, range, content, ammo, attack); } else if (token.equals("TWPHITH")) { int attack = getIntToken(aTok, -1); return getTwpHitHToken(pc, eq, range, content, ammo, attack); } else if (token.equals("TWPHITL")) { int attack = getIntToken(aTok, -1); return getTwpHitLToken(pc, eq, range, content, ammo, attack); } else if (token.equals("TWOHIT")) { int attack = getIntToken(aTok, -1); return getTwoHitToken(pc, eq, range, content, ammo, attack); } else if (token.equals("OHHIT")) { int attack = getIntToken(aTok, -1); return getOHHitToken(pc, eq, range, content, ammo, attack); } else if (token.equals("THHIT")) { int attack = getIntToken(aTok, -1); return getTHHitToken(pc, eq, range, content, ammo, attack); } else if (token.equals("CATEGORY")) { return getCategoryToken(eq); } else if (token.equals("HAND")) { return getHandToken(eq); } else if (token.equals("MAGICDAMAGE")) { return Delta.toString(getMagicDamageToken(pc, eq)); } else if (token.equals("MAGICHIT")) { return Delta.toString(getMagicHitToken(pc, eq)); } else if (token.equals("MISC")) { return Delta.toString(getMiscToken(pc, eq)); } else if (token.equals("FEATDAMAGE")) { return Delta.toString(getFeatDamageToken(pc, eq)); } else if (token.equals("FEATHIT")) { return Delta.toString(getFeatHitToken(pc, eq)); } else if (token.equals("TEMPLATEDAMAGE")) { return Delta.toString(getTemplateDamageToken(pc, eq)); } else if (token.equals("TEMPLATEHIT")) { return Delta.toString(getTemplateHitToken(pc, eq)); } else if (token.equals("DAMAGE")) { return getDamageToken(pc, eq, range, content, ammo, false, false); } else if (token.equals("BASEDAMAGE")) { return getDamageToken(pc, eq, range, content, ammo, false, true); } else if (token.equals("BASICDAMAGE")) { return getBasicDamageToken(pc, eq, range, content, ammo, false); } else if (token.equals("THDAMAGE")) { return getTHDamageToken(pc, eq, range, content, ammo, false); } else if (token.equals("OHDAMAGE")) { return getOHDamageToken(pc, eq, range, content, ammo, false); } else if (token.equals("DAMAGEBONUS") || token.equals("BONUSDAMAGE")) { return getDamageToken(pc, eq, range, content, ammo, true, false); } else if (token.equals("BASEDAMAGEBONUS")) { return getDamageToken(pc, eq, range, content, ammo, true, true); } else if (token.equals("THDAMAGEBONUS")) { return getTHDamageToken(pc, eq, range, content, ammo, true); } else if (token.equals("OHDAMAGEBONUS")) { return getOHDamageToken(pc, eq, range, content, ammo, true); } else if (token.equals("SIZE")) { return getSizeToken(eq); } else if (token.equals("SPROP")) { return getSpropToken(pc, eq, content, ammo); } else if (token.equals("REACH")) { return getReachToken(pc, eq) + ""; } else if (token.equals("REACHUNIT")) { return Globals.getGameModeUnitSet().getDistanceUnit(); } else if (token.equals("WT")) { return getWTToken(pc, eq); } else if (token.equals("RATEOFFIRE")) { FactKey<String> fk = FactKey.valueOf("RateOfFire"); String str = eq.getResolved(fk); return (str == null) ? "" : str; } else if (token.equals("ISLIGHT")) { return getIsLightToken(pc, eq); } else if (token.equals("QUALITY")) { Map<String, String> qualityMap = eq.getMapFor(MapKey.QUALITY); if (qualityMap != null) { if (aTok.hasMoreTokens()) { String next = aTok.nextToken(); try { int idx = Integer.parseInt(next); for (String value : qualityMap.values()) { idx--; if (idx == 0) { return value; } } } catch (NumberFormatException e) { String value = qualityMap.get(next); if (value != null) { return value; } } return ""; } Set<String> qualities = new TreeSet<>(); for (Map.Entry<String, String> me : qualityMap.entrySet()) { qualities.add(new StringBuilder().append(me.getKey()) .append(": ").append(me.getValue()).toString()); } return StringUtil.join(qualities, ", "); } return ""; } else if (token.equals("CHARGES")) { String retString = ""; int charges = eq.getRemainingCharges(); if (charges >= 0) { retString = charges + ""; } return retString; } Logging.errorPrint("Invalid WEAPON token: " + tokenSource, new Throwable()); return ""; } /** * Get the is light sub token * @param pc * @param eq * @return is light sub token */ public static String getIsLightToken(PlayerCharacter pc, Equipment eq) { return eq.isWeaponLightForPC(pc) ? "TRUE" : "FALSE"; } /** * Get the name sub token * @param eq * @param pc * @param star * @return name sub token */ public static String getNameToken(Equipment eq, PlayerCharacter pc, boolean star) { StringBuilder sb = new StringBuilder(); if (eq.isEquipped() && star) { sb.append("*"); } sb.append(OutputNameFormatting.parseOutputName(eq, pc)); sb.append(eq.getAppliedName()); return sb.toString(); } /** * Get output name token * @param eq * @param pc * @return out put name token */ public static String getOutputNameToken(Equipment eq, PlayerCharacter pc) { StringBuilder sb = new StringBuilder(); if (eq.isEquipped()) { sb.append("*"); } sb.append(OutputNameFormatting.parseOutputName(eq, pc)); sb.append(eq.getAppliedName()); return sb.toString(); } /** * Get the long name sub token * @param eq * @return long name sub token */ public static String getLongNameToken(Equipment eq) { StringBuilder sb = new StringBuilder(); if (eq.isEquipped()) { sb.append("*"); } sb.append(eq.longName()); sb.append(eq.getAppliedName()); return sb.toString(); } /** * Get Attacks sub token * @param pc * @param eq * @return Attacks sub token */ public static int getAttacksToken(PlayerCharacter pc, Equipment eq) { return (int) eq.bonusTo(pc, "WEAPON", "ATTACKS", true); } /** * Get ammunition count sub token * @param pc * @param eq * @return ammunition count sub token */ public static int getAmmunitionCountToken(PlayerCharacter pc, Equipment eq) { int ammoCount = 0; String containerCapacity = eq.getContainerCapacityString().toUpperCase(); for (Equipment equip : pc.getEquipmentListInOutputOrder()) { for (String type : equip.typeList()) { if (containerCapacity.indexOf(type) >= 0) { ++ammoCount; break; } } } return ammoCount; } /** * Get the ammunition token * @param pc * @param eq * @param ammo * @return the ammunition token */ public static String getAmmunitionToken(PlayerCharacter pc, Equipment eq, int ammo) { Equipment ammoUser = getAmmoUser(pc, eq, ammo); if (ammoUser != null) { return ammoUser.getName(); } return ""; } /** * Get the contents count sub token * @param eq * @return contents count sub token */ public static int getContentsCountToken(Equipment eq) { return eq.getContainedEquipmentCount(); } /** * Get Contents token * @param eq * @param content * @return Get Contents token */ public static String getContentsToken(Equipment eq, int content) { if (content > -1) { if (content < eq.getContainedEquipmentCount()) { return eq.getContainedEquipment(content).getName(); } } return ""; } /** * Get the Heft token * @param pc * @param eq * @return heft token */ public static String getHeft(PlayerCharacter pc, Equipment eq) { String retString = ""; if (pc.getDisplay().sizeInt() > eq.sizeInt()) { retString = "LIGHT"; } else if (pc.getDisplay().sizeInt() == eq.sizeInt()) { retString = "MEDIUM"; } else { retString = "HEAVY"; } return retString; } /** * Get the is type token * @param eq * @param type * @return is type token */ public static String getIsTypeToken(Equipment eq, String type) { return isTypeToken(eq, type) ? "TRUE" : "FALSE"; } /** * Get the istype token * @param eq * @param type * @return is type token */ public static boolean isTypeToken(Equipment eq, String type) { return eq.isType(type); } /** * Get the MULT token * @param pc * @param eq * @return MULT token */ public static String getMultToken(PlayerCharacter pc, Equipment eq) { String critMultVar = ControlUtilities.getControlToken(Globals.getContext(), CControl.CRITMULT); if (critMultVar != null) { return WeaponToken.getNewCritMultString(pc, eq, critMultVar); } String profName = getProfName(eq); StringBuilder sb = new StringBuilder(); boolean isDouble = (eq.isDouble() && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)); int mult = (int) pc.getTotalBonusTo("WEAPONPROF=" + profName, "CRITMULTADD") + getWeaponProfTypeBonuses(pc, eq, "CRITMULTADD", WPTYPEBONUS_PC); int critMult = eq.getCritMultiplier(); if (critMult <= 0) { sb.append(mult); } else { sb.append(critMult + mult); } int altCrit = eq.getAltCritMultiplier(); if (isDouble && (altCrit > 0)) { sb.append("/").append(altCrit + mult); } return sb.toString(); } public static String getNewCritMultString(PlayerCharacter pc, Equipment eq, String critMultVar) { CharID id = pc.getCharID(); Object critMult1 = eq.getEquipmentHead(1).getLocalVariable(id, critMultVar); Object critMult2 = eq.getEquipmentHead(2).getLocalVariable(id, critMultVar); if (critMult1.equals(critMult2)) { return critMult1.toString(); } StringBuilder sb = new StringBuilder(); sb.append(critMult1); sb.append("/"); sb.append(critMult2); return sb.toString(); } /** * Retrieve the proficiency name for the provided item of equipment. That is * the name of the weapon proficiency that is required to correctly use the * item. * @param eq The equipment item. * @return The name of the proficiency, or empty string if no proficiency is defined. */ private static String getProfName(Equipment eq) { CDOMSingleRef<WeaponProf> ref = eq.get(ObjectKey.WEAPON_PROF); String profName; if (ref == null) { profName = ""; } else { profName = ref.get().getKeyName(); } return profName; } /** * Get the range list token * @param eq * @param range * @param aPC * @return range list token */ public static String getRangeListToken(Equipment eq, int range, PlayerCharacter aPC) { List<String> rangeList = getRangeList(eq, true, aPC); if (range < rangeList.size()) { return Globals.getGameModeUnitSet().displayDistanceInUnitSet( Integer.parseInt(rangeList.get(range))) + Globals.getGameModeUnitSet().getDistanceUnit(); } return ""; } /** * Get the range token * @param eq * @param pc * @param units * @return range token */ public static String getRangeToken(Equipment eq, PlayerCharacter pc, boolean units) { StringBuilder sb = new StringBuilder(); sb.append(Globals.getGameModeUnitSet().displayDistanceInUnitSet( EqToken.getRange(pc, eq).intValue())); if (units) { sb.append(Globals.getGameModeUnitSet().getDistanceUnit()); } return sb.toString(); } /** * Get the size mod token * @param pc * @return the size mod token */ public static int getSizeModToken(PlayerCharacter pc) { return (int) pc.getSizeAdjustmentBonusTo("TOHIT", "TOHIT"); } /** * Get the category token * @param eq * @return category token */ public static String getCategoryToken(Equipment eq) { StringBuilder sb = new StringBuilder(); sb.append(weaponCategories(eq)); sb.append("-"); if (eq.isNatural()) { sb.append("Natural"); } // If we're going to add another type then seperate with a ',' // and set non standard to false if (appendSeperator(eq)) { sb.append(","); } // Check if Both or Melee or Ranged if (eq.isType("Both")) { if (eq.isMelee()) { sb.append("Both (Melee)"); } else if (eq.isRanged()) { sb.append("Both (Ranged)"); } } else if (eq.isMelee()) { sb.append("Melee"); } else if (eq.isRanged()) { sb.append("Ranged"); } if (isNonStandard(eq)) { sb.append("Non-Standard"); } return sb.toString(); } /** * Get the type token * @param eq * @return type token */ public static String getTypeToken(Equipment eq) { String types = weaponTypes(eq, true); if (eq.isDouble()) { types += ('/' + weaponTypes(eq, false)); } return types; } /** * Get hand token * @param eq * @return hand token */ public static String getHandToken(Equipment eq) { String location = eq.getLocation().getString(); return location.replaceAll(".*\\(", "").replaceAll("\\(.*", "") .replaceAll("\\).*", ""); } /** * Get Magic damage token * @param pc * @param eq * @return Magic damage token */ public static int getMagicDamageToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int magicdamage = eq.getBonusToDamage(pc, true) + (int) BonusCalc.charBonusTo(eq, "WEAPONPROF=" + profName, "DAMAGE", pc) + getWeaponProfTypeBonuses(pc, eq, "DAMAGE", WPTYPEBONUS_EQ); return magicdamage; } /** * Get the magic to hit token * @param pc * @param eq * @return magic to hit token */ public static int getMagicHitToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int magichit = eq.getBonusToHit(pc, true) + (int) BonusCalc.charBonusTo(eq, "WEAPONPROF=" + profName, "TOHIT", pc) + getWeaponProfTypeBonuses(pc, eq, "TOHIT", WPTYPEBONUS_EQ); return magichit; } /** * Get the misc token * @param pc * @param eq * @return misc token */ public static int getMiscToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int miscBonus = ((int) pc.getTotalBonusTo("WEAPONPROF=" + profName, "TOHIT") + getWeaponProfTypeBonuses(pc, eq, "TOHIT", WPTYPEBONUS_PC)) - (int) pc.getDisplay().getStatBonusTo("TOHIT", "TYPE.MELEE") - (int) pc.getSizeAdjustmentBonusTo("TOHIT", "TOHIT"); return miscBonus; } /** * Get the feat damage token * @param pc * @param eq * @return feat damage token */ public static int getFeatDamageToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int featBonus = (int) pc.getFeatBonusTo("WEAPON", "DAMAGE") - (int) pc.getFeatBonusTo("WEAPON", "DAMAGE-SHORTRANGE") + (int) pc.getFeatBonusTo("WEAPONPROF=" + profName, "DAMAGE") + getWeaponProfTypeBonuses(pc, eq, "DAMAGE", WPTYPEBONUS_FEAT); return featBonus; } /** * Get feat to hit token * @param pc * @param eq * @return Get feat to hit token */ public static int getFeatHitToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int featBonus = (int) pc.getFeatBonusTo("WEAPON", "TOHIT") + (int) pc.getFeatBonusTo("WEAPONPROF=" + profName, "TOHIT") + getWeaponProfTypeBonuses(pc, eq, "TOHIT", WPTYPEBONUS_FEAT); return featBonus; } /** * Get the template damage token * @param pc * @param eq * @return template damage token */ public static int getTemplateDamageToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int templateBonus = (int) pc.getTemplateBonusTo("WEAPON", "DAMAGE") + (int) pc.getTemplateBonusTo("WEAPONPROF=" + profName, "DAMAGE") + getWeaponProfTypeBonuses(pc, eq, "DAMAGE", WPTYPEBONUS_TEMPLATE); return templateBonus; } /** * Get the template to hit token * @param pc * @param eq * @return to hit token */ public static int getTemplateHitToken(PlayerCharacter pc, Equipment eq) { String profName = getProfName(eq); int templateBonus = (int) pc.getTemplateBonusTo("WEAPON", "TOHIT") + (int) pc.getTemplateBonusTo("WEAPONPROF=" + profName, "TOHIT") + getWeaponProfTypeBonuses(pc, eq, "TOHIT", WPTYPEBONUS_TEMPLATE); return templateBonus; } /** * Get the size token * @param eq * @return size token */ public static String getSizeToken(Equipment eq) { return eq.getSize(); } /** * Get the SPROP token * @param pc * @param eq * @return SPROP token */ public static String getSpropToken(PlayerCharacter pc, Equipment eq) { return getSpropToken(pc, eq, -1, -1); } /** * Get SPROP token * @param pc * @param eq * @param content * @param ammo * @return SPROP token */ public static String getSpropToken(PlayerCharacter pc, Equipment eq, int content, int ammo) { String sprop = eq.getSpecialProperties(pc); //Ammunition & Contents Modifier if (content > -1) { sprop = Constants.EMPTY_STRING; if ((content < eq.getContainedEquipmentCount()) && !Constants.EMPTY_STRING.equals(eq.getContainedEquipment( content).getSpecialProperties(pc))) { sprop = eq.getContainedEquipment(content).getSpecialProperties( pc); } } int ammoCount = 0; Equipment anEquip = null; if (ammo > -1) { final String containerCapacity = eq.getContainerCapacityString(); for (Equipment equip : pc.getEquipmentListInOutputOrder()) { sprop = Constants.EMPTY_STRING; for (String type : equip.typeList()) { if (containerCapacity.indexOf(type) >= 0) { ++ammoCount; anEquip = equip; break; } } if (ammoCount == (ammo + 1)) { break; } } } if ((anEquip != null) && (ammoCount > 0) && !Constants.EMPTY_STRING.equals(anEquip.getSpecialProperties(pc))) { sprop = anEquip.getSpecialProperties(pc); } if (sprop.startsWith(", ")) { sprop = sprop.substring(2); } return sprop; } /** * Get reach token * Formula is as follows: * REACH:(RACEREACH+(max(0,EQUIPREACH-5)))*EQUIPREACHMULT * @param pc the player * @param eq the equipment * @return reach token */ public static String getReachToken(PlayerCharacter pc, Equipment eq) { String eqReach = pc.getControl("EQREACH"); int sum; if (eqReach == null) { int dist = eq.getVariableValue( SettingsHandler.getGame().getWeaponReachFormula(), "", pc) .intValue(); String profName = getProfName(eq); int iAdd = (int) pc.getTotalBonusTo("WEAPONPROF=" + profName, "REACH") + getWeaponProfTypeBonuses(pc, eq, "REACH", WPTYPEBONUS_PC); sum = dist+iAdd; } else { sum = ((Number) eq.getLocalVariable(pc.getCharID(), eqReach)).intValue(); } return Globals.getGameModeUnitSet().displayDistanceInUnitSet(sum); } /** * Get weight in set token * @param pc * @param eq * @return weight in set token */ public static String getWTToken(PlayerCharacter pc, Equipment eq) { return Globals.getGameModeUnitSet().displayWeightInUnitSet( eq.getWeight(pc).doubleValue()); } /** * Get the number of attacks token * @param pc * @param eq * @return number of attacks token */ public static int getNumAttacksToken(PlayerCharacter pc, Equipment eq) { String melee = getMeleeAttackString(pc); String unarmed = getUnarmedAttackString(pc); String ranged = getRangedAttackString(pc); String weaponString = melee; if (eq.isRanged()) { weaponString = ranged; } if (eq.isMonk()) { if (unarmed.length() > melee.length()) { weaponString = unarmed; } else if ((unarmed.length() == melee.length()) && !melee.equals(unarmed)) { StringTokenizer mTok = new StringTokenizer(melee, "+/", false); StringTokenizer uTok = new StringTokenizer(unarmed, "+/", false); String msString = mTok.nextToken(); String usString = uTok.nextToken(); if (Integer.parseInt(usString) >= Integer.parseInt(msString)) { weaponString = unarmed; } } } StringTokenizer bTok = new StringTokenizer(weaponString, "/"); int extra_attacks = (int) eq.bonusTo(pc, "WEAPON", "ATTACKS", true); return (bTok.countTokens() + extra_attacks); } /** * Get critical token * @param pc * @param eq * @return critical token */ public static String getCritToken(PlayerCharacter pc, Equipment eq) { StringBuilder sb = new StringBuilder(); String critRangeVar = ControlUtilities.getControlToken(Globals.getContext(), CControl.CRITRANGE); if (critRangeVar != null) { EquipmentHead head = eq.getEquipmentHead(1); return getCritRangeHead(pc, head, critRangeVar).toString(); } boolean isDouble = (eq.isDouble() && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)); int rawCritRange = eq.getRawCritRange(true); // see if the weapon has any crit range if (rawCritRange == 0) { // no crit range! return "none"; } String profName = getProfName(eq); int dbl = (int) pc.getTotalBonusTo("WEAPONPROF=" + profName, "CRITRANGEDOUBLE") + getWeaponProfTypeBonuses(pc, eq, "CRITRANGEDOUBLE", WPTYPEBONUS_PC); int iAdd = (int) pc.getTotalBonusTo("WEAPONPROF=" + profName, "CRITRANGEADD") + getWeaponProfTypeBonuses(pc, eq, "CRITRANGEADD", WPTYPEBONUS_PC); int eqDbl = dbl + (int) eq.bonusTo(pc, "EQMWEAPON", "CRITRANGEDOUBLE", true); int critrange = eq.getRawCritRange(true) * (eqDbl + 1); critrange = 21 - (critrange + iAdd + (int) eq.bonusTo(pc, "EQMWEAPON", "CRITRANGEADD", true)); sb.append(critrange + ""); if (critrange < 20) { sb.append("-20"); } if (isDouble && (EqToken.getOldBonusedCritRange(pc, eq, false) > 0)) { eqDbl = dbl + (int) eq.bonusTo(pc, "EQMWEAPON", "CRITRANGEDOUBLE", false); int altCritRange = eq.getRawCritRange(false) * (eqDbl + 1); altCritRange = 21 - (altCritRange + iAdd + (int) eq.bonusTo(pc, "EQMWEAPON", "CRITRANGEADD", false)); if (altCritRange != critrange) { sb.append("/" + altCritRange); if (altCritRange < 20) { sb.append("-20"); } } } return sb.toString(); } /** * Get damage token * @param pc * @param eq * @param bonusOnly * @param base * @return damage token */ public static String getDamageToken(PlayerCharacter pc, Equipment eq, boolean bonusOnly, boolean base) { return getDamageToken(pc, eq, -1, -1, -1, bonusOnly, base); } /** * Get damage token * @param pc * @param eq * @param range * @param content * @param ammo * @param bonusOnly * @param base * @return damage token */ public static String getDamageToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, boolean bonusOnly, boolean base) { boolean isDouble = (eq.isDouble() && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)); boolean isDoubleSplit = (eq.isType("Head1") || eq.isType("Head2")); int damageMode = DAMAGEMODE_NORMAL; int hands = 1; if (eq.isNatural() && (eq.getLocation() == EquipmentLocation.EQUIPPED_SECONDARY)) { damageMode = DAMAGEMODE_OFFHAND; hands = 0; } else if (eq.isUnarmed()) { damageMode = DAMAGEMODE_BASIC; } else if (isDouble && !isDoubleSplit) { damageMode = DAMAGEMODE_DOUBLE; hands = 1; } else if ((isDoubleSplit) && (eq.isWeaponTwoHanded(pc))) { damageMode = DAMAGEMODE_TWOHANDS; hands = 2; } else if (pc.getDisplay().isSecondaryWeapon(eq)) { damageMode = DAMAGEMODE_OFFHAND; hands = 0; } else if (pc.getDisplay().isPrimaryWeapon(eq)) { if (eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) { damageMode = DAMAGEMODE_TWOHANDS; hands = 2; } else { damageMode = DAMAGEMODE_BASIC; } } else { // Not wielded, probably just carried if (eq.isWeaponTwoHanded(pc)) { damageMode = DAMAGEMODE_TWOHANDS; hands = 2; } else { damageMode = DAMAGEMODE_BASIC; } } return getDamage(pc, eq, range, content, ammo, bonusOnly, hands, damageMode, base); } /** * Get basic damage token * @param pc * @param eq * @param bonusOnly * @return basic damage token */ public static String getBasicDamageToken(PlayerCharacter pc, Equipment eq, boolean bonusOnly) { return getBasicDamageToken(pc, eq, -1, -1, -1, bonusOnly); } /** * Get basic damage token * @param pc * @param eq * @param range * @param content * @param ammo * @param bonusOnly * @return basic damage token */ public static String getBasicDamageToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, boolean bonusOnly) { int damageMode = DAMAGEMODE_BASIC; int hands = 1; return getDamage(pc, eq, range, content, ammo, bonusOnly, hands, damageMode, false); } /** * Get two handed damage token * @param pc * @param eq * @param bonusOnly * @return two handed damage token */ public static String getTHDamageToken(PlayerCharacter pc, Equipment eq, boolean bonusOnly) { return getTHDamageToken(pc, eq, -1, -1, -1, bonusOnly); } /** * Get two handed damage token * @param pc * @param eq * @param range * @param content * @param ammo * @param bonusOnly * @return two handed damage token */ public static String getTHDamageToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, boolean bonusOnly) { int damageMode = DAMAGEMODE_TWOHANDS; int hands = 2; return getDamage(pc, eq, range, content, ammo, bonusOnly, hands, damageMode, false); } /** * Get Off hand damage token * @param pc * @param eq * @param bonusOnly * @return Off hand damage token */ public static String getOHDamageToken(PlayerCharacter pc, Equipment eq, boolean bonusOnly) { return getOHDamageToken(pc, eq, -1, -1, -1, bonusOnly); } /** * Get Off hand damage token * @param pc * @param eq * @param range * @param content * @param ammo * @param bonusOnly * @return Off hand damage token */ public static String getOHDamageToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, boolean bonusOnly) { int damageMode = DAMAGEMODE_OFFHAND; int hands = 0; return getDamage(pc, eq, range, content, ammo, bonusOnly, hands, damageMode, false); } /** * Get total hit token * @param pc * @param eq * @return total hit token */ public static String getTotalHitToken(PlayerCharacter pc, Equipment eq) { return getTotalHitToken(pc, eq, -1, -1, -1, -1); } /** * Get total hit token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return total hit token */ public static String getTotalHitToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { CharacterDisplay display = pc.getDisplay(); boolean isDouble = (eq.isDouble() && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)); boolean isDoubleSplit = (eq.isType("Head1") || eq.isType("Head2")); int hitMode = HITMODE_TOTALHIT; // First do unarmed. if (eq.isUnarmed()) { hitMode = HITMODE_BASEHIT; } // next do Double weapons else if (isDouble && !isDoubleSplit) { hitMode = HITMODE_TWOHIT; } else if (!isDouble && isDoubleSplit) { hitMode = HITMODE_THHIT; } // eq is Primary else if (display.isPrimaryWeapon(eq) && display.hasSecondaryWeapons()) { Equipment sEq = display.getSecondaryWeapons().iterator().next(); if (sEq == null) { // Hmm, weird // default to off-hand light hitMode = HITMODE_TWPHITL; } else if (sEq.isWeaponLightForPC(pc)) { // offhand light hitMode = HITMODE_TWPHITL; } else { // offhand heavy hitMode = HITMODE_TWPHITH; } } // eq is Secondary else if (display.isSecondaryWeapon(eq) && display.hasPrimaryWeapons()) { if (eq.isWeaponLightForPC(pc)) { // offhand light hitMode = HITMODE_TWFOHL; } else { // offhand heavy hitMode = HITMODE_TWFOHH; } } // Just a single off-hand weapon else if (display.isSecondaryWeapon(eq) && !display.hasPrimaryWeapons()) { hitMode = HITMODE_OHHIT; } // Just a single primary weapon else if (display.isPrimaryWeapon(eq) && !display.hasSecondaryWeapons()) { if (eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) { // both hands hitMode = HITMODE_THHIT; } else { // single hand hitMode = HITMODE_BASEHIT; } } else { // Not double or single // Not primary or Secondary // probably just carried if (eq.isWeaponTwoHanded(pc)) { // Two Handed weapon hitMode = HITMODE_THHIT; } else { // one handed weapon hitMode = HITMODE_BASEHIT; } } return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } /** * Get base hit token * @param pc * @param eq * @return base hit token */ public static String getBaseHitToken(PlayerCharacter pc, Equipment eq) { return getBaseHitToken(pc, eq, -1, -1, -1, -1); } /** * Get base hit token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return base hit token */ public static String getBaseHitToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { int hitMode = HITMODE_BASEHIT; return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } /** * Get two weapon heavy off hand token * @param pc * @param eq * @return two weapon heavy off hand token */ public static String getTwpHitHToken(PlayerCharacter pc, Equipment eq) { return getTwpHitHToken(pc, eq, -1, -1, -1, -1); } /** * Get two weapon heavy off hand token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return two weapon heavy off hand token */ public static String getTwpHitHToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { int hitMode = HITMODE_TWPHITH; return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } /** * Get two weapon light off hand token * @param pc * @param eq * @return two weapon light off hand token */ public static String getTwpHitLToken(PlayerCharacter pc, Equipment eq) { return getTwpHitLToken(pc, eq, -1, -1, -1, -1); } /** * Get two weapon light off hand token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return two weapon light off hand token */ public static String getTwpHitLToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { int hitMode = HITMODE_TWPHITL; return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } /** * Get two hit token * @param pc * @param eq * @return two hit token */ public static String getTwoHitToken(PlayerCharacter pc, Equipment eq) { return getTwoHitToken(pc, eq, -1, -1, -1, -1); } /** * Get two hit token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return two hit token */ public static String getTwoHitToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { int hitMode = HITMODE_TWOHIT; return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } /** * Get Off Hand Hit Token * @param pc * @param eq * @return Off Hand Hit Token */ public static String getOHHitToken(PlayerCharacter pc, Equipment eq) { return getOHHitToken(pc, eq, -1, -1, -1, -1); } /** * Get Off Hand Hit Token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return Off Hand Hit Toke */ public static String getOHHitToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { int hitMode = HITMODE_OHHIT; return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } /** * Get the TH Hit Token * @param pc * @param eq * @return the TH Hit Token */ public static String getTHHitToken(PlayerCharacter pc, Equipment eq) { return getTHHitToken(pc, eq, -1, -1, -1, -1); } /** * Get the TH Hit Token * @param pc * @param eq * @param range * @param content * @param ammo * @param attackNum * @return the TH Hit Token */ public static String getTHHitToken(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int attackNum) { int hitMode = HITMODE_THHIT; return getToHit(pc, eq, range, content, ammo, hitMode, attackNum); } private static String getToHit(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, int hitMode, int attackNum) { boolean isDouble = (eq.isDouble() && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)); boolean isDoubleSplit = (eq.isType("Head1") || eq.isType("Head2")); // If it's a two handed weapon, but is not // wielded as two handed, just punt now! if (eq.isMelee() && (eq.isWeaponTwoHanded(pc))) { if ((!isDouble && !isDoubleSplit && (hitMode != HITMODE_THHIT)) || (isDoubleSplit && (hitMode == HITMODE_BASEHIT || hitMode == HITMODE_OHHIT || hitMode == HITMODE_TWPHITH))) { return SettingsHandler.getInvalidToHitText(); } } if (eq.isMelee() && eq.isWeaponOutsizedForPC(pc) && !eq.isNatural()) { return SettingsHandler.getInvalidToHitText(); } int weaponBaseBonus = (int) eq.bonusTo(pc, "WEAPON", "WEAPONBAB", true); CDOMSingleRef<WeaponProf> ref = eq.get(ObjectKey.WEAPON_PROF); WeaponProf prof; String profKey; if (ref == null) { profKey = ""; prof = null; } else { prof = ref.get(); profKey = prof.getKeyName(); } weaponBaseBonus += (int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "WEAPONBAB"); weaponBaseBonus += getWeaponProfTypeBonuses(pc, eq, "WEAPONBAB", WPTYPEBONUS_PC); // The Melee, Ranged and Unarmed attack sequence String melee = getMeleeAttackString(pc, 0, weaponBaseBonus); String ranged = getRangedAttackString(pc, 0, weaponBaseBonus); String unarmed = getUnarmedAttackString(pc, 0, weaponBaseBonus); // Must leave this for 3.0 compatibility // 3.0 Monk uses special attack progression if (eq.isMonk()) { if (unarmed.length() > melee.length()) { melee = unarmed; } else if ((unarmed.length() == melee.length()) && !melee.equals(unarmed)) { StringTokenizer mTok = new StringTokenizer(melee, "+/", false); StringTokenizer m1Tok = new StringTokenizer(melee, "+/", false); String msString = mTok.nextToken(); String m1sString = m1Tok.nextToken(); if (Integer.parseInt(m1sString) >= Integer.parseInt(msString)) { melee = unarmed; } } } // // Now do all the calculations // int baseBonus = 0; int secondaryBonus = 0; int primaryBonus = 0; // Natural weapons are different if (eq.isNatural()) { if (eq.getLocation() == EquipmentLocation.EQUIPPED_PRIMARY) { /* Primary Natural Weapons have no bonus or penalty * associated with secondary weapons/attacks */ baseBonus = 0; } else if (eq.getLocation() == EquipmentLocation.EQUIPPED_SECONDARY) { /* all secondary natural weapons attack at -5 */ baseBonus = -5; /* Unless the creature has bonuses to improve * secondary attacks, such as MultiAttack */ baseBonus += pc.getTotalBonusTo("COMBAT", "TOHIT-SECONDARY"); } } else { if ((hitMode == HITMODE_TOTALHIT && eq.isRanged()) || hitMode == HITMODE_BASEHIT || hitMode == HITMODE_THHIT) { baseBonus = 0; } else if (hitMode == HITMODE_TWPHITH || hitMode == HITMODE_TWPHITL) { // TWF Primary hand baseBonus = -6; } else if (hitMode == HITMODE_OHHIT) { baseBonus = -4; } else { // TWF off-hand baseBonus = -10; } // TWF with off hand light gets a bonus if ((hitMode == HITMODE_TWPHITL) || (hitMode == HITMODE_TWFOHL)) { baseBonus += pc.getOffHandLightBonus(); } if ((hitMode == HITMODE_TWOHIT) && (isDouble || isDoubleSplit || eq.isWeaponLightForPC(pc))) { baseBonus += pc.getOffHandLightBonus(); } if ((hitMode == HITMODE_TWOHIT) || (hitMode == HITMODE_OHHIT) || (hitMode == HITMODE_TWFOHL) || (hitMode == HITMODE_TWFOHH)) { secondaryBonus = (int) pc.getTotalBonusTo("COMBAT", "TOHIT-SECONDARY"); if (eq.isRanged()) { secondaryBonus -= (int) pc.getBonusDueToType("COMBAT", "TOHIT-SECONDARY", "NOTRANGED"); } if (hitMode == HITMODE_OHHIT) { // If only using one weapon, Two-weapon Fighting Bonus does not apply // If you have TWF, you have both TOHIT-P and TOHIT-S, so remove TOHIT-P // TODO: Rework on this code and/or on the lst, because it "sounds" wrong // Felipe Diniz - 12/Feb/2003 secondaryBonus -= (int) pc.getTotalBonusTo("COMBAT", "TOHIT-PRIMARY"); } } if (((hitMode == HITMODE_TWPHITH) || (hitMode == HITMODE_TWPHITL))) { primaryBonus = (int) pc.getTotalBonusTo("COMBAT", "TOHIT-PRIMARY"); if (eq.isRanged()) { primaryBonus -= (int) pc.getBonusDueToType("COMBAT", "TOHIT-PRIMARY", "NOTRANGED"); } } } /* If the character normally can't wield this weapon 1-handed, but for some reason they can (e.g. Monkey Grip) then check for TOHIT modifiers */ if (eq.getLocation() == EquipmentLocation.EQUIPPED_PRIMARY || eq.getLocation() == EquipmentLocation.EQUIPPED_SECONDARY || eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS) { // TODO Fix this // if (eq.isWeaponOneHanded(pc, wp, false) != eq.isWeaponOneHanded(pc, wp, true)) // { // baseBonus += (int) pc.getTotalBonusTo("WEAPONPROF=" + profName, "TOHITOVERSIZE"); // baseBonus += getWeaponProfTypeBonuses(pc, eq, "TOHITOVERSIZE", WPTYPEBONUS_PC); // } } if (hitMode == HITMODE_TWPHITH || hitMode == HITMODE_TWPHITL) { baseBonus += primaryBonus; } if (hitMode == HITMODE_TWOHIT || hitMode == HITMODE_OHHIT || hitMode == HITMODE_TWFOHL || hitMode == HITMODE_TWFOHH) { baseBonus += secondaryBonus; } /* An equipped buckler gives an additional -1 penalty to weapons used off hand or a weapon used two handed. */ /******************************************** * This is now all done via BONUS:COMBAT|TOHIT|-1|PREMULT:1,[PREEQUIPBOTH:1,TYPE=Melee],[PREEQUIPSECONDARY:1,TYPE=Melee] on the item(s) * Byngl - Nov 20, 2005 if (eq.isMelee() && (hitMode == HITMODE_THHIT || hitMode == HITMODE_TWOHIT || hitMode == HITMODE_OHHIT)) { for (Iterator e = pc.getEquipmentOfType("buckler", 1).iterator(); e.hasNext();) { baseBonus--; break; } } */ // Get BONUS:COMBAT|TOHIT.abc|x // Where abc: Ranged, Melee, Slashing, etc for (String type : eq.typeList()) { // Finesseable is a special case that // is Handled elsewhere if (type.equalsIgnoreCase("Finesseable")) { continue; } //Prevents weapon from getting both melee & ranged bonuses if ((range > -1 && type.equalsIgnoreCase("MELEE")) || (range == -1 && eq.isMelee() && (type.equalsIgnoreCase("THROWN") || type .equalsIgnoreCase("RANGED")))) { continue; } baseBonus += (int) pc.getTotalBonusTo("TOHIT", "TYPE." + type); baseBonus += (int) pc.getTotalBonusTo("COMBAT", "TOHIT." + type); } if (range == -1 && eq.isMelee() && eq.isFinessable(pc)) { baseBonus += (int) pc.getTotalBonusTo("COMBAT", "TOHIT.Finesseable"); } // 3.0 Syntax // This fixes Weapon Finesse for thrown weapons // BONUS:WEAPONPROF=abc|TOHIT|DEX|TYPE.NotRanged // // Dagger yields following: // WEAPONPROF=abc.TOHIT:NOTRANGED // if ((ref != null) && eq.isRanged()) { baseBonus -= (int) pc.getBonusDueToType("WEAPONPROF=" + profKey, "TOHIT", "NOTRANGED"); baseBonus -= getWeaponProfTypeBonuses(pc, eq, "TOHIT.NOTRANGED", WPTYPEBONUS_PC); } CharacterDisplay display = pc.getDisplay(); if (!eq.isNatural() && ((ref == null) || !display.hasWeaponProf(prof))) { baseBonus += display.getNonProficiencyPenalty(); } baseBonus += (int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "TOHIT"); baseBonus += getWeaponProfTypeBonuses(pc, eq, "TOHIT", WPTYPEBONUS_PC); if (range > -1) { int rangeSize = getRangeList(eq, true, pc).size(); int shortRange = SettingsHandler.getGame().getShortRangeDistance(); /* range here is an index that represents a number of range * increments, the actual distance is held in this range */ if (range < rangeSize) { int thisRange = Integer.parseInt(getRangeList(eq, true, pc).get(range)); // at short range, add SHORTRANGE bonus if (thisRange <= shortRange) { baseBonus += (int) pc.getTotalBonusTo("COMBAT", "TOHIT-SHORTRANGE"); baseBonus += (int) pc.getTotalBonusTo("TOHIT", "SHORTRANGE"); baseBonus += (int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "TOHIT-SHORTRANGE"); baseBonus += getWeaponProfTypeBonuses(pc, eq, "TOHIT-SHORTRANGE", WPTYPEBONUS_PC); baseBonus += (int) eq.bonusTo(pc, "WEAPON", "TOHIT-SHORTRANGE", true); } // Long Range To-Hit Modifier int defaultRange = Integer.parseInt(EqToken.getRange(pc, eq).toString()); int rangePenalty = SettingsHandler.getGame().getRangePenalty(); rangePenalty += pc.getTotalBonusTo("COMBAT", "RANGEPENALTY"); baseBonus += rangePenalty * (int) Math.max(Math .ceil(((float) thisRange / defaultRange)) - 1, 0); } } //Ammunition & Contents Modifier Equipment containedEq = null; if (content > -1) { if (content < eq.getContainedEquipmentCount()) { containedEq = eq.getContainedEquipment(content); baseBonus += containedEq.getBonusToHit(pc, true); } } Equipment ammoUser = getAmmoUser(pc, eq, ammo); if (ammoUser != null) { baseBonus += ammoUser.getBonusToHit(pc, true); } // do NOT include the size bonus/penalty since // it is call in pc.getTotalBonusTo() // include players TOHIT bonuses baseBonus += (int) pc.getTotalBonusTo("TOHIT", "TOHIT"); baseBonus += (int) pc.getTotalBonusTo("COMBAT", "TOHIT"); // subtract Armor and Shield non-proficiency baseBonus += modFromArmorOnWeaponRolls(pc); // include bonuses from Item itself baseBonus += eq.getBonusToHit(pc, true); // If not using ammo stacking, correct for stacked enhancement bonus if (!Globals.checkRule(RuleConstants.AMMOSTACKSWITHWEAPON)) { baseBonus += calcAmmoEqCorrection("WEAPON.TOHIT.ENHANCEMENT", eq, containedEq, ammoUser); } // BONUS:COMBAT|ATTACKS|# // represent extra attacks at BaB // such as from a weapon of 'Speed' int extra_attacks = (int) eq.bonusTo(pc, "WEAPON", "ATTACKS", true); // or possibly the "Haste" spell cast on PC extra_attacks += (int) pc.getTotalBonusTo("COMBAT", "ATTACKS"); String babProgression = null; /* The range == -1 here deals with the case where the weapon is both ranged and melee (e.g. a dagger). The range == -1 indicates that we want the melee progression */ if (eq.isMelee() && range == -1) { babProgression = melee; } else if (eq.isRanged()) { babProgression = ranged; } else { /* A weapon must either be ranged or melee. If it's not, trap it here */ return "???"; } StringTokenizer bTok = new StringTokenizer(babProgression, "/+"); String attack = Delta.toString(Integer.parseInt(bTok.nextToken())); StringBuilder newAttack = new StringBuilder(); for (int i = extra_attacks; i > 0; i--) { newAttack.append(attack).append("/"); } boolean progress = eq.getSafe(ObjectKey.ATTACKS_PROGRESS); int bonusProgress = (int)eq.bonusTo(pc, "WEAPON", "ATTACKSPROGRESS", true); if (bonusProgress != 0) { progress = bonusProgress > 0; } if (progress) { /* For normal weapons, we need to append the original * attack progression which was derived from the BAB to * the end of the extra attacks */ newAttack.append(babProgression); } else { /* This is for Natural weapons and any other weapon * which has its attack progression turned off. The * attack progression should consist of the full number * of attacks at the maximum tohit i.e. without * appending the attacks from the normal attack * progression */ newAttack.append(attack); } StringTokenizer aTok = new StringTokenizer(newAttack.toString(), "/+"); // When attackNum is > 0, the code is looking for a single attack // from the sequence. This section of code down to // if (buildNewAttackSequence) builds a new aTok which contains // only the single attack we're looking for. int selectAttack = attackNum; String singleAttack = ""; boolean buildNewAttackSequence = false; while (aTok.hasMoreTokens() && (selectAttack >= 0)) { singleAttack = aTok.nextToken(); selectAttack--; buildNewAttackSequence = true; } if (buildNewAttackSequence) { aTok = new StringTokenizer(singleAttack, "/+"); } int secondariesToadd = 1 + (int) pc.getTotalBonusTo("COMBAT", "ATTACKS-SECONDARY"); /* * The data team wishes to keep the old syntax for secondary attacks. * The docs have been updated to reflect this. The new syntax (with * the hyphen, see above) is not used in the repository. This comment * is here so that the old syntax will not be deprecated or removed in * the future. */ secondariesToadd += (int) pc.getTotalBonusTo("COMBAT", "SECONDARYATTACKS"); if (!display.hasPrimaryWeapons() && (hitMode == HITMODE_TOTALHIT)) { secondariesToadd = 100; } // Whether to construct a string for secondary attacks. This is only // needed for double weapons because single weapons in the off hand // are processed on their own as secondary weapons. Additionally, We // should only construct a secondary attack string if we are not // looking for a single attack from the sequence (attackNum < 0) boolean doDouble = isDouble && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS) && attackNum < 0; // If the weapon is being considered as a secondary weapon, then we // shouldn't add the full attack progression as a secondary weapon only // gets one attack (plus any added by feats, etc. see extra attacks // above) i.e. we may need to break out of the loop while aTok has more // tokens boolean considerEarlyExit = !isDouble && (hitMode == HITMODE_TWOHIT || display.isSecondaryWeapon(eq)); int toHit = 0; int secondariesAdded = 0; StringBuilder primaryAttack = new StringBuilder(20); StringBuilder secondaryAttack = new StringBuilder(20); StringBuilder totalAttack = new StringBuilder(); while (aTok.hasMoreTokens()) { if (primaryAttack.length() > 0) { primaryAttack.append('/'); } toHit = Integer.parseInt(aTok.nextToken()) + baseBonus; primaryAttack.append(Delta.toString(toHit)); if (doDouble && secondariesAdded < secondariesToadd) { if (secondaryAttack.length() > 0) { secondaryAttack.append('/'); } secondaryAttack.append(Delta.toString(toHit)); } // Just in case we are looping forever if (++secondariesAdded > 100) { break; } if (considerEarlyExit && secondariesAdded >= secondariesToadd) { break; } } totalAttack.append(primaryAttack.toString()); if (secondaryAttack.length() != 0 && (hitMode == HITMODE_TOTALHIT || hitMode == HITMODE_TWOHIT)) { totalAttack.append(";" + secondaryAttack); } return totalAttack.toString(); } private static int modFromArmorOnWeaponRolls(PlayerCharacter pc) { int bonus = 0; /* * Equipped some armor that we're not proficient in? acCheck penalty to * attack rolls */ for (Equipment eq : pc.getEquipmentOfType("Armor", 1)) { if ((eq != null) && (!pc.isProficientWith(eq))) { bonus += EqToken.getAcCheckTokenInt(pc, eq); } } /* * Equipped a shield that we're not proficient in? acCheck penalty to * attack rolls */ for (Equipment eq : pc.getEquipmentOfType("Shield", 1)) { if ((eq != null) && (!pc.isProficientWith(eq))) { bonus += EqToken.getAcCheckTokenInt(pc, eq); } } return bonus; } /** * Calculate the correction required to cancel out all enhancement bonuses * other than the highest one. This is because in some game modes, the * enhancement bonuses for ammo does not stack with that of the weapon. * Normally the key would be WEAPON.TOHIT.ENHANCEMENT or * WEAPON.DAMAGE.ENHANCEMENT * * @param aKey The bonus to get the correction for. * @param weapon The weapon that is holding the ammo or contents. * @param contents The contents fo the weapon * @param ammo The ammo being used in the weapon. * @return The correction from the total enhancement bonus to the highest one. */ private static int calcAmmoEqCorrection(String aKey, Equipment weapon, Equipment contents, Equipment ammo) { float maxEnhancement = 0; float totalEnhancement; float bonusVal; String bonus; if (weapon == null) { return 0; } bonus = weapon.getBonusMap().get(aKey); if (bonus != null) { maxEnhancement = Float.parseFloat(bonus); } totalEnhancement = maxEnhancement; if (contents != null) { bonus = contents.getBonusMap().get(aKey); if (bonus != null) { bonusVal = Float.parseFloat(bonus); totalEnhancement += bonusVal; if (bonusVal > maxEnhancement) { maxEnhancement = bonusVal; } } } if (ammo != null) { bonus = ammo.getBonusMap().get(aKey); if (bonus != null) { bonusVal = Float.parseFloat(bonus); totalEnhancement += bonusVal; if (bonusVal > maxEnhancement) { maxEnhancement = bonusVal; } } } return (int) (maxEnhancement - totalEnhancement); } private static String getDamage(PlayerCharacter pc, Equipment eq, int range, int content, int ammo, boolean bonusOnly, int hands, int damageMode, boolean base) { boolean isDouble = (eq.isDouble() && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)); boolean isDoubleSplit = (eq.isType("Head1") || eq.isType("Head2")); if (eq.isMelee() && (eq.isWeaponTwoHanded(pc))) { if (!isDouble && !isDoubleSplit && (damageMode != DAMAGEMODE_NORMAL) && (damageMode != DAMAGEMODE_TWOHANDS) && (damageMode != DAMAGEMODE_DOUBLE)) { return SettingsHandler.getInvalidDmgText(); } } if (eq.isMelee() && eq.isWeaponOutsizedForPC(pc) && !eq.isNatural()) { return SettingsHandler.getInvalidDmgText(); } if (eq.isWeaponLightForPC(pc) && (hands == 2)) { // if wielding a 'Light' weapon two handed // treat as if wielding 1 handed for damage bonus hands = 1; } String profName = getProfName(eq); String damString = getEqDamage(pc, eq); int meleeDamageStatBonus = (int) pc.getDisplay().getStatBonusTo("COMBAT", "DAMAGE.MELEE"); // TODO: remove this old syntax meleeDamageStatBonus += (int) pc.getDisplay().getStatBonusTo("DAMAGE", "TYPE.MELEE"); double meleeDamageMult = pc.getTotalBonusTo("COMBAT", "DAMAGEMULT:" + hands); meleeDamageMult += pc.getTotalBonusTo("WEAPONPROF=" + profName, "DAMAGEMULT:" + hands); meleeDamageMult += BonusCalc.charBonusTo(eq, "WEAPON", "DAMAGEMULT:" + hands, pc); int bonus = 0; int weaponProfBonus = 0; int eqbonus = 0; int totalBonus = 0; damString = getMonkUnarmed(pc, eq, damString); if (!base) { int index; for (index = 0; index < damString.length(); ++index) { if ((damString.charAt(index) == '+') || (damString.charAt(index) == '-')) { totalBonus = Delta.decode(damString.substring(index)).intValue(); break; } } eqbonus = getEqBonus(pc, eq, content, ammo); bonus = getGeneralBonus(pc, eq, range, meleeDamageStatBonus, meleeDamageMult); weaponProfBonus = getWeaponProfBonus(pc, eq, range); totalBonus += (bonus + weaponProfBonus + eqbonus); damString = damString.substring(0, index); } StringBuilder sb = new StringBuilder(); if (!"0d0".equalsIgnoreCase(damString)) { if (!bonusOnly) { sb.append(damString); } if ((totalBonus != 0) || bonusOnly) { sb.append(Delta.toString(totalBonus)); } } else { sb.append("0"); } // Handle Double weapons if ((damageMode == DAMAGEMODE_DOUBLE) && (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)) { // This is the 'Off-Hand' portion of the double weapon hands = 0; meleeDamageMult = pc.getTotalBonusTo("COMBAT", "DAMAGEMULT:" + hands); meleeDamageMult += pc.getTotalBonusTo("WEAPONPROF=" + profName, "DAMAGEMULT:" + hands); meleeDamageMult += BonusCalc.charBonusTo(eq, "WEAPON", "DAMAGEMULT:" + hands, pc); totalBonus -= eqbonus; /* * eq.getBonusToDamage(false) returns the eq bonus for * the secondary head */ eqbonus = eq.getBonusToDamage(pc, false); if (!eq.getAltDamage(pc).isEmpty()) { totalBonus = 0; damString = eq.getAltDamage(pc); if (damString.lastIndexOf('-') >= 0) { totalBonus = Integer.parseInt(damString.substring(damString .lastIndexOf('-'))); damString = damString.substring(0, damString.lastIndexOf('-')); } else if (damString.lastIndexOf('+') >= 0) { totalBonus = Integer.parseInt(damString.substring(damString .lastIndexOf('+') + 1)); damString = damString.substring(0, damString.lastIndexOf('+')); } } else { weaponProfBonus = 0; bonus = 0; } if (meleeDamageStatBonus > 0) { // getTotalBonusTo() includes the Stat Bonuses // so have to remove them before we can compute // the hands multiplier bonus -= meleeDamageStatBonus; // Off-hand, OneHanded and TwoHanded wield // have a Stat Bonus damage multiplier bonus += (meleeDamageMult * meleeDamageStatBonus); } totalBonus += bonus + weaponProfBonus + eqbonus; sb.append("/"); if (!"0d0".equalsIgnoreCase(damString)) { if (bonusOnly) { sb.append(damString); } if (totalBonus != 0 || bonusOnly) { sb.append(Delta.toString(totalBonus)); } } else { sb.append("0"); } } return sb.toString(); } private static int getGeneralBonus(PlayerCharacter pc, Equipment eq, int range, int meleeDamageStatBonus, double meleeDamageMult) { int bonus = 0; for (String type : eq.typeList()) { //Makes sure that thrown weapons only get the right bonus at the right time if ((range > -1 && type.equalsIgnoreCase("MELEE")) || (range == -1 && (type.equalsIgnoreCase("THROWN") || type .equalsIgnoreCase("RANGED")))) { continue; } bonus += (int) pc.getTotalBonusTo("COMBAT", "DAMAGE." + type); // TODO: remove this old syntax bonus += (int) pc.getTotalBonusTo("DAMAGE", "TYPE." + type); } if (eq.isFinessable(pc) && !eq.isType("Finesseable")) { bonus += (int) pc.getTotalBonusTo("COMBAT", "DAMAGE.Finesseable"); } if (eq.isMelee() && (meleeDamageStatBonus > 0)) { // getTotalBonusTo() includes the Stat Bonuses // so have to remove them before we can compute // the hands multiplier bonus -= meleeDamageStatBonus; // Off-hand, OneHanded and TwoHanded wield // have a Stat Bonus damage multiplier bonus += (meleeDamageMult * meleeDamageStatBonus); } else if (eq.isThrown()) { // Thrown weapons just get stat bonus // and its already been added in the // getTotalBonusTo(TYPE) above } // If at short range, add SHORTRANGE bonus if (range > -1) { int rangeSize = getRangeList(eq, true, pc).size(); if ((range < rangeSize) && (Integer.parseInt(getRangeList(eq, true, pc).get(range)) <= SettingsHandler .getGame().getShortRangeDistance())) { bonus += (int) eq.bonusTo(pc, "WEAPON", "DAMAGE-SHORTRANGE", true); bonus += (int) pc.getTotalBonusTo("DAMAGE", "SHORTRANGE"); bonus += (int) pc.getTotalBonusTo("COMBAT", "DAMAGE-SHORTRANGE"); } } return bonus; } private static int getWeaponProfBonus(PlayerCharacter pc, Equipment eq, int range) { String profKey = getProfName(eq); int weaponProfBonus = (int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "DAMAGE") + getWeaponProfTypeBonuses(pc, eq, "DAMAGE", WPTYPEBONUS_PC); if (eq.isRanged()) { weaponProfBonus -= ((int) pc.getBonusDueToType("WEAPONPROF=" + profKey.toUpperCase(), "DAMAGE", "NOTRANGED") + getWeaponProfTypeBonuses( pc, eq, "DAMAGE.NOTRANGED", WPTYPEBONUS_PC)); } // If at short range, add SHORTRANGE bonus if (range > -1) { int rangeSize = getRangeList(eq, true, pc).size(); if ((range < rangeSize) && (Integer.parseInt(getRangeList(eq, true, pc).get(range)) <= SettingsHandler .getGame().getShortRangeDistance())) { weaponProfBonus += ((int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "DAMAGE-SHORTRANGE") + getWeaponProfTypeBonuses(pc, eq, "DAMAGE-SHORTRANGE", WPTYPEBONUS_PC)); } } return weaponProfBonus; } private static int getEqBonus(PlayerCharacter pc, Equipment eq, int content, int ammo) { int eqbonus = eq.getBonusToDamage(pc, true); //Ammunition & Contents Modifier Equipment containedEq = null; if (content > -1) { if (content < eq.getContainedEquipmentCount()) { containedEq = eq.getContainedEquipment(content); eqbonus += containedEq.getBonusToDamage(pc, true); } } Equipment ammoUser = getAmmoUser(pc, eq, ammo); if (ammoUser != null) { eqbonus += ammoUser.getBonusToDamage(pc, true); } // If not using ammo stacking, correct for stacked enhancement bonus if (!Globals.checkRule(RuleConstants.AMMOSTACKSWITHWEAPON)) { eqbonus += calcAmmoEqCorrection("WEAPON.DAMAGE.ENHANCEMENT", eq, containedEq, ammoUser); } return eqbonus; } private static String getMonkUnarmed(PlayerCharacter pc, Equipment eq, String damString) { if (eq.isMonk() && eq.isUnarmed()) { int eqSize = pc.getDisplay().getRace().getSafe(FormulaKey.SIZE).resolve(pc, "") .intValue(); int iMod = pc.getDisplay().sizeInt(); /* This modifies damage (by size) from the default when the race is * not the default size and the character is the default size for * their race */ boolean applySize = (eqSize == iMod); String uDamString = UnarmedDamageDisplay.getUnarmedDamageString(pc, false, applySize); StringTokenizer bTok = new StringTokenizer(damString, " d+-", false); bTok.nextToken(); String b1String = bTok.nextToken(); StringTokenizer cTok = new StringTokenizer(uDamString, " d+-", false); cTok.nextToken(); String c1String = cTok.nextToken(); if (Integer.parseInt(b1String) < Integer.parseInt(c1String)) { damString = uDamString; } /* * This modifies damage by size when the character is a different size * than the race. It also modifies it by applying any Bonuses to damage * size. */ iMod += (int) pc.getTotalBonusTo("WEAPONPROF=Unarmed Strike", "DAMAGESIZE"); iMod += (int) pc.getTotalBonusTo("COMBAT", "DAMAGESIZE"); /* If not applying the race size modifier, then damString will * represent the damage as if this Character were the default * size. Set eqSize to adjust from damage for the default size, * not the race's actual size. */ if (!applySize) { final SizeAdjustment defAdj = SizeUtilities.getDefaultSizeAdjustment(); if (defAdj != null) { eqSize = defAdj.get(IntegerKey.SIZEORDER); } } damString = Globals.adjustDamage(damString, eqSize, iMod); } return damString; } private static String getEqDamage(PlayerCharacter pc, Equipment eq) { String retString = eq.getDamage(pc); if (pc == null) { return retString; } String profKey = getProfName(eq); if (eq.isNatural()) { // int eqSize = Globals.sizeInt(pc.getRace().getSize()); int eqSize = pc.getDisplay().racialSizeInt(); int iMod = pc.getDisplay().sizeInt(); iMod += (int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "DAMAGESIZE"); iMod += (int) pc.getTotalBonusTo("COMBAT", "DAMAGESIZE"); retString = Globals.adjustDamage(retString, eqSize, iMod); } else { int eqSize = eq.sizeInt(); int iMod = eqSize; iMod += (int) pc.getTotalBonusTo("WEAPONPROF=" + profKey, "DAMAGESIZE"); iMod += (int) pc.getTotalBonusTo("COMBAT", "DAMAGESIZE"); retString = Globals.adjustDamage(retString, eqSize, iMod); } return retString; } private static Equipment getAmmoUser(PlayerCharacter pc, Equipment eq, int ammo) { int ammoCount = 0; if (ammo < 0) { return null; } String containerCapacity = eq.getContainerCapacityString(); for (Equipment equip : pc.getEquipmentListInOutputOrder()) { for (String type : equip.typeList()) { if (containerCapacity.indexOf(type) >= 0) { ++ammoCount; break; } } if (ammoCount == (ammo + 1)) { return equip; } } return null; } private static int getWeaponProfTypeBonuses(PlayerCharacter pc, Equipment eq, String bonusType, int index) { int bonus = 0; boolean hasBoth = (eq.isRanged() && eq.isMelee()); CDOMSingleRef<WeaponProf> ref = eq.get(ObjectKey.WEAPON_PROF); if (ref == null) { return 0; } WeaponProf wp = ref.get(); StringTokenizer aTok = new StringTokenizer(wp.getType(), "."); while (aTok.hasMoreTokens()) { String tString = aTok.nextToken(); if (!hasBoth || !"RANGED".equalsIgnoreCase(tString)) { switch (index) { case WPTYPEBONUS_PC: bonus += (int) pc.getTotalBonusTo("WEAPONPROF=TYPE." + tString, bonusType); break; case WPTYPEBONUS_EQ: bonus += (int) BonusCalc.charBonusTo(eq, "WEAPONPROF=TYPE." + tString, bonusType, pc); break; case WPTYPEBONUS_FEAT: bonus += (int) pc.getFeatBonusTo("WEAPONPROF=TYPE." + tString, bonusType); break; case WPTYPEBONUS_TEMPLATE: bonus += (int) pc.getTemplateBonusTo("WEAPONPROF=TYPE." + tString, bonusType); break; default: Logging .errorPrint("In getWeaponProfTypeBonuses there is an unhandled case in a switch (the value is " + index + "."); break; } } } return bonus; } private static String weaponCategories(Equipment eq) { StringBuilder wc = new StringBuilder(10); StringTokenizer aTok = new StringTokenizer(SettingsHandler.getGame() .getWeaponCategories(), "|", false); while (aTok.hasMoreTokens()) { String type = aTok.nextToken(); if (eq.isType(type, true)) { if (wc.length() != 0) { wc.append('/'); } wc.append(type); } } if (wc.length() == 0) { wc.append("Non-Standard"); } return wc.toString(); } private static String weaponTypes(Equipment eq, boolean primary) { StringBuilder wt = new StringBuilder(10); StringTokenizer aTok = new StringTokenizer(SettingsHandler.getGame().getWeaponTypes(), "|", false); while (aTok.countTokens() >= 2) { String type = aTok.nextToken(); String abbrev = aTok.nextToken(); if (eq.isType(type, primary)) { wt.append(abbrev); } } return wt.toString(); } /** * Get the ranged attack string for this {@code pc} * * @param pc The character that this ranged attack string is for * @return The ranged attack string affected only by BAB */ private static String getRangedAttackString(PlayerCharacter pc) { return pc.getAttackString(AttackType.RANGED, 0, 0); } /** * Get the ranged attack string for this {@code pc}. Use * {@code bonus} to affect the size of attacks e.g. +9/+4 with * bonus 2 becomes +11/+6. Use {@code BABbonus} to affect the * size and number of attacks e.g. +9/+4 with BABBonus 2 becomes * +11/+6/+1. * * @param pc The character that this ranged attack string is for * @param bonus An increase to be applied to each number in the attack * string. * @param BABBonus A bonus Which also affects the number of attacks in * the returned attackString. * @return The ranged attack string with number and size of attacks * affected by BAB and bonus */ private static String getRangedAttackString(PlayerCharacter pc, int bonus, int BABBonus) { return pc.getAttackString(AttackType.RANGED, bonus, BABBonus); } /** * Get the melee attack string for this {@code pc} * * @param pc The character that this melee attack string is for * @return The melee attack string affected only by BAB */ private static String getMeleeAttackString(PlayerCharacter pc) { return pc.getAttackString(AttackType.MELEE, 0, 0); } /** * Get the melee attack string for this {@code pc}. Use * {@code bonus} to affect the size of attacks e.g. +9/+4 with * bonus 2 becomes +11/+6. Use {@code BABbonus} to affect the * size and number of attacks e.g. +9/+4 with BABBonus 2 becomes * +11/+6/+1. * * @param pc The character that this melee attack string is for * @param bonus An increase to be applied to each number in the attack * string. * @param BABBonus A bonus Which also affects the number of attacks in * the returned attackString. * @return The melee attack string with number and size of attacks * affected by BAB and bonus */ private static String getMeleeAttackString(PlayerCharacter pc, int bonus, int BABBonus) { return pc.getAttackString(AttackType.MELEE, bonus, BABBonus); } /** * Get the unarmed attack string for this {@code pc} * * @param pc The character that this unarmed attack string is for * @return The unarmed attack string affected only by BAB */ private static String getUnarmedAttackString(PlayerCharacter pc) { return pc.getAttackString(AttackType.UNARMED, 0, 0); } /** * Get the unarmed attack string for this {@code pc}. Use * {@code bonus} to affect the size of attacks e.g. +9/+4 with * bonus 2 becomes +11/+6. Use {@code BABbonus} to affect the * size and number of attacks e.g. +9/+4 with BABBonus 2 becomes * +11/+6/+1. * * @param pc The character that this unarmed attack string is for * @param bonus An increase to be applied to each number in the attack * string. * @param BABBonus A bonus Which also affects the number of attacks in * the returned attackString. * @return The unarmed attack string with number and size of attacks * affected by BAB and bonus */ private static String getUnarmedAttackString(PlayerCharacter pc, int bonus, int BABBonus) { return pc.getAttackString(AttackType.UNARMED, bonus, BABBonus); } /** * If the equipment has a type beyond natural then we need a * seperator * @param eq * @return true if we need a sepearator */ private static boolean appendSeperator(Equipment eq) { if (eq.isType("Natural") && (eq.isType("Both") || eq.isType("Melee") || eq.isType("Ranged"))) { return true; } return false; } /** * If none of the four types are true then it's non standard * @param eq * @return true if non standard */ private static boolean isNonStandard(Equipment eq) { if (eq.isType("Natural") || eq.isType("Both") || eq.isType("Melee") || eq.isType("Ranged")) { return false; } return true; } /** * Gets the range list of the Equipment object, adding the 30' range, if not present and required * * @param addShortRange boolean * @param aPC * @return The range list */ public static List<String> getRangeList(Equipment eq, boolean addShortRange, final PlayerCharacter aPC) { final List<String> aList = new ArrayList<>(); final int baseRange = EqToken.getRange(aPC, eq).intValue(); int aRange = baseRange; int maxIncrements = 0; if (eq.isRanged()) { if (eq.isThrown()) { maxIncrements = 5; } else { maxIncrements = 10; } } for (int numIncrements = 0; numIncrements < maxIncrements; ++numIncrements) { if (aRange == SettingsHandler.getGame().getShortRangeDistance()) { addShortRange = false; } if ((aRange > SettingsHandler.getGame().getShortRangeDistance()) && addShortRange) { aList.add(Integer.toString(SettingsHandler.getGame().getShortRangeDistance())); addShortRange = false; } aList.add(Integer.toString(aRange)); aRange += baseRange; } return aList; } public static String getNewCritRangeString(PlayerCharacter pc, Equipment eq, String critRangeVar) { StringBuilder sb = new StringBuilder(); boolean needSlash = false; for (EquipmentHead head : eq.getEquipmentHeads()) { if (needSlash) { sb.append("/"); } sb.append(getCritRangeHead(pc, head, critRangeVar)); needSlash = true; } return sb.toString(); } public static StringBuilder getCritRangeHead(PlayerCharacter pc, EquipmentHead head, String critRangeVar) { StringBuilder sb = new StringBuilder(); Integer range = (Integer) head.getLocalVariable(pc.getCharID(), critRangeVar); sb.append(range); if (range < 20) { sb.append("-20"); } return sb; } }