1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-12 01:01:04 -09:00

Work in progress changes to support As Foretold

* Modifies how cards with no mana cost are handled. You can now begin to cast them if there is an AlternativeCost that would allow you to play them.
This commit is contained in:
Mark Langen 2017-04-09 23:41:03 -06:00
parent 1ce32eb24e
commit b33e03862a
4 changed files with 230 additions and 7 deletions
Mage.Sets/src/mage
Mage/src/main/java/mage
abilities/effects
players

View file

@ -0,0 +1,228 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.Watcher;
/**
*
* @author stravant
*
* Note, this card is pretty hacky in its implementation due to the fact that the alternative cost system doesn't
* really support "once each turn" alternative costs in an obvious way, but it should work in all scenarios as far
* as I can see.
*/
public class AsForetold extends CardImpl {
public AsForetold(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
// At the beginning of your upkeep, put a time counter on As Foretold.
addAbility(
new BeginningOfUpkeepTriggeredAbility(
new AddCountersSourceEffect(CounterType.TIME.createInstance(), new StaticValue(1), true),
TargetController.YOU,
/* optional = */false));
// Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast with converted mana cost X or less, where X is the number of time counters on As Foretold.
addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AsForetoldAddAltCostEffect()), new AsForetoldAltCostUsedWatcher());
}
public AsForetold(final AsForetold card) {
super(card);
}
@Override
public AsForetold copy() {
return new AsForetold(this);
}
}
/**
* Used to determine what cast objects to apply the alternative cost to
*/
class SpellWithManaCostLessThanOrEqualToCondition implements Condition {
private int counters;
public SpellWithManaCostLessThanOrEqualToCondition(int counters) {
this.counters = counters;
}
@Override
public boolean apply(Game game, Ability source) {
MageObject object = game.getObject(source.getSourceId());
return object != null && !object.isLand() && object.getConvertedManaCost() <= counters;
}
}
/**
* Special AlternativeCostSourceAbility implementation. We wrap the call to askToActivateAlternativeCosts in order to
* tell when the alternative cost is used, and mark it as having been used this turn in the watcher
*/
class AsForetoldAlternativeCost extends AlternativeCostSourceAbility {
private UUID sourceAsForetold;
AsForetoldAlternativeCost(UUID sourceAsForetold, int timeCounters) {
super(new ManaCostsImpl("{0}"), new SpellWithManaCostLessThanOrEqualToCondition(timeCounters));
this.sourceAsForetold = sourceAsForetold;
}
AsForetoldAlternativeCost(final AsForetoldAlternativeCost ability) {
super(ability);
this.sourceAsForetold = ability.sourceAsForetold;
}
@Override
public AsForetoldAlternativeCost copy() {
return new AsForetoldAlternativeCost(this);
}
@Override
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
boolean activated = super.askToActivateAlternativeCosts(ability, game);
if (activated) {
// Get the watcher
AsForetoldAltCostUsedWatcher asForetoldAltCostUsedWatcher =
(AsForetoldAltCostUsedWatcher)game.getState().getWatchers()
.get("asForetoldAltCostUsedWatcher", sourceAsForetold);
// Mark as used
asForetoldAltCostUsedWatcher.markUsedThisTurn();
}
return activated;
}
}
/**
* The continuous effect that adds the option to pay the alternative cost if we haven't used it yet this turn
*/
class AsForetoldAddAltCostEffect extends ContinuousEffectImpl {
public AsForetoldAddAltCostEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "Once each turn, you may pay {0} rather than pay the mana cost for a spelly ou cast with converted mana cost X or less, where X is the number of time counters on {this}.";
}
public AsForetoldAddAltCostEffect(final AsForetoldAddAltCostEffect effect) {
super(effect);
}
@Override
public AsForetoldAddAltCostEffect copy() {
return new AsForetoldAddAltCostEffect(this);
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent != null) {
// Get the watcher
AsForetoldAltCostUsedWatcher asForetoldAltCostUsedWatcher =
(AsForetoldAltCostUsedWatcher)game.getState().getWatchers()
.get("asForetoldAltCostUsedWatcher", sourcePermanent.getId());
// If we haven't used it yet this turn, give the option of using the zero alternative cost
if (!asForetoldAltCostUsedWatcher.hasBeenUsedThisTurn()) {
int timeCounters = sourcePermanent.getCounters(game).getCount("time");
controller.getAlternativeSourceCosts().add(new AsForetoldAlternativeCost(sourcePermanent.getId(), timeCounters));
}
// Return true even if we didn't add the alt cost. We still applied the effect
return true;
}
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.RulesEffects;
}
}
/**
* Watcher used as extra storage to record whether a given As Foretold has been used this turn.
* Technically speaking this watcher doesn't *watch* any GameEvents, but it does "watch" the
* alternative cost being used. That just isn't possible to watch through a game event. It's still
* helpfull to co-op the Watcher system for this since it automatically handles ZoneChangeCounter
* stuff and resetting the condition at the end of the turn.
*/
class AsForetoldAltCostUsedWatcher extends Watcher {
public AsForetoldAltCostUsedWatcher() {
super("asForetoldAltCostUsedWatcher", WatcherScope.CARD);
}
public AsForetoldAltCostUsedWatcher(final AsForetoldAltCostUsedWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
// Nothing to do, we explicitly mark used in the alternative cost
}
public boolean hasBeenUsedThisTurn() {
return conditionMet();
}
public void markUsedThisTurn() {
condition = true;
}
@Override
public AsForetoldAltCostUsedWatcher copy() {
return new AsForetoldAltCostUsedWatcher(this);
}
}

