refactor: clean up triggered abilities from graveyard (#13044)

* some improvements to counter removing triggers

* fix Zone.GRAVEYARD text/conditions for beginning of upkeep triggers

* remaining refactors

* add condition text

* text corrections

* remove remaining generateZoneString

* remove superfluous hardcoded rules text param
This commit is contained in:
xenohedron 2024-10-27 15:23:54 -04:00 committed by GitHub
parent 90623b6a0e
commit aa7a610db2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 331 additions and 737 deletions

View file

@ -78,21 +78,15 @@ public class BeginningOfCombatTriggeredAbility extends TriggeredAbilityImpl {
private String generateTriggerPhrase() {
switch (targetController) {
case YOU:
return "At the beginning of combat on your turn, " + generateZoneString();
return "At the beginning of combat on your turn, ";
case OPPONENT:
return "At the beginning of combat on each opponent's turn, " + generateZoneString();
return "At the beginning of combat on each opponent's turn, ";
case EACH_PLAYER:
return "At the beginning of combat on each player's turn, " + generateZoneString();
return "At the beginning of combat on each player's turn, ";
case ANY:
return "At the beginning of each combat, " + generateZoneString();
return "At the beginning of each combat, ";
}
return "";
}
private String generateZoneString() {
if (getZone() == Zone.GRAVEYARD) {
return "if {this} is in your graveyard, ";
}
return "";
}
}

View file

@ -16,10 +16,6 @@ public class BeginningOfDrawTriggeredAbility extends TriggeredAbilityImpl {
/**
* The Ability sets if no target is defined the target pointer to the active
* player of the current draw phase
*
* @param effect
* @param targetController
* @param isOptional
*/
public BeginningOfDrawTriggeredAbility(Effect effect, TargetController targetController, boolean isOptional) {
this(Zone.BATTLEFIELD, effect, targetController, isOptional);
@ -106,26 +102,19 @@ public class BeginningOfDrawTriggeredAbility extends TriggeredAbilityImpl {
switch (targetController) {
case ACTIVE:
case YOU:
return "At the beginning of your draw step, " + generateZoneString();
return "At the beginning of your draw step, ";
case OPPONENT:
return "At the beginning of each opponent's draw step, " + generateZoneString();
return "At the beginning of each opponent's draw step, ";
case NOT_YOU:
return "At the beginning of each other player's draw step, " + generateZoneString();
return "At the beginning of each other player's draw step, ";
case ANY:
return "At the beginning of each player's draw step, " + generateZoneString();
return "At the beginning of each player's draw step, ";
case CONTROLLER_ATTACHED_TO:
return "At the beginning of the draw step of enchanted creature's controller, " + generateZoneString();
return "At the beginning of the draw step of enchanted creature's controller, ";
case ENCHANTED:
return "At the beginning of enchanted player's draw step, " + generateZoneString();
return "At the beginning of enchanted player's draw step, ";
}
return "";
}
private String generateZoneString() {
switch (getZone()) {
case GRAVEYARD:
return "if {this} is in your graveyard, ";
}
return "";
}
}

View file

@ -14,8 +14,8 @@ import mage.target.targetpointer.FixedTarget;
public class BeginningOfFirstMainTriggeredAbility extends TriggeredAbilityImpl {
private TargetController targetController;
private boolean setTargetPointer;
private final TargetController targetController;
private final boolean setTargetPointer;
public BeginningOfFirstMainTriggeredAbility(Effect effect, TargetController targetController, boolean isOptional) {
this(Zone.BATTLEFIELD, effect, targetController, isOptional, false);
@ -81,20 +81,13 @@ public class BeginningOfFirstMainTriggeredAbility extends TriggeredAbilityImpl {
private String generateTriggerPhrase() {
switch (targetController) {
case YOU:
return "At the beginning of your first main phase, " + generateZoneString();
return "At the beginning of your first main phase, ";
case OPPONENT:
return "At the beginning of each opponent's first main phase, " + generateZoneString();
return "At the beginning of each opponent's first main phase, ";
case ANY:
return "At the beginning of each player's first main phase, " + generateZoneString();
return "At the beginning of each player's first main phase, ";
}
return "";
}
private String generateZoneString() {
switch (getZone()) {
case GRAVEYARD:
return "if {this} is in your graveyard, ";
}
return "";
}
}

View file

@ -14,8 +14,8 @@ import mage.target.targetpointer.FixedTarget;
*/
public class BeginningOfPostCombatMainTriggeredAbility extends TriggeredAbilityImpl {
private TargetController targetController;
private boolean setTargetPointer;
private final TargetController targetController;
private final boolean setTargetPointer;
public BeginningOfPostCombatMainTriggeredAbility(Effect effect, TargetController targetController, boolean isOptional) {
this(Zone.BATTLEFIELD, effect, targetController, isOptional, false);
@ -90,22 +90,15 @@ public class BeginningOfPostCombatMainTriggeredAbility extends TriggeredAbilityI
private String generateTriggerPhrase() {
switch (targetController) {
case YOU:
return "At the beginning of each of your postcombat main phases, " + generateZoneString();
return "At the beginning of each of your postcombat main phases, ";
case OPPONENT:
return "At the beginning of each of your opponent's postcombat main phases, " + generateZoneString();
return "At the beginning of each of your opponent's postcombat main phases, ";
case ANY:
return "At the beginning of each postcombat main phase, " + generateZoneString();
return "At the beginning of each postcombat main phase, ";
case ENCHANTED:
return "At the beginning of each of enchanted player's postcombat main phases, " + generateZoneString();
return "At the beginning of each of enchanted player's postcombat main phases, ";
}
return "";
}
private String generateZoneString() {
switch (getZone()) {
case GRAVEYARD:
return "if {this} is in your graveyard, ";
}
return "";
}
}

View file

@ -16,8 +16,8 @@ import mage.watchers.Watcher;
*/
public class BeginningOfSecondMainTriggeredAbility extends TriggeredAbilityImpl {
private TargetController targetController;
private boolean setTargetPointer;
private final TargetController targetController;
private final boolean setTargetPointer;
public BeginningOfSecondMainTriggeredAbility(Effect effect, TargetController targetController, boolean isOptional) {
this(Zone.BATTLEFIELD, effect, targetController, isOptional, false);
@ -101,24 +101,17 @@ public class BeginningOfSecondMainTriggeredAbility extends TriggeredAbilityImpl
private String generateTriggerPhrase() {
switch (targetController) {
case YOU:
return "At the beginning of your second main phase, " + generateZoneString();
return "At the beginning of your second main phase, ";
case OPPONENT:
return "At the beginning of each opponent's second main phase, " + generateZoneString();
return "At the beginning of each opponent's second main phase, ";
case ANY:
return "At the beginning of each player's second main phase, " + generateZoneString();
return "At the beginning of each player's second main phase, ";
case ENCHANTED:
return "At the beginning of enchanted player's second main phase, " + generateZoneString();
return "At the beginning of enchanted player's second main phase, ";
}
return "";
}
private String generateZoneString() {
switch (getZone()) {
case GRAVEYARD:
return "if {this} is in your graveyard, ";
}
return "";
}
}
class MainPhaseWatcher extends Watcher {

View file

@ -16,7 +16,6 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
private final TargetController targetController;
private final boolean setTargetPointer;
protected String ruleTrigger;
public BeginningOfUpkeepTriggeredAbility(Effect effect, TargetController targetController, boolean isOptional) {
this(Zone.BATTLEFIELD, effect, targetController, isOptional);
@ -27,14 +26,9 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
}
public BeginningOfUpkeepTriggeredAbility(Zone zone, Effect effect, TargetController targetController, boolean isOptional, boolean setTargetPointer) {
this(zone, effect, targetController, isOptional, setTargetPointer, null);
}
public BeginningOfUpkeepTriggeredAbility(Zone zone, Effect effect, TargetController targetController, boolean isOptional, boolean setTargetPointer, String ruleTrigger) {
super(zone, effect, isOptional);
this.targetController = targetController;
this.setTargetPointer = setTargetPointer;
this.ruleTrigger = ruleTrigger;
setTriggerPhrase(generateTriggerPhrase());
}
@ -42,7 +36,6 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
super(ability);
this.targetController = ability.targetController;
this.setTargetPointer = ability.setTargetPointer;
this.ruleTrigger = ability.ruleTrigger;
}
@Override
@ -113,32 +106,22 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
}
private String generateTriggerPhrase() {
if (ruleTrigger != null && !ruleTrigger.isEmpty()) {
return ruleTrigger;
}
switch (targetController) {
case YOU:
return "At the beginning of your upkeep, " + generateZoneString();
return "At the beginning of your upkeep, ";
case OPPONENT:
return "At the beginning of each opponent's upkeep, " + generateZoneString();
return "At the beginning of each opponent's upkeep, ";
case ANY:
case ACTIVE:
return "At the beginning of each player's upkeep, " + generateZoneString();
return "At the beginning of each player's upkeep, ";
case EACH_PLAYER:
return "At the beginning of each upkeep, " + generateZoneString();
return "At the beginning of each upkeep, ";
case CONTROLLER_ATTACHED_TO:
return "At the beginning of the upkeep of enchanted creature's controller, " + generateZoneString();
return "At the beginning of the upkeep of enchanted creature's controller, ";
case ENCHANTED:
return "At the beginning of enchanted player's upkeep, " + generateZoneString();
return "At the beginning of enchanted player's upkeep, ";
}
return "";
}
private String generateZoneString() {
switch (getZone()) {
case GRAVEYARD:
return "if {this} is in your graveyard, ";
}
return "";
}
}

View file

@ -17,7 +17,7 @@ public class SurveilTriggeredAbility extends TriggeredAbilityImpl {
public SurveilTriggeredAbility(Zone zone, Effect effect) {
super(zone, effect);
setTriggerPhrase("Whenever you surveil, " + (zone == Zone.GRAVEYARD ? "if {this} is in your graveyard, " : ""));
setTriggerPhrase("Whenever you surveil, ");
}
private SurveilTriggeredAbility(final SurveilTriggeredAbility ability) {

View file

@ -10,7 +10,6 @@ import mage.game.Game;
*
* @author Susucr
*/
public enum SourceInGraveyardCondition implements Condition {
instance;
@ -18,4 +17,10 @@ public enum SourceInGraveyardCondition implements Condition {
public boolean apply(Game game, Ability source) {
return game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD;
}
@Override
public String toString() {
return "{this} is in your graveyard";
}
}

View file

@ -1,28 +1,20 @@
package mage.abilities.condition.common;
import java.util.Iterator;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.game.command.CommandObject;
/**
* As long as the sourceId permanent is on the battlefield, the condition is
* true.
*
* @author LevelX2
*/
import java.util.UUID;
public enum SourceOnBattlefieldOrCommandZoneCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
Iterator<CommandObject> commandZone = game.getState().getCommand().iterator();
while (commandZone.hasNext()) {
UUID thing = commandZone.next().getId();
for (CommandObject commandObject : game.getState().getCommand()) {
UUID thing = commandObject.getId();
if (thing.equals(source.getSourceId())) {
return true;
}
@ -32,7 +24,7 @@ public enum SourceOnBattlefieldOrCommandZoneCondition implements Condition {
@Override
public String toString() {
return "if {this} is on the battlefield";
return "if {this} is in the command zone or on the battlefield";
}
}

View file

@ -33,26 +33,20 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
int toRemove = Math.min(counter.getCount(), permanent.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) {
permanent.removeCounters(counter.getName(), toRemove, source, game);
if (!game.isSimulation()) {
game.informPlayers("Removed " + toRemove + ' ' + counter.getName() + " counter from " + permanent.getLogName());
}
game.informPlayers("Removed " + toRemove + ' ' + counter.getName() + " counter from " + permanent.getLogName());
}
return true;
}
if (!(source.getSourceObject(game) instanceof Permanent)) {
Card card = game.getCard(source.getSourceId());
if (card != null) {
int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) {
card.removeCounters(counter.getName(), toRemove, source, game);
if (!game.isSimulation()) {
game.informPlayers("Removed " + toRemove + ' ' + counter.getName()
+ " counter from " + card.getLogName()
+ " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
}
}
return true;
Card card = source.getSourceCardIfItStillExists(game);
if (card != null) {
int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) {
card.removeCounters(counter.getName(), toRemove, source, game);
game.informPlayers("Removed " + toRemove + ' ' + counter.getName()
+ " counter from " + card.getLogName()
+ " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
}
return true;
}
return false;
}

View file

@ -4,8 +4,6 @@ import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
@ -27,28 +25,28 @@ import mage.target.targetpointer.FixedTarget;
*/
public class RepairAbility extends DiesSourceTriggeredAbility {
private final String ruleText;
private final int count;
public RepairAbility(int count) {
super(new AddCountersSourceEffect(
CounterType.REPAIR.createInstance(), StaticValue.get(count), false, true));
addSubAbility(new RepairBeginningOfUpkeepInterveningIfTriggeredAbility());
addSubAbility(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD,
new RemoveCounterSourceEffect(CounterType.REPAIR.createInstance()), TargetController.YOU, false)
.setRuleVisible(false));
addSubAbility(new RepairCastFromGraveyardTriggeredAbility());
ruleText = "Repair " + count + " <i>(When this creature dies, put " + count
+ " repair counters on it. At the beginning of your upkeep, remove a repair counter. "
+ "Whenever the last is removed, you may cast it from graveyard until end of turn.)</i>";
this.count = count;
}
protected RepairAbility(final RepairAbility ability) {
super(ability);
this.ruleText = ability.ruleText;
this.count = ability.count;
}
@Override
public String getRule() {
return ruleText;
return "Repair " + count + " <i>(When this creature dies, put " + count
+ " repair counters on it. At the beginning of your upkeep, remove a repair counter. "
+ "Whenever the last is removed, you may cast it from graveyard until end of turn.)</i>";
}
@Override
@ -59,7 +57,7 @@ public class RepairAbility extends DiesSourceTriggeredAbility {
class RepairedCastFromGraveyardEffect extends OneShotEffect {
public RepairedCastFromGraveyardEffect() {
RepairedCastFromGraveyardEffect() {
super(Outcome.Benefit);
}
@ -75,11 +73,8 @@ class RepairedCastFromGraveyardEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Card card = game.getCard(source.getSourceId());
if (card == null) {
Card card = source.getSourceCardIfItStillExists(game);
if (controller == null || card == null) {
return false;
}
game.addEffect(new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, Duration.UntilYourNextEndStep)
@ -90,12 +85,12 @@ class RepairedCastFromGraveyardEffect extends OneShotEffect {
class RepairCastFromGraveyardTriggeredAbility extends TriggeredAbilityImpl {
public RepairCastFromGraveyardTriggeredAbility() {
RepairCastFromGraveyardTriggeredAbility() {
super(Zone.GRAVEYARD, new RepairedCastFromGraveyardEffect());
setRuleVisible(false);
}
public RepairCastFromGraveyardTriggeredAbility(RepairCastFromGraveyardTriggeredAbility ability) {
private RepairCastFromGraveyardTriggeredAbility(RepairCastFromGraveyardTriggeredAbility ability) {
super(ability);
}
@ -109,6 +104,7 @@ class RepairCastFromGraveyardTriggeredAbility extends TriggeredAbilityImpl {
if (event.getTargetId().equals(getSourceId())) {
Card card = game.getCard(getSourceId());
return card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD
&& event.getData().equals(CounterType.REPAIR.getName())
&& card.getCounters(game).getCount(CounterType.REPAIR) == 0;
}
return false;
@ -124,23 +120,3 @@ class RepairCastFromGraveyardTriggeredAbility extends TriggeredAbilityImpl {
return new RepairCastFromGraveyardTriggeredAbility(this);
}
}
class RepairBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility {
public RepairBeginningOfUpkeepInterveningIfTriggeredAbility() {
super(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD,
new RemoveCounterSourceEffect(CounterType.REPAIR.createInstance()), TargetController.YOU, false),
new SourceHasCounterCondition(CounterType.REPAIR),
"At the beginning of your upkeep, remove a repair counter from {this}");
this.setRuleVisible(false);
}
protected RepairBeginningOfUpkeepInterveningIfTriggeredAbility(final RepairBeginningOfUpkeepInterveningIfTriggeredAbility effect) {
super(effect);
}
@Override
public RepairBeginningOfUpkeepInterveningIfTriggeredAbility copy() {
return new RepairBeginningOfUpkeepInterveningIfTriggeredAbility(this);
}
}