/* * Globals.java * Copyright 2001 (C) Bryan McRoberts <merton_monk@yahoo.com> * * 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 April 21, 2001, 2:15 PM * * Current Ver: $Revision$ * */ package pcgen.core; import java.io.File; import java.io.IOException; import java.net.URI; import java.text.Collator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.logging.Level; import javax.swing.JFrame; import pcgen.cdom.base.CDOMObject; import pcgen.cdom.base.Constants; import pcgen.cdom.content.BaseDice; import pcgen.cdom.content.CNAbilityFactory; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.RaceType; import pcgen.cdom.enumeration.SourceFormat; import pcgen.cdom.enumeration.StringKey; import pcgen.cdom.enumeration.Type; import pcgen.core.character.EquipSlot; import pcgen.core.chooser.CDOMChooserFacadeImpl; import pcgen.core.utils.CoreUtility; import pcgen.facade.core.ChooserFacade; import pcgen.gui2.facade.Gui2InfoFactory; import pcgen.rules.context.AbstractReferenceContext; import pcgen.rules.context.ConsolidatedListCommitStrategy; import pcgen.rules.context.LoadContext; import pcgen.rules.context.RuntimeLoadContext; import pcgen.rules.context.RuntimeReferenceContext; import pcgen.system.ConfigurationSettings; import pcgen.system.PCGenSettings; import pcgen.util.Logging; import pcgen.util.chooser.ChooserFactory; import pcgen.util.enumeration.Load; import pcgen.util.enumeration.VisionType; /** * This is like the top level model container. However, * it is build from static methods rather than instantiated. * * @author Bryan McRoberts <merton_monk@users.sourceforge.net> * @author boomer70 <boomer70@yahoo.com> */ public final class Globals { /** These are changed during normal operation */ private static final List<PlayerCharacter> pcList = new ArrayList<>(); /** Race, a s_EMPTYRACE */ public static Race s_EMPTYRACE; /** NOTE: The defaultPath is duplicated in LstSystemLoader. */ private static final String defaultPcgPath = getUserFilesPath() + File.separator + "characters"; //$NON-NLS-1$ private static final List<String> custColumnWidth = new ArrayList<>(); private static SourceFormat sourceDisplay = SourceFormat.LONG; private static int selectedPaper = -1; /** we need maps for efficient lookups */ private static final Map<URI, Campaign> campaignMap = new HashMap<>(); private static final Map<String, Campaign> campaignNameMap = new HashMap<>(); private static final Map<String, String> eqSlotMap = new HashMap<>(); /** We use lists for efficient iteration */ private static final List<Campaign> campaignList = new ArrayList<>(85); // end of filter creation sets private static JFrame rootFrame; private static final StringBuilder section15 = new StringBuilder(30000); /** whether or not the GUI is used (false for command line) */ private static boolean useGUI = true; /** default location for options.ini on a Mac */ static final String defaultMacOptionsPath = System.getProperty("user.home") + "/Library/Preferences/pcgen"; private static final Comparator<CDOMObject> pObjectComp = (o1, o2) -> o1.getKeyName().compareToIgnoreCase(o2.getKeyName()); public static final Comparator<CDOMObject> pObjectNameComp = (o1, o2) -> { final Collator collator = Collator.getInstance(); // Check sort keys first String key1 = o1.get(StringKey.SORT_KEY); if (key1 == null) { key1 = o1.getDisplayName(); } String key2 = o2.get(StringKey.SORT_KEY); if (key2 == null) { key2 = o2.getDisplayName(); } if (!key1.equals(key2)) { return collator.compare(key1, key2); } if (!o1.getDisplayName().equals(o2.getDisplayName())) { return collator.compare(o1.getDisplayName(), o2.getDisplayName()); } // Fall back to keyname if the displayname is the same return collator.compare(o1.getKeyName(), o2.getKeyName()); }; // Optimizations used by any code needing empty arrays. All empty arrays // of the same type are idempotent. /** EMPTY_STRING_ARRAY*/ public static final String[] EMPTY_STRING_ARRAY = new String[0]; private Globals() { } /** * This method is used to locate a Campaign object based on its file * name (in URL syntax). * * @param aName String name of file, in URL.toString() format * @param complainOnError boolean true to log an error if the campaign * cannot be found * @return Campaign loaded from the given filename, or null if it * cannot be found */ public static Campaign getCampaignByURI(final URI aName, final boolean complainOnError) { final Campaign campaign = campaignMap.get(aName); if ((campaign == null) && complainOnError) { Logging.errorPrint("Could not find campaign by filename: " + aName); } return campaign; } /** * Get campaign list * @return campaign list */ public static List<Campaign> getCampaignList() { return campaignList; } /** * Get campaign by key * @param aKey * @return Campaign */ public static Campaign getCampaignKeyed(final String aKey) { final Campaign campaign = getCampaignKeyedSilently(aKey); if (campaign == null) { Logging.errorPrint("Could not find campaign: " + aKey); } return campaign; } /** * Get campaign by key * @param aKey * @return Campaign */ public static Campaign getCampaignKeyedSilently(final String aKey) { for ( final Campaign campaign : campaignList) { if (campaign.getKeyName().equalsIgnoreCase(aKey)) { return campaign; } } return null; } /** * Finds all PObjects that match the passed in type. All the types listed * in aType must match for the object to be returned. * @param aPObjectList List of PObjects to search * @param aType A "." separated list of TYPEs to match * @return List of PObjects matching all TYPEs */ public static <T extends CDOMObject> List<T> getPObjectsOfType(final Collection<T> aPObjectList, final String aType) { final ArrayList<T> ret = new ArrayList<>(aPObjectList.size()); final List<String> typeList = new ArrayList<>(); final StringTokenizer tok = new StringTokenizer(aType, "."); while (tok.hasMoreTokens()) { typeList.add(tok.nextToken()); } for (final T anObject : aPObjectList) { boolean match = false; for (final String type : typeList) { final boolean sense = (type.charAt(0) != '!'); if (anObject.isType(type) == sense) { match = true; } else { match = false; break; } } if (match) { ret.add(anObject); } } ret.trimToSize(); return ret; } // END Game Modes Section. /** * Get the default path * @return default path */ public static String getDefaultPath() { return ConfigurationSettings.getUserDir(); } /** * Get the default path * @return default path */ private static String getUserFilesPath() { return expandRelativePath(System.getProperty("user.home") + File.separator + ".pcgen"); } /** * Returns the name of the Default Spell Book, or null if there is no default spell book. * @return default spellbook */ public static String getDefaultSpellBook() { String book = null; if (SettingsHandler.getGame() != null) { book = SettingsHandler.getGame().getDefaultSpellBook(); } return book; } /** * Get equipment slot by name * @param aName * @return equipment slot */ public static EquipSlot getEquipSlotByName(final String aName) { for (final EquipSlot es : SystemCollections.getUnmodifiableEquipSlotList()) { if (es.getSlotName().equals(aName)) { return es; } } return null; } /** * Set equipment slot type count * @param aString * @param aNum */ public static void setEquipSlotTypeCount(final String aString, final String aNum) { eqSlotMap.put(aString, aNum); } /** * returns the # of slots for an equipmentslots Type * The number of slots is define by the NUMSLOTS: line * in the game mode file equipmentslots.lst * @param aType * @return equipment slot type count */ public static int getEquipSlotTypeCount(final String aType) { final String aNum = eqSlotMap.get(aType); if (aNum != null) { return Integer.parseInt(aNum); } return 0; } /** * Get game mode align text * @return game mode align text */ public static String getGameModeAlignmentText() { return SettingsHandler.getGame().getAlignmentText(); } /** * Get the game mode point pool name * @return game mode point pool name */ public static String getGameModePointPoolName() { return SettingsHandler.getGame().getPointPoolName(); } /** * TRUE if the game mode has a point pool * @return TRUE if the game mode has a point pool */ public static boolean getGameModeHasPointPool() { return !getGameModePointPoolName().isEmpty(); } /** * Get game mode unit set * @return game mode unit set */ public static UnitSet getGameModeUnitSet() { return SettingsHandler.getGame().getUnitSet(); } /** * Get global deity list * @return global deity lis */ public static List<String> getGlobalDeityList() { if (SettingsHandler.getGame() != null) { return SettingsHandler.getGame().getDeityList(); } return new ArrayList<>(); } /** * Return TRUE if in a particular game mode * @param gameMode * @return TRUE if in a particular game mode */ public static boolean isInGameMode(final String gameMode) { if ((gameMode.isEmpty()) || ((SettingsHandler.getGame() != null) && gameMode.equalsIgnoreCase(SettingsHandler.getGame().getName()))) { return true; } return false; } /** * Get PC List * @return List of Pcs */ public static List<PlayerCharacter> getPCList() { return pcList; } /** * Get paper count * @return paper count */ public static int getPaperCount() { return SettingsHandler.getGame().getModeContext().getReferenceContext() .getConstructedObjectCount(PaperInfo.class); } /** * Get paper info * @param infoType * @return paper info */ public static String getPaperInfo(final int infoType) { return getPaperInfo(selectedPaper, infoType); } /** * Get paper info * @param idx * @param infoType * @return paper info */ public static String getPaperInfo(final int idx, final int infoType) { if ((idx < 0) || (idx >= SettingsHandler.getGame().getModeContext().getReferenceContext() .getConstructedObjectCount(PaperInfo.class))) { return null; } final PaperInfo pi = SettingsHandler.getGame().getModeContext().getReferenceContext() .getItemInOrder(PaperInfo.class, idx); return pi.getPaperInfo(infoType); } /** * Sets the root frame * The root frame is the container in which all * other panels, frame etc are placed. * * @param frame the {@code PCGen_Frame1} which is to be root */ public static void setRootFrame(final JFrame frame) { rootFrame = frame; } /** * Returns the current root frame. * * @return the {@code rootFrame} property */ public static JFrame getRootFrame() { return rootFrame; } /** * Get the section 15 * @return section 15 */ public static StringBuilder getSection15() { return section15; } /** * Get selected paper * @return selected paper */ public static int getSelectedPaper() { return selectedPaper; } /** * Set source display * @param sourceType */ public static void setSourceDisplay(final SourceFormat sourceType) { sourceDisplay = sourceType; } /** * Get source display * @return source display */ public static SourceFormat getSourceDisplay() { return sourceDisplay; } /** * Sets whether to use the GUI or not * @param aBool */ public static void setUseGUI(final boolean aBool) { useGUI = aBool; } /** * TRUE if using UI * @return TRUE if using UI */ static boolean getUseGUI() { return useGUI; } /** * This method is called by the persistence layer to * add a campaign that it has located to the globl campaign list. * * @param campaign Campaign loaded from persistence to add to the * Global campaign list */ public static void addCampaign(final Campaign campaign) { campaignMap.put(campaign.getSourceURI(), campaign); campaignList.add(campaign); final Campaign oldCampaign = campaignNameMap.put(campaign.getName(), campaign); if (oldCampaign != null) { if (oldCampaign.getSourceURI().toString() .equalsIgnoreCase(campaign.getSourceURI().toString())) { Logging.errorPrint("The campaign (" + campaign.getName() + ") was referenced with the incorrect case: " + oldCampaign.getSourceURI() + " vs " + campaign.getSourceURI()); } else { Logging.errorPrint("Loaded Campaigns with matching names (" + campaign.getName() + ") at different Locations: " + oldCampaign.getSourceURI() + " " + campaign.getSourceURI()); } } } /** * Adjust damage * @param aDamage * @param baseSize * @param finalSize * @return adjusted damage */ public static String adjustDamage(final String aDamage, final int baseSize, final int finalSize) { final AbstractReferenceContext ref = getContext().getReferenceContext(); BaseDice bd = ref.silentlyGetConstructedCDOMObject(BaseDice.class, aDamage); int multiplier = 0; if (bd == null) { //Need to test for higher dice final RollInfo aRollInfo = new RollInfo(aDamage); final String baseDice = "1d" + Integer.toString(aRollInfo.sides); bd = ref.silentlyGetConstructedCDOMObject(BaseDice.class, baseDice); if (bd != null) { multiplier = aRollInfo.times; } } else { multiplier = 1; } RollInfo bi; if (bd == null) { bi = new RollInfo(aDamage); } else { List<RollInfo> steps = null; if (baseSize < finalSize) { steps = bd.getUpSteps(); } else if (baseSize > finalSize) { steps = bd.getDownSteps(); } else { // Not a warning? return aDamage; } final int difference = Math.abs(baseSize - finalSize); final int index; if (steps.size() > difference) { index = difference - 1; } else { index = steps.size() - 1; } bi = steps.get(index); } if (multiplier > 1) { // Ugh, have to do this for "cloning" to avoid polluting the master // RollInfo bi = new RollInfo(bi.toString()); bi.times *= multiplier; } return bi.toString(); } /** * Return true if resizing the equipment will have any "noticable" effect * checks for cost modification, armor bonus, weight, capacity * @param aEq * @param typeList * @return TRUE or FALSE */ public static boolean canResizeHaveEffect(final Equipment aEq, List<String> typeList) { // cycle through typeList and see if it matches one in the BONUS:ITEMCOST|TYPE=etc on sizeadjustment if (typeList == null) { typeList = aEq.typeList(); } final List<String> resizeTypeList = SettingsHandler.getGame().getResizableTypeList(); return typeList.stream().map(String::toUpperCase).anyMatch(resizeTypeList::contains); } /** * Find out the state of a PRERULE check * @param aKey * @return true or false */ public static boolean checkRule(final String aKey) { final RuleCheck rule = SettingsHandler.getGame().getModeContext().getReferenceContext() .silentlyGetConstructedCDOMObject(RuleCheck.class, aKey); if (rule == null) { return false; } if (SettingsHandler.hasRuleCheck(aKey)) { return SettingsHandler.getRuleCheck(aKey); } else { return rule.getDefault(); } } /** * This method is called by the persistence layer to clear the global * campaigns for a refresh. */ public static void clearCampaignsForRefresh() { emptyLists(); campaignMap.clear(); campaignList.clear(); } /** * Check if enough data has been loaded to support character creation. * Will also report to the log the number of items of each of the * necessary types that are currently loaded. * @return true or false */ public static boolean displayListsHappy() { // NOTE: If you add something here be sure to update the log output below final boolean listsHappy = checkListsHappy(); final Level logLevel = listsHappy ? Logging.DEBUG : Logging.WARNING; if (Logging.isLoggable(logLevel)) { Logging.log(logLevel, "Number of objects loaded. The following should " + "all be greater than 0:"); Logging.log(logLevel, "Races=" + getContext().getReferenceContext().getConstructedCDOMObjects(Race.class).size()); Logging.log(logLevel, "Classes=" + getContext().getReferenceContext().getConstructedCDOMObjects(PCClass.class).size()); Logging.log(logLevel, "Skills=" + getContext().getReferenceContext().getConstructedCDOMObjects(Skill.class).size()); Logging.log(logLevel, "Feats=" + getContext().getReferenceContext().getManufacturer(Ability.class, AbilityCategory.FEAT).getConstructedObjectCount()); Logging.log(logLevel, "Equipment=" + getContext().getReferenceContext().getConstructedCDOMObjects(Equipment.class).size()); Logging.log(logLevel, "ArmorProfs=" + getContext().getReferenceContext().getConstructedCDOMObjects(ArmorProf.class).size()); Logging.log(logLevel, "ShieldProfs=" + getContext().getReferenceContext().getConstructedCDOMObjects(ShieldProf.class).size()); Logging.log(logLevel, "WeaponProfs=" + getContext().getReferenceContext().getConstructedCDOMObjects(WeaponProf.class).size()); Logging.log(logLevel, "Kits=" + getContext().getReferenceContext().getConstructedCDOMObjects(Kit.class).size()); Logging.log(logLevel, "Templates=" + getContext().getReferenceContext().getConstructedCDOMObjects(PCTemplate.class).size()); } return listsHappy; } /** * Check if enough data has been loaded to support character creation. * @return true or false */ private static boolean checkListsHappy() { // NOTE: If you add something here be sure to update the log output in displayListsHappy above final boolean listsHappy = !((getContext().getReferenceContext().getConstructedCDOMObjects(Race.class).isEmpty()) || (getContext().getReferenceContext().getConstructedCDOMObjects(PCClass.class).isEmpty()) // || (getContext().ref.getConstructedCDOMObjects(Skill.class).size() == 0) // || (getContext().ref.getManufacturer( // Ability.class, AbilityCategory.FEAT).getConstructedObjectCount() == 0) || (getContext().getReferenceContext().getConstructedCDOMObjects(Equipment.class).isEmpty()) || (getContext().getReferenceContext().getConstructedCDOMObjects(WeaponProf.class).isEmpty())); return listsHappy; } /** * Clears all lists of game data. */ public static void emptyLists() { // These lists do not need cleared; they are tied to game mode // alignmentList // checkList // gameModeList // campaignList // statList // All other lists should be cleared!!! ////////////////////////////////////// // DO NOT CLEAR THESE HERE!!! // They only get loaded once. // //birthplaceList.clear(); //cityList.clear(); //hairStyleList.clear(); //helpContextFileList.clear(); //interestsList.clear(); //locationList.clear(); //paperInfo.clear(); //phobiaList.clear(); //phraseList.clear(); //schoolsList.clear(); //sizeAdjustmentList.clear(); //specialsList.clear(); //speechList.clear(); //traitList.clear(); //unitSet.clear(); ////////////////////////////////////// // Clear Maps (not strictly necessary, but done for consistency) VisionType.clearConstants(); // Perform other special cleanup Equipment.clearEquipmentTypes(); SettingsHandler.getGame().clearLoadContext(); RaceType.clearConstants(); createEmptyRace(); CNAbilityFactory.reset(); } /** * Execute post export commands for standard files * @param fileName */ public static void executePostExportCommandStandard(final String fileName) { final String postExportCommand = SettingsHandler.getPostExportCommandStandard(); executePostExportCommand(fileName, postExportCommand); } /** * Execute post export commands for PDF * @param fileName */ public static void executePostExportCommandPDF(final String fileName) { final String postExportCommand = SettingsHandler.getPostExportCommandPDF(); executePostExportCommand(fileName, postExportCommand); } /** * Execute any post export commands * @param fileName * @param postExportCommand */ private static void executePostExportCommand(final String fileName, final String postExportCommand) { final List<String> aList = new ArrayList<>(); final StringTokenizer aTok = new StringTokenizer(postExportCommand, " "); while (aTok.hasMoreTokens()) { aList.add(aTok.nextToken()); } final String[] cmdArray = new String[aList.size()]; for (int idx = 0; idx < aList.size(); idx++) { final String s = aList.get(idx); if (s.contains("%")) { final String beforeString = s.substring(0, s.indexOf("%")); final String afterString = s.substring(s.indexOf("%") + 1); cmdArray[idx] = beforeString + fileName + afterString; } else { cmdArray[idx] = s; } } if (cmdArray.length > 0) { try { Runtime.getRuntime().exec(cmdArray); } catch (final IOException ex) { Logging.errorPrint("Could not execute " + postExportCommand + " after exporting " + fileName, ex); } } } /** * Select the paper * @param paperName * @return TRUE if OK */ public static boolean selectPaper(final String paperName) { for (int i = 0; i < SettingsHandler.getGame().getModeContext().getReferenceContext() .getConstructedObjectCount(PaperInfo.class); ++i) { final PaperInfo pi = SettingsHandler.getGame().getModeContext().getReferenceContext() .getItemInOrder(PaperInfo.class, i); if (pi.getName().equals(paperName)) { selectedPaper = i; PCGenSettings.getInstance().setProperty(PCGenSettings.PAPERSIZE, paperName); return true; } } selectedPaper = -1; return false; } /** * Apply the user's preferences to the initial state of the Globals. */ public static void initPreferences() { if (selectedPaper != -1) { // Already initialized return; } final String papersize = PCGenSettings.getInstance().initProperty(PCGenSettings.PAPERSIZE, "A4"); selectPaper(papersize); } /** * Sorts chooser lists using the appropriate method, based on the type of the first item in either list. * Not pretty, but it works. * * @param availableList * @param selectedList */ public static void sortChooserLists(final List availableList, final List selectedList) { final boolean nonPObjectInList; if (!availableList.isEmpty()) { nonPObjectInList = ! (availableList.get(0) instanceof CDOMObject); } else if (!selectedList.isEmpty()) { nonPObjectInList = ! (selectedList.get(0) instanceof CDOMObject); } else { nonPObjectInList = false; } if (nonPObjectInList) { Collections.sort(availableList); // NOCHOICE feats add nulls to the selectedList if ((!selectedList.isEmpty()) && (selectedList.get(0) != null)) { Collections.sort(selectedList); } } else { sortPObjectListByName(availableList); sortPObjectListByName(selectedList); } } /** * Sort Pcgen Object list * @param aList * @return Sorted list of Pcgen Objects */ static List<? extends CDOMObject> sortPObjectList(final List<? extends CDOMObject> aList) { aList.sort(pObjectComp); return aList; } /** * Sort Pcgen Object list by name * @param <T> * * @param aList * @return Sorted list of Pcgen Objects */ public static <T extends CDOMObject> List<T> sortPObjectListByName(final List<T> aList) { aList.sort(pObjectNameComp); return aList; } static String getBonusFeatString() { final List<String> bonusFeatLevels = SettingsHandler.getGame().getBonusFeatLevels(); if ((bonusFeatLevels == null) || bonusFeatLevels.isEmpty()) { // Default to no bonus feats. return "9999|0"; } return bonusFeatLevels.get(0); } static int getBonusStatsForLevel(final int level, final PlayerCharacter aPC) { int num = 0; for (final String s : SettingsHandler.getGame().getBonusStatLevels()) { num = bonusParsing(s, level, num, aPC); } return num; } /** * Get a choice from a list * @param title The title of the chooser dialog. * @param choiceList The list of possible choices. * @param selectedList The values already selected (none of which should be in the available list). * @param pool The number of choices the user can make. * @param pc The character the choice is being made for. * @return a choice */ public static <T> List<T> getChoiceFromList(final String title, final List<T> choiceList, final List<T> selectedList, final int pool, final PlayerCharacter pc) { return getChoiceFromList(title, choiceList, selectedList, pool, false, false, pc); } /** * Ask the user for a choice from a list. * @param title The title of the chooser dialog. * @param choiceList The list of possible choices. * @param selectedList The values already selected (none of which should be in the available list). * @param pool The number of choices the user can make. * @param forceChoice true if the user will be forced to make all choices. * @param preferRadioSelection true if this would be better presented as a radio button list * @param pc The character the choice is being made for. * @return The list of choices made by the user. */ public static <T> List<T> getChoiceFromList(final String title, final List<T> choiceList, final List<T> selectedList, final int pool, final boolean forceChoice, final boolean preferRadioSelection, final PlayerCharacter pc) { List<T> startingSelectedList = new ArrayList<>(); if (selectedList != null) { startingSelectedList = selectedList; } final CDOMChooserFacadeImpl<T> chooserFacade = new CDOMChooserFacadeImpl<>(title, choiceList, startingSelectedList, pool); chooserFacade.setAllowsDups(false); chooserFacade.setRequireCompleteSelection(forceChoice); chooserFacade.setInfoFactory(new Gui2InfoFactory(pc)); chooserFacade.setDefaultView(ChooserFacade.ChooserTreeViewType.NAME); chooserFacade.setPreferRadioSelection(preferRadioSelection); ChooserFactory.getDelegate().showGeneralChooser(chooserFacade); return chooserFacade.getFinalSelected(); } static List<String> getCustColumnWidth() { return custColumnWidth; } static String getDefaultPcgPath() { return expandRelativePath(defaultPcgPath); } /** * returns the location of the "filepaths.ini" file * which could be one of several locations * depending on the OS and user preferences * @return option path */ static String getFilepathsPath() { // first see if it was specified on the command line String aPath = System.getProperty("pcgen.filepaths"); //$NON-NLS-1$ if (aPath == null) { aPath = System.getProperty("user.dir") + File.separator + "filepaths.ini"; //$NON-NLS-1$ //$NON-NLS-2$; } else { File testPath=new File(expandRelativePath(aPath)); if (testPath.exists() && testPath.isDirectory()) { aPath = testPath.getAbsolutePath() + File.separator + "filepaths.ini"; //$NON-NLS-1$ testPath=new File(aPath); } if (testPath.exists() && !testPath.canWrite()) { Logging .errorPrint("WARNING: The filepaths file you specified is not updatable. " + "Filepath changes will not be saved. File is " + testPath.getAbsolutePath()); } } return expandRelativePath(aPath); } /** * returns the location of the "filter.ini" file * which could be one of several locations * depending on the OS and user preferences * @return filter path */ static String getFilterPath() { // first see if it was specified on the command line String aPath = System.getProperty("pcgen.filter"); if (aPath == null) { aPath = getFilePath("filter.ini"); } else { File testPath=new File(expandRelativePath(aPath)); if (testPath.exists() && testPath.isDirectory()) { aPath = testPath.getAbsolutePath() + File.separator + "filter.ini"; testPath=new File(aPath); } if (testPath.exists() && !testPath.canWrite()) { Logging .errorPrint("WARNING: The filter file you specified is not updatable. " + "Filter changes will not be saved. File is " + testPath.getAbsolutePath()); } } return expandRelativePath(aPath); } /** * returns the location of the "options.ini" file * which could be one of several locations * depending on the OS and user preferences * @return option path */ static String getOptionsPath() { // first see if it was specified on the command line String aPath = System.getProperty("pcgen.options"); if (aPath == null) { aPath = getFilePath("options.ini"); } else { File testPath=new File(expandRelativePath(aPath)); if (testPath.exists() && testPath.isDirectory()) { aPath = testPath.getAbsolutePath() + File.separator + "options.ini"; testPath=new File(aPath); } if (testPath.exists() && !testPath.canWrite()) { Logging .errorPrint("WARNING: The options file you specified is not updatable. " + "Settings changes will not be saved. File is " + testPath.getAbsolutePath()); } } return expandRelativePath(aPath); } public static int getSkillMultiplierForLevel(final int level) { final List<String> sml = SettingsHandler.getGame().getSkillMultiplierLevels(); if ((level > sml.size()) || (level <= 0)) { return 1; } return Integer.parseInt(sml.get(level - 1)); } /** * Reduce/increase damage for modified size as per DMG p.162 * @param aDamage * @param baseSize * @param newSize * @return String */ public static String adjustDamage(final String aDamage, final SizeAdjustment baseSize, final SizeAdjustment newSize) { if (aDamage.isEmpty()) { return aDamage; } final int baseIndex = baseSize.get(IntegerKey.SIZEORDER); final int newIndex = newSize.get(IntegerKey.SIZEORDER); return adjustDamage(aDamage, baseIndex, newIndex); } public static double calcEncumberedMove(final Load load, final double unencumberedMove) { final double encumberedMove; switch (load) { case LIGHT: encumberedMove = unencumberedMove; break; case MEDIUM: case HEAVY: if (CoreUtility.doublesEqual(unencumberedMove, 5)) { encumberedMove = 5; } else if (CoreUtility.doublesEqual(unencumberedMove, 10)) { encumberedMove = 5; } else { encumberedMove = (Math.floor(unencumberedMove / 15) * 10) + (((int) unencumberedMove) % 15); } break; case OVERLOAD: encumberedMove = 0; break; default: Logging.errorPrint("The load " + load + " is not possible."); encumberedMove = 0; break; } return encumberedMove; } // Methods static void initCustColumnWidth(final List<String> l) { custColumnWidth.clear(); custColumnWidth.addAll(l); } /** * Get a writable path for storing files * First check to see if it's been set in-program * Then check user home directory * Else use directory pcgen started from * @param aString * @return file path */ private static String getFilePath(final String aString) { final String fType = SettingsHandler.getFilePaths(); if ((fType == null) || fType.equals("pcgen")) { // we are either running PCGen for the first // time or user wants default file locations return System.getProperty("user.dir") + File.separator + aString; } else if (fType.equals("user")) { // use the users "home" directory + .pcgen return System.getProperty("user.home") + File.separator + ".pcgen" + File.separator + aString; } else if (fType.equals("mac_user")) { // use the users "home" directory + standard Mac settings return System.getProperty("user.home") + "/Library/Preferences/pcgen" + File.separator + aString; } else { // use the specified directory return fType + File.separator + aString; } } private static int bonusParsing(final String l, final int level, int num, final PlayerCharacter aPC) { // should be in format levelnum,rangenum[,numchoices] final StringTokenizer aTok = new StringTokenizer(l, "|", false); final int startLevel = Integer.parseInt(aTok.nextToken()); final String rangeLevelFormula = aTok.nextToken(); final int rangeLevel = aPC.getVariableValue(rangeLevelFormula, "").intValue(); int numChoices = 1; if (aTok.hasMoreTokens()) { numChoices = Integer.parseInt(aTok.nextToken()); } if ((level == startLevel) || ((level > startLevel) && (rangeLevel > 0) && (((level - startLevel) % rangeLevel) == 0))) { num+=numChoices; } return num; } public static void createEmptyRace() { if (s_EMPTYRACE == null) { s_EMPTYRACE = new Race(); s_EMPTYRACE.setName(Constants.NONESELECTED); s_EMPTYRACE.addToListFor(ListKey.TYPE, Type.HUMANOID); } getContext().getReferenceContext().importObject(s_EMPTYRACE); } private static String expandRelativePath(String path) { if (path.startsWith("@")) { path = System.getProperty("user.dir") + File.separator + path.substring(1); } return path; } public static LoadContext getContext() { return SettingsHandler.getGame().getContext(); } private static final LoadContext globalContext = new RuntimeLoadContext( new RuntimeReferenceContext(), new ConsolidatedListCommitStrategy()); public static LoadContext getGlobalContext() { return globalContext; } }