/* * Copyright (c) 1998-2017 by Richard A. Wilkes. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, version 2.0. If a copy of the MPL was not distributed with * this file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This Source Code Form is "Incompatible With Secondary Licenses", as * defined by the Mozilla Public License, version 2.0. */ package com.trollworks.gcs.weapon; import com.trollworks.gcs.character.GURPSCharacter; import com.trollworks.gcs.common.DataFile; import com.trollworks.gcs.skill.SkillDefault; import com.trollworks.gcs.skill.SkillDefaultType; import com.trollworks.gcs.widgets.outline.ListRow; import com.trollworks.toolkit.io.xml.XMLReader; import com.trollworks.toolkit.io.xml.XMLWriter; import com.trollworks.toolkit.utility.text.Numbers; import java.io.IOException; import java.util.HashSet; import java.util.StringTokenizer; /** The stats for a melee weapon. */ public class MeleeWeaponStats extends WeaponStats { /** The root XML tag. */ public static final String TAG_ROOT = "melee_weapon"; //$NON-NLS-1$ private static final String TAG_REACH = "reach"; //$NON-NLS-1$ private static final String TAG_PARRY = "parry"; //$NON-NLS-1$ private static final String TAG_BLOCK = "block"; //$NON-NLS-1$ /** The field ID for reach changes. */ public static final String ID_REACH = PREFIX + TAG_REACH; /** The field ID for parry changes. */ public static final String ID_PARRY = PREFIX + TAG_PARRY; /** The field ID for block changes. */ public static final String ID_BLOCK = PREFIX + TAG_BLOCK; private String mReach; private String mParry; private String mBlock; /** * Creates a new {@link MeleeWeaponStats}. * * @param owner The owning piece of equipment or advantage. */ public MeleeWeaponStats(ListRow owner) { super(owner); } /** * Creates a clone of the specified {@link MeleeWeaponStats}. * * @param owner The owning piece of equipment or advantage. * @param other The {@link MeleeWeaponStats} to clone. */ public MeleeWeaponStats(ListRow owner, MeleeWeaponStats other) { super(owner, other); mReach = other.mReach; mParry = other.mParry; mBlock = other.mBlock; } /** * Creates a {@link MeleeWeaponStats}. * * @param owner The owning piece of equipment or advantage. * @param reader The reader to load from. */ public MeleeWeaponStats(ListRow owner, XMLReader reader) throws IOException { super(owner, reader); } @Override public WeaponStats clone(ListRow owner) { return new MeleeWeaponStats(owner, this); } @Override protected void initialize() { mReach = EMPTY; mParry = EMPTY; mBlock = EMPTY; } @Override protected void loadSelf(XMLReader reader) throws IOException { String name = reader.getName(); if (TAG_REACH.equals(name)) { mReach = reader.readText(); } else if (TAG_PARRY.equals(name)) { mParry = reader.readText(); } else if (TAG_BLOCK.equals(name)) { mBlock = reader.readText(); } else { super.loadSelf(reader); } } @Override protected String getRootTag() { return TAG_ROOT; } @Override protected void saveSelf(XMLWriter out) { out.simpleTagNotEmpty(TAG_REACH, mReach); out.simpleTagNotEmpty(TAG_PARRY, mParry); out.simpleTagNotEmpty(TAG_BLOCK, mBlock); } /** @return The parry. */ public String getParry() { return mParry; } /** @return The parry, fully resolved for the user's skills, if possible. */ public String getResolvedParry() { return getResolvedValue(mParry, SkillDefaultType.Parry); } private String getResolvedValue(String input, SkillDefaultType baseDefaultType) { DataFile df = getOwner().getDataFile(); if (df instanceof GURPSCharacter) { GURPSCharacter character = (GURPSCharacter) df; StringTokenizer tokenizer = new StringTokenizer(input, "\n\r", true); //$NON-NLS-1$ StringBuffer buffer = new StringBuffer(); int skillLevel = Integer.MAX_VALUE; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (!token.equals("\n") && !token.equals("\r")) { //$NON-NLS-1$ //$NON-NLS-2$ int max = token.length(); int i = skipSpaces(token, 0); if (i < max) { char ch = token.charAt(i); boolean neg = false; int modifier = 0; boolean found = false; if (ch == '-' || ch == '+') { neg = ch == '-'; if (++i < max) { ch = token.charAt(i); } } while (i < max && ch >= '0' && ch <= '9') { found = true; modifier *= 10; modifier += ch - '0'; if (++i < max) { ch = token.charAt(i); } } if (found) { String num; if (skillLevel == Integer.MAX_VALUE) { int best = Integer.MIN_VALUE; for (SkillDefault skillDefault : getDefaults()) { SkillDefaultType type = skillDefault.getType(); int level = type.getSkillLevelFast(character, skillDefault, new HashSet<String>()); if (level != Integer.MIN_VALUE && type != baseDefaultType) { level = level / 2 + 3 + (baseDefaultType == SkillDefaultType.Parry ? character.getParryBonus() : character.getBlockBonus()); } if (level > best) { best = level; } } skillLevel = best != Integer.MIN_VALUE ? best : 0; } num = Numbers.format(skillLevel + (neg ? -modifier : modifier)); if (i < max) { buffer.append(num); token = token.substring(i); } else { token = num; } } } } buffer.append(token); } return buffer.toString(); } return input; } /** * Sets the value of parry. * * @param parry The value to set. */ public void setParry(String parry) { parry = sanitize(parry); if (!mParry.equals(parry)) { mParry = parry; notifySingle(ID_PARRY); } } /** @return The block. */ public String getBlock() { return mBlock; } /** @return The block, fully resolved for the user's skills, if possible. */ public String getResolvedBlock() { return getResolvedValue(mBlock, SkillDefaultType.Block); } /** * Sets the value of block. * * @param block The value to set. */ public void setBlock(String block) { block = sanitize(block); if (!mBlock.equals(block)) { mBlock = block; notifySingle(ID_BLOCK); } } /** @return The reach. */ public String getReach() { return mReach; } /** * Sets the value of reach. * * @param reach The value to set. */ public void setReach(String reach) { reach = sanitize(reach); if (!mReach.equals(reach)) { mReach = reach; notifySingle(ID_REACH); } } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof MeleeWeaponStats && super.equals(obj)) { MeleeWeaponStats mws = (MeleeWeaponStats) obj; return mReach.equals(mws.mReach) && mParry.equals(mws.mParry) && mBlock.equals(mws.mBlock); } return false; } }