@Override public String evaluate(PlayerCharacter pc) { Skill skill = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Skill.class, rank); if (skill == null || !pc.hasSkill(skill)) { return "0.0"; } return SkillRankControl.getRank(pc, skill).toString(); }
/** * Needs documentation. * * @param pc update skills for this PC * @param aSkill Skill to update * @param aRank Number of ranks to add * @param aCost Cost of added ranks * @param langList Languages to be selected for a language skill * @param pcClass skills apply to this class * @return <code>true</code> for success TODO What about throwing on failure? */ private boolean updatePCSkills( final PlayerCharacter pc, final Skill aSkill, final int aRank, final double aCost, List<Language> langList, final PCClass pcClass) { pc.addSkill(aSkill); boolean oldImporting = pc.isImporting(); pc.setImporting(true); final String aString = SkillRankControl.modRanks(aRank, pcClass, true, pc, aSkill); pc.setImporting(oldImporting); if (aString.length() > 0) { Logging.errorPrint("SKILL: " + aString); return false; } // Add any supplied languages ChoiceManagerList<Language> controller = ChooserUtilities.getConfiguredController(aSkill, pc, null, new ArrayList<String>()); for (Language lang : langList) { if (!controller.conditionallyApply(pc, lang)) { Logging.errorPrint("Failed to apply Language into Skill: " + lang.getLSTformat()); } } // // Fix up the skill pools to reflect what we just spent. // double ptsToSpend = aCost; if (ptsToSpend >= 0.0) { for (PCLevelInfo info : pc.getLevelInfo()) { if (info.getClassKeyName().equals(pcClass.getKeyName())) { // We are spending this class' points. int remaining = info.getSkillPointsRemaining(); if (remaining == 0) { continue; } int left = remaining - (int) Math.min(remaining, ptsToSpend); info.setSkillPointsRemaining(left); ptsToSpend -= (remaining - left); if (ptsToSpend <= 0) { break; } } } } return true; }
/* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ @Override protected void setUp() throws Exception { super.setUp(); final PlayerCharacter character = getCharacter(); myClass = new PCClass(); myClass.setName("My Class"); knowledge = new Skill(); Globals.getContext().unconditionallyProcess(knowledge, "CLASSES", "My Class"); knowledge.setName("KNOWLEDGE (ARCANA)"); TestHelper.addType(knowledge, "KNOWLEDGE.INT"); character.addSkill(knowledge); SkillRankControl.modRanks(8.0, myClass, true, character, knowledge); }
/** * Test to ensure that a number of skills test will correctly require a number of separate skills * at the required level. * * @throws Exception */ public void testMultiSkills() throws Exception { final PreSkillParser producer = new PreSkillParser(); final Prerequisite prereq = producer.parse("SKILL", "2,TYPE.Knowledge=4", false, false); final PlayerCharacter character = getCharacter(); boolean passes = PrereqHandler.passes(prereq, character, null); assertFalse("Should not pass 2 knowledge skill test with 1 skill", passes); final Skill extraKnow = new Skill(); Globals.getContext().unconditionallyProcess(extraKnow, "CLASSES", "MyClass"); extraKnow.setName("KNOWLEDGE (RELIGION)"); TestHelper.addType(extraKnow, "KNOWLEDGE.INT"); character.addSkill(extraKnow); SkillRankControl.modRanks(5.0, myClass, true, character, extraKnow); passes = PrereqHandler.passes(prereq, character, null); assertTrue("Should pass 2 knowledge skill test with 2 skills", passes); character.removeSkill(knowledge); character.calcActiveBonuses(); }
public static String getRanksExplanation(PlayerCharacter pc, Skill sk) { StringBuilder sb = new StringBuilder(100); boolean needComma = false; for (PCClass pcc : pc.getSkillRankClasses(sk)) { if (needComma) { sb.append(", "); } sb.append(pcc == null ? "None" : pcc.getKeyName()); sb.append(':'); Double rank = pc.getSkillRankForClass(sk, pcc); sb.append(rank == null ? 0 : rank); needComma = true; } double bonus = SkillRankControl.getSkillRankBonusTo(pc, sk); if (bonus != 0d) { if (sb.length() > 0) { sb.append("; "); } sb.append("Skillrank bonus "); sb.append(NumberFormat.getNumberInstance().format(bonus)); } return sb.toString(); }
/** * 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(); }
void subLevel(final PlayerCharacter aPC) { if (aPC != null) { int total = aPC.getTotalLevels(); int oldLevel = aPC.getLevel(this); int spMod = 0; final PCLevelInfo pcl = aPC.getLevelInfoFor(getKeyName(), oldLevel); if (pcl != null) { spMod = pcl.getSkillPointsGained(aPC); } else { Logging.errorPrint( "ERROR: could not find class/level info for " + getDisplayName() + "/" + oldLevel); } final int newLevel = oldLevel - 1; if (oldLevel > 0) { PCClassLevel classLevel = aPC.getActiveClassLevel(this, oldLevel - 1); aPC.removeHP(classLevel); } // aPC.adjustFeats(-aPC.getBonusFeatsForNewLevel(this)); setLevel(newLevel, aPC); aPC.removeKnownSpellsForClassLevel(this); doMinusLevelMods(aPC, newLevel + 1); DomainApplication.removeDomainsForLevel(this, newLevel + 1, aPC); if (newLevel == 0) { SubClassApplication.setSubClassKey(aPC, this, Constants.NONE); // // Remove all skills associated with this class // for (Skill skill : aPC.getSkillSet()) { SkillRankControl.setZeroRanks(this, aPC, skill); } Integer currentPool = aPC.getSkillPool(this); spMod = currentPool == null ? 0 : currentPool; } if (!isMonster() && (total > aPC.getTotalLevels())) { total = aPC.getTotalLevels(); // Roll back any stat changes that were made as part of the // level final List<PCLevelInfoStat> moddedStats = new ArrayList<>(); if (pcl.getModifiedStats(true) != null) { moddedStats.addAll(pcl.getModifiedStats(true)); } if (pcl.getModifiedStats(false) != null) { moddedStats.addAll(pcl.getModifiedStats(false)); } if (!moddedStats.isEmpty()) { for (PCLevelInfoStat statToRollback : moddedStats) { for (PCStat aStat : aPC.getStatSet()) { if (aStat.equals(statToRollback.getStat())) { aPC.setStat(aStat, aPC.getStat(aStat) - statToRollback.getStatMod()); break; } } } } } aPC.setLevelWithoutConsequence(this, newLevel); if (isMonster() || (total != 0)) { Integer currentPool = aPC.getSkillPool(this); int newSkillPool = (currentPool == null ? 0 : currentPool) - spMod; aPC.setSkillPool(this, newSkillPool); aPC.setDirty(true); } if (aPC.getLevel(this) == 0) { aPC.removeClass(this); } aPC.validateCharacterDomains(); if (!aPC.isImporting()) { final int maxxp = aPC.minXPForNextECL(); if (aPC.getXP() >= maxxp) { aPC.setXP(Math.max(maxxp - 1, 0)); } } } else { Logging.errorPrint("No current pc in subLevel()? How did this happen?"); return; } }
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); }