dungeons = player.getCompletedDungeons();
+ for (int i = 0; i < dungeons.size(); ++i) {
+ Card d1 = dungeons.get(i);
+ boolean hasSameName = false;
+ for (int j = i - 1; j >= 0; --j) {
+ Card d2 = dungeons.get(j);
+ if (d1.getName().equals(d2.getName())) {
+ hasSameName = true;
+ break;
+ }
+ }
+ if (!hasSameName) {
+ ++amount;
+ }
+ }
+ return doXMath(amount, m, source, ctb);
+ }
+
return doXMath(0, m, source, ctb);
}
@@ -3395,7 +3421,6 @@ public class AbilityUtils {
return doXMath(n, CardFactoryUtil.extractOperators(s), source, ctb);
}
-
/**
*
* handlePaid.
diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java
index c528e15ce83..7bdbb06db4b 100644
--- a/forge-game/src/main/java/forge/game/ability/ApiType.java
+++ b/forge-game/src/main/java/forge/game/ability/ApiType.java
@@ -7,7 +7,7 @@ import java.util.Map;
import forge.game.ability.effects.*;
import forge.util.ReflectionUtil;
-/**
+/**
* TODO: Write javadoc for this type.
*
*/
@@ -172,6 +172,7 @@ public enum ApiType {
UnattachAll (UnattachAllEffect.class),
Untap (UntapEffect.class),
UntapAll (UntapAllEffect.class),
+ Venture (VentureEffect.class),
Vote (VoteEffect.class),
WinsGame (GameWinEffect.class),
@@ -187,7 +188,7 @@ public enum ApiType {
private final Class extends SpellAbilityEffect> clsEffect;
private static final Map allValues = new HashMap<>();
-
+
static {
for(ApiType t : ApiType.values()) {
allValues.put(t.name().toLowerCase(), t);
@@ -197,7 +198,7 @@ public enum ApiType {
ApiType(Class extends SpellAbilityEffect> clsEf) { this(clsEf, true); }
ApiType(Class extends SpellAbilityEffect> clsEf, final boolean isStateLess) {
clsEffect = clsEf;
- instanceEffect = isStateLess ? ReflectionUtil.makeDefaultInstanceOf(clsEf) : null;
+ instanceEffect = isStateLess ? ReflectionUtil.makeDefaultInstanceOf(clsEf) : null;
}
public static ApiType smartValueOf(String value) {
diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java
index 7928d57ff2b..4c29e29dbb9 100644
--- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java
@@ -329,7 +329,6 @@ public abstract class SpellAbilityEffect {
}
protected static void addSelfTrigger(final SpellAbility sa, String location, final Card card) {
-
String trigStr = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield " +
"| TriggerDescription$ At the beginning of the end step, " + location.toLowerCase() + " CARDNAME.";
@@ -511,7 +510,7 @@ public abstract class SpellAbilityEffect {
}
}
- // build an Effect with that infomation
+ // build an Effect with that information
String name = host.getName() + "'s Effect";
final Card eff = createEffect(sa, controller, name, host.getImageKey());
@@ -617,8 +616,29 @@ public abstract class SpellAbilityEffect {
combat.addBlocker(attacker, c);
combat.orderAttackersForDamageAssignment(c);
+ {
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Attacker, attacker);
+ runParams.put(AbilityKey.Blocker, c);
+ game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedByCreature, runParams, false);
+ }
+ {
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Attackers, attacker);
+ game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedOnce, runParams, false);
+ }
+
// Run triggers for new blocker and add it to damage assignment order
if (!wasBlocked) {
+ final CardCollection blockers = combat.getBlockers(attacker);
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Attacker, attacker);
+ runParams.put(AbilityKey.Blockers, blockers);
+ runParams.put(AbilityKey.NumBlockers, blockers.size());
+ runParams.put(AbilityKey.Defender, combat.getDefenderByAttacker(attacker));
+ runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(attacker));
+ game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
+
combat.setBlocked(attacker, true);
combat.addBlockerToDamageAssignmentOrder(attacker, c);
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java
index 9800cc32764..fe4bc675585 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java
@@ -93,7 +93,7 @@ public class AmassEffect extends TokenEffectBase {
GameEntityCounterTable table = new GameEntityCounterTable();
for(final Card tgtCard : tgtCards) {
- tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, true, table);
+ tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, sa, true, table);
game.updateLastStateForCard(tgtCard);
if (remember) {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java
index 10bb5c57ba9..44d7d2cbd43 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java
@@ -59,6 +59,9 @@ public class AnimateAllEffect extends AnimateEffectBase {
if (types.hasSubtype("ChosenType")) {
types.clear();
types.add(host.getChosenType());
+ } else if (types.hasSubtype("ChosenType2")) {
+ types.clear();
+ types.add(host.getChosenType2());
}
final List keywords = new ArrayList<>();
@@ -125,7 +128,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
}
CardCollectionView list;
-
+
if (!sa.usesTargeting() && !sa.hasParam("Defined")) {
list = game.getCardsIn(ZoneType.Battlefield);
} else {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java
index 2bb8cf9c569..9b09ca23032 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java
@@ -71,6 +71,9 @@ public class AnimateEffect extends AnimateEffectBase {
if (types.hasSubtype("ChosenType")) {
types.clear();
types.add(source.getChosenType());
+ } else if (types.hasSubtype("ChosenType2")) {
+ types.clear();
+ types.add(source.getChosenType2());
}
final List keywords = Lists.newArrayList();
@@ -227,7 +230,7 @@ public class AnimateEffect extends AnimateEffectBase {
}
// allow SVar substitution for keywords
for (int i = 0; i < keywords.size(); i++) {
- final String k = keywords.get(i);
+ final String k = keywords.get(i);
if (sa.hasSVar(k)) {
keywords.add("\"" + k + "\"");
keywords.remove(k);
diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java
index 17e96421e05..42d914afa41 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java
@@ -120,7 +120,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
if (!addType.isEmpty() || !removeType.isEmpty() || removeCreatureTypes) {
c.addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
- removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, timestamp);
+ removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, timestamp, true, false);
}
c.addChangedCardKeywords(keywords, removeKeywords, removeAll, removeLandTypes, timestamp);
@@ -133,7 +133,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
c.addHiddenExtrinsicKeyword(k);
}
- c.addColor(colors, !sa.hasParam("OverwriteColors"), timestamp);
+ c.addColor(colors, !sa.hasParam("OverwriteColors"), timestamp, false);
if (sa.hasParam("LeaveBattlefield")) {
addLeaveBattlefieldReplacement(c, sa, sa.getParam("LeaveBattlefield"));
diff --git a/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java
index 0746c83624a..c5f78d3e8d9 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java
@@ -90,7 +90,7 @@ public class AttachEffect extends SpellAbilityEffect {
// If Cast Targets will be checked on the Stack
for (final Card attachment : attachments) {
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
- if ( sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message) )
+ if (sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message))
continue;
handleAttachment(attachment, attachTo, sa);
}
@@ -118,7 +118,6 @@ public class AttachEffect extends SpellAbilityEffect {
* the o
*/
public static void handleAttachment(final Card card, final Object o, final SpellAbility sa) {
-
if (card == null) { return; }
if (o instanceof Card) {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java
index cf998098665..bc03ee03349 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java
@@ -7,6 +7,7 @@ import java.util.Map;
import com.google.common.collect.Maps;
import forge.game.Game;
+import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
@@ -46,7 +47,8 @@ public class BalanceEffect extends SpellAbilityEffect {
validCards.add(CardLists.getValidCards(players.get(i).getCardsIn(zone), valid, activator, source, sa));
min = Math.min(min, validCards.get(i).size());
}
-
+
+ Map params = AbilityKey.newMap();
CardZoneTable table = new CardZoneTable();
for (int i = 0; i < players.size(); i++) {
Player p = players.get(i);
@@ -59,7 +61,7 @@ public class BalanceEffect extends SpellAbilityEffect {
} else { // Battlefield
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
if ( null == card ) continue;
- game.getAction().sacrifice(card, sa, table);
+ game.getAction().sacrifice(card, sa, table, params);
}
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java
index 7d2d07e1985..5ed22a22f26 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java
@@ -291,7 +291,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
* @return a {@link java.lang.String} object.
*/
private static String changeKnownOriginStackDescription(final SpellAbility sa) {
-
final StringBuilder sb = new StringBuilder();
final Card host = sa.getHostCard();
@@ -709,7 +708,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("WithCountersType")) {
CounterType cType = CounterType.getType(sa.getParam("WithCountersType"));
int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa);
- movedCard.addCounter(cType, cAmount, player, true, counterTable);
+ movedCard.addCounter(cType, cAmount, player, sa, true, counterTable);
}
if (sa.hasParam("ExileFaceDown") || sa.hasParam("FaceDown")) {
@@ -1047,7 +1046,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// only multi-select if player can select more than one
if (changeNum > 1 && allowMultiSelect(decider, sa)) {
List selectedCards;
- if (! sa.hasParam("SelectPrompt")) {
+ if (!sa.hasParam("SelectPrompt")) {
// new default messaging for multi select
if (fetchList.size() > changeNum) {
//Select up to %changeNum cards from %players %origin
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java
index 7ec25a53304..e102233667e 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java
@@ -200,7 +200,6 @@ public class CharmEffect extends SpellAbilityEffect {
}
private static void chainAbilities(SpellAbility sa, List chosen) {
-
if (chosen == null) {
return;
}
@@ -243,5 +242,4 @@ public class CharmEffect extends SpellAbilityEffect {
}
-
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java
index 36b0acf9f67..2bfb5c1bbb0 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java
@@ -65,7 +65,7 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
for (final Player p : tgtPlayers) {
if ((tgt == null) || p.canBeTargetedBy(sa)) {
String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes);
- if (!sa.hasParam("Secondary")) {
+ if (!sa.hasParam("ChooseType2")) {
card.setChosenType(choice);
} else {
card.setChosenType2(choice);
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java
index 5d4ff084e57..d82cef7699f 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java
@@ -143,7 +143,7 @@ public class CloneEffect extends SpellAbilityEffect {
tgtCard.updateStateForView();
// when clone is itself, cleanup from old abilities
- if (host.equals(tgtCard)) {
+ if (host.equals(tgtCard) && !sa.hasParam("ImprintRememberedNoCleanup")) {
tgtCard.clearImprintedCards();
tgtCard.clearRemembered();
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java
index 4dbdcee1100..7bdd8ae23cd 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java
@@ -4,13 +4,14 @@ import java.util.List;
import forge.GameCommand;
import forge.game.Game;
+import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.Lang;
import forge.util.TextUtil;
-/**
+/**
* TODO: Write javadoc for this type.
*
*/
@@ -18,16 +19,18 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
-
+
List tgtPlayers = getTargetPlayers(sa);
return TextUtil.concatWithSpace(sa.getActivatingPlayer().toString(),"controls", Lang.joinHomogenous(tgtPlayers),"during their next turn");
}
-
+
@SuppressWarnings("serial")
@Override
public void resolve(SpellAbility sa) {
final Player activator = sa.getActivatingPlayer();
final Game game = activator.getGame();
+ final Player controller = sa.hasParam("Controller") ? AbilityUtils.getDefinedPlayers(
+ sa.getHostCard(), sa.getParam("Controller"), sa).get(0) : activator;
List tgtPlayers = getTargetPlayers(sa);
@@ -37,13 +40,13 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
@Override
public void run() {
// CR 800.4b
- if (activator.hasLost()) {
+ if (controller.hasLost()) {
return;
}
long ts = game.getNextTimestamp();
- pTarget.addController(ts, activator);
-
+ pTarget.addController(ts, controller);
+
// on following cleanup release control
game.getEndOfTurn().addUntil(new GameCommand() {
@Override
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java
index 42cccd4b5be..e6abc6b6fcd 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java
@@ -99,9 +99,9 @@ public class CopyPermanentEffect extends TokenEffectBase {
if (sa.hasParam("RandomCopied")) {
List copysource = Lists.newArrayList(cards);
List choice = Lists.newArrayList();
- final String num = sa.hasParam("RandomNum") ? sa.getParam("RandomNum") : "1";
+ final String num = sa.getParamOrDefault("RandomNum","1");
int ncopied = AbilityUtils.calculateAmount(host, num, sa);
- while(ncopied > 0 && !copysource.isEmpty()) {
+ while (ncopied > 0 && !copysource.isEmpty()) {
final PaperCard cp = Aggregates.random(copysource);
Card possibleCard = Card.fromPaperCard(cp, activator); // Need to temporarily set the Owner so the Game is set
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java
index ea4cf2586e5..0d1c59228d6 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java
@@ -73,10 +73,8 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
controllers = AbilityUtils.getDefinedPlayers(card, sa.getParam("Controller"), sa);
}
-
final List tgtSpells = getTargetSpells(sa);
-
if (tgtSpells.size() == 0 || amount == 0) {
return;
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java
index 3999c0a81be..d2d1c5eecaa 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java
@@ -12,6 +12,7 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardFactoryUtil;
+import forge.game.card.CardZoneTable;
import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility;
@@ -113,6 +114,8 @@ public class CounterEffect extends SpellAbilityEffect {
}
}
+ Map params = AbilityKey.newMap();
+ CardZoneTable table = new CardZoneTable();
for (final SpellAbility tgtSA : sas) {
final Card tgtSACard = tgtSA.getHostCard();
// should remember even that spell cannot be countered, e.g. Dovescape
@@ -137,7 +140,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, true, null);
+ game.getAction().destroy(tgtSACard, sa, true, table, params);
}
if (sa.hasParam("RememberCountered")) {
@@ -152,6 +155,7 @@ public class CounterEffect extends SpellAbilityEffect {
}
}
}
+ table.triggerChangesZoneAll(game, sa);
} // end counterResolve
/**
@@ -189,7 +193,7 @@ public class CounterEffect extends SpellAbilityEffect {
params.put(AbilityKey.StackSi, si);
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
- if (srcSA.hasParam("DestinationChoice")) {//Hinder
+ if (srcSA.hasParam("DestinationChoice")) { //Hinder
List pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos, null);
}
@@ -207,7 +211,6 @@ public class CounterEffect extends SpellAbilityEffect {
} else if (destination.equals("Battlefield")) {
if (tgtSA instanceof SpellPermanent) {
Card c = tgtSA.getHostCard();
- System.out.println(c + " is SpellPermanent");
c.setController(srcSA.getActivatingPlayer(), 0);
game.getAction().moveToPlay(c, srcSA.getActivatingPlayer(), srcSA, params);
} else {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java
index 9a20378f42e..a7fc037f994 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java
@@ -147,8 +147,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
Map countersToAdd = Maps.newHashMap();
for (Card src : srcCards) {
- // rule 121.5: If the first and second objects are the same object, nothing
- // happens
+ // rule 121.5: If the first and second objects are the same object, nothing happens
if (src.equals(dest)) {
continue;
}
@@ -163,7 +162,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
}
}
for (Map.Entry e : countersToAdd.entrySet()) {
- dest.addCounter(e.getKey(), e.getValue(), player, true, table);
+ dest.addCounter(e.getKey(), e.getValue(), player, sa, true, table);
}
game.updateLastStateForCard(dest);
@@ -199,8 +198,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
boolean updateSource = false;
for (final Card dest : tgtCards) {
- // rule 121.5: If the first and second objects are the same object, nothing
- // happens
+ // rule 121.5: If the first and second objects are the same object, nothing happens
if (source.equals(dest)) {
continue;
}
@@ -225,7 +223,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
if (cnum > 0) {
source.subtractCounter(cType, cnum);
- cur.addCounter(cType, cnum, player, true, table);
+ cur.addCounter(cType, cnum, player, sa, true, table);
game.updateLastStateForCard(cur);
updateSource = true;
}
@@ -262,8 +260,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
for (final Card dest : tgtCards) {
if (null != dest) {
- // rule 121.5: If the first and second objects are the same object, nothing
- // happens
+ // rule 121.5: If the first and second objects are the same object, nothing happens
if (source.equals(dest)) {
continue;
}
@@ -311,7 +308,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
}
for (Map.Entry e : countersToAdd.entrySet()) {
- cur.addCounter(e.getKey(), e.getValue(), player, true, table);
+ cur.addCounter(e.getKey(), e.getValue(), player, sa, true, table);
}
game.updateLastStateForCard(cur);
}
@@ -329,8 +326,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
final PlayerController pc = player.getController();
final Game game = host.getGame();
- // rule 121.5: If the first and second objects are the same object, nothing
- // happens
+ // rule 121.5: If the first and second objects are the same object, nothing happens
if (src.equals(dest)) {
return;
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java
index 84c1a609ab6..8f7cd9f9653 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java
@@ -54,10 +54,10 @@ public class CountersMultiplyEffect extends SpellAbilityEffect {
continue;
}
if (counterType != null) {
- gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, true, table);
+ gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, sa, true, table);
} else {
for (Map.Entry e : gameCard.getCounters().entrySet()) {
- gameCard.addCounter(e.getKey(), e.getValue() * n, player, true, table);
+ gameCard.addCounter(e.getKey(), e.getValue() * n, player, sa, true, table);
}
}
game.updateLastStateForCard(gameCard);
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java
index b2705b69516..b1cbf040fe7 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java
@@ -30,7 +30,7 @@ public class CountersNoteEffect extends SpellAbilityEffect {
if (mode.equals(MODE_STORE)) {
noteCounters(c, source);
} else if (mode.equals(MODE_LOAD)) {
- loadCounters(c, source, p, table);
+ loadCounters(c, source, p, sa, table);
}
}
table.triggerCountersPutAll(game);
@@ -44,13 +44,13 @@ public class CountersNoteEffect extends SpellAbilityEffect {
}
}
- private void loadCounters(Card notee, Card source, final Player p, GameEntityCounterTable table) {
+ private void loadCounters(Card notee, Card source, final Player p, final SpellAbility sa, GameEntityCounterTable table) {
for(Entry svar : source.getSVars().entrySet()) {
String key = svar.getKey();
if (key.startsWith(NOTE_COUNTERS)) {
notee.addCounter(
CounterType.getType(key.substring(NOTE_COUNTERS.length())),
- Integer.parseInt(svar.getValue()), p, false, table);
+ Integer.parseInt(svar.getValue()), p, sa, false, table);
}
// TODO Probably should "remove" the svars that were temporarily used
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java
index 0ae32240416..22ed2be4fdc 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java
@@ -48,7 +48,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
GameEntityCounterTable table = new GameEntityCounterTable();
for (final GameEntity ge : result) {
for (final CounterType ct : ge.getCounters().keySet()) {
- ge.addCounter(ct, 1, p, true, true, table);
+ ge.addCounter(ct, 1, p, sa, true, true, table);
}
if (ge instanceof Card) {
Card c = (Card) ge;
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java
index fe8fd590385..d80af630170 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java
@@ -71,7 +71,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
if (etbcounter) {
tgtCard.addEtbCounter(CounterType.getType(type), counterAmount, placer);
} else {
- tgtCard.addCounter(CounterType.getType(type), counterAmount, placer, inBattlefield, table);
+ tgtCard.addCounter(CounterType.getType(type), counterAmount, placer, sa, inBattlefield, table);
}
game.updateLastStateForCard(tgtCard);
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java
index f5b35b965d9..cd5dbe0c895 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java
@@ -226,10 +226,10 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (eachExistingCounter) {
for (CounterType ct : choices) {
if (obj instanceof Player) {
- ((Player) obj).addCounter(ct, counterAmount, placer, true, table);
+ ((Player) obj).addCounter(ct, counterAmount, placer, sa, true, table);
}
if (obj instanceof Card) {
- gameCard.addCounter(ct, counterAmount, placer, true, table);
+ gameCard.addCounter(ct, counterAmount, placer, sa, true, table);
}
}
continue;
@@ -253,7 +253,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) {
for (Entry cti : c.getCounters().entrySet()) {
if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) {
- gameCard.addCounter(cti.getKey(), cti.getValue(), placer, true, table);
+ gameCard.addCounter(cti.getKey(), cti.getValue(), placer, sa, true, table);
}
}
}
@@ -338,7 +338,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (etbcounter) {
gameCard.addEtbCounter(counterType, counterAmount, placer);
} else {
- int addedAmount = gameCard.addCounter(counterType, counterAmount, placer, true, table);
+ int addedAmount = gameCard.addCounter(counterType, counterAmount, placer, sa, true, table);
if (addedAmount > 0) {
counterAdded = true;
}
@@ -372,7 +372,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (etbcounter) {
gameCard.addEtbCounter(counterType, counterAmount, placer);
} else {
- if (gameCard.addCounter(counterType, counterAmount, placer, false, table) > 0) {
+ if (gameCard.addCounter(counterType, counterAmount, placer, sa, false, table) > 0) {
counterAdded = true;
}
}
@@ -388,7 +388,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
} else if (obj instanceof Player) {
// Add Counters to players!
Player pl = (Player) obj;
- pl.addCounter(counterType, counterAmount, placer, true, table);
+ pl.addCounter(counterType, counterAmount, placer, sa, true, table);
}
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java
index 7490946e2c9..2c535de9099 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java
@@ -111,7 +111,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
boolean apply = zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack);
- tgtCard.addCounter(chosenType, counterAmount, pl, apply, table);
+ tgtCard.addCounter(chosenType, counterAmount, pl, sa, apply, table);
} else {
tgtCard.subtractCounter(chosenType, counterAmount);
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
index 8485f02f2d7..c0990697197 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
@@ -60,9 +60,9 @@ public class DamageDealEffect extends DamageBaseEffect {
// if use targeting we show all targets and corresponding damage
if (spellAbility.usesTargeting()) {
if (spellAbility.hasParam("DivideEvenly")) {
- stringBuilder.append("divided evenly (rounded down) to\n");
+ stringBuilder.append("divided evenly (rounded down) to \n");
} else if (spellAbility.isDividedAsYouChoose()) {
- stringBuilder.append("divided to\n");
+ stringBuilder.append("divided to \n");
} else
stringBuilder.append("to ");
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java
index cad9aff89be..32f6ca883c0 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java
@@ -31,7 +31,6 @@ public class DelayedTriggerEffect extends SpellAbilityEffect {
}
return "";
-
}
@Override
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java
index 5fed2c7a4ef..4f0acaa45be 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java
@@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameActionUtil;
+import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -44,7 +45,6 @@ public class DestroyAllEffect extends SpellAbilityEffect {
*/
@Override
public void resolve(SpellAbility sa) {
-
final boolean noRegen = sa.hasParam("NoRegen");
final Card card = sa.getHostCard();
final Game game = sa.getActivatingPlayer().getGame();
@@ -90,10 +90,12 @@ public class DestroyAllEffect extends SpellAbilityEffect {
}
CardZoneTable table = new CardZoneTable();
+ Map params = AbilityKey.newMap();
+ params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
Map cachedMap = Maps.newHashMap();
for (Card c : list) {
- if (game.getAction().destroy(c, sa, !noRegen, table) && remDestroyed) {
+ if (game.getAction().destroy(c, sa, !noRegen, table, params) && remDestroyed) {
card.addRemembered(CardUtil.getLKICopy(c, cachedMap));
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java
index 240a87bb202..f7eaba7e38d 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java
@@ -8,9 +8,10 @@ import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameActionUtil;
+import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
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.spellability.SpellAbility;
@@ -72,13 +73,16 @@ public class DestroyEffect extends SpellAbilityEffect {
card.clearRemembered();
}
- CardCollection tgtCards = getTargetCards(sa);
- CardCollection untargetedCards = CardUtil.getRadiance(sa);
+ CardCollectionView tgtCards = getTargetCards(sa);
+ CardCollectionView untargetedCards = CardUtil.getRadiance(sa);
if (tgtCards.size() > 1) {
- tgtCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard, sa);
+ tgtCards = GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard, sa);
}
+ Map params = AbilityKey.newMap();
+ params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
+
CardZoneTable table = new CardZoneTable();
Map cachedMap = Maps.newHashMap();
for (final Card tgtC : tgtCards) {
@@ -90,24 +94,24 @@ public class DestroyEffect extends SpellAbilityEffect {
if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) {
continue;
}
- internalDestroy(gameCard, sa, table, cachedMap);
+ internalDestroy(gameCard, sa, table, cachedMap, params);
}
}
if (untargetedCards.size() > 1) {
- untargetedCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa);
+ untargetedCards = GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa);
}
for (final Card unTgtC : untargetedCards) {
if (unTgtC.isInPlay()) {
- internalDestroy(unTgtC, sa, table, cachedMap);
+ internalDestroy(unTgtC, sa, table, cachedMap, params);
}
}
table.triggerChangesZoneAll(game, sa);
}
- protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map cachedMap) {
+ protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map cachedMap, Map params) {
final Card card = sa.getHostCard();
final Game game = card.getGame();
@@ -122,9 +126,9 @@ public class DestroyEffect extends SpellAbilityEffect {
card.addRemembered(gameCard.getAttachedCards());
}
if (sac) {
- destroyed = game.getAction().sacrifice(gameCard, sa, table) != null;
+ destroyed = game.getAction().sacrifice(gameCard, sa, table, params) != null;
} else {
- destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table);
+ destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table, params);
}
if (destroyed && remDestroyed) {
card.addRemembered(gameCard);
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java
index 467fd6d72af..ad67308edc1 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java
@@ -336,7 +336,7 @@ public class DigEffect extends SpellAbilityEffect {
} else if (destZone1.equals(ZoneType.Exile)) {
if (sa.hasParam("ExileWithCounter")) {
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
- 1, player, true, counterTable);
+ 1, player, sa, true, counterTable);
}
c.setExiledWith(effectHost);
c.setExiledBy(effectHost.getController());
@@ -408,7 +408,7 @@ public class DigEffect extends SpellAbilityEffect {
if (destZone2 == ZoneType.Exile) {
if (sa.hasParam("ExileWithCounter")) {
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
- 1, player, true, counterTable);
+ 1, player, sa, true, counterTable);
}
c.setExiledWith(effectHost);
c.setExiledBy(effectHost.getController());
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java
index cbe19e79a5b..fa8dd58e4b8 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java
@@ -173,26 +173,25 @@ public class DigUntilEffect extends SpellAbilityEffect {
if (optionalFound && !p.getController().confirmAction(sa, null,
Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()))) {
continue;
+ }
+ Card m = null;
+ if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
+ c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
+ m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa);
+ if (sa.hasParam("Tapped")) {
+ c.setTapped(true);
+ }
+ if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) {
+ combatChanged = true;
+ }
+ } else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
+ //Don't do anything
} else {
- Card m = null;
- if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
- c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
- m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa);
- if (sa.hasParam("Tapped")) {
- c.setTapped(true);
- }
- if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) {
- combatChanged = true;
- }
- } else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
- //Don't do anything
- } else {
- 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);
- }
+ 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);
}
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java
index 846cafbbe82..15978d35795 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java
@@ -18,6 +18,8 @@ public class ETBReplacementEffect extends SpellAbilityEffect {
Map params = AbilityKey.newMap();
params.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
params.put(AbilityKey.ReplacementEffect, sa.getReplacementEffect());
- sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), sa, params);
+ final SpellAbility root = sa.getRootAbility();
+ SpellAbility cause = (SpellAbility) root.getReplacingObject(AbilityKey.Cause);
+ sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), cause, params);
}
}
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java
index 44a1097eb3e..04471bed67a 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java
@@ -247,9 +247,12 @@ public class EffectEffect extends SpellAbilityEffect {
}
// Set Chosen Type
- if (!hostCard.getChosenType().isEmpty()) {
+ if (hostCard.hasChosenType()) {
eff.setChosenType(hostCard.getChosenType());
}
+ if (hostCard.hasChosenType2()) {
+ eff.setChosenType2(hostCard.getChosenType2());
+ }
// Set Chosen name
if (!hostCard.getNamedCard().isEmpty()) {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java
index 8a3985aeb55..b820370714a 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java
@@ -83,7 +83,7 @@ public class ExploreEffect extends SpellAbilityEffect {
// if the card is not more in the game anymore
// this might still return true but its no problem
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) {
- c.addCounter(CounterEnumType.P1P1, 1, pl, true, table);
+ c.addCounter(CounterEnumType.P1P1, 1, pl, sa, true, table);
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java
index 0570993af24..47c042d245a 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java
@@ -65,19 +65,18 @@ public class FightEffect extends DamageBaseEffect {
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeFight", CardTranslation.getTranslatedName(fighters.get(0).getName()), CardTranslation.getTranslatedName(fighters.get(1).getName())))) {
return;
- } else {
- dealDamage(sa, fighters.get(0), fighters.get(1));
-
- for (Card c : fighters) {
- final Map runParams = AbilityKey.newMap();
- runParams.put(AbilityKey.Fighter, c);
- game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
- }
-
- final Map runParams = AbilityKey.newMap();
- runParams.put(AbilityKey.Fighters, fighters);
- game.getTriggerHandler().runTrigger(TriggerType.FightOnce, runParams, false);
}
+
+ dealDamage(sa, fighters.get(0), fighters.get(1));
+
+ for (Card c : fighters) {
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Fighter, c);
+ game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
+ }
+
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Fighters, fighters);
}
private static List getFighters(SpellAbility sa) {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java
index fa30bbe8fe8..16d97edd34d 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java
@@ -58,8 +58,8 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
Card lki = CardUtil.getLKICopy(gameCard);
lki.clearControllers();
lki.setOwner(sa.getActivatingPlayer());
- // if this trigger is part of ETBReplacement it shouldn't run with LKI from incomplete zone change (Wall of Stolen Identity)
- final Card trigHost = sa.getRootAbility().getReplacementEffect() != null && sa.getRootAbility().getReplacementEffect().getMode().equals(ReplacementType.Moved) ? gameCard : lki;
+ // if this trigger is part of ETBReplacement it shouldn't run with LKI from incomplete zone change (Mimic Vat + Wall of Stolen Identity)
+ final Card trigHost = sa.getRootAbility().getReplacementEffect() != null && sa.getRootAbility().getReplacementEffect().getMode().equals(ReplacementType.Moved) && gameCard.getZone() == null ? gameCard : lki;
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, trigHost, sa.isIntrinsic(), null);
immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java
index 73f1edd0db6..537d7011279 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java
@@ -3,6 +3,7 @@ package forge.game.ability.effects;
import static forge.util.TextUtil.toManaString;
import java.util.List;
+import java.util.Map;
import org.apache.commons.lang3.StringUtils;
@@ -61,34 +62,51 @@ public class ManaEffect extends SpellAbilityEffect {
String[] colorsNeeded = express.isEmpty() ? null : express.split(" ");
boolean differentChoice = abMana.getOrigProduced().contains("Different");
ColorSet fullOptions = colorOptions;
- for (int nMana = 0; nMana < amount; nMana++) {
- String choice = "";
- if (colorsNeeded != null && colorsNeeded.length > nMana) { // select from express choices if possible
- colorOptions = ColorSet
- .fromMask(fullOptions.getColor() & ManaAtom.fromName(colorsNeeded[nMana]));
- }
- if (colorOptions.isColorless() && colorsProduced.length > 0) {
- // If we just need generic mana, no reason to ask the controller for a choice,
- // just use the first possible color.
- choice = colorsProduced[differentChoice ? nMana : 0];
- } else {
- byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
- differentChoice && (colorsNeeded == null || colorsNeeded.length <= nMana) ? fullOptions : colorOptions);
- if (chosenColor == 0)
- throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + card.getName());
-
- if (differentChoice) {
- fullOptions = ColorSet.fromMask(fullOptions.getColor() - chosenColor);
+ // Use specifyManaCombo if possible
+ if (colorsNeeded == null && amount > 1 && !sa.hasParam("TwoEach")) {
+ Map choices = p.getController().specifyManaCombo(sa, colorOptions, amount, differentChoice);
+ for (Map.Entry e : choices.entrySet()) {
+ Byte chosenColor = e.getKey();
+ String choice = MagicColor.toShortString(chosenColor);
+ Integer count = e.getValue();
+ while (count > 0) {
+ if (choiceString.length() > 0) {
+ choiceString.append(" ");
+ }
+ choiceString.append(choice);
+ --count;
}
- choice = MagicColor.toShortString(chosenColor);
}
+ } else {
+ for (int nMana = 0; nMana < amount; nMana++) {
+ String choice = "";
+ if (colorsNeeded != null && colorsNeeded.length > nMana) { // select from express choices if possible
+ colorOptions = ColorSet
+ .fromMask(fullOptions.getColor() & ManaAtom.fromName(colorsNeeded[nMana]));
+ }
+ if (colorOptions.isColorless() && colorsProduced.length > 0) {
+ // If we just need generic mana, no reason to ask the controller for a choice,
+ // just use the first possible color.
+ choice = colorsProduced[differentChoice ? nMana : 0];
+ } else {
+ byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
+ differentChoice && (colorsNeeded == null || colorsNeeded.length <= nMana) ? fullOptions : colorOptions);
+ if (chosenColor == 0)
+ throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + card.getName());
- if (nMana > 0) {
- choiceString.append(" ");
- }
- choiceString.append(choice);
- if (sa.hasParam("TwoEach")) {
- choiceString.append(" ").append(choice);
+ if (differentChoice) {
+ fullOptions = ColorSet.fromMask(fullOptions.getColor() - chosenColor);
+ }
+ choice = MagicColor.toShortString(chosenColor);
+ }
+
+ if (nMana > 0) {
+ choiceString.append(" ");
+ }
+ choiceString.append(choice);
+ if (sa.hasParam("TwoEach")) {
+ choiceString.append(" ").append(choice);
+ }
}
}
@@ -128,7 +146,7 @@ public class ManaEffect extends SpellAbilityEffect {
if (type.equals("EnchantedManaCost")) {
Card enchanted = card.getEnchantingCard();
- if (enchanted == null )
+ if (enchanted == null )
continue;
StringBuilder sb = new StringBuilder();
@@ -234,7 +252,7 @@ public class ManaEffect extends SpellAbilityEffect {
* a {@link forge.card.spellability.AbilityMana} object.
* @param af
* a {@link forge.game.ability.AbilityFactory} object.
- *
+ *
* @return a {@link java.lang.String} object.
*/
diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java
index 3de152f6f87..3e5a0a4d5fd 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java
@@ -183,18 +183,11 @@ public class PlayEffect extends SpellAbilityEffect {
activator.addController(controlledByTimeStamp, controlledByPlayer);
}
+ boolean singleOption = tgtCards.size() == 1 && amount == 1 && optional;
+
while (!tgtCards.isEmpty() && amount > 0) {
activator.getController().tempShowCards(showCards);
- Card tgtCard = null;
- if (tgtCards.size() == 1 && amount == 1 && optional) {
- tgtCard = tgtCards.get(0);
- if (!controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantPlayCard", CardTranslation.getTranslatedName(tgtCard.getName())))) {
- break;
- }
- } else {
- tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), optional, null);
- }
-
+ Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), !singleOption && optional, null);
activator.getController().endTempShowCards();
if (tgtCard == null) {
break;
@@ -210,12 +203,16 @@ public class PlayEffect extends SpellAbilityEffect {
game.getAction().revealTo(tgtCard, activator);
}
- if (!sa.hasParam("AllowRepeats")) {
- tgtCards.remove(tgtCard);
+ if (singleOption && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantPlayCard", CardTranslation.getTranslatedName(tgtCard.getName())))) {
+ if (wasFaceDown) {
+ tgtCard.turnFaceDownNoUpdate();
+ tgtCard.updateStateForView();
+ }
+ break;
}
- if (wasFaceDown) {
- tgtCard.updateStateForView();
+ if (!sa.hasParam("AllowRepeats")) {
+ tgtCards.remove(tgtCard);
}
final Card original = tgtCard;
@@ -272,6 +269,10 @@ public class PlayEffect extends SpellAbilityEffect {
}
// in case player canceled from choice dialog
if (tgtSA == null) {
+ if (wasFaceDown) {
+ tgtCard.turnFaceDownNoUpdate();
+ tgtCard.updateStateForView();
+ }
continue;
}
@@ -329,7 +330,7 @@ public class PlayEffect extends SpellAbilityEffect {
source.addRemembered(tgtSA.getHostCard());
}
- //Forgot only of playing was successful
+ //Forgot only if playing was successful
if (sa.hasParam("ForgetRemembered")) {
source.clearRemembered();
}
@@ -397,7 +398,6 @@ public class PlayEffect extends SpellAbilityEffect {
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
}
-
protected void addIllusionaryMaskReplace(Card c, SpellAbility sa) {
final Card hostCard = sa.getHostCard();
final Game game = hostCard.getGame();
diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java
index ba98dfdccf3..0a1042cc0ae 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java
@@ -150,7 +150,6 @@ public class PumpEffect extends SpellAbilityEffect {
*/
@Override
protected String getStackDescription(final SpellAbility sa) {
-
final StringBuilder sb = new StringBuilder();
List tgts = Lists.newArrayList();
tgts.addAll(getCardsfromTargets(sa));
diff --git a/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java
index 9a37000a753..7d81128d7bc 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java
@@ -50,7 +50,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
game.resetPlayersAttackedOnNextTurn();
game.resetPlayersAttackedOnNextTurn();
GameAction action = game.getAction();
-
+
for (Player p: players) {
p.setStartingLife(p.getStartingLife());
p.clearCounters();
@@ -58,6 +58,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
p.onCleanupPhase();
p.setLandsPlayedLastTurn(0);
p.resetCommanderStats();
+ p.resetCompletedDungeons();
CardCollection newLibrary = new CardCollection(p.getCardsIn(restartZones, false));
List filteredCards = null;
@@ -74,7 +75,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
}
}
p.getZone(ZoneType.Command).removeAllCards(true);
-
+
for (Card c : newLibrary) {
action.moveToLibrary(c, 0, sa);
}
@@ -85,15 +86,15 @@ public class RestartGameEffect extends SpellAbilityEffect {
trigHandler.clearSuppression(TriggerType.Shuffled);
trigHandler.clearSuppression(TriggerType.ChangesZone);
-
+
game.resetTurnOrder();
game.setAge(GameStage.RestartedByKarn);
// Do not need this because ability will resolve only during that player's turn
//game.getPhaseHandler().setPlayerTurn(sa.getActivatingPlayer());
-
+
// Set turn number?
-
- // The rest is handled by phaseHandler
+
+ // The rest is handled by phaseHandler
}
/* (non-Javadoc)
diff --git a/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java
index 4dea92f1a35..cb8602ca2cb 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java
@@ -59,7 +59,7 @@ public class RevealEffect extends SpellAbilityEffect {
if (valid.isEmpty())
continue;
- if( cnt > valid.size() )
+ if (cnt > valid.size())
cnt = valid.size();
int min = cnt;
diff --git a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java
index ab26ada5ced..5328f6a9170 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java
@@ -19,6 +19,28 @@ import forge.util.MyRandom;
public class RollDiceEffect extends SpellAbilityEffect {
+ public static String makeFormatedDescription(SpellAbility sa) {
+ StringBuilder sb = new StringBuilder();
+ final String key = "ResultSubAbilities";
+ if (sa.hasParam(key)) {
+ String [] diceAbilities = sa.getParam(key).split(",");
+ for (String ab : diceAbilities) {
+ String [] kv = ab.split(":");
+ String desc = sa.getAdditionalAbility(kv[0]).getDescription();
+ if (!desc.isEmpty()) {
+ sb.append("\n\n").append(desc);
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private static int getRollAdvange(final Player player) {
+ String str = "If you would roll one or more dice, instead roll that many dice plus one and ignore the lowest roll.";
+ return player.getKeywords().getAmount(str);
+ }
+
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
*/
@@ -32,9 +54,9 @@ public class RollDiceEffect extends SpellAbilityEffect {
} else {
stringBuilder.append(player).append(" rolls ");
}
- stringBuilder.append(sa.getParamOrDefault("Amt", "a")).append(" ");
+ stringBuilder.append(sa.getParamOrDefault("Amount", "a")).append(" ");
stringBuilder.append(sa.getParamOrDefault("Sides", "6")).append("-sided ");
- if (sa.getParamOrDefault("Amt", "1").equals("1")) {
+ if (sa.getParamOrDefault("Amount", "1").equals("1")) {
stringBuilder.append("die.");
} else {
stringBuilder.append("dice.");
@@ -42,6 +64,73 @@ public class RollDiceEffect extends SpellAbilityEffect {
return stringBuilder.toString();
}
+ private void rollDice(SpellAbility sa, Player player, int amount, int sides) {
+ final Card host = sa.getHostCard();
+ final int modifier = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Modifier", "0"), sa);
+ final int advantage = getRollAdvange(player);
+ amount += advantage;
+ int total = 0;
+ List rolls = new ArrayList<>();
+
+ for (int i = 0; i < amount; i++) {
+ int roll = MyRandom.getRandom().nextInt(sides) + 1;
+ rolls.add(roll);
+ total += roll;
+ }
+
+ if (amount > 0) {
+ String message = Localizer.getInstance().getMessage("lblPlayerRolledResult", player, StringUtils.join(rolls, ", "));
+ player.getGame().getAction().notifyOfValue(sa, player, message, null);
+ }
+
+ // Ignore lowest rolls
+ if (advantage > 0) {
+ rolls.sort(null);
+ for (int i = advantage - 1; i >= 0; --i) {
+ total -= rolls.get(i);
+ rolls.remove(i);
+ }
+ }
+
+ // Run triggers
+ for (Integer roll : rolls) {
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Player, player);
+ runParams.put(AbilityKey.Result, roll);
+ player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
+ }
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.Player, player);
+ runParams.put(AbilityKey.Result, rolls);
+ player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDieOnce, runParams, false);
+
+ total += modifier;
+ if (sa.hasParam("ResultSVar")) {
+ host.setSVar(sa.getParam("ResultSVar"), Integer.toString(total));
+ }
+
+ Map diceAbilities = sa.getAdditionalAbilities();
+ SpellAbility resultAbility = null;
+ for (Map.Entry e: diceAbilities.entrySet()) {
+ String diceKey = e.getKey();
+ if (diceKey.contains("-")) {
+ String [] ranges = diceKey.split("-");
+ if (Integer.parseInt(ranges[0]) <= total && Integer.parseInt(ranges[1]) >= total) {
+ resultAbility = e.getValue();
+ break;
+ }
+ } else if (StringUtils.isNumeric(diceKey) && Integer.parseInt(diceKey) == total) {
+ resultAbility = e.getValue();
+ break;
+ }
+ }
+ if (resultAbility != null) {
+ AbilityUtils.resolve(resultAbility);
+ } else if (sa.hasAdditionalAbility("Else")) {
+ AbilityUtils.resolve(sa.getAdditionalAbility("Else"));
+ }
+ }
+
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityEffect#resolve(forge.card.spellability.SpellAbility)
*/
@@ -49,42 +138,13 @@ public class RollDiceEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
- int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Amt", "1"), sa);
+ int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Amount", "1"), sa);
int sides = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Sides", "6"), sa);
final PlayerCollection playersToRoll = getTargetPlayers(sa);
- for(Player player : playersToRoll) {
- int total = 0;
- List rolls = new ArrayList<>();
- for (int i = 0; i < amount; i++) {
- int roll = MyRandom.getRandom().nextInt(sides) + 1;
- rolls.add(roll);
-
- final Map runParams = AbilityKey.newMap();
- runParams.put(AbilityKey.Player, player);
- runParams.put(AbilityKey.Result, roll);
- player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
-
- total += roll;
- }
-
- if (amount > 0) {
- String message = Localizer.getInstance().getMessage("lblPlayerRolledResult", player, StringUtils.join(rolls, ", "));
- player.getGame().getAction().notifyOfValue(sa, player, message, null);
- }
-
- if (sa.hasParam("ResultSVar")) {
- host.setSVar(sa.getParam("ResultSVar"), ""+total);
- }
- if (sa.hasAdditionalAbility("OnDoubles") && rolls.get(0).equals(rolls.get(1))) {
- AbilityUtils.resolve(sa.getAdditionalAbility("OnDoubles"));
- }
- if (sa.hasAdditionalAbility("On"+total)) {
- AbilityUtils.resolve(sa.getAdditionalAbility("On"+total));
- } else if (sa.hasAdditionalAbility("Else")) {
- AbilityUtils.resolve(sa.getAdditionalAbility("Else"));
- }
+ for (Player player : playersToRoll) {
+ rollDice(sa, player, amount, sides);
}
}
-}
\ No newline at end of file
+}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java
index 70128e6bc29..c8028fa4a4b 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java
@@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameActionUtil;
+import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -86,9 +87,12 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
CardZoneTable table = new CardZoneTable();
Map cachedMap = Maps.newHashMap();
+ Map params = AbilityKey.newMap();
+ params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
+
for (Card sac : list) {
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
- if (game.getAction().sacrifice(sac, sa, table) != null) {
+ if (game.getAction().sacrifice(sac, sa, table, params) != null) {
if (remSacrificed) {
card.addRemembered(lKICopy);
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java
index 197509ff0dd..2b5dfdff324 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java
@@ -1,7 +1,10 @@
package forge.game.ability.effects;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.lang3.StringUtils;
@@ -55,25 +58,25 @@ public class SacrificeEffect extends SpellAbilityEffect {
}
} else if (sa.hasParam("CumulativeUpkeep")) {
GameEntityCounterTable table = new GameEntityCounterTable();
- card.addCounter(CounterEnumType.AGE, 1, activator, true, table);
+ card.addCounter(CounterEnumType.AGE, 1, activator, sa, true, table);
table.triggerCountersPutAll(game);
Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true);
Cost payCost = new Cost(ManaCost.ZERO, true);
int n = card.getCounters(CounterEnumType.AGE);
-
+
// multiply cost
for (int i = 0; i < n; ++i) {
payCost.add(cumCost);
}
-
+
sa.setCumulativeupkeep(true);
game.updateLastStateForCard(card);
-
+
StringBuilder sb = new StringBuilder();
sb.append("Cumulative upkeep for ").append(card);
-
+
boolean isPaid = activator.getController().payManaOptional(card, payCost, sa, sb.toString(), ManaPaymentPurpose.CumulativeUpkeep);
final Map runParams = AbilityKey.mapFromCard(card);
runParams.put(AbilityKey.CumulativeUpkeepPaid, isPaid);
@@ -90,6 +93,7 @@ public class SacrificeEffect extends SpellAbilityEffect {
final List tgts = getTargetPlayers(sa);
final boolean devour = sa.hasParam("Devour");
final boolean exploit = sa.hasParam("Exploit");
+ final boolean sacEachValid = sa.hasParam("SacEachValid");
String valid = sa.getParam("SacValid");
if (valid == null) {
@@ -106,42 +110,68 @@ public class SacrificeEffect extends SpellAbilityEffect {
final String remSVar = sa.getParam("RememberSacrificedSVar");
int countSacrificed = 0;
CardZoneTable table = new CardZoneTable();
+ Map params = AbilityKey.newMap();
+ params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
if (valid.equals("Self") && game.getZoneOf(card) != null) {
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
- if (game.getAction().sacrifice(card, sa, table) != null) {
+ if (game.getAction().sacrifice(card, sa, table, params) != null) {
countSacrificed++;
if (remSacrificed) {
card.addRemembered(card);
}
}
}
- }
- else {
+ } else {
CardCollectionView choosenToSacrifice = null;
for (final Player p : tgts) {
CardCollectionView battlefield = p.getCardsIn(ZoneType.Battlefield);
- CardCollectionView validTargets = AbilityUtils.filterListByType(battlefield, valid, sa);
- if (!destroy) {
- validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa));
- }
-
- if (sa.hasParam("Random")) {
- choosenToSacrifice = Aggregates.random(validTargets, Math.min(amount, validTargets.size()), new CardCollection());
- } else if (sa.hasParam("OptionalSacrifice") && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantSacrifice"))) {
- choosenToSacrifice = CardCollection.EMPTY;
+ if (sacEachValid) { // Sacrifice maximum permanents in any combination of types specified by SacValid
+ String [] validArray = valid.split(" & ");
+ String [] msgArray = msg.split(" & ");
+ List validTargetsList = new ArrayList<>(validArray.length);
+ for (String subValid : validArray) {
+ CardCollectionView validTargets = AbilityUtils.filterListByType(battlefield, subValid, sa);
+ validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa));
+ validTargetsList.add(new CardCollection(validTargets));
+ }
+ CardCollection chosenCards = new CardCollection();
+ for (int i = 0; i < validArray.length; ++i) {
+ CardCollection validTargets = validTargetsList.get(i);
+ if (validTargets.isEmpty()) continue;
+ if (validTargets.size() > 1 && i < validArray.length - 1) {
+ removeCandidates(validTargets, validTargetsList, new HashSet<>(), i + 1, 0, amount);
+ }
+ choosenToSacrifice = p.getController().choosePermanentsToSacrifice(sa, amount, amount, validTargets, msgArray[i]);
+ for (int j = i + 1; j < validArray.length; ++j) {
+ validTargetsList.get(j).removeAll(choosenToSacrifice);
+ }
+ chosenCards.addAll(choosenToSacrifice);
+ }
+ choosenToSacrifice = chosenCards;
} else {
- boolean isOptional = sa.hasParam("Optional");
- boolean isStrict = sa.hasParam("StrictAmount");
- int minTargets = isOptional ? 0 : amount;
- boolean notEnoughTargets = isStrict && validTargets.size() < minTargets;
-
- if (!notEnoughTargets) {
- choosenToSacrifice = destroy ?
- p.getController().choosePermanentsToDestroy(sa, minTargets, amount, validTargets, msg) :
- p.getController().choosePermanentsToSacrifice(sa, minTargets, amount, validTargets, msg);
- } else {
+ CardCollectionView validTargets = AbilityUtils.filterListByType(battlefield, valid, sa);
+ if (!destroy) {
+ validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa));
+ }
+
+ if (sa.hasParam("Random")) {
+ choosenToSacrifice = Aggregates.random(validTargets, Math.min(amount, validTargets.size()), new CardCollection());
+ } else if (sa.hasParam("OptionalSacrifice") && !p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantSacrifice"))) {
choosenToSacrifice = CardCollection.EMPTY;
+ } else {
+ boolean isOptional = sa.hasParam("Optional");
+ boolean isStrict = sa.hasParam("StrictAmount");
+ int minTargets = isOptional ? 0 : amount;
+ boolean notEnoughTargets = isStrict && validTargets.size() < minTargets;
+
+ if (!notEnoughTargets) {
+ choosenToSacrifice = destroy ?
+ p.getController().choosePermanentsToDestroy(sa, minTargets, amount, validTargets, msg) :
+ p.getController().choosePermanentsToSacrifice(sa, minTargets, amount, validTargets, msg);
+ } else {
+ choosenToSacrifice = CardCollection.EMPTY;
+ }
}
}
@@ -152,8 +182,8 @@ public class SacrificeEffect extends SpellAbilityEffect {
Map cachedMap = Maps.newHashMap();
for (Card sac : choosenToSacrifice) {
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
- boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, table) != null;
- boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table);
+ boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, table, params) != null;
+ boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table, params);
// Run Devour Trigger
if (devour) {
card.addDevoured(lKICopy);
@@ -229,4 +259,33 @@ public class SacrificeEffect extends SpellAbilityEffect {
return sb.toString();
}
+
+ private void removeCandidates(CardCollection validTargets, List validTargetsList, Set union, int index, int included, int amount) {
+ if (index >= validTargetsList.size()) {
+ if (union.size() <= included * amount) {
+ validTargets.removeAll(union);
+ }
+ return;
+ }
+
+ removeCandidates(validTargets, validTargetsList, union, index + 1, included, amount);
+
+
+ CardCollection candidate = validTargetsList.get(index);
+ if (candidate.isEmpty()) {
+ return;
+ }
+
+ if (union.isEmpty()) {
+ if (candidate.size() <= amount) {
+ validTargets.removeAll(candidate.asSet());
+ } else {
+ removeCandidates(validTargets, validTargetsList, candidate.asSet(), index + 1, included + 1, amount);
+ }
+ } else {
+ Set unionClone = new HashSet<>(union);
+ unionClone.addAll(candidate.asSet());
+ removeCandidates(validTargets, validTargetsList, unionClone, index + 1, included + 1, amount);
+ }
+ }
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java
index 1989de4d3df..aff51d34bbb 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java
@@ -160,7 +160,7 @@ public class SetStateEffect extends SpellAbilityEffect {
}
game.fireEvent(new GameEventCardStatsChanged(gameCard));
if (sa.hasParam("Mega")) {
- gameCard.addCounter(CounterEnumType.P1P1, 1, p, true, table);
+ gameCard.addCounter(CounterEnumType.P1P1, 1, p, sa, true, table);
}
if (remChanged) {
host.addRemembered(gameCard);
diff --git a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java
new file mode 100644
index 00000000000..33fb9613ce2
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java
@@ -0,0 +1,131 @@
+package forge.game.ability.effects;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.base.Predicates;
+
+import forge.StaticData;
+import forge.card.CardRulesPredicates;
+import forge.card.ICardFace;
+import forge.game.Game;
+import forge.game.ability.AbilityKey;
+import forge.game.ability.SpellAbilityEffect;
+import forge.game.card.Card;
+import forge.game.card.CardCollectionView;
+import forge.game.card.CounterType;
+import forge.game.event.GameEventCardCounters;
+import forge.game.player.Player;
+import forge.game.spellability.SpellAbility;
+import forge.game.trigger.Trigger;
+import forge.game.trigger.TriggerType;
+import forge.game.trigger.WrappedAbility;
+import forge.game.zone.ZoneType;
+import forge.item.PaperCard;
+import forge.util.Localizer;
+
+public class VentureEffect extends SpellAbilityEffect {
+
+ private Card getDungeonCard(SpellAbility sa, Player player) {
+ final Game game = player.getGame();
+
+ CardCollectionView commandCards = player.getCardsIn(ZoneType.Command);
+ for (Card card : commandCards) {
+ if (card.getType().isDungeon()) {
+ if (!card.isInLastRoom()) {
+ return card;
+ }
+ // If the current dungeon is already in last room, complete it first.
+ game.getAction().completeDungeon(player, card);
+ break;
+ }
+ }
+
+ // Create a new dugeon card chosen by player in command zone.
+ List dungeonCards = StaticData.instance().getVariantCards().getAllCards(
+ Predicates.compose(CardRulesPredicates.Presets.IS_DUNGEON, PaperCard.FN_GET_RULES));
+ List faces = new ArrayList<>();
+ for (PaperCard pc : dungeonCards) {
+ faces.add(pc.getRules().getMainPart());
+ }
+ String message = Localizer.getInstance().getMessage("lblChooseDungeon");
+ String chosen = player.getController().chooseCardName(sa, faces, message);
+ Card dungeon = Card.fromPaperCard(StaticData.instance().getVariantCards().getUniqueByName(chosen), player);
+
+ game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
+ game.getAction().moveTo(ZoneType.Command, dungeon, sa);
+ game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
+
+ return dungeon;
+ }
+
+ private String chooseNextRoom(SpellAbility sa, Player player, Card dungeon, String room) {
+ String nextRoomParam = "";
+ for (final Trigger t : dungeon.getTriggers()) {
+ SpellAbility roomSA = t.getOverridingAbility();
+ if (roomSA.getParam("RoomName").equals(room)) {
+ nextRoomParam = roomSA.getParam("NextRoomName");
+ break;
+ }
+ }
+ String [] nextRoomNames = nextRoomParam.split(",");
+ if (nextRoomNames.length > 1) {
+ List candidates = new ArrayList<>();
+ for (String nextRoomName : nextRoomNames) {
+ for (final Trigger t : dungeon.getTriggers()) {
+ SpellAbility roomSA = t.getOverridingAbility();
+ if (roomSA.getParam("RoomName").equals(nextRoomName)) {
+ candidates.add(new WrappedAbility(t, roomSA, player));
+ break;
+ }
+ }
+ }
+ final String title = Localizer.getInstance().getMessage("lblChooseRoom");
+ SpellAbility chosen = player.getController().chooseSingleSpellForEffect(candidates, sa, title, null);
+ return chosen.getParam("RoomName");
+ } else {
+ return nextRoomNames[0];
+ }
+ }
+
+ private void ventureIntoDungeon(SpellAbility sa, Player player) {
+ if (player.getVenturedThisTurn() >= 1 && player.hasKeyword("You can't venture into the dungeon more than once each turn.")) {
+ return;
+ }
+
+ final Game game = player.getGame();
+ Card dungeon = getDungeonCard(sa, player);
+ String room = dungeon.getCurrentRoom();
+ String nextRoom = null;
+
+ // Determine next room to venture into
+ if (room == null || room.isEmpty()) {
+ SpellAbility roomSA = dungeon.getTriggers().get(0).getOverridingAbility();
+ nextRoom = roomSA.getParam("RoomName");
+ } else {
+ nextRoom = chooseNextRoom(sa, player, dungeon, room);
+ }
+
+ dungeon.setCurrentRoom(nextRoom);
+ // TODO: Currently play the Add Counter sound, but maybe add soundeffect for marker?
+ game.fireEvent(new GameEventCardCounters(dungeon, CounterType.getType("LEVEL"), 0, 1));
+
+ // Run RoomEntered trigger
+ final Map runParams = AbilityKey.mapFromCard(dungeon);
+ runParams.put(AbilityKey.RoomName, nextRoom);
+ game.getTriggerHandler().runTrigger(TriggerType.RoomEntered, runParams, false);
+
+ player.incrementVenturedThisTurn();
+ }
+
+ @Override
+ public void resolve(SpellAbility sa) {
+ for (final Player p : getTargetPlayers(sa)) {
+ if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) {
+ ventureIntoDungeon(sa, p);
+ }
+ }
+ }
+
+}
diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java
index d2df0aa6b34..e86d3ffcaff 100644
--- a/forge-game/src/main/java/forge/game/card/Card.java
+++ b/forge-game/src/main/java/forge/game/card/Card.java
@@ -118,6 +118,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
private final Map changedCardKeywords = Maps.newTreeMap();
private final Map changedCardTraits = Maps.newTreeMap();
private final Map changedCardColors = Maps.newTreeMap();
+
+ private final Map changedCardTypesCharacterDefining = Maps.newTreeMap();
+ private final Map changedCardColorsCharacterDefining = Maps.newTreeMap();
+
private final NavigableMap clonedStates = Maps.newTreeMap();
private final NavigableMap textChangeStates = Maps.newTreeMap();
@@ -244,6 +248,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
private EvenOdd chosenEvenOdd = null;
private Direction chosenDirection = null;
private String chosenMode = "";
+ private String currentRoom = null;
private Card exiledWith = null;
private Player exiledBy = null;
@@ -1182,7 +1187,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public final boolean hasTrigger(final Trigger t) {
return currentState.hasTrigger(t);
}
-
public final boolean hasTrigger(final int id) {
return currentState.hasTrigger(id);
}
@@ -1205,8 +1209,15 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
public final int getXManaCostPaid() {
- if (getCastSA() != null) {
- Integer paid = getCastSA().getXManaCostPaid();
+ SpellAbility castSA;
+ if (getCopiedPermanent() != null) {
+ castSA = getCopiedPermanent().getCastSA();
+ }
+ else {
+ castSA = getCastSA();
+ }
+ if (castSA != null) {
+ Integer paid = castSA.getXManaCostPaid();
return paid == null ? 0 : paid;
}
return 0;
@@ -1324,21 +1335,21 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
return true;
}
- public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) {
- return addCounter(counterType, n, source, applyMultiplier, true, table);
+ public final int addCounter(final CounterType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, GameEntityCounterTable table) {
+ return addCounter(counterType, n, source, cause, applyMultiplier, true, table);
}
- public final int addCounterFireNoEvents(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) {
- return addCounter(counterType, n, source, applyMultiplier, false, table);
+ public final int addCounterFireNoEvents(final CounterType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, GameEntityCounterTable table) {
+ return addCounter(counterType, n, source, cause, applyMultiplier, false, table);
}
- public final int addCounter(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) {
- return addCounter(counterType, n, source, applyMultiplier, true, table);
+ public final int addCounter(final CounterEnumType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, GameEntityCounterTable table) {
+ return addCounter(counterType, n, source, cause, applyMultiplier, true, table);
}
- public final int addCounterFireNoEvents(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) {
- return addCounter(counterType, n, source, applyMultiplier, false, table);
+ public final int addCounterFireNoEvents(final CounterEnumType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, GameEntityCounterTable table) {
+ return addCounter(counterType, n, source, cause, applyMultiplier, false, table);
}
@Override
- public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
+ public int addCounter(final CounterType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
int addAmount = n;
if(addAmount <= 0 || !canReceiveCounters(counterType)) {
// As per rule 107.1b
@@ -1346,6 +1357,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
final Map repParams = AbilityKey.mapFromAffected(this);
repParams.put(AbilityKey.Source, source);
+ repParams.put(AbilityKey.Cause, cause);
repParams.put(AbilityKey.CounterType, counterType);
repParams.put(AbilityKey.CounterNum, addAmount);
repParams.put(AbilityKey.EffectOnly, applyMultiplier);
@@ -1436,7 +1448,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public boolean removeCounterTimestamp(CounterType counterType) {
return removeCounterTimestamp(counterType, true);
}
-
public boolean removeCounterTimestamp(CounterType counterType, boolean updateView) {
Long old = counterTypeTimestamps.remove(counterType);
if (old != null) {
@@ -1522,6 +1533,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
}
+ public final int sumAllCounters() {
+ int count = 0;
+ for (final Integer value2 : counters.values()) {
+ count += value2;
+ }
+ return count;
+ }
+
public final String getSVar(final String var) {
return currentState.getSVar(var);
}
@@ -1552,18 +1571,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
currentState.removeSVar(var);
}
- public final int sumAllCounters() {
- int count = 0;
- for (final Integer value2 : counters.values()) {
- count += value2;
- }
- return count;
- }
-
public final int getTurnInZone() {
return turnInZone;
}
-
public final void setTurnInZone(final int turn) {
turnInZone = turn;
}
@@ -1571,7 +1581,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public final Player getTurnInController() {
return turnInController;
}
-
public final void setTurnInController(final Player p) {
turnInController = p;
}
@@ -1579,7 +1588,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public final void setManaCost(final ManaCost s) {
currentState.setManaCost(s);
}
-
public final ManaCost getManaCost() {
return currentState.getManaCost();
}
@@ -1721,6 +1729,23 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
view.updateChosenMode(this);
}
+ public String getCurrentRoom() {
+ return currentRoom;
+ }
+ public void setCurrentRoom(String room) {
+ currentRoom = room;
+ view.updateCurrentRoom(this);
+ }
+ public boolean isInLastRoom() {
+ for (final Trigger t : getTriggers()) {
+ SpellAbility sa = t.getOverridingAbility();
+ if (sa.getParam("RoomName").equals(currentRoom) && !sa.hasParam("NextRoom")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public boolean hasChosenName() {
return chosenName != null;
}
@@ -2051,7 +2076,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")
- || keyword.startsWith("Encore") || keyword.startsWith("Mutate")) {
+ || keyword.startsWith("Encore") || keyword.startsWith("Mutate") || keyword.startsWith("Dungeon")) {
// keyword parsing takes care of adding a proper description
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
sbLong.append(getName()).append(" can't be blocked ");
@@ -2283,12 +2308,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
if (stAb.isSecondary() ||
!stAb.getParam("Mode").equals("CantBlockBy") ||
stAb.isSuppressed() || !stAb.checkConditions() ||
- !stAb.hasParam("ValidAttacker")) {
+ !stAb.hasParam("ValidAttacker") ||
+ (stAb.hasParam("ValidBlocker") && stAb.getParam("ValidBlocker").equals("Creature.Self"))) {
continue;
}
final Card host = stAb.getHostCard();
if (isValid(stAb.getParam("ValidAttacker").split(","), host.getController(), host, stAb)) {
- String currentName = (host.getName());
+ String currentName = host.getName();
String desc1 = TextUtil.fastReplace(stAb.toString(), "CARDNAME", currentName);
String desc = TextUtil.fastReplace(desc1,"NICKNAME", currentName.split(",")[0]);
if (host.getEffectSource() != null) {
@@ -3371,7 +3397,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
public Iterable getChangedCardTypes() {
- return Iterables.unmodifiableIterable(changedCardTypes.values());
+ return Iterables.unmodifiableIterable(Iterables.concat(changedCardTypesCharacterDefining.values(), changedCardTypes.values()));
}
public Map getChangedCardTypesMap() {
@@ -3379,10 +3405,17 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
public boolean clearChangedCardTypes() {
- if (changedCardTypes.isEmpty())
- return false;
+ boolean changed = false;
+
+ if (!changedCardTypesCharacterDefining.isEmpty())
+ changed = true;
+ changedCardTypesCharacterDefining.clear();
+
+ if (!changedCardTypes.isEmpty())
+ changed = true;
changedCardTypes.clear();
- return true;
+
+ return changed;
}
public boolean clearChangedCardKeywords() {
@@ -3393,10 +3426,17 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
public boolean clearChangedCardColors() {
- if (changedCardColors.isEmpty())
- return false;
+ boolean changed = false;
+
+ if (!changedCardTypesCharacterDefining.isEmpty())
+ changed = true;
+ changedCardTypesCharacterDefining.clear();
+
+ if (!changedCardColors.isEmpty())
+ changed = true;
changedCardColors.clear();
- return true;
+
+ return changed;
}
public Map getChangedCardKeywords() {
@@ -3411,18 +3451,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
final boolean removeSuperTypes, final boolean removeCardTypes, final boolean removeSubTypes,
final boolean removeLandTypes, final boolean removeCreatureTypes, final boolean removeArtifactTypes,
final boolean removeEnchantmentTypes,
- final long timestamp) {
- addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes, removeLandTypes,
- removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, timestamp, true);
- }
+ final long timestamp, final boolean updateView, final boolean cda) {
- public final void addChangedCardTypes(final CardType addType, final CardType removeType,
- final boolean removeSuperTypes, final boolean removeCardTypes, final boolean removeSubTypes,
- final boolean removeLandTypes, final boolean removeCreatureTypes, final boolean removeArtifactTypes,
- final boolean removeEnchantmentTypes,
- final long timestamp, final boolean updateView) {
-
- changedCardTypes.put(timestamp, new CardChangedType(
+ (cda ? changedCardTypesCharacterDefining : changedCardTypes).put(timestamp, new CardChangedType(
addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes));
if (updateView) {
@@ -3434,17 +3465,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
final boolean removeSuperTypes, final boolean removeCardTypes, final boolean removeSubTypes,
final boolean removeLandTypes, final boolean removeCreatureTypes, final boolean removeArtifactTypes,
final boolean removeEnchantmentTypes,
- final long timestamp) {
- addChangedCardTypes(types, removeTypes, removeSuperTypes, removeCardTypes, removeSubTypes,
- removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes,
- timestamp, true);
- }
-
- public final void addChangedCardTypes(final Iterable types, final Iterable removeTypes,
- final boolean removeSuperTypes, final boolean removeCardTypes, final boolean removeSubTypes,
- final boolean removeLandTypes, final boolean removeCreatureTypes, final boolean removeArtifactTypes,
- final boolean removeEnchantmentTypes,
- final long timestamp, final boolean updateView) {
+ final long timestamp, final boolean updateView, final boolean cda) {
CardType addType = null;
CardType removeType = null;
if (types != null) {
@@ -3457,7 +3478,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes,
- timestamp, updateView);
+ timestamp, updateView, cda);
}
public final void removeChangedCardTypes(final long timestamp) {
@@ -3465,21 +3486,26 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
}
public final void removeChangedCardTypes(final long timestamp, final boolean updateView) {
- if (changedCardTypes.remove(timestamp) != null && updateView) {
+ boolean removed = false;
+ removed |= changedCardTypes.remove(timestamp) != null;
+ removed |= changedCardTypesCharacterDefining.remove(timestamp) != null;
+ if (removed && updateView) {
currentState.getView().updateType(currentState);
}
}
- public final void addColor(final String s, final boolean addToColors, final long timestamp) {
- changedCardColors.put(timestamp, new CardColor(s, addToColors, timestamp));
+ public final void addColor(final String s, final boolean addToColors, final long timestamp, final boolean cda) {
+ (cda ? changedCardColorsCharacterDefining : changedCardColors).put(timestamp, new CardColor(s, addToColors, timestamp));
currentState.getView().updateColors(this);
currentState.getView().updateHasChangeColors(!getChangedCardColors().isEmpty());
}
public final void removeColor(final long timestampIn) {
- final CardColor removeCol = changedCardColors.remove(timestampIn);
+ boolean removed = false;
+ removed |= changedCardColors.remove(timestampIn) != null;
+ removed |= changedCardColorsCharacterDefining.remove(timestampIn) != null;
- if (removeCol != null) {
+ if (removed) {
currentState.getView().updateColors(this);
currentState.getView().updateHasChangeColors(!getChangedCardColors().isEmpty());
}
@@ -3496,9 +3522,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
return determineColor(currentState);
}
public final ColorSet determineColor(CardState state) {
- final Iterable colorList = changedCardColors.values();
byte colors = state.getColor();
- for (final CardColor cc : colorList) {
+ for (final CardColor cc : Iterables.concat(changedCardColorsCharacterDefining.values(), changedCardColors.values())) {
if (cc.isAdditional()) {
colors |= cc.getColorMask();
} else {
@@ -4015,7 +4040,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public final boolean hasKeyword(Keyword keyword) {
return hasKeyword(keyword, currentState);
}
-
public final boolean hasKeyword(Keyword key, CardState state) {
return state.hasKeyword(key);
}
@@ -4024,7 +4048,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public final boolean hasKeyword(String keyword) {
return hasKeyword(keyword, currentState);
}
-
public final boolean hasKeyword(String keyword, CardState state) {
if (keyword.startsWith("HIDDEN")) {
keyword = keyword.substring(7);
@@ -4049,8 +4072,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp) {
addChangedCardKeywords(keywords, removeKeywords, removeAllKeywords, removeIntrinsicKeywords, timestamp, true);
}
-
-
public final void addChangedCardKeywords(final List keywords, final List removeKeywords,
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp, final boolean updateView) {
// if the key already exists - merge entries
@@ -4104,7 +4125,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
public final KeywordsChange removeChangedCardKeywords(final long timestamp) {
return removeChangedCardKeywords(timestamp, true);
}
-
public final KeywordsChange removeChangedCardKeywords(final long timestamp, final boolean updateView) {
KeywordsChange change = changedCardKeywords.remove(timestamp);
if (change != null && updateView) {
@@ -4222,7 +4242,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
changedTextTypes.add(timestamp, originalWord, newWord);
if (getType().hasSubtype(originalWord)) {
addChangedCardTypes(CardType.parse(newWord, true), CardType.parse(originalWord, true),
- false, false, false, false, false, false, false, timestamp);
+ false, false, false, false, false, false, false, timestamp, true, false);
}
updateKeywordsChangedText(timestamp);
updateChangedText();
@@ -4604,7 +4624,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
getGame().getTriggerHandler().registerActiveTrigger(this, false);
getGame().getTriggerHandler().runTrigger(TriggerType.PhaseIn, runParams, false);
}
-
+
game.updateLastStateForCard(this);
return true;
@@ -4752,8 +4772,15 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
final String excR = incR[1];
final String[] exRs = excR.split("\\+"); // Exclusive Restrictions are ...
for (String exR : exRs) {
- if (!hasProperty(exR, sourceController, source, spellAbility)) {
- return testFailed;
+ if (exR.startsWith("!")) {
+ exR = exR.substring(1);
+ if (hasProperty(exR, sourceController, source, spellAbility)) {
+ return testFailed;
+ }
+ } else {
+ if (!hasProperty(exR, sourceController, source, spellAbility)) {
+ return testFailed;
+ }
}
}
}
@@ -5215,7 +5242,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
|| source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT));
if (wither) { // 120.3d
- addCounter(CounterType.get(CounterEnumType.M1M1), damageIn, source.getController(), true, counterTable);
+ addCounter(CounterType.get(CounterEnumType.M1M1), damageIn, source.getController(), null, true, counterTable);
damageType = DamageType.M1M1Counters;
}
else { // 120.3e
@@ -5401,7 +5428,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
bestowTimestamp = getGame().getNextTimestamp();
addChangedCardTypes(new CardType(Collections.singletonList("Aura"), true),
new CardType(Collections.singletonList("Creature"), true),
- false, false, false, false, false, false, true, bestowTimestamp, updateView);
+ false, false, false, false, false, false, true, bestowTimestamp, updateView, false);
addChangedCardKeywords(Collections.singletonList("Enchant creature"), Lists.newArrayList(),
false, false, bestowTimestamp, updateView);
}
@@ -6553,7 +6580,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars {
changed = true;
}
} else {
- changed |= addCounter(ct, e.getValue(), e.getRowKey(), true, table) > 0;
+ changed |= addCounter(ct, e.getValue(), e.getRowKey(), null, true, table) > 0;
}
}
return changed;
diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java
index b35d00512e4..b5de258fe41 100644
--- a/forge-game/src/main/java/forge/game/card/CardFactory.java
+++ b/forge-game/src/main/java/forge/game/card/CardFactory.java
@@ -152,7 +152,7 @@ public class CardFactory {
}
final String finalColors = tmp;
- c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), c.getTimestamp());
+ c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), c.getTimestamp(), false);
}
c.clearControllers();
@@ -231,7 +231,6 @@ public class CardFactory {
// Would like to move this away from in-game entities
String originalPicture = cp.getImageKey(false);
- //System.out.println(c.getName() + " -> " + originalPicture);
c.setImageKey(originalPicture);
c.setToken(cp.isToken());
@@ -535,7 +534,6 @@ public class CardFactory {
}
public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki) {
-
if (from.getTargetRestrictions() != null) {
to.setTargetRestrictions(from.getTargetRestrictions());
}
@@ -563,7 +561,7 @@ public class CardFactory {
to.setConditions((SpellAbilityCondition) from.getConditions().copy());
}
- // do this after other abilties are copied
+ // do this after other abilities are copied
if (p != null) {
to.setActivatingPlayer(p, lki);
}
diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java
index f1aaeaae705..569f9821f62 100644
--- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java
+++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java
@@ -19,6 +19,7 @@ package forge.game.card;
import java.util.Arrays;
import java.util.EnumSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -519,28 +520,6 @@ public class CardFactoryUtil {
return types.size();
}
- /**
- *
- * getNeededXDamage.
- *
- *
- * @param ability
- * a {@link forge.game.spellability.SpellAbility} object.
- * @return a int.
- */
- public static int getNeededXDamage(final SpellAbility ability) {
- // when targeting a creature, make sure the AI won't overkill on X
- // damage
- final Card target = ability.getTargetCard();
- int neededDamage = -1;
-
- if ((target != null)) {
- neededDamage = target.getNetToughness() - target.getDamage();
- }
-
- return neededDamage;
- }
-
/**
* Adds the ability factory abilities.
*
@@ -1295,7 +1274,7 @@ public class CardFactoryUtil {
sbTrig.append("Living Weapon (").append(inst.getReminderText()).append(")");
final StringBuilder sbGerm = new StringBuilder();
- sbGerm.append("DB$ Token | TokenAmount$ 1 | TokenScript$ b_0_0_germ |TokenOwner$ You | RememberTokens$ True");
+ sbGerm.append("DB$ Token | TokenAmount$ 1 | TokenScript$ b_0_0_phyrexian_germ |TokenOwner$ You | RememberTokens$ True");
final SpellAbility saGerm = AbilityFactory.getAbility(sbGerm.toString(), card);
@@ -1398,7 +1377,7 @@ public class CardFactoryUtil {
inst.addTrigger(triggerDrawn);
} else if (keyword.startsWith("Modular")) {
final String abStr = "DB$ PutCounter | ValidTgts$ Artifact.Creature | " +
- "TgtPrompt$ Select target artifact creature | CounterType$ P1P1 | CounterNum$ ModularX";
+ "TgtPrompt$ Select target artifact creature | CounterType$ P1P1 | CounterNum$ ModularX | Modular$ True";
String trigStr = "Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard" +
" | OptionalDecider$ TriggeredCardController | TriggerController$ TriggeredCardController" +
@@ -1839,6 +1818,41 @@ public class CardFactoryUtil {
inst.addTrigger(parsedUpkeepTrig);
inst.addTrigger(parsedSacTrigger);
+ } else if (keyword.startsWith("Dungeon")) {
+ final List abs = Arrays.asList(keyword.substring("Dungeon:".length()).split(","));
+ final Map saMap = new LinkedHashMap<>();
+
+ for(String ab : abs) {
+ saMap.put(ab, AbilityFactory.getAbility(card, ab));
+ }
+ for (SpellAbility sa : saMap.values()) {
+ String roomName = sa.getParam("RoomName");
+ StringBuilder trigStr = new StringBuilder("Mode$ RoomEntered | TriggerZones$ Command");
+ trigStr.append(" | ValidCard$ Card.Self | ValidRoom$ ").append(roomName);
+ trigStr.append(" | TriggerDescription$ ").append(roomName).append(" — ").append(sa.getDescription());
+ if (sa.hasParam("NextRoom")) {
+ boolean first = true;
+ StringBuilder nextRoomParam = new StringBuilder();
+ trigStr.append(" (→ ");
+ for (String nextRoomSVar : sa.getParam("NextRoom").split(",")) {
+ if (!first) {
+ trigStr.append(" or ");
+ nextRoomParam.append(",");
+ }
+ String nextRoomName = saMap.get(nextRoomSVar).getParam("RoomName");
+ trigStr.append(nextRoomName);
+ nextRoomParam.append(nextRoomName);
+ first = false;
+ }
+ trigStr.append(")");
+ sa.putParam("NextRoomName", nextRoomParam.toString());
+ }
+
+ // Need to set intrinsic to false here, else the first room won't get triggered
+ final Trigger t = TriggerHandler.parseTrigger(trigStr.toString(), card, false);
+ t.setOverridingAbility(sa);
+ inst.addTrigger(t);
+ }
} else if (keyword.startsWith("Ward")) {
final String[] k = keyword.split(":");
final Cost cost = new Cost(k[1], false);
@@ -3087,7 +3101,7 @@ public class CardFactoryUtil {
int counters = AbilityUtils.calculateAmount(c, k[1], this);
GameEntityCounterTable table = new GameEntityCounterTable();
- c.addCounter(CounterEnumType.TIME, counters, getActivatingPlayer(), true, table);
+ c.addCounter(CounterEnumType.TIME, counters, getActivatingPlayer(), this, true, table);
table.triggerCountersPutAll(game);
String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(),"has suspended", c.getName(), "with", String.valueOf(counters),"time counters on it.");
diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java
index 4d2f4291871..17441dd1cb1 100644
--- a/forge-game/src/main/java/forge/game/card/CardProperty.java
+++ b/forge-game/src/main/java/forge/game/card/CardProperty.java
@@ -47,12 +47,13 @@ public class CardProperty {
// by name can also have color names, so needs to happen before colors.
if (property.startsWith("named")) {
- String name = TextUtil.fastReplace(property.substring(5), ";", ","); // for some legendary cards
+ String name = TextUtil.fastReplace(property.substring(5), ";", ","); // workaround for card name with ","
if (!card.sharesNameWith(name)) {
return false;
}
} else if (property.startsWith("notnamed")) {
- if (card.sharesNameWith(property.substring(8))) {
+ String name = TextUtil.fastReplace(property.substring(8), ";", ","); // workaround for card name with ","
+ if (card.sharesNameWith(name)) {
return false;
}
} else if (property.startsWith("sameName")) {
diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java
index f6181cdf596..69bee0fc573 100644
--- a/forge-game/src/main/java/forge/game/card/CardUtil.java
+++ b/forge-game/src/main/java/forge/game/card/CardUtil.java
@@ -221,9 +221,9 @@ public final class CardUtil {
newCopy.getCurrentState().copyFrom(in.getState(in.getFaceupCardStateName()), true);
if (in.isFaceDown()) {
- // prevent StackDescription from revealing face
- newCopy.setName(in.toString());
newCopy.turnFaceDownNoUpdate();
+ // prevent StackDescription from revealing face
+ newCopy.updateStateForView();
}
if (in.isAdventureCard() && in.getFaceupCardStateName().equals(CardStateName.Original)) {
@@ -275,7 +275,7 @@ public final class CardUtil {
newCopy.addRemembered(in.getRemembered());
newCopy.addImprintedCards(in.getImprintedCards());
- for(Table.Cell cl : in.getEtbCounters()) {
+ for (Table.Cell cl : in.getEtbCounters()) {
newCopy.addEtbCounter(cl.getColumnKey(), cl.getValue(), cl.getRowKey());
}
diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java
index 8c143e87aac..c6c09df0127 100644
--- a/forge-game/src/main/java/forge/game/card/CardView.java
+++ b/forge-game/src/main/java/forge/game/card/CardView.java
@@ -389,6 +389,13 @@ public class CardView extends GameEntityView {
set(TrackableProperty.ChosenMode, c.getChosenMode());
}
+ public String getCurrentRoom() {
+ return get(TrackableProperty.CurrentRoom);
+ }
+ void updateCurrentRoom(Card c) {
+ set(TrackableProperty.CurrentRoom, c.getCurrentRoom());
+ }
+
private String getRemembered() {
return get(TrackableProperty.Remembered);
}
diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java
index aaddc15cd7b..857b0738076 100644
--- a/forge-game/src/main/java/forge/game/combat/Combat.java
+++ b/forge-game/src/main/java/forge/game/combat/Combat.java
@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
@@ -59,7 +59,7 @@ import forge.util.collect.FCollectionView;
*
* Combat class.
*
- *
+ *
* @author Forge
* @version $Id$
*/
@@ -78,7 +78,7 @@ public class Combat {
private Map lkiCache = Maps.newHashMap();
private CardDamageMap damageMap = new CardDamageMap();
- // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW)
+ // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW)
private CardCollection combatantsThatDealtFirstStrikeDamage = new CardCollection();
public Combat(final Player attacker) {
@@ -110,7 +110,7 @@ public class Combat {
for (Entry entry : combat.blockedBands.entries()) {
blockedBands.put(bandsMap.get(entry.getKey()), map.map(entry.getValue()));
}
-
+
for (Entry entry : combat.attackersOrderedForDamageAssignment.entrySet()) {
attackersOrderedForDamageAssignment.put(map.map(entry.getKey()), map.mapCollection(entry.getValue()));
}
@@ -277,7 +277,7 @@ public class Combat {
}
}
return null;
- }
+ }
public final Player getDefenderPlayerByAttacker(final Card c) {
GameEntity defender = getDefenderByAttacker(c);
@@ -300,10 +300,10 @@ public class Combat {
return ab;
}
}
- CombatLki lki = lkiCache.get(c);
+ CombatLki lki = lkiCache.get(c);
return lki == null || !lki.isAttacker ? null : lki.getFirstBand();
}
-
+
public final AttackingBand getBandOfAttackerNotNull(final Card c) {
AttackingBand band = getBandOfAttacker(c);
if (band == null) {
@@ -314,8 +314,8 @@ public class Combat {
public final List getAttackingBands() {
return Lists.newArrayList(attackedByBands.values());
- }
-
+ }
+
/**
* Checks if a card is attacking, returns true if the card was attacking when it left the battlefield
*/
@@ -333,7 +333,7 @@ public class Combat {
}
return false;
}
-
+
/**
* Checks if a card is currently attacking, returns false if the card is not currently attacking, even if its LKI was.
*/
@@ -355,7 +355,7 @@ public class Combat {
}
public final CardCollection getBlockers(final Card card) {
- // If requesting the ordered blocking list pass true, directly.
+ // If requesting the ordered blocking list pass true, directly.
AttackingBand band = getBandOfAttacker(card);
Collection blockers = blockedBands.get(band);
return blockers == null ? new CardCollection() : new CardCollection(blockers);
@@ -456,7 +456,7 @@ public class Combat {
}
/** If there are multiple blockers, the Attacker declares the Assignment Order */
- public void orderBlockersForDamageAssignment() { // this method performs controller's role
+ public void orderBlockersForDamageAssignment() { // this method performs controller's role
List> blockersNeedManualOrdering = new ArrayList<>();
for (AttackingBand band : attackedByBands.values()) {
if (band.isEmpty()) continue;
@@ -475,15 +475,15 @@ public class Combat {
}
}
}
-
- // brought this out of iteration on bands to avoid concurrency problems
+
+ // brought this out of iteration on bands to avoid concurrency problems
for (Pair pair : blockersNeedManualOrdering) {
orderBlockersForDamageAssignment(pair.getLeft(), pair.getRight());
}
}
-
+
/** If there are multiple blockers, the Attacker declares the Assignment Order */
- public void orderBlockersForDamageAssignment(Card attacker, CardCollection blockers) { // this method performs controller's role
+ public void orderBlockersForDamageAssignment(Card attacker, CardCollection blockers) { // this method performs controller's role
if (blockers.size() <= 1) {
blockersOrderedForDamageAssignment.put(attacker, new CardCollection(blockers));
return;
@@ -513,7 +513,7 @@ public class Combat {
* Add a blocker to the damage assignment order of an attacker. The
* relative order of creatures already blocking the attacker may not be
* changed. Performs controller's role.
- *
+ *
* @param attacker the attacking creature.
* @param blocker the blocking creature.
*/
@@ -527,7 +527,7 @@ public class Combat {
blockersOrderedForDamageAssignment.put(attacker, orderedBlockers);
}
}
-
+
public void orderAttackersForDamageAssignment() { // this method performs controller's role
// If there are multiple blockers, the Attacker declares the Assignment Order
for (final Card blocker : getAllBlockers()) {
@@ -538,7 +538,7 @@ public class Combat {
public void orderAttackersForDamageAssignment(Card blocker) { // this method performs controller's role
CardCollection attackers = getAttackersBlockedBy(blocker);
// They need a reverse map here: Blocker => List
-
+
Player blockerCtrl = blocker.getController();
CardCollection orderedAttacker = attackers.size() <= 1 ? attackers : blockerCtrl.getController().orderAttackers(blocker, attackers);
@@ -549,11 +549,11 @@ public class Combat {
// removes references to this attacker from all indices and orders
public void unregisterAttacker(final Card c, AttackingBand ab) {
blockersOrderedForDamageAssignment.remove(c);
-
+
Collection blockers = blockedBands.get(ab);
if (blockers != null) {
for (Card b : blockers) {
- // Clear removed attacker from assignment order
+ // Clear removed attacker from assignment order
if (attackersOrderedForDamageAssignment.containsKey(b)) {
attackersOrderedForDamageAssignment.get(b).remove(c);
}
@@ -655,7 +655,7 @@ public class Combat {
}
return true;
}
-
+
// Call this method right after turn-based action of declare blockers has been performed
public final void fireTriggersForUnblockedAttackers(final Game game) {
boolean bFlag = false;
@@ -698,7 +698,7 @@ public class Combat {
if (!dealDamageThisPhase(blocker, firstStrikeDamage)) {
continue;
}
-
+
if (firstStrikeDamage) {
combatantsThatDealtFirstStrikeDamage.add(blocker);
}
@@ -737,7 +737,7 @@ public class Combat {
if (!dealDamageThisPhase(attacker, firstStrikeDamage)) {
continue;
}
-
+
if (firstStrikeDamage) {
combatantsThatDealtFirstStrikeDamage.add(attacker);
}
@@ -750,7 +750,7 @@ public class Combat {
if (damageDealt <= 0) {
continue;
}
-
+
AttackingBand band = getBandOfAttacker(attacker);
if (band == null) {
continue;
@@ -764,6 +764,13 @@ public class Combat {
CardTranslation.getTranslatedName(attacker.getName()))));
boolean trampler = attacker.hasKeyword(Keyword.TRAMPLE);
orderedBlockers = blockersOrderedForDamageAssignment.get(attacker);
+ boolean assignCombatDamageToCreature = ((orderedBlockers == null || orderedBlockers.isEmpty()) &&
+ getDefendersCreatures().size() > 0 &&
+ attacker.hasKeyword("If CARDNAME is unblocked, you may have it assign its combat damage to " +
+ "a creature defending player controls.") &&
+ attacker.getController().getController().confirmAction(null, null,
+ Localizer.getInstance().getMessage("lblAssignCombatDamageToCreature",
+ CardTranslation.getTranslatedName(attacker.getName()))));
if (divideCombatDamageAsChoose) {
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
orderedBlockers = getDefendersCreatures();
@@ -790,7 +797,11 @@ public class Combat {
defender = getDefenderPlayerByAttacker(attacker);
}
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
- if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked
+ if (assignCombatDamageToCreature) {
+ Card chosen = attacker.getController().getController().chooseCardsForEffect(getDefendersCreatures(),
+ null, Localizer.getInstance().getMessage("lblChooseCreature"), 1, 1, false, null).get(0);
+ damageMap.put(attacker, chosen, damageDealt);
+ } else if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked
damageMap.put(attacker, defender, damageDealt);
} // No damage happens if blocked but no blockers left
}
@@ -827,7 +838,7 @@ public class Combat {
} // for
return assignedDamage;
}
-
+
private final boolean dealDamageThisPhase(Card combatant, boolean firstStrikeDamage) {
// During first strike damage, double strike and first strike deal damage
// During regular strike damage, double strike and anyone who hasn't dealt damage deal damage
@@ -885,7 +896,7 @@ public class Combat {
public boolean isPlayerAttacked(Player who) {
for (GameEntity defender : attackedByBands.keySet()) {
Card defenderAsCard = defender instanceof Card ? (Card)defender : null;
- if ((null != defenderAsCard && defenderAsCard.getController() != who) ||
+ if ((null != defenderAsCard && defenderAsCard.getController() != who) ||
(null == defenderAsCard && defender != who)) {
continue; // defender is not related to player 'who'
}
diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java
index 88ad8460560..d83cb74157a 100644
--- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java
+++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java
@@ -45,7 +45,7 @@ public class CostAdjustment {
}
Cost result = cost.copy();
-
+
boolean isStateChangeToFaceDown = false;
if (sa.isSpell() && sa.isCastFaceDown()) {
// Turn face down to apply cost modifiers correctly
@@ -60,7 +60,7 @@ public class CostAdjustment {
result.add(new Cost(ManaCost.get(n), false));
}
}
-
+
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack));
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Command));
@@ -68,7 +68,7 @@ public class CostAdjustment {
cardsOnBattlefield.add(host);
}
final List raiseAbilities = Lists.newArrayList();
-
+
// Sort abilities to apply them in proper order
for (Card c : cardsOnBattlefield) {
for (final StaticAbility stAb : c.getStaticAbilities()) {
@@ -94,11 +94,10 @@ public class CostAdjustment {
}
return result;
}
-
+
private static void applyRaise(final Cost cost, final SpellAbility sa, final StaticAbility st) {
final Card hostCard = st.getHostCard();
- final Card card = sa.getHostCard();
-
+
if (!checkRequirement(sa, st)) {
return;
}
@@ -121,7 +120,7 @@ public class CostAdjustment {
String amount = st.getParam("Amount");
if ("Escalate".equals(amount)) {
SpellAbility sub = sa;
- while(sub != null) {
+ while (sub != null) {
if (sub.getDirectSVars().containsKey("CharmOrder")) {
count++;
}
@@ -138,7 +137,7 @@ public class CostAdjustment {
count = Integer.parseInt(amount);
} else {
if (st.hasParam("AffectedAmount")) {
- count = AbilityUtils.calculateAmount(card, amount, st);
+ count = AbilityUtils.calculateAmount(hostCard, st.hasSVar(amount) ? st.getSVar(amount) : amount, sa);
} else {
count = AbilityUtils.calculateAmount(hostCard, amount, st);
}
@@ -148,17 +147,17 @@ public class CostAdjustment {
// Amount 1 as default
count = 1;
}
- for(int i = 0; i < count; ++i) {
+ for (int i = 0; i < count; ++i) {
cost.add(part);
}
}
-
+
// If cardsToDelveOut is null, will immediately exile the delved cards and remember them on the host card.
// Otherwise, will return them in cardsToDelveOut and the caller is responsible for doing the above.
public static final void adjust(ManaCostBeingPaid cost, final SpellAbility sa, CardCollection cardsToDelveOut, boolean test) {
final Game game = sa.getActivatingPlayer().getGame();
final Card originalCard = sa.getHostCard();
-
+
if (sa.isTrigger()) {
return;
}
@@ -204,7 +203,7 @@ public class CostAdjustment {
}
// need to reduce generic extra because of 2 hybrid mana
cost.decreaseGenericMana(sumGeneric);
-
+
if (sa.isSpell() && sa.isOffering()) { // cost reduction from offerings
adjustCostByOffering(cost, sa);
}
@@ -263,7 +262,7 @@ public class CostAdjustment {
}
Map convokedCards = sa.getActivatingPlayer().getController().chooseCardsForConvokeOrImprovise(sa, cost.toManaCost(), untappedCards, improvise);
-
+
// Convoked creats are tapped here, setting up their taps triggers,
// Then again when payment is done(In InputPayManaCost.done()) with suppression of Taps triggers.
// This is to make sure that triggers go off at the right time
@@ -296,35 +295,30 @@ public class CostAdjustment {
final CardCollectionView toSacList = sa.getHostCard().getController().getController().choosePermanentsToSacrifice(sa, 0, 1, canOffer, offeringType);
- if (!toSacList.isEmpty()) {
- toSac = toSacList.getFirst();
- }
- else {
+ if (toSacList.isEmpty()) {
return;
}
-
+ toSac = toSacList.getFirst();
+
cost.subtractManaCost(toSac.getManaCost());
-
+
sa.setSacrificedAsOffering(toSac);
toSac.setUsedToPay(true); //stop it from interfering with mana input
}
private static void adjustCostByEmerge(final ManaCostBeingPaid cost, final SpellAbility sa) {
-
Card toSac = null;
CardCollectionView canEmerge = CardLists.filter(sa.getActivatingPlayer().getCreaturesInPlay(), CardPredicates.canBeSacrificedBy(sa));
final CardCollectionView toSacList = sa.getHostCard().getController().getController().choosePermanentsToSacrifice(sa, 0, 1, canEmerge, "Creature");
- if (!toSacList.isEmpty()) {
- toSac = toSacList.getFirst();
- }
- else {
+ if (toSacList.isEmpty()) {
return;
}
-
+ toSac = toSacList.getFirst();
+
cost.decreaseGenericMana(toSac.getCMC());
-
+
sa.setSacrificedAsEmerge(toSac);
toSac.setUsedToPay(true); //stop it from interfering with mana input
}
diff --git a/forge-game/src/main/java/forge/game/cost/CostPartMana.java b/forge-game/src/main/java/forge/game/cost/CostPartMana.java
index cdc958d7962..19d7d609d93 100644
--- a/forge-game/src/main/java/forge/game/cost/CostPartMana.java
+++ b/forge-game/src/main/java/forge/game/cost/CostPartMana.java
@@ -112,13 +112,11 @@ public class CostPartMana extends CostPart {
@Override
public boolean isUndoable() { return true; }
-
@Override
public final String toString() {
return cost.toString();
}
-
@Override
public final boolean canPay(final SpellAbility ability, final Player payer) {
// For now, this will always return true. But this should probably be
diff --git a/forge-game/src/main/java/forge/game/cost/CostPutCounter.java b/forge-game/src/main/java/forge/game/cost/CostPutCounter.java
index 914db9d175e..f6f58c8894e 100644
--- a/forge-game/src/main/java/forge/game/cost/CostPutCounter.java
+++ b/forge-game/src/main/java/forge/game/cost/CostPutCounter.java
@@ -177,7 +177,7 @@ public class CostPutCounter extends CostPartWithList {
@Override
protected Card doPayment(SpellAbility ability, Card targetCard){
final Integer i = this.convertAmount();
- targetCard.addCounter(this.getCounter(), i, ability.getActivatingPlayer(), ability.getRootAbility().isTrigger(), counterTable);
+ targetCard.addCounter(this.getCounter(), i, ability.getActivatingPlayer(), null, ability.getRootAbility().isTrigger(), counterTable);
return targetCard;
}
diff --git a/forge-game/src/main/java/forge/game/cost/CostSacrifice.java b/forge-game/src/main/java/forge/game/cost/CostSacrifice.java
index 65a536890d8..f9166c185c1 100644
--- a/forge-game/src/main/java/forge/game/cost/CostSacrifice.java
+++ b/forge-game/src/main/java/forge/game/cost/CostSacrifice.java
@@ -126,7 +126,7 @@ public class CostSacrifice extends CostPartWithList {
@Override
protected Card doPayment(SpellAbility ability, Card targetCard) {
// no table there, it is already handled by CostPartWithList
- return targetCard.getGame().getAction().sacrifice(targetCard, ability, null);
+ return targetCard.getGame().getAction().sacrifice(targetCard, ability, null, null);
}
/* (non-Javadoc)
diff --git a/forge-game/src/main/java/forge/game/cost/PaymentDecision.java b/forge-game/src/main/java/forge/game/cost/PaymentDecision.java
index 743a4160d2e..bb0e7c2bb51 100644
--- a/forge-game/src/main/java/forge/game/cost/PaymentDecision.java
+++ b/forge-game/src/main/java/forge/game/cost/PaymentDecision.java
@@ -58,7 +58,6 @@ public class PaymentDecision {
return res;
}
-
public static PaymentDecision number(int c) {
return new PaymentDecision(c);
}
@@ -77,7 +76,6 @@ public class PaymentDecision {
return new PaymentDecision(null, manas, null, null, null);
}
-
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
diff --git a/forge-game/src/main/java/forge/game/keyword/Hexproof.java b/forge-game/src/main/java/forge/game/keyword/Hexproof.java
index e6a6fce1834..bdd15dd5ca2 100644
--- a/forge-game/src/main/java/forge/game/keyword/Hexproof.java
+++ b/forge-game/src/main/java/forge/game/keyword/Hexproof.java
@@ -32,6 +32,5 @@ public class Hexproof extends KeywordInstance {
}
return false;
}
-
-
+
}
diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java
index d2b3dd41127..fe9dd404b4e 100644
--- a/forge-game/src/main/java/forge/game/keyword/Keyword.java
+++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java
@@ -100,7 +100,7 @@ public enum Keyword {
LANDWALK("Landwalk", KeywordWithType.class, false, "This creature is unblockable as long as defending player controls a %s."),
LEVEL_UP("Level up", KeywordWithCost.class, false, "%s: Put a level counter on this. Level up only as a sorcery."),
LIFELINK("Lifelink", SimpleKeyword.class, true, "Damage dealt by this creature also causes its controller to gain that much life."),
- LIVING_WEAPON("Living weapon", SimpleKeyword.class, true, "When this Equipment enters the battlefield, create a 0/0 black Germ creature token, then attach this to it."),
+ LIVING_WEAPON("Living Weapon", SimpleKeyword.class, true, "When this Equipment enters the battlefield, create a 0/0 black Phyrexian Germ creature token, then attach this to it."),
MADNESS("Madness", KeywordWithCost.class, false, "If you discard this card, discard it into exile. When you do, cast it for %s or put it into your graveyard."),
MELEE("Melee", SimpleKeyword.class, false, "Whenever this creature attacks, it gets +1/+1 until end of turn for each opponent you attacked this combat."),
MENTOR("Mentor", SimpleKeyword.class, false, "Whenever this creature attacks, put a +1/+1 counter on target attacking creature with lesser power."),
diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java
index 58fb8f511a0..d740b3640e9 100644
--- a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java
+++ b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java
@@ -22,10 +22,9 @@ import io.sentry.event.BreadcrumbBuilder;
public abstract class KeywordInstance> implements KeywordInterface {
private Keyword keyword;
private String original;
-
-
+
private boolean hidden;
-
+
private List triggers = Lists.newArrayList();
private List replacements = Lists.newArrayList();
private List abilities = Lists.newArrayList();
@@ -53,7 +52,7 @@ public abstract class KeywordInstance> implements K
public String getReminderText() {
String result = formatReminderText(keyword.reminderText);
Matcher m = Pattern.compile("\\{(\\w):(.+?)\\}").matcher(result);
-
+
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, Lang.nounWithNumeral(m.group(1), m.group(2)));
@@ -75,7 +74,7 @@ public abstract class KeywordInstance> implements K
}
protected abstract void parse(String details);
protected abstract String formatReminderText(String reminderText);
-
+
/*
* (non-Javadoc)
@@ -181,7 +180,7 @@ public abstract class KeywordInstance> implements K
public final void addTrigger(final Trigger trg) {
triggers.add(trg);
}
-
+
/*
* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#addReplacement(forge.game.replacement.ReplacementEffect)
@@ -197,7 +196,7 @@ public abstract class KeywordInstance> implements K
public final void addSpellAbility(final SpellAbility s) {
abilities.add(s);
}
-
+
/*
* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#addStaticAbility(forge.game.staticability.StaticAbility)
@@ -205,9 +204,7 @@ public abstract class KeywordInstance> implements K
public final void addStaticAbility(final StaticAbility st) {
staticAbilities.add(st);
}
-
-
-
+
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#getHidden()
*/
@@ -222,7 +219,7 @@ public abstract class KeywordInstance> implements K
public void setHidden(boolean val) {
hidden = val;
}
-
+
/*
* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#getTriggers()
diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java
index 9974a79bebf..b80e6f3732b 100644
--- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java
+++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java
@@ -293,7 +293,7 @@ public class PhaseHandler implements java.io.Serializable {
// all Saga get Lore counter at the begin of pre combat
for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) {
if (c.getType().hasSubtype("Saga")) {
- c.addCounter(CounterEnumType.LORE, 1, playerTurn, false, table);
+ c.addCounter(CounterEnumType.LORE, 1, playerTurn, null, false, table);
}
}
table.triggerCountersPutAll(game);
@@ -505,7 +505,7 @@ public class PhaseHandler implements java.io.Serializable {
eventEndCombat = new GameEventCombatEnded(attackers, blockers);
}
endCombat();
- for(Player player : game.getPlayers()) {
+ for (Player player : game.getPlayers()) {
player.resetCombatantsThisCombat();
}
diff --git a/forge-game/src/main/java/forge/game/phase/Untap.java b/forge-game/src/main/java/forge/game/phase/Untap.java
index 1fec36dc06b..c6894964c1f 100644
--- a/forge-game/src/main/java/forge/game/phase/Untap.java
+++ b/forge-game/src/main/java/forge/game/phase/Untap.java
@@ -87,7 +87,6 @@ public class Untap extends Phase {
* @return a boolean.
*/
public static boolean canUntap(final Card c) {
-
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")
|| c.hasKeyword("This card doesn't untap during your next untap step.")
|| c.hasKeyword("This card doesn't untap during your next two untap steps.")
diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java
index fd673898f49..600fbfc3153 100644
--- a/forge-game/src/main/java/forge/game/player/Player.java
+++ b/forge-game/src/main/java/forge/game/player/Player.java
@@ -193,7 +193,8 @@ public class Player extends GameEntity implements Comparable {
private boolean activateLoyaltyAbilityThisTurn = false;
private boolean tappedLandForManaThisTurn = false;
private int attackersDeclaredThisTurn = 0;
- private PlayerCollection attackedOpponentsThisTurn = new PlayerCollection();
+ private int venturedThisTurn = 0;
+ private List completedDungeons = new ArrayList<>();
private final Map zones = Maps.newEnumMap(ZoneType.class);
private final Map adjustLandPlays = Maps.newHashMap();
@@ -430,7 +431,6 @@ public class Player extends GameEntity implements Comparable {
public boolean isOpponentOf(Player other) {
return other != this && other != null && (other.teamNumber < 0 || other.teamNumber != teamNumber);
}
-
public boolean isOpponentOf(String other) {
Player otherPlayer = null;
for (Player p : game.getPlayers()) {
@@ -474,9 +474,7 @@ public class Player extends GameEntity implements Comparable {
public final boolean gainLife(int lifeGain, final Card source) {
return gainLife(lifeGain, source, null);
}
-
public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) {
-
// Run any applicable replacement effects.
final Map repParams = AbilityKey.mapFromAffected(this);
repParams.put(AbilityKey.LifeGained, lifeGain);
@@ -544,7 +542,6 @@ public class Player extends GameEntity implements Comparable {
public final int loseLife(final int toLose) {
return loseLife(toLose, false);
}
-
public final int loseLife(final int toLose, final boolean manaBurn) {
int lifeLost = 0;
if (!canLoseLife()) {
@@ -867,12 +864,11 @@ public class Player extends GameEntity implements Comparable {
return true;
}
- public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) {
- return addCounter(counterType, n, source, applyMultiplier, true, table);
+ public final int addCounter(final CounterType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, GameEntityCounterTable table) {
+ return addCounter(counterType, n, source, cause, applyMultiplier, true, table);
}
-
@Override
- public int addCounter(CounterType counterType, int n, final Player source, boolean applyMultiplier, boolean fireEvents, GameEntityCounterTable table) {
+ public int addCounter(CounterType counterType, int n, final Player source, final SpellAbility cause, boolean applyMultiplier, boolean fireEvents, GameEntityCounterTable table) {
int addAmount = n;
if (addAmount <= 0 || !canReceiveCounters(counterType)) {
// Can't add negative or 0 counters, bail out now
@@ -881,6 +877,7 @@ public class Player extends GameEntity implements Comparable {
final Map repParams = AbilityKey.mapFromAffected(this);
repParams.put(AbilityKey.Source, source);
+ repParams.put(AbilityKey.Cause, cause);
repParams.put(AbilityKey.CounterType, counterType);
repParams.put(AbilityKey.CounterNum, addAmount);
repParams.put(AbilityKey.EffectOnly, applyMultiplier);
@@ -980,7 +977,7 @@ public class Player extends GameEntity implements Comparable {
}
public final void addPoisonCounters(final int num, final Card source, GameEntityCounterTable table) {
int oldPoison = getCounters(CounterEnumType.POISON);
- addCounter(CounterEnumType.POISON, num, source.getController(), false, true, table);
+ addCounter(CounterEnumType.POISON, num, source.getController(), null, false, true, table);
if (oldPoison != getCounters(CounterEnumType.POISON)) {
game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num));
@@ -999,7 +996,6 @@ public class Player extends GameEntity implements Comparable {
public final void addChangedKeywords(final String[] addKeywords, final String[] removeKeywords, final Long timestamp) {
addChangedKeywords(ImmutableList.copyOf(addKeywords), ImmutableList.copyOf(removeKeywords), timestamp);
}
-
public final void addChangedKeywords(final List addKeywords, final List removeKeywords, final Long timestamp) {
// if the key already exists - merge entries
KeywordsChange cks = null;
@@ -1049,8 +1045,6 @@ public class Player extends GameEntity implements Comparable {
public final void removeKeyword(final String keyword) {
removeKeyword(keyword, true);
}
-
-
public final void removeKeyword(final String keyword, final boolean allInstances) {
boolean keywordRemoved = false;
@@ -1081,7 +1075,6 @@ public class Player extends GameEntity implements Comparable {
public final boolean hasKeyword(final String keyword) {
return keywords.contains(keyword);
}
-
@Override
public final boolean hasKeyword(final Keyword keyword) {
return keywords.contains(keyword);
@@ -1171,7 +1164,6 @@ public class Player extends GameEntity implements Comparable {
public boolean hasProtectionFrom(final Card source, final boolean checkSBA) {
return hasProtectionFrom(source, checkSBA, false);
}
-
public boolean hasProtectionFrom(final Card source, final boolean checkSBA, final boolean damageSource) {
final boolean colorlessDamage = damageSource && source.hasKeyword("Colorless Damage Source");
for (KeywordInterface ki : keywords) {
@@ -1637,6 +1629,7 @@ public class Player extends GameEntity implements Comparable {
public final int getNumDiscardedThisTurn() {
return numDiscardedThisTurn;
}
+
public final void resetNumDiscardedThisTurn() {
numDiscardedThisTurn = 0;
}
@@ -1762,7 +1755,7 @@ public class Player extends GameEntity implements Comparable {
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming) {
// Dakkon Blackblade Avatar will use a similar effect
if (canPlayLand(land, ignoreZoneAndTiming)) {
- this.playLandNoCheck(land);
+ playLandNoCheck(land);
return true;
}
@@ -1775,8 +1768,8 @@ public class Player extends GameEntity implements Comparable {
if (land.isFaceDown()) {
land.turnFaceUp(null);
}
- final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
game.copyLastState();
+ final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
game.updateLastStateForCard(c);
// play a sound
@@ -1924,7 +1917,6 @@ public class Player extends GameEntity implements Comparable {
public boolean hasTappedLandForManaThisTurn() {
return tappedLandForManaThisTurn;
}
-
public void setTappedLandForManaThisTurn(boolean tappedLandForManaThisTurn) {
this.tappedLandForManaThisTurn = tappedLandForManaThisTurn;
}
@@ -1956,6 +1948,26 @@ public class Player extends GameEntity implements Comparable {
attackersDeclaredThisTurn = 0;
}
+ public final int getVenturedThisTurn() {
+ return venturedThisTurn;
+ }
+ public final void incrementVenturedThisTurn() {
+ venturedThisTurn++;
+ }
+ public final void resetVenturedThisTurn() {
+ venturedThisTurn = 0;
+ }
+
+ public final List getCompletedDungeons() {
+ return completedDungeons;
+ }
+ public void addCompletedDungeon(Card dungeon) {
+ completedDungeons.add(dungeon);
+ }
+ public void resetCompletedDungeons() {
+ completedDungeons.clear();
+ }
+
public final void altWinBySpellEffect(final String sourceName) {
if (cantWin()) {
System.out.println("Tried to win, but currently can't.");
@@ -2003,7 +2015,6 @@ public class Player extends GameEntity implements Comparable {
boolean isAnyOppLoseProof = false;
for (Player p : game.getPlayers()) {
if (p == this || p.getOutcome() != null) {
-
continue; // except self and already dead
}
isAnyOppLoseProof |= p.hasKeyword("You can't lose the game.");
@@ -2082,7 +2093,6 @@ public class Player extends GameEntity implements Comparable {
public final boolean hasRevolt() {
return revolt;
}
-
public final void setRevolt(final boolean val) {
revolt = val;
}
@@ -2142,7 +2152,6 @@ public class Player extends GameEntity implements Comparable {
public final void setLibrarySearched(final int l) {
numLibrarySearchedOwn = l;
}
-
public final int getLibrarySearched() {
return numLibrarySearchedOwn;
}
@@ -2166,7 +2175,6 @@ public class Player extends GameEntity implements Comparable {
@Override
public final boolean isValid(final String restriction, final Player sourceController, final Card source, CardTraitBase spellAbility) {
-
final String[] incR = restriction.split("\\.", 2);
if (incR[0].equals("Opponent")) {
@@ -2319,6 +2327,7 @@ public class Player extends GameEntity implements Comparable {
public final void resetSpellCastThisGame() {
spellsCastThisGame = 0;
}
+
public final int getLifeGainedByTeamThisTurn() {
return lifeGainedByTeamThisTurn;
}
@@ -2492,6 +2501,7 @@ public class Player extends GameEntity implements Comparable {
resetSacrificedThisTurn();
clearAssignedDamage();
resetAttackersDeclaredThisTurn();
+ resetVenturedThisTurn();
setRevolt(false);
resetProwl();
setSpellsCastLastTurn(getSpellsCastThisTurn());
@@ -2646,7 +2656,6 @@ public class Player extends GameEntity implements Comparable {
public int getStartingHandSize() {
return startingHandSize;
}
-
public void setStartingHandSize(int shs) {
startingHandSize = shs;
}
@@ -2686,7 +2695,6 @@ public class Player extends GameEntity implements Comparable {
* Puts my currently active planes, if any, at the bottom of my planar deck.
*/
public void leaveCurrentPlane() {
-
final Map runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Cards, new CardCollection(currentPlanes));
game.getTriggerHandler().runTrigger(TriggerType.PlaneswalkedFrom, runParams, false);
@@ -2745,11 +2753,9 @@ public class Player extends GameEntity implements Comparable {
public CardCollectionView getInboundTokens() {
return inboundTokens;
}
-
public void addInboundToken(Card c) {
inboundTokens.add(c);
}
-
public void removeInboundToken(Card c) {
inboundTokens.remove(c);
}
@@ -2790,7 +2796,6 @@ public class Player extends GameEntity implements Comparable {
ColorSet identity = ColorSet.fromMask(ci);
return identity;
}
-
public ColorSet getNotCommanderColorID() {
if (commanders.isEmpty()) {
return null;
@@ -2803,7 +2808,6 @@ public class Player extends GameEntity implements Comparable {
Integer cast = commanderCast.get(commander);
return cast == null ? 0 : cast.intValue();
}
-
public void incCommanderCast(Card commander) {
commanderCast.put(commander, getCommanderCast(commander) + 1);
getView().updateCommanderCast(this, commander);
@@ -2841,9 +2845,11 @@ public class Player extends GameEntity implements Comparable {
public void setExtraTurnCount(final int val) {
view.setExtraTurnCount(val);
}
+
public void setHasPriority(final boolean val) {
view.setHasPriority(val);
}
+
public boolean isAI() {
return view.isAI();
}
@@ -3136,7 +3142,6 @@ public class Player extends GameEntity implements Comparable {
public CardCollectionView getLostOwnership() {
return lostOwnership;
}
-
public CardCollectionView getGainedOwnership() {
return gainedOwnership;
}
@@ -3208,6 +3213,7 @@ public class Player extends GameEntity implements Comparable {
this.updateZoneForView(com);
}
}
+
public void updateKeywordCardAbilityText() {
if(getKeywordCard() == null)
return;
@@ -3251,10 +3257,10 @@ public class Player extends GameEntity implements Comparable {
keywordEffect = null;
}
}
+
public boolean hasBlessing() {
return blessingEffect != null;
}
-
public void setBlessing(boolean bless) {
// no need to to change
if ((blessingEffect != null) == bless) {
@@ -3356,7 +3362,6 @@ public class Player extends GameEntity implements Comparable {
getView().updateAdditionalVote(this);
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
}
-
public void removeAdditionalVote(long timestamp) {
if (additionalVotes.remove(timestamp) != null) {
getView().updateAdditionalVote(this);
@@ -3419,7 +3424,6 @@ public class Player extends GameEntity implements Comparable {
public Set getControlVote() {
return controlVotes;
}
-
public void setControlVote(Set value) {
controlVotes.clear();
controlVotes.addAll(value);
diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java
index 6a94b3e4ccb..a11be5b4fdd 100644
--- a/forge-game/src/main/java/forge/game/player/PlayerController.java
+++ b/forge-game/src/main/java/forge/game/player/PlayerController.java
@@ -48,9 +48,9 @@ import forge.item.PaperCard;
import forge.util.ITriggerEvent;
import forge.util.collect.FCollectionView;
-/**
+/**
* A prototype for player controller class
- *
+ *
* Handles phase skips for now.
*/
public abstract class PlayerController {
@@ -109,21 +109,22 @@ public abstract class PlayerController {
public abstract Map assignCombatDamage(Card attacker, CardCollectionView blockers, int damageDealt, GameEntity defender, boolean overrideOrder);
public abstract Map divideShield(Card effectSource, Map affected, int shieldAmount);
+ public abstract Map specifyManaCombo(SpellAbility sa, ColorSet colorSet, int manaAmount, boolean different);
public abstract Integer announceRequirements(SpellAbility ability, String announce);
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
public abstract CardCollectionView choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional);
- public abstract boolean chooseTargetsFor(SpellAbility currentAbility); // this is bad a function for it assigns targets to sa inside its body
+ public abstract boolean chooseTargetsFor(SpellAbility currentAbility); // this is bad a function for it assigns targets to sa inside its body
// Specify a target of a spell (Spellskite)
public abstract Pair chooseTarget(SpellAbility sa, List> allTargets);
- // Q: why is there min/max and optional at once? A: This is to handle cases like 'choose 3 to 5 cards or none at all'
+ // Q: why is there min/max and optional at once? A: This is to handle cases like 'choose 3 to 5 cards or none at all'
public abstract CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map params);
-
+
public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title, Map params) { return chooseSingleEntityForEffect(optionList, null, sa, title, false, null, params); }
- public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title, boolean isOptional, Map params) { return chooseSingleEntityForEffect(optionList, null, sa, title, isOptional, null, params); }
+ public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title, boolean isOptional, Map params) { return chooseSingleEntityForEffect(optionList, null, sa, title, isOptional, null, params); }
public abstract T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player relatedPlayer, Map params);
public abstract List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, int num, Map params);
@@ -209,7 +210,7 @@ public abstract class PlayerController {
public final boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { return chooseBinary(sa, question, kindOfChoice, (Boolean) null); }
public abstract boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultChioce);
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map params) { return chooseBinary(sa, question, kindOfChoice); }
-
+
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap);
@@ -260,7 +261,7 @@ public abstract class PlayerController {
public abstract String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message);
public abstract String chooseCardName(SpellAbility sa, List faces, String message);
- // better to have this odd method than those if playerType comparison in ChangeZone
+ // better to have this odd method than those if playerType comparison in ChangeZone
public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider);
public abstract List chooseCardsForZoneChange(ZoneType destination, List origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider);
diff --git a/forge-game/src/main/java/forge/game/player/PlayerProperty.java b/forge-game/src/main/java/forge/game/player/PlayerProperty.java
index f28ca0692d4..2d56c9676e1 100644
--- a/forge-game/src/main/java/forge/game/player/PlayerProperty.java
+++ b/forge-game/src/main/java/forge/game/player/PlayerProperty.java
@@ -17,7 +17,6 @@ import forge.util.TextUtil;
public class PlayerProperty {
public static boolean playerHasProperty(Player player, String property, Player sourceController, Card source, CardTraitBase spellAbility) {
-
Game game = player.getGame();
if (property.equals("You")) {
if (!player.equals(sourceController)) {
@@ -382,7 +381,7 @@ public class PlayerProperty {
return false;
}
} else if (property.equals("castSpellThisTurn")) {
- if (player.getSpellsCastThisTurn() > 0) {
+ if (player.getSpellsCastThisTurn() == 0) {
return false;
}
} else if (property.equals("attackedWithCreaturesThisTurn")) {
diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java b/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java
index 3721aa0e7d9..f3dfdfd8bf9 100644
--- a/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java
+++ b/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java
@@ -54,6 +54,10 @@ public class ReplaceAddCounter extends ReplacementEffect {
return false;
}
+ if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
+ return false;
+ }
+
if (hasParam("ValidCounterType")) {
String type = getParam("ValidCounterType");
if (CounterType.getType(type) != runParams.get(AbilityKey.CounterType)) {
diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceDiscard.java b/forge-game/src/main/java/forge/game/replacement/ReplaceDiscard.java
index 8caf443f447..42baa247551 100644
--- a/forge-game/src/main/java/forge/game/replacement/ReplaceDiscard.java
+++ b/forge-game/src/main/java/forge/game/replacement/ReplaceDiscard.java
@@ -44,7 +44,6 @@ public class ReplaceDiscard extends ReplacementEffect {
*/
@Override
public boolean canReplace(Map runParams) {
-
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Affected))) {
return false;
}
diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceMoved.java b/forge-game/src/main/java/forge/game/replacement/ReplaceMoved.java
index 7ee18b00973..4d9b3bbb264 100644
--- a/forge-game/src/main/java/forge/game/replacement/ReplaceMoved.java
+++ b/forge-game/src/main/java/forge/game/replacement/ReplaceMoved.java
@@ -28,7 +28,6 @@ public class ReplaceMoved extends ReplacementEffect {
*/
@Override
public boolean canReplace(Map runParams) {
-
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Affected))) {
return false;
}
diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java
index dc963073153..dd397fb70ca 100644
--- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java
+++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java
@@ -281,7 +281,7 @@ public class ReplacementHandler {
// Updated Replacements need to be logged elsewhere because its otherwise in the wrong order
if (res != ReplacementResult.Updated) {
String message = chosenRE.getDescription();
- if ( !StringUtils.isEmpty(message))
+ if (!StringUtils.isEmpty(message))
if (chosenRE.getHostCard() != null) {
message = TextUtil.fastReplace(message, "CARDNAME", chosenRE.getHostCard().getName());
}
@@ -410,6 +410,7 @@ public class ReplacementHandler {
final GameEntity target = et.getKey();
int playerIndex = (target instanceof Player ? players.indexOf(((Player) target)) :
players.indexOf(((Card) target).getController()));
+ if (playerIndex == -1) continue;
Map>> replaceCandidateMap = replaceDamageList.get(playerIndex);
for (Map.Entry e : et.getValue().entrySet()) {
Card source = e.getKey();
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
index da47da4ceba..cb1ed0c530b 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
@@ -385,7 +385,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
manaPart = manaPart0;
}
-
// Spell, and Ability, and other Ability objects override this method
public abstract boolean canPlay();
@@ -765,6 +764,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
resetTriggeringObjects();
resetTriggerRemembered();
+ if (isActivatedAbility()) {
+ setXManaCostPaid(null);
+ }
+
// reset last state when finished resolving
setLastStateBattlefield(CardCollection.EMPTY);
setLastStateGraveyard(CardCollection.EMPTY);
@@ -804,7 +807,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return description;
}
public void setDescription(final String s) {
- originalDescription = s;
+ originalDescription = TextUtil.fastReplace(s, "VERT", "|");
description = originalDescription;
}
@@ -957,7 +960,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return this.isAlternativeCost(AlternativeCost.Foretold);
}
-
/**
* @return the aftermath
*/
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
index 4ba13945094..524d41b5f53 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
@@ -111,7 +111,6 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
ability.resetPaidHash();
splicedCards = sa.getSplicedCards();
- // TODO getXManaCostPaid should be on the SA, not the Card
xManaPaid = sa.getXManaCostPaid();
// Triggering info
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
index 41def75cf00..f3d5d417ab7 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
@@ -299,7 +299,6 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
* @return true, if successful
*/
public final boolean applyAbility(final String mode, final Card card, final SpellAbility spellAbility) {
-
// don't apply the ability if it hasn't got the right mode
if (!getParam("Mode").equals(mode)) {
return false;
@@ -334,7 +333,6 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
}
public final boolean applyAbility(String mode, Card card, CounterType type) {
-
// don't apply the ability if it hasn't got the right mode
if (!getParam("Mode").equals(mode)) {
return false;
@@ -353,7 +351,6 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
}
public final boolean applyAbility(String mode, Player player, CounterType type) {
-
// don't apply the ability if it hasn't got the right mode
if (!getParam("Mode").equals(mode)) {
return false;
@@ -400,7 +397,6 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
* @return true, if successful
*/
public final boolean applyAbility(final String mode, final Card card, final GameEntity target) {
-
// don't apply the ability if it hasn't got the right mode
if (!getParam("Mode").equals(mode)) {
return false;
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java
index 9c84816a495..f9d75340e3e 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java
@@ -57,6 +57,9 @@ public class StaticAbilityCantBeCast {
}
public static boolean cantBeActivatedAbility(final SpellAbility spell, final Card card, final Player activator) {
+ if (spell.isTrigger()) {
+ return false;
+ }
final Game game = activator.getGame();
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
@@ -98,7 +101,6 @@ public class StaticAbilityCantBeCast {
* @return true, if successful
*/
public static boolean applyCantBeCastAbility(final StaticAbility stAb, final SpellAbility spell, final Card card, final Player activator) {
-
if (!stAb.matchesValidParam("ValidCard", card)) {
return false;
}
@@ -153,7 +155,6 @@ public class StaticAbilityCantBeCast {
* @return true, if successful
*/
public static boolean applyCantBeActivatedAbility(final StaticAbility stAb, final SpellAbility spellAbility, final Card card, final Player activator) {
-
if (!stAb.matchesValidParam("ValidCard", card)) {
return false;
}
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
index 75f26c24433..fb835169e84 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
@@ -844,12 +844,12 @@ public final class StaticAbilityContinuous {
if ((addTypes != null) || (removeTypes != null)) {
affectedCard.addChangedCardTypes(addTypes, removeTypes, removeSuperTypes, removeCardTypes,
removeSubTypes, removeLandTypes, removeCreatureTypes, removeArtifactTypes,
- removeEnchantmentTypes, hostCard.getTimestamp());
+ removeEnchantmentTypes, hostCard.getTimestamp(), true, stAb.hasParam("CharacteristicDefining"));
}
// add colors
if (addColors != null) {
- affectedCard.addColor(addColors, !overwriteColors, hostCard.getTimestamp());
+ affectedCard.addColor(addColors, !overwriteColors, hostCard.getTimestamp(), stAb.hasParam("CharacteristicDefining"));
}
if (layer == StaticAbilityLayer.RULES) {
@@ -966,6 +966,13 @@ public final class StaticAbilityContinuous {
final Player controller = hostCard.getController();
if (stAb.hasParam("CharacteristicDefining")) {
+ if (stAb.hasParam("ExcludeZone")) {
+ for (ZoneType zt : ZoneType.listValueOf(stAb.getParam("ExcludeZone"))) {
+ if (hostCard.isInZone(zt)) {
+ return CardCollection.EMPTY;
+ }
+ }
+ }
return new CardCollection(hostCard); // will always be the card itself
}
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java
index 3db32174d63..4164631b830 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java
@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
+import forge.game.card.CardCollectionView;
import forge.game.card.CardZoneTable;
import forge.game.spellability.SpellAbility;
@@ -22,8 +23,17 @@ public class StaticAbilityPanharmonicon {
public static int handlePanharmonicon(final Game game, final Trigger t, final Map runParams) {
int n = 0;
+ CardCollectionView cardList = null;
+ // currently only used for leave the battlefield trigger
+ if (runParams.containsKey(AbilityKey.LastStateBattlefield)) {
+ cardList = (CardCollectionView) runParams.get(AbilityKey.LastStateBattlefield);
+ }
+ if (cardList == null) {
+ cardList = t.getMode() == TriggerType.ChangesZone && "Battlefield".equals(t.getParam("Origin")) ? game.getLastStateBattlefield() : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES);
+ }
+
// Checks only the battlefield, as those effects only work from there
- for (final Card ca : t.getMode() == TriggerType.ChangesZone ? game.getLastStateBattlefield() : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
+ for (final Card ca : cardList) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.getParam("Mode").equals(MODE) || stAb.isSuppressed() || !stAb.checkConditions()) {
continue;
diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java
index 0ba078ab88d..186e8357ff5 100644
--- a/forge-game/src/main/java/forge/game/trigger/Trigger.java
+++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java
@@ -340,6 +340,12 @@ public abstract class Trigger extends TriggerReplacementBase {
}
}
+ if (hasParam("ResolvedLimit")) {
+ if (this.getOverridingAbility().getResolvedThisTurn() >= Integer.parseInt(getParam("ResolvedLimit"))) {
+ return false;
+ }
+ }
+
if (!meetsCommonRequirements(this.mapParams))
return false;
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerCompletedDungeon.java b/forge-game/src/main/java/forge/game/trigger/TriggerCompletedDungeon.java
new file mode 100644
index 00000000000..8312a3d6c8e
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerCompletedDungeon.java
@@ -0,0 +1,35 @@
+package forge.game.trigger;
+
+import java.util.Map;
+
+import forge.game.ability.AbilityKey;
+import forge.game.card.Card;
+import forge.game.spellability.SpellAbility;
+import forge.util.Localizer;
+
+public class TriggerCompletedDungeon extends Trigger {
+
+ public TriggerCompletedDungeon(final Map params, final Card host, final boolean intrinsic) {
+ super(params, host, intrinsic);
+ }
+
+ @Override
+ public final boolean performTest(final Map runParams) {
+ if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public final void setTriggeringObjects(final SpellAbility sa, Map runParams) {
+ sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player);
+ }
+
+ public String getImportantStackObjects(SpellAbility sa) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Localizer.getInstance().getMessage("lblPlayer")).append(": ").append(sa.getTriggeringObject(AbilityKey.Player));
+ return sb.toString();
+ }
+}
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java b/forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java
index f7a107009d4..ab1762f3b1d 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java
@@ -73,7 +73,6 @@ public class TriggerDiscarded extends Trigger {
return true;
}
-
/** {@inheritDoc} */
@Override
public final void setTriggeringObjects(final SpellAbility sa, Map runParams) {
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerEnteredRoom.java b/forge-game/src/main/java/forge/game/trigger/TriggerEnteredRoom.java
new file mode 100644
index 00000000000..1f7e1a8e414
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerEnteredRoom.java
@@ -0,0 +1,42 @@
+package forge.game.trigger;
+
+import java.util.Map;
+
+import forge.game.ability.AbilityKey;
+import forge.game.card.Card;
+import forge.game.spellability.SpellAbility;
+
+public class TriggerEnteredRoom extends Trigger {
+
+ public TriggerEnteredRoom(final Map params, final Card host, final boolean intrinsic) {
+ super(params, host, intrinsic);
+ }
+
+ @Override
+ public final boolean performTest(final Map runParams) {
+ if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
+ return false;
+ }
+
+ if (!matchesValidParam("ValidRoom", runParams.get(AbilityKey.RoomName))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public final void setTriggeringObjects(final SpellAbility sa, Map runParams) {
+ sa.setTriggeringObjectsFrom(runParams, AbilityKey.RoomName);
+ }
+
+ public String getImportantStackObjects(SpellAbility sa) {
+ Object roomName = sa.getTriggeringObject(AbilityKey.RoomName);
+ if (roomName != null) {
+ StringBuilder sb = new StringBuilder("Room: ");
+ sb.append(roomName);
+ return sb.toString();
+ }
+ return "";
+ }
+}
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java
index efdbb7d9d6d..e749f21f367 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java
@@ -235,7 +235,7 @@ public class TriggerHandler {
TriggerType.Exploited.equals(t.getMode()) ||
TriggerType.Sacrificed.equals(t.getMode()) ||
TriggerType.Destroyed.equals(t.getMode()) ||
- (TriggerType.ChangesZone.equals(t.getMode()) && "Battlefield".equals(t.getParam("Origin")))) {
+ (TriggerType.ChangesZone.equals(t.getMode()) && "Battlefield".equals(t.getParam("Origin")))) { // TODO needs additional logic in case origin=Any
registerOneTrigger(t);
}
}
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerRolledDie.java b/forge-game/src/main/java/forge/game/trigger/TriggerRolledDie.java
index 2e22f76f275..a478b73e932 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerRolledDie.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerRolledDie.java
@@ -17,7 +17,8 @@ public class TriggerRolledDie extends Trigger {
}
/** {@inheritDoc}
- * @param runParams*/
+ * @param runParams
+ */
@Override
public final boolean performTest(final Map runParams) {
if (hasParam("ValidPlayer")) {
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerRolledDieOnce.java b/forge-game/src/main/java/forge/game/trigger/TriggerRolledDieOnce.java
new file mode 100644
index 00000000000..d7eb65bd34f
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerRolledDieOnce.java
@@ -0,0 +1,41 @@
+package forge.game.trigger;
+
+import java.util.Map;
+
+import forge.game.ability.AbilityKey;
+import forge.game.card.Card;
+import forge.game.spellability.SpellAbility;
+import forge.util.Localizer;
+
+public class TriggerRolledDieOnce extends Trigger {
+
+ public TriggerRolledDieOnce(final Map params, final Card host, final boolean intrinsic) {
+ super(params, host, intrinsic);
+ }
+
+ /** {@inheritDoc}
+ * @param runParams
+ */
+ @Override
+ public final boolean performTest(final Map runParams) {
+ if (hasParam("ValidPlayer")) {
+ if (!matchesValid(runParams.get(AbilityKey.Player), getParam("ValidPlayer").split(","),
+ this.getHostCard())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void setTriggeringObjects(final SpellAbility sa, Map runParams) {
+ sa.setTriggeringObjectsFrom(runParams, AbilityKey.Result, AbilityKey.Player);
+ }
+
+ @Override
+ public String getImportantStackObjects(SpellAbility sa) {
+ return Localizer.getInstance().getMessage("lblPlayer") + ": " + sa.getTriggeringObject(AbilityKey.Player) + ", " +
+ Localizer.getInstance().getMessage("lblResultIs", sa.getTriggeringObject(AbilityKey.Result));
+ }
+}
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java
index 606da2ee827..b332b264256 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java
@@ -7,7 +7,7 @@ import java.util.Map;
import forge.game.card.Card;
-/**
+/**
* TODO: Write javadoc for this type.
*
*/
@@ -58,6 +58,7 @@ public enum TriggerType {
Discarded(TriggerDiscarded.class),
DiscardedAll(TriggerDiscardedAll.class),
Drawn(TriggerDrawn.class),
+ DungeonCompleted(TriggerCompletedDungeon.class),
Evolved(TriggerEvolved.class),
ExcessDamage(TriggerExcessDamage.class),
Exerted(TriggerExerted.class),
@@ -88,6 +89,8 @@ public enum TriggerType {
Regenerated(TriggerRegenerated.class),
Revealed(TriggerRevealed.class),
RolledDie(TriggerRolledDie.class),
+ RolledDieOnce(TriggerRolledDieOnce.class),
+ RoomEntered(TriggerEnteredRoom.class),
Sacrificed(TriggerSacrificed.class),
Scry(TriggerScry.class),
SearchedLibrary(TriggerSearchedLibrary.class),
diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
index bade2975b81..189ea22af57 100644
--- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
+++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
@@ -10,11 +10,14 @@ import com.google.common.collect.Maps;
import forge.card.mana.ManaCost;
import forge.game.Game;
+import forge.game.GameEntityCounterTable;
import forge.game.ability.AbilityKey;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
+import forge.game.card.CardDamageMap;
import forge.game.card.CardState;
+import forge.game.card.CardZoneTable;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.Ability;
@@ -505,6 +508,39 @@ public class WrappedAbility extends Ability {
// TODO: CardCollection
}
+ @Override
+ public CardDamageMap getDamageMap() {
+ return sa.getDamageMap();
+ }
+ @Override
+ public CardDamageMap getPreventMap() {
+ return sa.getPreventMap();
+ }
+ @Override
+ public GameEntityCounterTable getCounterTable() {
+ return sa.getCounterTable();
+ }
+ @Override
+ public CardZoneTable getChangeZoneTable() {
+ return sa.getChangeZoneTable();
+ }
+ @Override
+ public void setDamageMap(final CardDamageMap map) {
+ sa.setDamageMap(map);
+ }
+ @Override
+ public void setPreventMap(final CardDamageMap map) {
+ sa.setPreventMap(map);
+ }
+ @Override
+ public void setCounterTable(final GameEntityCounterTable table) {
+ sa.setCounterTable(table);
+ }
+ @Override
+ public void setChangeZoneTable(final CardZoneTable table) {
+ sa.setChangeZoneTable(table);
+ }
+
public boolean isAlternativeCost(AlternativeCost ac) {
return sa.isAlternativeCost(ac);
}
diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java
index b430d36e39f..acec5248da0 100644
--- a/forge-game/src/main/java/forge/game/zone/MagicStack.java
+++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java
@@ -543,7 +543,6 @@ public class MagicStack /* extends MyObservable */ implements Iterable