mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 04:08:01 +00:00
merge latest trunk
This commit is contained in:
@@ -7075,6 +7075,27 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("greatestRememberedCMC")) {
|
||||
final List<Card> list = new ArrayList<Card>();
|
||||
for (final Object o : source.getRemembered()) {
|
||||
if (o instanceof Card) {
|
||||
list.add(Singletons.getModel().getGame().getCardState((Card) o));
|
||||
}
|
||||
}
|
||||
if (!list.contains(this)) {
|
||||
return false;
|
||||
}
|
||||
for (final Card crd : list) {
|
||||
if (crd.getRules() != null && crd.getRules().getSplitType() == CardSplitType.Split) {
|
||||
if (crd.getCMC(Card.SplitCMCMode.LeftSplitCMC) > this.getCMC() || crd.getCMC(Card.SplitCMCMode.RightSplitCMC) > this.getCMC()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (crd.getCMC() > this.getCMC()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("lowestCMC")) {
|
||||
final List<Card> list = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield);
|
||||
for (final Card crd : list) {
|
||||
@@ -8067,7 +8088,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
for (final Card ca : Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield)) {
|
||||
for (final ReplacementEffect re : ca.getReplacementEffects()) {
|
||||
HashMap<String, String> params = re.getMapParams();
|
||||
Map<String, String> params = re.getMapParams();
|
||||
if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) {
|
||||
continue;
|
||||
}
|
||||
@@ -8989,7 +9010,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
* the rE
|
||||
*/
|
||||
public void addReplacementEffect(final ReplacementEffect replacementEffect) {
|
||||
final ReplacementEffect replacementEffectCopy = replacementEffect.getCopy();
|
||||
final ReplacementEffect replacementEffectCopy = replacementEffect.getCopy(); // doubtful - every caller provides a newly parsed instance, why copy?
|
||||
replacementEffectCopy.setHostCard(this);
|
||||
this.getCharacteristics().getReplacementEffects().add(replacementEffectCopy);
|
||||
}
|
||||
@@ -9110,7 +9131,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
*/
|
||||
public boolean hasETBReplacement() {
|
||||
for (final ReplacementEffect re : getReplacementEffects()) {
|
||||
final HashMap<String, String> params = re.getMapParams();
|
||||
final Map<String, String> params = re.getMapParams();
|
||||
if (!(re instanceof ReplaceMoved)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import forge.card.MagicColor;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.ability.ApiType;
|
||||
import forge.card.spellability.AbilityManaPart;
|
||||
import forge.card.spellability.AbilitySub;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -269,7 +270,7 @@ public final class CardUtil {
|
||||
}
|
||||
}
|
||||
} else if (reflectProperty.equals("Produced")) {
|
||||
final String producedColors = (String) abMana.getTriggeringObject("Produced");
|
||||
final String producedColors = abMana instanceof AbilitySub ? (String) abMana.getRootAbility().getTriggeringObject("Produced") : (String) abMana.getTriggeringObject("Produced");
|
||||
for (final String col : Constant.Color.ONLY_COLORS) {
|
||||
final String s = MagicColor.toShortString(col);
|
||||
if (producedColors.contains(s)) {
|
||||
|
||||
@@ -19,6 +19,8 @@ package forge;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCostBeingPaid;
|
||||
@@ -46,7 +48,7 @@ public enum Color {
|
||||
/** The Blue. */
|
||||
Blue(16);
|
||||
|
||||
public static final Color[] WUBRG = new Color[] { White, Blue, Black, Red, Green };
|
||||
public static final ImmutableList<Color> WUBRG = ImmutableList.of( White, Blue, Black, Red, Green );
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private int flag = 0;
|
||||
@@ -128,7 +130,7 @@ public enum Color {
|
||||
final EnumSet<Color> colors = EnumSet.of(Color.Colorless);
|
||||
for( int i = 0; i < MagicColor.NUMBER_OR_COLORS; i++ ) {
|
||||
if( cc.hasAnyColor(MagicColor.WUBRG[i]) )
|
||||
colors.add(Color.WUBRG[i]);
|
||||
colors.add(Color.WUBRG.get(i));
|
||||
}
|
||||
if (colors.size() > 1) {
|
||||
colors.remove(Color.Colorless);
|
||||
|
||||
@@ -731,6 +731,10 @@ public class AbilityUtils {
|
||||
players.add(((Card) rem).getController());
|
||||
}
|
||||
}
|
||||
} else if (defined.equals("ImprintedController")) {
|
||||
for (final Card rem : card.getImprinted()) {
|
||||
players.add(rem.getController());
|
||||
}
|
||||
} else if (defined.startsWith("Triggered")) {
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
Object o = null;
|
||||
|
||||
@@ -786,10 +786,11 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|
||||
//only add useful keywords unless P/T bonus is significant
|
||||
if (totToughness + totPower < 4 && !keywords.isEmpty()) {
|
||||
final int pow = totPower;
|
||||
prefList = CardLists.filter(prefList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return containsUsefulKeyword(keywords, c, sa);
|
||||
return containsUsefulKeyword(keywords, c, sa, pow);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -805,18 +806,25 @@ public class AttachAi extends SpellAbilityAi {
|
||||
if (attachSource.isAura() && !attachSource.getName().equals("Daybreak Coronet")) {
|
||||
// TODO For Auras like Rancor, that aren't as likely to lead to
|
||||
// card disadvantage, this check should be skipped
|
||||
prefList = CardLists.filter(prefList, Predicates.not(Presets.ENCHANTED));
|
||||
}
|
||||
|
||||
if (!grantingAbilities && keywords.isEmpty()) {
|
||||
// Probably prefer to Enchant Creatures that Can Attack
|
||||
// Filter out creatures that can't Attack or have Defender
|
||||
prefList = CardLists.filter(prefList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !c.isCreature() || CombatUtil.canAttackNextTurn(c);
|
||||
return !c.isEnchanted() || c.hasKeyword("Hexproof");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!grantingAbilities) {
|
||||
// Probably prefer to Enchant Creatures that Can Attack
|
||||
// Filter out creatures that can't Attack or have Defender
|
||||
if (keywords.isEmpty()) {
|
||||
prefList = CardLists.filter(prefList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !c.isCreature() || CombatUtil.canAttackNextTurn(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
c = ComputerUtilCard.getBestAI(prefList);
|
||||
} else {
|
||||
// If we grant abilities, we may want to put it on something Weak?
|
||||
@@ -955,9 +963,9 @@ public class AttachAi extends SpellAbilityAi {
|
||||
* @param sa SpellAbility
|
||||
* @return true, if successful
|
||||
*/
|
||||
private static boolean containsUsefulKeyword(final ArrayList<String> keywords, final Card card, final SpellAbility sa) {
|
||||
private static boolean containsUsefulKeyword(final ArrayList<String> keywords, final Card card, final SpellAbility sa, final int powerBonus) {
|
||||
for (final String keyword : keywords) {
|
||||
if (isUsefulAttachKeyword(keyword, card, sa)) {
|
||||
if (isUsefulAttachKeyword(keyword, card, sa, powerBonus)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -993,7 +1001,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
* @param sa SpellAbility
|
||||
* @return true, if is useful keyword
|
||||
*/
|
||||
private static boolean isUsefulAttachKeyword(final String keyword, final Card card, final SpellAbility sa) {
|
||||
private static boolean isUsefulAttachKeyword(final String keyword, final Card card, final SpellAbility sa, final int powerBonus) {
|
||||
final PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler();
|
||||
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
|
||||
return false;
|
||||
@@ -1006,14 +1014,14 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|| 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
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| !CombatUtil.canAttackNextTurn(card)
|
||||
|| !CombatUtil.canBeBlocked(card)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Haste")) {
|
||||
if (!card.hasSickness() || !ph.isPlayerTurn(sa.getActivatingPlayer()) || card.isTapped()
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| card.hasKeyword("CARDNAME can attack as though it had haste.")
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| !CombatUtil.canAttackNextTurn(card)) {
|
||||
@@ -1022,22 +1030,22 @@ public class AttachAi extends SpellAbilityAi {
|
||||
} else if (keyword.endsWith("Indestructible")) {
|
||||
return true;
|
||||
} else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) {
|
||||
if (card.getNetCombatDamage() <= 0
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| ((!CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card))
|
||||
&& !CombatUtil.canBlock(card, true))) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Double Strike") || keyword.equals("Lifelink")) {
|
||||
if (card.getNetCombatDamage() <= 0
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| (!CombatUtil.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("First Strike")) {
|
||||
if (card.getNetCombatDamage() <= 0 || card.hasKeyword("Double Strike")) {
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0 || card.hasKeyword("Double Strike")) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.startsWith("Flanking")) {
|
||||
if (card.getNetCombatDamage() <= 0
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| !CombatUtil.canAttackNextTurn(card)
|
||||
|| !CombatUtil.canBeBlocked(card)) {
|
||||
return false;
|
||||
@@ -1048,18 +1056,18 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Trample")) {
|
||||
if (card.getNetCombatDamage() <= 1
|
||||
if (card.getNetCombatDamage() + powerBonus <= 1
|
||||
|| !CombatUtil.canBeBlocked(card)
|
||||
|| !CombatUtil.canAttackNextTurn(card)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Infect")) {
|
||||
if (card.getNetCombatDamage() <= 0
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| !CombatUtil.canAttackNextTurn(card)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Vigilance")) {
|
||||
if (card.getNetCombatDamage() <= 0
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0
|
||||
|| !CombatUtil.canAttackNextTurn(card)
|
||||
|| !CombatUtil.canBlock(card, true)) {
|
||||
return false;
|
||||
|
||||
@@ -424,8 +424,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
* a {@link forge.CardList} object.
|
||||
* @return a {@link forge.Card} object.
|
||||
*/
|
||||
private static Card basicManaFixing(final Player ai, final List<Card> list) { // Search for a
|
||||
// Basic Land
|
||||
private static Card basicManaFixing(final Player ai, final List<Card> list) { // Search for a Basic Land
|
||||
|
||||
final List<Card> combined = new ArrayList<Card>(ai.getCardsIn(ZoneType.Battlefield));
|
||||
combined.addAll(ai.getCardsIn(ZoneType.Hand));
|
||||
@@ -457,6 +456,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (minType != null) {
|
||||
result = CardLists.getType(list, minType);
|
||||
}
|
||||
|
||||
// pick dual lands if available
|
||||
if (Iterables.any(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS))) {
|
||||
result = CardLists.filter(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
||||
}
|
||||
|
||||
return result.get(0);
|
||||
}
|
||||
@@ -1173,7 +1177,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
} else if (origin.contains(ZoneType.Library)
|
||||
&& (type.contains("Basic") || areAllBasics(type))) {
|
||||
c = basicManaFixing(ai, fetchList);
|
||||
} else if (ZoneType.Hand.equals(destination) && CardLists.getNotType(fetchList, "Creature").size() == 0) {
|
||||
} else if (ZoneType.Hand.equals(destination) && CardLists.getNotType(fetchList, "Creature").isEmpty()) {
|
||||
c = chooseCreature(ai, fetchList);
|
||||
} else if (ZoneType.Battlefield.equals(destination) || ZoneType.Graveyard.equals(destination)) {
|
||||
if (!activator.equals(ai) && sa.hasParam("GainControl")) {
|
||||
@@ -1190,7 +1194,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
// Does AI need a land?
|
||||
List<Card> hand = ai.getCardsIn(ZoneType.Hand);
|
||||
if (CardLists.filter(hand, Presets.LANDS).size() == 0 && CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() < 4) {
|
||||
if (CardLists.filter(hand, Presets.LANDS).isEmpty() && CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() < 4) {
|
||||
boolean canCastSomething = false;
|
||||
for (Card cardInHand : hand) {
|
||||
canCastSomething |= ComputerUtilMana.payManaCost(cardInHand.getFirstSpellAbility(), ai, true, 0, false);
|
||||
|
||||
@@ -2,6 +2,9 @@ package forge.card.ability.effects;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import forge.Card;
|
||||
import forge.Singletons;
|
||||
import forge.card.ability.AbilityFactory;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
@@ -9,6 +12,10 @@ import forge.card.ability.SpellAbilityEffect;
|
||||
import forge.card.spellability.AbilitySub;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.GameAction;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class ClashEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -25,7 +32,7 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
*/
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final boolean victory = sa.getSourceCard().getController().clashWithOpponent(sa.getSourceCard());
|
||||
final boolean victory = clashWithOpponent(sa.getSourceCard());
|
||||
|
||||
// Run triggers
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
@@ -56,4 +63,85 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
Singletons.getModel().getGame().getTriggerHandler().runTrigger(TriggerType.Clashed, runParams, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* clashWithOpponent.
|
||||
* </p>
|
||||
*
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean clashWithOpponent(final Card source) {
|
||||
/*
|
||||
* Each clashing player reveals the top card of his or her library, then
|
||||
* puts that card on the top or bottom. A player wins if his or her card
|
||||
* had a higher mana cost.
|
||||
*
|
||||
* Clash you win or win you don't. There is no tie.
|
||||
*/
|
||||
final Player player = source.getController();
|
||||
final Player opponent = player.getOpponent();
|
||||
final ZoneType lib = ZoneType.Library;
|
||||
|
||||
final PlayerZone pLib = player.getZone(lib);
|
||||
final PlayerZone oLib = opponent.getZone(lib);
|
||||
|
||||
final StringBuilder reveal = new StringBuilder();
|
||||
|
||||
Card pCard = null;
|
||||
Card oCard = null;
|
||||
|
||||
if (pLib.size() > 0) {
|
||||
pCard = pLib.get(0);
|
||||
}
|
||||
if (oLib.size() > 0) {
|
||||
oCard = oLib.get(0);
|
||||
}
|
||||
|
||||
if ((pLib.size() == 0) && (oLib.size() == 0)) {
|
||||
return false;
|
||||
} else if (pLib.isEmpty()) {
|
||||
clashMoveToTopOrBottom(opponent, oCard);
|
||||
return false;
|
||||
} else if (oLib.isEmpty()) {
|
||||
clashMoveToTopOrBottom(player, pCard);
|
||||
return true;
|
||||
} else {
|
||||
final int pCMC = pCard.getCMC();
|
||||
final int oCMC = oCard.getCMC();
|
||||
|
||||
// TODO: Split cards will return two CMC values, so both players may become winners of clash
|
||||
|
||||
reveal.append(player).append(" reveals: ").append(pCard.getName()).append(". CMC = ").append(pCMC);
|
||||
reveal.append("\r\n");
|
||||
reveal.append(opponent).append(" reveals: ").append(oCard.getName()).append(". CMC = ").append(oCMC);
|
||||
reveal.append("\r\n\r\n");
|
||||
if (pCMC > oCMC) {
|
||||
reveal.append(player).append(" wins clash.");
|
||||
} else {
|
||||
reveal.append(player).append(" loses clash.");
|
||||
}
|
||||
JOptionPane.showMessageDialog(null, reveal.toString(), source.getName(), JOptionPane.PLAIN_MESSAGE);
|
||||
clashMoveToTopOrBottom(player, pCard);
|
||||
clashMoveToTopOrBottom(opponent, oCard);
|
||||
// JOptionPane.showMessageDialog(null, reveal.toString(),
|
||||
// source.getName(), JOptionPane.PLAIN_MESSAGE);
|
||||
return pCMC > oCMC;
|
||||
}
|
||||
}
|
||||
|
||||
public final void clashMoveToTopOrBottom(final Player p, final Card c) {
|
||||
GameAction action = p.getGame().getAction();
|
||||
boolean putOnTop = p.getController().willPutCardOnTop(c);
|
||||
if ( putOnTop )
|
||||
action.moveToLibrary(c);
|
||||
else
|
||||
action.moveToBottomOfLibrary(c);
|
||||
|
||||
// computer just puts the card back until such time it can make a smarter decision
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -100,9 +100,7 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
final Card eff = new Card();
|
||||
eff.setName(name);
|
||||
eff.addType("Effect"); // Or Emblem
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets
|
||||
// nuked
|
||||
eff.addController(controller);
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(controller);
|
||||
eff.setImageKey(sa.hasParam("Image") ? sa.getParam("Image") : hostCard.getImageKey());
|
||||
eff.setColor(hostCard.getColor());
|
||||
|
||||
@@ -151,8 +151,6 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
}
|
||||
if (sa.hasParam("CopyCard")) {
|
||||
tgtCard = CardDb.getCard(tgtCard).toForgeCard(sa.getActivatingPlayer());
|
||||
// when copying something stolen:
|
||||
tgtCard.addController(sa.getActivatingPlayer());
|
||||
|
||||
tgtCard.setToken(true);
|
||||
tgtCard.setCopiedSpell(true);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.card.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -39,8 +40,12 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
List<Card> repeatCards = null;
|
||||
|
||||
if (sa.hasParam("RepeatCards")) {
|
||||
ZoneType zone = sa.hasParam("Zone") ? ZoneType.smartValueOf(sa.getParam("Zone")) : ZoneType.Battlefield;
|
||||
|
||||
List<ZoneType> zone = new ArrayList<ZoneType>();
|
||||
if (sa.hasParam("Zone")) {
|
||||
zone = ZoneType.listValueOf(sa.getParam("Zone"));
|
||||
} else {
|
||||
zone.add(ZoneType.Battlefield);
|
||||
}
|
||||
repeatCards = CardLists.getValidCards(game.getCardsIn(zone),
|
||||
sa.getParam("RepeatCards"), source.getController(), source);
|
||||
loopOverCards = true;
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package forge.card.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.ability.SpellAbilityEffect;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class ScryEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -42,7 +49,45 @@ public class ScryEffect extends SpellAbilityEffect {
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||
p.scry(num);
|
||||
scry(p, num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* scry.
|
||||
* </p>
|
||||
*
|
||||
* @param numScry
|
||||
* a int.
|
||||
*/
|
||||
public final void scry(Player p, int numScry) {
|
||||
final List<Card> topN = new ArrayList<Card>();
|
||||
final PlayerZone library = p.getZone(ZoneType.Library);
|
||||
numScry = Math.min(numScry, library.size());
|
||||
|
||||
if ( numScry == 0 )
|
||||
return;
|
||||
|
||||
for (int i = 0; i < numScry; i++) {
|
||||
topN.add(library.get(i));
|
||||
}
|
||||
|
||||
ImmutablePair<List<Card>, List<Card>> lists = p.getController().arrangeForScry(topN);
|
||||
List<Card> toTop = lists.getLeft();
|
||||
List<Card> toBottom = lists.getRight();
|
||||
|
||||
if ( null != toBottom) {
|
||||
for(Card c : toBottom) {
|
||||
p.getGame().getAction().moveToBottomOfLibrary(c);
|
||||
}
|
||||
}
|
||||
|
||||
if ( null != toTop ) {
|
||||
Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus.
|
||||
for(Card c : toTop) {
|
||||
p.getGame().getAction().moveToLibrary(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3146,7 +3146,6 @@ public class CardFactoryUtil {
|
||||
eff.addType("Effect"); // Or Emblem
|
||||
eff.setToken(true); // Set token to true, so when leaving
|
||||
// play it gets nuked
|
||||
eff.addController(card.getController());
|
||||
eff.setOwner(card.getController());
|
||||
eff.setColor(card.getColor());
|
||||
eff.setImmutable(true);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
@@ -36,7 +36,7 @@ public class ReplaceDamage extends ReplacementEffect {
|
||||
* @param map the map
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceDamage(HashMap<String, String> map, Card host) {
|
||||
public ReplaceDamage(Map<String, String> map, Card host) {
|
||||
super(map, host);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ReplaceDamage extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("DamageDone")) {
|
||||
return false;
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class ReplaceDamage extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) {
|
||||
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject("DamageAmount", runParams.get("DamageAmount"));
|
||||
sa.setReplacingObject("Target", runParams.get("Affected"));
|
||||
sa.setReplacingObject("Source", runParams.get("DamageSource"));
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -34,7 +34,7 @@ public class ReplaceDiscard extends ReplacementEffect {
|
||||
* @param params the params
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceDiscard(final HashMap<String, String> params, final Card host) {
|
||||
public ReplaceDiscard(final Map<String, String> params, final Card host) {
|
||||
super(params, host);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ReplaceDiscard extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("Discard")) {
|
||||
return false;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class ReplaceDiscard extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) {
|
||||
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject("Card", runParams.get("Card"));
|
||||
sa.setReplacingObject("Player", runParams.get("Affected"));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ReplaceDraw extends ReplacementEffect {
|
||||
* @param params the params
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceDraw(final HashMap<String, String> params, final Card host) {
|
||||
public ReplaceDraw(final Map<String, String> params, final Card host) {
|
||||
super(params, host);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ReplaceDraw extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("Draw")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -34,7 +34,7 @@ public class ReplaceGainLife extends ReplacementEffect {
|
||||
* @param map the map
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceGainLife(HashMap<String, String> map, Card host) {
|
||||
public ReplaceGainLife(Map<String, String> map, Card host) {
|
||||
super(map, host);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ReplaceGainLife extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("GainLife")) {
|
||||
return false;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class ReplaceGainLife extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) {
|
||||
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject("LifeGained", runParams.get("LifeGained"));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ReplaceGameLoss extends ReplacementEffect {
|
||||
* @param map the map
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceGameLoss(HashMap<String, String> map, Card host) {
|
||||
public ReplaceGameLoss(Map<String, String> map, Card host) {
|
||||
super(map, host);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ReplaceGameLoss extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("GameLoss")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -18,7 +18,7 @@ public class ReplaceMoved extends ReplacementEffect {
|
||||
* @param mapParams   HashMap<String, String>
|
||||
* @param host   Card
|
||||
*/
|
||||
public ReplaceMoved(final HashMap<String, String> mapParams, final Card host) {
|
||||
public ReplaceMoved(final Map<String, String> mapParams, final Card host) {
|
||||
super(mapParams, host);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class ReplaceMoved extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("Moved")) {
|
||||
return false;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class ReplaceMoved extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) {
|
||||
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject("Card", runParams.get("Affected"));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ReplaceSetInMotion extends ReplacementEffect {
|
||||
* @param params the params
|
||||
* @param host the host
|
||||
*/
|
||||
public ReplaceSetInMotion(final HashMap<String, String> params, final Card host) {
|
||||
public ReplaceSetInMotion(final Map<String, String> params, final Card host) {
|
||||
super(params, host);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ReplaceSetInMotion extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("SetInMotion")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forge.card.replacement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -17,7 +17,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
|
||||
* @param mapParams   HashMap<String, String>
|
||||
* @param host   Card
|
||||
*/
|
||||
public ReplaceTurnFaceUp(final HashMap<String, String> mapParams, final Card host) {
|
||||
public ReplaceTurnFaceUp(final Map<String, String> mapParams, final Card host) {
|
||||
super(mapParams, host);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(HashMap<String, Object> runParams) {
|
||||
public boolean canReplace(Map<String, Object> runParams) {
|
||||
if (!runParams.get("Event").equals("TurnFaceUp")) {
|
||||
return false;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) {
|
||||
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject("Card", runParams.get("Affected"));
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
}
|
||||
|
||||
/** The map params, denoting what to replace. */
|
||||
private HashMap<String, String> mapParams = new HashMap<String, String>();
|
||||
private Map<String, String> mapParams = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -127,7 +127,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
*
|
||||
* @return a {@link java.util.HashMap} object.
|
||||
*/
|
||||
public final HashMap<String, String> getMapParams() {
|
||||
public final Map<String, String> getMapParams() {
|
||||
return this.mapParams;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
* @param mapParams
|
||||
* the mapParams to set
|
||||
*/
|
||||
public final void setMapParams(final HashMap<String, String> mapParams) {
|
||||
public final void setMapParams(final Map<String, String> mapParams) {
|
||||
this.mapParams = mapParams;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
* the run params
|
||||
* @return true, if successful
|
||||
*/
|
||||
public abstract boolean canReplace(final HashMap<String, Object> runParams);
|
||||
public abstract boolean canReplace(final Map<String, Object> runParams);
|
||||
|
||||
/**
|
||||
* To string.
|
||||
@@ -219,7 +219,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
* @param spellAbility
|
||||
* the SpellAbility
|
||||
*/
|
||||
public void setReplacingObjects(final HashMap<String, Object> runParams, final SpellAbility spellAbility) {
|
||||
public void setReplacingObjects(final Map<String, Object> runParams, final SpellAbility spellAbility) {
|
||||
// Should be overridden by replacers that need it.
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
* @param host
|
||||
* the host
|
||||
*/
|
||||
public ReplacementEffect(final HashMap<String, String> map, final Card host) {
|
||||
public ReplacementEffect(final Map<String, String> map, final Card host) {
|
||||
this.setMapParams(map);
|
||||
this.setHostCard(host);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.Card;
|
||||
import forge.Singletons;
|
||||
@@ -34,6 +35,7 @@ import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiDialog;
|
||||
import forge.util.FileSection;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
@@ -174,9 +176,9 @@ public class ReplacementHandler {
|
||||
* @param replacementEffect
|
||||
* the replacement effect to run
|
||||
*/
|
||||
private ReplacementResult executeReplacement(final HashMap<String, Object> runParams,
|
||||
private ReplacementResult executeReplacement(final Map<String, Object> runParams,
|
||||
final ReplacementEffect replacementEffect, final Player decider, final GameState game) {
|
||||
final HashMap<String, String> mapParams = replacementEffect.getMapParams();
|
||||
final Map<String, String> mapParams = replacementEffect.getMapParams();
|
||||
|
||||
SpellAbility effectSA = null;
|
||||
|
||||
@@ -257,7 +259,7 @@ public class ReplacementHandler {
|
||||
* @return A finished instance
|
||||
*/
|
||||
public static ReplacementEffect parseReplacement(final String repParse, final Card host) {
|
||||
final HashMap<String, String> mapParams = ReplacementHandler.parseParams(repParse);
|
||||
final Map<String, String> mapParams = FileSection.parseToMap(repParse, "$", "|");
|
||||
return ReplacementHandler.parseReplacement(mapParams, host);
|
||||
}
|
||||
|
||||
@@ -272,7 +274,7 @@ public class ReplacementHandler {
|
||||
* The card that hosts the replacement effect
|
||||
* @return The finished instance
|
||||
*/
|
||||
public static ReplacementEffect parseReplacement(final HashMap<String, String> mapParams, final Card host) {
|
||||
public static ReplacementEffect parseReplacement(final Map<String, String> mapParams, final Card host) {
|
||||
ReplacementEffect ret = null;
|
||||
|
||||
final String eventToReplace = mapParams.get("Event");
|
||||
@@ -301,45 +303,4 @@ public class ReplacementHandler {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* parseParams.
|
||||
* </p>
|
||||
*
|
||||
* @param repParse
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a {@link java.util.HashMap} object.
|
||||
*/
|
||||
private static HashMap<String, String> parseParams(final String repParse) {
|
||||
final HashMap<String, String> mapParams = new HashMap<String, String>();
|
||||
|
||||
if (repParse.length() == 0) {
|
||||
throw new RuntimeException("ReplacementFactory : registerTrigger -- trigParse too short");
|
||||
}
|
||||
|
||||
final String[] params = repParse.split("\\|");
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
params[i] = params[i].trim();
|
||||
}
|
||||
|
||||
for (final String param : params) {
|
||||
final String[] splitParam = param.split("\\$");
|
||||
for (int i = 0; i < splitParam.length; i++) {
|
||||
splitParam[i] = splitParam[i].trim();
|
||||
}
|
||||
|
||||
if (splitParam.length != 2) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("ReplacementFactory Parsing Error in registerTrigger() : Split length of ");
|
||||
sb.append(param).append(" is not 2.");
|
||||
throw new RuntimeException(sb.toString());
|
||||
}
|
||||
|
||||
mapParams.put(splitParam[0], splitParam[1]);
|
||||
}
|
||||
|
||||
return mapParams;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package forge.card.spellability;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -498,7 +499,7 @@ public class SpellPermanent extends Spell {
|
||||
for (final ReplacementEffect re : card.getReplacementEffects()) {
|
||||
// These Replacements all care for ETB effects
|
||||
|
||||
final HashMap<String, String> params = re.getMapParams();
|
||||
final Map<String, String> params = re.getMapParams();
|
||||
if (!(re instanceof ReplaceMoved)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -406,19 +406,24 @@ public class WrappedAbility extends Ability implements ISpellAbility {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ArrayList<Object> tgts = null;
|
||||
// make sure the targets won't change
|
||||
if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) {
|
||||
tgts = new ArrayList<Object>(sa.getTarget().getTargetChoices().getTargets());
|
||||
}
|
||||
// This isn't quite right, but better than canPlayAI
|
||||
if (!sa.doTrigger(this.isMandatory(), (AIPlayer)decider)) {
|
||||
return;
|
||||
}
|
||||
if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) {
|
||||
for (Object tgt : tgts) {
|
||||
sa.getTarget().getTargetChoices().clear();
|
||||
sa.getTarget().getTargetChoices().addTarget(tgt);
|
||||
if (triggerParams.containsKey("DelayedTrigger")) {
|
||||
//TODO: The only card with an optional delayed trigger is Shirei, Shizo's Caretaker,
|
||||
// needs to be expanded when a more difficult cards comes up
|
||||
} else {
|
||||
ArrayList<Object> tgts = null;
|
||||
// make sure the targets won't change
|
||||
if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) {
|
||||
tgts = new ArrayList<Object>(sa.getTarget().getTargetChoices().getTargets());
|
||||
}
|
||||
// This isn't quite right, but better than canPlayAI
|
||||
if (!sa.doTrigger(this.isMandatory(), (AIPlayer) decider)) {
|
||||
return;
|
||||
}
|
||||
if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) {
|
||||
for (Object tgt : tgts) {
|
||||
sa.getTarget().getTargetChoices().clear();
|
||||
sa.getTarget().getTargetChoices().addTarget(tgt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +84,9 @@ public class InputMulligan extends Input {
|
||||
@Override
|
||||
public final void selectButtonCancel() {
|
||||
final Player humanPlayer = Singletons.getControl().getPlayer();
|
||||
final int newHand = humanPlayer.doMulligan();
|
||||
humanPlayer.doMulligan();
|
||||
|
||||
if (newHand == 0) {
|
||||
if (humanPlayer.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||
this.end();
|
||||
} else {
|
||||
ButtonUtil.enableAllFocusOk();
|
||||
|
||||
@@ -332,7 +332,6 @@ public class GameNew {
|
||||
for (final IPaperCard cp : cards) {
|
||||
Card c = cp.toForgeCard(player);
|
||||
c.setOwner(player);
|
||||
c.addController(player);
|
||||
bf.add(c, false);
|
||||
c.setSickness(true);
|
||||
c.setStartsGameInPlay(true);
|
||||
|
||||
@@ -118,8 +118,12 @@ public class GameState {
|
||||
endOfTurn = new EndOfTurn(this);
|
||||
endOfCombat = new EndOfCombat(this);
|
||||
|
||||
if ( match0.getGameType() == GameType.Quest)
|
||||
events.register(Singletons.getModel().getQuest()); // this one listens to player's mulligans ATM
|
||||
|
||||
events.register(Singletons.getControl().getSoundSystem());
|
||||
events.register(gameLog);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
@@ -1222,4 +1223,19 @@ public class ComputerUtil {
|
||||
return (handList.size() > AI_MULLIGAN_THRESHOLD) && hasLittleCmc0Cards;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static boolean scryWillMoveCardToBottomOfLibrary(AIPlayer player, Card c) {
|
||||
boolean bottom = false;
|
||||
if (c.isBasicLand()) {
|
||||
List<Card> bl = player.getCardsIn(ZoneType.Battlefield);
|
||||
int nBasicLands = Iterables.size(Iterables.filter(bl, CardPredicates.Presets.BASIC_LANDS));
|
||||
bottom = nBasicLands > 5; // if control more than 5 Basic land, probably don't need more
|
||||
} else if (c.isCreature()) {
|
||||
List<Card> cl = player.getCardsIn(ZoneType.Battlefield);
|
||||
cl = CardLists.filter(cl, CardPredicates.Presets.CREATURES);
|
||||
bottom = cl.size() > 5; // if control more than 5 Creatures, probably don't need more
|
||||
}
|
||||
return bottom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,14 +124,14 @@ public class ComputerUtilCard {
|
||||
*/
|
||||
public static Card getBestLandAI(final List<Card> list) {
|
||||
final List<Card> land = CardLists.filter(list, CardPredicates.Presets.LANDS);
|
||||
if (!(land.size() > 0)) {
|
||||
if (land.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prefer to target non basic lands
|
||||
final List<Card> nbLand = CardLists.filter(land, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
||||
|
||||
if (nbLand.size() > 0) {
|
||||
if (!nbLand.isEmpty()) {
|
||||
// TODO - Rank non basics?
|
||||
return Aggregates.random(nbLand);
|
||||
}
|
||||
@@ -243,11 +243,11 @@ public class ComputerUtilCard {
|
||||
public static Card getBestAI(final List<Card> list) {
|
||||
// Get Best will filter by appropriate getBest list if ALL of the list
|
||||
// is of that type
|
||||
if (CardLists.getNotType(list, "Creature").size() == 0) {
|
||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||
return ComputerUtilCard.getBestCreatureAI(list);
|
||||
}
|
||||
|
||||
if (CardLists.getNotType(list, "Land").size() == 0) {
|
||||
if (CardLists.getNotType(list, "Land").isEmpty()) {
|
||||
return getBestLandAI(list);
|
||||
}
|
||||
|
||||
|
||||
@@ -1695,14 +1695,18 @@ public class ComputerUtilCombat {
|
||||
Card lastBlocker = null;
|
||||
for (final Card b : block) {
|
||||
lastBlocker = b;
|
||||
final int enoughDamageToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true);
|
||||
if (enoughDamageToKill <= dmgCanDeal) {
|
||||
damageMap.put(b, enoughDamageToKill);
|
||||
dmgCanDeal -= enoughDamageToKill;
|
||||
final int dmgToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true);
|
||||
if (dmgToKill <= dmgCanDeal) {
|
||||
damageMap.put(b, dmgToKill);
|
||||
dmgCanDeal -= dmgToKill;
|
||||
} else {
|
||||
damageMap.put(b, dmgCanDeal);
|
||||
dmgCanDeal = 0;
|
||||
break;
|
||||
// if it can't be killed choose the minimum damage
|
||||
int dmg = Math.min(b.getLethalDamage(), dmgCanDeal);
|
||||
damageMap.put(b, dmg);
|
||||
dmgCanDeal -= dmg;
|
||||
if (dmgCanDeal <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // for
|
||||
|
||||
@@ -1829,7 +1833,7 @@ public class ComputerUtilCombat {
|
||||
// Predict replacement effects
|
||||
for (final Card ca : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (final ReplacementEffect re : ca.getReplacementEffects()) {
|
||||
HashMap<String, String> params = re.getMapParams();
|
||||
Map<String, String> params = re.getMapParams();
|
||||
if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
15
src/main/java/forge/game/event/MulliganEvent.java
Normal file
15
src/main/java/forge/game/event/MulliganEvent.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package forge.game.event;
|
||||
|
||||
import forge.game.player.Player;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class MulliganEvent extends Event {
|
||||
|
||||
public final Player player;
|
||||
public MulliganEvent(Player p) {
|
||||
player = p;
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
||||
if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) {
|
||||
this.setPlayersPriorityPermission(false);
|
||||
} else {
|
||||
this.getPlayerTurn().drawCards(1, true);
|
||||
this.getPlayerTurn().drawCard();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.game.player;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
@@ -135,41 +134,6 @@ public class AIPlayer extends Player {
|
||||
|
||||
// /////////////////////////
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected final void doScry(final List<Card> topN, final int n) {
|
||||
int num = n;
|
||||
for (int i = 0; i < num; i++) {
|
||||
boolean bottom = false;
|
||||
if (topN.get(i).isBasicLand()) {
|
||||
List<Card> bl = this.getCardsIn(ZoneType.Battlefield);
|
||||
int nBasicLands = Iterables.size(Iterables.filter(bl, CardPredicates.Presets.BASIC_LANDS));
|
||||
|
||||
bottom = nBasicLands > 5; // if control more than 5 Basic land,
|
||||
// probably don't need more
|
||||
} else if (topN.get(i).isCreature()) {
|
||||
List<Card> cl = this.getCardsIn(ZoneType.Battlefield);
|
||||
cl = CardLists.filter(cl, CardPredicates.Presets.CREATURES);
|
||||
bottom = cl.size() > 5; // if control more than 5 Creatures,
|
||||
// probably don't need more
|
||||
}
|
||||
if (bottom) {
|
||||
final Card c = topN.get(i);
|
||||
game.getAction().moveToBottomOfLibrary(c);
|
||||
// topN.remove(c);
|
||||
}
|
||||
}
|
||||
num = topN.size();
|
||||
// put the rest on top in random order
|
||||
for (int i = 0; i < num; i++) {
|
||||
final Random rndm = MyRandom.getRandom();
|
||||
final int r = rndm.nextInt(topN.size());
|
||||
final Card c = topN.get(r);
|
||||
game.getAction().moveToLibrary(c);
|
||||
topN.remove(r);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void sacrificePermanent(final String prompt, final List<Card> choices) {
|
||||
@@ -180,25 +144,6 @@ public class AIPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected final void clashMoveToTopOrBottom(final Card c) {
|
||||
// computer just puts the card back until such time it can make a
|
||||
// smarter decision
|
||||
game.getAction().moveToLibrary(c);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.Player#discard_Chains_of_Mephistopheles()
|
||||
*/
|
||||
@Override
|
||||
protected final void discardChainsOfMephistopheles() {
|
||||
this.discard(1, null);
|
||||
this.drawCard();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.player.Player#getType()
|
||||
*/
|
||||
|
||||
@@ -24,13 +24,9 @@ import forge.Singletons;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.control.input.Input;
|
||||
import forge.game.GameState;
|
||||
import forge.game.GameType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiDialog;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.bazaar.QuestItemType;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -103,44 +99,6 @@ public class HumanPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.Player#discard_Chains_of_Mephistopheles()
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected final void discardChainsOfMephistopheles() {
|
||||
Singletons.getModel().getMatch().getInput().setInputInterrupt(PlayerUtil.inputChainsDiscard());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected final void doScry(final List<Card> topN, final int n) {
|
||||
int num = n;
|
||||
for (int i = 0; i < num; i++) {
|
||||
final Card c = GuiChoose.oneOrNone("Put on bottom of library.", topN);
|
||||
if (c != null) {
|
||||
topN.remove(c);
|
||||
game.getAction().moveToBottomOfLibrary(c);
|
||||
} else {
|
||||
// no card chosen for the bottom
|
||||
break;
|
||||
}
|
||||
}
|
||||
num = topN.size();
|
||||
for (int i = 0; i < num; i++) {
|
||||
final Card c = GuiChoose.one("Put on top of library.", topN);
|
||||
if (c != null) {
|
||||
topN.remove(c);
|
||||
game.getAction().moveToLibrary(c);
|
||||
}
|
||||
// no else - a card must have been chosen
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void sacrificePermanent(final String prompt, final List<Card> choices) {
|
||||
@@ -148,21 +106,6 @@ public class HumanPlayer extends Player {
|
||||
Singletons.getModel().getMatch().getInput().setInput(in);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected final void clashMoveToTopOrBottom(final Card c) {
|
||||
String choice = "";
|
||||
final String[] choices = { "top", "bottom" };
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(c);
|
||||
choice = GuiChoose.one(c.getName() + " - Top or bottom of Library", choices);
|
||||
|
||||
if (choice.equals("bottom")) {
|
||||
game.getAction().moveToBottomOfLibrary(c);
|
||||
} else {
|
||||
game.getAction().moveToLibrary(c);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.player.Player#getType()
|
||||
*/
|
||||
@@ -171,19 +114,6 @@ public class HumanPlayer extends Player {
|
||||
return PlayerType.HUMAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int doMulligan() {
|
||||
int newHand = super.doMulligan();
|
||||
final QuestController quest = Singletons.getModel().getQuest();
|
||||
final boolean isQuest = Singletons.getModel().getMatch().getGameType().equals(GameType.Quest);
|
||||
if (isQuest && quest.getAssets().hasItem(QuestItemType.SLEIGHT) && (getStats().getMulliganCount() == 1)) {
|
||||
drawCard();
|
||||
newHand++;
|
||||
getStats().notifyOpeningHandSize(newHand);
|
||||
}
|
||||
return newHand;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.player.Player#getController()
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
@@ -42,6 +41,8 @@ import forge.Constant.Preferences;
|
||||
import forge.CounterType;
|
||||
import forge.GameEntity;
|
||||
import forge.Singletons;
|
||||
import forge.card.ability.AbilityFactory;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.mana.ManaCost;
|
||||
@@ -50,6 +51,7 @@ import forge.card.replacement.ReplacementResult;
|
||||
import forge.card.spellability.Ability;
|
||||
import forge.card.spellability.Spell;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.card.staticability.StaticAbility;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.GameActionUtil;
|
||||
@@ -61,9 +63,11 @@ import forge.game.event.CardDiscardedEvent;
|
||||
import forge.game.event.DrawCardEvent;
|
||||
import forge.game.event.LandPlayedEvent;
|
||||
import forge.game.event.LifeLossEvent;
|
||||
import forge.game.event.MulliganEvent;
|
||||
import forge.game.event.PoisonCounterEvent;
|
||||
import forge.game.event.ShuffleEvent;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.PlayerZoneBattlefield;
|
||||
import forge.game.zone.Zone;
|
||||
@@ -124,6 +128,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
/** The num drawn this turn. */
|
||||
private int numDrawnThisTurn = 0;
|
||||
private int numDrawnThisDrawStep = 0;
|
||||
|
||||
/** The slowtrip list. */
|
||||
private List<Card> slowtripList = new ArrayList<Card>();
|
||||
@@ -191,6 +196,10 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
this.setName(lobbyPlayer.getName());
|
||||
}
|
||||
|
||||
public GameState getGame() { // I'll probably regret about this
|
||||
return game;
|
||||
}
|
||||
|
||||
public final PlayerStatistics getStats() {
|
||||
return stats;
|
||||
}
|
||||
@@ -1224,16 +1233,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
return this.drawCards(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* drawCards.
|
||||
* </p>
|
||||
*
|
||||
* @return a List<Card> of cards actually drawn
|
||||
*/
|
||||
public final List<Card> drawCards() {
|
||||
return this.drawCards(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -1244,70 +1243,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
*/
|
||||
public abstract boolean dredge();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* drawCards.
|
||||
* </p>
|
||||
*
|
||||
* @param n
|
||||
* a int.
|
||||
* @return a List<Card> of cards actually drawn
|
||||
*/
|
||||
public final List<Card> drawCards(final int n) {
|
||||
return this.drawCards(n, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* drawCards.
|
||||
* </p>
|
||||
*
|
||||
* @param n
|
||||
* a int.
|
||||
* @param firstFromDraw
|
||||
* true if this is the card drawn from that player's draw step
|
||||
* each turn
|
||||
* @return a List<Card> of cards actually drawn
|
||||
*/
|
||||
public final List<Card> drawCards(final int n, final boolean firstFromDraw) {
|
||||
final List<Card> drawn = new ArrayList<Card>();
|
||||
|
||||
if (!this.canDraw()) {
|
||||
return drawn;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
||||
// TODO: multiple replacements need to be selected by the controller
|
||||
if (this.getDredge().size() != 0) {
|
||||
if (this.dredge()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstFromDraw && game.isCardInPlay("Chains of Mephistopheles")) {
|
||||
if (!this.getZone(ZoneType.Hand).isEmpty()) {
|
||||
if (this.isHuman()) {
|
||||
this.discardChainsOfMephistopheles();
|
||||
} else { // Computer
|
||||
this.discard(1, null);
|
||||
// true causes this code not to be run again
|
||||
drawn.addAll(this.drawCards(1, true));
|
||||
}
|
||||
} else {
|
||||
this.mill(1);
|
||||
}
|
||||
} else {
|
||||
drawn.addAll(this.doDraw());
|
||||
}
|
||||
}
|
||||
|
||||
// Play the Draw sound
|
||||
Singletons.getModel().getGame().getEvents().post(new DrawCardEvent());
|
||||
|
||||
return drawn;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO Write javadoc for this method.
|
||||
@@ -1318,20 +1253,53 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
* a GamePlayerRating object
|
||||
* @return an int
|
||||
*/
|
||||
public int doMulligan() {
|
||||
public void doMulligan() {
|
||||
final List<Card> hand = new ArrayList<Card>(getCardsIn(ZoneType.Hand));
|
||||
for (final Card c : hand) {
|
||||
game.getAction().moveToLibrary(c);
|
||||
}
|
||||
shuffle();
|
||||
final int newHand = hand.size() - 1;
|
||||
for (int i = 0; i < newHand; i++) {
|
||||
drawCard();
|
||||
}
|
||||
drawCards(hand.size() - 1);
|
||||
|
||||
game.getEvents().post(new MulliganEvent(this)); // quest listener may interfere here
|
||||
final int newHand = getCardsIn(ZoneType.Hand).size();
|
||||
game.getGameLog().add("Mulligan", this + " has mulliganed down to " + newHand + " cards.", 0);
|
||||
stats.notifyHasMulliganed();
|
||||
stats.notifyOpeningHandSize(newHand);
|
||||
return newHand;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* drawCards.
|
||||
* </p>
|
||||
*
|
||||
* @param n
|
||||
* a int.
|
||||
* @return a List<Card> of cards actually drawn
|
||||
*/
|
||||
public final List<Card> drawCards(final int n) {
|
||||
final List<Card> drawn = new ArrayList<Card>();
|
||||
|
||||
if (!this.canDraw()) {
|
||||
return drawn;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
||||
// TODO: multiple replacements need to be selected by the controller
|
||||
if (!this.getDredge().isEmpty()) {
|
||||
if (this.dredge()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
drawn.addAll(this.doDraw());
|
||||
}
|
||||
|
||||
// Play the Draw sound
|
||||
game.getEvents().post(new DrawCardEvent());
|
||||
|
||||
return drawn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1354,7 +1322,46 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
return drawn;
|
||||
}
|
||||
|
||||
if (library.size() != 0) {
|
||||
// ======== Chains of Mephistopheles hardcode. =========
|
||||
// This card requires player to either discard a card, and then he may proceed drawing, or mill 1 - then no draw will happen
|
||||
// It's oracle-worded as a replacement effect ("If a player would draw a card ... discards a card instead") (rule 419.1a)
|
||||
// Yet, gatherer's rulings read: The effect is cumulative. If there are two of these on the battlefield, each of them will modify each draw
|
||||
// That means player isn't supposed to choose one replacement effect out of two (generated by Chains Of Mephistopheles), but both happen.
|
||||
// So it's not a common replacement effect and has to handled by special code.
|
||||
|
||||
// This is why the code is placed after any other replacement effects could affect the draw event.
|
||||
List<Card> chainsList = null;
|
||||
for(Card c: game.getCardsInGame()) {
|
||||
if ( c.getName().equals("Chains of Mephistopheles") ) {
|
||||
if ( null == chainsList )
|
||||
chainsList = new ArrayList<Card>();
|
||||
chainsList.add(c);
|
||||
}
|
||||
}
|
||||
if (chainsList != null && (numDrawnThisDrawStep > 0 || !game.getPhaseHandler().is(PhaseType.DRAW))) {
|
||||
for(Card c : chainsList) {
|
||||
// I have to target this player - don't know how to do it.
|
||||
Target target = new Target(c, null, "");
|
||||
target.addTarget(this);
|
||||
|
||||
if (getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||
SpellAbility saMill = AbilityFactory.getAbility(c.getSVar("MillOne"), c);
|
||||
saMill.setActivatingPlayer(c.getController());
|
||||
saMill.setTarget(target);
|
||||
AbilityUtils.resolve(saMill, false);
|
||||
|
||||
return drawn; // Draw is cancelled
|
||||
} else {
|
||||
SpellAbility saDiscard = AbilityFactory.getAbility(c.getSVar("DiscardOne"), c);
|
||||
saDiscard.setActivatingPlayer(c.getController());
|
||||
saDiscard.setTarget(target);
|
||||
AbilityUtils.resolve(saDiscard, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// End of = Chains of Mephistopheles hardcode. =========
|
||||
|
||||
if (!library.isEmpty()) {
|
||||
|
||||
Card c = library.get(0);
|
||||
c = game.getAction().moveToHand(c);
|
||||
@@ -1377,6 +1384,8 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
this.setLastDrawnCard(c);
|
||||
c.setDrawnThisTurn(true);
|
||||
this.numDrawnThisTurn++;
|
||||
if ( game.getPhaseHandler().is(PhaseType.DRAW))
|
||||
this.numDrawnThisDrawStep++;
|
||||
|
||||
// Miracle draws
|
||||
if (this.numDrawnThisTurn == 1 && game.getPhaseHandler().getTurn() != 0) {
|
||||
@@ -1556,6 +1565,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
*/
|
||||
public final void resetNumDrawnThisTurn() {
|
||||
this.numDrawnThisTurn = 0;
|
||||
this.numDrawnThisDrawStep = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1575,11 +1585,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
// /
|
||||
// //////////////////////////////
|
||||
|
||||
/**
|
||||
* Discard_ chains_of_ mephistopheles.
|
||||
*/
|
||||
protected abstract void discardChainsOfMephistopheles();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* discard.
|
||||
@@ -1843,35 +1848,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
// //////////////////////////////
|
||||
|
||||
// //////////////////////////////
|
||||
/**
|
||||
* <p>
|
||||
* doScry.
|
||||
* </p>
|
||||
*
|
||||
* @param topN
|
||||
* a {@link forge.CardList} object.
|
||||
* @param n
|
||||
* a int.
|
||||
*/
|
||||
protected abstract void doScry(List<Card> topN, int n);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* scry.
|
||||
* </p>
|
||||
*
|
||||
* @param numScry
|
||||
* a int.
|
||||
*/
|
||||
public final void scry(int numScry) {
|
||||
final List<Card> topN = new ArrayList<Card>();
|
||||
final PlayerZone library = this.getZone(ZoneType.Library);
|
||||
numScry = Math.min(numScry, library.size());
|
||||
for (int i = 0; i < numScry; i++) {
|
||||
topN.add(library.get(i));
|
||||
}
|
||||
this.doScry(topN, topN.size());
|
||||
}
|
||||
|
||||
// /////////////////////////////
|
||||
|
||||
@@ -2696,87 +2673,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
this.lifeLostThisTurn = n;
|
||||
}
|
||||
|
||||
// //////////////////////////////
|
||||
//
|
||||
// Clash
|
||||
//
|
||||
// ///////////////////////////////
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* clashWithOpponent.
|
||||
* </p>
|
||||
*
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean clashWithOpponent(final Card source) {
|
||||
/*
|
||||
* Each clashing player reveals the top card of his or her library, then
|
||||
* puts that card on the top or bottom. A player wins if his or her card
|
||||
* had a higher mana cost.
|
||||
*
|
||||
* Clash you win or win you don't. There is no tie.
|
||||
*/
|
||||
final Player player = source.getController();
|
||||
final Player opponent = player.getOpponent();
|
||||
final ZoneType lib = ZoneType.Library;
|
||||
|
||||
final PlayerZone pLib = player.getZone(lib);
|
||||
final PlayerZone oLib = opponent.getZone(lib);
|
||||
|
||||
final StringBuilder reveal = new StringBuilder();
|
||||
|
||||
Card pCard = null;
|
||||
Card oCard = null;
|
||||
|
||||
if (pLib.size() > 0) {
|
||||
pCard = pLib.get(0);
|
||||
}
|
||||
if (oLib.size() > 0) {
|
||||
oCard = oLib.get(0);
|
||||
}
|
||||
|
||||
if ((pLib.size() == 0) && (oLib.size() == 0)) {
|
||||
return false;
|
||||
} else if (pLib.size() == 0) {
|
||||
opponent.clashMoveToTopOrBottom(oCard);
|
||||
return false;
|
||||
} else if (oLib.size() == 0) {
|
||||
player.clashMoveToTopOrBottom(pCard);
|
||||
return true;
|
||||
} else {
|
||||
final int pCMC = pCard.getCMC();
|
||||
final int oCMC = oCard.getCMC();
|
||||
reveal.append(player).append(" reveals: ").append(pCard.getName()).append(". CMC = ").append(pCMC);
|
||||
reveal.append("\r\n");
|
||||
reveal.append(opponent).append(" reveals: ").append(oCard.getName()).append(". CMC = ").append(oCMC);
|
||||
reveal.append("\r\n\r\n");
|
||||
if (pCMC > oCMC) {
|
||||
reveal.append(player).append(" wins clash.");
|
||||
} else {
|
||||
reveal.append(player).append(" loses clash.");
|
||||
}
|
||||
JOptionPane.showMessageDialog(null, reveal.toString(), source.getName(), JOptionPane.PLAIN_MESSAGE);
|
||||
player.clashMoveToTopOrBottom(pCard);
|
||||
opponent.clashMoveToTopOrBottom(oCard);
|
||||
// JOptionPane.showMessageDialog(null, reveal.toString(),
|
||||
// source.getName(), JOptionPane.PLAIN_MESSAGE);
|
||||
return pCMC > oCMC;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* clashMoveToTopOrBottom.
|
||||
* </p>
|
||||
*
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
protected abstract void clashMoveToTopOrBottom(Card c);
|
||||
|
||||
/**
|
||||
* a Player or Planeswalker that this Player must attack if able in an
|
||||
* upcoming combat. This is cleared at the end of each combat.
|
||||
|
||||
@@ -3,6 +3,8 @@ package forge.game.player;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import forge.Card;
|
||||
import forge.GameEntity;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -104,4 +106,6 @@ public abstract class PlayerController {
|
||||
|
||||
/** Shows the card to this player*/
|
||||
public abstract void reveal(String string, List<Card> cards, ZoneType zone, Player owner);
|
||||
public abstract ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN);
|
||||
public abstract boolean willPutCardOnTop(Card c);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package forge.game.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import forge.Card;
|
||||
import forge.GameEntity;
|
||||
import forge.card.spellability.Spell;
|
||||
@@ -228,4 +232,27 @@ public class PlayerControllerAi extends PlayerController {
|
||||
// We don't know how to reveal cards to AI
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN) {
|
||||
List<Card> toBottom = new ArrayList<Card>();
|
||||
List<Card> toTop = new ArrayList<Card>();
|
||||
|
||||
for (Card c: topN) {
|
||||
if (ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c))
|
||||
toBottom.add(c);
|
||||
else
|
||||
toTop.add(c);
|
||||
}
|
||||
|
||||
// put the rest on top in random order
|
||||
Collections.shuffle(toTop);
|
||||
return ImmutablePair.of(toTop, toBottom);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean willPutCardOnTop(Card c) {
|
||||
return true; // AI does not know what will happen next (another clash or that would become his topdeck)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.Map;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import forge.Card;
|
||||
import forge.GameEntity;
|
||||
@@ -288,4 +289,33 @@ public class PlayerControllerHuman extends PlayerController {
|
||||
message = String.format("Looking at %s's %s", owner, zone);
|
||||
GuiChoose.oneOrNone(message, cards);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN) {
|
||||
List<Card> toBottom = null;
|
||||
List<Card> toTop = null;
|
||||
|
||||
if (topN.size() == 1) {
|
||||
if (willPutCardOnTop(topN.get(0)))
|
||||
toTop = topN;
|
||||
else
|
||||
toBottom = topN;
|
||||
} else {
|
||||
toBottom = GuiChoose.order("Select cards to be put on the bottom of your library", "Cards to put on the bottom", -1, topN, null, null);
|
||||
topN.removeAll(toBottom);
|
||||
if ( topN.isEmpty() )
|
||||
toTop = null;
|
||||
else if ( topN.size() == 1 )
|
||||
toTop = topN;
|
||||
else
|
||||
toTop = GuiChoose.order("Arrange cards to be put on top of your library", "Cards arranged", 0, topN, null, null);
|
||||
}
|
||||
return ImmutablePair.of(toTop, toBottom);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean willPutCardOnTop(Card c) {
|
||||
return GuiDialog.confirm(c, "Where will you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,45 +153,6 @@ public final class PlayerUtil {
|
||||
return target;
|
||||
} // input_discard()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* input_chainsDiscard.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.control.input.Input} object.
|
||||
*/
|
||||
public static Input inputChainsDiscard() {
|
||||
final Input target = new Input() {
|
||||
private static final long serialVersionUID = 2856894846224546303L;
|
||||
|
||||
@Override
|
||||
public void showMessage() {
|
||||
if (Singletons.getControl().getPlayer().getZone(ZoneType.Hand).size() == 0) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
CMatchUI.SINGLETON_INSTANCE.showMessage("Chains of Mephistopheles:\n" + "Select a card to discard");
|
||||
ButtonUtil.disableAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectCard(final Card card) {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(card);
|
||||
if (zone.is(ZoneType.Hand)) {
|
||||
card.getController().discard(card, null);
|
||||
this.done();
|
||||
}
|
||||
}
|
||||
|
||||
private void done() {
|
||||
this.stop();
|
||||
// hack to not trigger Chains of Mephistopheles recursively
|
||||
Singletons.getControl().getPlayer().drawCards(1, true);
|
||||
}
|
||||
};
|
||||
return target;
|
||||
} // input_chainsDiscard()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* input_sacrificePermanent.
|
||||
|
||||
@@ -15,35 +15,18 @@ import forge.util.MyRandom;
|
||||
*/
|
||||
public class GuiDialog {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* showYesNoDialog.
|
||||
* </p>
|
||||
*
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
* @param question
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
private static final String[] defaultConfirmOptions = { "Yes", "No" };
|
||||
public static boolean confirm(final Card c, final String question) {
|
||||
return GuiDialog.confirm(c, question, true);
|
||||
return GuiDialog.confirm(c, question, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* showYesNoDialog.
|
||||
* </p>
|
||||
*
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
* @param question
|
||||
* a {@link java.lang.String} object.
|
||||
* @param defaultNo
|
||||
* true if the default option should be "No", false otherwise
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean confirm(final Card c, String question, final boolean defaultChoice) {
|
||||
public static boolean confirm(final Card c, final String question, final boolean defaultChoice) {
|
||||
return GuiDialog.confirm(c, question, defaultChoice, null);
|
||||
}
|
||||
public static boolean confirm(final Card c, final String question, String[] options) {
|
||||
return GuiDialog.confirm(c, question, true, options);
|
||||
}
|
||||
|
||||
public static boolean confirm(final Card c, String question, final boolean defaultIsYes, final String[] options) {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(c);
|
||||
final StringBuilder title = new StringBuilder();
|
||||
if ( c != null)
|
||||
@@ -54,14 +37,11 @@ public class GuiDialog {
|
||||
}
|
||||
|
||||
int answer;
|
||||
if (!defaultChoice) {
|
||||
final Object[] options = { "Yes", "No" };
|
||||
answer = JOptionPane.showOptionDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
|
||||
} else {
|
||||
answer = JOptionPane.showConfirmDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION);
|
||||
}
|
||||
|
||||
|
||||
String[] opts = options == null ? defaultConfirmOptions : options;
|
||||
answer = JOptionPane.showOptionDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE, null, opts, opts[defaultIsYes ? 0 : 1]);
|
||||
|
||||
return answer == JOptionPane.YES_OPTION;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,6 @@ public final class GuiDisplayUtil {
|
||||
final Card dummy = new Card();
|
||||
final Player human = Singletons.getControl().getPlayer();
|
||||
dummy.setOwner(human);
|
||||
dummy.addController(human);
|
||||
Map<String, String> produced = new HashMap<String, String>();
|
||||
produced.put("Produced", "W W W W W W W U U U U U U U B B B B B B B G G G G G G G R R R R R R R 7");
|
||||
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
|
||||
|
||||
@@ -21,14 +21,19 @@ import java.io.File;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.GameFormat;
|
||||
import forge.game.event.Event;
|
||||
import forge.game.event.MulliganEvent;
|
||||
import forge.game.player.PlayerType;
|
||||
import forge.item.CardPrinted;
|
||||
import forge.item.PreconDeck;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.quest.bazaar.QuestBazaarManager;
|
||||
import forge.quest.bazaar.QuestItemType;
|
||||
import forge.quest.bazaar.QuestPetStorage;
|
||||
import forge.quest.data.GameFormatQuest;
|
||||
import forge.quest.data.QuestAchievements;
|
||||
@@ -434,4 +439,16 @@ public class QuestController {
|
||||
|
||||
return unlocksAvaliable > unlocksSpent ? Math.min(unlocksAvaliable - unlocksSpent, cntLocked) : 0;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void receiveGameEvent(Event ev) { // Receives events only during quest games
|
||||
if ( ev instanceof MulliganEvent ) {
|
||||
MulliganEvent mev = (MulliganEvent)ev;
|
||||
// First mulligan is free
|
||||
if (mev.player.getType() == PlayerType.HUMAN && getAssets().hasItem(QuestItemType.SLEIGHT) && mev.player.getStats().getMulliganCount() == 0) {
|
||||
mev.player.drawCard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user