/* * Copyright (c) 2008 Tom Parker <thpr@users.sourceforge.net> * * This program 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 program 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package plugin.lsttokens.race; import pcgen.base.formula.AddingFormula; import pcgen.base.formula.DividingFormula; import pcgen.base.formula.MultiplyingFormula; import pcgen.base.formula.SubtractingFormula; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.base.Constants; import pcgen.cdom.content.HitDie; import pcgen.cdom.content.Processor; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.processor.ContextProcessor; import pcgen.cdom.processor.HitDieFormula; import pcgen.cdom.processor.HitDieLock; import pcgen.cdom.processor.HitDieStep; import pcgen.core.PCClass; import pcgen.core.Race; import pcgen.rules.context.LoadContext; import pcgen.rules.persistence.token.AbstractNonEmptyToken; import pcgen.rules.persistence.token.CDOMPrimaryToken; import pcgen.rules.persistence.token.ParseResult; /** * Class deals with HITDIE Token */ public class HitdieToken extends AbstractNonEmptyToken<Race> implements CDOMPrimaryToken<Race> { private static final Class<PCClass> PCCLASS_CLASS = PCClass.class; @Override public String getTokenName() { return "HITDIE"; } @Override protected ParseResult parseNonEmptyToken(LoadContext context, Race race, String value) { try { String lock = value; int pipeLoc = lock.indexOf(Constants.PIPE); if (pipeLoc != lock.lastIndexOf(Constants.PIPE)) { return new ParseResult.Fail(getTokenName() + " has more than one pipe, " + "is not of format: <int>[|<prereq>]", context); } // Do not initialize, null is significant CDOMReference<PCClass> owner = null; if (pipeLoc != -1) { // Has a limitation String lockPre = lock.substring(pipeLoc + 1); if (lockPre.startsWith("CLASS.TYPE=")) { String substring = lock.substring(pipeLoc + 12); if (substring.isEmpty()) { return new ParseResult.Fail( "Cannot have Empty Type Limitation in " + getTokenName() + ": " + value, context); } ParseResult pr = checkForIllegalSeparator('.', substring); if (!pr.passed()) { return pr; } owner = context.getReferenceContext().getCDOMTypeReference(PCCLASS_CLASS, substring.split("\\.")); } else if (lockPre.startsWith(Constants.LST_CLASS_EQUAL)) { String substring = lock.substring(pipeLoc + 7); if (substring.isEmpty()) { return new ParseResult.Fail( "Cannot have Empty Class Limitation in " + getTokenName() + ": " + value, context); } owner = context.getReferenceContext().getCDOMReference(PCCLASS_CLASS, substring); } else { return new ParseResult.Fail("Invalid Limitation in HITDIE: " + lockPre, context); } lock = lock.substring(0, pipeLoc); } Processor<HitDie> hdm; if (lock.startsWith("%/")) { // HITDIE:%/num --- divides the classes hit die by num. int denom = Integer.parseInt(lock.substring(2)); if (denom <= 0) { return new ParseResult.Fail(getTokenName() + " was expecting a Positive Integer " + "for dividing Lock, was : " + lock.substring(2), context); } hdm = new HitDieFormula(new DividingFormula(denom)); } else if (lock.startsWith("%*")) { // HITDIE:%*num --- multiplies the classes hit die by num. int mult = Integer.parseInt(lock.substring(2)); if (mult <= 0) { return new ParseResult.Fail(getTokenName() + " was expecting a Positive " + "Integer for multiplying Lock, was : " + lock.substring(2), context); } hdm = new HitDieFormula(new MultiplyingFormula(mult)); } else if (lock.startsWith("%+")) { // possibly redundant with BONUS:HD MAX|num // HITDIE:%+num --- adds num to the classes hit die. int add = Integer.parseInt(lock.substring(2)); if (add <= 0) { return new ParseResult.Fail(getTokenName() + " was expecting a Positive " + "Integer for adding Lock, was : " + lock.substring(2), context); } hdm = new HitDieFormula(new AddingFormula(add)); } else if (lock.startsWith("%-")) { // HITDIE:%-num --- subtracts num from the classes hit die. // possibly redundant with BONUS:HD MAX|num if that will // take negative numbers. int sub = Integer.parseInt(lock.substring(2)); if (sub <= 0) { return new ParseResult.Fail(getTokenName() + " was expecting a Positive " + "Integer for subtracting Lock, was : " + lock.substring(2), context); } hdm = new HitDieFormula(new SubtractingFormula(sub)); } else if (lock.startsWith("%up")) { // HITDIE:%upnum --- moves the hit die num steps up the die size // list d4,d6,d8,d10,d12. Stops at d12. int steps = Integer.parseInt(lock.substring(3)); if (steps <= 0) { return new ParseResult.Fail("Invalid Step Count: " + steps + " in " + getTokenName() + " up (must be positive)", context); } if (steps >= 5) { return new ParseResult.Fail("Invalid Step Count: " + steps + " in " + getTokenName() + " up (too large)", context); } hdm = new HitDieStep(steps, new HitDie(12)); } else if (lock.startsWith("%Hup")) { // HITDIE:%upnum --- moves the hit die num steps up the die size // list d4,d6,d8,d10,d12. Stops at d12. int steps = Integer.parseInt(lock.substring(4)); if (steps <= 0) { return new ParseResult.Fail("Invalid Step Count: " + steps + " in " + getTokenName(), context); } hdm = new HitDieStep(steps, null); } else if (lock.startsWith("%down")) { // HITDIE:%downnum --- moves the hit die num steps down the die // size // list d4,d6,d8,d10,d12. Stops at d4. int steps = Integer.parseInt(lock.substring(5)); if (steps <= 0) { return new ParseResult.Fail("Invalid Step Count: " + steps + " in " + getTokenName() + " down (must be positive)", context); } if (steps >= 5) { return new ParseResult.Fail("Invalid Step Count: " + steps + " in " + getTokenName() + " down (too large)", context); } hdm = new HitDieStep(-steps, new HitDie(4)); } else if (lock.startsWith("%Hdown")) { // HITDIE:%downnum --- moves the hit die num steps down the die // size // list. No limit. int steps = Integer.parseInt(lock.substring(6)); if (steps <= 0) { return new ParseResult.Fail("Invalid Step Count: " + steps + " in " + getTokenName(), context); } hdm = new HitDieStep(-steps, null); } else { int i = Integer.parseInt(lock); if (i <= 0) { return new ParseResult.Fail("Invalid HitDie: " + i + " in " + getTokenName(), context); } // HITDIE:num --- sets the hit die to num regardless of class. hdm = new HitDieLock(new HitDie(i)); } Processor<HitDie> mod = owner == null ? hdm : new ContextProcessor<>(hdm, owner); context.getObjectContext().put(race, ObjectKey.HITDIE, mod); return ParseResult.SUCCESS; } catch (NumberFormatException nfe) { return new ParseResult.Fail("Invalid Number (must be an Integer) in " + getTokenName() + ": " + nfe.getLocalizedMessage(), context); } } @Override public String[] unparse(LoadContext context, Race race) { Processor<HitDie> hdcf = context.getObjectContext().getObject(race, ObjectKey.HITDIE); if (hdcf == null) { return null; } return new String[] { hdcf.getLSTformat() }; } @Override public Class<Race> getTokenClass() { return Race.class; } }