mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Tweak cost and value multipliers for non-commons
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1243,6 +1243,7 @@ forge-gui-mobile/src/forge/card/CardImageRenderer.java -text
|
||||
forge-gui-mobile/src/forge/card/CardListPreview.java -text
|
||||
forge-gui-mobile/src/forge/card/CardRenderer.java -text
|
||||
forge-gui-mobile/src/forge/card/CardZoom.java -text
|
||||
forge-gui-mobile/src/forge/card/ColorSetImage.java -text
|
||||
forge-gui-mobile/src/forge/card/GameEntityPicker.java -text
|
||||
forge-gui-mobile/src/forge/deck/AddBasicLandsDialog.java -text
|
||||
forge-gui-mobile/src/forge/deck/FDeckChooser.java -text
|
||||
|
||||
@@ -44,6 +44,10 @@ public enum CardRarity {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getLongName() {
|
||||
return longName;
|
||||
}
|
||||
|
||||
public static CardRarity smartValueOf(String input) {
|
||||
for (CardRarity r : CardRarity.values()) {
|
||||
if (r.name().equalsIgnoreCase(input) || r.shortName.equalsIgnoreCase(input) || r.longName.equalsIgnoreCase(input)) {
|
||||
|
||||
37
forge-gui-mobile/src/forge/card/ColorSetImage.java
Normal file
37
forge-gui-mobile/src/forge/card/ColorSetImage.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package forge.card;
|
||||
|
||||
import forge.Graphics;
|
||||
import forge.assets.FImage;
|
||||
import forge.assets.FSkinImage;
|
||||
|
||||
public class ColorSetImage implements FImage {
|
||||
private final ColorSet colorSet;
|
||||
private final int shardCount;
|
||||
|
||||
public ColorSetImage(ColorSet colorSet0) {
|
||||
colorSet = colorSet0;
|
||||
shardCount = colorSet.getOrderedShards().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return FSkinImage.MANA_W.getWidth() * shardCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return FSkinImage.MANA_W.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
float imageSize = w / shardCount;
|
||||
if (imageSize > h) {
|
||||
imageSize = h;
|
||||
float w0 = imageSize * shardCount;
|
||||
x += (w - w0) / 2;
|
||||
w = w0;
|
||||
}
|
||||
CardFaceSymbols.drawColorSet(g, colorSet, x, y, imageSize);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import com.badlogic.gdx.math.Rectangle;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.Graphics;
|
||||
import forge.animation.ForgeAnimation;
|
||||
@@ -19,6 +20,7 @@ import forge.assets.FSkinFont;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.assets.FSkinTexture;
|
||||
import forge.assets.TextRenderer;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRenderer;
|
||||
import forge.card.CardZoom;
|
||||
import forge.card.ColorSet;
|
||||
@@ -127,7 +129,7 @@ public class ConquestAEtherScreen extends FScreen {
|
||||
shardCost = 0;
|
||||
}
|
||||
else {
|
||||
shardCost = pool.getShardValue(btnRarityFilter.selectedOption.getRarity(), FModel.getConquestPreferences().getPrefInt(CQPref.AETHER_BASE_PULL_COST));
|
||||
shardCost = pool.getShardValue(btnRarityFilter.selectedOption.getRarity(0), FModel.getConquestPreferences().getPrefInt(CQPref.AETHER_BASE_PULL_COST));
|
||||
}
|
||||
display.updateMessage();
|
||||
}
|
||||
@@ -138,7 +140,50 @@ public class ConquestAEtherScreen extends FScreen {
|
||||
ConquestData model = FModel.getConquest().getModel();
|
||||
if (model.getAEtherShards() < shardCost) { return; }
|
||||
|
||||
PaperCard card = Aggregates.random(filteredPool);
|
||||
//determine final pool to pull from based on rarity odds
|
||||
Iterable<PaperCard> rewardPool;
|
||||
boolean lowerRarity = true;
|
||||
CardRarity minRarity = btnRarityFilter.selectedOption.getRarity(0);
|
||||
CardRarity rarity = btnRarityFilter.selectedOption.getRarity(Math.random());
|
||||
while (true) {
|
||||
final CardRarity allowedRarity = rarity;
|
||||
rewardPool = Iterables.filter(filteredPool, new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard card) {
|
||||
if (allowedRarity == card.getRarity()) { return true; }
|
||||
if (allowedRarity == CardRarity.Rare && card.getRarity() == CardRarity.Special) { return true; }
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (Iterables.isEmpty(rewardPool)) { //if pool is empty, must reduce rarity and try again
|
||||
if (rarity == minRarity) {
|
||||
if (lowerRarity) { lowerRarity = false; } //switch to increasing rarity if min rarity empty
|
||||
else { break; } //shouldn't happen, but prevent infinite loop
|
||||
}
|
||||
switch (rarity) {
|
||||
case MythicRare:
|
||||
rarity = lowerRarity ? CardRarity.Rare : CardRarity.Common;
|
||||
continue;
|
||||
case Rare:
|
||||
rarity = lowerRarity ? CardRarity.Uncommon : CardRarity.MythicRare;
|
||||
continue;
|
||||
case Uncommon:
|
||||
rarity = lowerRarity ? CardRarity.Common : CardRarity.Rare;
|
||||
continue;
|
||||
case Common:
|
||||
if (lowerRarity) { break; } //shouldn't happen
|
||||
rarity = CardRarity.Uncommon;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
PaperCard card = Aggregates.random(rewardPool);
|
||||
if (card == null) { return; } //shouldn't happen, but prevent crash if it does
|
||||
|
||||
pool.remove(card);
|
||||
filteredPool.remove(card);
|
||||
|
||||
@@ -331,7 +376,7 @@ public class ConquestAEtherScreen extends FScreen {
|
||||
if (selectedOption == selectedOption0) { return; }
|
||||
selectedOption = selectedOption0;
|
||||
|
||||
FSkinProp skinProp = selectedOption.skinProp;
|
||||
FSkinProp skinProp = selectedOption.getSkinProp();
|
||||
if (skinProp != null) {
|
||||
setIcon(FSkin.getImages().get(skinProp));
|
||||
}
|
||||
@@ -349,9 +394,9 @@ public class ConquestAEtherScreen extends FScreen {
|
||||
|
||||
private Predicate<PaperCard> buildFilterPredicate(Predicate<PaperCard> predicate) {
|
||||
if (predicate == null) {
|
||||
return selectedOption.predicate;
|
||||
return selectedOption.getPredicate();
|
||||
}
|
||||
return Predicates.and(predicate, selectedOption.predicate);
|
||||
return Predicates.and(predicate, selectedOption.getPredicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -56,6 +56,9 @@ public class ConquestPrefsScreen extends FScreen {
|
||||
scroller.add(new PrefsOption("Base Exile Value", CQPref.AETHER_BASE_EXILE_VALUE, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Base Retrieve Cost", CQPref.AETHER_BASE_RETRIEVE_COST, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Base Pull Cost", CQPref.AETHER_BASE_PULL_COST, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Uncommon Multiplier", CQPref.AETHER_UNCOMMON_MULTIPLIER, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Rare Multiplier", CQPref.AETHER_RARE_MULTIPLIER, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Mythic Multiplier", CQPref.AETHER_MYTHIC_MULTIPLIER, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Starting Shards", CQPref.AETHER_START_SHARDS, PrefsGroup.AETHER));
|
||||
scroller.add(new PrefsOption("Chaos Wheel Shard Value", CQPref.AETHER_WHEEL_SHARDS, PrefsGroup.AETHER));
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import forge.limited.GauntletMini;
|
||||
import forge.planarconquest.ConquestController;
|
||||
import forge.planarconquest.ConquestPlane;
|
||||
import forge.planarconquest.ConquestPreferences;
|
||||
import forge.planarconquest.ConquestUtil;
|
||||
import forge.player.GamePlayerUtil;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
@@ -178,6 +179,7 @@ public final class FModel {
|
||||
CardPreferences.load();
|
||||
DeckPreferences.load();
|
||||
ItemManagerConfig.load();
|
||||
ConquestUtil.updateRarityFilterOdds();
|
||||
|
||||
achievements = new HashMap<>();
|
||||
achievements.put(GameType.Constructed, new ConstructedAchievements());
|
||||
|
||||
@@ -12,8 +12,6 @@ public class ConquestAwardPool {
|
||||
private final BoosterPool commons, uncommons, rares, mythics;
|
||||
|
||||
public ConquestAwardPool(Iterable<PaperCard> cards) {
|
||||
ConquestPreferences prefs = FModel.getConquestPreferences();
|
||||
|
||||
commons = new BoosterPool();
|
||||
uncommons = new BoosterPool();
|
||||
rares = new BoosterPool();
|
||||
@@ -38,32 +36,20 @@ public class ConquestAwardPool {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate odds of each rarity
|
||||
float commonOdds = commons.getOdds(prefs.getPrefInt(CQPref.BOOSTER_COMMONS));
|
||||
float uncommonOdds = uncommons.getOdds(prefs.getPrefInt(CQPref.BOOSTER_UNCOMMONS));
|
||||
int raresPerBooster = prefs.getPrefInt(CQPref.BOOSTER_RARES);
|
||||
float rareOdds = rares.getOdds(raresPerBooster);
|
||||
float mythicOdds = mythics.getOdds((float)raresPerBooster / (float)prefs.getPrefInt(CQPref.BOOSTERS_PER_MYTHIC));
|
||||
|
||||
//determine multipliers for each rarity based on ratio of odds
|
||||
commons.multiplier = 1;
|
||||
uncommons.multiplier = commonOdds / uncommonOdds;
|
||||
rares.multiplier = commonOdds / rareOdds;
|
||||
mythics.multiplier = mythics.isEmpty() ? 0 : commonOdds / mythicOdds;
|
||||
}
|
||||
|
||||
public int getShardValue(CardRarity rarity, int baseValue) {
|
||||
ConquestPreferences prefs = FModel.getConquestPreferences();
|
||||
switch (rarity) {
|
||||
case Common:
|
||||
return baseValue;
|
||||
case Uncommon:
|
||||
return Math.round(baseValue * uncommons.multiplier);
|
||||
return Math.round((float)baseValue * (float)prefs.getPrefInt(CQPref.AETHER_UNCOMMON_MULTIPLIER));
|
||||
case Rare:
|
||||
case Special:
|
||||
return Math.round(baseValue * rares.multiplier);
|
||||
return Math.round((float)baseValue * (float)prefs.getPrefInt(CQPref.AETHER_RARE_MULTIPLIER));
|
||||
case MythicRare:
|
||||
return Math.round(baseValue * mythics.multiplier);
|
||||
return Math.round((float)baseValue * (float)prefs.getPrefInt(CQPref.AETHER_MYTHIC_MULTIPLIER));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -84,7 +70,6 @@ public class ConquestAwardPool {
|
||||
|
||||
public class BoosterPool {
|
||||
private final List<PaperCard> cards = new ArrayList<PaperCard>();
|
||||
private float multiplier;
|
||||
|
||||
private BoosterPool() {
|
||||
}
|
||||
@@ -93,12 +78,6 @@ public class ConquestAwardPool {
|
||||
return cards.isEmpty();
|
||||
}
|
||||
|
||||
private float getOdds(float perBoosterCount) {
|
||||
int count = cards.size();
|
||||
if (count == 0) { return 0; }
|
||||
return (float)perBoosterCount / (float)count;
|
||||
}
|
||||
|
||||
private void add(PaperCard c) {
|
||||
cards.add(c);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package forge.planarconquest;
|
||||
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.PreferencesStore;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -33,6 +34,9 @@ public class ConquestPreferences extends PreferencesStore<ConquestPreferences.CQ
|
||||
AETHER_BASE_EXILE_VALUE("75"),
|
||||
AETHER_BASE_RETRIEVE_COST("150"),
|
||||
AETHER_BASE_PULL_COST("200"),
|
||||
AETHER_UNCOMMON_MULTIPLIER("3"),
|
||||
AETHER_RARE_MULTIPLIER("10"),
|
||||
AETHER_MYTHIC_MULTIPLIER("25"),
|
||||
AETHER_START_SHARDS("3000"),
|
||||
AETHER_WHEEL_SHARDS("1000"),
|
||||
|
||||
@@ -65,6 +69,12 @@ public class ConquestPreferences extends PreferencesStore<ConquestPreferences.CQ
|
||||
super(ForgeConstants.CONQUEST_PREFS_FILE, CQPref.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
super.save();
|
||||
ConquestUtil.updateRarityFilterOdds();
|
||||
}
|
||||
|
||||
protected CQPref[] getEnumValues() {
|
||||
return CQPref.values();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.planarconquest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -30,6 +31,7 @@ import forge.deck.generation.DeckGeneratorMonoColor;
|
||||
import forge.deck.generation.IDeckGenPool;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.planarconquest.ConquestPreferences.CQPref;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.quest.QuestUtil;
|
||||
import forge.util.FileUtil;
|
||||
@@ -182,19 +184,19 @@ public class ConquestUtil {
|
||||
NONCREATURE_PERMANENT (FSkinProp.IMG_ENCHANTMENT, new TypeFilter(EnumSet.of(CoreType.Artifact, CoreType.Enchantment, CoreType.Planeswalker, CoreType.Land)), "Noncreature Permanent"),
|
||||
INSTANT_SORCERY (FSkinProp.IMG_SORCERY, new TypeFilter(EnumSet.of(CoreType.Instant, CoreType.Sorcery)), "Instant or Sorcery"),
|
||||
|
||||
COMMON (FSkinProp.IMG_PW_BADGE_COMMON, new RarityFilter(CardRarity.Common), "Common"),
|
||||
UNCOMMON (FSkinProp.IMG_PW_BADGE_UNCOMMON, new RarityFilter(CardRarity.Uncommon), "Uncommon"),
|
||||
RARE (FSkinProp.IMG_PW_BADGE_RARE, new RarityFilter(CardRarity.Rare), "Rare"),
|
||||
MYTHIC (FSkinProp.IMG_PW_BADGE_MYTHIC, new RarityFilter(CardRarity.MythicRare), "Mythic Rare"),
|
||||
COMMON (FSkinProp.IMG_PW_BADGE_COMMON, new RarityFilter(EnumSet.of(CardRarity.Common, CardRarity.Uncommon, CardRarity.Rare, CardRarity.Special, CardRarity.MythicRare)), "Common"),
|
||||
UNCOMMON (FSkinProp.IMG_PW_BADGE_UNCOMMON, new RarityFilter(EnumSet.of(CardRarity.Uncommon, CardRarity.Rare, CardRarity.Special, CardRarity.MythicRare)), "Uncommon"),
|
||||
RARE (FSkinProp.IMG_PW_BADGE_RARE, new RarityFilter(EnumSet.of(CardRarity.Rare, CardRarity.Special, CardRarity.MythicRare)), "Rare"),
|
||||
MYTHIC (FSkinProp.IMG_PW_BADGE_MYTHIC, new RarityFilter(EnumSet.of(CardRarity.MythicRare)), "Mythic Rare (100%)"),
|
||||
|
||||
CMC_LOW (FSkinProp.IMG_CMC_LOW, new CMCFilter(0, 3), "CMC 0-3"),
|
||||
CMC_LOW_MID (FSkinProp.IMG_CMC_LOW_MID, new CMCFilter(2, 5), "CMC 2-5"),
|
||||
CMC_MID_HIGH (FSkinProp.IMG_CMC_MID_HIGH, new CMCFilter(4, 7), "CMC 4-7"),
|
||||
CMC_HIGH (FSkinProp.IMG_CMC_HIGH, new CMCFilter(6, -1), "CMC 6+");
|
||||
|
||||
public final FSkinProp skinProp;
|
||||
public final Predicate<PaperCard> predicate;
|
||||
public final String caption;
|
||||
private final FSkinProp skinProp;
|
||||
private final Predicate<PaperCard> predicate;
|
||||
private String caption;
|
||||
|
||||
private AEtherFilter(final FSkinProp skinProp0, final Predicate<PaperCard> predicate0, final String caption0) {
|
||||
skinProp = skinProp0;
|
||||
@@ -207,6 +209,10 @@ public class ConquestUtil {
|
||||
return skinProp;
|
||||
}
|
||||
|
||||
public Predicate<PaperCard> getPredicate() {
|
||||
return predicate;
|
||||
}
|
||||
|
||||
public ColorSet getColor() {
|
||||
if (predicate instanceof ColorFilter) {
|
||||
return ((ColorFilter)predicate).color;
|
||||
@@ -214,16 +220,19 @@ public class ConquestUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public EnumSet<CoreType> getTypes() {
|
||||
if (predicate instanceof ColorFilter) {
|
||||
return ((TypeFilter)predicate).types;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CardRarity getRarity() {
|
||||
public CardRarity getRarity(double randomSeed) {
|
||||
if (predicate instanceof RarityFilter) {
|
||||
return ((RarityFilter)predicate).rarity;
|
||||
float total = 0;
|
||||
CardRarity rarity = null;
|
||||
EnumMap<CardRarity, Double> rarityOdds = ((RarityFilter)predicate).rarityOdds;
|
||||
for (Entry<CardRarity, Double> entry : rarityOdds.entrySet()) {
|
||||
rarity = entry.getKey();
|
||||
total += entry.getValue();
|
||||
if (randomSeed < total) {
|
||||
return rarity;
|
||||
}
|
||||
}
|
||||
return rarity;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -249,6 +258,25 @@ public class ConquestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateRarityFilterOdds() {
|
||||
ConquestPreferences prefs = FModel.getConquestPreferences();
|
||||
|
||||
EnumMap<CardRarity, Double> odds = new EnumMap<CardRarity, Double>(CardRarity.class);
|
||||
double commonsPerBooster = prefs.getPrefInt(CQPref.BOOSTER_COMMONS);
|
||||
double uncommonPerBooster = prefs.getPrefInt(CQPref.BOOSTER_UNCOMMONS);
|
||||
double raresPerBooster = prefs.getPrefInt(CQPref.BOOSTER_RARES);
|
||||
double mythicsPerBooster = raresPerBooster / (double)prefs.getPrefInt(CQPref.BOOSTERS_PER_MYTHIC);
|
||||
|
||||
odds.put(CardRarity.Common, 1d);
|
||||
odds.put(CardRarity.Uncommon, uncommonPerBooster / commonsPerBooster);
|
||||
odds.put(CardRarity.Rare, raresPerBooster / commonsPerBooster);
|
||||
odds.put(CardRarity.MythicRare, mythicsPerBooster / commonsPerBooster);
|
||||
|
||||
for (AEtherFilter filter : RARITY_FILTERS) {
|
||||
filter.caption = ((RarityFilter)filter.predicate).updateOdds(odds);
|
||||
}
|
||||
}
|
||||
|
||||
public static final AEtherFilter[] COLOR_FILTERS = new AEtherFilter[] {
|
||||
AEtherFilter.W,
|
||||
AEtherFilter.U,
|
||||
@@ -327,18 +355,47 @@ public class ConquestUtil {
|
||||
}
|
||||
|
||||
private static class RarityFilter implements Predicate<PaperCard> {
|
||||
private final CardRarity rarity;
|
||||
private final EnumMap<CardRarity, Double> rarityOdds;
|
||||
|
||||
private RarityFilter(CardRarity rarity0) {
|
||||
rarity = rarity0;
|
||||
private RarityFilter(EnumSet<CardRarity> rarities0) {
|
||||
rarityOdds = new EnumMap<CardRarity, Double>(CardRarity.class);
|
||||
for (CardRarity rarity : rarities0) {
|
||||
rarityOdds.put(rarity, 0d); //values will be set later
|
||||
}
|
||||
}
|
||||
|
||||
private String updateOdds(EnumMap<CardRarity, Double> oddsLookup) {
|
||||
double baseOdds = 0;
|
||||
double remainingOdds = 1;
|
||||
CardRarity baseRarity = null;
|
||||
String caption = "";
|
||||
|
||||
for (CardRarity rarity : rarityOdds.keySet()) {
|
||||
Double odds = oddsLookup.get(rarity);
|
||||
if (odds == null) { continue; } //skip Special rarity
|
||||
|
||||
if (baseRarity == null) {
|
||||
baseRarity = rarity;
|
||||
baseOdds = odds;
|
||||
}
|
||||
else {
|
||||
odds /= baseOdds;
|
||||
remainingOdds -= odds;
|
||||
caption += ", " + rarity.getLongName() + " (" + (Math.round(1000 * odds) / 10) + "%)"; //round to nearest single decimal point
|
||||
rarityOdds.put(rarity, odds);
|
||||
}
|
||||
}
|
||||
|
||||
//prepend base rarity and odds
|
||||
caption = baseRarity.getLongName() + " (" + (Math.round(1000 * remainingOdds) / 10) + "%)" + caption;
|
||||
rarityOdds.put(baseRarity, remainingOdds);
|
||||
|
||||
return caption;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(PaperCard card) {
|
||||
CardRarity cardRarity = card.getRarity();
|
||||
if (cardRarity == rarity) { return true; }
|
||||
if (cardRarity == CardRarity.Special && rarity == CardRarity.Rare) { return true; } //treat specials as rares
|
||||
return false;
|
||||
return rarityOdds.containsKey(card.getRarity());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public abstract class PreferencesStore<T extends Enum<T>> {
|
||||
protected abstract T valueOf(String name);
|
||||
protected abstract String getPrefDefault(T key);
|
||||
|
||||
public final void save() {
|
||||
public void save() {
|
||||
BufferedWriter writer = null;
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(filename));
|
||||
|
||||
Reference in New Issue
Block a user