Merge branch 'delayedTriggerX' into 'master'

DelayedTriggerEffect: make the trigger remember the spawning Ability

See merge request core-developers/forge!3427
This commit is contained in:
Michael Kamensky
2020-11-23 04:39:00 +00:00
12 changed files with 53 additions and 38 deletions

View File

@@ -719,7 +719,7 @@ public class Game {
getNextPlayerAfter(p).initPlane(); getNextPlayerAfter(p).initPlane();
} }
if (p != null && p.equals(getMonarch())) { if (p != null && p.isMonarch()) {
// if the player who lost was the Monarch, someone else will be the monarch // if the player who lost was the Monarch, someone else will be the monarch
if(p.equals(getPhaseHandler().getPlayerTurn())) { if(p.equals(getPhaseHandler().getPlayerTurn())) {
getAction().becomeMonarch(getNextPlayerAfter(p)); getAction().becomeMonarch(getNextPlayerAfter(p));

View File

@@ -1642,6 +1642,12 @@ public class AbilityUtils {
return CardFactoryUtil.doXMath(0, expr, c); return CardFactoryUtil.doXMath(0, expr, c);
} }
// ImmediateTrigger should check for the Ability which created the trigger
if (t.getSpawningAbility() != null) {
root = t.getSpawningAbility().getRootAbility();
return CardFactoryUtil.doXMath(root.getXManaCostPaid(), expr, c);
}
// 107.3k If an objects enters-the-battlefield triggered ability or replacement effect refers to X, // 107.3k If an objects enters-the-battlefield triggered ability or replacement effect refers to X,
// and the spell that became that object as it resolved had a value of X chosen for any of its costs, // and the spell that became that object as it resolved had a value of X chosen for any of its costs,
// the value of X for that ability is the same as the value of X for that spell, although the value of X for that permanent is 0. // the value of X for that ability is the same as the value of X for that spell, although the value of X for that permanent is 0.

View File

@@ -58,6 +58,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect {
Card lki = CardUtil.getLKICopy(gameCard); Card lki = CardUtil.getLKICopy(gameCard);
lki.setOwner(sa.getActivatingPlayer()); lki.setOwner(sa.getActivatingPlayer());
final Trigger delTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic()); final Trigger delTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic());
delTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
if (triggerRemembered != null) { if (triggerRemembered != null) {
for (final String rem : triggerRemembered.split(",")) { for (final String rem : triggerRemembered.split(",")) {

View File

@@ -57,6 +57,7 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
Card lki = CardUtil.getLKICopy(gameCard); Card lki = CardUtil.getLKICopy(gameCard);
lki.setOwner(sa.getActivatingPlayer()); lki.setOwner(sa.getActivatingPlayer());
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic()); final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic());
immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
// Need to copy paid costs // Need to copy paid costs
@@ -72,11 +73,6 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
} }
} }
if (sa.hasParam("RememberDefinedNumber")) {
immediateTrig.addRemembered((Integer) AbilityUtils.calculateAmount(sa.getHostCard(),
sa.getParam("RememberDefinedNumber"), sa));
}
if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) { if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) {
AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false); AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false);
// need to set Parent to null, otherwise it might have wrong root ability // need to set Parent to null, otherwise it might have wrong root ability

View File

