mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
[WOE] implement Troublemaker Ouphe, Torch the Tower (add Bargain ability) (#10812)
* add start of Bargain Current version probably has a bunch of bugs related to zcc and copy. * add Torch the Tower * add Torch the Tower tests * add better than nothing activationKey before tag cost tracking gets cleaned up --------- Co-authored-by: Evan Kranzler <theelk801@gmail.com>
This commit is contained in:
parent
2eef675369
commit
8169799213
8 changed files with 623 additions and 0 deletions
|
|
@ -0,0 +1,38 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.keyword.BargainAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Checks if the spell was cast with the alternate Bargain cost
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public enum BargainedCondition implements Condition {
|
||||
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// TODO: replace by Tag Cost Tracking.
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (sourceObject instanceof Card) {
|
||||
for (Ability ability : ((Card) sourceObject).getAbilities(game)) {
|
||||
if (ability instanceof BargainAbility) {
|
||||
return ((BargainAbility) ability).wasBargained(game, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{this} was Bargained";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package mage.abilities.hint;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Displays an hint only when the condition is true.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class ConditionTrueHint implements Hint {
|
||||
|
||||
private Condition condition;
|
||||
private String trueText;
|
||||
private Color trueColor;
|
||||
private Boolean useIcons;
|
||||
|
||||
public ConditionTrueHint(Condition condition) {
|
||||
this(condition, condition.toString());
|
||||
}
|
||||
|
||||
public ConditionTrueHint(Condition condition, String textWithIcons) {
|
||||
this(condition, textWithIcons, null, true);
|
||||
}
|
||||
|
||||
public ConditionTrueHint(Condition condition, String trueText, Color trueColor, Boolean useIcons) {
|
||||
this.condition = condition;
|
||||
this.trueText = CardUtil.getTextWithFirstCharUpperCase(trueText);
|
||||
this.trueColor = trueColor;
|
||||
this.useIcons = useIcons;
|
||||
}
|
||||
|
||||
protected ConditionTrueHint(final ConditionTrueHint hint) {
|
||||
this.condition = hint.condition;
|
||||
this.trueText = hint.trueText;
|
||||
this.trueColor = hint.trueColor;
|
||||
this.useIcons = hint.useIcons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Game game, Ability ability) {
|
||||
String icon;
|
||||
if (condition.apply(game, ability)) {
|
||||
icon = this.useIcons ? HintUtils.HINT_ICON_GOOD : null;
|
||||
return HintUtils.prepareText(this.trueText, this.trueColor, icon);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hint copy() {
|
||||
return new ConditionTrueHint(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package mage.abilities.hint.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.common.BargainedCondition;
|
||||
import mage.abilities.hint.ConditionTrueHint;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public enum BargainCostWasPaidHint implements Hint {
|
||||
|
||||
instance;
|
||||
private static final ConditionTrueHint hint = new ConditionTrueHint(BargainedCondition.instance, "bargained");
|
||||
|
||||
@Override
|
||||
public String getText(Game game, Ability ability) {
|
||||
return hint.getText(game, ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hint copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
149
Mage/src/main/java/mage/abilities/keyword/BargainAbility.java
Normal file
149
Mage/src/main/java/mage/abilities/keyword/BargainAbility.java
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.hint.common.BargainCostWasPaidHint;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.TokenPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* Written before ruling was clarified. Feel free to put the ruling once it gets there.
|
||||
* <p>
|
||||
* Bargain is a keyword static ability that adds an optional additional cost.
|
||||
* <p>
|
||||
* Bargain means "You may sacrifice an artifact, enchantment, or token as you cast this spell".
|
||||
* <p>
|
||||
* If a spell bargain cost is paid, the spell or the permanent it becomes is bargained.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class BargainAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
|
||||
|
||||
private static final FilterControlledPermanent bargainFilter = new FilterControlledPermanent("an artifact, enchantment, or token");
|
||||
private static final String promptString = "Bargain? (To Bargain, sacrifice an artifact, enchantment, or token)";
|
||||
private static final String keywordText = "Bargain";
|
||||
private static final String reminderText = "You may sacrifice an artifact, enchantment, or token as you cast this spell.";
|
||||
private final String rule;
|
||||
|
||||
private String activationKey; // TODO: replace by Tag Cost Tracking.
|
||||
|
||||
protected OptionalAdditionalCost additionalCost;
|
||||
|
||||
static {
|
||||
bargainFilter.add(Predicates.or(
|
||||
CardType.ARTIFACT.getPredicate(),
|
||||
CardType.ENCHANTMENT.getPredicate(),
|
||||
TokenPredicate.TRUE
|
||||
));
|
||||
}
|
||||
|
||||
public BargainAbility() {
|
||||
super(Zone.STACK, null);
|
||||
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new SacrificeTargetCost(bargainFilter));
|
||||
this.additionalCost.setRepeatable(false);
|
||||
this.rule = additionalCost.getName() + additionalCost.getReminderText();
|
||||
this.setRuleAtTheTop(true);
|
||||
this.addHint(BargainCostWasPaidHint.instance);
|
||||
this.activationKey = null;
|
||||
}
|
||||
|
||||
private BargainAbility(final BargainAbility ability) {
|
||||
super(ability);
|
||||
this.rule = ability.rule;
|
||||
this.additionalCost = ability.additionalCost.copy();
|
||||
this.activationKey = ability.activationKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BargainAbility copy() {
|
||||
return new BargainAbility(this);
|
||||
}
|
||||
|
||||
public void resetBargain() {
|
||||
if (additionalCost != null) {
|
||||
additionalCost.reset();
|
||||
}
|
||||
this.activationKey = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOptionalAdditionalCosts(Ability ability, Game game) {
|
||||
if (!(ability instanceof SpellAbility)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = game.getPlayer(ability.getControllerId());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetBargain();
|
||||
boolean canPay = additionalCost.canPay(ability, this, ability.getControllerId(), game);
|
||||
if (!canPay || !player.chooseUse(Outcome.Sacrifice, promptString, ability, game)) {
|
||||
return;
|
||||
}
|
||||
|
||||
additionalCost.activate();
|
||||
for (Cost cost : ((Costs<Cost>) additionalCost)) {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
this.activationKey = getActivationKey(ability, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCastMessageSuffix() {
|
||||
return additionalCost.getCastSuffixMessage(0);
|
||||
}
|
||||
|
||||
public boolean wasBargained(Game game, Ability source) {
|
||||
return activationKey != null && getActivationKey(source, game).equalsIgnoreCase(activationKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: remove with Tag Cost Tracking.
|
||||
* Return activation zcc key for searching spell's settings in source object
|
||||
*
|
||||
* @param source
|
||||
* @param game
|
||||
*/
|
||||
public static String getActivationKey(Ability source, Game game) {
|
||||
// Bargain activates in STACK zone so all zcc must be from "stack moment"
|
||||
// Use cases:
|
||||
// * resolving spell have same zcc (example: check kicker status in sorcery/instant);
|
||||
// * copied spell have same zcc as source spell (see Spell.copySpell and zcc sync);
|
||||
// * creature/token from resolved spell have +1 zcc after moved to battlefield (example: check kicker status in ETB triggers/effects);
|
||||
|
||||
// find object info from the source ability (it can be a permanent or a spell on stack, on the moment of trigger/resolve)
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
Zone sourceObjectZone = game.getState().getZone(sourceObject.getId());
|
||||
int zcc = CardUtil.getActualSourceObjectZoneChangeCounter(game, source);
|
||||
|
||||
// find "stack moment" zcc:
|
||||
// * permanent cards enters from STACK to BATTLEFIELD (+1 zcc)
|
||||
// * permanent tokens enters from OUTSIDE to BATTLEFIELD (+1 zcc, see prepare code in TokenImpl.putOntoBattlefieldHelper)
|
||||
// * spells and copied spells resolves on STACK (zcc not changes)
|
||||
if (sourceObjectZone != Zone.STACK) {
|
||||
--zcc;
|
||||
}
|
||||
|
||||
return zcc + "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue