public static void removeDomainsForLevel( PCClass cl, final int removedLevel, final PlayerCharacter aPC) { /* * Note this uses ALL of the domains up to and including this level, * because there is the possibility (albeit strange) that the PC was * qualified at a previous level change, but the PlayerCharacter is now * not qualified for the given Domain. Even this has quirks, since it is * only applied at the time of level increase, but I think that quirk * should be resolved by a CDOM system around 6.0 - thpr 10/23/06 */ for (QualifiedObject<CDOMSingleRef<Domain>> qo : cl.getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if (ref == null) { ref = qo.getRawObject(); aPC.removeDomain(ref.resolvesTo()); } } for (int i = 0; i <= removedLevel; i++) { // TODO This stinks for really high level characters - can this ever // get null back? PCClassLevel pcl = aPC.getActiveClassLevel(cl, i); for (QualifiedObject<CDOMSingleRef<Domain>> qo : pcl.getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if ((ref == null) || (i == removedLevel)) { ref = qo.getRawObject(); aPC.removeDomain(ref.resolvesTo()); } } } }
/* (non-Javadoc) * @see pcgen.core.prereq.PrerequisiteTest#passes(pcgen.core.PlayerCharacter) */ @Override public int passes(final Prerequisite prereq, final CharacterDisplay display, CDOMObject source) { // // If game mode doesn't support alignment, then pass the prereq // int runningTotal = 0; if (Globals.getGameModeAlignmentText().length() == 0) { runningTotal = 1; } else { CDOMSingleRef<PCAlignment> deityAlign = null; // $NON-NLS-1$ Deity deity = display.getDeity(); if (deity != null) { deityAlign = deity.get(ObjectKey.ALIGNMENT); } if (deityAlign != null) { String desiredAlignIdentifier = prereq.getOperand(); PCAlignment desiredAlign = getPCAlignment(desiredAlignIdentifier); if (desiredAlign.equals(deityAlign.get())) { runningTotal = 1; } } } return countedTotal(prereq, runningTotal); }
public static void addDomainsUpToLevel(PCClass cl, final int aLevel, final PlayerCharacter aPC) { // any domains set by level would have already been saved // and don't need to be re-set at level up time if (aPC.isImporting()) { return; } /* * Note this uses ALL of the domains up to and including this level, * because there is the possibility (albeit strange) that the PC was not * qualified at a previous level change, but the PlayerCharacter is now * qualified for the given Domain. Even this has quirks, since it is * only applied at the time of level increase, but I think that quirk * should be resolved by a CDOM system around 6.0 - thpr 10/23/06 */ for (QualifiedObject<CDOMSingleRef<Domain>> qo : cl.getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if (ref != null) { addDomain(aPC, cl, ref.resolvesTo()); } } for (int i = 0; i <= aLevel; i++) { // TODO This stinks for really high level characters - can this ever // get null back? PCClassLevel pcl = aPC.getActiveClassLevel(cl, i); for (QualifiedObject<CDOMSingleRef<Domain>> qo : pcl.getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if (ref != null) { addDomain(aPC, cl, ref.resolvesTo()); } } } }
/** * Returns the stat to use for bonus spells. * * <p>The method checks to see if a BONUSSPELLSTAT: has been set for the class. If it is set to a * stat that stat is returned. If it is set to None null is returned. If it is set to Default then * the BASESPELLSTAT is returned. * * @return the stat to use for bonus spells. */ public PCStat bonusSpellStat() { Boolean hbss = get(ObjectKey.HAS_BONUS_SPELL_STAT); if (hbss == null) { return baseSpellStat(); } else if (hbss) { CDOMSingleRef<PCStat> bssref = get(ObjectKey.BONUS_SPELL_STAT); if (bssref != null) { return bssref.get(); } } return null; }
public boolean appliesToRace(Race r) { List<CDOMSingleRef<Race>> list = getListFor(ListKey.APPLIED_RACE); if (list != null) { for (CDOMSingleRef<Race> ref : list) { Race race = ref.get(); if (race.equals(r)) { return true; } } } return false; }
/* * FINALPCCLASSANDLEVEL This is required in PCClassLevel and should be present in * PCClass for PCClassLevel creation (in the factory) */ public final String getSpellBaseStat() { Boolean useStat = get(ObjectKey.USE_SPELL_SPELL_STAT); if (useStat == null) { return "None"; } else if (useStat) { return "SPELL"; } Boolean otherCaster = get(ObjectKey.CASTER_WITHOUT_SPELL_STAT); if (otherCaster) { return "OTHER"; } CDOMSingleRef<PCStat> ss = get(ObjectKey.SPELL_STAT); // TODO This could be null, do we need to worry about it? return ss.get().getKeyName(); }
@Override public boolean testApply(Kit aKit, PlayerCharacter aPC, List<String> warnings) { skillsToAdd = new ArrayList<KitSkillAdd>(); List<Skill> skillChoices = getSkillChoices(aPC); if (skillChoices == null || skillChoices.size() == 0) { // They didn't make a choice so don't add any ranks. return false; } for (Skill skill : skillChoices) { BigDecimal ranksLeftToAdd = getRank(); if (ranksLeftToAdd == null) { ranksLeftToAdd = BigDecimal.ONE; } double ranksLeft = ranksLeftToAdd.doubleValue(); List<PCClass> classList = new ArrayList<PCClass>(); if (className != null) { String classKey = className.resolvesTo().getKeyName(); // Make sure if they specified a class to add from we try that // class first. PCClass pcClass = aPC.getClassKeyed(classKey); if (pcClass != null) { classList.add(pcClass); } else { warnings.add( "SKILL: Could not find specified class " + classKey + " in PC to add ranks from."); } } for (PCClass pcClass : aPC.getClassSet()) { if (!classList.contains(pcClass)) { classList.add(pcClass); } } // Try and find a class we can add them from. boolean oldImporting = aPC.isImporting(); aPC.setImporting(true); for (PCClass pcClass : classList) { final KitSkillAdd sta = addRanks(aPC, pcClass, skill, ranksLeft, isFree(), warnings); if (sta != null) { skillsToAdd.add(sta); ranksLeft -= sta.getRanks(); if (ranksLeft <= 0.0) { break; } } } aPC.setImporting(oldImporting); if (ranksLeft > 0.0) { warnings.add( "SKILL: Could not add " + ranksLeft + " ranks to " + skill.getKeyName() + ". Not enough points."); } } return true; }
/* * REFACTOR Why is this returning an INT and not a PCStat or something like * that? or why is the user not just using getSpellBaseStat and processing * the response by itself?? */ public PCStat baseSpellStat() { if (getSafe(ObjectKey.USE_SPELL_SPELL_STAT)) { return null; } if (getSafe(ObjectKey.CASTER_WITHOUT_SPELL_STAT)) { return null; } CDOMSingleRef<PCStat> ss = get(ObjectKey.SPELL_STAT); if (ss != null) { return ss.get(); } if (Logging.isDebugMode()) { Logging.debugPrint( "Found Class: " + getDisplayName() + " that did not have any SPELLSTAT defined"); } return null; }
@Override public <R> Collection<R> getCollection(PlayerCharacter pc, Converter<T, R> c) { /* * In theory the converter can be ignored here, since an equivalent * would exist within the ChooseInformation */ List<R> currentItems = getList(pc, ref.get()); if (currentItems == null) { return Collections.emptySet(); } return new HashSet<>(currentItems); }
@Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof AbilityToken) { AbilityToken<?> other = (AbilityToken<?>) obj; if (ref == null) { return (other.ref == null) && (refClass == null) && (other.refClass == null); } return refClass.equals(other.refClass) && ref.equals(other.ref); } return false; }
@Override public String[] unparse(LoadContext context, CDOMObject obj) { Collection<CDOMReference<? extends CDOMList<?>>> changedLists = context.getListContext().getChangedLists(obj, AbilityList.class); Changes<ListKey<ChooseSelectionActor<?>>> actors = context.getObjectContext().getListChanges(obj, ListKey.GA_CAKEYS); Set<String> returnSet = new TreeSet<String>(); TripleKeyMapToList< Nature, CDOMSingleRef<AbilityCategory>, List<Prerequisite>, CDOMReference<Ability>> m = new TripleKeyMapToList< Nature, CDOMSingleRef<AbilityCategory>, List<Prerequisite>, CDOMReference<Ability>>(); TripleKeyMapToList< Nature, CDOMSingleRef<AbilityCategory>, List<Prerequisite>, CDOMReference<Ability>> clear = new TripleKeyMapToList< Nature, CDOMSingleRef<AbilityCategory>, List<Prerequisite>, CDOMReference<Ability>>(); Changes<ChooseSelectionActor<?>> listChanges = context.getObjectContext().getListChanges(obj, ListKey.NEW_CHOOSE_ACTOR); Collection<ChooseSelectionActor<?>> listAdded = listChanges.getAdded(); if (listAdded != null && !listAdded.isEmpty()) { for (ChooseSelectionActor<?> csa : listAdded) { if (csa.getSource().equals(getTokenName())) { try { AbilitySelector as = (AbilitySelector) csa; StringBuilder sb = new StringBuilder(); sb.append(as.getAbilityCategory().getLSTformat(false)).append(Constants.PIPE); sb.append(as.getNature()).append(Constants.PIPE); sb.append(as.getLstFormat()); returnSet.add(sb.toString()); } catch (PersistenceLayerException e) { context.addWriteMessage(getTokenName() + " encountered error: " + e.getMessage()); return null; } } } } for (CDOMReference ref : changedLists) { AssociatedChanges<CDOMReference<Ability>> changes = context.getListContext().getChangesInList(getTokenName(), obj, ref); if (changes.includesGlobalClear()) { CDOMDirectSingleRef<AbilityList> dr = (CDOMDirectSingleRef<AbilityList>) ref; AbilityList al = dr.get(); StringBuilder sb = new StringBuilder(); sb.append(al.getCategory().getLSTformat(false)).append(Constants.PIPE); sb.append(al.getNature()).append(Constants.PIPE); sb.append(Constants.LST_DOT_CLEAR); returnSet.add(sb.toString()); } MapToList<CDOMReference<Ability>, AssociatedPrereqObject> mtl = changes.getAddedAssociations(); if (mtl != null) { for (CDOMReference<Ability> ab : mtl.getKeySet()) { for (AssociatedPrereqObject assoc : mtl.getListFor(ab)) { Nature nature = assoc.getAssociation(AssociationKey.NATURE); CDOMSingleRef<AbilityCategory> cat = assoc.getAssociation(AssociationKey.CATEGORY); m.addToListFor(nature, cat, assoc.getPrerequisiteList(), ab); } } } mtl = changes.getRemovedAssociations(); if (mtl != null) { for (CDOMReference<Ability> ab : mtl.getKeySet()) { for (AssociatedPrereqObject assoc : mtl.getListFor(ab)) { Nature nature = assoc.getAssociation(AssociationKey.NATURE); CDOMSingleRef<AbilityCategory> cat = assoc.getAssociation(AssociationKey.CATEGORY); clear.addToListFor(nature, cat, assoc.getPrerequisiteList(), ab); } } } } for (Nature nature : m.getKeySet()) { for (CDOMSingleRef<AbilityCategory> category : m.getSecondaryKeySet(nature)) { for (List<Prerequisite> prereqs : m.getTertiaryKeySet(nature, category)) { StringBuilder sb = new StringBuilder(); sb.append(category.getLSTformat(false)).append(Constants.PIPE); sb.append(nature); List<CDOMReference<Ability>> clearList = clear.removeListFor(nature, category, prereqs); if (clearList != null && !clearList.isEmpty()) { sb.append(Constants.PIPE); sb.append(Constants.LST_DOT_CLEAR_DOT); sb.append( ReferenceUtilities.joinLstFormat( clearList, Constants.PIPE + Constants.LST_DOT_CLEAR_DOT)); } sb.append(Constants.PIPE); sb.append( ReferenceUtilities.joinLstFormat( m.getListFor(nature, category, prereqs), Constants.PIPE)); if (prereqs != null && !prereqs.isEmpty()) { sb.append(Constants.PIPE); sb.append(getPrerequisiteString(context, prereqs)); } returnSet.add(sb.toString()); } } } for (Nature nature : clear.getKeySet()) { for (CDOMSingleRef<AbilityCategory> category : clear.getSecondaryKeySet(nature)) { for (List<Prerequisite> prereqs : clear.getTertiaryKeySet(nature, category)) { StringBuilder sb = new StringBuilder(); sb.append(category.getLSTformat(false)).append(Constants.PIPE); sb.append(nature).append(Constants.PIPE).append(Constants.LST_DOT_CLEAR_DOT); sb.append( ReferenceUtilities.joinLstFormat( clear.getListFor(nature, category, prereqs), Constants.PIPE + Constants.LST_DOT_CLEAR_DOT)); if (prereqs != null && !prereqs.isEmpty()) { sb.append(Constants.PIPE); sb.append(getPrerequisiteString(context, prereqs)); } returnSet.add(sb.toString()); } } } Collection<ListKey<ChooseSelectionActor<?>>> addedActors = actors.getAdded(); if (addedActors != null) { for (ListKey<ChooseSelectionActor<?>> lk : addedActors) { Changes<ChooseSelectionActor<?>> cras = context.getObjectContext().getListChanges(obj, lk); for (ChooseSelectionActor<?> cra : cras.getAdded()) { if (getTokenName().equals(cra.getSource())) { try { AbilityTargetSelector ats = (AbilityTargetSelector) cra; StringBuilder sb = new StringBuilder(); sb.append(ats.getAbilityCategory().getLSTformat(false)).append(Constants.PIPE); sb.append(ats.getNature()).append(Constants.PIPE).append(cra.getLstFormat()); List<Prerequisite> prereqs = ats.getPrerequisiteList(); if (prereqs != null && !prereqs.isEmpty()) { sb.append(Constants.PIPE); sb.append(getPrerequisiteString(context, prereqs)); } returnSet.add(sb.toString()); } catch (PersistenceLayerException e) { context.addWriteMessage(getTokenName() + " encountered error: " + e.getMessage()); return null; } } } } } if (returnSet.isEmpty()) { return null; } return returnSet.toArray(new String[returnSet.size()]); }
/** * Builds up a string describing what makes up the misc modifier for a skill for a character. This * can either be in long form '+2[skill TUMBLE gteq 5|TYPE=SYNERGY.STACK]' or in short form * '+2[TUMBLE]'. Any modifiers that cannot be determined will be displayed as a single entry of * 'OTHER'. * * @param aPC The character associated with this skill. * @param shortForm True if the abbreviated form should be used. * @return The explanation of the misc modifier's make-up. */ public static String getModifierExplanation(Skill sk, PlayerCharacter aPC, boolean shortForm) { double bonusObjTotal = 0.0; List<String> explanation = new ArrayList<>(); String keyName = sk.getKeyName(); String bonusKey = ("SKILL." + keyName).toUpperCase(); for (BonusObj bonus : aPC.getActiveBonusList()) { // calculate bonus and add to activeBonusMap if (aPC.isApplied(bonus) && "SKILL".equals(bonus.getBonusName())) { boolean include = bonusForThisSkill(bonus, keyName); if (!include) { for (BonusPair bp : aPC.getStringListFromBonus(bonus)) { String bpKey = bp.fullyQualifiedBonusType.toUpperCase(); if (bpKey.equals(bonusKey)) { include = true; break; } } } if (include) { double iBonus = 0; for (BonusPair bp : aPC.getStringListFromBonus(bonus)) { String bpKey = bp.fullyQualifiedBonusType.toUpperCase(); if (bpKey.startsWith(bonusKey)) { iBonus += bp.resolve(aPC).doubleValue(); } } if (!CoreUtility.doublesEqual(iBonus, 0.0)) { explanation.add(Delta.toString((int) iBonus) + aPC.getBonusContext(bonus, shortForm)); bonusObjTotal += iBonus; } } } } StringBuilder bonusDetails = new StringBuilder(); bonusDetails.append(StringUtil.joinToStringBuilder(explanation, " ")); // TODO: Need to add other bonuses which are not encoded as bonus // objects // - familiars, racial, feats - and add them to bonusObjTotal double bonus; CDOMSingleRef<PCStat> statref = sk.get(ObjectKey.KEY_STAT); if (statref != null) { PCStat stat = statref.get(); bonus = aPC.getStatModFor(stat); bonus += aPC.getTotalBonusTo("SKILL", "STAT." + stat.getKeyName()); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "STAT"); } // The catch-all for non-bonusObj modifiers. bonus = aPC.getTotalBonusTo("SKILL", keyName) - bonusObjTotal; SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "OTHER"); // loop through all current skill types checking for boni for (Type singleType : sk.getTrueTypeList(false)) { bonus = aPC.getTotalBonusTo("SKILL", "TYPE." + singleType); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "TYPE." + singleType); } // now check for any lists of skills, etc bonus = aPC.getTotalBonusTo("SKILL", "LIST"); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "LIST"); // now check for ALL bonus = aPC.getTotalBonusTo("SKILL", "ALL"); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "ALL"); // these next two if-blocks try to get BONUS:[C]CSKILL|TYPE=xxx|y to // function if (aPC.isClassSkill(sk)) { bonus = aPC.getTotalBonusTo("CSKILL", keyName); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "CSKILL"); // loop through all current skill types checking for boni for (Type singleType : sk.getTrueTypeList(false)) { bonus = aPC.getTotalBonusTo("CSKILL", "TYPE." + singleType); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "CSKILL"); } bonus = aPC.getTotalBonusTo("CSKILL", "LIST"); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "CSKILL"); } if (!aPC.isClassSkill(sk) && !sk.getSafe(ObjectKey.EXCLUSIVE)) { bonus = aPC.getTotalBonusTo("CCSKILL", keyName); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "CCSKILL"); // loop through all current skill types checking for boni for (Type singleType : sk.getTrueTypeList(false)) { bonus = aPC.getTotalBonusTo("CCSKILL", "TYPE." + singleType); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "CCSKILL"); } bonus = aPC.getTotalBonusTo("CCSKILL", "LIST"); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "CCSKILL"); } // Encumbrance int aCheckMod = sk.getSafe(ObjectKey.ARMOR_CHECK).calculateBonus(aPC); SkillCostDisplay.appendBonusDesc(bonusDetails, aCheckMod, "ARMOR"); String aString = SettingsHandler.getGame().getRankModFormula(); if (!aString.isEmpty()) { aString = aString.replaceAll( Pattern.quote("$$RANK$$"), SkillRankControl.getTotalRank(aPC, sk).toString()); bonus = aPC.getVariableValue(aString, "").intValue(); SkillCostDisplay.appendBonusDesc(bonusDetails, bonus, "RANKS"); } return bonusDetails.toString(); }
private KitSkillAdd addRanks( PlayerCharacter pc, PCClass pcClass, Skill aSkill, double ranksLeftToAdd, boolean isFree, List<String> warnings) { if (!isFree && pcClass.getSkillPool(pc) == 0) { return null; } double curRank = 0.0; if (pc.hasSkill(aSkill)) { curRank = pc.getRank(aSkill).doubleValue(); } double ranksToAdd = ranksLeftToAdd; if (!Globals.checkRule(RuleConstants.SKILLMAX) && (ranksToAdd > 0.0)) { ranksToAdd = Math.min(pc.getMaxRank(aSkill, pcClass).doubleValue(), curRank + ranksLeftToAdd); ranksToAdd -= curRank; if (!CoreUtility.doublesEqual(ranksToAdd, ranksLeftToAdd)) { warnings.add( "SKILL: Could not add " + (ranksLeftToAdd - ranksToAdd) + " to " + aSkill.getDisplayName() + ". Exceeds MAXRANK of " + pc.getMaxRank(aSkill, pcClass) + "."); } } int ptsToSpend = 0; int[] points = new int[pc.getLevelInfoSize()]; if (!isFree) { double ranksAdded = 0.0; int skillCost = pc.getSkillCostForClass(aSkill, pcClass).getCost(); ptsToSpend = (int) (ranksToAdd * skillCost); for (int i = 0; i < pc.getLevelInfoSize(); i++) { PCLevelInfo info = pc.getLevelInfo(i); if (info.getClassKeyName().equals(pcClass.getKeyName())) { // We are spending this class' points. points[i] = info.getSkillPointsRemaining(); } else { points[i] = -1; } } for (int i = 0; i < points.length; i++) { int remaining = points[i]; if (remaining <= 0) { continue; } int left = remaining - Math.min(remaining, ptsToSpend); points[i] = left; int spent = (remaining - left); ptsToSpend -= spent; ranksAdded += ((double) spent / (double) skillCost); if (ranksAdded == ranksToAdd || ptsToSpend <= 0) { break; } } ranksToAdd = ranksAdded; ptsToSpend = (int) (ranksToAdd * skillCost); } pc.addSkill(aSkill); String ret = SkillRankControl.modRanks(ranksToAdd, pcClass, false, pc, aSkill); if (ret.length() > 0) { if (isFree && ret.indexOf("You do not have enough skill points.") != -1) { SkillRankControl.modRanks(ranksToAdd, pcClass, true, pc, aSkill); } else { warnings.add(ret); return null; } } if (!isFree) { for (int i = 0; i < pc.getLevelInfoSize(); i++) { PCLevelInfo info = pc.getLevelInfo(i); if (points[i] >= 0) { info.setSkillPointsRemaining(points[i]); } } } List<Language> langList = new ArrayList<Language>(); if (ChooseActivation.hasChooseToken(aSkill) && !selection.isEmpty()) { ChoiceManagerList<Language> controller = ChooserUtilities.getConfiguredController(aSkill, pc, null, new ArrayList<String>()); int limit = (int) ranksToAdd; for (CDOMSingleRef<Language> ref : selection) { Language lang = ref.resolvesTo(); if (controller.conditionallyApply(pc, lang)) { langList.add(lang); limit--; } if (limit <= 0) { break; } } } return new KitSkillAdd(aSkill, ranksToAdd, ptsToSpend, langList, pcClass); }
@Override public String getLSTformat(boolean useAny) { return "ABILITY=" + category.getLSTformat() + "[" + ref.getLSTformat(useAny) + "]"; }
@Override public int hashCode() { return ref == null ? -57 : ref.hashCode(); }