Text generation improvements for triggered abilities (#10638)

This commit is contained in:
xenohedron 2023-07-17 20:03:01 -04:00 committed by GitHub
parent 0c7965a725
commit fa72e243e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 86 additions and 43 deletions

View file

@ -43,6 +43,10 @@ public interface TriggeredAbility extends Ability {
TriggeredAbility setTriggersOnceEachTurn(boolean triggersOnce);
TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce);
TriggeredAbility setReplaceRuleText(boolean replaceRuleText);
boolean checkInterveningIfClause(Game game);
boolean isOptional();

View file

@ -26,8 +26,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
protected boolean leavesTheBattlefieldTrigger;
private boolean triggersOnceEachTurn = false;
private boolean doOnlyOnceEachTurn = false;
protected boolean replaceRuleText = true;
private GameEvent triggerEvent = null;
private String triggerPhrase = null; // TODO: This could be changed to final if all constructors set a value
private String triggerPhrase = null;
protected TriggeredAbilityImpl(Zone zone, Effect effect) {
this(zone, effect, false);
@ -54,6 +55,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
this.leavesTheBattlefieldTrigger = ability.leavesTheBattlefieldTrigger;
this.triggersOnceEachTurn = ability.triggersOnceEachTurn;
this.doOnlyOnceEachTurn = ability.doOnlyOnceEachTurn;
this.replaceRuleText = ability.replaceRuleText;
this.triggerEvent = ability.triggerEvent;
this.triggerPhrase = ability.triggerPhrase;
}
@ -103,11 +105,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
return lastTurnTriggered == null || lastTurnTriggered != game.getTurnNum();
}
public TriggeredAbility setTriggersOnceEachTurn(boolean triggersOnce) {
this.triggersOnceEachTurn = triggersOnce;
return this;
}
@Override
public boolean checkUsedAlready(Game game) {
if (!doOnlyOnceEachTurn) {
@ -119,12 +116,25 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
return lastTurnUsed != null && lastTurnUsed == game.getTurnNum();
}
@Override
public TriggeredAbility setTriggersOnceEachTurn(boolean triggersOnce) {
this.triggersOnceEachTurn = triggersOnce;
return this;
}
@Override
public TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce) {
this.optional = true;
this.doOnlyOnceEachTurn = doOnlyOnce;
return this;
}
@Override
public TriggeredAbility setReplaceRuleText(boolean replaceRuleText) {
this.replaceRuleText = replaceRuleText;
return this;
}
@Override
public boolean checkInterveningIfClause(Game game) {
return true;
@ -202,41 +212,25 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
}
} else if (!ruleLow.startsWith("{this}")
&& (this.getTargets().isEmpty()
|| ruleLow.startsWith("attach")
|| ruleLow.startsWith("change")
|| ruleLow.startsWith("counter")
|| ruleLow.startsWith("destroy")
|| ruleLow.startsWith("distribute")
|| ruleLow.startsWith("sacrifice")
|| ruleLow.startsWith("exchange")
|| ruleLow.startsWith("exile")
|| ruleLow.startsWith("gain")
|| ruleLow.startsWith("goad")
|| ruleLow.startsWith("have")
|| ruleLow.startsWith("move")
|| ruleLow.startsWith("prevent")
|| ruleLow.startsWith("put")
|| ruleLow.startsWith("remove")
|| ruleLow.startsWith("return")
|| ruleLow.startsWith("shuffle")
|| ruleLow.startsWith("turn")
|| ruleLow.startsWith("tap")
|| ruleLow.startsWith("untap"))) {
|| startsWithVerb(ruleLow))) {
sb.append("you may ");
} else if (!ruleLow.startsWith("its controller may")) {
sb.append("you may have ");
superRule = superRule
.replace(" becomes ", " become ")
.replace(" blocks ", " block ")
.replace(" deals ", " deal ")
.replace(" discards ", " discard ")
.replace(" gains ", " gain ")
.replace(" gets ", " get ")
.replace(" loses ", " lose ")
.replace(" mills ", " mill ")
.replace(" sacrifices ", " sacrifice ");
superRule = ruleWithFixedVerbGrammar(superRule);
}
}
if (replaceRuleText
&& triggerPhrase != null
&& triggerPhrase.contains("{this}")
&& !triggerPhrase.contains("other")
&& !triggerPhrase.contains(" of a ")
&& !triggerPhrase.contains(" by a ")
&& !triggerPhrase.contains(" to a ")
&& !triggerPhrase.contains(" blocks a ")
&& (superRule.startsWith("{this}")
|| superRule.startsWith("sacrifice {this}")
)) {
superRule = superRule.replace("{this} ", "it ");
}
sb.append(superRule);
if (triggersOnceEachTurn) {
@ -246,10 +240,44 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
sb.append(" Do this only once each turn.");
}
}
return sb.toString();
}
private static boolean startsWithVerb(String ruleLow) {
return ruleLow.startsWith("attach")
|| ruleLow.startsWith("change")
|| ruleLow.startsWith("counter")
|| ruleLow.startsWith("destroy")
|| ruleLow.startsWith("distribute")
|| ruleLow.startsWith("sacrifice")
|| ruleLow.startsWith("exchange")
|| ruleLow.startsWith("exile")
|| ruleLow.startsWith("gain")
|| ruleLow.startsWith("goad")
|| ruleLow.startsWith("have")
|| ruleLow.startsWith("move")
|| ruleLow.startsWith("prevent")
|| ruleLow.startsWith("put")
|| ruleLow.startsWith("remove")
|| ruleLow.startsWith("return")
|| ruleLow.startsWith("shuffle")
|| ruleLow.startsWith("turn")
|| ruleLow.startsWith("tap")
|| ruleLow.startsWith("untap");
}
private static String ruleWithFixedVerbGrammar(String rule) {
return rule.replace(" becomes ", " become ")
.replace(" blocks ", " block ")
.replace(" deals ", " deal ")
.replace(" discards ", " discard ")
.replace(" gains ", " gain ")
.replace(" gets ", " get ")
.replace(" loses ", " lose ")
.replace(" mills ", " mill ")
.replace(" sacrifices ", " sacrifice ");
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {

View file

@ -24,6 +24,7 @@ public class BecomesBlockedByCreatureTriggeredAbility extends TriggeredAbilityIm
public BecomesBlockedByCreatureTriggeredAbility(Effect effect, FilterCreaturePermanent filter, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.replaceRuleText = false;
setTriggerPhrase("Whenever {this} becomes blocked by " + CardUtil.addArticle(filter.getMessage()) + ", ");
}

View file

@ -32,6 +32,7 @@ public class BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility extends Triggered
public BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.replaceRuleText = false;
this.rule = rule;
setTriggerPhrase("Whenever {this} blocks or becomes blocked by one or more " + (filter != null ? filter.getMessage() : "creatures") + ", ");
}

View file

@ -34,6 +34,7 @@ public class HeroicAbility extends TriggeredAbilityImpl {
if (isHeroic) {
this.setAbilityWord(AbilityWord.HEROIC);
}
this.replaceRuleText = false;
setTriggerPhrase("Whenever you cast a spell that targets {this}, ");
}

View file

@ -17,6 +17,7 @@ public class PackTacticsAbility extends TriggeredAbilityImpl {
public PackTacticsAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
this.replaceRuleText = false;
this.setAbilityWord(AbilityWord.PACK_TACTICS);
setTriggerPhrase("Whenever {this} attacks, if you attacked with creatures with total power 6 or greater this combat, ");
}

View file

@ -37,6 +37,7 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl {
public OrTriggeredAbility(Zone zone, Effect effect, boolean optional, String ruleTrigger, TriggeredAbility... abilities) {
super(zone, effect, optional);
this.ruleTrigger = ruleTrigger;
this.replaceRuleText = false;
Collections.addAll(this.triggeredAbilities, abilities);
for (TriggeredAbility ability : triggeredAbilities) {
//Remove useless data