From 1e10d49d0f56edcb23e0eae07663d558907e3a16 Mon Sep 17 00:00:00 2001 From: Adam Pantel <> Date: Mon, 5 Apr 2021 17:51:26 -0400 Subject: [PATCH] Strict Proctor --- .../java/forge/game/ability/AbilityKey.java | 2 + .../game/trigger/TriggerAbilityTriggered.java | 131 ++++++++++++++++++ .../forge/game/trigger/TriggerHandler.java | 6 +- .../java/forge/game/trigger/TriggerType.java | 1 + .../main/java/forge/game/zone/MagicStack.java | 7 + .../cardsfolder/upcoming/strict_proctor.txt | 8 ++ 6 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java create mode 100644 forge-gui/res/cardsfolder/upcoming/strict_proctor.txt diff --git a/forge-game/src/main/java/forge/game/ability/AbilityKey.java b/forge-game/src/main/java/forge/game/ability/AbilityKey.java index 12b138c40f6..b3f4f97dd62 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityKey.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityKey.java @@ -75,6 +75,7 @@ public enum AbilityKey { LifeGained("LifeGained"), Mana("Mana"), MergedCards("MergedCards"), + Mode("Mode"), MonstrosityAmount("MonstrosityAmount"), NewCard("NewCard"), NewCounterAmount("NewCounterAmount"), @@ -118,6 +119,7 @@ public enum AbilityKey { Token("Token"), TokenNum("TokenNum"), Transformer("Transformer"), + TriggeredParams("TriggeredParams"), Vehicle("Vehicle"), Won("Won"); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java b/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java new file mode 100644 index 00000000000..4ac73391b5a --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java @@ -0,0 +1,131 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * 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 . + */ +package forge.game.trigger; + +import com.google.common.collect.ImmutableList; +import forge.game.Game; +import forge.game.ability.AbilityKey; +import forge.game.card.Card; +import forge.game.card.CardZoneTable; +import forge.game.spellability.SpellAbility; +import forge.game.zone.ZoneType; +import forge.util.Localizer; + +import java.util.*; + +/** + *

+ * TriggerAbilityTriggered class. + *

+ * + * @author Forge + * @version $Id$ + */ +public class TriggerAbilityTriggered extends Trigger { + + public TriggerAbilityTriggered(final Map params, final Card host, final boolean intrinsic) { + super(params, host, intrinsic); + } + + /** {@inheritDoc} + * @param runParams*/ + @Override + public final boolean performTest(final Map runParams) { + final SpellAbility spellAbility = (SpellAbility) runParams.get(AbilityKey.SpellAbility); + if (spellAbility == null) { + System.out.println("TriggerAbilityTriggered performTest encountered spellAbility == null. runParams2 = " + runParams); + return false; + } + final Card source = spellAbility.getHostCard(); + final Iterable causes = (Iterable) runParams.get(AbilityKey.Cause); + final Game game = source.getGame(); + + if (hasParam("ValidMode")) { + List validModes = Arrays.asList(getParam("ValidMode").split(",")); + String mode = (String) runParams.get(AbilityKey.Mode); + if (!validModes.contains(mode)) { + return false; + } + } + + if (hasParam("ValidDestination")) { + List validDestinations = Arrays.asList(getParam("ValidDestination").split(",")); + List destinations = Arrays.asList(((String)runParams.get(AbilityKey.Destination)).split(",")); + if (Collections.disjoint(validDestinations, destinations)) { + return false; + } + } + + if (!matchesValidParam("ValidSource", source)) { + return false; + } + + if (hasParam("ValidCause")) { + boolean match = false; + for (Card cause : causes) { + if(matchesValidParam("ValidCause", cause)) { + match = true; + } + } + if (!match) { + return false; + } + } + + return true; + } + + + /** {@inheritDoc} */ + @Override + public final void setTriggeringObjects(final SpellAbility sa, Map runParams) { + final SpellAbility triggeredSA = (SpellAbility) runParams.get(AbilityKey.SpellAbility); + sa.setTriggeringObject(AbilityKey.Source, triggeredSA.getHostCard()); + sa.setTriggeringObjectsFrom( + runParams, + AbilityKey.SpellAbility, + AbilityKey.Cause); + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + sb.append(Localizer.getInstance().getMessage("lblSpellAbility")).append(": ").append(sa.getTriggeringObject(AbilityKey.SpellAbility)); + return sb.toString(); + } + + public static void addTriggeringObject(Trigger regtrig, SpellAbility sa, Map runParams) { + Map newRunParams = AbilityKey.newMap(); + newRunParams.put(AbilityKey.Mode, regtrig.getMode().toString()); + if (regtrig.getMode() == TriggerType.ChangesZone) { + newRunParams.put(AbilityKey.Destination, runParams.get(AbilityKey.Destination)); + newRunParams.put(AbilityKey.Cause, ImmutableList.of(runParams.get(AbilityKey.Card))); + } else if (regtrig.getMode() == TriggerType.ChangesZoneAll) { + final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards); + Set destinations = new HashSet<>(); + for (ZoneType dest : ZoneType.values()) { + if (table.containsColumn(dest) && !table.column(dest).isEmpty()) { + destinations.add(dest.toString()); + } + } + newRunParams.put(AbilityKey.Destination, String.join(",", destinations)); + newRunParams.put(AbilityKey.Cause, table.allCards()); + } + sa.setTriggeringObject(AbilityKey.TriggeredParams, newRunParams); + } +} 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 f7946d52b1b..867ed21fed0 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -17,10 +17,7 @@ */ package forge.game.trigger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; @@ -557,6 +554,7 @@ public class TriggerHandler { sa.setTrigger(regtrig); sa.setSourceTrigger(regtrig.getId()); regtrig.setTriggeringObjects(sa, runParams); + TriggerAbilityTriggered.addTriggeringObject(regtrig, sa, runParams); sa.setTriggerRemembered(regtrig.getTriggerRemembered()); if (regtrig.hasParam("TriggerController")) { 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 21e5c5f99e9..affa61ccdb4 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -14,6 +14,7 @@ import forge.game.card.Card; public enum TriggerType { Abandoned(TriggerAbandoned.class), AbilityCast(TriggerSpellAbilityCastOrCopy.class), + AbilityTriggered(TriggerAbilityTriggered.class), Adapt(TriggerAdapt.class), Always(TriggerAlways.class), Attached(TriggerAttached.class), 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 6bc8329400a..58be16c0c8a 100644 --- a/forge-game/src/main/java/forge/game/zone/MagicStack.java +++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java @@ -332,6 +332,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable newRunParams = (Map) sp.getTriggeringObject(AbilityKey.TriggeredParams); + newRunParams.put(AbilityKey.SpellAbility, sp); + game.getTriggerHandler().runTrigger(TriggerType.AbilityTriggered, newRunParams, false); + } } else { // Run Copy triggers if (sp.isSpell()) { diff --git a/forge-gui/res/cardsfolder/upcoming/strict_proctor.txt b/forge-gui/res/cardsfolder/upcoming/strict_proctor.txt new file mode 100644 index 00000000000..8265feb2f8a --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/strict_proctor.txt @@ -0,0 +1,8 @@ +Name:Strict Proctor +ManaCost:1 W +Types:Creature Spirit Cleric +PT:1/3 +K:Flying +T:Mode$ AbilityTriggered | ValidDestination$ Battlefield | ValidMode$ ChangesZone,ChangesZoneAll | TriggerZones$ Battlefield | Execute$ TrigCounter | TriggerDescription$ Whenever a permanent entering the battlefield causes a triggered ability to trigger, counter that ability unless its controller pays {2}. +SVar:TrigCounter:DB$ Counter | Defined$ TriggeredSpellAbility | UnlessCost$ 2 | UnlessPayer$ TriggeredSpellAbilityController +Oracle:Flying\nWhenever a permanent entering the battlefield causes a triggered ability to trigger, counter that ability unless its controller pays {2}.