mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Merge branch 'playTriggerChangesZoneAll' into 'master'
Update PhaseHandler for Trigger ChangesZoneAll See merge request core-developers/forge!1214
This commit is contained in:
@@ -1514,7 +1514,7 @@ public class ComputerUtilMana {
|
||||
final Card offering = sa.getSacrificedAsOffering();
|
||||
offering.setUsedToPay(false);
|
||||
if (costIsPaid && !test) {
|
||||
sa.getHostCard().getController().getGame().getAction().sacrifice(offering, sa);
|
||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null);
|
||||
}
|
||||
sa.resetSacrificedAsOffering();
|
||||
}
|
||||
@@ -1522,7 +1522,7 @@ public class ComputerUtilMana {
|
||||
final Card emerge = sa.getSacrificedAsEmerge();
|
||||
emerge.setUsedToPay(false);
|
||||
if (costIsPaid && !test) {
|
||||
sa.getHostCard().getController().getGame().getAction().sacrifice(emerge, sa);
|
||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null);
|
||||
}
|
||||
sa.resetSacrificedAsEmerge();
|
||||
}
|
||||
|
||||
@@ -126,7 +126,8 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
return chance;
|
||||
}
|
||||
|
||||
private List<Card> determineGoodBlockers(final Card attacker,final Player ai,final Player defender, SpellAbility sa, final boolean onlyLethal, final boolean testTapped) {
|
||||
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
|
||||
final boolean onlyLethal, final boolean testTapped) {
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
|
||||
|
||||
@@ -926,6 +926,8 @@ public class GameAction {
|
||||
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
|
||||
boolean checkAgain = false;
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
|
||||
for (final Player p : game.getPlayers()) {
|
||||
for (final ZoneType zt : ZoneType.values()) {
|
||||
if (zt == ZoneType.Battlefield) {
|
||||
@@ -973,8 +975,8 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
checkAgain |= stateBasedAction_Saga(c);
|
||||
checkAgain |= stateBasedAction704_attach(c); // Attachment
|
||||
checkAgain |= stateBasedAction_Saga(c, table);
|
||||
checkAgain |= stateBasedAction704_attach(c, table); // Attachment
|
||||
|
||||
if (c.isCreature() && c.isAttachedToEntity()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached
|
||||
c.unattachFromEntity(c.getEntityAttachedTo());
|
||||
@@ -999,24 +1001,19 @@ public class GameAction {
|
||||
orderedNoRegCreats = true;
|
||||
}
|
||||
for (Card c : noRegCreats) {
|
||||
sacrificeDestroy(c, null);
|
||||
sacrificeDestroy(c, null, table);
|
||||
}
|
||||
}
|
||||
if (desCreats != null) {
|
||||
if (desCreats.size() > 1 && !orderedDesCreats) {
|
||||
desCreats = CardLists.filter(desCreats, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
return card.canBeDestroyed();
|
||||
}
|
||||
});
|
||||
desCreats = CardLists.filter(desCreats, CardPredicates.Presets.CAN_BE_DESTROYED);
|
||||
if (!desCreats.isEmpty()) {
|
||||
desCreats = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, desCreats, ZoneType.Graveyard);
|
||||
}
|
||||
orderedDesCreats = true;
|
||||
}
|
||||
for (Card c : desCreats) {
|
||||
destroy(c, null);
|
||||
destroy(c, null, true, table);
|
||||
}
|
||||
}
|
||||
setHoldCheckingStaticAbilities(false);
|
||||
@@ -1026,20 +1023,21 @@ public class GameAction {
|
||||
}
|
||||
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (handleLegendRule(p)) {
|
||||
if (handleLegendRule(p, table)) {
|
||||
checkAgain = true;
|
||||
}
|
||||
|
||||
if (handlePlaneswalkerRule(p)) {
|
||||
if (handlePlaneswalkerRule(p, table)) {
|
||||
checkAgain = true;
|
||||
}
|
||||
}
|
||||
// 704.5m World rule
|
||||
checkAgain |= handleWorldRule();
|
||||
checkAgain |= handleWorldRule(table);
|
||||
|
||||
if (game.getCombat() != null) {
|
||||
game.getCombat().removeAbsentCombatants();
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
if (!checkAgain) {
|
||||
break; // do not continue the loop
|
||||
}
|
||||
@@ -1072,7 +1070,7 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean stateBasedAction_Saga(Card c) {
|
||||
private boolean stateBasedAction_Saga(Card c, CardZoneTable table) {
|
||||
boolean checkAgain = false;
|
||||
if (!c.getType().hasSubtype("Saga")) {
|
||||
return false;
|
||||
@@ -1085,13 +1083,13 @@ public class GameAction {
|
||||
}
|
||||
if (!game.getStack().hasSimultaneousStackEntries() &&
|
||||
!game.getStack().hasSourceOnStack(c, SpellAbilityPredicates.isChapter())) {
|
||||
sacrifice(c, null);
|
||||
sacrifice(c, null, table);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
}
|
||||
|
||||
private boolean stateBasedAction704_attach(Card c) {
|
||||
private boolean stateBasedAction704_attach(Card c, CardZoneTable table) {
|
||||
boolean checkAgain = false;
|
||||
|
||||
if (c.isAttachedToEntity()) {
|
||||
@@ -1113,7 +1111,7 @@ public class GameAction {
|
||||
|
||||
// cleanup aura
|
||||
if (c.isAura() && c.isInPlay() && !c.isEnchanting()) {
|
||||
moveToGraveyard(c, null, null);
|
||||
sacrificeDestroy(c, null, table);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
@@ -1243,7 +1241,7 @@ public class GameAction {
|
||||
game.getStack().clearSimultaneousStack();
|
||||
}
|
||||
|
||||
private boolean handlePlaneswalkerRule(Player p) {
|
||||
private boolean handlePlaneswalkerRule(Player p, CardZoneTable table) {
|
||||
// get all Planeswalkers
|
||||
final List<Card> list = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANESWALKERS);
|
||||
boolean recheck = false;
|
||||
@@ -1252,7 +1250,7 @@ public class GameAction {
|
||||
|
||||
for (Card c : list) {
|
||||
if (c.getCounters(CounterType.LOYALTY) <= 0) {
|
||||
moveToGraveyard(c, null, null);
|
||||
sacrificeDestroy(c, null, table);
|
||||
// Play the Destroy sound
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
recheck = true;
|
||||
@@ -1286,7 +1284,7 @@ public class GameAction {
|
||||
return recheck;
|
||||
}
|
||||
|
||||
private boolean handleLegendRule(Player p) {
|
||||
private boolean handleLegendRule(Player p, CardZoneTable table) {
|
||||
final List<Card> a = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Legendary");
|
||||
if (a.isEmpty() || game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) {
|
||||
return false;
|
||||
@@ -1314,7 +1312,7 @@ public class GameAction {
|
||||
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)");
|
||||
for (Card c: cc) {
|
||||
if (c != toKeep) {
|
||||
sacrificeDestroy(c, null);
|
||||
sacrificeDestroy(c, null, table);
|
||||
}
|
||||
}
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
@@ -1323,7 +1321,7 @@ public class GameAction {
|
||||
return recheck;
|
||||
}
|
||||
|
||||
private boolean handleWorldRule() {
|
||||
private boolean handleWorldRule(CardZoneTable table) {
|
||||
final List<Card> worlds = CardLists.getType(game.getCardsIn(ZoneType.Battlefield), "World");
|
||||
if (worlds.size() <= 1) {
|
||||
return false;
|
||||
@@ -1348,35 +1346,28 @@ public class GameAction {
|
||||
}
|
||||
|
||||
for (Card c : worlds) {
|
||||
sacrificeDestroy(c, null);
|
||||
sacrificeDestroy(c, null, table);
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final Card sacrifice(final Card c, final SpellAbility source) {
|
||||
return sacrifice(c, source, null);
|
||||
}
|
||||
public final Card sacrifice(final Card c, final SpellAbility source, CardZoneTable table) {
|
||||
if (!c.canBeSacrificedBy(source)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
c.getController().addSacrificedThisTurn(c, source);
|
||||
|
||||
return sacrificeDestroy(c, source);
|
||||
return sacrificeDestroy(c, source, table);
|
||||
}
|
||||
|
||||
public final boolean destroy(final Card c, final SpellAbility sa) {
|
||||
if (!c.canBeDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return destroy(c, sa, true);
|
||||
}
|
||||
public final boolean destroyNoRegeneration(final Card c, final SpellAbility sa) {
|
||||
return destroy(c, sa, false);
|
||||
}
|
||||
|
||||
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate) {
|
||||
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, CardZoneTable table) {
|
||||
Player activator = null;
|
||||
if (!c.canBeDestroyed()) {
|
||||
return false;
|
||||
@@ -1408,7 +1399,7 @@ public class GameAction {
|
||||
runParams.put("Causer", activator);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Destroyed, runParams, false);
|
||||
|
||||
final Card sacrificed = sacrificeDestroy(c, sa);
|
||||
final Card sacrificed = sacrificeDestroy(c, sa, table);
|
||||
return sacrificed != null;
|
||||
}
|
||||
|
||||
@@ -1416,12 +1407,15 @@ public class GameAction {
|
||||
* @return the sacrificed Card in its new location, or {@code null} if the
|
||||
* sacrifice wasn't successful.
|
||||
*/
|
||||
public final Card sacrificeDestroy(final Card c, SpellAbility cause) {
|
||||
protected final Card sacrificeDestroy(final Card c, SpellAbility cause, CardZoneTable table) {
|
||||
if (!c.isInPlay()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Card newCard = moveToGraveyard(c, cause, null);
|
||||
if (table != null) {
|
||||
table.put(ZoneType.Battlefield, newCard.getZone().getZoneType(), newCard);
|
||||
}
|
||||
|
||||
return newCard;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ActivateAbilityEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
SpellAbility manaAb = p.getController().chooseSingleSpellForEffect(
|
||||
possibleAb, sa, "Choose a mana ability:", ImmutableMap.of());
|
||||
possibleAb, sa, "Choose a mana ability:", ImmutableMap.<String, Object>of());
|
||||
p.getController().playChosenSpellAbility(manaAb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -41,6 +42,7 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
min = Math.min(min, validCards.get(i).size());
|
||||
}
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for(int i = 0; i < players.size(); i++) {
|
||||
Player p = players.get(i);
|
||||
int numToBalance = validCards.get(i).size() - min;
|
||||
@@ -50,15 +52,16 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
if (zone.equals(ZoneType.Hand)) {
|
||||
for (Card card : p.getController().chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance)) {
|
||||
if ( null == card ) continue;
|
||||
p.discard(card, sa);
|
||||
p.discard(card, sa, table);
|
||||
}
|
||||
} else { // Battlefield
|
||||
// TODO: "can'e be sacrificed"
|
||||
for(Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
||||
if ( null == card ) continue;
|
||||
game.getAction().sacrifice(card, sa);
|
||||
game.getAction().sacrifice(card, sa, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
// movedCards should have same timestamp
|
||||
long ts = game.getNextTimestamp();
|
||||
final Map<ZoneType, CardCollection> triggerList = Maps.newEnumMap(ZoneType.class);
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
for (final Card c : cards) {
|
||||
final Zone originZone = game.getZoneOf(c);
|
||||
|
||||
@@ -209,21 +209,13 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (!movedCard.getZone().equals(originZone)) {
|
||||
if (!triggerList.containsKey(originZone.getZoneType())) {
|
||||
triggerList.put(originZone.getZoneType(), new CardCollection());
|
||||
}
|
||||
triggerList.get(originZone.getZoneType()).add(movedCard);
|
||||
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
|
||||
}
|
||||
}
|
||||
|
||||
game.getTriggerHandler().resetActiveTriggers(false);
|
||||
|
||||
if (!triggerList.isEmpty()) {
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Cards", triggerList);
|
||||
runParams.put("Destination", destination);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
}
|
||||
triggerList.triggerChangesZoneAll(game);
|
||||
|
||||
// if Shuffle parameter exists, and any amount of cards were owned by
|
||||
// that player, then shuffle that library
|
||||
|
||||
@@ -429,7 +429,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean optional = sa.hasParam("Optional");
|
||||
final long ts = game.getNextTimestamp();
|
||||
final Map<ZoneType, CardCollection> triggerList = Maps.newEnumMap(ZoneType.class);
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
|
||||
for (final Card tgtC : tgtCards) {
|
||||
if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) {
|
||||
@@ -602,10 +602,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
if (!movedCard.getZone().equals(originZone)) {
|
||||
if (!triggerList.containsKey(originZone.getZoneType())) {
|
||||
triggerList.put(originZone.getZoneType(), new CardCollection());
|
||||
}
|
||||
triggerList.get(originZone.getZoneType()).add(movedCard);
|
||||
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
|
||||
|
||||
if (remember != null) {
|
||||
hostCard.addRemembered(movedCard);
|
||||
@@ -619,12 +616,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (!triggerList.isEmpty()) {
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Cards", triggerList);
|
||||
runParams.put("Destination", destination);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
}
|
||||
triggerList.triggerChangesZoneAll(game);
|
||||
|
||||
// for things like Gaea's Blessing
|
||||
if (destination.equals(ZoneType.Library) && sa.hasParam("Shuffle") && "True".equals(sa.getParam("Shuffle"))) {
|
||||
@@ -951,8 +943,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
CardCollection movedCards = new CardCollection();
|
||||
long ts = game.getNextTimestamp();
|
||||
final Map<ZoneType, CardCollection> triggerList = Maps.newEnumMap(ZoneType.class);
|
||||
for (Card c : chosenCards) {
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
for (final Card c : chosenCards) {
|
||||
Card movedCard = null;
|
||||
final Zone originZone = game.getZoneOf(c);
|
||||
if (destination.equals(ZoneType.Library)) {
|
||||
@@ -1117,10 +1109,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
movedCards.add(movedCard);
|
||||
|
||||
if (originZone != null) {
|
||||
if (!triggerList.containsKey(originZone.getZoneType())) {
|
||||
triggerList.put(originZone.getZoneType(), new CardCollection());
|
||||
}
|
||||
triggerList.get(originZone.getZoneType()).add(movedCard);
|
||||
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
|
||||
}
|
||||
|
||||
if (champion) {
|
||||
@@ -1152,13 +1141,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
player.shuffle(sa);
|
||||
}
|
||||
|
||||
if (!triggerList.isEmpty()) {
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Cards", triggerList);
|
||||
runParams.put("Destination", destination);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
}
|
||||
|
||||
triggerList.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
private static boolean allowMultiSelect(Player decider, SpellAbility sa) {
|
||||
@@ -1231,24 +1214,4 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkCanIndirectlyAttachTo(final Card source, final Card target) {
|
||||
final SpellAbility attachEff = source.getFirstAttachSpell();
|
||||
|
||||
if (attachEff == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Game game = source.getGame();
|
||||
final TargetRestrictions tgt = attachEff.getTargetRestrictions();
|
||||
|
||||
Player attachEffCtrl = attachEff.getActivatingPlayer();
|
||||
if (attachEffCtrl == null && attachEff.getHostCard() != null) {
|
||||
attachEffCtrl = attachEff.getHostCard().getController();
|
||||
}
|
||||
|
||||
CardCollectionView list = game.getCardsIn(tgt.getZone());
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), attachEffCtrl, source, attachEff);
|
||||
return list.contains(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,12 +206,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
final Ability ability = new Ability(hostCard, ManaCost.ZERO) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
|
||||
if (bNoRegen) {
|
||||
game.getAction().destroyNoRegeneration(c, null);
|
||||
} else {
|
||||
game.getAction().destroy(c, null);
|
||||
}
|
||||
game.getAction().destroy(c, null, !bNoRegen, null);
|
||||
}
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -119,7 +119,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
|
||||
// Destroy Permanent may be able to be turned into a SubAbility
|
||||
if (tgtSA.isAbility() && sa.hasParam("DestroyPermanent")) {
|
||||
game.getAction().destroy(tgtSACard, sa);
|
||||
game.getAction().destroy(tgtSACard, sa, true, null);
|
||||
}
|
||||
|
||||
if (sa.hasParam("RememberCountered")) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -8,7 +7,9 @@ import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -78,30 +79,20 @@ public class DestroyAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// exclude cards that can't be destroyed at this moment
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
return card.canBeDestroyed();
|
||||
}
|
||||
});
|
||||
list = CardLists.filter(list, CardPredicates.Presets.CAN_BE_DESTROYED);
|
||||
|
||||
if (list.size() > 1) {
|
||||
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard);
|
||||
}
|
||||
|
||||
if (noRegen) {
|
||||
for (Card c : list) {
|
||||
if (game.getAction().destroyNoRegeneration(c, sa) && remDestroyed) {
|
||||
card.addRemembered(CardUtil.getLKICopy(c));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Card c : list) {
|
||||
if (game.getAction().destroy(c, sa) && remDestroyed) {
|
||||
card.addRemembered(CardUtil.getLKICopy(c));
|
||||
}
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
|
||||
for (Card c : list) {
|
||||
if (game.getAction().destroy(c, sa, !noRegen, table) && remDestroyed) {
|
||||
card.addRemembered(CardUtil.getLKICopy(c));
|
||||
}
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -77,8 +77,6 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
CardCollection tgtCards = getTargetCards(sa);
|
||||
CardCollection untargetedCards = new CardCollection();
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
|
||||
if (sa.hasParam("Radiance")) {
|
||||
for (final Card c : CardUtil.getRadiance(card, tgtCards.get(0),
|
||||
sa.getParam("ValidTgts").split(","))) {
|
||||
@@ -90,19 +88,18 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
tgtCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard);
|
||||
}
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for (final Card tgtC : tgtCards) {
|
||||
if (tgtC.isInPlay() && ((tgt == null) || tgtC.canBeTargetedBy(sa))) {
|
||||
if (tgtC.isInPlay() && (!sa.usesTargeting() || tgtC.canBeTargetedBy(sa))) {
|
||||
boolean destroyed = false;
|
||||
final Card lki = CardUtil.getLKICopy(tgtC);
|
||||
if (remAttached) {
|
||||
card.addRemembered(tgtC.getAttachedCards());
|
||||
}
|
||||
if (sac) {
|
||||
destroyed = game.getAction().sacrifice(tgtC, sa) != null;
|
||||
} else if (noRegen) {
|
||||
destroyed = game.getAction().destroyNoRegeneration(tgtC, sa);
|
||||
destroyed = game.getAction().sacrifice(tgtC, sa, table) != null;
|
||||
} else {
|
||||
destroyed = game.getAction().destroy(tgtC, sa);
|
||||
destroyed = game.getAction().destroy(tgtC, sa, !noRegen, table);
|
||||
}
|
||||
if (destroyed && remDestroyed) {
|
||||
card.addRemembered(tgtC);
|
||||
@@ -121,16 +118,16 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
if (unTgtC.isInPlay()) {
|
||||
boolean destroyed = false;
|
||||
if (sac) {
|
||||
destroyed = game.getAction().sacrifice(unTgtC, sa) != null;
|
||||
} else if (noRegen) {
|
||||
destroyed = game.getAction().destroyNoRegeneration(unTgtC, sa);
|
||||
destroyed = game.getAction().sacrifice(unTgtC, sa, table) != null;
|
||||
} else {
|
||||
destroyed = game.getAction().destroy(unTgtC, sa);
|
||||
destroyed = game.getAction().destroy(unTgtC, sa, !noRegen, table);
|
||||
} if (destroyed && remDestroyed) {
|
||||
card.addRemembered(unTgtC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.player.DelayedReveal;
|
||||
import forge.game.player.Player;
|
||||
@@ -109,6 +110,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for (final Player p : tgtPlayers) {
|
||||
if (tgt != null && !p.canBeTargetedBy(sa)) {
|
||||
continue;
|
||||
@@ -301,6 +303,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
effectHost = sa.getHostCard();
|
||||
}
|
||||
for (Card c : movedCards) {
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
final PlayerZone zone = c.getOwner().getZone(destZone1);
|
||||
|
||||
if (zone.is(ZoneType.Library) || zone.is(ZoneType.PlanarDeck) || zone.is(ZoneType.SchemeDeck)) {
|
||||
@@ -322,6 +325,9 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
c.setExiledWith(effectHost);
|
||||
}
|
||||
}
|
||||
if (!origin.equals(c.getZone().getZoneType())) {
|
||||
table.put(origin, c.getZone().getZoneType(), c);
|
||||
}
|
||||
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
c.setState(CardStateName.FaceDown, true);
|
||||
@@ -357,11 +363,16 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
Collections.reverse(afterOrder);
|
||||
}
|
||||
for (final Card c : afterOrder) {
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
Card m;
|
||||
if (destZone2 == ZoneType.Library) {
|
||||
game.getAction().moveToLibrary(c, libraryPosition2, sa);
|
||||
m = game.getAction().moveToLibrary(c, libraryPosition2, sa);
|
||||
}
|
||||
else {
|
||||
game.getAction().moveToVariantDeck(c, destZone2, libraryPosition2, sa);
|
||||
m = game.getAction().moveToVariantDeck(c, destZone2, libraryPosition2, sa);
|
||||
}
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
table.put(origin, m.getZone().getZoneType(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,8 +380,12 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
// just move them randomly
|
||||
for (int i = 0; i < rest.size(); i++) {
|
||||
Card c = rest.get(i);
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
final PlayerZone toZone = c.getOwner().getZone(destZone2);
|
||||
c = game.getAction().moveTo(toZone, c, sa);
|
||||
if (!origin.equals(c.getZone().getZoneType())) {
|
||||
table.put(origin, c.getZone().getZoneType(), c);
|
||||
}
|
||||
if (destZone2 == ZoneType.Battlefield && !keywords.isEmpty()) {
|
||||
for (final String kw : keywords) {
|
||||
c.addExtrinsicKeyword(kw);
|
||||
@@ -386,6 +401,8 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
}
|
||||
//table trigger there
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
// TODO This should be somewhere else, maybe like CardUtil or something like that
|
||||
|
||||
@@ -5,9 +5,9 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.MyRandom;
|
||||
@@ -76,6 +76,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
|
||||
String[] type = new String[]{"Card"};
|
||||
if (sa.hasParam("Valid")) {
|
||||
@@ -94,8 +95,6 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean remember = sa.hasParam("RememberFound");
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
|
||||
final ZoneType foundDest = ZoneType.smartValueOf(sa.getParam("FoundDestination"));
|
||||
final int foundLibPos = AbilityUtils.calculateAmount(host, sa.getParam("FoundLibraryPosition"), sa);
|
||||
final ZoneType revealedDest = ZoneType.smartValueOf(sa.getParam("RevealedDestination"));
|
||||
@@ -107,11 +106,13 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
final boolean optional = sa.hasParam("Optional");
|
||||
final boolean optionalFound = sa.hasParam("OptionalFoundMove");
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
|
||||
for (final Player p : getTargetPlayers(sa)) {
|
||||
if (p == null) {
|
||||
continue;
|
||||
}
|
||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||
if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
||||
if (optional && !p.getController().confirmAction(sa, null, "Do you want to dig your library?")) {
|
||||
continue;
|
||||
}
|
||||
@@ -142,7 +143,6 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
final Game game = p.getGame();
|
||||
if (revealed.size() > 0) {
|
||||
game.getAction().reveal(revealed, p, false);
|
||||
}
|
||||
@@ -157,19 +157,24 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
final Iterator<Card> itr = found.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Card c = itr.next();
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
if (optionalFound && !p.getController().confirmAction(sa, null,
|
||||
"Do you want to put that card to " + foundDest.name() + "?")) {
|
||||
continue;
|
||||
} else {
|
||||
Card m = null;
|
||||
if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
|
||||
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
|
||||
game.getAction().moveTo(c.getController().getZone(foundDest), c, sa);
|
||||
m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa);
|
||||
} else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
|
||||
//Don't do anything
|
||||
} else {
|
||||
game.getAction().moveTo(foundDest, c, foundLibPos, sa);
|
||||
m = game.getAction().moveTo(foundDest, c, foundLibPos, sa);
|
||||
}
|
||||
revealed.remove(c);
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
table.put(origin, m.getZone().getZoneType(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,7 +206,11 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
final Iterator<Card> itr = revealed.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Card c = itr.next();
|
||||
game.getAction().moveTo(noneFoundDest, c, noneFoundLibPos, sa);
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
final Card m = game.getAction().moveTo(noneFoundDest, c, noneFoundLibPos, sa);
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
table.put(origin, m.getZone().getZoneType(), m);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Allow ordering the rest of the revealed cards
|
||||
@@ -216,7 +225,11 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
final Iterator<Card> itr = revealed.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Card c = itr.next();
|
||||
game.getAction().moveTo(revealedDest, c, revealedLibPos, sa);
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
final Card m = game.getAction().moveTo(revealedDest, c, revealedLibPos, sa);
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
table.put(origin, m.getZone().getZoneType(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +238,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
}
|
||||
} // end foreach player
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
} // end resolve
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
@@ -10,12 +8,17 @@ import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import forge.util.Lang;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,10 +32,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
|
||||
if (!tgtPlayers.isEmpty()) {
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
sb.append(p.toString()).append(" ");
|
||||
}
|
||||
sb.append(Lang.joinHomogenous(tgtPlayers)).append(" ");
|
||||
|
||||
if (mode.equals("RevealYouChoose")) {
|
||||
sb.append("reveals their hand.").append(" You choose (");
|
||||
@@ -105,8 +105,6 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
final String mode = sa.getParam("Mode");
|
||||
//final boolean anyNumber = sa.hasParam("AnyNumber");
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
|
||||
final List<Card> discarded = new ArrayList<Card>();
|
||||
final List<Player> targets = getTargetPlayers(sa),
|
||||
discarders;
|
||||
@@ -115,7 +113,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
// In this case the target need not be the discarding player
|
||||
discarders = getDefinedPlayersOrTargeted(sa);
|
||||
firstTarget = Iterables.getFirst(targets, null);
|
||||
if (tgt != null && !firstTarget.canBeTargetedBy(sa)) {
|
||||
if (sa.usesTargeting() && !firstTarget.canBeTargetedBy(sa)) {
|
||||
firstTarget = null;
|
||||
}
|
||||
} else {
|
||||
@@ -123,8 +121,9 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
|
||||
final CardZoneTable table = new CardZoneTable();
|
||||
for (final Player p : discarders) {
|
||||
if ((mode.equals("RevealTgtChoose") && firstTarget != null) || tgt == null || p.canBeTargetedBy(sa)) {
|
||||
if ((mode.equals("RevealTgtChoose") && firstTarget != null) || !sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
||||
if (sa.hasParam("RememberDiscarder")) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
@@ -140,7 +139,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
for (final Card c : toDiscard) {
|
||||
boolean hasDiscarded = p.discard(c, sa) != null;
|
||||
boolean hasDiscarded = p.discard(c, sa, table) != null;
|
||||
if (hasDiscarded) {
|
||||
discarded.add(c);
|
||||
}
|
||||
@@ -164,7 +163,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
for(Card c : toDiscard) { // without copying will get concurrent modification exception
|
||||
boolean hasDiscarded = p.discard(c, sa) != null;
|
||||
boolean hasDiscarded = p.discard(c, sa, table) != null;
|
||||
if( hasDiscarded && shouldRemember )
|
||||
source.addRemembered(c);
|
||||
}
|
||||
@@ -178,7 +177,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
for (final Card c : dPHand) {
|
||||
p.discard(c, sa);
|
||||
p.discard(c, sa, table);
|
||||
discarded.add(c);
|
||||
}
|
||||
}
|
||||
@@ -216,7 +215,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
for (Card c : toDiscardView) {
|
||||
if (p.discard(c, sa) != null) {
|
||||
if (p.discard(c, sa, table) != null) {
|
||||
discarded.add(c);
|
||||
}
|
||||
}
|
||||
@@ -233,7 +232,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
for (Card c : toDiscard) {
|
||||
c.getController().discard(c, sa);
|
||||
c.getController().discard(c, sa, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +259,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
|
||||
// Reveal cards that will be discarded?
|
||||
for (final Card c : dPChHand) {
|
||||
p.discard(c, sa);
|
||||
p.discard(c, sa, table);
|
||||
discarded.add(c);
|
||||
}
|
||||
} else if (mode.equals("RevealYouChoose") || mode.equals("RevealTgtChoose") || mode.equals("TgtChoose")) {
|
||||
@@ -304,7 +303,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
for (Card card : toBeDiscarded) {
|
||||
if (card == null) { continue; }
|
||||
p.discard(card, sa);
|
||||
p.discard(card, sa, table);
|
||||
discarded.add(card);
|
||||
}
|
||||
}
|
||||
@@ -317,5 +316,8 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
source.addRemembered(c);
|
||||
}
|
||||
}
|
||||
|
||||
// run trigger if something got milled
|
||||
table.triggerChangesZoneAll(source.getGame());
|
||||
} // discardResolve()
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameLogEntryType;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.List;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
public class MillEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
final int numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
|
||||
final boolean bottom = sa.hasParam("FromBottom");
|
||||
final boolean facedown = sa.hasParam("ExileFaceDown");
|
||||
@@ -28,27 +30,27 @@ public class MillEffect extends SpellAbilityEffect {
|
||||
source.clearRemembered();
|
||||
}
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
|
||||
ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||
if (destination == null) {
|
||||
destination = ZoneType.Graveyard;
|
||||
}
|
||||
|
||||
final CardZoneTable table = new CardZoneTable();
|
||||
|
||||
for (final Player p : getTargetPlayers(sa)) {
|
||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||
if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
||||
if (sa.hasParam("Optional")) {
|
||||
final String prompt = TextUtil.concatWithSpace("Do you want to put card(s) from library to", TextUtil.addSuffix(destination.toString(),"?"));
|
||||
if (!p.getController().confirmAction(sa, null, prompt)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
final CardCollectionView milled = p.mill(numCards, destination, bottom);
|
||||
final CardCollectionView milled = p.mill(numCards, destination, bottom, sa, table);
|
||||
// Reveal the milled cards, so players don't have to manually inspect the
|
||||
// graveyard to figure out which ones were milled.
|
||||
if (!facedown && reveal) { // do not reveal when exiling face down
|
||||
if (showRevealDialog) {
|
||||
p.getGame().getAction().reveal(milled, p, false);
|
||||
game.getAction().reveal(milled, p, false);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(p).append(" milled ").append(milled).append(" to ").append(destination);
|
||||
@@ -78,6 +80,9 @@ public class MillEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run trigger if something got milled
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,10 +90,7 @@ public class MillEffect extends SpellAbilityEffect {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final int numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
|
||||
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
for (final Player p : tgtPlayers) {
|
||||
sb.append(p.toString()).append(" ");
|
||||
}
|
||||
sb.append(Lang.joinHomogenous(getTargetPlayers(sa))).append(" ");
|
||||
|
||||
final ZoneType dest = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||
if ((dest == null) || dest.equals(ZoneType.Graveyard)) {
|
||||
|
||||
@@ -63,12 +63,14 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
|
||||
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard);
|
||||
}
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for (Card sac : list) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac);
|
||||
if (game.getAction().sacrifice(sac, sa) != null && remSacrificed) {
|
||||
if (game.getAction().sacrifice(sac, sa, table) != null && remSacrificed) {
|
||||
card.addRemembered(lKICopy);
|
||||
}
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,14 +91,15 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
final boolean remSacrificed = sa.hasParam("RememberSacrificed");
|
||||
final String remSVar = sa.getParam("RememberSacrificedSVar");
|
||||
int countSacrificed = 0;
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
|
||||
if (valid.equals("Self") && game.getZoneOf(card) != null) {
|
||||
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
|
||||
if (game.getAction().sacrifice(card, sa) != null) {
|
||||
countSacrificed++;
|
||||
if (remSacrificed) {
|
||||
card.addRemembered(card);
|
||||
}
|
||||
if (game.getAction().sacrifice(card, sa, table) != null) {
|
||||
countSacrificed++;
|
||||
if (remSacrificed) {
|
||||
card.addRemembered(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,8 +136,8 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
|
||||
for (Card sac : choosenToSacrifice) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac);
|
||||
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa) != null;
|
||||
boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa);
|
||||
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, table) != null;
|
||||
boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table);
|
||||
// Run Devour Trigger
|
||||
if (devour) {
|
||||
card.addDevoured(lKICopy);
|
||||
@@ -168,6 +169,8 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
} while (root != null);
|
||||
}
|
||||
}
|
||||
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -219,6 +219,7 @@ public final class CardUtil {
|
||||
newCopy.setSetCode(in.getSetCode());
|
||||
newCopy.setOwner(in.getOwner());
|
||||
newCopy.setController(in.getController(), 0);
|
||||
newCopy.setCommander(in.isCommander());
|
||||
|
||||
// needed to ensure that the LKI object has correct CMC info no matter what state the original card was in
|
||||
// (e.g. Scrap Trawler + transformed Harvest Hand)
|
||||
|
||||
48
forge-game/src/main/java/forge/game/card/CardZoneTable.java
Normal file
48
forge-game/src/main/java/forge/game/card/CardZoneTable.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package forge.game.card;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ForwardingTable;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardCollection> {
|
||||
// TODO use EnumBasedTable if exist
|
||||
private Table<ZoneType, ZoneType, CardCollection> dataMap = HashBasedTable.create();
|
||||
|
||||
/**
|
||||
* special put logic, add Card to Card Collection
|
||||
*/
|
||||
public CardCollection put(ZoneType rowKey, ZoneType columnKey, Card value) {
|
||||
CardCollection old;
|
||||
if (contains(rowKey, columnKey)) {
|
||||
old = get(rowKey, columnKey);
|
||||
old.add(value);
|
||||
} else {
|
||||
old = new CardCollection(value);
|
||||
dataMap.put(rowKey, columnKey, old);
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Table<ZoneType, ZoneType, CardCollection> delegate() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
public void triggerChangesZoneAll(final Game game) {
|
||||
if (!isEmpty()) {
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Cards", this);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package forge.game.cost;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.card.mana.ManaCost;
|
||||
@@ -19,14 +17,15 @@ import forge.game.spellability.Spell;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CostAdjustment {
|
||||
|
||||
public static Cost adjust(final Cost cost, final SpellAbility sa) {
|
||||
@@ -214,7 +213,7 @@ public class CostAdjustment {
|
||||
if (sa.getHostCard().hasKeyword(Keyword.DELVE)) {
|
||||
sa.getHostCard().clearDelved();
|
||||
|
||||
final CardCollection delved = new CardCollection();
|
||||
final CardZoneTable table = new CardZoneTable();
|
||||
final Player pc = sa.getActivatingPlayer();
|
||||
final CardCollection mutableGrave = new CardCollection(pc.getCardsIn(ZoneType.Graveyard));
|
||||
final CardCollectionView toExile = pc.getController().chooseCardsToDelve(cost.getUnpaidShards(ManaCostShard.GENERIC), mutableGrave);
|
||||
@@ -224,17 +223,11 @@ public class CostAdjustment {
|
||||
cardsToDelveOut.add(c);
|
||||
} else if (!test) {
|
||||
sa.getHostCard().addDelved(c);
|
||||
delved.add(game.getAction().exile(c, null, null));
|
||||
final Card d = game.getAction().exile(c, null, null);
|
||||
table.put(ZoneType.Graveyard, d.getZone().getZoneType(), d);
|
||||
}
|
||||
}
|
||||
if (!delved.isEmpty()) {
|
||||
final Map<ZoneType, CardCollection> triggerList = Maps.newEnumMap(ZoneType.class);
|
||||
triggerList.put(ZoneType.Graveyard, delved);
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Cards", triggerList);
|
||||
runParams.put("Destination", ZoneType.Exile);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) {
|
||||
adjustCostByConvokeOrImprovise(cost, sa, false, test);
|
||||
|
||||
@@ -164,7 +164,7 @@ public class CostDiscard extends CostPartWithList {
|
||||
*/
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
return targetCard.getController().discard(targetCard, ability);
|
||||
return targetCard.getController().discard(targetCard, ability, null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -21,8 +21,10 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.Zone;
|
||||
|
||||
/**
|
||||
* The Class CostPartWithList.
|
||||
@@ -35,6 +37,8 @@ public abstract class CostPartWithList extends CostPart {
|
||||
/** The lists: one for LKI, one for the actual cards. */
|
||||
private final CardCollection lkiList = new CardCollection();
|
||||
protected final CardCollection cardList = new CardCollection();
|
||||
|
||||
protected final CardZoneTable table = new CardZoneTable();
|
||||
// set is here because executePayment() adds card to list, while ai's decide payment does the same thing.
|
||||
// set allows to avoid duplication
|
||||
|
||||
@@ -52,6 +56,7 @@ public abstract class CostPartWithList extends CostPart {
|
||||
public final void resetLists() {
|
||||
lkiList.clear();
|
||||
cardList.clear();
|
||||
table.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,13 +102,21 @@ public abstract class CostPartWithList extends CostPart {
|
||||
|
||||
public final boolean executePayment(SpellAbility ability, Card targetCard) {
|
||||
lkiList.add(CardUtil.getLKICopy(targetCard));
|
||||
final Zone origin = targetCard.getZone();
|
||||
final Card newCard = doPayment(ability, targetCard);
|
||||
cardList.add(newCard);
|
||||
|
||||
// need to update the LKI info to ensure correct interaction with cards which may trigger on this
|
||||
// (e.g. Necroskitter + a creature dying from a -1/-1 counter on a cost payment).
|
||||
targetCard.getGame().updateLastStateForCard(targetCard);
|
||||
|
||||
if (newCard != null) {
|
||||
final Zone newZone = newCard.getZone();
|
||||
cardList.add(newCard);
|
||||
|
||||
if (!origin.equals(newZone)) {
|
||||
table.put(origin.getZoneType(), newZone.getZoneType(), newCard);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,11 +125,13 @@ public abstract class CostPartWithList extends CostPart {
|
||||
if (canPayListAtOnce()) { // This is used by reveal. Without it when opponent would reveal hand, you'll get N message boxes.
|
||||
lkiList.addAll(targetCards);
|
||||
cardList.addAll(doListPayment(ability, targetCards));
|
||||
handleChangeZoneTrigger(ability);
|
||||
return true;
|
||||
}
|
||||
for (Card c: targetCards) {
|
||||
executePayment(ability, c);
|
||||
}
|
||||
handleChangeZoneTrigger(ability);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,4 +160,15 @@ public abstract class CostPartWithList extends CostPart {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void handleChangeZoneTrigger(SpellAbility ability) {
|
||||
if (table.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy table because the original get cleaned after the cost is done
|
||||
final CardZoneTable copyTable = new CardZoneTable();
|
||||
copyTable.putAll(table);
|
||||
copyTable.triggerChangesZoneAll(ability.getHostCard().getGame());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,7 +116,8 @@ public class CostSacrifice extends CostPartWithList {
|
||||
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
return targetCard.getGame().getAction().sacrifice(targetCard, ability);
|
||||
// no table there, it is already handled by CostPartWithList
|
||||
return targetCard.getGame().getAction().sacrifice(targetCard, ability, null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -29,6 +29,7 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
@@ -38,9 +39,11 @@ import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||
import forge.game.player.PlayerController.ManaPaymentPurpose;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.LandAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.CollectionSuppliers;
|
||||
import forge.util.TextUtil;
|
||||
@@ -359,9 +362,11 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
int numDiscard = playerTurn.isUnlimitedHandSize() || handSize <= max || handSize == 0 ? 0 : handSize - max;
|
||||
|
||||
if (numDiscard > 0) {
|
||||
final CardZoneTable table = new CardZoneTable();
|
||||
for (Card c : playerTurn.getController().chooseCardsToDiscardToMaximumHandSize(numDiscard)){
|
||||
playerTurn.discard(c, null);
|
||||
playerTurn.discard(c, null, table);
|
||||
}
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
// Rule 514.2
|
||||
@@ -983,7 +988,23 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
pFirstPriority = pPlayerPriority; // all opponents have to pass before stack is allowed to resolve
|
||||
for (SpellAbility sa : chosenSa) {
|
||||
Card saHost = sa.getHostCard();
|
||||
final Zone originZone = saHost.getZone();
|
||||
|
||||
// TODO it has no return value if successful
|
||||
pPlayerPriority.getController().playChosenSpellAbility(sa);
|
||||
|
||||
saHost = game.getCardState(saHost);
|
||||
final Zone currentZone = saHost.getZone();
|
||||
|
||||
// Need to check if Zone did change
|
||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa instanceof LandAbility)) {
|
||||
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
||||
triggerList.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
}
|
||||
loopCount++;
|
||||
} while (loopCount < 999 || !pPlayerPriority.getController().isAI());
|
||||
|
||||
@@ -1581,12 +1581,13 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return numDrawnThisDrawStep;
|
||||
}
|
||||
|
||||
public final Card discard(final Card c, final SpellAbility sa) {
|
||||
public final Card discard(final Card c, final SpellAbility sa, CardZoneTable table) {
|
||||
// TODO: This line should be moved inside CostPayment somehow
|
||||
/*if (sa != null) {
|
||||
sa.addCostToHashList(c, "Discarded");
|
||||
}*/
|
||||
final Card source = sa != null ? sa.getHostCard() : null;
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
|
||||
boolean discardToTopOfLibrary = null != sa && sa.hasParam("DiscardToTopOfLibrary");
|
||||
boolean discardMadness = sa != null && sa.hasParam("Madness");
|
||||
@@ -1622,6 +1623,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
newCard = game.getAction().moveToGraveyard(c, sa, null);
|
||||
// Play the Discard sound
|
||||
}
|
||||
if (table != null) {
|
||||
table.put(origin, newCard.getZone().getZoneType(), newCard);
|
||||
}
|
||||
sb.append(".");
|
||||
numDiscardedThisTurn++;
|
||||
// Run triggers
|
||||
@@ -1660,18 +1664,13 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
numCardsInHandStartedThisTurnWith = num;
|
||||
}
|
||||
|
||||
public final CardCollectionView mill(final int n) {
|
||||
return mill(n, ZoneType.Graveyard, false);
|
||||
}
|
||||
public final CardCollectionView mill(final int n, final ZoneType zone,
|
||||
final boolean bottom) {
|
||||
public final CardCollectionView mill(final int n, final ZoneType destination,
|
||||
final boolean bottom, SpellAbility sa, CardZoneTable table) {
|
||||
final CardCollectionView lib = getCardsIn(ZoneType.Library);
|
||||
final CardCollection milled = new CardCollection();
|
||||
|
||||
final int max = Math.min(n, lib.size());
|
||||
|
||||
final ZoneType destination = getZone(zone).getZoneType();
|
||||
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (bottom) {
|
||||
milled.add(lib.get(lib.size() - i - 1));
|
||||
@@ -1682,12 +1681,15 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
|
||||
CardCollectionView milledView = milled;
|
||||
|
||||
if (destination == ZoneType.Graveyard && milled.size() > 1) {
|
||||
milledView = GameActionUtil.orderCardsByTheirOwners(game, milled, ZoneType.Graveyard);
|
||||
}
|
||||
|
||||
for (Card m : milledView) {
|
||||
game.getAction().moveTo(destination, m, null, null);
|
||||
final ZoneType origin = m.getZone().getZoneType();
|
||||
final Card d = game.getAction().moveTo(destination, m, sa, null);
|
||||
table.put(origin, d.getZone().getZoneType(), d);
|
||||
}
|
||||
|
||||
return milled;
|
||||
|
||||
@@ -15,44 +15,10 @@ public class TriggerChangesZoneAll extends Trigger {
|
||||
|
||||
@Override
|
||||
public boolean performTest(Map<String, Object> runParams2) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<ZoneType, CardCollection> moved = (Map<ZoneType, CardCollection>) runParams2.get("Cards");
|
||||
final CardZoneTable table = (CardZoneTable) runParams2.get("Cards");
|
||||
|
||||
if (hasParam("Destination")) {
|
||||
if (!getParam("Destination").equals("Any")) {
|
||||
if (!runParams2.get("Destination").equals(ZoneType.valueOf(getParam("Destination")))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final CardCollection allCards = new CardCollection();
|
||||
|
||||
if (hasParam("Origin")) {
|
||||
if (!getParam("Origin").equals("Any")) {
|
||||
if (getParam("Origin") == null) {
|
||||
return false;
|
||||
}
|
||||
final List<ZoneType> origin = ZoneType.listValueOf((String)getParam("Origin"));
|
||||
for (ZoneType z : origin) {
|
||||
if (moved.containsKey(z)) {
|
||||
allCards.addAll(moved.get(z));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (CardCollection c : moved.values()) {
|
||||
allCards.addAll(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("ValidCards")) {
|
||||
|
||||
int count = CardLists.getValidCardCount(allCards, getParam("ValidCards").split(","),this.getHostCard().getController(),
|
||||
this.getHostCard());
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
if (filterCards(table).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -60,30 +26,9 @@ public class TriggerChangesZoneAll extends Trigger {
|
||||
|
||||
@Override
|
||||
public void setTriggeringObjects(SpellAbility sa) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<ZoneType, CardCollection> moved = (Map<ZoneType, CardCollection>) getRunParams().get("Cards");
|
||||
final CardZoneTable table = (CardZoneTable) getRunParams().get("Cards");
|
||||
|
||||
CardCollection allCards = new CardCollection();
|
||||
|
||||
if (hasParam("Origin")) {
|
||||
if (!getParam("Origin").equals("Any") && getParam("Origin") != null) {
|
||||
final List<ZoneType> origin = ZoneType.listValueOf((String)getParam("Origin"));
|
||||
for (ZoneType z : origin) {
|
||||
if (moved.containsKey(z)) {
|
||||
allCards.addAll(moved.get(z));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (CardCollection c : moved.values()) {
|
||||
allCards.addAll(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("ValidCards")) {
|
||||
allCards = CardLists.getValidCards(allCards, getParam("ValidCards").split(","),this.getHostCard().getController(),
|
||||
this.getHostCard(), sa);
|
||||
}
|
||||
CardCollection allCards = this.filterCards(table);
|
||||
|
||||
sa.setTriggeringObject("Cards", allCards);
|
||||
sa.setTriggeringObject("Amount", allCards.size());
|
||||
@@ -95,4 +40,50 @@ public class TriggerChangesZoneAll extends Trigger {
|
||||
sb.append("Amount: ").append(sa.getTriggeringObject("Amount"));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private CardCollection filterCards(CardZoneTable table) {
|
||||
CardCollection allCards = new CardCollection();
|
||||
ZoneType destination = null;
|
||||
|
||||
if (hasParam("Destination")) {
|
||||
if (!getParam("Destination").equals("Any")) {
|
||||
destination = ZoneType.valueOf(getParam("Destination"));
|
||||
if (!table.containsColumn(destination)) {
|
||||
return allCards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("Origin") && !getParam("Origin").equals("Any")) {
|
||||
if (getParam("Origin") == null) {
|
||||
return allCards;
|
||||
}
|
||||
final List<ZoneType> origin = ZoneType.listValueOf(getParam("Origin"));
|
||||
for (ZoneType z : origin) {
|
||||
if (table.containsRow(z)) {
|
||||
if (destination != null) {
|
||||
allCards.addAll(table.row(z).get(destination));
|
||||
} else {
|
||||
for (CardCollection c : table.row(z).values()) {
|
||||
allCards.addAll(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (destination != null) {
|
||||
for (CardCollection c : table.column(destination).values()) {
|
||||
allCards.addAll(c);
|
||||
}
|
||||
} else {
|
||||
for (CardCollection c : table.values()) {
|
||||
allCards.addAll(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("ValidCards")) {
|
||||
allCards = CardLists.getValidCards(allCards, getParam("ValidCards").split(","),
|
||||
getHostCard().getController(), getHostCard(), null);
|
||||
}
|
||||
return allCards;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Myth Unbound
|
||||
ManaCost:2 G
|
||||
Types:Enchantment
|
||||
S:Mode$ ReduceCost | ValidCard$ Card.IsCommander+YouOwn | Type$ Spell | Amount$ AffectedX | Description$ Your commander costs {1} less to cast for each time it's been cast from the command zone this game.
|
||||
T:Mode$ ChangesZoneAll | ValidCard$ Card.IsCommander+YouOwn | Origin$ Any | Destination$ Command | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever your commander is put into the command zone from anywhere, draw a card.
|
||||
T:Mode$ ChangesZone | ValidCard$ Card.IsCommander+YouOwn | Origin$ Any | Destination$ Command | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever your commander is put into the command zone from anywhere, draw a card.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
|
||||
SVar:AffectedX:Count$CommanderCastFromCommandZone
|
||||
AI:RemoveDeck:Random
|
||||
|
||||
@@ -4,8 +4,9 @@ Types:Legendary Creature Naga Shaman
|
||||
PT:3/3
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, put the top three cards of your library into your graveyard.
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigMill | Secondary$ True | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, put the top three cards of your library into your graveyard.
|
||||
T:Mode$ ChangesZone | ValidCard$ Creature.YouCtrl | OncePerEffect$ True | Origin$ Library | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever one or more creature cards are put into your graveyard from your library, create a 2/2 black Zombie creature token.
|
||||
T:Mode$ ChangesZoneAll | ValidCards$ Creature.YouOwn | Origin$ Library | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever one or more creature cards are put into your graveyard from your library, create a 2/2 black Zombie creature token.
|
||||
SVar:TrigMill:DB$Mill | NumCards$ 3 | Defined$ You
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenName$ Zombie | TokenTypes$ Creature,Zombie | TokenOwner$ You | TokenColors$ Black | TokenPower$ 2 | TokenToughness$ 2 | TokenImage$ b 2 2 zombie KTK |
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_2_2_zombie | TokenOwner$ You
|
||||
DeckHas:Ability$Token & Ability$Graveyard
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/sidisi_brood_tyrant.jpg
|
||||
Oracle:Whenever Sidisi, Brood Tyrant enters the battlefield or attacks, put the top three cards of your library into your graveyard.\nWhenever one or more creature cards are put into your graveyard from your library, create a 2/2 black Zombie creature token.
|
||||
Oracle:Whenever Sidisi, Brood Tyrant enters the battlefield or attacks, put the top three cards of your library into your graveyard.\nWhenever one or more creature cards are put into your graveyard from your library, create a 2/2 black Zombie creature token.
|
||||
|
||||
@@ -5,7 +5,8 @@ PT:6/6
|
||||
K:Deathtouch
|
||||
K:UpkeepCost:Sac<1/Land>
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ AdjustLandPlays:1 | Description$ You may play an additional land on each of your turns.
|
||||
T:Mode$ ChangesZone | ValidCard$ Land.YouOwn | OncePerEffect$ True | Origin$ Any | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever one or more land cards are put into your graveyard from anywhere, draw a card.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
|
||||
T:Mode$ ChangesZoneAll | ValidCards$ Land.YouOwn | Origin$ Any | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever one or more land cards are put into your graveyard from anywhere, draw a card.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
|
||||
DeckHas:Ability$Graveyard
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/the_gitrog_monster.jpg
|
||||
Oracle:Deathtouch\nAt the beginning of your upkeep, sacrifice The Gitrog Monster unless you sacrifice a land.\nYou may play an additional land on each of your turns.\nWhenever one or more land cards are put into your graveyard from anywhere, draw a card.
|
||||
|
||||
@@ -2,8 +2,9 @@ Name:Turntimber Sower
|
||||
ManaCost:2 G
|
||||
Types:Creature Elf Druid
|
||||
PT:3/3
|
||||
T:Mode$ ChangesZone | ValidCard$ Land.YouOwn | Origin$ Any | Destination$ Graveyard | Execute$ TrigToken | OncePerEffect$ True | TriggerZones$ Battlefield | TriggerDescription$ Whenever one or more land cards are put into your graveyard from anywhere, create a 0/1 green Plant creature token.
|
||||
SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenName$ Plant | TokenTypes$ Creature,Plant | TokenOwner$ You | TokenColors$ Green | TokenPower$ 0 | TokenToughness$ 1 | TokenImage$ g 0 1 plant c18
|
||||
AI:RemoveDeck:Random
|
||||
T:Mode$ ChangesZoneAll | ValidCards$ Land.YouOwn | Origin$ Any | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever one or more land cards are put into your graveyard from anywhere, create a 0/1 green Plant creature token.
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_0_1_plant | TokenOwner$ You
|
||||
A:AB$ ChangeZone | Cost$ G Sac<3/Creature> | TgtPrompt$ Choose target land card in your graveyard | ValidTgts$ Land.YouCtrl | Origin$ Graveyard | Destination$ Hand | SpellDescription$ Return target land card from your graveyard to your hand.
|
||||
DeckHas:Ability$Token & Ability$Graveyard
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Whenever one or more land cards are put into your graveyard from anywhere, create a 0/1 green Plant creature token.\n{G}, Sacrifice three creatures: Return target land card from your graveyard to your hand.
|
||||
|
||||
6
forge-gui/res/tokenscripts/b_2_2_zombie.txt
Normal file
6
forge-gui/res/tokenscripts/b_2_2_zombie.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Zombie
|
||||
ManaCost:no cost
|
||||
Types:Creature Zombie
|
||||
Colors:black
|
||||
PT:2/2
|
||||
Oracle:
|
||||
6
forge-gui/res/tokenscripts/g_0_1_plant.txt
Normal file
6
forge-gui/res/tokenscripts/g_0_1_plant.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Plant
|
||||
ManaCost:no cost
|
||||
Types:Creature Plant
|
||||
Colors:green
|
||||
PT:0/1
|
||||
Oracle:
|
||||
@@ -2,7 +2,6 @@ package forge.player;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.FThreads;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
@@ -34,7 +33,6 @@ import forge.util.gui.SGuiChoose;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -310,7 +308,7 @@ public class HumanPlay {
|
||||
|
||||
final HumanCostDecision hcd = new HumanCostDecision(controller, p, sourceAbility, source);
|
||||
boolean mandatory = cost.isMandatory();
|
||||
|
||||
|
||||
//the following costs do not need inputs
|
||||
for (CostPart part : parts) {
|
||||
boolean mayRemovePart = true;
|
||||
@@ -355,7 +353,7 @@ public class HumanPlay {
|
||||
}
|
||||
else if (part instanceof CostGainLife) {
|
||||
PaymentDecision pd = part.accept(hcd);
|
||||
|
||||
|
||||
if (pd == null)
|
||||
return false;
|
||||
else
|
||||
@@ -369,7 +367,7 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
PaymentDecision pd = part.accept(hcd);
|
||||
|
||||
|
||||
if (pd == null)
|
||||
return false;
|
||||
else
|
||||
@@ -520,22 +518,19 @@ public class HumanPlay {
|
||||
}
|
||||
}
|
||||
else if (part instanceof CostExile) {
|
||||
final CardCollection exiledList = new CardCollection();
|
||||
CostExile costExile = (CostExile) part;
|
||||
|
||||
ZoneType from = ZoneType.Graveyard;
|
||||
if ("All".equals(part.getType())) {
|
||||
if (!p.getController().confirmPayment(part, "Do you want to exile all cards in your graveyard?", sourceAbility)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardCollection cards = new CardCollection(p.getCardsIn(ZoneType.Graveyard));
|
||||
for (final Card card : cards) {
|
||||
exiledList.add(p.getGame().getAction().exile(card, null));
|
||||
}
|
||||
costExile.executePayment(sourceAbility, p.getCardsIn(ZoneType.Graveyard));
|
||||
}
|
||||
else {
|
||||
CostExile costExile = (CostExile) part;
|
||||
from = costExile.getFrom();
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source, sourceAbility);
|
||||
CardCollection list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source, sourceAbility);
|
||||
final int nNeeded = getAmountFromPart(costPart, source, sourceAbility);
|
||||
if (list.size() < nNeeded) {
|
||||
return false;
|
||||
@@ -546,11 +541,10 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
list = list.subList(0, nNeeded);
|
||||
for (Card c : list) {
|
||||
exiledList.add(p.getGame().getAction().exile(c, null));
|
||||
}
|
||||
costExile.executePayment(sourceAbility, list);
|
||||
} else {
|
||||
// replace this with input
|
||||
CardCollection newList = new CardCollection();
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = p.getGame().getCard(SGuiChoose.oneOrNone("Exile from " + from, CardView.getCollection(list)));
|
||||
if (c == null) {
|
||||
@@ -558,19 +552,11 @@ public class HumanPlay {
|
||||
}
|
||||
|
||||
list.remove(c);
|
||||
exiledList.add(p.getGame().getAction().exile(c, null));
|
||||
newList.add(c);
|
||||
}
|
||||
costExile.executePayment(sourceAbility, newList);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exiledList.isEmpty()) {
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
final Map<ZoneType, CardCollection> triggerList = Maps.newEnumMap(ZoneType.class);
|
||||
triggerList.put(from, exiledList);
|
||||
runParams.put("Cards", triggerList);
|
||||
runParams.put("Destination", ZoneType.Exile);
|
||||
p.getGame().getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
}
|
||||
}
|
||||
else if (part instanceof CostPutCardToLib) {
|
||||
int amount = Integer.parseInt(((CostPutCardToLib) part).getAmount());
|
||||
@@ -651,10 +637,7 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardCollection cards = new CardCollection(p.getCardsIn(ZoneType.Hand));
|
||||
for (final Card card : cards) {
|
||||
p.discard(card, sourceAbility);
|
||||
}
|
||||
((CostDiscard)part).executePayment(sourceAbility, p.getCardsIn(ZoneType.Hand));
|
||||
} else {
|
||||
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
@@ -722,7 +705,7 @@ public class HumanPlay {
|
||||
String promptCurrent = current == null ? "" : "Current Card: " + current;
|
||||
prompt = source + "\n" + promptCurrent;
|
||||
}
|
||||
|
||||
|
||||
sourceAbility.clearManaPaid();
|
||||
boolean paid = p.getController().payManaCost(cost.getCostMana(), sourceAbility, prompt, false);
|
||||
if (!paid) {
|
||||
@@ -743,42 +726,33 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Card c : inp.getSelected()) {
|
||||
cpl.executePayment(sourceAbility, c);
|
||||
}
|
||||
cpl.executePayment(sourceAbility, new CardCollection(inp.getSelected()));
|
||||
|
||||
if (sourceAbility != null) {
|
||||
cpl.reportPaidCardsTo(sourceAbility);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static boolean handleOfferingConvokeAndDelve(final SpellAbility ability, CardCollection cardsToDelve, boolean manaInputCancelled) {
|
||||
Card hostCard = ability.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
final CardZoneTable table = new CardZoneTable();
|
||||
if (!manaInputCancelled && !cardsToDelve.isEmpty()) {
|
||||
Card hostCard = ability.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
final CardCollection delved = new CardCollection();
|
||||
final Map<ZoneType, CardCollection> triggerList = Maps.newEnumMap(ZoneType.class);
|
||||
|
||||
for (final Card c : cardsToDelve) {
|
||||
hostCard.addDelved(c);
|
||||
delved.add(game.getAction().exile(c, null));
|
||||
}
|
||||
|
||||
if (!delved.isEmpty()) {
|
||||
triggerList.put(ZoneType.Graveyard, delved);
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Cards", triggerList);
|
||||
runParams.put("Destination", ZoneType.Exile);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
|
||||
final ZoneType o = c.getZone().getZoneType();
|
||||
final Card d = game.getAction().exile(c, null);
|
||||
table.put(o, d.getZone().getZoneType(), d);
|
||||
}
|
||||
}
|
||||
if (ability.isOffering() && ability.getSacrificedAsOffering() != null) {
|
||||
final Card offering = ability.getSacrificedAsOffering();
|
||||
offering.setUsedToPay(false);
|
||||
if (!manaInputCancelled) {
|
||||
ability.getHostCard().getGame().getAction().sacrifice(offering, ability);
|
||||
game.getAction().sacrifice(offering, ability, table);
|
||||
}
|
||||
ability.resetSacrificedAsOffering();
|
||||
}
|
||||
@@ -786,7 +760,7 @@ public class HumanPlay {
|
||||
final Card emerge = ability.getSacrificedAsEmerge();
|
||||
emerge.setUsedToPay(false);
|
||||
if (!manaInputCancelled) {
|
||||
ability.getHostCard().getGame().getAction().sacrifice(emerge, ability);
|
||||
game.getAction().sacrifice(emerge, ability, table);
|
||||
}
|
||||
ability.resetSacrificedAsEmerge();
|
||||
}
|
||||
@@ -799,9 +773,12 @@ public class HumanPlay {
|
||||
}
|
||||
ability.clearTappedForConvoke();
|
||||
}
|
||||
if (!table.isEmpty() && !manaInputCancelled) {
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
return !manaInputCancelled;
|
||||
}
|
||||
|
||||
|
||||
public static boolean payManaCost(final PlayerControllerHuman controller, final ManaCost realCost, final CostPartMana mc, final SpellAbility ability, final Player activator, String prompt, ManaConversionMatrix matrix, boolean isActivatedSa) {
|
||||
final Card source = ability.getHostCard();
|
||||
ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction());
|
||||
@@ -887,7 +864,7 @@ public class HumanPlay {
|
||||
if (ability.getSacrificedAsOffering() != null) {
|
||||
System.out.println("Finishing up Offering");
|
||||
offering.setUsedToPay(false);
|
||||
activator.getGame().getAction().sacrifice(offering, ability);
|
||||
activator.getGame().getAction().sacrifice(offering, ability, null);
|
||||
ability.resetSacrificedAsOffering();
|
||||
}
|
||||
}
|
||||
@@ -898,7 +875,7 @@ public class HumanPlay {
|
||||
if (ability.getSacrificedAsEmerge() != null) {
|
||||
System.out.println("Finishing up Emerge");
|
||||
emerge.setUsedToPay(false);
|
||||
activator.getGame().getAction().sacrifice(emerge, ability);
|
||||
activator.getGame().getAction().sacrifice(emerge, ability, null);
|
||||
ability.resetSacrificedAsEmerge();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user