@@ -1167,7 +1167,7 @@ public class CardFactoryUtil {
return doXMath(Integer.parseInt(sq[cc.hasLandfall() ? 1 : 2]), m, c); return doXMath(Integer.parseInt(sq[cc.hasLandfall() ? 1 : 2]), m, c);
} }
if (sq[0].contains("Monarch")) { if (sq[0].contains("Monarch")) {
return doXMath(Integer.parseInt(sq[cc.equals(game.getMonarch()) ? 1 : 2]), m, c); return doXMath(Integer.parseInt(sq[cc.isMonarch() ? 1 : 2]), m, c);
} }
if (sq[0].contains("Blessing")) { if (sq[0].contains("Blessing")) {
return doXMath(Integer.parseInt(sq[cc.hasBlessing() ? 1 : 2]), m, c); return doXMath(Integer.parseInt(sq[cc.hasBlessing() ? 1 : 2]), m, c);

View File

@@ -3113,6 +3113,10 @@ public class Player extends GameEntity implements Comparable<Player> {
return view; return view;
} }
public boolean isMonarch() {
return equals(game.getMonarch());
}
public void createMonarchEffect() { public void createMonarchEffect() {
final PlayerZone com = getZone(ZoneType.Command); final PlayerZone com = getZone(ZoneType.Command);
if (monarchEffect == null) { if (monarchEffect == null) {

View File

@@ -70,7 +70,11 @@ public class PlayerProperty {
return false; return false;
} }
} else if (property.equals("isMonarch")) { } else if (property.equals("isMonarch")) {
if (!player.equals(game.getMonarch())) { if (!player.isMonarch()) {
return false;
}
} else if (property.equals("isNotMonarch")) {
if (player.isMonarch()) {
return false; return false;
} }
} else if (property.equals("hasBlessing")) { } else if (property.equals("hasBlessing")) {

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
@@ -44,7 +44,7 @@ import forge.util.TextUtil;
* <p> * <p>
* Abstract Trigger class. Constructed by reflection only * Abstract Trigger class. Constructed by reflection only
* </p> * </p>
* *
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
@@ -74,11 +74,13 @@ public abstract class Trigger extends TriggerReplacementBase {
private Set<PhaseType> validPhases; private Set<PhaseType> validPhases;
private SpellAbility spawningAbility = null;
/** /**
* <p> * <p>
* Constructor for Trigger. * Constructor for Trigger.
* </p> * </p>
* *
* @param params * @param params
* a {@link java.util.HashMap} object. * a {@link java.util.HashMap} object.
* @param host * @param host
@@ -109,14 +111,14 @@ public abstract class Trigger extends TriggerReplacementBase {
* <p> * <p>
* toString. * toString.
* </p> * </p>
* *
* @return a {@link java.lang.String} object. * @return a {@link java.lang.String} object.
*/ */
@Override @Override
public final String toString() { public final String toString() {
return toString(false); return toString(false);
} }
public String toString(boolean active) { public String toString(boolean active) {
if (hasParam("TriggerDescription") && !this.isSuppressed()) { if (hasParam("TriggerDescription") && !this.isSuppressed()) {
@@ -148,9 +150,9 @@ public abstract class Trigger extends TriggerReplacementBase {
SpellAbility sa = ensureAbility(); SpellAbility sa = ensureAbility();
return replaceAbilityText(desc, sa); return replaceAbilityText(desc, sa);
} }
public final String replaceAbilityText(final String desc, SpellAbility sa) { public final String replaceAbilityText(final String desc, SpellAbility sa) {
String result = desc; String result = desc;
@@ -204,7 +206,7 @@ public abstract class Trigger extends TriggerReplacementBase {
* <p> * <p>
* phasesCheck. * phasesCheck.
* </p> * </p>
* *
* @return a boolean. * @return a boolean.
*/ */
public final boolean phasesCheck(final Game game) { public final boolean phasesCheck(final Game game) {
@@ -270,7 +272,7 @@ public abstract class Trigger extends TriggerReplacementBase {
* <p> * <p>
* requirementsCheck. * requirementsCheck.
* </p> * </p>
* @param game * @param game
* *
* @return a boolean. * @return a boolean.
*/ */
@@ -398,7 +400,7 @@ public abstract class Trigger extends TriggerReplacementBase {
* <p> * <p>
* performTest. * performTest.
* </p> * </p>
* *
* @param runParams * @param runParams
* a {@link HashMap} object. * a {@link HashMap} object.
* @return a boolean. * @return a boolean.
@@ -409,7 +411,7 @@ public abstract class Trigger extends TriggerReplacementBase {
* <p> * <p>
* setTriggeringObjects. * setTriggeringObjects.
* </p> * </p>
* *
* @param sa * @param sa
* a {@link forge.game.spellability.SpellAbility} object. * a {@link forge.game.spellability.SpellAbility} object.
*/ */
@@ -439,7 +441,7 @@ public abstract class Trigger extends TriggerReplacementBase {
public void addRemembered(Object o) { public void addRemembered(Object o) {
this.triggerRemembered.add(o); this.triggerRemembered.add(o);
} }
public List<Object> getTriggerRemembered() { public List<Object> getTriggerRemembered() {
return this.triggerRemembered; return this.triggerRemembered;
} }
@@ -453,7 +455,7 @@ public abstract class Trigger extends TriggerReplacementBase {
} }
/** /**
* *
* @param triggerType * @param triggerType
* the triggerType to set * the triggerType to set
* @param triggerType * @param triggerType
@@ -493,6 +495,14 @@ public abstract class Trigger extends TriggerReplacementBase {
//public String getImportantStackObjects(SpellAbility sa) { return ""; }; //public String getImportantStackObjects(SpellAbility sa) { return ""; };
abstract public String getImportantStackObjects(SpellAbility sa); abstract public String getImportantStackObjects(SpellAbility sa);
public SpellAbility getSpawningAbility() {
return spawningAbility;
}
public void setSpawningAbility(SpellAbility ability) {
spawningAbility = ability;
}
public int getActivationsThisTurn() { public int getActivationsThisTurn() {
return this.numberTurnActivations; return this.numberTurnActivations;
} }

View File

@@ -4,11 +4,8 @@ Types:Legendary Creature Human Wizard
K:Deathtouch K:Deathtouch
PT:3/3 PT:3/3
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigImmediateTrig | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, you may pay {X}. When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else. T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigImmediateTrig | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, you may pay {X}. When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else.
SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ X | Execute$ TrigChange | RememberDefinedNumber$ X | References$ X | TriggerDescription$ When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else. SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ X | Execute$ TrigChange | References$ X | SpellDescription$ When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else.
SVar:X:Count$xPaid SVar:X:Count$xPaid
SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn+cmcEQY | TgtPrompt$ Choose target creature card with converted mana cost X | References$ Y | RememberTargets$ True | AILogic$ BeforeCombat | SubAbility$ DBPutCounter | SpellDescription$ Return target creature card with converted mana cost X from your graveyard to the battlefield. SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn+cmcEQX | TgtPrompt$ Choose target creature card with converted mana cost X | References$ X | WithCounters$ CORPSE_1 | AILogic$ BeforeCombat | LeaveBattlefield$ Exile | SpellDescription$ Return target creature card with converted mana cost X from your graveyard to the battlefield.
SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ CORPSE | CounterNum$ 1 | SubAbility$ DBPump
SVar:DBPump:DB$ Pump | Defined$ Remembered | LeaveBattlefield$ Exile
SVar:Y:Count$TriggerRememberAmount
SVar:HasAttackEffect:TRUE SVar:HasAttackEffect:TRUE
Oracle:Deathtouch\nWhenever Isareth the Awakener attacks, you may pay {X}. When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else. Oracle:Deathtouch\nWhenever Isareth the Awakener attacks, you may pay {X}. When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else.

View File

@@ -5,9 +5,8 @@ PT:3/4
K:Reach K:Reach
K:Partner K:Partner
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+YouCtrl | Execute$ TrigPayCost | TriggerZones$ Battlefield | TriggerDescription$ Whenever another creature enters the battlefield under your control, you may pay {2}. When you do, that creature deals damage equal to its power to target creature. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+YouCtrl | Execute$ TrigPayCost | TriggerZones$ Battlefield | TriggerDescription$ Whenever another creature enters the battlefield under your control, you may pay {2}. When you do, that creature deals damage equal to its power to target creature.
SVar:TrigPayCost:AB$ ImmediateTrigger | Cost$ 2 | Execute$ TrigDealDamage | RememberObjects$ TriggeredCard | SubAbility$ DBCleanup | TriggerDescription$ When you pay {2}, that creature deals damage equal to its power to target creature. SVar:TrigPayCost:AB$ ImmediateTrigger | Cost$ 2 | Execute$ TrigDealDamage | CopyTriggeringObjects$ True | TriggerDescription$ When you pay {2}, that creature deals damage equal to its power to target creature.
SVar:TrigDealDamage:DB$ DealDamage | DamageSource$ DelayTriggerRememberedLKI | NumDmg$ XPower | References$ XPower | ValidTgts$ Creature SVar:TrigDealDamage:DB$ DealDamage | DamageSource$ TriggeredCardLKICopy | NumDmg$ X | References$ X | ValidTgts$ Creature
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:TriggeredCard$CardPower
SVar:XPower:TriggerRememberedLKI$CardPower
DeckNeeds:Type$Creature DeckNeeds:Type$Creature
Oracle:Reach\nWhenever another creature enters the battlefield under your control, you may pay {2}. When you do, that creature deals damage equal to its power to target creature.\nPartner (You can have two commanders if both have partner.) Oracle:Reach\nWhenever another creature enters the battlefield under your control, you may pay {2}. When you do, that creature deals damage equal to its power to target creature.\nPartner (You can have two commanders if both have partner.)

View File

@@ -3,10 +3,9 @@ ManaCost:2 G
Types:Legendary Creature Elf Warrior Types:Legendary Creature Elf Warrior
PT:2/2 PT:2/2
T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ TrigPayCost | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, you may pay {X}{X}. When you do, distribute X +1/+1 counters among any number of target Elf creatures you control. T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ TrigPayCost | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, you may pay {X}{X}. When you do, distribute X +1/+1 counters among any number of target Elf creatures you control.
SVar:TrigPayCost:AB$ ImmediateTrigger | Cost$ X X | RememberDefinedNumber$ X | References$ X | Execute$ TrigPutCounters | TriggerDescription$ When you pay {X}{X}, distribute X +1/+1 counters among any number of target Elf creatures you control. SVar:TrigPayCost:AB$ ImmediateTrigger | Cost$ X X | References$ X | Execute$ TrigPutCounters | TriggerDescription$ When you pay {X}{X}, distribute X +1/+1 counters among any number of target Elf creatures you control.
SVar:TrigPutCounters:DB$ PutCounter | ValidTgts$ Creature.Elf+YouCtrl | TgtPrompt$ Select any number of target Elf creatures you control to distribute counters to | CounterType$ P1P1 | CounterNum$ Y | TargetMin$ 1 | TargetMax$ Y | DividedAsYouChoose$ Y | References$ Y SVar:TrigPutCounters:DB$ PutCounter | ValidTgts$ Creature.Elf+YouCtrl | TgtPrompt$ Select any number of target Elf creatures you control to distribute counters to | CounterType$ P1P1 | CounterNum$ Y | TargetMin$ 1 | TargetMax$ X | DividedAsYouChoose$ X | References$ X
SVar:X:Count$xPaid SVar:X:Count$xPaid
SVar:Y:Count$TriggerRememberAmount
K:Partner K:Partner
DeckHas:Ability$Counters DeckHas:Ability$Counters
DeckHints:Type$Elf DeckHints:Type$Elf

View File

@@ -5,9 +5,8 @@ PT:2/2
K:Flash K:Flash
K:Reach K:Reach
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonHuman+Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever another non-Human creature enters the battlefield under your control, you may pay {X}. When you do, put X +1/+1 counters on CARDNAME. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonHuman+Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever another non-Human creature enters the battlefield under your control, you may pay {X}. When you do, put X +1/+1 counters on CARDNAME.
SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ X | Execute$ TrigPutCounter | RememberDefinedNumber$ X | References$ X | TriggerDescription$ When you do, put X +1/+1 counters on CARDNAME. SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ X | Execute$ TrigPutCounter | References$ X | TriggerDescription$ When you do, put X +1/+1 counters on CARDNAME.
SVar:X:Count$xPaid SVar:X:Count$xPaid
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ Y | References$ Y SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | References$ X
SVar:Y:Count$TriggerRememberAmount
DeckHas:Ability$Counters DeckHas:Ability$Counters
Oracle:Flash\nReach\nWhenever another non-Human creature enters the battlefield under your control, you may pay {X}. When you do, put X +1/+1 counters on Wildborn Preserver. Oracle:Flash\nReach\nWhenever another non-Human creature enters the battlefield under your control, you may pay {X}. When you do, put X +1/+1 counters on Wildborn Preserver.