View file

@ -72,6 +72,7 @@ public class Amonkhet extends ExpansionSet {
cards.add(new SetCardInfo("Angler Drake", 41, Rarity.UNCOMMON, mage.cards.a.AnglerDrake.class));
cards.add(new SetCardInfo("Anointer Priest", 3, Rarity.COMMON, mage.cards.a.AnointerPriest.class));
cards.add(new SetCardInfo("Archfiend of Ifnir", 78, Rarity.RARE, mage.cards.a.ArchfiendOfIfnir.class));
cards.add(new SetCardInfo("As Foretold", 42, Rarity.MYTHIC, mage.cards.a.AsForetold.class));
cards.add(new SetCardInfo("Aven Mindcensor", 5, Rarity.RARE, mage.cards.a.AvenMindcensor.class));
cards.add(new SetCardInfo("Bontu's Monument", 225, Rarity.UNCOMMON, mage.cards.b.BontusMonument.class));
cards.add(new SetCardInfo("Canyon Slough", 239, Rarity.RARE, mage.cards.c.CanyonSlough.class));

View file

@ -61,8 +61,8 @@ public abstract class EffectImpl implements Effect {
public EffectImpl(final EffectImpl effect) {
this.id = effect.id;
this.outcome = effect.outcome;
this.effectType = effect.effectType;
this.staticText = effect.staticText;
this.effectType = effect.effectType;
this.targetPointer = effect.targetPointer.copy();
if (effect.values != null) {
values = new HashMap<>();

View file

@ -1281,9 +1281,6 @@ public abstract class PlayerImpl implements Player, Serializable {
if (Zone.GRAVEYARD == zone && canPlayCardsFromGraveyard()) {
for (ActivatedAbility ability : candidateAbilites.getPlayableAbilities(Zone.HAND)) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.getManaCosts().isEmpty() && ability.getCosts().isEmpty() && ability instanceof SpellAbility) {
continue; // You can't play spells from graveyard that have no costs
}
if (ability.canActivate(playerId, game)) {
output.put(ability.getId(), ability);
}
@ -1293,9 +1290,6 @@ public abstract class PlayerImpl implements Player, Serializable {
if (zone != Zone.BATTLEFIELD && game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
for (Ability ability : candidateAbilites) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.getManaCosts().isEmpty() && ability.getCosts().isEmpty() && ability instanceof SpellAbility && !(Objects.equals(ability.getSourceId(), getCastSourceIdWithAlternateMana()))) {
continue; // You can't play spells that have no costs, unless you can play them without paying their mana costs
}
ability.setControllerId(this.getId());
if (ability instanceof ActivatedAbility && ability.getZone().match(Zone.HAND)
&& ((ActivatedAbility) ability).canActivate(playerId, game)) {