merge latest trunk

This commit is contained in:
myk
2013-03-04 04:02:25 +00:00
150 changed files with 2550 additions and 1186 deletions

View File

@@ -6876,10 +6876,6 @@ public class Card extends GameEntity implements Comparable<Card> {
for (final Object rem : source.getRemembered()) {
if (rem instanceof Card) {
final Card card = (Card) rem;
System.out.println(this + " vs " + card);
System.out.println(this.getOwner().equals(card.getOwner()));
System.out.println(this.getOwner());
System.out.println(card.getOwner());
if (!this.getOwner().equals(card.getOwner())) {
return false;
}

View File

@@ -180,7 +180,7 @@ public class BoosterData {
* @see forge.util.StorageReaderFile#read(java.lang.String)
*/
@Override
protected BoosterData read(String line) {
protected BoosterData read(String line, int i) {
final FileSection section = FileSection.parse(line, ":", "|");
int nC = section.getInt("Commons", 0);
int nU = section.getInt("Uncommons", 0);

View File

@@ -229,11 +229,11 @@ public final class CardBlock implements Comparable<CardBlock> {
* @see forge.util.StorageReaderFile#read(java.lang.String)
*/
@Override
protected CardBlock read(String line) {
protected CardBlock read(String line, int i) {
final String[] sParts = line.trim().split("\\|");
String name = null;
int index = -1;
int index = 1+i;
final List<CardEdition> sets = new ArrayList<CardEdition>(9); // add support for up to 9 different sets in a block!
final ArrayList<MetaSet> metas = new ArrayList<MetaSet>();
CardEdition landSet = null;
@@ -245,8 +245,6 @@ public final class CardBlock implements Comparable<CardBlock> {
final String key = kv[0].toLowerCase();
if ("name".equals(key)) {
name = kv[1];
} else if ("index".equals(key)) {
index = Integer.parseInt(kv[1]);
} else if ("set0".equals(key) || "set1".equals(key) || "set2".equals(key) || "set3".equals(key)
|| "set4".equals(key) || "set5".equals(key) || "set6".equals(key) || "set7".equals(key)
|| "set8".equals(key)) {

View File

@@ -208,9 +208,9 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
}
@Override
protected CardEdition read(String line) {
protected CardEdition read(String line, int i) {
FileSection section = FileSection.parse(line, ":", "|");
int index = section.getInt("index", -1);
int index = 1+i;
String code2 = section.get("code2");
String code = section.get("code3");
String type = section.get("type");

View File

@@ -57,7 +57,7 @@ public class FatPackData {
* @see forge.util.StorageReaderFile#read(java.lang.String)
*/
@Override
protected FatPackData read(String line) {
protected FatPackData read(String line, int i) {
final FileSection section = FileSection.parse(line, ":", "|");
int nBoosters = section.getInt("Boosters", 0);
int nLand = section.getInt("BasicLands", 0);

View File

@@ -93,7 +93,7 @@ public final class FormatCollection extends StorageView<GameFormat> {
* @see forge.util.StorageReaderFile#read(java.lang.String)
*/
@Override
protected GameFormat read(String line) {
protected GameFormat read(String line, int i) {
final List<String> sets = new ArrayList<String>(); // default: all sets allowed
final List<String> bannedCards = new ArrayList<String>(); // default:
// nothing
@@ -101,7 +101,7 @@ public final class FormatCollection extends StorageView<GameFormat> {
FileSection section = FileSection.parse(line, ":", "|");
String name = section.get("name");
int index = section.getInt("index", 0);
int index = 1 + i;
String strSets = section.get("sets");
if ( null != strSets ) {
sets.addAll(Arrays.asList(strSets.split(", ")));

View File

@@ -143,7 +143,6 @@ public class AbilityUtils {
final Object crd = root.getTriggeringObject(defined.substring(9));
if (crd instanceof Card) {
c = Singletons.getModel().getGame().getCardState((Card) crd);
c = (Card) crd;
} else if (crd instanceof List<?>) {
for (final Card cardItem : (List<Card>) crd) {
cards.add(cardItem);
@@ -1110,6 +1109,8 @@ public class AbilityUtils {
if (paid) {
unpaidCommand = paidCommand;
}
ability.setActivatingPlayer(payer);
ability.setTarget(sa.getTarget());
GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, paidCommand, unpaidCommand, sa, game);
waitForInput = true; // wait for the human input
break; // multiple human players are not supported

View File

@@ -84,6 +84,12 @@ public abstract class SpellAbilityAi {
if (sa.getRestrictions().getPlaneswalker() && Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
return true;
}
if (sa.isTrigger()) {
return true;
}
if (sa.isSpell() && !sa.isBuyBackAbility()) {
return false;
}
PhaseHandler phase = Singletons.getModel().getGame().getPhaseHandler();
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);

View File

@@ -25,6 +25,7 @@ import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.card.staticability.StaticAbility;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.phase.CombatUtil;
import forge.game.phase.PhaseHandler;
@@ -46,7 +47,13 @@ public class AttachAi extends SpellAbilityAi {
final Card source = sa.getSourceCard();
if (abCost != null) {
// No Aura spells have Additional Costs
// AI currently disabled for these costs
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
}
// prevent run-away activations - first time will always return true
@@ -991,10 +998,12 @@ public class AttachAi extends SpellAbilityAi {
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
return false;
}
final boolean evasive = (keyword.endsWith("Unblockable") || keyword.equals("Fear")
final boolean evasive = (keyword.equals("Unblockable") || keyword.equals("Fear")
|| keyword.equals("Intimidate") || keyword.equals("Shadow")
|| keyword.equals("Flying") || keyword.equals("Horsemanship")
|| keyword.endsWith("walk"));
|| keyword.endsWith("walk") || keyword.equals("CARDNAME can't be blocked except by Walls.")
|| keyword.equals("All creatures able to block CARDNAME do so.")
|| keyword.equals("CARDNAME can't be blocked by more than one creature."));
// give evasive keywords to creatures that can attack and deal damage
if (evasive) {
if (card.getNetCombatDamage() <= 0

View File

@@ -294,8 +294,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
chance &= (r.nextFloat() < .8);
final AbilitySub subAb = sa.getSubAbility();
chance &= subAb == null || subAb.getAi().chkDrawbackWithSubs(ai, subAb);
@@ -747,7 +745,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
// Blink permanents with ETB triggers
else if (sa.isAbility() && (sa.getPayCosts() != null) && SpellAbilityAi.playReusable(ai, sa)) {
else if (SpellAbilityAi.playReusable(ai, sa)) {
aiPermanents = CardLists.filter(aiPermanents, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -1319,8 +1317,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
player.shuffle();
}
if ((!ZoneType.Battlefield.equals(destination) && !"Card".equals(type) && !defined)
|| (sa.hasParam("Reveal") && !fetched.isEmpty())) {
if (((!ZoneType.Battlefield.equals(destination) && !"Card".equals(type) && !defined)
|| (sa.hasParam("Reveal") && !fetched.isEmpty()))
&& !sa.hasParam("NoReveal")) {
final String picked = player + " picked:";
if (fetched.size() > 0) {
GuiChoose.one(picked, fetched);

View File

@@ -4,11 +4,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import forge.card.ability.SpellAbilityAi;
import forge.card.ability.effects.CharmEffect;
import org.apache.commons.lang.math.RandomUtils;
import forge.card.ability.SpellAbilityAi;import forge.card.ability.effects.CharmEffect;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.util.MyRandom;
public class CharmAi extends SpellAbilityAi {
@@ -22,7 +23,7 @@ public class CharmAi extends SpellAbilityAi {
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
List<AbilitySub> chooseFrom = CharmEffect.makePossibleOptions(sa);
List<AbilitySub> chosenList = chooseOptionsAi(ai, timingRight, chooseFrom, num, min);
List<AbilitySub> chosenList = chooseOptionsAi(ai, timingRight, chooseFrom, num, min, false);
if (chosenList == null || chosenList.isEmpty()) {
return false;
@@ -32,9 +33,17 @@ public class CharmAi extends SpellAbilityAi {
return r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
}
public static List<AbilitySub> chooseOptionsAi(final AIPlayer ai, boolean playNow, List<AbilitySub> choices, int num, int min) {
public static List<AbilitySub> chooseOptionsAi(final AIPlayer ai, boolean playNow, List<AbilitySub> choices, int num, int min, boolean opponentChoser) {
List<AbilitySub> chosenList = new ArrayList<AbilitySub>();
if (opponentChoser) {
// This branch is for "An Opponent chooses" Charm spells from Alliances
// Current just choose the first available spell, which seem generally less disastrous for the AI.
//return choices.subList(0, 1);
return choices.subList(1, choices.size());
}
for (int i = 0; i < num; i++) {
AbilitySub thisPick = null;
for (SpellAbility sub : choices) {
@@ -57,4 +66,9 @@ public class CharmAi extends SpellAbilityAi {
}
return chosenList.size() >= min ? chosenList : null;
}
public static Player determineOpponentChooser(AIPlayer ai, SpellAbility sa, List<Player> opponents) {
return opponents.get(RandomUtils.nextInt(opponents.size()));
}
}

View File

@@ -77,6 +77,13 @@ public class ControlGainAi extends SpellAbilityAi {
// if Defined, then don't worry about targeting
if (tgt == null) {
if (sa.hasParam("AllValid")) {
List<Card> tgtCards = ai.getOpponent().getCardsIn(ZoneType.Battlefield);
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
if (tgtCards.isEmpty()) {
return false;
}
}
return true;
} else {
tgt.resetTargets();

View File

@@ -80,7 +80,7 @@ public class CounterAi extends SpellAbilityAi {
if (toPay <= usableManaSources) {
// If this is a reusable Resource, feel free to play it most of
// the time
if (!sa.getPayCosts().isReusuableResource() || sa.isSpell()) {
if (!SpellAbilityAi.playReusable(ai,sa)) {
return false;
}
}
@@ -148,7 +148,7 @@ public class CounterAi extends SpellAbilityAi {
if (toPay <= usableManaSources) {
// If this is a reusable Resource, feel free to play it most
// of the time
if (!sa.getPayCosts().isReusuableResource() || (MyRandom.getRandom().nextFloat() < .4)) {
if (!SpellAbilityAi.playReusable(ai,sa) || (MyRandom.getRandom().nextFloat() < .4)) {
return false;
}
}

View File

@@ -124,7 +124,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
}
}
if (sa.isTrigger() || sa instanceof AbilitySub || SpellAbilityAi.playReusable(ai, sa)) {
if (SpellAbilityAi.playReusable(ai, sa)) {
return chance;
}

View File

@@ -91,7 +91,7 @@ public class DamageAllAi extends SpellAbilityAi {
}
int minGain = 200; // The minimum gain in destroyed creatures
if (sa.getPayCosts().isReusuableResource()) {
if (sa.getPayCosts() != null && sa.getPayCosts().isReusuableResource()) {
minGain = 100;
}

View File

@@ -2,9 +2,12 @@ package forge.card.ability.ai;
import java.util.Random;
import forge.Card;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.game.ai.ComputerUtilMana;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -14,6 +17,7 @@ public class DigUntilAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(AIPlayer ai, SpellAbility sa) {
Card source = sa.getSourceCard();
double chance = .4; // 40 percent chance with instant speed stuff
if (SpellAbilityAi.isSorcerySpeed(sa)) {
chance = .667; // 66.7% chance for sorcery speed (since it will
@@ -36,6 +40,18 @@ public class DigUntilAi extends SpellAbilityAi {
libraryOwner = opp;
}
final String num = sa.getParam("Amount");
if ((num != null) && num.equals("X") && source.getSVar(num).equals("Count$xPaid")) {
// Set PayX here to maximum value.
if (!(sa instanceof AbilitySub) || source.getSVar("PayX").equals("")) {
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai);
if (numCards <= 0) {
return false;
}
source.setSVar("PayX", Integer.toString(numCards));
}
}
// return false if nothing to dig into
if (libraryOwner.getCardsIn(ZoneType.Library).isEmpty()) {
return false;

View File

@@ -132,7 +132,7 @@ public class LifeLoseAi extends SpellAbilityAi {
}
boolean randomReturn = r.nextFloat() <= .6667;
if (SpellAbilityAi.playReusable(ai, sa) || priority) {
if (priority || SpellAbilityAi.playReusable(ai, sa)) {
randomReturn = true;
}

View File

@@ -166,7 +166,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
final boolean evasive = (keyword.endsWith("Unblockable") || keyword.endsWith("Fear")
|| keyword.endsWith("Intimidate") || keyword.endsWith("Shadow")
|| keyword.contains("CantBeBlockedBy"));
|| keyword.contains("CantBeBlockedBy") || keyword.endsWith("CARDNAME can't be blocked except by Walls."));
final boolean combatRelevant = (keyword.endsWith("First Strike") || keyword.contains("Bushido"));
// give evasive keywords to creatures that can or do attack
if (evasive) {

View File

@@ -50,17 +50,37 @@ public class CharmEffect extends SpellAbilityEffect {
final List<AbilitySub> choices = makePossibleOptions(sa);
List<AbilitySub> chosen = null;
Card source = sa.getSourceCard();
Player activator = sa.getActivatingPlayer();
if (activator.isHuman()) {
Player chooser = sa.getActivatingPlayer();
if (sa.hasParam("Chooser")) {
// Three modal cards require you to choose a player to make the modal choice'
// Two of these also reference the chosen player during the spell effect
String choose = sa.getParam("Chooser");
List<Player> opponents = activator.getOpponents();
int numOpps = opponents.size();
if (numOpps == 1) {
chooser = opponents.get(0);
} else {
if (activator.isComputer()) {
chooser = CharmAi.determineOpponentChooser((AIPlayer)activator, sa, opponents);
} else {
chooser = GuiChoose.one("Choose an opponent", opponents);
}
}
source.setChosenPlayer(chooser);
}
if (chooser.isHuman()) {
String modeTitle = String.format("%s activated %s - Choose a mode", activator, source);
chosen = new ArrayList<AbilitySub>();
for (int i = 0; i < num; i++) {
AbilitySub a;
if (i < min) {
a = GuiChoose.one("Choose a mode", choices);
a = GuiChoose.one(modeTitle, choices);
} else {
a = GuiChoose.oneOrNone("Choose a mode", choices);
a = GuiChoose.oneOrNone(modeTitle, choices);
}
if (null == a) {
break;
@@ -70,7 +90,7 @@ public class CharmEffect extends SpellAbilityEffect {
chosen.add(a);
}
} else {
chosen = CharmAi.chooseOptionsAi((AIPlayer)activator, sa.isTrigger(), choices, num, min);
chosen = CharmAi.chooseOptionsAi((AIPlayer)chooser, sa.isTrigger(), choices, num, min, !chooser.equals(activator));
}
chainAbilities(sa, chosen);

View File

@@ -143,7 +143,7 @@ public class DigEffect extends SpellAbilityEffect {
}
// Singletons.getModel().getGameAction().revealToCopmuter(top.toArray());
// - for when it exists
} else if (choser.isHuman()) {
} else if (choser.isHuman() && !sa.hasParam("NoLooking")) {
// show the user the revealed cards
GuiChoose.one("Looking at cards from library", top);
}

View File

@@ -270,7 +270,7 @@ public class DiscardEffect extends RevealEffectBase {
List<Card> chosen = getDiscardedList(p, dPChHand);
for (Card c : chosen) {
dPChHand.remove(chosen);
dPChHand.remove(c);
toBeDiscarded.add(c);
}
} else

View File

@@ -48,7 +48,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
playerLibraries.put(p, newLibrary);
}
GameNew.restartGame(game, sa.getActivatingPlayer(), playerLibraries);
GameNew.restartGame(Singletons.getModel().getMatch(), game, sa.getActivatingPlayer(), playerLibraries);
}
/* (non-Javadoc)

View File

@@ -3,15 +3,19 @@ package forge.card.cardfactory;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import forge.Card;
import forge.Command;
import forge.Singletons;
import forge.card.cost.Cost;
import forge.card.mana.ManaCost;
import forge.card.spellability.Ability;
import forge.card.spellability.AbilityActivated;
import forge.card.spellability.Target;
import forge.control.input.Input;
import forge.control.input.InputSelectManyCards;
import forge.game.ai.ComputerUtil;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.Zone;
@@ -54,8 +58,10 @@ class CardFactoryArtifacts {
@Override
public boolean canPlayAI() {
this.getTarget().resetTargets();
final List<Card> libList = getActivatingPlayer().getOpponent().getCardsIn(ZoneType.Library);
return !libList.isEmpty() && ComputerUtil.targetHumanAI(this);
Player human = getActivatingPlayer().getOpponent();
final List<Card> libList = human.getCardsIn(ZoneType.Library);
this.getTarget().addTarget(human);
return !libList.isEmpty() && canTarget(human);
}
@Override
@@ -98,6 +104,7 @@ class CardFactoryArtifacts {
sb.append("Put the top two cards of target player's library into that player's graveyard. ");
sb.append("If both cards share a color, repeat this process.");
ab1.setDescription(sb.toString());
ab1.setStackDescription(sb.toString());
card.addSpellAbility(ab1);
} // *************** END ************ END **************************
@@ -191,5 +198,126 @@ class CardFactoryArtifacts {
ability.setStackDescription(sbStack.toString());
card.addSpellAbility(ability);
} // *************** END ************ END **************************
// *************** START *********** START **************************
else if (cardName.equals("Temporal Aperture")) {
/*
* 5, Tap: Shuffle your library, then reveal the top card. Until end
* of turn, for as long as that card remains on top of your library,
* play with the top card of your library revealed and you may play
* that card without paying its mana cost. (If it has X in its mana
* cost, X is 0.)
*/
final Card[] topCard = new Card[1];
final Ability freeCast = new Ability(card, ManaCost.ZERO) {
@Override
public boolean canPlay() {
final PlayerZone lib = card.getController().getZone(ZoneType.Library);
return super.canPlay() && ((lib.size() > 0) && lib.get(0).equals(topCard[0]));
}
@Override
public void resolve() {
final Card freeCard = topCard[0];
final Player player = card.getController();
if (freeCard != null) {
if (freeCard.isLand()) {
if (player.canPlayLand(freeCard)) {
player.playLand(freeCard);
} else {
JOptionPane.showMessageDialog(null, "You can't play any more lands this turn.", "",
JOptionPane.INFORMATION_MESSAGE);
}
} else {
Singletons.getModel().getGame().getActionPlay().playCardWithoutManaCost(freeCard, player);
}
} else {
final StringBuilder sb = new StringBuilder();
sb.append("Error in ").append(cardName).append(". freeCard is null");
JOptionPane.showMessageDialog(null, sb.toString(), "", JOptionPane.INFORMATION_MESSAGE);
}
}
@Override
public boolean canPlayAI() {
return false;
}
};
freeCast.setDescription("Play the previously revealed top card of your library for free.");
final StringBuilder sb = new StringBuilder();
sb.append(cardName).append(" - play card without paying its mana cost.");
freeCast.setStackDescription(sb.toString());
class AbilityTemporalAperture extends AbilityActivated {
public AbilityTemporalAperture(final Card ca, final Cost co, final Target t) {
super(ca, co, t);
}
@Override
public AbilityActivated getCopy() {
AbilityActivated res = new AbilityTemporalAperture(getSourceCard(),
getPayCosts(), getTarget() == null ? null : new Target(getTarget()));
CardFactoryUtil.copySpellAbility(this, res);
return res;
}
private static final long serialVersionUID = -7328518969488588777L;
@Override
public void resolve() {
final PlayerZone lib = card.getController().getZone(ZoneType.Library);
if (lib.size() > 0) {
// shuffle your library
card.getController().shuffle();
// reveal the top card
topCard[0] = lib.get(0);
final StringBuilder sb = new StringBuilder();
sb.append("Revealed card:\n").append(topCard[0].getName());
JOptionPane.showMessageDialog(null, sb.toString(), card.getName(), JOptionPane.PLAIN_MESSAGE);
card.addSpellAbility(freeCast);
card.addExtrinsicKeyword("Play with the top card of your library revealed.");
Singletons.getModel().getGame().getEndOfTurn().addUntil(new Command() {
private static final long serialVersionUID = -2860753262177388046L;
@Override
public void execute() {
card.removeSpellAbility(freeCast);
card.removeExtrinsicKeyword("Play with the top card of your library revealed.");
}
});
}
} // resolve
@Override
public boolean canPlayAI() {
return false;
}
}
final Cost abCost = new Cost(card, "5 T", true);
final AbilityActivated ability = new AbilityTemporalAperture(card, abCost, null);
final StringBuilder sbStack = new StringBuilder();
sbStack.append(card).append(" - Shuffle your library, then reveal the top card.");
ability.setStackDescription(sbStack.toString());
final StringBuilder sbDesc = new StringBuilder();
sbDesc.append(abCost).append("Shuffle your library, then reveal the top card. ");
sbDesc.append("Until end of turn, for as long as that card remains on top of your ");
sbDesc.append("library, play with the top card of your library revealed ");
sbDesc.append("and you may play that card without paying its mana cost. ");
sbDesc.append("(If it has X in its mana cost, X is 0.)");
ability.setDescription(sbDesc.toString());
card.addSpellAbility(ability);
} // *************** END ************ END **************************
}
}

View File

@@ -78,7 +78,7 @@ public class Cost {
* @return a boolean.
*/
public final boolean hasNoManaCost() {
return this.getTotalMana().toString().equals(ManaCost.ZERO.toString());
return this.getTotalMana().isZero();
}
/**
@@ -373,7 +373,6 @@ public class Cost {
}
public final CostPartMana getCostMana() {
// TODO: Change where ChangeCost happens
for (final CostPart part : this.costParts) {
if (part instanceof CostPartMana) {
return (CostPartMana) part;

View File

@@ -78,7 +78,6 @@ public class CostPutCounter extends CostPartWithList {
super(amount, type, description);
this.counter = cntr;
}
@Override
public boolean isReusable() { return true; }

View File

@@ -56,6 +56,9 @@ public class CostReveal extends CostPartWithList {
super(amount, type, description);
}
@Override
public boolean isReusable() { return true; }
/*
* (non-Javadoc)
*

View File

@@ -45,7 +45,7 @@ public class CostUnattach extends CostPartWithList {
public boolean isUndoable() { return false; }
@Override
public boolean isReusable() { return false; }
public boolean isReusable() { return true; }
/*
* (non-Javadoc)

View File

@@ -183,6 +183,10 @@ public final class ManaCost implements Comparable<ManaCost> {
return this.shards.isEmpty() && !this.isNoCost();
}
public boolean isZero() {
return genericCost == 0 && isPureGeneric();
}
/*
* (non-Javadoc)
*

View File

@@ -51,7 +51,6 @@ public abstract class SpellAbility implements ISpellAbility {
// choices for constructor isPermanent argument
private String description = "";
private Player targetPlayer = null;
private String stackDescription = "";
private ManaCost manaCost = null;
private ManaCost multiKickerManaCost = null;
@@ -1111,7 +1110,6 @@ public abstract class SpellAbility implements ISpellAbility {
if (tgt != null) {
tgt.addTarget(card);
} else {
this.targetPlayer = null; // reset setTargetPlayer()
this.targetCard = card;
}
String desc = "";
@@ -1124,74 +1122,6 @@ public abstract class SpellAbility implements ISpellAbility {
this.setStackDescription(desc);
}
// /**
// * <p>
// * Getter for the field <code>targetList</code>.
// * </p>
// *
// * @return a {@link forge.CardList} object.
// */
// public List<Card> getTargetList() {
// return this.targetList;
// }
// /**
// * <p>
// * Setter for the field <code>targetList</code>.
// * </p>
// *
// * @param list
// * a {@link forge.CardList} object.
// */
// public void setTargetList(final List<Card> list) {
// // The line below started to create a null error at
// // forge.CardFactoryUtil.canBeTargetedBy(CardFactoryUtil.java:3329)
// // after ForgeSVN r2699. I hope that commenting out the line below will
// // not result in other bugs. :)
// // targetPlayer = null;//reset setTargetPlayer()
//
// this.targetList = list;
// final StringBuilder sb = new StringBuilder();
// sb.append(this.getSourceCard().getName()).append(" - targeting ");
// for (int i = 0; i < this.targetList.size(); i++) {
//
// if (!this.targetList.get(i).isFaceDown()) {
// sb.append(this.targetList.get(i));
// } else {
// sb.append("Morph(").append(this.targetList.get(i).getUniqueNumber()).append(")");
// }
//
// if (i < (this.targetList.size() - 1)) {
// sb.append(", ");
// }
// }
// this.setStackDescription(sb.toString());
// }
/**
* <p>
* Setter for the field <code>targetPlayer</code>.
* </p>
*
* @param p
* a {@link forge.game.player.Player} object.
*/
public void setTargetPlayer(final Player p) {
if (p == null) {
throw new RuntimeException("SpellAbility : setTargetPlayer() error, argument is " + p + " source card is "
+ this.getSourceCard());
}
final Target tgt = this.getTarget();
if (tgt != null) {
tgt.addTarget(p);
} else {
this.targetCard = null; // reset setTargetCard()
this.targetPlayer = p;
}
this.setStackDescription(this.getSourceCard().getName() + " - targeting " + p);
}
/**
* <p>
* Getter for the field <code>targetPlayer</code>.
@@ -1200,18 +1130,15 @@ public abstract class SpellAbility implements ISpellAbility {
* @return a {@link forge.game.player.Player} object.
*/
public Player getTargetPlayer() {
if (this.targetPlayer == null) {
final Target tgt = this.getTarget();
if (tgt != null) {
final ArrayList<Player> list = tgt.getTargetPlayers();
final Target tgt = this.getTarget();
if (tgt != null) {
final ArrayList<Player> list = tgt.getTargetPlayers();
if (!list.isEmpty()) {
return list.get(0);
}
if (!list.isEmpty()) {
return list.get(0);
}
return null;
}
return this.targetPlayer;
return null;
}
/**

View File

@@ -81,6 +81,9 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
if (value.equals("Kicked")) {
this.setKicked(true);
}
if (value.equals("AllTargetsLegal")) {
this.setAllTargetsLegal(true);
}
}
if (params.containsKey("ConditionZone")) {
@@ -196,6 +199,14 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
return false;
}
}
if (this.isAllTargetsLegal()) {
SpellAbility root = sa.getRootAbility();
for (Card c : root.getTarget().getTargetCards()) {
if (!CardFactoryUtil.isTargetStillValid(sa, c)) {
return false;
}
}
}
if (this.isSorcerySpeed() && !activator.canCastSorcery()) {
return false;

View File

@@ -69,6 +69,7 @@ public class SpellAbilityVariables {
this.threshold = sav.isThreshold();
this.metalcraft = sav.isThreshold();
this.hellbent = sav.isHellbent();
this.allTargetsLegal = sav.isAllTargetsLegal();
this.prowl = new ArrayList<String>(sav.getProwl());
this.isPresent = sav.getIsPresent();
this.presentCompare = sav.getPresentCompare();
@@ -136,6 +137,8 @@ public class SpellAbilityVariables {
/** The Kicked. */
private boolean kicked = false;
private boolean allTargetsLegal = false;
/** The prowl. */
private ArrayList<String> prowl = new ArrayList<String>();
@@ -519,6 +522,21 @@ public class SpellAbilityVariables {
}
/**
* @return the allTargetsLegal
*/
public boolean isAllTargetsLegal() {
return allTargetsLegal;
}
/**
* @param allTargetsLegal0 the allTargetsLegal to set
*/
public void setAllTargetsLegal(boolean allTargets) {
this.allTargetsLegal = allTargets;
}
/**
* <p>
* Setter for the field <code>prowl</code>.

View File

@@ -340,16 +340,6 @@ public class WrappedAbility extends Ability implements ISpellAbility {
sa.setTargetCard(card);
}
// @Override
// public void setTargetList(final List<Card> list) {
// sa.setTargetList(list);
// }
@Override
public void setTargetPlayer(final Player p) {
sa.setTargetPlayer(p);
}
@Override
public void setType(final String s) {
sa.setType(s);

View File

@@ -25,6 +25,7 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.game.zone.MagicStack;
import forge.gui.match.controllers.CMessage;
import forge.util.MyObservable;
/**
@@ -65,7 +66,7 @@ public class InputControl extends MyObservable implements java.io.Serializable {
*/
public final void setInput(final Input in) {
boolean isInputEmpty = this.input == null || this.input instanceof InputPassPriority;
System.out.println(in.getClass().getName());
//System.out.println(in.getClass().getName());
if (!this.game.getStack().isResolving() && isInputEmpty) {
this.input = in;
} else {
@@ -134,12 +135,9 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* @param update
* a boolean.
*/
public final void resetInput() { resetInput(true); }
public final void resetInput(final boolean update) {
public final void resetInput() {
this.input = null;
if (update) {
this.updateObservers();
}
this.updateObservers();
}
/**
@@ -244,4 +242,19 @@ public class InputControl extends MyObservable implements java.io.Serializable {
return pc.getDefaultInput();
} // getInput()
public final void setNewInput(GameState game) {
PhaseHandler ph = game.getPhaseHandler();
final Input tmp = getActualInput();
//String message = String.format("%s's %s, priority of %s [%sP] input is %s", ph.getPlayerTurn(), ph.getPhase(), ph.getPriorityPlayer(), ph.isPlayerPriorityAllowed() ? "+" : "-", tmp == null ? "null" : tmp.getClass().getSimpleName());
//System.out.println(message);
if (tmp != null) {
//System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ input is " + tmp.getClass().getName() );
CMessage.SINGLETON_INSTANCE.getInputControl().setInput(tmp);
} else if (!ph.isPlayerPriorityAllowed()) {
// System.out.println("cannot have priority, forced to pass");
ph.getPriorityPlayer().getController().passPriority();
}
}
} // InputControl

View File

@@ -62,10 +62,19 @@ public class InputMulligan extends Input {
ButtonUtil.setButtonText("No", "Yes");
ButtonUtil.enableAllFocusOk();
final String str =
(Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn().equals(Singletons.getControl().getPlayer())
? "You're going first. " : "The computer is going first. ");
CMatchUI.SINGLETON_INSTANCE.showMessage(str + "Do you want to Mulligan?");
GameState game = Singletons.getModel().getGame();
Player startingPlayer = game.getPhaseHandler().getPlayerTurn();
Player localPlayer = Singletons.getControl().getPlayer();
StringBuilder sb = new StringBuilder();
sb.append(startingPlayer.getName()).append(" is going first. ");
if (!startingPlayer.equals(localPlayer)) {
sb.append("You are going ").append(game.getOrdinalPosition(localPlayer, startingPlayer)).append(". ");
}
sb.append("Do you want to Mulligan?");
CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString());
}
/** {@inheritDoc} */

View File

@@ -46,7 +46,7 @@ import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* TODO: Write javadoc for this type.
* This class knows how to make a file out of a deck object and vice versa.
*
*/
public class DeckSerializer extends StorageReaderFolder<Deck> implements IItemSerializer<Deck> {

View File

@@ -248,7 +248,7 @@ public class GameAction {
copied.getCharacteristics().resetCardColor();
}
if (zoneFrom.is(ZoneType.Battlefield)) {
if (zoneFrom.is(ZoneType.Battlefield) && !c.isToken()) {
copied.setSuspendCast(false);
copied.setState(CardCharacteristicName.Original);
// Soulbond unpairing

View File

@@ -11,8 +11,6 @@ import java.util.Set;
import javax.swing.JOptionPane;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@@ -25,11 +23,13 @@ import forge.CardUtil;
import forge.Singletons;
import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType;
import forge.control.input.Input;
import forge.control.input.InputControl;
import forge.control.input.InputMulligan;
import forge.deck.Deck;
import forge.deck.CardPool;
import forge.deck.DeckSection;
import forge.error.BugReporter;
import forge.game.event.FlipCoinEvent;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -38,9 +38,11 @@ import forge.game.player.LobbyPlayer;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.gui.match.controllers.CMessage;
import forge.gui.match.views.VAntes;
import forge.item.CardPrinted;
import forge.item.IPaperCard;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.util.Aggregates;
import forge.util.MyRandom;
@@ -50,6 +52,45 @@ import forge.util.MyRandom;
* All of these methods can and should be static.
*/
public class GameNew {
/**
* TODO: Write javadoc for this type.
*
*/
public static final class GameInputUpdatesThread extends Thread {
private final MatchController match;
private final GameState game;
private boolean wasChangedRecently;
/**
* TODO: Write javadoc for Constructor.
* @param match
* @param game
*/
public GameInputUpdatesThread(MatchController match, GameState game) {
this.match = match;
this.game = game;
}
public void run(){
while(!game.isGameOver()) {
boolean needsNewInput = CMessage.SINGLETON_INSTANCE.getInputControl().isValid() == false;
if ( needsNewInput ) {
match.getInput().setNewInput(game);
wasChangedRecently = true;
}
try {
Thread.sleep(wasChangedRecently ? 2 : 40);
wasChangedRecently = false;
} catch (InterruptedException e) {
BugReporter.reportException(e);
break;
}
}
}
}
public static final ForgePreferences preferences = Singletons.getModel().getPreferences();
private static void preparePlayerLibrary(Player player, final ZoneType zoneType, CardPool secion, boolean canRandomFoil, Random generator) {
PlayerZone library = player.getZone(zoneType);
@@ -60,7 +101,7 @@ public class GameNew {
final Card card = cardPrinted.toForgeCard(player);
// apply random pictures for cards
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_CARD_ART)) {
if (preferences.getPrefBoolean(FPref.UI_RANDOM_CARD_ART)) {
final int cntVariants = cardPrinted.getRules().getEditionInfo(cardPrinted.getEdition()).getCopiesCount();
if (cntVariants > 1) {
card.setRandomPicture(generator.nextInt(cntVariants - 1) + 1);
@@ -86,8 +127,8 @@ public class GameNew {
* TODO: Accept something like match state as parameter. Match should be aware of players,
* their decks and other special starting conditions.
*/
public static void newGame(final Map<Player, PlayerStartConditions> playersConditions, final GameState game, final boolean canRandomFoil) {
Singletons.getModel().getMatch().getInput().clearInput();
public static void newGame(final MatchController match, final Map<Player, PlayerStartConditions> playersConditions, final GameState game, final boolean canRandomFoil) {
match.getInput().clearInput();
Card.resetUniqueNumber();
// need this code here, otherwise observables fail
@@ -97,7 +138,7 @@ public class GameNew {
trigHandler.clearDelayedTrigger();
// friendliness
boolean useAnte = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE);
boolean useAnte = preferences.getPrefBoolean(FPref.UI_ANTE);
final Set<CardPrinted> rAICards = new HashSet<CardPrinted>();
Map<Player, Set<CardPrinted>> removedAnteCards = new HashMap<Player, Set<CardPrinted>>();
@@ -112,8 +153,8 @@ public class GameNew {
initVariantsZones(player, psc);
GameType gameType = Singletons.getModel().getMatch().getGameType();
boolean isFirstGame = Singletons.getModel().getMatch().getPlayedGames().isEmpty();
GameType gameType = match.getGameType();
boolean isFirstGame = match.getPlayedGames().isEmpty();
boolean hasSideboard = psc.getOriginalDeck().has(DeckSection.Sideboard);
boolean canSideBoard = !isFirstGame && gameType.isSideboardingAllowed() && hasSideboard;
@@ -132,7 +173,7 @@ public class GameNew {
preparePlayerLibrary(player, ZoneType.Sideboard, myDeck.get(DeckSection.Sideboard), canRandomFoil, generator);
// Shuffling
if (player instanceof AIPlayer && Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_SMOOTH_LAND)) {
if (player instanceof AIPlayer && preferences.getPrefBoolean(FPref.UI_SMOOTH_LAND)) {
// AI may do this instead of shuffling its deck
final Iterable<Card> c1 = GameNew.smoothComputerManaCurve(player.getCardsIn(ZoneType.Library));
player.getZone(ZoneType.Library).setCards(c1);
@@ -174,7 +215,7 @@ public class GameNew {
JOptionPane.showMessageDialog(null, ante.toString(), "", JOptionPane.INFORMATION_MESSAGE);
}
GameNew.actuateGame(game, false);
GameNew.actuateGame(match, game, false);
}
private static void initVariantsZones(final Player player, final PlayerStartConditions psc) {
@@ -243,8 +284,7 @@ public class GameNew {
}
// ultimate of Karn the Liberated
public static void restartGame(final GameState game, final Player startingTurn, Map<Player, List<Card>> playerLibraries) {
MatchController match = Singletons.getModel().getMatch();
public static void restartGame(final MatchController match, final GameState game, final Player startingTurn, Map<Player, List<Card>> playerLibraries) {
Map<LobbyPlayer, PlayerStartConditions> players = match.getPlayers();
Map<Player, PlayerStartConditions> playersConditions = new HashMap<Player, PlayerStartConditions>();
@@ -291,7 +331,7 @@ public class GameNew {
PhaseHandler phaseHandler = game.getPhaseHandler();
phaseHandler.setPlayerTurn(startingTurn);
GameNew.actuateGame(game, true);
GameNew.actuateGame(match, game, true);
}
/**
@@ -302,10 +342,10 @@ public class GameNew {
* newGame, then when all is ready, call this function.
* @param isRestartedGame Whether the actuated game is the first start or a restart
*/
private static void actuateGame(final GameState game, boolean isRestartedGame) {
private static void actuateGame(final MatchController match, final GameState game, boolean isRestartedGame) {
if (!isRestartedGame) {
// Deciding which cards go to ante
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE)) {
if (preferences.getPrefBoolean(FPref.UI_ANTE)) {
final String nl = System.getProperty("line.separator");
final StringBuilder msg = new StringBuilder();
for (final Player p : game.getPlayers()) {
@@ -324,15 +364,24 @@ public class GameNew {
JOptionPane.showMessageDialog(null, msg, "Ante", JOptionPane.INFORMATION_MESSAGE);
}
GameOutcome lastGameOutcome = Singletons.getModel().getMatch().getLastGameOutcome();
GameOutcome lastGameOutcome = match.getLastGameOutcome();
// Only cut/coin toss if it's the first game of the match
if (lastGameOutcome == null) {
GameNew.seeWhoPlaysFirstDice();
Player goesFirst;
Player humanPlayer = Singletons.getControl().getPlayer();
boolean isFirstGame = lastGameOutcome == null;
if (isFirstGame) {
goesFirst = GameNew.seeWhoPlaysFirstDice(game);
} else {
Player human = Singletons.getControl().getPlayer();
Player goesFirst = lastGameOutcome.isWinner(human.getLobbyPlayer()) ? human.getOpponent() : human;
setPlayersFirstTurn(goesFirst, false);
goesFirst = lastGameOutcome.isWinner(humanPlayer.getLobbyPlayer()) ? humanPlayer.getOpponent() : humanPlayer;
}
String message = goesFirst + ( isFirstGame ? " has won the coin toss." : " lost the last game.");
boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(message);
if ( goesFirst != humanPlayer ) {
JOptionPane.showMessageDialog(null, message + "\nComputer Going First", "You are drawing", JOptionPane.INFORMATION_MESSAGE);
}
goesFirst = willPlay ? goesFirst : goesFirst.getOpponent();
game.getPhaseHandler().setPlayerTurn(goesFirst);
}
// Draw <handsize> cards
@@ -340,9 +389,20 @@ public class GameNew {
p.drawCards(p.getMaxHandSize());
}
game.getPhaseHandler().setPhaseState(PhaseType.MULLIGAN);
InputControl control = Singletons.getModel().getMatch().getInput();
control.setInput(new InputMulligan());
InputControl control = match.getInput();
Input tmp = new InputMulligan();
control.setInput(tmp);
Thread thGame = new GameInputUpdatesThread(match, game);
match.getInput().getInput().showMessage();
thGame.setName("Game input updater");
thGame.start();
} // newGame()
private static String buildFourColumnList(String firstLine, Iterable<CardPrinted> cAnteRemoved) {
@@ -418,49 +478,14 @@ public class GameNew {
* <p>
* seeWhoPlaysFirstCoinToss.
* </p>
* @return
*/
private static void seeWhoPlaysFirstDice() {
int playerDie = 0;
int computerDie = 0;
while (playerDie == computerDie) {
playerDie = MyRandom.getRandom().nextInt(20);
computerDie = MyRandom.getRandom().nextInt(20);
}
private static Player seeWhoPlaysFirstDice(final GameState game) {
// Play the Flip Coin sound
Singletons.getModel().getGame().getEvents().post(new FlipCoinEvent());
game.getEvents().post(new FlipCoinEvent());
List<Player> allPlayers = Singletons.getModel().getGame().getPlayers();
setPlayersFirstTurn(allPlayers.get(MyRandom.getRandom().nextInt(allPlayers.size())), true);
List<Player> allPlayers = game.getPlayers();
return allPlayers.get(MyRandom.getRandom().nextInt(allPlayers.size()));
}
private static void setPlayersFirstTurn(Player goesFirst, boolean firstGame) {
StringBuilder sb = new StringBuilder(goesFirst.toString());
if (firstGame) {
sb.append(" has won the coin toss.");
}
else {
sb.append(" lost the last game.");
}
if (goesFirst.isHuman()) {
if (!humanPlayOrDraw(sb.toString())) {
goesFirst = goesFirst.getOpponent();
}
} else {
sb.append("\nComputer Going First");
JOptionPane.showMessageDialog(null, sb.toString(), "Play or Draw?", JOptionPane.INFORMATION_MESSAGE);
}
Singletons.getModel().getGame().getPhaseHandler().setPlayerTurn(goesFirst);
} // seeWhoPlaysFirstDice()
private static boolean humanPlayOrDraw(String message) {
final String[] possibleValues = { "Play", "Draw" };
final Object playDraw = JOptionPane.showOptionDialog(null, message + "\n\nWould you like to play or draw?",
"Play or Draw?", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null,
possibleValues, possibleValues[0]);
return !playDraw.equals(1);
}
}

View File

@@ -478,6 +478,21 @@ public class GameState {
return roIngamePlayers.get(iPlayer);
}
public String getOrdinalPosition(Player player, Player startingPlayer) {
int startPosition = roIngamePlayers.indexOf(startingPlayer);
int position = roIngamePlayers.indexOf(player) + startPosition + 1;
String[] sufixes = new String[] { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
switch (position % 100) {
case 11:
case 12:
case 13:
return position + "th";
default:
return position + sufixes[position % 10];
}
}
/**
* Only game knows how to get suitable players out of just connected clients.

View File

@@ -19,7 +19,7 @@ import forge.game.player.Player;
import forge.game.player.PlayerStatistics;
import forge.game.player.PlayerType;
import forge.game.zone.ZoneType;
import forge.gui.GuiInput;
import forge.gui.InputProxy;
import forge.gui.framework.EDocID;
import forge.gui.framework.SDisplayUtil;
import forge.gui.match.CMatchUI;
@@ -149,8 +149,7 @@ public class MatchController {
Singletons.getControl().changeState(FControl.Screens.MATCH_SCREEN);
SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc());
// set all observers
GuiInput inputControl = CMessage.SINGLETON_INSTANCE.getInputControl();
InputProxy inputControl = CMessage.SINGLETON_INSTANCE.getInputControl();
input.addObserver(inputControl);
currentGame.getStack().addObserver(inputControl);
currentGame.getPhaseHandler().addObserver(inputControl);
@@ -159,7 +158,7 @@ public class MatchController {
// some observers are set in CMatchUI.initMatch
final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed;
GameNew.newGame(startConditions, currentGame, canRandomFoil);
GameNew.newGame(this, startConditions, currentGame, canRandomFoil);
// TODO restore this functionality!!!
//VMatchUI.SINGLETON_INSTANCE.getViewDevMode().getDocument().setVisible(Preferences.DEV_MODE);

View File

@@ -161,7 +161,9 @@ public class AiInputCommon extends Input {
sa = computer.getSpellAbilityToPlay();
if ( sa == null ) break;
//System.out.println("Playing sa: " + sa);
ComputerUtil.handlePlayingSpellAbility(player, sa, game);
if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) {
break;
}
} while ( sa != null );
}

View File

@@ -425,14 +425,16 @@ public class ComputerUtil {
int count = 0;
while (count < amount) {
final Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList);
if (prefCard != null) {
sacList.add(prefCard);
typeList.remove(prefCard);
count++;
} else {
Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList);
if (prefCard == null) {
prefCard = ComputerUtilCard.getWorstAI(typeList);
}
if (prefCard == null) {
return null;
}
sacList.add(prefCard);
typeList.remove(prefCard);
count++;
}
return sacList;
}
@@ -945,19 +947,6 @@ public class ComputerUtil {
return false;
}
public static boolean targetHumanAI(final SpellAbility sa) {
if (sa == null || sa.getActivatingPlayer() == null) {
return false;
}
Player human = sa.getActivatingPlayer().getOpponent();
if (!sa.canTarget(human)) {
return false;
}
sa.setTargetPlayer(human);
return true;
}
// returns true if it's better to wait until blockers are declared
/**
* <p>

View File

@@ -1648,13 +1648,13 @@ public class ComputerUtilCombat {
public static Map<Card, Integer> distributeAIDamage(final Card attacker, final List<Card> block, int dmgCanDeal, GameEntity defender) {
Map<Card, Integer> damageMap = new HashMap<Card, Integer>();
if (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|| attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked.")) {
boolean isAttacking = defender != null;
if (isAttacking && (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|| attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked."))) {
damageMap.put(null, dmgCanDeal);
return damageMap;
}
boolean isAttacking = defender != null;
final boolean hasTrample = attacker.hasKeyword("Trample");

View File

@@ -460,6 +460,7 @@ public class ComputerUtilMana {
private static List<Card> getAvailableMana(final Player ai, final boolean checkPlayable) {
final GameState game = Singletons.getModel().getGame();
final List<Card> list = ai.getCardsIn(ZoneType.Battlefield);
list.addAll(ai.getCardsIn(ZoneType.Hand));
final List<Card> manaSources = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {

View File

@@ -2522,6 +2522,10 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
if (!source.getRemembered().contains(this)) {
return false;
}
} else if (property.equals("IsNotRemembered")) {
if (source.getRemembered().contains(this)) {
return false;
}
} else if (property.startsWith("EnchantedBy")) {
if (!this.getEnchantedBy().contains(source)) {
return false;

View File

@@ -30,24 +30,6 @@ import forge.Card;
* @version $Id$
*/
interface IZone {
/**
* <p>
* setUpdate.
* </p>
*
* @param b
* a boolean.
*/
void setUpdate(boolean b);
/**
* <p>
* getUpdate.
* </p>
*
* @return a boolean.
*/
boolean getUpdate();
/**
* <p>
@@ -101,7 +83,7 @@ interface IZone {
* @param o
* a {@link java.lang.Object} object.
*/
void remove(Object o);
void remove(Card o);
/**
* <p>

View File

@@ -680,7 +680,7 @@ public class MagicStack extends MyObservable {
// Create a new object, since the triggers aren't happening right away
runParams = new HashMap<String, Object>();
runParams.put("SourceSA", sp);
if (chosenTargets.size() > 0) {
if (!chosenTargets.isEmpty()) {
HashSet<Object> distinctObjects = new HashSet<Object>();
for (final TargetChoices tc : chosenTargets) {
if ((tc != null) && (tc.getTargetCards() != null)) {
@@ -704,10 +704,6 @@ public class MagicStack extends MyObservable {
else if (sp.getTargetCard() != null) {
runParams.put("Target", sp.getTargetCard());
game.getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
} else if (sp.getTargetPlayer() != null) {
runParams.put("Target", sp.getTargetPlayer());
game.getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
}
}
@@ -1075,9 +1071,6 @@ public class MagicStack extends MyObservable {
else if (sa.getTargetCard() != null) {
fizzle = !CardFactoryUtil.isTargetStillValid(sa, sa.getTargetCard());
}
else if (sa.getTargetPlayer() != null) {
fizzle = !sa.getTargetPlayer().canBeTargetedBy(sa);
}
else {
// Set fizzle to the same as the parent if there's no target info
fizzle = parentFizzled;

View File

@@ -17,7 +17,6 @@
*/
package forge.game.zone;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Predicate;
@@ -181,7 +180,7 @@ public class PlayerZoneBattlefield extends PlayerZone {
/** {@inheritDoc} */
@Override
public final void remove(final Object o) {
public final void remove(final Card o) {
super.remove(o);
@@ -244,30 +243,6 @@ public class PlayerZoneBattlefield extends PlayerZone {
}
}
/**
* <p>
* Setter for the field <code>trigger</code>.
* </p>
*
* @param b
* a boolean.
*/
public final void setTrigger(final boolean b) {
this.trigger = b;
}
/**
* <p>
* Setter for the field <code>leavesTrigger</code>.
* </p>
*
* @param b
* a boolean.
*/
public final void setLeavesTrigger(final boolean b) {
this.leavesTrigger = b;
}
/**
* <p>
* setTriggers.
@@ -300,7 +275,7 @@ public class PlayerZoneBattlefield extends PlayerZone {
// getCards(false) to get Phased Out cards
if (!filter) {
return new ArrayList<Card>(cardList);
return super.getCards(false);
}
return Lists.newArrayList(Iterables.filter(cardList, isNotPhased));

View File

@@ -66,6 +66,8 @@ public class Zone extends MyObservable implements IZone, Observer, java.io.Seria
public Zone(final ZoneType zone) {
this.zoneName = zone;
this.roCardList = Collections.unmodifiableList(cardList);
//System.out.println(zoneName + " (ct) " + Integer.toHexString(System.identityHashCode(roCardList)));
}
// ************ BEGIN - these methods fire updateObservers() *************
@@ -191,7 +193,7 @@ public class Zone extends MyObservable implements IZone, Observer, java.io.Seria
* an Object
*/
@Override
public void remove(final Object c) {
public void remove(final Card c) {
this.cardList.remove(c);
this.update();
}
@@ -287,6 +289,7 @@ public class Zone extends MyObservable implements IZone, Observer, java.io.Seria
*/
@Override
public final List<Card> getCards() {
//System.out.println(zoneName + ": " + Integer.toHexString(System.identityHashCode(roCardList)));
return this.getCards(true);
}
@@ -322,29 +325,6 @@ public class Zone extends MyObservable implements IZone, Observer, java.io.Seria
}
}
/**
* Sets the update.
*
* @param b
* a boolean.
*/
@Override
public final void setUpdate(final boolean b) {
this.update = b;
}
/**
* <p>
* Getter for the field <code>update</code>.
* </p>
*
* @return a boolean.
*/
@Override
public final boolean getUpdate() {
return this.update;
}
/**
* <p>
* toString.

View File

@@ -18,7 +18,6 @@
package forge.gui;
import java.awt.Color;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
@@ -61,8 +60,6 @@ import forge.game.zone.ZoneType;
import forge.item.CardDb;
import forge.item.CardPrinted;
import forge.item.IPaperCard;
import forge.view.arcane.PlayArea;
import forge.view.arcane.util.Animation;
/**
* <p>
@@ -255,91 +252,6 @@ public final class GuiDisplayUtil {
}
/**
* <p>
* setupPlayZone.
* </p>
*
* @param p
* a {@link forge.view.arcane.PlayArea} object.
* @param c
* an array of {@link forge.Card} objects.
*/
public static void setupPlayZone(final PlayArea p, final List<Card> c) {
List<Card> tmp, diff;
tmp = new ArrayList<Card>();
for (final forge.view.arcane.CardPanel cpa : p.getCardPanels()) {
tmp.add(cpa.getGameCard());
}
diff = new ArrayList<Card>(tmp);
diff.removeAll(c);
if (diff.size() == p.getCardPanels().size()) {
p.clear();
} else {
for (final Card card : diff) {
p.removeCardPanel(p.getCardPanel(card.getUniqueNumber()));
}
}
diff = new ArrayList<Card>(c);
diff.removeAll(tmp);
List<forge.view.arcane.CardPanel> panelList = new ArrayList<forge.view.arcane.CardPanel>();
for (final Card card : diff) {
panelList.add(p.addCard(card));
}
if (!diff.isEmpty()) {
p.doLayout();
}
for (final forge.view.arcane.CardPanel toPanel : panelList) {
p.scrollRectToVisible(new Rectangle(toPanel.getCardX(), toPanel.getCardY(), toPanel
.getCardWidth(), toPanel.getCardHeight()));
Animation.moveCard(toPanel);
}
for (final Card card : c) {
final forge.view.arcane.CardPanel toPanel = p.getCardPanel(card.getUniqueNumber());
if (card.isTapped()) {
toPanel.setTapped(true);
toPanel.setTappedAngle(forge.view.arcane.CardPanel.TAPPED_ANGLE);
} else {
toPanel.setTapped(false);
toPanel.setTappedAngle(0);
}
toPanel.getAttachedPanels().clear();
if (card.isEnchanted()) {
final ArrayList<Card> enchants = card.getEnchantedBy();
for (final Card e : enchants) {
final forge.view.arcane.CardPanel cardE = p.getCardPanel(e.getUniqueNumber());
if (cardE != null) {
toPanel.getAttachedPanels().add(cardE);
}
}
}
if (card.isEquipped()) {
final ArrayList<Card> enchants = card.getEquippedBy();
for (final Card e : enchants) {
final forge.view.arcane.CardPanel cardE = p.getCardPanel(e.getUniqueNumber());
if (cardE != null) {
toPanel.getAttachedPanels().add(cardE);
}
}
}
if (card.isEnchantingCard()) {
toPanel.setAttachedToPanel(p.getCardPanel(card.getEnchantingCard().getUniqueNumber()));
} else if (card.isEquipping()) {
toPanel.setAttachedToPanel(p.getCardPanel(card.getEquipping().get(0).getUniqueNumber()));
} else {
toPanel.setAttachedToPanel(null);
}
toPanel.setCard(toPanel.getGameCard());
}
p.invalidate();
p.repaint();
}
/**
* <p>
* updateGUI.

View File

@@ -21,9 +21,7 @@ import java.util.Observable;
import java.util.Observer;
import forge.Card;
import forge.Singletons;
import forge.control.input.Input;
import forge.game.phase.PhaseHandler;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.util.MyObservable;
@@ -36,28 +34,16 @@ import forge.util.MyObservable;
* @author Forge
* @version $Id$
*/
public class GuiInput extends MyObservable implements Observer {
public class InputProxy extends MyObservable implements Observer {
/** The input. */
private Input input;
private volatile boolean valid = false;
/** {@inheritDoc} */
@Override
public final void update(final Observable observable, final Object obj) {
PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler();
final Input tmp = Singletons.getModel().getMatch().getInput().getActualInput();
// System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ actual input is " + ( tmp == null ? "null" : tmp.getClass().getName()) + "; MHP = " + ph.mayPlayerHavePriority() );
if (tmp != null) {
// System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ input is " + tmp.getClass().getName() );
this.setInput(tmp);
} else if (!ph.isPlayerPriorityAllowed()) {
//System.out.println("cannot have priority, forced to pass");
ph.passPriority();
}
valid = false;
}
/**
* <p>
* Setter for the field <code>input</code>.
@@ -66,9 +52,10 @@ public class GuiInput extends MyObservable implements Observer {
* @param in
* a {@link forge.control.input.Input} object.
*/
private void setInput(final Input in) {
public void setInput(final Input in) {
valid = true;
this.input = in;
this.input.showMessage();
this.input.showMessage(); // this call may invalidate the input by the time it returns
}
/**
@@ -121,10 +108,13 @@ public class GuiInput extends MyObservable implements Observer {
return this.getInput().toString();
}
/** @return {@link forge.gui.GuiInput.Input} */
/** @return {@link forge.gui.InputProxy.Input} */
public Input getInput() {
return this.input;
}
public boolean isValid() {
return valid;
}
}

View File

@@ -28,7 +28,7 @@ import javax.swing.JButton;
import forge.Command;
import forge.game.MatchController;
import forge.gui.GuiInput;
import forge.gui.InputProxy;
import forge.gui.framework.ICDoc;
import forge.gui.framework.SDisplayUtil;
import forge.gui.match.views.VMessage;
@@ -42,7 +42,7 @@ public enum CMessage implements ICDoc {
/** */
SINGLETON_INSTANCE;
private GuiInput inputControl = new GuiInput();
private InputProxy inputControl = new InputProxy();
private Component lastFocusedButton = null;
private final ActionListener actCancel = new ActionListener() {
@@ -87,7 +87,7 @@ public enum CMessage implements ICDoc {
*
* @return GuiInput
*/
public GuiInput getInputControl() {
public InputProxy getInputControl() {
return this.inputControl;
}

View File

@@ -29,9 +29,7 @@ import forge.Card;
import forge.Command;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.gui.GuiDisplayUtil;
import forge.gui.framework.ICDoc;
import forge.gui.match.CMatchUI;
import forge.gui.match.controllers.CMessage;
@@ -55,8 +53,7 @@ public class CCommand implements ICDoc {
private final Observer observerPlay = new Observer() {
@Override
public void update(final Observable a, final Object b) {
final PlayerZone pZone = (PlayerZone) a;
GuiDisplayUtil.setupPlayZone(CCommand.this.view.getTabletop(), pZone.getCards(false));
CCommand.this.view.getTabletop().setupPlayZone();
}
};
@@ -118,7 +115,7 @@ public class CCommand implements ICDoc {
if (c != null && c.isInZone(ZoneType.Command)) {
//TODO: Cast commander/activate avatar/roll planar dice here.
CMessage.SINGLETON_INSTANCE.getInputControl().getInput().selectCard(c);
CMessage.SINGLETON_INSTANCE.getInputControl().selectCard(c, player.getZone(ZoneType.Command));
}
}

View File

@@ -50,7 +50,6 @@ import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.gui.ForgeAction;
import forge.gui.GuiChoose;
import forge.gui.GuiDisplayUtil;
import forge.gui.framework.ICDoc;
import forge.gui.match.CMatchUI;
import forge.gui.match.controllers.CMessage;
@@ -147,8 +146,7 @@ public class CField implements ICDoc {
private final Observer observerPlay = new Observer() {
@Override
public void update(final Observable a, final Object b) {
final PlayerZone pZone = (PlayerZone) a;
GuiDisplayUtil.setupPlayZone(CField.this.view.getTabletop(), pZone.getCards(false));
CField.this.view.getTabletop().setupPlayZone();
}
};

View File

@@ -23,6 +23,7 @@ import javax.swing.border.MatteBorder;
import net.miginfocom.swing.MigLayout;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab;
import forge.gui.framework.EDocID;
@@ -66,7 +67,7 @@ public class VCommand implements IVDoc<CCommand> {
// TODO player is hard-coded into tabletop...should be dynamic
// (haven't looked into it too deeply). Doublestrike 12-04-12
tabletop = new PlayArea(scroller, id0 == EDocID.COMMAND_0);
tabletop = new PlayArea(scroller, id0 == EDocID.COMMAND_0, player.getZone(ZoneType.Command).getCards(false));
control = new CCommand(player, this);

View File

@@ -121,7 +121,7 @@ public class VField implements IVDoc<CField> {
// TODO player is hard-coded into tabletop...should be dynamic
// (haven't looked into it too deeply). Doublestrike 12-04-12
tabletop = new PlayArea(scroller, id0 == EDocID.FIELD_1);
tabletop = new PlayArea(scroller, id0 == EDocID.FIELD_1, player.getZone(ZoneType.Battlefield).getCards(false));
control = new CField(player, this, playerViewer);

View File

@@ -29,7 +29,6 @@ import com.esotericsoftware.minlog.Log;
import forge.card.mana.ManaCostShard;
import forge.card.mana.ManaCost;
import forge.view.arcane.util.UI;
/**
* <p>
@@ -167,11 +166,11 @@ public class CardFaceSymbols {
* @param w an int
* @param h and int
*/
public static void draw(final Graphics g, String s, int x, final int y, final int w, final int h) {
public static void drawOther(final Graphics g, String s, int x, final int y, final int w, final int h) {
if (s.length() == 0) {
return;
}
s = UI.getDisplayManaCost(s);
StringTokenizer tok = new StringTokenizer(s, " ");
while (tok.hasMoreTokens()) {
String symbol = tok.nextToken();

View File

@@ -24,6 +24,7 @@ import java.util.List;
import com.google.common.base.Function;
import forge.quest.data.GameFormatQuest;
import forge.util.FileSection;
import forge.util.storage.StorageReaderFile;
/**
@@ -121,7 +122,7 @@ public class QuestWorld implements Comparable<QuestWorld>{
* @see forge.util.StorageReaderFile#read(java.lang.String)
*/
@Override
protected QuestWorld read(String line) {
protected QuestWorld read(String line, int i) {
String useName = null;
String useDir = null;
GameFormatQuest useFormat = null;
@@ -129,9 +130,13 @@ public class QuestWorld implements Comparable<QuestWorld>{
final List<String> sets = new ArrayList<String>();
final List<String> bannedCards = new ArrayList<String>(); // if both empty, no format
// This is what you need to use here =>
// FileSection.parse(line, ":", "|");
final String[] sParts = line.trim().split("\\|");
for (final String sPart : sParts) {
final String[] kv = sPart.split(":", 2);
final String key = kv[0].toLowerCase();
if ("name".equals(key)) {

View File

@@ -70,12 +70,13 @@ public abstract class StorageReaderFile<T> implements IItemReader<T> {
public Map<String, T> readAll() {
final Map<String, T> result = new TreeMap<String, T>();
int idx = 0;
for (final String s : FileUtil.readFile(this.file)) {
if (!this.lineContainsObject(s)) {
continue;
}
final T item = this.read(s);
final T item = this.read(s, idx);
if (null == item) {
final String msg = "An object stored in " + this.file.getPath()
+ " failed to load.\nPlease submit this as a bug with the mentioned file attached.";
@@ -83,6 +84,7 @@ public abstract class StorageReaderFile<T> implements IItemReader<T> {
continue;
}
idx++;
result.put(this.keySelector.apply(item), item);
}
@@ -95,8 +97,8 @@ public abstract class StorageReaderFile<T> implements IItemReader<T> {
* @param line
* the line
* @return the t
*/
protected abstract T read(String line);
*/
protected abstract T read(String line, int idx);
/**
* Line contains object.

View File

@@ -415,7 +415,7 @@ public class CardPanel extends JPanel implements CardContainer {
if (this.getCard() != null && this.getGameCard().getFoil() > 0) {
final String fl = String.format("foil%02d", this.getCard().getFoil());
final int z = Math.round(this.cardWidth * CardPanel.BLACK_BORDER_SIZE);
CardFaceSymbols.draw(g, fl, this.cardXOffset + z, this.cardYOffset + z, this.cardWidth - (2 * z),
CardFaceSymbols.drawOther(g, fl, this.cardXOffset + z, this.cardYOffset + z, this.cardWidth - (2 * z),
this.cardHeight - (2 * z));
}
}

View File

@@ -18,10 +18,12 @@
package forge.view.arcane;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
@@ -33,7 +35,6 @@ import forge.Card;
import forge.Constant;
import forge.gui.match.CMatchUI;
import forge.view.arcane.util.CardPanelMouseListener;
import forge.view.arcane.util.UI;
/**
* Manages mouse events and common functionality for CardPanel containing
@@ -296,7 +297,7 @@ public abstract class CardPanelContainer extends JPanel {
* a {@link forge.view.arcane.CardPanel} object.
*/
public final void removeCardPanel(final CardPanel fromPanel) {
UI.invokeAndWait(new Runnable() {
CardPanelContainer.invokeAndWait(new Runnable() {
@Override
public void run() {
if (CardPanelContainer.this.getMouseDragPanel() != null) {
@@ -321,7 +322,7 @@ public abstract class CardPanelContainer extends JPanel {
* </p>
*/
public final void clear() {
UI.invokeAndWait(new Runnable() {
CardPanelContainer.invokeAndWait(new Runnable() {
@Override
public void run() {
CardPanelContainer.this.getCardPanels().clear();
@@ -623,4 +624,25 @@ public abstract class CardPanelContainer extends JPanel {
public void setMouseDragPanel(final CardPanel mouseDragPanel0) {
this.mouseDragPanel = mouseDragPanel0;
}
/**
* <p>
* invokeAndWait.
* </p>
*
* @param runnable
* a {@link java.lang.Runnable} object.
*/
public static void invokeAndWait(final Runnable runnable) {
if (EventQueue.isDispatchThread()) {
runnable.run();
return;
}
try {
EventQueue.invokeAndWait(runnable);
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@@ -27,6 +27,7 @@ import java.util.List;
import javax.swing.JScrollPane;
import forge.Card;
import forge.view.arcane.util.Animation;
import forge.view.arcane.util.CardPanelMouseListener;
/**
@@ -65,6 +66,8 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
private int extraCardSpacingX, cardSpacingX, cardSpacingY;
private int stackSpacingX, stackSpacingY;
private List<Card> model;
/**
* <p>
* Constructor for PlayArea.
@@ -76,10 +79,11 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
* a boolean.
* @param modelRef
*/
public PlayArea(final JScrollPane scrollPane, final boolean mirror) {
public PlayArea(final JScrollPane scrollPane, final boolean mirror, List<Card> modelRef) {
super(scrollPane);
this.setBackground(Color.white);
this.mirror = mirror;
this.model = modelRef;
}
private final CardStackRow collectAllLands() {
@@ -210,6 +214,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
while (deltaCardWidth > 0) {
List<CardStackRow> template = tryArrangePilesOfWidth(lands, tokens, creatures, others);
//System.out.println(template == null ? "won't fit" : "Fits @ " + cardWidth + " !!! " + template.toString());
deltaCardWidth = (getCardWidth() - lastGoodCardWidth) / 2;
if (template != null) {
@@ -254,6 +259,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
int x = 0;
int y = PlayArea.GUTTER_Y;
//System.out.println("-------- " + (mirror ? "^" : "_") + " (Positioning ) Card width = " + cardWidth + ". Playarea = " + playAreaWidth + " x " + playAreaHeight );
for (final CardStackRow row : template) {
int rowBottom = 0;
x = PlayArea.GUTTER_X;
@@ -273,10 +279,11 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
this.setComponentZOrder(panel, panelIndex);
final int panelX = x + (stackPosition * this.stackSpacingX);
final int panelY = y + (stackPosition * this.stackSpacingY);
//System.out.println("... placinng " + panel.getCard() + " @ (" + panelX + ", " + panelY + ")" );
panel.setCardBounds(panelX, panelY, this.getCardWidth(), this.cardHeight);
}
rowBottom = Math.max(rowBottom, y + stack.getHeight());
x += stack.getWidth() + cardSpacingX;
x += stack.getWidth();
}
y = rowBottom;
}
@@ -287,6 +294,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
int afterFirstRow;
//System.out.println( "======== " + ( mirror ? "^" : "_" ) + " (try arrange) Card width = " + cardWidth + ". PlayArea = " + playAreaWidth + " x " + playAreaHeight + " ========");
boolean landsFit, tokensFit, creaturesFit;
if (this.mirror) {
// Wrap all creatures and lands.
@@ -350,13 +358,13 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
// card width.
final boolean isMinimalSize = this.getCardWidth() == this.getCardWidthMin();
// System.err.format("[%d] @ %d - Repaint playarea - %s %n", new Date().getTime(), cntRepaints++, mirror ? "MIRROR" : "DIRECT");
CardStackRow currentRow = new CardStackRow();
for (final CardStack stack : sourceRow) {
final int rowWidth = currentRow.getWidth();
final int stackWidth = stack.getWidth();
//System.out.printf("Adding %s (+%dpx), current row is %dpx and has %s \n", stack, stackWidth, rowWidth, currentRow );
// If the row is not empty and this stack doesn't fit, add the row.
if (rowWidth + stack.getWidth() > this.playAreaWidth && !currentRow.isEmpty() ) {
if (rowWidth + stackWidth > this.playAreaWidth && !currentRow.isEmpty() ) {
// Stop processing if the row is too wide or tall.
if (rowWidth > this.playAreaWidth || this.getRowsHeight(template) + sourceRow.getHeight() > this.playAreaHeight) {
@@ -384,6 +392,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
template.add(insertIndex, currentRow);
} else return false;
}
//System.out.println("... row complete! " + currentRow.getWidth() + "px");
return true;
}
@@ -405,6 +414,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
private int planOthersRow(final List<CardStack> sourceRow, final int firstPile, final List<CardStackRow> template, final CardStackRow rowToFill) {
int rowWidth = rowToFill.getWidth();
// System.out.println("This row has:" + rowToFill + "; want to add:" + sourceRow );
for (int i = firstPile; i < sourceRow.size(); i++ ) {
CardStack stack = sourceRow.get(i);
@@ -478,6 +488,89 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
super.mouseLeftClicked(panel, evt);
}
/**
* <p>
* setupPlayZone.
* </p>
*
* @param newList
* an array of {@link forge.Card} objects.
*/
public void setupPlayZone() {
List<Card> oldCards, toDelete;
oldCards = new ArrayList<Card>();
for (final CardPanel cpa : getCardPanels()) {
oldCards.add(cpa.getGameCard());
}
toDelete = new ArrayList<Card>(oldCards);
toDelete.removeAll(model);
if (toDelete.size() == getCardPanels().size()) {
clear();
} else {
for (final Card card : toDelete) {
removeCardPanel(getCardPanel(card.getUniqueNumber()));
}
}
List<Card> toAdd = new ArrayList<Card>(model);
toAdd.removeAll(oldCards);
List<CardPanel> newPanels = new ArrayList<CardPanel>();
for (final Card card : toAdd) {
newPanels.add(addCard(card));
}
if (!toAdd.isEmpty()) {
doLayout();
}
for (final CardPanel toPanel : newPanels) {
scrollRectToVisible(new Rectangle(toPanel.getCardX(), toPanel.getCardY(), toPanel.getCardWidth(), toPanel.getCardHeight()));
Animation.moveCard(toPanel);
}
for (final Card card : model) {
final CardPanel toPanel = getCardPanel(card.getUniqueNumber());
if (card.isTapped()) {
toPanel.setTapped(true);
toPanel.setTappedAngle(forge.view.arcane.CardPanel.TAPPED_ANGLE);
} else {
toPanel.setTapped(false);
toPanel.setTappedAngle(0);
}
toPanel.getAttachedPanels().clear();
if (card.isEnchanted()) {
final ArrayList<Card> enchants = card.getEnchantedBy();
for (final Card e : enchants) {
final forge.view.arcane.CardPanel cardE = getCardPanel(e.getUniqueNumber());
if (cardE != null) {
toPanel.getAttachedPanels().add(cardE);
}
}
}
if (card.isEquipped()) {
final ArrayList<Card> enchants = card.getEquippedBy();
for (final Card e : enchants) {
final forge.view.arcane.CardPanel cardE = getCardPanel(e.getUniqueNumber());
if (cardE != null) {
toPanel.getAttachedPanels().add(cardE);
}
}
}
if (card.isEnchantingCard()) {
toPanel.setAttachedToPanel(getCardPanel(card.getEnchantingCard().getUniqueNumber()));
} else if (card.isEquipping()) {
toPanel.setAttachedToPanel(getCardPanel(card.getEquipping().get(0).getUniqueNumber()));
} else {
toPanel.setAttachedToPanel(null);
}
toPanel.setCard(toPanel.getGameCard());
}
invalidate();
repaint();
}
private static enum RowType {
Land,
Creature,

View File

@@ -140,6 +140,18 @@ public abstract class Animation {
protected void end() {
}
/**
* <p>
* invokeLater.
* </p>
*
* @param runnable
* a {@link java.lang.Runnable} object.
*/
public static void invokeLater(final Runnable runnable) {
EventQueue.invokeLater(runnable);
}
/**
* Uses averaging of the time between the past few frames to provide smooth
* animation.
@@ -248,7 +260,7 @@ public abstract class Animation {
public static void moveCardToPlay(final int startX, final int startY, final int startWidth, final int endX,
final int endY, final int endWidth, final CardPanel animationPanel, final CardPanel placeholder,
final JLayeredPane layeredPane, final int speed) {
UI.invokeLater(new Runnable() {
Animation.invokeLater(new Runnable() {
@Override
public void run() {
final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO);
@@ -343,7 +355,7 @@ public abstract class Animation {
public static void moveCard(final int startX, final int startY, final int startWidth, final int endX,
final int endY, final int endWidth, final CardPanel animationPanel, final CardPanel placeholder,
final JLayeredPane layeredPane, final int speed) {
UI.invokeLater(new Runnable() {
Animation.invokeLater(new Runnable() {
@Override
public void run() {
final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO);
@@ -397,19 +409,14 @@ public abstract class Animation {
* a {@link forge.view.arcane.CardPanel} object.
*/
public static void moveCard(final CardPanel placeholder) {
UI.invokeLater(new Runnable() {
Animation.invokeLater(new Runnable() {
@Override
public void run() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (placeholder != null) {
placeholder.setDisplayEnabled(true);
// placeholder.setImage(imagePanel);
placeholder.setCard(placeholder.getGameCard());
}
}
});
if (placeholder != null) {
placeholder.setDisplayEnabled(true);
// placeholder.setImage(imagePanel);
placeholder.setCard(placeholder.getGameCard());
}
}
});
}

View File

@@ -1,326 +0,0 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Nate
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.view.arcane.util;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.concurrent.ConcurrentMap;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.ViewportLayout;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.ImageView;
import com.google.common.collect.MapMaker;
/**
* UI utility functions.
*
* @author Forge
* @version $Id$
*/
public class UI {
/** Constant <code>imageCache</code>. */
private static ConcurrentMap<URI, Image> imageCache = new MapMaker().softValues().makeMap();
/**
* <p>
* getToggleButton.
* </p>
*
* @return a {@link javax.swing.JToggleButton} object.
*/
public static JToggleButton getToggleButton() {
JToggleButton button = new JToggleButton();
button.setMargin(new Insets(2, 4, 2, 4));
return button;
}
/**
* <p>
* getButton.
* </p>
*
* @return a {@link javax.swing.JButton} object.
*/
public static JButton getButton() {
JButton button = new JButton();
button.setMargin(new Insets(2, 4, 2, 4));
return button;
}
/**
* <p>
* setTitle.
* </p>
*
* @param panel
* a {@link javax.swing.JPanel} object.
* @param title
* a {@link java.lang.String} object.
*/
public static void setTitle(final JPanel panel, final String title) {
Border border = panel.getBorder();
if (border instanceof TitledBorder) {
((TitledBorder) panel.getBorder()).setTitle(title);
panel.repaint();
} else {
panel.setBorder(BorderFactory.createTitledBorder(title));
}
}
/**
* <p>
* getFileURL.
* </p>
*
* @param path
* a {@link java.lang.String} object.
* @return a {@link java.net.URL} object.
*/
public static URL getFileURL(final String path) {
File file = new File(path);
if (file.exists()) {
try {
return file.toURI().toURL();
} catch (MalformedURLException ignored) {
}
}
return UI.class.getResource(path);
}
/**
* <p>
* getImageIcon.
* </p>
*
* @param path
* a {@link java.lang.String} object.
* @return a {@link javax.swing.ImageIcon} object.
*/
public static ImageIcon getImageIcon(final String path) {
InputStream stream = null;
try {
try {
stream = UI.class.getResourceAsStream(path);
if (stream == null && new File(path).exists()) {
stream = new FileInputStream(path);
}
if (stream == null) {
throw new RuntimeException("Image not found: " + path);
}
byte[] data = new byte[stream.available()];
stream.read(data);
return new ImageIcon(data);
} finally {
if (stream != null) {
stream.close();
}
}
} catch (IOException ex) {
throw new RuntimeException("Error reading image: " + path);
}
}
/**
* <p>
* setHTMLEditorKit.
* </p>
*
* @param editorPane
* a {@link javax.swing.JEditorPane} object.
*/
public static void setHTMLEditorKit(final JEditorPane editorPane) {
editorPane.getDocument().putProperty("imageCache", imageCache); // Read
// internally
// by
// ImageView,
// but
// never
// written.
// Extend all this shit to cache images.
editorPane.setEditorKit(new HTMLEditorKit() {
private static final long serialVersionUID = -562969765076450440L;
@Override
public ViewFactory getViewFactory() {
return new HTMLFactory() {
@Override
public View create(final Element elem) {
Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
if (o instanceof HTML.Tag) {
HTML.Tag kind = (HTML.Tag) o;
if (kind == HTML.Tag.IMG) {
return new ImageView(elem) {
@Override
public URL getImageURL() {
URL url = super.getImageURL();
// Put an image into the cache to be
// read by other ImageView methods.
if (url != null && imageCache.get(url) == null) {
try {
imageCache.put(url.toURI(), Toolkit.getDefaultToolkit()
.createImage(url));
} catch (URISyntaxException e) {
}
}
return url;
}
};
}
}
return super.create(elem);
}
};
}
});
}
/**
* <p>
* setVerticalScrollingView.
* </p>
*
* @param scrollPane
* a {@link javax.swing.JScrollPane} object.
* @param view
* a {@link java.awt.Component} object.
*/
public static void setVerticalScrollingView(final JScrollPane scrollPane, final Component view) {
final JViewport viewport = new JViewport();
viewport.setLayout(new ViewportLayout() {
private static final long serialVersionUID = -4436977380450713628L;
@Override
public void layoutContainer(final Container parent) {
viewport.setViewPosition(new Point(0, 0));
Dimension viewportSize = viewport.getSize();
int width = viewportSize.width;
int height = Math.max(view.getPreferredSize().height, viewportSize.height);
viewport.setViewSize(new Dimension(width, height));
}
});
viewport.setView(view);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setViewport(viewport);
}
/**
* <p>
* getDisplayManaCost.
* </p>
*
* @param manaCost
* a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
public static String getDisplayManaCost(String manaCost) {
manaCost = manaCost.replace("/", "");
manaCost = manaCost.replace("X 0", "X");
// A pipe in the cost means
// "process left of the pipe as the card color, but display right of the pipe as the cost".
int pipePosition = manaCost.indexOf("{|}");
if (pipePosition != -1) {
manaCost = manaCost.substring(pipePosition + 3);
}
return manaCost;
}
/**
* <p>
* invokeLater.
* </p>
*
* @param runnable
* a {@link java.lang.Runnable} object.
*/
public static void invokeLater(final Runnable runnable) {
EventQueue.invokeLater(runnable);
}
/**
* <p>
* invokeAndWait.
* </p>
*
* @param runnable
* a {@link java.lang.Runnable} object.
*/
public static void invokeAndWait(final Runnable runnable) {
if (EventQueue.isDispatchThread()) {
runnable.run();
return;
}
try {
EventQueue.invokeAndWait(runnable);
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
/**
* <p>
* setDefaultFont.
* </p>
*
* @param font
* a {@link java.awt.Font} object.
*/
public static void setDefaultFont(final Font font) {
for (Object key : Collections.list(UIManager.getDefaults().keys())) {
Object value = UIManager.get(key);
if (value instanceof javax.swing.plaf.FontUIResource) {
UIManager.put(key, font);
}
}
}
}