mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
Merge branch 'suspendHaste' into 'master'
Suspend: reworked suspend See merge request core-developers/forge!573
This commit is contained in:
@@ -44,7 +44,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
|
||||
/** The temporarily suppressed. */
|
||||
protected boolean temporarilySuppressed = false;
|
||||
|
||||
private Map<String, String> sVars = Maps.newHashMap();
|
||||
protected Map<String, String> sVars = Maps.newHashMap();
|
||||
|
||||
/** Keys of descriptive (text) parameters. */
|
||||
private static final ImmutableList<String> descriptiveKeys = ImmutableList.<String>builder()
|
||||
|
||||
@@ -105,10 +105,6 @@ public class GameAction {
|
||||
boolean fromBattlefield = zoneFrom != null && zoneFrom.is(ZoneType.Battlefield);
|
||||
boolean toHand = zoneTo.is(ZoneType.Hand);
|
||||
|
||||
// TODO: part of a workaround for suspend-cast creaturs bounced to hand
|
||||
boolean zoneChangedEarly = false;
|
||||
Zone originalZone = c.getZone();
|
||||
|
||||
//Rule 110.5g: A token that has left the battlefield can't move to another zone
|
||||
if (c.isToken() && zoneFrom != null && !fromBattlefield && !zoneFrom.is(ZoneType.Command)) {
|
||||
return c;
|
||||
@@ -158,13 +154,6 @@ public class GameAction {
|
||||
c.setState(CardStateName.Original, true);
|
||||
}
|
||||
|
||||
if (fromBattlefield && toHand && c.wasSuspendCast()) {
|
||||
// TODO: This has to be set early for suspend-cast creatures bounced to hand, otherwise they
|
||||
// end up in a state when they are considered on the battlefield. There should be a better solution.
|
||||
c.setZone(zoneTo);
|
||||
zoneChangedEarly = true;
|
||||
}
|
||||
|
||||
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
|
||||
if (fromBattlefield && c.getSVar("EndOfTurnLeavePlay").equals("Dash")) {
|
||||
c.removeSVar("EndOfTurnLeavePlay");
|
||||
@@ -295,10 +284,6 @@ public class GameAction {
|
||||
|
||||
ReplacementResult repres = game.getReplacementHandler().run(repParams);
|
||||
if (repres != ReplacementResult.NotReplaced) {
|
||||
if (zoneChangedEarly) {
|
||||
c.setZone(originalZone); // TODO: part of a workaround for bounced suspend-cast cards
|
||||
}
|
||||
|
||||
// reset failed manifested Cards back to original
|
||||
if (c.isManifested()) {
|
||||
c.turnFaceUp(false, false);
|
||||
@@ -315,10 +300,6 @@ public class GameAction {
|
||||
|
||||
copied.getOwner().removeInboundToken(copied);
|
||||
|
||||
if (c.wasSuspendCast()) {
|
||||
copied = GameAction.addSuspendTriggers(copied);
|
||||
}
|
||||
|
||||
if (suppress) {
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
}
|
||||
@@ -396,7 +377,7 @@ public class GameAction {
|
||||
}
|
||||
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true);
|
||||
if (zoneFrom != null && zoneFrom.is(ZoneType.Battlefield)) {
|
||||
if (zoneFrom != null && zoneFrom.is(ZoneType.Battlefield) && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
|
||||
final Map<String, Object> runParams2 = Maps.newHashMap();
|
||||
runParams2.put("Card", lastKnownInfo);
|
||||
runParams2.put("OriginalController", zoneFrom.getPlayer());
|
||||
@@ -436,7 +417,6 @@ public class GameAction {
|
||||
|
||||
if (fromBattlefield) {
|
||||
if (!c.isToken()) {
|
||||
copied.setSuspendCast(false);
|
||||
copied.setState(CardStateName.Original, true);
|
||||
}
|
||||
// Soulbond unpairing
|
||||
@@ -1508,44 +1488,6 @@ public class GameAction {
|
||||
return sacrificed != null;
|
||||
}
|
||||
|
||||
private static Card addSuspendTriggers(final Card c) {
|
||||
if (c.getSVar("HasteFromSuspend").equals("True")) {
|
||||
return c;
|
||||
}
|
||||
c.setSVar("HasteFromSuspend", "True");
|
||||
|
||||
final GameCommand intoPlay = new GameCommand() {
|
||||
private static final long serialVersionUID = -4514610171270596654L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (c.isInPlay() && c.isCreature()) {
|
||||
c.addExtrinsicKeyword("Haste");
|
||||
c.updateStateForView();
|
||||
}
|
||||
} // execute()
|
||||
};
|
||||
|
||||
c.addComesIntoPlayCommand(intoPlay);
|
||||
|
||||
final GameCommand loseControl = new GameCommand() {
|
||||
private static final long serialVersionUID = -4514610171270596654L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (c.getSVar("HasteFromSuspend").equals("True")) {
|
||||
c.setSVar("HasteFromSuspend", "False");
|
||||
c.removeExtrinsicKeyword("Haste");
|
||||
c.updateStateForView();
|
||||
}
|
||||
} // execute()
|
||||
};
|
||||
|
||||
c.addChangeControllerCommand(loseControl);
|
||||
c.addLeavesPlayCommand(loseControl);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sacrificed Card in its new location, or {@code null} if the
|
||||
* sacrifice wasn't successful.
|
||||
|
||||
@@ -185,10 +185,6 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if(sa.hasParam("SuspendCast")) {
|
||||
tgtCard.setSuspendCast(true);
|
||||
}
|
||||
|
||||
// lands will be played
|
||||
if (tgtCard.isLand()) {
|
||||
if (controller.playLand(tgtCard, true)) {
|
||||
|
||||
@@ -60,9 +60,6 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
||||
for (String kw : hiddenkws) {
|
||||
tgtC.addHiddenExtrinsicKeyword(kw);
|
||||
}
|
||||
if (suspend && !tgtC.hasSuspend()) {
|
||||
tgtC.setSuspend(true);
|
||||
}
|
||||
|
||||
if (sa.hasParam("RememberAllPumped")) {
|
||||
sa.getHostCard().addRemembered(tgtC);
|
||||
|
||||
@@ -30,9 +30,11 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
private static void applyPump(final SpellAbility sa, final Card applyTo,
|
||||
final int a, final int d, final List<String> keywords,
|
||||
final long timestamp) {
|
||||
final Card host = sa.getHostCard();
|
||||
//if host is not on the battlefield don't apply
|
||||
// Suspend should does Affect the Stack
|
||||
if (sa.hasParam("UntilLoseControlOfHost")
|
||||
&& !sa.getHostCard().isInPlay()) {
|
||||
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
||||
return;
|
||||
}
|
||||
final Game game = sa.getActivatingPlayer().getGame();
|
||||
@@ -45,9 +47,6 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
redrawPT |= kw.contains("CARDNAME's power and toughness are switched");
|
||||
} else {
|
||||
kws.add(kw);
|
||||
if (kw.equals("Suspend") && !applyTo.hasSuspend()) {
|
||||
applyTo.setSuspend(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,8 +161,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
private long bestowTimestamp = -1;
|
||||
private long transformedTimestamp = 0;
|
||||
private boolean suspendCast = false;
|
||||
private boolean suspend = false;
|
||||
private boolean tributed = false;
|
||||
private boolean embalmed = false;
|
||||
private boolean eternalized = false;
|
||||
@@ -3754,17 +3752,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public final boolean hasSuspend() {
|
||||
return suspend;
|
||||
}
|
||||
public final void setSuspend(final boolean b) {
|
||||
suspend = b;
|
||||
}
|
||||
|
||||
public final boolean wasSuspendCast() {
|
||||
return suspendCast;
|
||||
}
|
||||
public final void setSuspendCast(final boolean b) {
|
||||
suspendCast = b;
|
||||
return hasKeyword(Keyword.SUSPEND) && getLastKnownZone().is(ZoneType.Exile)
|
||||
&& getCounters(CounterType.TIME) >= 1;
|
||||
}
|
||||
|
||||
public final boolean isPhasedOut() {
|
||||
|
||||
@@ -2950,10 +2950,27 @@ public class CardFactoryUtil {
|
||||
playTrig.append(" | TriggerDescription$ When the last time counter is removed from this card, if it's exiled, play it without paying its mana cost if able. ");
|
||||
playTrig.append("If you can't, it remains exiled. If you cast a creature spell this way, it gains haste until you lose control of the spell or the permanent it becomes.");
|
||||
|
||||
final String abPlay = "DB$ Play | Defined$ Self | WithoutManaCost$ True | SuspendCast$ True";
|
||||
String abPlay = "DB$ Play | Defined$ Self | WithoutManaCost$ True | SuspendCast$ True";
|
||||
if (card.isPermanent()) {
|
||||
abPlay += "| RememberPlayed$ True";
|
||||
}
|
||||
|
||||
final SpellAbility saPlay = AbilityFactory.getAbility(abPlay, card);
|
||||
|
||||
if (card.isPermanent()) {
|
||||
final String abPump = "DB$ Pump | Defined$ Remembered | KW$ Haste | PumpZone$ Stack "
|
||||
+ "| ConditionDefined$ Remembered | ConditionPresent$ Creature | UntilLoseControlOfHost$ True";
|
||||
final AbilitySub saPump = (AbilitySub)AbilityFactory.getAbility(abPump, card);
|
||||
|
||||
String dbClean = "DB$ Cleanup | ClearRemembered$ True";
|
||||
final AbilitySub saCleanup = (AbilitySub) AbilityFactory.getAbility(dbClean, card);
|
||||
saPump.setSubAbility(saCleanup);
|
||||
|
||||
saPlay.setSubAbility(saPump);
|
||||
}
|
||||
|
||||
final Trigger parsedPlayTrigger = TriggerHandler.parseTrigger(playTrig.toString(), card, intrinsic);
|
||||
parsedPlayTrigger.setOverridingAbility(AbilityFactory.getAbility(abPlay, card));
|
||||
parsedPlayTrigger.setOverridingAbility(saPlay);
|
||||
|
||||
inst.addTrigger(parsedUpkeepTrig);
|
||||
inst.addTrigger(parsedPlayTrigger);
|
||||
@@ -4026,8 +4043,6 @@ public class CardFactoryUtil {
|
||||
inst.addSpellAbility(newSA);
|
||||
|
||||
} else if (keyword.startsWith("Suspend") && !keyword.equals("Suspend")) {
|
||||
// really needed?
|
||||
card.setSuspend(true);
|
||||
// only add it if suspend has counter and cost
|
||||
final String[] k = keyword.split(":");
|
||||
|
||||
@@ -4051,8 +4066,6 @@ public class CardFactoryUtil {
|
||||
public void resolve() {
|
||||
final Game game = card.getGame();
|
||||
final Card c = game.getAction().exile(this.getHostCard(), this);
|
||||
// better check?
|
||||
c.setSuspend(true);
|
||||
|
||||
int counters = AbilityUtils.calculateAmount(c, k[1], this);
|
||||
c.addCounter(CounterType.TIME, counters, c, true);
|
||||
|
||||
@@ -1357,8 +1357,7 @@ public class CardProperty {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("suspended")) {
|
||||
if (!card.hasSuspend() || !game.isCardExiled(card)
|
||||
|| !(card.getCounters(CounterType.TIME) >= 1)) {
|
||||
if (!card.hasSuspend()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("delved")) {
|
||||
|
||||
@@ -138,7 +138,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
}
|
||||
|
||||
if (!(isInstant || activator.canCastSorcery() || flash || getRestrictions().isInstantSpeed()
|
||||
|| this.hasSVar("IsCastFromPlayEffect")
|
||||
|| hasSVar("IsCastFromPlayEffect")
|
||||
|| (card.isFaceDown() && !card.getLastKnownZone().is(ZoneType.Battlefield) && card.getState(CardStateName.Original).getType().isInstant()))) {
|
||||
return false;
|
||||
}
|
||||
@@ -148,7 +148,11 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
}
|
||||
|
||||
// for uncastables like lotus bloom, check if manaCost is blank (except for morph spells)
|
||||
if (!isCastFaceDown() && isBasicSpell() && card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost().isNoCost()) {
|
||||
// but ignore if it comes from PlayEffect
|
||||
if (!isCastFaceDown()
|
||||
&& !hasSVar("IsCastFromPlayEffect")
|
||||
&& isBasicSpell()
|
||||
&& card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost().isNoCost()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -854,6 +854,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
// clear maps for copy, the values will be added later
|
||||
clone.additionalAbilities = Maps.newHashMap();
|
||||
clone.additionalAbilityLists = Maps.newHashMap();
|
||||
clone.sVars = Maps.newHashMap();
|
||||
// run special copy Ability to make a deep copy
|
||||
CardFactory.copySpellAbility(this, clone, host, activ, lki);
|
||||
} catch (final CloneNotSupportedException e) {
|
||||
|
||||
@@ -89,16 +89,7 @@ public class HumanPlay {
|
||||
|
||||
// extra play check
|
||||
if (sa.isSpell() && !sa.canPlay()) {
|
||||
// Exceptional cases where canPlay should not run
|
||||
boolean exemptFromCheck = false;
|
||||
if (source.hasSuspend() && p.getGame().isCardExiled(source) && source.getCounters(CounterType.TIME) == 0) {
|
||||
// A card is about to ETB from Suspend
|
||||
exemptFromCheck = true;
|
||||
}
|
||||
|
||||
if (!exemptFromCheck) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
|
||||
Reference in New Issue
Block a user