Tweak cost and value multipliers for non-commons

This commit is contained in:
drdev
2016-03-20 16:29:49 +00:00
parent d0811678c3
commit 5af9e90a2e
10 changed files with 192 additions and 54 deletions

1
.gitattributes vendored
View File

@@ -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

View File

@@ -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)) {

View 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);
}
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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());

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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());
}
}

View File

@@ -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));