Merge branch 'master' into historicformats

# Conflicts:
#	forge-game/src/main/java/forge/game/GameFormat.java
This commit is contained in:
maustin
2018-04-18 23:00:56 +01:00
86 changed files with 343 additions and 9847 deletions

View File

@@ -1,4 +1,3 @@
eclipse.preferences.version=1
encoding//src/main/java=ISO-8859-1
encoding//src/test/java=ISO-8859-1
encoding/<project>=UTF-8

View File

@@ -126,6 +126,7 @@ public enum SpellApiToAi {
.put(ApiType.Repeat, RepeatAi.class)
.put(ApiType.RepeatEach, RepeatEachAi.class)
.put(ApiType.ReplaceEffect, AlwaysPlayAi.class)
.put(ApiType.ReplaceDamage, AlwaysPlayAi.class)
.put(ApiType.ReplaceSplitDamage, AlwaysPlayAi.class)
.put(ApiType.RestartGame, RestartGameAi.class)
.put(ApiType.Reveal, RevealAi.class)

View File

@@ -124,11 +124,11 @@ public class DigAi extends SpellAbilityAi {
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer) {
Card chosen = ComputerUtilCard.getBestAI(valid);
if (sa.getActivatingPlayer().isOpponentOf(ai) && relatedPlayer.isOpponentOf(ai)) {
return ComputerUtilCard.getWorstPermanentAI(valid, false, true, false, false);
} else {
return ComputerUtilCard.getBestAI(valid);
}
return chosen;
}
/* (non-Javadoc)

View File

@@ -1,7 +1,5 @@
package forge.ai.ability;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCombat;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.Game;

4
forge-ai/src/test/java/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -1,7 +1,6 @@
package forge.card;
import forge.card.mana.ManaCost;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.*;

View File

@@ -21,7 +21,6 @@ import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardDb.SetPreference;
import forge.card.CardDb;
import forge.item.IPaperCard;
import forge.item.PaperCard;

View File

@@ -3,7 +3,6 @@ package forge.token;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import forge.card.*;
import forge.item.PaperCard;
import forge.item.PaperToken;
import java.util.*;

View File

@@ -7,8 +7,6 @@ import forge.card.CardRules;
import forge.card.CardSplitType;
import forge.item.PaperCard;
import java.io.File;
import org.apache.commons.lang3.StringUtils;
public class ImageUtil {

4
forge-core/src/test/java/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -40,6 +40,7 @@ import forge.game.zone.ZoneType;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -215,7 +216,7 @@ public final class GameActionUtil {
// reset static abilities
if (lkicheck) {
game.getAction().checkStaticAbilities(false, Sets.newHashSet(), CardCollection.EMPTY);
game.getAction().checkStaticAbilities(false, new HashSet<Card>(), CardCollection.EMPTY);
}
}

View File

@@ -124,6 +124,7 @@ public enum ApiType {
Repeat (RepeatEffect.class),
RepeatEach (RepeatEachEffect.class),
ReplaceEffect (ReplaceEffect.class),
ReplaceDamage (ReplaceDamageEffect.class),
ReplaceSplitDamage (ReplaceSplitDamageEffect.class),
RestartGame (RestartGameEffect.class),
Reveal (RevealEffect.class),

View File

@@ -0,0 +1,79 @@
package forge.game.ability.effects;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.replacement.ReplacementResult;
import forge.game.spellability.SpellAbility;
public class ReplaceDamageEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard();
final Game game = card.getGame();
// outside of Replacement Effect, unwanted result
if (!sa.getRootAbility().isReplacementAbility()) {
return;
}
String varValue = sa.getParamOrDefault("VarName", "1");
@SuppressWarnings("unchecked")
Map<String, Object> originalParams = (Map<String, Object>) sa.getReplacingObject("OriginalParams");
Map<String, Object> params = Maps.newHashMap(originalParams);
Integer dmg = (Integer) sa.getReplacingObject("DamageAmount");
int prevent = AbilityUtils.calculateAmount(card, varValue, sa);
// Currently it does reduce damage by amount, need second mode for Setting Damage
if (prevent > 0) {
int n = Math.min(dmg, prevent);
dmg -= n;
prevent -= n;
if (card.getType().hasStringType("Effect") && prevent <= 0) {
game.getAction().exile(card, null);
} else if (!StringUtils.isNumeric(varValue)) {
card.setSVar(varValue, "Number$" + prevent);
}
}
// no damage for original target anymore
if (dmg <= 0) {
originalParams.put("ReplacementResult", ReplacementResult.Replaced);
return;
}
params.put("DamageAmount", dmg);
//try to call replacementHandler with new Params
ReplacementResult result = game.getReplacementHandler().run(params);
switch (result) {
case NotReplaced:
case Updated: {
for (Map.Entry<String, Object> e : params.entrySet()) {
originalParams.put(e.getKey(), e.getValue());
}
// effect was updated
originalParams.put("ReplacementResult", ReplacementResult.Updated);
break;
}
default:
// effect was replaced with something else
originalParams.put("ReplacementResult", result);
break;
}
}
}

View File

@@ -552,6 +552,10 @@ public class CardFactoryUtil {
return doXMath(player.getCardsIn(ZoneType.Battlefield).size(), m, source);
}
if (value.contains("StartingLife")) {
return doXMath(player.getStartingLife(), m, source);
}
if (value.contains("LifeTotal")) {
return doXMath(player.getLife(), m, source);
}

View File

@@ -47,6 +47,10 @@ import forge.util.TextUtil;
* @version $Id$
*/
public class Cost implements Serializable {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
private boolean isAbility = true;
private final List<CostPart> costParts = Lists.newArrayList();
private boolean isMandatory = false;

View File

@@ -32,6 +32,11 @@ import forge.game.spellability.SpellAbility;
* The Class CostAddMana.
*/
public class CostAddMana extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* CostCostAddMana.
* @param amount

View File

@@ -25,6 +25,11 @@ import forge.game.spellability.SpellAbility;
*/
public class CostChooseCreatureType extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost mill.
*

View File

@@ -27,6 +27,11 @@ import forge.game.spellability.SpellAbility;
*/
public class CostDamage extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public CostDamage(final String amount) {
this.setAmount(amount);
}

View File

@@ -34,6 +34,11 @@ public class CostDiscard extends CostPartWithList {
// Inputs
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost discard.
*

View File

@@ -28,6 +28,11 @@ import java.util.List;
* The Class CostPayLife.
*/
public class CostDraw extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* CostDraw.
* @param amount

View File

@@ -29,6 +29,11 @@ import forge.game.zone.ZoneType;
*/
public class CostExert extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost Exert.
*

View File

@@ -38,6 +38,10 @@ public class CostExile extends CostPartWithList {
// ExileFromTop<Num/Type{/TypeDescription}> (of library)
// ExileSameGrave<Num/Type{/TypeDescription}>
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public final ZoneType from;
public final boolean sameZone;

View File

@@ -33,6 +33,11 @@ public class CostExileFromStack extends CostPart {
// ExileFromStack<Num/Type{/TypeDescription}>
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost exile.
*

View File

@@ -29,6 +29,11 @@ import forge.game.zone.ZoneType;
* This is for the "ExiledMoveToGrave" Cost.
*/
public class CostExiledMoveToGrave extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
// ExiledMoveToGrave<Num/Type{/TypeDescription}>
public CostExiledMoveToGrave(final String amount, final String type, final String description) {
super(amount, type, description);

View File

@@ -27,6 +27,11 @@ import forge.game.spellability.SpellAbility;
*/
public class CostFlipCoin extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost FlipCoin.
*

View File

@@ -31,6 +31,11 @@ import forge.game.zone.ZoneType;
public class CostGainControl extends CostPartWithList {
// GainControl<Num/Type{/TypeDescription}>
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost return.
*

View File

@@ -28,6 +28,10 @@ import java.util.List;
* The Class CostGainLife.
*/
public class CostGainLife extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
private final int cntPlayers; // MAX_VALUE means ALL/EACH PLAYERS
/**

View File

@@ -31,6 +31,11 @@ import forge.game.zone.ZoneType;
*/
public class CostMill extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost mill.
*

View File

@@ -31,6 +31,10 @@ import java.io.Serializable;
* The Class CostPart.
*/
public abstract class CostPart implements Comparable<CostPart>, Cloneable, Serializable {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
private String originalAmount;
private String amount;
private final String originalType, originalTypeDescription;

View File

@@ -22,12 +22,14 @@ import forge.card.mana.ManaCostShard;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.io.Serializable;
/**
* The mana component of any spell or ability cost
*/
public class CostPartMana extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
// "Leftover"
private final ManaCost cost;
private boolean xCantBe0 = false;

View File

@@ -28,6 +28,10 @@ import forge.game.spellability.SpellAbility;
* The Class CostPartWithList.
*/
public abstract class CostPartWithList extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/** The lists: one for LKI, one for the actual cards. */
private final CardCollection lkiList = new CardCollection();
protected final CardCollection cardList = new CardCollection();

View File

@@ -27,6 +27,11 @@ import forge.game.spellability.SpellAbility;
public class CostPayEnergy extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
int paidAmount = 0;
/**

View File

@@ -26,6 +26,10 @@ import forge.game.spellability.SpellAbility;
* The Class CostPayLife.
*/
public class CostPayLife extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
int paidAmount = 0;
/**

View File

@@ -36,6 +36,10 @@ public class CostPutCardToLib extends CostPartWithList {
// PutCardToLibFromSameGrave<Num/LibPos/Type{/TypeDescription}>
// PutCardToLibFromGrave<Num/LibPos/Type{/TypeDescription}>
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public final ZoneType from;
public final boolean sameZone;
private String libPosition = "0";

View File

@@ -32,7 +32,11 @@ import java.util.List;
* The Class CostPutCounter.
*/
public class CostPutCounter extends CostPartWithList {
// Put Counter doesn't really have a "Valid" portion of the cost
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
// Put Counter doesn't really have a "Valid" portion of the cost
private final CounterType counter;
private int lastPaidAmount = 0;

View File

@@ -30,6 +30,10 @@ import java.util.Map;
* The Class CostRemoveAnyCounter.
*/
public class CostRemoveAnyCounter extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
// RemoveAnyCounter<Num/Type/{TypeDescription}>
// Power Conduit and Chisei, Heart of Oceans
// Both cards have "Remove a counter from a permanent you control"

View File

@@ -40,6 +40,10 @@ public class CostRemoveCounter extends CostPartWithList {
// Quillspike, Rift Elemental, Sage of Fables, Spike Rogue
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public final CounterType counter;
public final ZoneType zone;
private int cntRemoved;

View File

@@ -30,6 +30,12 @@ import forge.game.zone.ZoneType;
public class CostReturn extends CostPartWithList {
// Return<Num/Type{/TypeDescription}>
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost return.
*

View File

@@ -33,6 +33,11 @@ import forge.game.zone.ZoneType;
public class CostReveal extends CostPartWithList {
// Reveal<Num/Type/TypeDescription>
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public CostReveal(final String amount, final String type, final String description) {
super(amount, type, description);
}

View File

@@ -30,6 +30,11 @@ import forge.game.zone.ZoneType;
*/
public class CostSacrifice extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost sacrifice.
*

View File

@@ -26,6 +26,11 @@ import forge.game.spellability.SpellAbility;
*/
public class CostTap extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost tap.
*/

View File

@@ -32,6 +32,10 @@ import forge.util.TextUtil;
*/
public class CostTapType extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public final boolean canTapSource;
/**

View File

@@ -32,6 +32,12 @@ public class CostUnattach extends CostPartWithList {
// Unattach<CARDNAME> if ability is on the Equipment
// Unattach<Card.Attached+namedHeartseeker/Equipped Heartseeker> if equipped creature has the ability
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost unattach.
*/

View File

@@ -26,6 +26,11 @@ import forge.game.spellability.SpellAbility;
*/
public class CostUntap extends CostPart {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
/**
* Instantiates a new cost untap.
*/

View File

@@ -29,6 +29,10 @@ import forge.game.zone.ZoneType;
* The Class CostUntapType.
*/
public class CostUntapType extends CostPartWithList {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
public final boolean canUntapSource;
public CostUntapType(final String amount, final String type, final String description, boolean hasUntapInPrice) {

View File

@@ -20,8 +20,6 @@ package forge.game.mana;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.IParserManaCost;

View File

@@ -5,6 +5,10 @@ import forge.game.cost.Cost;
import java.io.Serializable;
public class OptionalCostValue implements Serializable {
/**
* Serializables need a version ID.
*/
private static final long serialVersionUID = 1L;
private OptionalCost type;
private Cost cost;

View File

@@ -14,7 +14,6 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableList;
import forge.model.FModel;
import forge.quest.data.QuestPreferences.QPref;
/**
* Handles editor preferences saving and loading.

View File

@@ -500,7 +500,6 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
* @param qty a negative quantity will prompt the user for a number
*/
private void addMakeFoil(final int qty) {
final int shortcutModifiers = 0;
String label = "Foil " + SItemManagerUtil.getItemDisplayString(getItemManager().getSelectedItems(), qty, false);
GuiUtils.addMenuItem(menu, label, null, new Runnable() {
@@ -526,7 +525,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
cardManager.addItem(foiledCard, quantity);
cardManager.setSelectedItem(foiledCard);
}
}, true, shortcutModifiers == 0);
}, true, true);
}
private void addItem(final String verb, final String dest, final boolean toAlternate, final int qty, final int shortcutModifiers) {

View File

@@ -8,7 +8,6 @@ import javax.swing.SwingUtilities;
import com.google.common.collect.Iterables;
import forge.deck.CommanderDeckGenerator;
import forge.deck.DeckProxy;
import forge.deck.DeckType;
import forge.deckchooser.DecksComboBoxEvent;

View File

@@ -19,7 +19,6 @@ import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import forge.deckchooser.DecksComboBoxEvent;
import net.miginfocom.swing.MigLayout;
import com.google.common.collect.ImmutableList;

View File

@@ -7,7 +7,6 @@ import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab;
import forge.gui.framework.EDocID;
import forge.model.FModel;
import forge.properties.ForgePreferences;
import forge.screens.home.EMenuGroup;
import forge.screens.home.IVSubmenu;
import forge.screens.home.StartButton;

View File

@@ -932,7 +932,7 @@ public final class CMatchUI
if (delayedReveal != null) {
reveal(delayedReveal.getMessagePrefix(), delayedReveal.getCards()); //TODO: Merge this into search dialog
}
return (List) order(title,"Selected", 0, optionList.size(), optionList, null, null, false);
return (List<GameEntityView>) order(title,"Selected", 0, optionList.size(), optionList, null, null, false);
}
@Override

View File

@@ -287,6 +287,11 @@ public enum FView {
updateWarningOverlay.add(updateWarningContentPanel, constraints);
btnRemindMeLater.setCommand(new UiCommand() {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void run() {
SOverlayUtils.hideOverlay();
@@ -294,6 +299,11 @@ public enum FView {
});
btnDoNotRemindMe.setCommand(new UiCommand() {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void run() {
if (FOptionPane.showConfirmDialog("Are you sure? You can re-enable this warning in Forge's general preferences.")) {
@@ -305,6 +315,11 @@ public enum FView {
});
btnDownloadLatestJava.setCommand(new UiCommand() {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void run() {
try {

View File

@@ -28,7 +28,7 @@ import forge.error.ExceptionHandler;
*/
public final class Main {
/**
* Main entrypoint for Forge
* Main entry point for Forge
*/
public static void main(final String[] args) {
// HACK - temporary solution to "Comparison method violates it's general contract!" crash

View File

@@ -17,7 +17,6 @@
*/
package forge.card;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import forge.Graphics;
import forge.assets.FSkinImage;
import forge.card.mana.ManaCost;

View File

@@ -13,7 +13,6 @@ import forge.assets.FSkinImage;
import forge.deck.CardThemedDeckGenerator;
import forge.deck.CommanderDeckGenerator;
import forge.deck.DeckProxy;
import forge.deck.FDeckViewer;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.item.IPaperCard;

View File

@@ -13,7 +13,6 @@ import forge.Graphics;
import forge.assets.*;
import forge.card.*;
import forge.deck.io.DeckPreferences;
import forge.item.IPaperCard;
import forge.item.PaperCard;
import forge.itemmanager.CardManager;
import forge.itemmanager.ColumnDef;
@@ -29,7 +28,6 @@ import forge.menu.FPopupMenu;
import forge.model.FModel;
import forge.planarconquest.ConquestUtil;
import forge.properties.ForgePreferences.FPref;
import forge.quest.data.QuestPreferences.QPref;
import forge.screens.FScreen;
import forge.screens.TabPageScreen;
import forge.toolbox.*;
@@ -927,6 +925,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
break;
case Brawl:
additionalFilter = DeckFormat.Brawl.isLegalCommanderPredicate();
break;
default:
// Do nothing
}
cardManager.setCaption("Commanders");
}
@@ -939,10 +940,13 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
break;
case Brawl:
additionalFilter = DeckFormat.Brawl.isLegalCardForCommanderOrLegalPartnerPredicate(commanders);
break;
default:
// Do nothing
}
cardManager.setCaption("Cards");
}
//fall through to below
// fall through to below
default:
if (cardManager.getWantUnique()) {
cardManager.setPool(editorType.applyCardFilter(ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getUniqueCards(), PaperCard.class), additionalFilter), true);

View File

@@ -22,8 +22,6 @@ import forge.assets.FSkinColor;
import forge.assets.FSkinColor.Colors;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.model.FModel;
import forge.properties.ForgePreferences;
import forge.screens.match.views.VCardDisplayArea.CardAreaPanel;
import forge.toolbox.FDisplayObject;
import forge.util.Utils;

View File

@@ -43,6 +43,7 @@ import forge.toolbox.FEvent.FEventHandler;
import forge.toolbox.FLabel;
import forge.util.Aggregates;
import forge.util.Callback;
import forge.util.MyRandom;
import forge.util.Utils;
public class ConquestAEtherScreen extends FScreen {
@@ -148,7 +149,7 @@ public class ConquestAEtherScreen extends FScreen {
//determine final pool to pull from based on rarity odds
Iterable<PaperCard> rewardPool;
CardRarity minRarity = btnRarityFilter.selectedOption.getRarity();
CardRarity rarity = btnRarityFilter.selectedOption.getRarity(Math.random());
CardRarity rarity = btnRarityFilter.selectedOption.getRarity(MyRandom.getRandom().nextDouble());
while (true) {
final CardRarity allowedRarity = rarity;
rewardPool = Iterables.filter(filteredPool, new Predicate<PaperCard>() {

View File

@@ -1,5 +1,5 @@
- Dominaria -
This version of Forge features the initial implementation of the upcoming Dominaria set. Currently, all the cards except two are scripted, and the remaining cards are planned to be coded in the upcoming release. Please consider this early implementation of the set beta quality and be sure to report the issues you encounter while playing with the new cards.
This version of Forge features the initial implementation of the upcoming Dominaria set. All the cards are scripted. Please consider this early implementation of the set beta quality and be sure to report the issues you encounter while playing with the new cards.
- Multiplayer on Mobile Forge -
Mobile Forge now supports 3 and 4 player multiplayer matches in Constructed mode, you can select the number of players from the dropdown menu on the Constructed tab. Please note that matches with more than two players are more taxing for the mobile device and thus require more powerful hardware for prolonged gameplay without considerable slowdowns.

View File

@@ -0,0 +1,12 @@
Name:Healing Grace
ManaCost:W
Types:Instant
A:SP$ ChooseSource | Cost$ W | Choices$ Card,Emblem | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice. You gain 3 life.
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature,Player | TgtPrompt$ Select target to prevent damage to | ReplacementEffects$ GraceDamage | SVars$ GraceDmg,X | References$ GraceDamage,GraceDmg,X | ForgetOnMoved$ Battlefield | RememberObjects$ Targeted | SubAbility$ DBGainLife
SVar:GraceDamage:Event$ DamageDone | ValidTarget$ Creature.IsRemembered,Player.IsRemembered | ValidSource$ Card.ChosenCard,Emblem.ChosenCard | ReplaceWith$ GraceDmg | PreventionEffect$ True | Description$ Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice.
SVar:GraceDmg:DB$ ReplaceDamage | VarName$ X | References$ X
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 3 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
SVar:X:Number$3
SVar:RemAIDeck:True
Oracle:Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice. You gain 3 life.

View File

@@ -0,0 +1,13 @@
Name:Torgaar, Famine Incarnate
ManaCost:6 B B
Types:Legendary Creature Avatar
PT:7/6
A:SP$ PermanentCreature | Cost$ 6 B B Sac<X/Creature> | Announce$ X | References$ X,Y
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ Y | EffectZone$ All | References$ Y | Description$ As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost.
SVar:X:XChoice
SVar:Y:SVar$X/Times.2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSetLife | TriggerDescription$ When CARDNAME enters the battlefield, up to one target players life total becomes half their starting life total, rounded down.
SVar:TrigSetLife:DB$ SetLife | ValidTgts$ Player | LifeAmount$ HalfLife | TargetMin$ 0 | TargetMax$ 1 | References$ HalfLife
SVar:HalfLife:TargetedPlayer$StartingLife/HalfDown
SVar:RemAIDeck:True
Oracle:As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost.\nWhen Torgaar, Famine Incarnate enters the battlefield, up to one target players life total becomes half their starting life total, rounded down.

View File

@@ -1,100 +0,0 @@
<html>
<head>
<title>Tail-based by Web Sockets</title>
<link href="css/core.css" media="all" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="js/jquery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/observable.js"></script>
<script type="text/javascript" src="js/socket.js"></script>
<script type='text/javascript' src="js/main.js"></script>
</head>
<body>
<div class="wrap">
<div class="title">
<span>Your server: <b>ws://</b></span>
<input id="ws_uri" type="text" value="localhost:81/" name="uri"/>
<button id="connect" name="connect" class="cn">Connect</button>
<button id="disconn" name="disconn" class="dc" disabled="disabled">Disconnect</button>
</div>
<ul class="messages" id='messages'></ul>
<div class="packets" id='input'>
<span>Raw text to send:</span>
<input type="text" name="packet" value="" />
<button id="send" class="send">Send</button>
</div>
</div>
<script type="text/javascript">
if (!window.WebSocket)
alert("WebSocket not supported by this browser");
var server = Socket();
var listener = {
onOpen : function() {
$('#input').slideDown();
},
onMessage : function(m) {
if (m.data) {
addLi("incoming", m.data);
}
},
onClose : function(m) {
addLi("error", "Connection was closed (" + m.code + "): " + m.reason);
onDisconnectClicked();
$('#input').fadeOut();
}
};
server.addObserver(listener);
function addLi(className, text) {
var spanText = document.createElement('li');
spanText.className = className;
spanText.innerHTML = text;
var messageBox = $('#messages')[0];
messageBox.appendChild(spanText);
messageBox.scrollTop = messageBox.scrollHeight - messageBox.clientHeight;
}
function onConnectClicked() {
var uri = $("#ws_uri").val()
addLi("connecting", "Connecting to ws://" + uri + " ..." )
server.connect(uri);
$('#connect').attr("disabled", "disabled")
$('#disconn').removeAttr("disabled")
}
function onDisconnectClicked() {
server.close();
$('#disconn').attr("disabled", "disabled")
$('#connect').removeAttr("disabled")
}
function onSendClicked() {
var toSend = $("#input input").val();
$("#input input").val("");
addLi("outcoming", toSend);
server.send(toSend)
}
function onInputKey(event) {
if( event.keyCode == 13 )
onSendClicked();
}
function onReady() {
$('#connect').on("click", onConnectClicked);
$('#disconn').on("click", onDisconnectClicked);
$('#send').on("click", onSendClicked);
$("#input input").on("keypress", onInputKey);
}
$(onReady)
</script>
</body>
</html>

View File

@@ -1,23 +0,0 @@
ul, ol { margin: 0; }
div { border: 0px solid black; }
div.wrap { width: 640px; margin: 100px auto;}
.title { height: 24px; background-color: #ddd; padding: 4px; border: 1px solid black; border-bottom: 0px }
.title input {width: 300px; }
.title span { display: inline-block; width: 120px; text-align: right; }
.messages { height: 30ex; overflow: auto; background-color: #fff; padding: 4px; border: 1px solid black; list-style: none; }
.messages .incoming { color: #006; }
.messages .incoming:before { content: "<< "}
.messages .outcoming { color: #060; }
.messages .outcoming:before { content: ">> "}
.messages .error { color: #600; }
.messages li:nth-child(2n) { background-color: #f7f7f7; }
.packets { padding: 4px; background-color: #ddd; border: 1px solid black; border-top: 0px; display: none; }
.packets span { display: inline-block; width: 120px; text-align: right; }
.packets input {width: 400px; }
.packets button { width: 100px; }
span.alert { font-style: italic; }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,23 +0,0 @@
var Observable = function(eventNames) {
var _t = {};
var observers = {};
_t.addObserver = function(obj) {
for(var i = 0; i < eventNames.length; i++) {
var evName = eventNames[i]
var method = obj["on" + evName];
if( typeof(method) === 'function') {
var handlers = observers[evName]
if( 'undefined' === typeof(handler))
handlers = observers[evName] = [];
handlers.push(obj);
}
}
}
_t.fireEvent = function() { // usually invoked as .apply(EventName, args)
var q = observers[this]
if ( q ) for( var i = 0; i < q.length; i++ ) q[i]['on'+ this].apply(q[i], arguments);
}
return _t;
}

View File

@@ -1,27 +0,0 @@
// There should be some kind of fallback to Flash-powered sockets (IE 9-, Opera with sockets switched off)
var Socket = function() {
var _t = Observable(["Open", "Message", "Close", "Error"]);
function onOpen() { _t.fireEvent.apply("Open", arguments); }
function onClose() { _t.fireEvent.apply("Close", arguments); }
function onError() { _t.fireEvent.apply("Error", arguments); }
function onMessage() { _t.fireEvent.apply("Message", arguments); }
var ws;
_t.connect = function(location) {
ws = new WebSocket("ws://" + location);
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onclose = onClose;
ws.onerror = onError;
}
// _t.getWs = function() { return ws; }
_t.isOpen = function() { return ws && ws.readyState == ws.OPEN; }
_t.close = function() { ws && ws.close(); }
_t.send = function(text) { text != null && text.length > 0 && ws && ws.send(text); };
return _t;
};

View File

@@ -4,7 +4,6 @@ import com.google.common.collect.Sets;
import forge.card.mana.ManaCostShard;
import forge.game.GameView;
import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.card.CardView;
import forge.game.card.CardView.CardStateView;
import forge.game.card.CounterType;

View File

@@ -6,7 +6,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
import forge.deck.generation.DeckGeneratorBase;
import forge.deck.io.CardThemedMatrixIO;
import forge.deck.io.DeckStorage;
import forge.game.GameFormat;

View File

@@ -1,7 +1,5 @@
package forge.deck;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import forge.card.CardEdition;
import forge.game.GameFormat;
import forge.item.PaperCard;

View File

@@ -7,16 +7,12 @@ import forge.card.CardEdition;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
import forge.deck.generation.DeckGeneratorBase;
import forge.deck.generation.IDeckGenPool;
import forge.game.GameFormat;
import forge.game.GameType;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.util.ItemPool;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by maustin on 09/05/2017.

View File

@@ -1,7 +1,6 @@
package forge.deck;
import forge.model.FModel;
import forge.properties.ForgePreferences;
public enum DeckType {
CUSTOM_DECK ("Custom User Decks"),

View File

@@ -5,6 +5,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardDb;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
@@ -322,7 +323,7 @@ public class DeckgenUtil {
/** @return {@link forge.deck.Deck} */
public static Deck getRandomCustomDeck() {
final IStorage<Deck> allDecks = FModel.getDecks().getConstructed();
final int rand = (int) (Math.floor(Math.random() * allDecks.size()));
final int rand = (int) (Math.floor(MyRandom.getRandom().nextDouble() * allDecks.size()));
final String name = allDecks.getItemNames().toArray(new String[0])[rand];
return allDecks.get(name);
}
@@ -330,14 +331,14 @@ public class DeckgenUtil {
/** @return {@link forge.deck.Deck} */
public static Deck getRandomPreconDeck() {
final List<DeckProxy> allDecks = DeckProxy.getAllPreconstructedDecks(QuestController.getPrecons());
final int rand = (int) (Math.floor(Math.random() * allDecks.size()));
final int rand = (int) (Math.floor(MyRandom.getRandom().nextDouble() * allDecks.size()));
return allDecks.get(rand).getDeck();
}
/** @return {@link forge.deck.Deck} */
public static Deck getRandomThemeDeck() {
final List<DeckProxy> allDecks = DeckProxy.getAllThemeDecks();
final int rand = (int) (Math.floor(Math.random() * allDecks.size()));
final int rand = (int) (Math.floor(MyRandom.getRandom().nextDouble() * allDecks.size()));
return allDecks.get(rand).getDeck();
}
@@ -353,7 +354,7 @@ public class DeckgenUtil {
allQuestDecks.add(e.getEventDeck());
}
final int rand = (int) (Math.floor(Math.random() * allQuestDecks.size()));
final int rand = (int) (Math.floor(MyRandom.getRandom().nextDouble() * allQuestDecks.size()));
return allQuestDecks.get(rand);
}
@@ -540,7 +541,7 @@ public class DeckgenUtil {
if(partners.size()>0&&commander.getRules().canBePartnerCommander()){
selectedPartner=partners.get(MyRandom.getRandom().nextInt(partners.size()));
preSelectedCards.remove(selectedPartner);
preSelectedCards.removeAll(StaticData.instance().getCommonCards().getAllCards(selectedPartner.getName()));
}
//randomly remove cards
int removeCount=0;
@@ -562,6 +563,7 @@ public class DeckgenUtil {
++i;
}
preSelectedCards.removeAll(toRemove);
preSelectedCards.removeAll(StaticData.instance().getCommonCards().getAllCards(commander.getName()));
gen = new CardThemedCommanderDeckBuilder(commander, selectedPartner,preSelectedCards,forAi,format);
}else{
cardDb = FModel.getMagicDb().getCommonCards();
@@ -582,6 +584,7 @@ public class DeckgenUtil {
}
List<PaperCard> shortList = cardList.subList(1, shortlistlength);
shortList.remove(commander);
shortList.removeAll(StaticData.instance().getCommonCards().getAllCards(commander.getName()));
gen = new CardThemedCommanderDeckBuilder(commander, selectedPartner,shortList,forAi,format);
}

View File

@@ -8,6 +8,7 @@ import forge.deck.DeckType;
import forge.deck.DeckgenUtil;
import forge.deck.NetDeckCategory;
import forge.model.FModel;
import forge.util.MyRandom;
public class GauntletUtil {
public static GauntletData createQuickGauntlet(final Deck userDeck, final int numOpponents, final List<DeckType> allowedDeckTypes, NetDeckCategory netDecks) {
@@ -23,7 +24,7 @@ public class GauntletUtil {
final Object[] netDeckNames = netDecks != null ? netDecks.getItemNames().toArray() : null;
for (int i = 0; i < numOpponents; i++) {
int randType = (int)Math.floor(Math.random() * allowedDeckTypes.size());
int randType = (int)Math.floor(MyRandom.getRandom().nextDouble() * allowedDeckTypes.size());
switch (allowedDeckTypes.get(randType)) {
case COLOR_DECK:
deck = DeckgenUtil.getRandomColorDeck(true);
@@ -58,7 +59,7 @@ public class GauntletUtil {
eventNames.add(deck.getName());
break;
case NET_DECK:
int deckIndex = (int)Math.floor(Math.random() * netDeckNames.length);
int deckIndex = (int)Math.floor(MyRandom.getRandom().nextDouble() * netDeckNames.length);
deck = netDecks.get((String) netDeckNames[deckIndex]);
eventNames.add(deck.getName());
break;

View File

@@ -4,6 +4,7 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.*;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
@@ -503,17 +504,22 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
@Override
public boolean apply(PaperCard card) {
return format.isLegalCard(card)
&&!card.getRules().getManaCost().isPureGeneric()
&& !card.getRules().getManaCost().isPureGeneric()
&& colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor())
&& !deckList.contains(card)
&& (keyCard == null || !keyCard.equals(card))
&& (secondKeyCard==null || !secondKeyCard.equals(card))
&&!card.getRules().getAiHints().getRemAIDecks()
&&!card.getRules().getAiHints().getRemRandomDecks()
&&!card.getRules().getMainPart().getType().isLand();
&& !card.getRules().getAiHints().getRemAIDecks()
&& !card.getRules().getAiHints().getRemRandomDecks()
&& !card.getRules().getMainPart().getType().isLand();
}
};
List<PaperCard> randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool));
//ensure we do not add more keycards in case they are commanders
if (keyCard != null) {
randomPool.removeAll(StaticData.instance().getCommonCards().getAllCards(keyCard.getName()));
}
if (secondKeyCard != null) {
randomPool.removeAll(StaticData.instance().getCommonCards().getAllCards(secondKeyCard.getName()));
}
Collections.shuffle(randomPool, MyRandom.getRandom());
Iterator<PaperCard> iRandomPool=randomPool.iterator();
while (deckList.size() < targetSize) {

View File

@@ -27,8 +27,6 @@ import forge.ai.AiProfileUtil;
import forge.card.CardPreferences;
import forge.card.CardType;
import forge.deck.CardRelationMatrixGenerator;
import forge.deck.DeckFormat;
import forge.deck.io.CardThemedMatrixIO;
import forge.deck.io.DeckPreferences;
import forge.game.GameFormat;
import forge.game.GameType;

View File

@@ -1,7 +1,6 @@
package forge.net;
import forge.match.LobbySlotType;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import forge.GuiBase;

View File

@@ -221,22 +221,25 @@ public final class FServerManager {
DatagramSocket s = new DatagramSocket();
s.connect(InetAddress.getByAddress(this.externalAddress), 0);
NetworkInterface n = NetworkInterface.getByInetAddress(s.getLocalAddress());
Enumeration en = n.getInetAddresses();
Enumeration<InetAddress> en = n.getInetAddresses();
while (en.hasMoreElements()) {
InetAddress addr = (InetAddress) en.nextElement();
if (addr instanceof Inet4Address) {
if (preferIPv6) {
continue;
}
s.close();
return addr.getHostAddress();
}
if (addr instanceof Inet6Address) {
if (preferIpv4) {
continue;
}
s.close();
return addr.getHostAddress();
}
}
s.close();
return null;
}

View File

@@ -209,7 +209,7 @@ public final class BoosterUtils {
case RANDOM_BALANCED:
preferredColors.clear();
int numberOfColors = COLOR_COUNT_PROBABILITIES[(int) (Math.random() * COLOR_COUNT_PROBABILITIES.length)];
int numberOfColors = COLOR_COUNT_PROBABILITIES[(int) (MyRandom.getRandom().nextDouble() * COLOR_COUNT_PROBABILITIES.length)];
if (numberOfColors < 6) {
Collections.shuffle(possibleColors);
for (int i = 0; i < numberOfColors; i++) {
@@ -218,13 +218,16 @@ public final class BoosterUtils {
} else {
preferredColors.addAll(possibleColors);
}
includeArtifacts = Math.random() < 0.5;
includeArtifacts = MyRandom.getRandom().nextDouble() < 0.5;
// Fall through
case BALANCED:
populateBalancedFilters(colorFilters, preferredColors, cardPool, includeArtifacts);
break;
case RANDOM:
populateRandomFilters(colorFilters);
break;
default:
// Do nothing
}
@@ -238,13 +241,13 @@ public final class BoosterUtils {
for (int i = 0; i < MAX_BIAS; i++) {
Predicate<CardRules> predicate;
byte color = possibleColors.get((int) (Math.random() * 6));
if (Math.random() < 0.6) {
byte color = possibleColors.get((int) (MyRandom.getRandom().nextDouble() * 6));
if (MyRandom.getRandom().nextDouble() < 0.6) {
predicate = CardRulesPredicates.isMonoColor(color);
} else {
predicate = CardRulesPredicates.hasColor(color);
}
if (Math.random() < 0.1) {
if (MyRandom.getRandom().nextDouble() < 0.1) {
predicate = Predicates.and(predicate, CardRulesPredicates.Presets.IS_MULTICOLOR);
}
colorFilters.add(predicate);

View File

@@ -36,6 +36,7 @@ import forge.quest.io.ReadPriceList;
import forge.tournament.system.TournamentBracket;
import forge.tournament.system.TournamentPairing;
import forge.tournament.system.TournamentPlayer;
import forge.util.MyRandom;
import forge.util.NameGenerator;
import forge.util.TextUtil;
import forge.util.storage.IStorage;
@@ -478,7 +479,7 @@ public class QuestEventDraft implements IQuestEvent {
int attempts = 25;
while (promo == null && attempts-- > 0) {
randomCard = cardsInEdition.get((int) (Math.random() * cardsInEdition.size()));
randomCard = cardsInEdition.get((int) (MyRandom.getRandom().nextDouble() * cardsInEdition.size()));
promo = FModel.getMagicDb().getCommonCards().getCard(randomCard.name, randomEdition.getCode());
}
@@ -497,7 +498,7 @@ public class QuestEventDraft implements IQuestEvent {
editions.add(FModel.getMagicDb().getEditions().get(booster));
}
return editions.get((int) (Math.random() * editions.size()));
return editions.get((int) (MyRandom.getRandom().nextDouble() * editions.size()));
}
@@ -925,7 +926,7 @@ public class QuestEventDraft implements IQuestEvent {
int attempts = 50;
do {
icon = (int) Math.floor(Math.random() * numberOfIcons);
icon = (int) Math.floor(MyRandom.getRandom().nextDouble() * numberOfIcons);
} while ((icon < 0 || usedIcons.contains(icon)) && attempts-- > 0);
event.aiIcons[i] = icon;

View File

@@ -126,7 +126,7 @@ public class QuestEventDuelManager {
}
}
QuestEventDuel randomOpponent = possibleDuels.get(((int) (possibleDuels.size() * Math.random())));
QuestEventDuel randomOpponent = possibleDuels.get(((int) (possibleDuels.size() * MyRandom.getRandom().nextDouble())));
duel.setTitle("Random Opponent");
duel.setIconImageKey(randomOpponent.getIconImageKey());
@@ -183,7 +183,7 @@ public class QuestEventDuelManager {
} else {
addDuel(duelOpponents, QuestEventDifficulty.HARD, 2);
addDuel(duelOpponents, QuestEventDifficulty.EXPERT, 1);
if (Math.random() * 3 < 2) {
if (MyRandom.getRandom().nextDouble() * 3 < 2) {
randomDuelDifficulty = QuestEventDifficulty.HARD;
} else {
randomDuelDifficulty = QuestEventDifficulty.EXPERT;

View File

@@ -43,7 +43,6 @@ import forge.quest.bazaar.QuestItemType;
import forge.quest.bazaar.QuestPetController;
import forge.quest.data.QuestAchievements;
import forge.quest.data.QuestAssets;
import forge.quest.data.QuestPreferences.QPref;
import forge.util.gui.SGuiChoose;
import forge.util.gui.SOptionPane;

View File

@@ -610,7 +610,7 @@ public final class QuestUtilCards {
}
if (!temp.isEmpty()) {
toAddTo.add(temp.get((int) (Math.random() * temp.size())));
toAddTo.add(temp.get((int) (MyRandom.getRandom().nextDouble() * temp.size())));
return amount - 1;
}

View File

@@ -4,6 +4,7 @@ import forge.deck.Deck;
import forge.deck.DeckType;
import forge.deck.DeckgenUtil;
import forge.model.FModel;
import forge.util.MyRandom;
import java.io.File;
import java.util.ArrayList;
@@ -23,7 +24,7 @@ public class TournamentUtil {
final List<Deck> decks = new ArrayList<Deck>();
for (int i = 0; i < numOpponents; i++) {
int randType = (int)Math.floor(Math.random() * allowedDeckTypes.size());
int randType = (int)Math.floor(MyRandom.getRandom().nextDouble() * allowedDeckTypes.size());
switch (allowedDeckTypes.get(randType)) {
case COLOR_DECK:
deck = DeckgenUtil.getRandomColorDeck(true);