Please test! Some changes to the display of user choices, showing also a longer text in tooltip window.

This commit is contained in:
LevelX2 2015-06-28 21:55:48 +02:00
parent cac04616f3
commit df3e6db569
352 changed files with 2277 additions and 2034 deletions

View file

@ -104,7 +104,7 @@ public abstract class MageObjectImpl implements MageObject {
@Override
public String getLogName() {
return GameLog.getColoredObjectName(this);
return GameLog.getColoredObjectIdName(this);
}
@Override

View file

@ -346,7 +346,7 @@ public abstract class AbilityImpl implements Ability {
for (Cost cost : optionalCosts) {
if (cost instanceof ManaCost) {
cost.clearPaid();
if (controller.chooseUse(Outcome.Benefit, "Pay optional cost " + cost.getText() + "?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Pay optional cost " + cost.getText() + "?", this, game)) {
manaCostsToPay.add((ManaCost) cost);
}
}
@ -576,7 +576,7 @@ public abstract class AbilityImpl implements Ability {
protected boolean useAlternativeCost(Game game) {
for (AlternativeCost cost : alternativeCosts) {
if (cost.isAvailable(game, this)) {
if (game.getPlayer(this.controllerId).chooseUse(Outcome.Neutral, "Use alternative cost " + cost.getName(), game)) {
if (game.getPlayer(this.controllerId).chooseUse(Outcome.Neutral, "Use alternative cost " + cost.getName(), this, game)) {
return cost.pay(this, game, sourceId, controllerId, false);
}
}
@ -1013,9 +1013,9 @@ public abstract class AbilityImpl implements Ability {
if (object instanceof StackAbility) {
Card card = game.getCard(((StackAbility) object).getSourceId());
if (card != null) {
sb.append(GameLog.getColoredObjectName(card));
sb.append(GameLog.getColoredObjectIdName(card));
} else {
sb.append(GameLog.getColoredObjectName(object));
sb.append(GameLog.getColoredObjectIdName(object));
}
} else {
if (object instanceof Spell) {
@ -1027,7 +1027,7 @@ public abstract class AbilityImpl implements Ability {
}
sb.append(getOptionalTextSuffix(game, spell));
} else {
sb.append(GameLog.getColoredObjectName(object));
sb.append(GameLog.getColoredObjectIdName(object));
}
}
} else {

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities;
import java.util.UUID;
@ -80,24 +79,19 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
// TODO: Implement for all TriggeredAbilities so this default method can be removed
/*@Override
public boolean checkEventType(GameEvent event, Game game) {
return true;
}*/
public boolean checkEventType(GameEvent event, Game game) {
return true;
}*/
@Override
public boolean resolve(Game game) {
MageObject object = game.getObject(sourceId);
if (optional) {
MageObject object = game.getObject(getSourceId());
Player player = game.getPlayer(this.getControllerId());
StringBuilder sb = new StringBuilder();
if (object != null) {
sb.append("Use the following ability from ").append(object.getLogName()).append("? ");
sb.append(this.getRule(object.getLogName()));
}
else {
sb.append("Use the following ability? ").append(this.getRule());
}
if (!player.chooseUse(getEffects().get(0).getOutcome(), sb.toString(), game)) {
if (player != null) {
if (!player.chooseUse(getEffects().get(0).getOutcome(), (object != null ? this.getRule(object.getLogName()) : this.getRule()), this, game)) {
return false;
}
} else {
return false;
}
}
@ -116,11 +110,11 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
sb.append("Ability triggers: ").append(object.getLogName()).append(" - ").append(this.getRule(object.getLogName()));
} else {
sb.append("Ability triggers: ").append(this.getRule());
}
}
String targetText = getTargetDescriptionForLog(getTargets(), game);
if (!targetText.isEmpty()) {
sb.append(" - ").append(targetText);
}
}
return sb.toString();
}
@ -162,18 +156,22 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
/**
* 603.6. Trigger events that involve objects changing zones are called zone-change triggers.
* Many abilities with zone-change triggers attempt to do something to that object after it
* changes zones. During resolution, these abilities look for the object in the zone that
* it moved to. If the object is unable to be found in the zone it went to, the part of the
* ability attempting to do something to the object will fail to do anything. The ability could
* be unable to find the object because the object never entered the specified zone, because it
* left the zone before the ability resolved, or because it is in a zone that is hidden from
* a player, such as a library or an opponents hand. (This rule applies even if the object
* leaves the zone and returns again before the ability resolves.) The most common zone-change
* triggers are enters-the-battlefield triggers and leaves-the-battlefield triggers.
* 603.6. Trigger events that involve objects changing zones are called
* zone-change triggers. Many abilities with zone-change triggers
* attempt to do something to that object after it changes zones. During
* resolution, these abilities look for the object in the zone that it
* moved to. If the object is unable to be found in the zone it went to,
* the part of the ability attempting to do something to the object will
* fail to do anything. The ability could be unable to find the object
* because the object never entered the specified zone, because it left
* the zone before the ability resolved, or because it is in a zone that
* is hidden from a player, such as a library or an opponents hand.
* (This rule applies even if the object leaves the zone and returns
* again before the ability resolves.) The most common zone-change
* triggers are enters-the-battlefield triggers and
* leaves-the-battlefield triggers.
*/
if (event != null && event.getTargetId() != null && event.getTargetId().equals(getSourceId())) {
switch (event.getType()) {
@ -182,18 +180,18 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
if (event.getType().equals(EventType.DESTROYED_PERMANENT)) {
source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
} else {
if (((ZoneChangeEvent)event).getTarget() != null) {
source = ((ZoneChangeEvent)event).getTarget();
} else {
source = game.getLastKnownInformation(getSourceId(), ((ZoneChangeEvent)event).getZone());
if (((ZoneChangeEvent) event).getTarget() != null) {
source = ((ZoneChangeEvent) event).getTarget();
} else {
source = game.getLastKnownInformation(getSourceId(), ((ZoneChangeEvent) event).getZone());
}
}
case PHASED_OUT:
case PHASED_IN:
if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) {
return this.hasSourceObjectAbility(game, source, event);
}
if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) {
return this.hasSourceObjectAbility(game, source, event);
}
}
}
return super.isInUseableZone(game, source, event);

View file

@ -127,7 +127,7 @@ class KinshipBaseEffect extends OneShotEffect {
Cards cards = new CardsImpl(card);
controller.lookAtCards(sourcePermanent.getName(), cards, game);
if (CardUtil.shareSubtypes(sourcePermanent, card)) {
if (controller.chooseUse(outcome,new StringBuilder("Kinship - Reveal ").append(card.getLogName()).append("?").toString(), game)) {
if (controller.chooseUse(outcome,new StringBuilder("Kinship - Reveal ").append(card.getLogName()).append("?").toString(), source, game)) {
controller.revealCards(sourcePermanent.getName(), cards, game);
for (Effect effect: kinshipEffects) {
effect.setTargetPointer(new FixedTarget(card.getId()));

View file

@ -148,7 +148,7 @@ class PutIntoGraveFromAnywhereEffect extends ReplacementEffectImpl {
if (controller == null || object == null) {
return false;
}
if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), game)) {
if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), source, game)) {
return false;
}
}

View file

@ -98,7 +98,7 @@ class PactEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "?", game)) {
if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "?", source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)){
return true;

View file

@ -70,12 +70,14 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
}
/**
*
*
* @param cost alternate cost to pay
* @param condition only if the condition is true it's possible to use the alternate costs
* @param condition only if the condition is true it's possible to use the
* alternate costs
* @param rule if != null used as rule text
* @param filter filters the cards this alternate cost can be applied to
* @param onlyMana if true only the mana costs are replaced by this costs, other costs stay untouched
* @param onlyMana if true only the mana costs are replaced by this costs,
* other costs stay untouched
*/
public AlternativeCostSourceAbility(Cost cost, Condition condition, String rule, FilterCard filter, boolean onlyMana) {
super(Zone.ALL, null);
@ -86,7 +88,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
this.filter = filter;
this.onlyMana = onlyMana;
}
public AlternativeCostSourceAbility(Condition condition, String rule, FilterCard filter, boolean onlyMana, DynamicCost dynamicCost) {
super(Zone.ALL, null);
this.setRuleAtTheTop(true);
@ -109,14 +111,14 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
@Override
public void addCost(Cost cost) {
AlternativeCost2 alternativeCost = convertToAlternativeCost(cost);
if(alternativeCost != null) {
this.alternateCosts.add(alternativeCost);
}
AlternativeCost2 alternativeCost = convertToAlternativeCost(cost);
if (alternativeCost != null) {
this.alternateCosts.add(alternativeCost);
}
}
private AlternativeCost2 convertToAlternativeCost(Cost cost) {
return cost != null ? new AlternativeCost2Impl(null, cost.getText(), cost) : null;
return cost != null ? new AlternativeCost2Impl(null, cost.getText(), cost) : null;
}
@Override
@ -143,26 +145,25 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
}
Player player = game.getPlayer(ability.getControllerId());
if (player != null) {
Costs<AlternativeCost2> alternativeCosts;
if(dynamicCost != null) {
alternativeCosts = new CostsImpl<>();
alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(ability, game)));
} else {
alternativeCosts = this.alternateCosts;
}
String costChoiceText;
if(dynamicCost != null) {
costChoiceText = dynamicCost.getText(ability, game);
} else {
costChoiceText = alternativeCosts.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCosts.getText() +")";
}
if (alternativeCosts.canPay(ability, ability.getSourceId(), ability.getControllerId(), game) &&
player.chooseUse(Outcome.Benefit, costChoiceText, game)) {
Costs<AlternativeCost2> alternativeCosts;
if (dynamicCost != null) {
alternativeCosts = new CostsImpl<>();
alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(ability, game)));
} else {
alternativeCosts = this.alternateCosts;
}
String costChoiceText;
if (dynamicCost != null) {
costChoiceText = dynamicCost.getText(ability, game);
} else {
costChoiceText = alternativeCosts.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCosts.getText() + ")";
}
if (alternativeCosts.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
&& player.chooseUse(Outcome.Benefit, costChoiceText, this, game)) {
ability.getManaCostsToPay().clear();
if(!onlyMana) {
if (!onlyMana) {
ability.getCosts().clear();
}
for (Cost cost : alternativeCosts) {
@ -189,13 +190,13 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
@Override
public boolean isActivated(Ability source, Game game) {
Costs<AlternativeCost2> alternativeCosts;
if(dynamicCost != null) {
alternativeCosts = new CostsImpl<>();
alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(source, game)));
} else {
alternativeCosts = this.alternateCosts;
}
Costs<AlternativeCost2> alternativeCosts;
if (dynamicCost != null) {
alternativeCosts = new CostsImpl<>();
alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(source, game)));
} else {
alternativeCosts = this.alternateCosts;
}
for (AlternativeCost2 cost : alternativeCosts) {
if (cost.isActivated(game)) {
return true;
@ -206,7 +207,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
@Override
public String getCastMessageSuffix(Game game) {
return alternateCosts.isEmpty() ? " without paying it's mana costs":" using alternative casting costs";
return alternateCosts.isEmpty() ? " without paying it's mana costs" : " using alternative casting costs";
}
@Override
@ -227,7 +228,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
}
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 alternativeCost : alternateCosts) {
for (AlternativeCost2 alternativeCost : alternateCosts) {
if (numberCosts == 0) {
if (alternativeCost.getCost() instanceof ManaCost) {
sb.append("pay ");
@ -261,5 +262,5 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
alterCosts.addAll(alternateCosts);
return alterCosts;
}
}

View file

@ -1,35 +1,33 @@
/*
* 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.
*/
* 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.abilities.costs;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCost;
import mage.constants.Outcome;
@ -37,13 +35,8 @@ import mage.game.Game;
import mage.players.Player;
import mage.target.Targets;
/**
*
* @author LevelX2
*/
public class OrCost implements Cost {
private Cost firstCost;
private Cost secondCost;
private String description;
@ -92,12 +85,12 @@ public class OrCost implements Cost {
if (selectedCost == null) {
Player controller = game.getPlayer(controllerId);
if (controller != null) {
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
if (firstCost instanceof ManaCost) {
sb.append("Pay ");
}
sb.append(firstCost.getText()).append("?");
if (controller.chooseUse(Outcome.Detriment, sb.toString(), game)) {
if (controller.chooseUse(Outcome.Detriment, sb.toString(), ability, game)) {
selectedCost = firstCost;
} else {
selectedCost = secondCost;

View file

@ -29,12 +29,13 @@
package mage.abilities.costs.common;
import java.util.UUID;
import mage.constants.Outcome;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.costs.CostImpl;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
@ -46,7 +47,7 @@ public class RevealTargetFromHandCost extends CostImpl {
public RevealTargetFromHandCost(TargetCardInHand target) {
this.addTarget(target);
this.text = (target.getNumberOfTargets() == 0 ?"you may ":"") + "reveal " + target.getTargetName();
this.text = (target.getNumberOfTargets() == 0 ? "you may " : "") + "reveal " + target.getTargetName();
}
public RevealTargetFromHandCost(final RevealTargetFromHandCost cost) {
@ -70,8 +71,9 @@ public class RevealTargetFromHandCost extends CostImpl {
cards.add(card);
}
}
if (numberCardsRevealed > 0 ) {
player.revealCards("card cost", cards, game);
if (numberCardsRevealed > 0) {
MageObject baseObject = game.getBaseObject(sourceId);
player.revealCards(baseObject == null ? "card cost" : baseObject.getIdName(), cards, game);
}
if (targets.get(0).getNumberOfTargets() <= numberCardsRevealed) {
paid = true; // e.g. for optional additional costs. example: Dragonlord's Prerogative also true if 0 cards shown

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.decorator;
import mage.abilities.Ability;
@ -42,8 +41,7 @@ import mage.game.permanent.Permanent;
*
* @author LevelX2
*/
public class ConditionalRestrictionEffect extends RestrictionEffect {
public class ConditionalRestrictionEffect extends RestrictionEffect {
protected RestrictionEffect effect;
protected RestrictionEffect otherwiseEffect;
@ -55,7 +53,7 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
public ConditionalRestrictionEffect(RestrictionEffect effect, Condition condition) {
this(Duration.WhileOnBattlefield, effect, condition, null);
}
public ConditionalRestrictionEffect(Duration duration, RestrictionEffect effect, Condition condition, RestrictionEffect otherwiseEffect) {
super(duration);
this.effectType = EffectType.RESTRICTION;
@ -93,7 +91,6 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
initDone = true;
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (!initDone) { // if simpleStaticAbility, init won't be called
@ -102,7 +99,7 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
conditionState = condition.apply(game, source);
if (conditionState) {
effect.setTargetPointer(this.targetPointer);
return effect.applies(permanent, source,game);
return effect.applies(permanent, source, game);
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
return otherwiseEffect.applies(permanent, source, game);
@ -147,11 +144,11 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
}
@Override
public boolean canBeUntapped(Permanent permanent, Game game) {
public boolean canBeUntapped(Permanent permanent, Ability source, Game game) {
if (conditionState) {
return effect.canBeUntapped(permanent, game);
return effect.canBeUntapped(permanent, source, game);
} else if (otherwiseEffect != null) {
return otherwiseEffect.canBeUntapped(permanent, game);
return otherwiseEffect.canBeUntapped(permanent, source, game);
}
return true;
}

View file

@ -85,7 +85,7 @@ public class AsTurnedFaceUpEffect extends ReplacementEffectImpl {
if (controller == null || object == null) {
return false;
}
if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), game)) {
if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), source, game)) {
return false;
}
}

View file

@ -628,7 +628,7 @@ public class ContinuousEffects implements Serializable {
if (spliceAbilities.size() > 0) {
Player controller = game.getPlayer(abilityToModify.getControllerId());
if (controller.chooseUse(Outcome.Benefit, "Splice a card?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Splice a card?", abilityToModify, game)) {
Cards cardsToReveal = new CardsImpl();
do {
FilterCard filter = new FilterCard("a card to splice");
@ -655,7 +655,7 @@ public class ContinuousEffects implements Serializable {
spliceAbilities.remove(selectedAbility);
}
}
} while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", game));
} while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", abilityToModify, game));
controller.revealCards("Spliced cards", cardsToReveal, game);
}
}

View file

@ -111,7 +111,7 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
if (controller == null || object == null) {
return false;
}
if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), game)) {
if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), source, game)) {
return false;
}
}

View file

@ -77,7 +77,7 @@ public class PlaneswalkerRedirectionEffect extends RedirectionEffect {
Player player = game.getPlayer(playerId);
if (target != null && player != null) {
int numPlaneswalkers = game.getBattlefield().countAll(filter, target.getId(), game);
if (numPlaneswalkers > 0 && player.chooseUse(outcome, "Redirect damage to planeswalker?", game)) {
if (numPlaneswalkers > 0 && player.chooseUse(outcome, "Redirect damage to planeswalker?", source, game)) {
redirectTarget = new TargetPermanent(filter);
if (numPlaneswalkers == 1) {
List<Permanent> planeswalker = game.getBattlefield().getAllActivePermanents(filter, target.getId(), game);

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.abilities.effects;
import java.util.UUID;
@ -48,7 +47,7 @@ public abstract class RestrictionEffect extends ContinuousEffectImpl {
public RestrictionEffect(Duration duration, Outcome outcome) {
super(duration, outcome);
this.effectType = EffectType.RESTRICTION;
this.effectType = EffectType.RESTRICTION;
}
public RestrictionEffect(final RestrictionEffect effect) {
@ -65,7 +64,7 @@ public abstract class RestrictionEffect extends ContinuousEffectImpl {
public boolean canAttack(Game game) {
return true;
}
public boolean canAttack(UUID defenderId, Ability source, Game game) {
return true;
}
@ -80,22 +79,23 @@ public abstract class RestrictionEffect extends ContinuousEffectImpl {
/**
* Called for all attackers after all blocking decisions are made
*
*
* @param attacker
* @param source
* @param game
* @return true = block is ok false = block is not valid (human: back to defining blockers, AI: remove blocker)
* @return true = block is ok false = block is not valid (human: back to
* defining blockers, AI: remove blocker)
*/
public boolean canBeBlockedCheckAfter(Permanent attacker, Ability source, Game game) {
return true;
}
public boolean canBeUntapped(Permanent permanent, Game game) {
public boolean canBeUntapped(Permanent permanent, Ability source, Game game) {
return true;
}
public boolean canUseActivatedAbilities(Permanent permanent, Ability source, Game game) {
return true;
}
}

View file

@ -109,7 +109,7 @@ public class AmplifyEffect extends ReplacementEffectImpl {
filter.add(filterSubtypes.get(0));
}
if (controller.getHand().count(filter, source.getSourceId(), source.getControllerId(), game) > 0){
if (controller.chooseUse(outcome, "Reveal cards to Amplify?", game)) {
if (controller.chooseUse(outcome, "Reveal cards to Amplify?", source, game)) {
TargetCardInHand target = new TargetCardInHand(0, Integer.MAX_VALUE, filter);
if (controller.chooseTarget(outcome, target, source, game) && !target.getTargets().isEmpty()) {
Cards cards = new CardsImpl();

View file

@ -73,7 +73,7 @@ public class CastCardFromOutsideTheGameEffect extends OneShotEffect {
return false;
}
while (player.chooseUse(Outcome.Benefit, choiceText, game)) {
while (player.chooseUse(Outcome.Benefit, choiceText, source, game)) {
Cards cards = player.getSideboard();
if (cards.isEmpty()) {
if (!game.isSimulation())

View file

@ -89,7 +89,7 @@ public class CipherEffect extends OneShotEffect {
TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent();
if (controller != null) {
if (target.canChoose(source.getControllerId(), game)
&& controller.chooseUse(outcome, "Cipher this spell to a creature?", game)) {
&& controller.chooseUse(outcome, "Cipher this spell to a creature?", source, game)) {
controller.chooseTarget(outcome, target, source, game);
Card sourceCard = game.getCard(source.getSourceId());
Permanent targetCreature = game.getPermanent(target.getFirstTarget());

View file

@ -25,10 +25,10 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common;
import java.io.ObjectStreamException;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.MageSingleton;
import mage.abilities.effects.Effect;
@ -47,54 +47,72 @@ import mage.target.Target;
import mage.target.common.TargetOpponent;
/**
1. The controller of the spell or ability chooses an opponent. (This doesn't target the opponent.)
2. Each player involved in the clash reveals the top card of his or her library.
3. The converted mana costs of the revealed cards are noted.
4. In turn order, each player involved in the clash chooses to put his or her revealed card on either the top or bottom of his or her library. (Note that the player whose turn it is does this first, not necessarily the controller of the clash spell or ability.) When the second player makes this decision, he or she will know what the first player chose. Then all cards are moved at the same time.
5. The clash is over. If one player in the clash revealed a card with a higher converted mana cost than all other cards revealed in the clash, that player wins the clash.
6. If any abilities trigger when a player clashes, they trigger and wait to be put on the stack.
7. The clash spell or ability finishes resolving. That usually involves a bonus gained by the controller of the clash spell or ability if he or she won the clash.
8. Abilities that triggered during the clash are put on the stack.
There are no draws or losses in a clash. Either you win it or you don't.
Each spell or ability with clash says what happens if you (the controller of that spell or ability) win the clash. Typically, if you don't win the clash, nothing happens.
If no one reveals a card with a higher converted mana cost (for example, each player reveals a card with converted mana cost 2), no one wins the clash.
An X in a revealed card's mana cost is treated as 0.
A card without a mana cost (such as a land) has a converted mana cost of 0.
If one or more of the clashing players reveals a split card, each of the split card's converted mana costs is considered individually. In this way, it's possible for multiple players to win a clash. For example, if Player A reveals a split card with converted mana costs 1 and 3, and Player B reveals a card with converted mana cost 2, they'll both win. (Player A's card has a higher converted mana cost than Player B's card, since 3 is greater than 2. Player B's card has a higher converted mana cost than Player A's card, since 2 is greater than 1.)
* 1. The controller of the spell or ability chooses an opponent. (This doesn't
* target the opponent.) 2. Each player involved in the clash reveals the top
* card of his or her library. 3. The converted mana costs of the revealed cards
* are noted. 4. In turn order, each player involved in the clash chooses to put
* his or her revealed card on either the top or bottom of his or her library.
* (Note that the player whose turn it is does this first, not necessarily the
* controller of the clash spell or ability.) When the second player makes this
* decision, he or she will know what the first player chose. Then all cards are
* moved at the same time. 5. The clash is over. If one player in the clash
* revealed a card with a higher converted mana cost than all other cards
* revealed in the clash, that player wins the clash. 6. If any abilities
* trigger when a player clashes, they trigger and wait to be put on the stack.
* 7. The clash spell or ability finishes resolving. That usually involves a
* bonus gained by the controller of the clash spell or ability if he or she won
* the clash. 8. Abilities that triggered during the clash are put on the stack.
*
* There are no draws or losses in a clash. Either you win it or you don't. Each
* spell or ability with clash says what happens if you (the controller of that
* spell or ability) win the clash. Typically, if you don't win the clash,
* nothing happens. If no one reveals a card with a higher converted mana cost
* (for example, each player reveals a card with converted mana cost 2), no one
* wins the clash. An X in a revealed card's mana cost is treated as 0. A card
* without a mana cost (such as a land) has a converted mana cost of 0. If one
* or more of the clashing players reveals a split card, each of the split
* card's converted mana costs is considered individually. In this way, it's
* possible for multiple players to win a clash. For example, if Player A
* reveals a split card with converted mana costs 1 and 3, and Player B reveals
* a card with converted mana cost 2, they'll both win. (Player A's card has a
* higher converted mana cost than Player B's card, since 3 is greater than 2.
* Player B's card has a higher converted mana cost than Player A's card, since
* 2 is greater than 1.)
*
* @author LevelX2
*/
public class ClashEffect extends OneShotEffect implements MageSingleton {
private static final ClashEffect fINSTANCE = new ClashEffect();
private static final ClashEffect fINSTANCE = new ClashEffect();
private Object readResolve() throws ObjectStreamException {
return fINSTANCE;
}
}
private ClashEffect() {
super(Outcome.Benefit);
this.staticText = "Clash with an opponent";
}
public static ClashEffect getInstance() {
return fINSTANCE;
}
}
public ClashEffect(final ClashEffect effect) {
super(effect);
}
@Override
public ClashEffect copy() {
return new ClashEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CLASH, controller.getId(), controller.getId()))) {
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null
&& !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CLASH, controller.getId(), controller.getId()))) {
// choose opponent
Target target = new TargetOpponent(true);
target.setTargetName("an opponent to clash with");
@ -110,11 +128,11 @@ public class ClashEffect extends OneShotEffect implements MageSingleton {
// Reveal top cards of involved players
StringBuilder message = new StringBuilder("Clash: ");
message.append(controller.getLogName());
if (controller.getLibrary().size() > 0) {
if (controller.getLibrary().size() > 0) {
Cards cards = new CardsImpl();
cardController = controller.getLibrary().getFromTop(game);
cards.add(cardController);
controller.revealCards("for clash by " + controller.getLogName(), cards, game);
controller.revealCards(sourceObject.getIdName() + ": Clash card of " + controller.getName(), cards, game);
cmcController = cardController.getManaCost().convertedManaCost();
message.append(" (").append(cmcController).append(")");
} else {
@ -125,7 +143,7 @@ public class ClashEffect extends OneShotEffect implements MageSingleton {
Cards cards = new CardsImpl();
cardOpponent = opponent.getLibrary().getFromTop(game);
cards.add(cardOpponent);
opponent.revealCards("for clash by " + opponent.getLogName(), cards, game);
opponent.revealCards(sourceObject.getIdName() + ": Clash card of " + opponent.getName(), cards, game);
cmcOpponent = cardOpponent.getManaCost().convertedManaCost();
message.append(" (").append(cmcOpponent).append(")");
} else {
@ -141,37 +159,37 @@ public class ClashEffect extends OneShotEffect implements MageSingleton {
game.informPlayer(controller, opponent.getLogName() + " won the clash!");
} else {
message.append(" no winner ");
}
}
game.informPlayers(message.toString());
}
}
// decide to put the cards on top or on the buttom of library in turn order beginning with the active player in turn order
PlayerList playerList = game.getPlayerList().copy();
playerList.setCurrent(game.getActivePlayerId());
do {
Player current = playerList.getCurrent(game);
if (cardController != null && current.getId().equals(controller.getId())) {
topController = current.chooseUse(Outcome.Detriment, "Put " + cardController.getLogName() + " back on top of your library? (otherwise it goes to bottom)" , game);
}
topController = current.chooseUse(Outcome.Detriment, "Put " + cardController.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game);
}
if (cardOpponent != null && current.getId().equals(opponent.getId())) {
topOpponent = current.chooseUse(Outcome.Detriment, "Put " + cardOpponent.getLogName() + " back on top of your library? (otherwise it goes to bottom)" , game);
}
topOpponent = current.chooseUse(Outcome.Detriment, "Put " + cardOpponent.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game);
}
} while (!playerList.getNext(game).getId().equals(game.getActivePlayerId()));
// put the cards back to library
if (cardController != null) {
if (cardController != null) {
controller.moveCardToLibraryWithInfo(cardController, source.getSourceId(), game, Zone.LIBRARY, topController, true);
}
if (cardOpponent != null) {
if (cardOpponent != null) {
opponent.moveCardToLibraryWithInfo(cardOpponent, source.getSourceId(), game, Zone.LIBRARY, topOpponent, true);
}
game.fireEvent(new GameEvent(EventType.CLASHED, opponent.getId(), source.getSourceId(), controller.getId(), 0, cmcController > cmcOpponent));
// set opponent to DoIfClashWonEffect
for (Effect effect :source.getEffects()) {
for (Effect effect : source.getEffects()) {
if (effect instanceof DoIfClashWonEffect) {
effect.setValue("clashOpponent", opponent);
}
}
}
}
return cmcController > cmcOpponent;
}
}
}
}
return false;

View file

@ -93,7 +93,7 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
message = costToPay.getText() + " to prevent counter effect?";
}
costToPay.clearPaid();
if (!(player.chooseUse(Outcome.Benefit, message, game) && costToPay.pay(source, game, spell.getSourceId(), spell.getControllerId(), false))) {
if (!(player.chooseUse(Outcome.Benefit, message, source, game) && costToPay.pay(source, game, spell.getSourceId(), spell.getControllerId(), false))) {
return game.getStack().counter(spell.getId(), source.getSourceId(), game);
}
return true;

View file

@ -136,7 +136,7 @@ public class DevourEffect extends ReplacementEffectImpl {
if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) {
return false;
}
if (controller.chooseUse(Outcome.Detriment, "Devour creatures?", game)) {
if (controller.chooseUse(Outcome.Detriment, "Devour creatures?", source, game)) {
controller.chooseTarget(Outcome.Detriment, target, source, game);
if (target.getTargets().size() > 0) {
List<ArrayList<String>> cardSubtypes = new ArrayList<>();

View file

@ -79,7 +79,7 @@ public class DoIfClashWonEffect extends OneShotEffect {
message = CardUtil.replaceSourceName(message, mageObject.getLogName());
}
if (chooseUseText == null || player.chooseUse(executingEffect.getOutcome(), message, game)) {
if (chooseUseText == null || player.chooseUse(executingEffect.getOutcome(), message, source, game)) {
if (ClashEffect.getInstance().apply(game, source)) {
if (setTargetPointerToClashedOpponent) {
Object opponent = getValue("clashOpponent");

View file

@ -14,6 +14,7 @@ import mage.players.Player;
import mage.util.CardUtil;
public class DoIfCostPaid extends OneShotEffect {
protected Effects executingEffects = new Effects();
private final Cost cost;
private String chooseUseText;
@ -39,7 +40,7 @@ public class DoIfCostPaid extends OneShotEffect {
public void addEffect(Effect effect) {
executingEffects.add(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = getPayingPlayer(game, source);
@ -48,28 +49,27 @@ public class DoIfCostPaid extends OneShotEffect {
String message;
if (chooseUseText == null) {
String effectText = executingEffects.getText(source.getModes().getMode());
if (effectText.length() > 0 && effectText.charAt(effectText.length()-1)=='.') {
effectText = effectText.substring(0, effectText.length()-1);
if (effectText.length() > 0 && effectText.charAt(effectText.length() - 1) == '.') {
effectText = effectText.substring(0, effectText.length() - 1);
}
message = getCostText() +" and " + effectText + "?";
message = getCostText() + " and " + effectText + "?";
} else {
message = chooseUseText;
}
message = CardUtil.replaceSourceName(message, mageObject.getLogName());
boolean result = true;
if (cost.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(executingEffects.get(0).getOutcome(), message, game)) {
if (cost.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(executingEffects.get(0).getOutcome(), message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) {
for(Effect effect: executingEffects) {
for (Effect effect : executingEffects) {
effect.setTargetPointer(this.targetPointer);
if (effect instanceof OneShotEffect) {
result &= effect.apply(game, source);
}
else {
} else {
game.addEffect((ContinuousEffect) effect, source);
}
}
player.resetStoredBookmark(game); // otherwise you can undo card drawn with Mentor of the Meek
}
}
return result;
@ -108,4 +108,3 @@ public class DoIfCostPaid extends OneShotEffect {
return new DoIfCostPaid(this);
}
}

View file

@ -91,7 +91,7 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
// check if any player is willing to pay
for (UUID playerId: controller.getInRange()) {
Player player = game.getPlayer(playerId);
if (player != null && cost.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, game)) {
if (player != null && cost.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) {
if (!game.isSimulation())

View file

@ -90,7 +90,7 @@ public class DrawCardTargetEffect extends OneShotEffect {
if (upTo) {
cardsToDraw = player.getAmount(0, cardsToDraw, "Draw how many cards?", game);
}
if (!optional || player.chooseUse(outcome, "Use draw effect?", game)) {
if (!optional || player.chooseUse(outcome, "Use draw effect?", source, game)) {
player.drawCards(cardsToDraw, game);
}
return true;

View file

@ -82,7 +82,7 @@ public class DrawDiscardControllerEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
if (!optional || player.chooseUse(outcome, "Use draw, then discard effect?", game)) {
if (!optional || player.chooseUse(outcome, "Use draw, then discard effect?", source, game)) {
player.drawCards(cardsToDraw, game);
player.discard(cardsToDiscard, false, source, game);
}

View file

@ -78,7 +78,7 @@ public class EnterBattlefieldPayCostOrPutGraveyardEffect extends ReplacementEffe
if (player != null && cost != null && sourceObject != null){
boolean replace = true;
if (cost.canPay(source, source.getSourceId(), player.getId(), game)) {
if (player.chooseUse(outcome, cost.getText() + "? (otherwise " + sourceObject.getLogName() + " is put into graveyard)", game)) {
if (player.chooseUse(outcome, cost.getText() + "? (otherwise " + sourceObject.getLogName() + " is put into graveyard)", source, game)) {
cost.clearPaid();
replace = !cost.pay(source, game, source.getSourceId(), source.getControllerId(), false);
}

View file

@ -70,7 +70,7 @@ public class HideawayPlayEffect extends OneShotEffect {
if (card.getCardType().contains(CardType.LAND)) {
// If the revealed card is a land, you can play it only if it's your turn and you haven't yet played a land this turn.
if (game.getActivePlayerId().equals(source.getControllerId()) && controller.canPlayLand()) {
if (controller.chooseUse(Outcome.Benefit, "Play " + card.getLogName() + " from Exile?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Play " + card.getLogName() + " from Exile?", source, game)) {
card.setFaceDown(false, game);
return controller.playLand(card, game);
}
@ -82,7 +82,7 @@ public class HideawayPlayEffect extends OneShotEffect {
// The land's last ability allows you to play the removed card as part of the resolution of that ability.
// Timing restrictions based on the card's type are ignored (for instance, if it's a creature or sorcery).
// Other play restrictions are not (such as "Play [this card] only during combat").
if (controller.chooseUse(Outcome.Benefit, "Cast "+ card.getLogName() + " without paying it's mana cost?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Cast "+ card.getLogName() + " without paying it's mana cost?", source, game)) {
card.setFaceDown(false, game);
return controller.cast(card.getSpellAbility(), game, true);
}

View file

@ -127,7 +127,7 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
protected void actionWithSelectedCards(Cards cards, Game game, Ability source, String windowName) {
Player player = game.getPlayer(source.getControllerId());
if (player != null && foundCardsToPick > 0) {
if (!optional || player.chooseUse(Outcome.DrawCard, getMayText(), game)) {
if (!optional || player.chooseUse(Outcome.DrawCard, getMayText(), source, game)) {
FilterCard pickFilter = filter.copy();
pickFilter.setMessage(getPickText());
TargetCard target = new TargetCard((upTo ? 0:numberToPick.calculate(game, source, this)),numberToPick.calculate(game, source, this), Zone.PICK, pickFilter);

View file

@ -27,8 +27,6 @@
*/
package mage.abilities.effects.common;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
@ -38,6 +36,8 @@ import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -142,7 +142,7 @@ public class LookLibraryControllerEffect extends OneShotEffect {
player.setTopCardRevealed(topCardRevealed);
this.mayShuffle(player, game);
this.mayShuffle(player, source, game);
return true;
}
@ -162,19 +162,19 @@ public class LookLibraryControllerEffect extends OneShotEffect {
* @param game
*/
protected void putCardsBack(Ability source, Player player, Cards cards, Game game) {
switch(targetZoneLookedCards) {
case LIBRARY:
switch (targetZoneLookedCards) {
case LIBRARY:
if (putOnTop) {
player.putCardsOnTopOfLibrary(cards, game, source, true);
} else {
player.putCardsOnBottomOfLibrary(cards, game, source, true);
}
break;
case GRAVEYARD:
case GRAVEYARD:
player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game);
break;
default:
// not supported yet
// not supported yet
}
}
@ -184,8 +184,8 @@ public class LookLibraryControllerEffect extends OneShotEffect {
* @param player
* @param game
*/
protected void mayShuffle(Player player, Game game) {
if (this.mayShuffleAfter && player.chooseUse(Outcome.Benefit, "Shuffle your library?", game)) {
protected void mayShuffle(Player player, Ability source, Game game) {
if (this.mayShuffleAfter && player.chooseUse(Outcome.Benefit, "Shuffle your library?", source, game)) {
player.shuffleLibrary(game);
}
}
@ -226,7 +226,6 @@ public class LookLibraryControllerEffect extends OneShotEffect {
sb.append(", where {X} is the number of cards ").append(numberOfCards.getMessage());
}
if (!middleText.isEmpty()) {
sb.append(middleText);
} else if (numberLook > 1) {

View file

@ -45,7 +45,7 @@ public class LookLibraryMayPutToBottomEffect extends OneShotEffect {
return false;
}
controller.lookAtCards(sourceObject.getName(), new CardsImpl(card), game);
boolean toBottom = controller.chooseUse(outcome, "Put card on the bottom of your library?", game);
boolean toBottom = controller.chooseUse(outcome, "Put card on the bottom of your library?", source, game);
return controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, !toBottom, false);
}
return true;

View file

@ -26,11 +26,11 @@ public class MayTapOrUntapTargetEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
if (target != null && player != null) {
if (target.isTapped()) {
if (player.chooseUse(Outcome.Untap, "Untap that permanent?", game)) {
if (player.chooseUse(Outcome.Untap, "Untap that permanent?", source, game)) {
target.untap(game);
}
} else {
if (player.chooseUse(Outcome.Tap, "Tap that permanent?", game)) {
if (player.chooseUse(Outcome.Tap, "Tap that permanent?", source, game)) {
target.tap(game);
}
}

View file

@ -34,7 +34,7 @@ public class PutCreatureOnBattlefieldEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null || !player.chooseUse(Outcome.PutCreatureInPlay, choiceText, game)) {
if (player == null || !player.chooseUse(Outcome.PutCreatureInPlay, choiceText, source, game)) {
return false;
}

View file

@ -67,7 +67,7 @@ public class PutLandFromHandOntoBattlefieldEffect extends OneShotEffect {
if (controller != null) {
Target target = new TargetCardInHand(new FilterLandCard("land card"));
if (target.canChoose(source.getSourceId(), source.getControllerId(), game) &&
controller.chooseUse(outcome, "Put land onto battlefield?", game) &&
controller.chooseUse(outcome, "Put land onto battlefield?", source, game) &&
controller.choose(outcome, target, source.getSourceId(), game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {

View file

@ -75,7 +75,7 @@ public class PutOntoBattlefieldTargetEffect extends OneShotEffect {
if (controller == null || !controller.chooseUse(Outcome.PutCreatureInPlay,
new StringBuilder("Put ")
.append(source.getTargets() != null ? source.getTargets().get(0).getTargetName() : "target")
.append(" onto the battlefield?").toString(), game)) {
.append(" onto the battlefield?").toString(), source, game)) {
return false;
}
}

View file

@ -42,7 +42,7 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect {
}
String message = CardUtil.replaceSourceName(sb.toString(), sourceObject.getLogName());
message = Character.toUpperCase(message.charAt(0)) + message.substring(1);
if (player.chooseUse(Outcome.Benefit, message, game)) {
if (player.chooseUse(Outcome.Benefit, message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) {
return true;

View file

@ -39,8 +39,6 @@ import mage.players.Player;
*
* @author LevelX2
*/
public class SkipUntapOptionalSourceEffect extends RestrictionEffect {
public SkipUntapOptionalSourceEffect() {
@ -54,15 +52,15 @@ public class SkipUntapOptionalSourceEffect extends RestrictionEffect {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return permanent.getId().equals(source.getSourceId()) &&
permanent.getControllerId().equals(game.getActivePlayerId()) && // your untap step
return permanent.getId().equals(source.getSourceId())
&& permanent.getControllerId().equals(game.getActivePlayerId()) && // your untap step
permanent.isTapped();
}
@Override
public boolean canBeUntapped(Permanent permanent, Game game) {
public boolean canBeUntapped(Permanent permanent, Ability source, Game game) {
Player player = game.getPlayer(permanent.getControllerId());
return player != null && player.chooseUse(Outcome.Benefit, "Untap " + permanent.getLogName() + "?", game);
return player != null && player.chooseUse(Outcome.Benefit, "Untap " + permanent.getLogName() + "?", source, game);
}
@Override

View file

@ -61,7 +61,7 @@ public class TapSourceUnlessPaysEffect extends OneShotEffect {
Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && permanent != null) {
if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game)
&& player.chooseUse(Outcome.Benefit, cost.getText() + "? (otherwise " + permanent.getName() + " becomes tapped)", game)) {
&& player.chooseUse(Outcome.Benefit, cost.getText() + "? (otherwise " + permanent.getName() + " becomes tapped)", source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) {
return true;

View file

@ -73,7 +73,7 @@ public class BecomesColorOrColorsTargetEffect extends OneShotEffect {
if (controller != null && target != null) {
for (int i = 0; i < 5; i++) {
if (!controller.chooseUse(Outcome.Neutral, "Do you wish to choose another color?", game)) {
if (!controller.chooseUse(Outcome.Neutral, "Do you wish to choose another color?", source, game)) {
break;
}
ChoiceColor choiceColor = new ChoiceColor();

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities.effects.common.continuous;
import java.util.Iterator;
@ -83,7 +82,8 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
* @param toughness
* @param duration
* @param filter
* @param lockedIn if true, power and toughness will be calculated only once, when the ability resolves
* @param lockedIn if true, power and toughness will be calculated only
* once, when the ability resolves
* @param excludeSource
*/
public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource, boolean lockedIn) {
@ -129,7 +129,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
@Override
public boolean apply(Game game, Ability source) {
if (this.affectedObjectsSet) {
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) {
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) {
Permanent permanent = it.next().getPermanent(game);
if (permanent != null) {
permanent.addPower(power.calculate(game, source, this));
@ -154,19 +154,19 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
String message = null;
StringBuilder sb = new StringBuilder();
if (excludeSource) {
sb.append("Other ");
sb.append("other ");
}
sb.append(filter.getMessage());
sb.append(" you control get ");
String p = power.toString();
if(!p.startsWith("-")) {
if (!p.startsWith("-")) {
sb.append("+");
}
sb.append(p).append("/");
String t = toughness.toString();
if(!t.startsWith("-")){
if(p.startsWith("-")) {
if (!t.startsWith("-")) {
if (p.startsWith("-")) {
sb.append("-");
} else {
sb.append("+");
@ -174,7 +174,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
}
sb.append(t);
sb.append((duration==Duration.EndOfTurn?" until end of turn":""));
sb.append((duration == Duration.EndOfTurn ? " until end of turn" : ""));
if (t.equals("X")) {
message = toughness.getMessage();
} else if (p.equals("X")) {

View file

@ -132,7 +132,7 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
Permanent permanent = ((ZoneChangeEvent)event).getTarget();
if (permanent != null) {
Player player = game.getPlayer(permanent.getOwnerId());
if (player != null && player.chooseUse(Outcome.Benefit, "Move commander to command zone?", game)){
if (player != null && player.chooseUse(Outcome.Benefit, "Move commander to command zone?", source, game)){
boolean result = permanent.moveToZone(Zone.COMMAND, source.getSourceId(), game, false);
if (!game.isSimulation())
game.informPlayers(player.getLogName() + " has moved his or her commander to the command zone");
@ -152,7 +152,7 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
}
if (card != null) {
Player player = game.getPlayer(card.getOwnerId());
if (player != null && player.chooseUse(Outcome.Benefit, "Move commander to command zone?", game)){
if (player != null && player.chooseUse(Outcome.Benefit, "Move commander to command zone?", source, game)){
boolean result = card.moveToZone(Zone.COMMAND, source.getSourceId(), game, false);
if (!game.isSimulation())
game.informPlayers(player.getLogName() + " has moved his or her commander to the command zone");

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
@ -73,10 +72,10 @@ public class UntapAllDuringEachOtherPlayersUntapStepEffect extends ContinuousEff
if (!applied && layer.equals(Layer.RulesEffects)) {
if (!game.getActivePlayerId().equals(source.getControllerId()) && game.getStep().getType() == PhaseStep.UNTAP) {
game.getState().setValue(source.getSourceId() + "applied", true);
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
boolean untap = true;
for (RestrictionEffect effect: game.getContinuousEffects().getApplicableRestrictionEffects(permanent, game).keySet()) {
untap &= effect.canBeUntapped(permanent, game);
for (RestrictionEffect effect : game.getContinuousEffects().getApplicableRestrictionEffects(permanent, game).keySet()) {
untap &= effect.canBeUntapped(permanent, source, game);
}
if (untap) {
permanent.untap(game);

View file

@ -28,8 +28,6 @@
package mage.abilities.keyword;
import java.util.Iterator;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
@ -43,6 +41,7 @@ import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
@ -51,16 +50,16 @@ import mage.players.Player;
/**
* 702.25. Buyback
*
* 702.25a Buyback appears on some instants and sorceries. It represents two static
* abilities that function while the spell is on the stack. "Buyback [cost]" means
* "You may pay an additional [cost] as you cast this spell" and "If the buyback
* cost was paid, put this spell into its owner's hand instead of into that player's
* graveyard as it resolves." Paying a spell's buyback cost follows the rules for
* paying additional costs in rules 601.2b and 601.2e-g.
* 702.25a Buyback appears on some instants and sorceries. It represents two
* static abilities that function while the spell is on the stack. "Buyback
* [cost]" means "You may pay an additional [cost] as you cast this spell" and
* "If the buyback cost was paid, put this spell into its owner's hand instead
* of into that player's graveyard as it resolves." Paying a spell's buyback
* cost follows the rules for paying additional costs in rules 601.2b and
* 601.2e-g.
*
* @author LevelX2
*/
public class BuybackAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
private static final String keywordText = "Buyback";
@ -69,25 +68,25 @@ public class BuybackAbility extends StaticAbility implements OptionalAdditionalS
protected OptionalAdditionalCost buybackCost;
public BuybackAbility(String manaString) {
super(Zone.STACK, new BuybackEffect());
this.buybackCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString));
setRuleAtTheTop(true);
super(Zone.STACK, new BuybackEffect());
this.buybackCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString));
setRuleAtTheTop(true);
}
public BuybackAbility(Cost cost) {
super(Zone.STACK, new BuybackEffect());
this.buybackCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, cost);
setRuleAtTheTop(true);
super(Zone.STACK, new BuybackEffect());
this.buybackCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, cost);
setRuleAtTheTop(true);
}
public BuybackAbility(final BuybackAbility ability) {
super(ability);
buybackCost = ability.buybackCost;
super(ability);
buybackCost = ability.buybackCost;
}
@Override
public BuybackAbility copy() {
return new BuybackAbility(this);
return new BuybackAbility(this);
}
@Override
@ -117,7 +116,7 @@ public class BuybackAbility extends StaticAbility implements OptionalAdditionalS
if (player != null) {
this.resetBuyback();
if (buybackCost != null) {
if (player.chooseUse(Outcome.Benefit,new StringBuilder("Pay ").append(buybackCost.getText(false)).append(" ?").toString(), game)) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(buybackCost.getText(false)).append(" ?").toString(), ability, game)) {
buybackCost.activate();
for (Iterator it = ((Costs) buybackCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
@ -133,7 +132,6 @@ public class BuybackAbility extends StaticAbility implements OptionalAdditionalS
}
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
@ -186,8 +184,8 @@ class BuybackEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(source.getSourceId())) {
ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
if (zEvent.getFromZone() == Zone.STACK ) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getFromZone() == Zone.STACK) {
return true;
}
}
@ -210,4 +208,4 @@ class BuybackEffect extends ReplacementEffectImpl {
return false;
}
}
}

View file

@ -114,7 +114,7 @@ public class CascadeAbility extends TriggeredAbilityImpl {
player.getLibrary().reset();
if (card != null) {
if (player.chooseUse(outcome, "Use cascade effect on " + card.getName() + "?", game)) {
if (player.chooseUse(outcome, "Use cascade effect on " + card.getName() + "?", source, game)) {
if(player.cast(card.getSpellAbility(), game, true)){
exile.remove(card.getId());
}

View file

@ -126,7 +126,7 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
Player player = game.getPlayer(controllerId);
if (player != null) {
this.resetConspire();
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(conspireCost.getText(false)).append(" ?").toString(), game)) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(conspireCost.getText(false)).append(" ?").toString(), ability, game)) {
conspireCost.activate();
for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
@ -245,8 +245,9 @@ class ConspireEffect extends OneShotEffect {
copy.setCopiedSpell(true);
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString());
}
return true;
}
}

View file

@ -107,7 +107,7 @@ class CumulativeUpkeepEffect extends OneShotEffect {
for(int i = 0 ; i < ageCounter; i++){
totalCost.add((ManaCost) cumulativeCost.copy());
}
if (player.chooseUse(Outcome.Benefit, "Pay " + totalCost.getText() + "?", game)) {
if (player.chooseUse(Outcome.Benefit, "Pay " + totalCost.getText() + "?", source, game)) {
totalCost.clearPaid();
if (totalCost.payOrRollback(source, game, source.getSourceId(), source.getControllerId())){
return true;
@ -121,7 +121,7 @@ class CumulativeUpkeepEffect extends OneShotEffect {
for(int i = 0 ; i < ageCounter; i++){
totalCost.add(cumulativeCost.copy());
}
if (player.chooseUse(Outcome.Benefit, totalCost.getText() + "?", game)) {
if (player.chooseUse(Outcome.Benefit, totalCost.getText() + "?", source, game)) {
totalCost.clearPaid();
int bookmark = game.bookmarkState();
if (totalCost.pay(source, game, source.getSourceId(), source.getControllerId(), false)){

View file

@ -67,7 +67,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
protected List<AlternativeCost2> alternativeSourceCosts = new LinkedList<>();
// needed to check activation status, if card changes zone after casting it
private int zoneChangeCounter = 0;
private int zoneChangeCounter = 0;
public DashAbility(Card card, String manaString) {
super(Zone.ALL, null);
@ -75,7 +75,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
this.addDashCost(manaString);
Ability ability = new EntersBattlefieldAbility(
new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false),
DashedCondition.getInstance(), false, "","");
DashedCondition.getInstance(), false, "", "");
Effect effect = new ReturnToHandTargetEffect();
effect.setText("return the dashed creature from the battlefield to its owner's hand");
effect.setTargetPointer(new FixedTarget(card.getId()));
@ -85,24 +85,24 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
}
public DashAbility(final DashAbility ability) {
super(ability);
this.alternativeSourceCosts.addAll(ability.alternativeSourceCosts);
this.zoneChangeCounter = ability.zoneChangeCounter;
super(ability);
this.alternativeSourceCosts.addAll(ability.alternativeSourceCosts);
this.zoneChangeCounter = ability.zoneChangeCounter;
}
@Override
public DashAbility copy() {
return new DashAbility(this);
return new DashAbility(this);
}
public final AlternativeCost2 addDashCost(String manaString) {
AlternativeCost2 evokeCost = new AlternativeCost2Impl(KEYWORD, REMINDER_TEXT, new ManaCostsImpl(manaString));
alternativeSourceCosts.add(evokeCost);
return evokeCost;
AlternativeCost2 evokeCost = new AlternativeCost2Impl(KEYWORD, REMINDER_TEXT, new ManaCostsImpl(manaString));
alternativeSourceCosts.add(evokeCost);
return evokeCost;
}
public void resetDash() {
for (AlternativeCost2 cost: alternativeSourceCosts) {
for (AlternativeCost2 cost : alternativeSourceCosts) {
cost.reset();
}
zoneChangeCounter = 0;
@ -111,9 +111,9 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
@Override
public boolean isActivated(Ability ability, Game game) {
Card card = game.getCard(sourceId);
if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter +1) {
for (AlternativeCost2 cost: alternativeSourceCosts) {
if(cost.isActivated(game)) {
if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter + 1) {
for (AlternativeCost2 cost : alternativeSourceCosts) {
if (cost.isActivated(game)) {
return true;
}
}
@ -132,9 +132,9 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
Player player = game.getPlayer(controllerId);
if (player != null) {
this.resetDash();
for (AlternativeCost2 dashCost: alternativeSourceCosts) {
if (dashCost.canPay(ability, sourceId, controllerId, game) &&
player.chooseUse(Outcome.Benefit, new StringBuilder(KEYWORD).append(" the creature for ").append(dashCost.getText(true)).append(" ?").toString(), game)) {
for (AlternativeCost2 dashCost : alternativeSourceCosts) {
if (dashCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, new StringBuilder(KEYWORD).append(" the creature for ").append(dashCost.getText(true)).append(" ?").toString(), ability, game)) {
activateDash(dashCost, game);
ability.getManaCostsToPay().clear();
ability.getCosts().clear();
@ -168,23 +168,23 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 dashCost: alternativeSourceCosts) {
if (numberCosts == 0) {
sb.append(dashCost.getText(false));
remarkText = dashCost.getReminderText();
} else {
sb.append(" and/or ").append(dashCost.getText(true));
}
++numberCosts;
}
if (numberCosts == 1) {
StringBuilder sb = new StringBuilder();
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 dashCost : alternativeSourceCosts) {
if (numberCosts == 0) {
sb.append(dashCost.getText(false));
remarkText = dashCost.getReminderText();
} else {
sb.append(" and/or ").append(dashCost.getText(true));
}
++numberCosts;
}
if (numberCosts == 1) {
sb.append(" ").append(remarkText);
}
}
return sb.toString();
return sb.toString();
}
@Override
@ -203,7 +203,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
@Override
public Costs<Cost> getCosts() {
Costs<Cost> alterCosts = new CostsImpl<>();
for (AlternativeCost2 aCost: alternativeSourceCosts) {
for (AlternativeCost2 aCost : alternativeSourceCosts) {
alterCosts.add(aCost.getCost());
}
return alterCosts;

View file

@ -98,7 +98,7 @@ class DredgeEffect extends ReplacementEffectImpl {
Player player = game.getPlayer(source.getControllerId());
if (player != null && player.getLibrary().size() >= amount
&& player.chooseUse(outcome, new StringBuilder("Dredge ").append(sourceCard.getLogName()).
append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), game)) {
append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(player.getLogName()).append(" dreges ").append(sourceCard.getLogName()).toString());
}

View file

@ -145,7 +145,7 @@ class EchoEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && source.getSourceObjectIfItStillExists(game) != null) {
if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) {
return true;

View file

@ -28,8 +28,6 @@
package mage.abilities.keyword;
import java.util.Iterator;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
@ -40,24 +38,24 @@ import mage.abilities.costs.OptionalAdditionalCostImpl;
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
/**
* 702.40. Entwine
*
* 702.40a Entwine is a static ability of modal spells (see rule 700.2) that functions
* while the spell is on the stack. "Entwine [cost]" means "You may choose all modes
* of this spell instead of just one. If you do, you pay an additional [cost]." Using
* the entwine ability follows the rules for choosing modes and paying additional costs
* in rules 601.2b and 601.2e-g.
* 702.40a Entwine is a static ability of modal spells (see rule 700.2) that
* functions while the spell is on the stack. "Entwine [cost]" means "You may
* choose all modes of this spell instead of just one. If you do, you pay an
* additional [cost]." Using the entwine ability follows the rules for choosing
* modes and paying additional costs in rules 601.2b and 601.2e-g.
*
* 702.40b If the entwine cost was paid, follow the text of each of the modes in the order
* written on the card when the spell resolves.
* 702.40b If the entwine cost was paid, follow the text of each of the modes in
* the order written on the card when the spell resolves.
*
* @author LevelX2
*/
public class EntwineAbility extends StaticAbility implements OptionalAdditionalModeSourceCosts {
private static final String keywordText = "Entwine";
@ -65,24 +63,24 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
protected OptionalAdditionalCost additionalCost;
public EntwineAbility(String manaString) {
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
}
public EntwineAbility(Cost cost) {
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, cost);
setRuleAtTheTop(true);
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, cost);
setRuleAtTheTop(true);
}
public EntwineAbility(final EntwineAbility ability) {
super(ability);
additionalCost = ability.additionalCost;
super(ability);
additionalCost = ability.additionalCost;
}
@Override
public EntwineAbility copy() {
return new EntwineAbility(this);
return new EntwineAbility(this);
}
@Override
@ -112,7 +110,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
if (player != null) {
this.resetCosts();
if (additionalCost != null) {
if (player.chooseUse(Outcome.Benefit,new StringBuilder("Pay ").append(additionalCost.getText(false)).append(" ?").toString(), game)) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
additionalCost.activate();
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
@ -130,7 +128,6 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
}
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities.keyword;
import java.util.Iterator;
@ -55,7 +54,6 @@ import mage.players.Player;
*
* @author LevelX2
*/
public class EvokeAbility extends StaticAbility implements AlternativeSourceCosts {
protected static final String EVOKE_KEYWORD = "Evoke";
@ -64,7 +62,7 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
protected List<AlternativeCost2> evokeCosts = new LinkedList<>();
// needed to check activation status, if card changes zone after casting it
private int zoneChangeCounter = 0;
private int zoneChangeCounter = 0;
public EvokeAbility(Card card, String manaString) {
super(Zone.ALL, null);
@ -77,24 +75,24 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
}
public EvokeAbility(final EvokeAbility ability) {
super(ability);
this.evokeCosts.addAll(ability.evokeCosts);
this.zoneChangeCounter = ability.zoneChangeCounter;
super(ability);
this.evokeCosts.addAll(ability.evokeCosts);
this.zoneChangeCounter = ability.zoneChangeCounter;
}
@Override
public EvokeAbility copy() {
return new EvokeAbility(this);
return new EvokeAbility(this);
}
public final AlternativeCost2 addEvokeCost(String manaString) {
AlternativeCost2 evokeCost = new AlternativeCost2Impl(EVOKE_KEYWORD, REMINDER_TEXT, new ManaCostsImpl(manaString));
evokeCosts.add(evokeCost);
return evokeCost;
AlternativeCost2 evokeCost = new AlternativeCost2Impl(EVOKE_KEYWORD, REMINDER_TEXT, new ManaCostsImpl(manaString));
evokeCosts.add(evokeCost);
return evokeCost;
}
public void resetEvoke() {
for (AlternativeCost2 cost: evokeCosts) {
for (AlternativeCost2 cost : evokeCosts) {
cost.reset();
}
zoneChangeCounter = 0;
@ -103,30 +101,30 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
@Override
public boolean isActivated(Ability ability, Game game) {
Card card = game.getCard(sourceId);
if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter +1) {
for (AlternativeCost2 cost: evokeCosts) {
if(cost.isActivated(game)) {
if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter + 1) {
for (AlternativeCost2 cost : evokeCosts) {
if (cost.isActivated(game)) {
return true;
}
}
}
return false;
}
@Override
public boolean isAvailable(Ability source, Game game) {
return true;
}
@Override
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
Player player = game.getPlayer(controllerId);
if (player != null) {
this.resetEvoke();
for (AlternativeCost2 evokeCost: evokeCosts) {
if (evokeCost.canPay(ability, sourceId, controllerId, game) &&
player.chooseUse(Outcome.Benefit, new StringBuilder(EVOKE_KEYWORD).append(" the creature for ").append(evokeCost.getText(true)).append(" ?").toString(), game)) {
for (AlternativeCost2 evokeCost : evokeCosts) {
if (evokeCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, new StringBuilder(EVOKE_KEYWORD).append(" the creature for ").append(evokeCost.getText(true)).append(" ?").toString(), ability, game)) {
activateEvoke(evokeCost, game);
ability.getManaCostsToPay().clear();
ability.getCosts().clear();
@ -160,23 +158,23 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 evokeCost: evokeCosts) {
if (numberCosts == 0) {
sb.append(evokeCost.getText(false));
remarkText = evokeCost.getReminderText();
} else {
sb.append(" and/or ").append(evokeCost.getText(true));
}
++numberCosts;
}
if (numberCosts == 1) {
StringBuilder sb = new StringBuilder();
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 evokeCost : evokeCosts) {
if (numberCosts == 0) {
sb.append(evokeCost.getText(false));
remarkText = evokeCost.getReminderText();
} else {
sb.append(" and/or ").append(evokeCost.getText(true));
}
++numberCosts;
}
if (numberCosts == 1) {
sb.append(" ").append(remarkText);
}
}
return sb.toString();
return sb.toString();
}
@Override
@ -191,13 +189,13 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
}
return sb.toString();
}
@Override
public Costs<Cost> getCosts() {
Costs<Cost> alterCosts = new CostsImpl<>();
for (AlternativeCost2 aCost: evokeCosts) {
for (AlternativeCost2 aCost : evokeCosts) {
alterCosts.add(aCost.getCost());
}
return alterCosts;
}
}
}

View file

@ -101,7 +101,7 @@ class ExtortEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && permanent != null) {
if (player.chooseUse(Outcome.Damage, new StringBuilder("Extort opponents? (").append(permanent.getName()).append(")").toString(), game)) {
if (player.chooseUse(Outcome.Damage, new StringBuilder("Extort opponents? (").append(permanent.getName()).append(")").toString(), source, game)) {
Cost cost = new ManaCostsImpl("{W/B}");
if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) {
int loseLife = 0;

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities.keyword;
import java.util.HashMap;
@ -52,33 +51,31 @@ import mage.players.Player;
/**
*
* 20121001 702.31. Kicker
* 702.31a Kicker is a static ability that functions while the spell with kicker
* is on the stack. "Kicker [cost]" means "You may pay an additional [cost]
* as you cast this spell." Paying a spell's kicker cost(s) follows the
* rules for paying additional costs in rules 601.2b and 601.2e-g.
* 702.31b The phrase "Kicker [cost 1] and/or [cost 2]" means the same thing as
* "Kicker [cost 1], kicker [cost 2]."
* 702.31c Multikicker is a variant of the kicker ability. "Multikicker [cost]"
* means "You may pay an additional [cost] any number of times as you cast
* this spell." A multikicker cost is a kicker cost.
* 702.31d If a spell's controller declares the intention to pay any of that spell's
* kicker costs, that spell has been "kicked." If a spell has two kicker
* costs or has multikicker, it may be kicked multiple times. See rule 601.2b.
* 702.31e Objects with kicker or multikicker have additional abilities that specify
* what happens if they are kicked. These abilities are linked to the kicker
* or multikicker abilities printed on that object: they can refer only to
* those specific kicker or multikicker abilities. See rule 607,
* "Linked Abilities."
* 702.31f Objects with more than one kicker cost have abilities that each correspond
* to a specific kicker cost. They contain the phrases "if it was kicked with
* its [A] kicker" and "if it was kicked with its [B] kicker," where A and B
* are the first and second kicker costs listed on the card, respectively. Each
* of those abilities is linked to the appropriate kicker ability.
* 702.31g If part of a spell's ability has its effect only if that spell was kicked,
* and that part of the ability includes any targets, the spell's controller
* chooses those targets only if that spell was kicked. Otherwise, the spell is
* cast as if it did not have those targets. See rule 601.2c.
* 20121001 702.31. Kicker 702.31a Kicker is a static ability that functions
* while the spell with kicker is on the stack. "Kicker [cost]" means "You may
* pay an additional [cost] as you cast this spell." Paying a spell's kicker
* cost(s) follows the rules for paying additional costs in rules 601.2b and
* 601.2e-g. 702.31b The phrase "Kicker [cost 1] and/or [cost 2]" means the same
* thing as "Kicker [cost 1], kicker [cost 2]." 702.31c Multikicker is a variant
* of the kicker ability. "Multikicker [cost]" means "You may pay an additional
* [cost] any number of times as you cast this spell." A multikicker cost is a
* kicker cost. 702.31d If a spell's controller declares the intention to pay
* any of that spell's kicker costs, that spell has been "kicked." If a spell
* has two kicker costs or has multikicker, it may be kicked multiple times. See
* rule 601.2b. 702.31e Objects with kicker or multikicker have additional
* abilities that specify what happens if they are kicked. These abilities are
* linked to the kicker or multikicker abilities printed on that object: they
* can refer only to those specific kicker or multikicker abilities. See rule
* 607, "Linked Abilities." 702.31f Objects with more than one kicker cost have
* abilities that each correspond to a specific kicker cost. They contain the
* phrases "if it was kicked with its [A] kicker" and "if it was kicked with its
* [B] kicker," where A and B are the first and second kicker costs listed on
* the card, respectively. Each of those abilities is linked to the appropriate
* kicker ability. 702.31g If part of a spell's ability has its effect only if
* that spell was kicked, and that part of the ability includes any targets, the
* spell's controller chooses those targets only if that spell was kicked.
* Otherwise, the spell is cast as if it did not have those targets. See rule
* 601.2c.
*
* @author LevelX2
*
@ -90,61 +87,61 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
protected static final String KICKER_REMINDER_COST = "(You may {cost} in addition to any other costs as you cast this spell.)";
protected Map<String, Integer> activations = new HashMap<>(); // zoneChangeCounter, activations
protected String keywordText;
protected String reminderText;
protected List<OptionalAdditionalCost> kickerCosts = new LinkedList<>();
private int xManaValue = 0;
private int xManaValue = 0;
public KickerAbility(String manaString) {
this(KICKER_KEYWORD, KICKER_REMINDER_MANA);
this.addKickerCost(manaString);
this(KICKER_KEYWORD, KICKER_REMINDER_MANA);
this.addKickerCost(manaString);
}
public KickerAbility(Cost cost) {
this(KICKER_KEYWORD, KICKER_REMINDER_COST);
this.addKickerCost(cost);
this(KICKER_KEYWORD, KICKER_REMINDER_COST);
this.addKickerCost(cost);
}
public KickerAbility(String keywordText, String reminderText) {
super(Zone.STACK, null);
name = keywordText;
this.keywordText = keywordText;
this.reminderText = reminderText;
setRuleAtTheTop(true);
super(Zone.STACK, null);
name = keywordText;
this.keywordText = keywordText;
this.reminderText = reminderText;
setRuleAtTheTop(true);
}
public KickerAbility(final KickerAbility ability) {
super(ability);
this.kickerCosts.addAll(ability.kickerCosts);
this.keywordText = ability.keywordText;
this.reminderText = ability.reminderText;
this.xManaValue = ability.xManaValue;
this.activations.putAll(ability.activations);
super(ability);
this.kickerCosts.addAll(ability.kickerCosts);
this.keywordText = ability.keywordText;
this.reminderText = ability.reminderText;
this.xManaValue = ability.xManaValue;
this.activations.putAll(ability.activations);
}
@Override
public KickerAbility copy() {
return new KickerAbility(this);
return new KickerAbility(this);
}
public final OptionalAdditionalCost addKickerCost(String manaString) {
OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
kickerCosts.add(kickerCost);
return kickerCost;
OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
kickerCosts.add(kickerCost);
return kickerCost;
}
public final OptionalAdditionalCost addKickerCost(Cost cost) {
OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, cost);
kickerCosts.add(kickerCost);
return kickerCost;
OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, cost);
kickerCosts.add(kickerCost);
return kickerCost;
}
public void resetKicker(Game game, Ability source) {
String key = getActivationKey(source, "", game);
activations.remove(key);
for (OptionalAdditionalCost cost: kickerCosts) {
activations.remove(key);
for (OptionalAdditionalCost cost : kickerCosts) {
cost.reset();
}
}
@ -152,7 +149,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
public int getXManaValue() {
return xManaValue;
}
public int getKickedCounter(Game game, Ability source) {
String key = getActivationKey(source, "", game);
if (activations.containsKey(key)) {
@ -169,7 +166,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
return false;
}
public List<OptionalAdditionalCost> getKickerCosts () {
public List<OptionalAdditionalCost> getKickerCosts() {
return kickerCosts;
}
@ -186,50 +183,50 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
int zcc = source.getSourceObjectZoneChangeCounter();
if (source.getSourceObjectZoneChangeCounter() == 0) {
zcc = game.getState().getZoneChangeCounter(source.getSourceId());
}
}
if (zcc > 0 && (source.getAbilityType().equals(AbilityType.TRIGGERED) || source.getAbilityType().equals(AbilityType.STATIC))) {
--zcc;
}
return String.valueOf(zcc) + ((kickerCosts.size() > 1) ? costText :"");
return String.valueOf(zcc) + ((kickerCosts.size() > 1) ? costText : "");
}
@Override
public void addOptionalAdditionalCosts(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
Player player = game.getPlayer(controllerId);
if (player != null) {
this.resetKicker(game, ability);
for (OptionalAdditionalCost kickerCost: kickerCosts) {
for (OptionalAdditionalCost kickerCost : kickerCosts) {
boolean again = true;
while (player.isInGame() && again) {
String times = "";
if (kickerCost.isRepeatable()) {
int activatedCount = getKickedCounter(game, ability);
times = Integer.toString(activatedCount + 1) + (activatedCount == 0 ? " time ":" times ");
times = Integer.toString(activatedCount + 1) + (activatedCount == 0 ? " time " : " times ");
}
if (kickerCost.canPay(ability, sourceId, controllerId, game) &&
player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", game)) {
if (kickerCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) {
this.activateKicker(kickerCost, ability, game);
for (Iterator it = ((Costs) kickerCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
if (cost instanceof ManaCostsImpl) {
List<VariableManaCost> varCosts = ((ManaCostsImpl)cost).getVariableCosts();
List<VariableManaCost> varCosts = ((ManaCostsImpl) cost).getVariableCosts();
if (!varCosts.isEmpty()) {
// use only first variable cost
xManaValue = game.getPlayer(this.controllerId).announceXMana(varCosts.get(0).getMinX(), Integer.MAX_VALUE, "Announce kicker value for " + varCosts.get(0).getText(), game, this);
// kicker variable X costs handled internally as multikicker with {1} cost (no multikicker on card)
if (!game.isSimulation()) {
game.informPlayers(game.getPlayer(this.controllerId).getLogName() + " announced a value of " + xManaValue +" for " + " kicker X ");
game.informPlayers(game.getPlayer(this.controllerId).getLogName() + " announced a value of " + xManaValue + " for " + " kicker X ");
}
ability.getManaCostsToPay().add(new GenericManaCost(xManaValue));
} else {
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
}
} else {
ability.getCosts().add(cost.copy());
}
}
again = kickerCost.isRepeatable();
} else {
again = false;
@ -240,26 +237,25 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
}
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
int numberKicker = 0;
String remarkText = "";
for (OptionalAdditionalCost kickerCost: kickerCosts) {
if (numberKicker == 0) {
sb.append(kickerCost.getText(false));
remarkText = kickerCost.getReminderText();
} else {
sb.append(" and/or ").append(kickerCost.getText(true));
}
++numberKicker;
}
if (numberKicker == 1) {
StringBuilder sb = new StringBuilder();
int numberKicker = 0;
String remarkText = "";
for (OptionalAdditionalCost kickerCost : kickerCosts) {
if (numberKicker == 0) {
sb.append(kickerCost.getText(false));
remarkText = kickerCost.getReminderText();
} else {
sb.append(" and/or ").append(kickerCost.getText(true));
}
++numberKicker;
}
if (numberKicker == 1) {
sb.append(" ").append(remarkText);
}
}
return sb.toString();
return sb.toString();
}
@Override

View file

@ -88,7 +88,7 @@ class MadnessReplacementEffect extends ReplacementEffectImpl {
if (controller != null) {
Card card = game.getCard(event.getTargetId());
if (card != null) {
if (controller.chooseUse(outcome, "Move " + card.getLogName() + " to exile to cast it by Madness?", game)) {
if (controller.chooseUse(outcome, "Move " + card.getLogName() + " to exile to cast it by Madness?", source, game)) {
controller.moveCardToExileWithInfo(card, source.getSourceId(), "Madness", source.getSourceId(), game, ((ZoneChangeEvent) event).getFromZone(), true);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.MADNESS_CARD_EXILED, card.getId(), card.getId(),controller.getId()));
return true;

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities.keyword;
import java.util.Iterator;
@ -58,42 +57,44 @@ import mage.players.Player;
/**
* 702.36. Morph
*
* 702.36a Morph is a static ability that functions in any zone from which you could play
* the card it’s on, and the morph effect works any time the card is face down.
* "Morph [cost]" means "You may cast this card as a 2/2 face-down creature, with no text,
* no name, no subtypes, and no mana cost by paying {3} rather than paying its mana cost."
* (See rule 707, "Face-Down Spells and Permanents.")
* 702.36a Morph is a static ability that functions in any zone from which you
* could play the card it’s on, and the morph effect works any time the card is
* face down. "Morph [cost]" means "You may cast this card as a 2/2 face-down
* creature, with no text, no name, no subtypes, and no mana cost by paying {3}
* rather than paying its mana cost." (See rule 707, "Face-Down Spells and
* Permanents.")
*
* 702.36b To cast a card using its morph ability, turn it face down. It becomes a 2/2
* face-down creature card, with no text, no name, no subtypes, and no mana cost. Any
* effects or prohibitions that would apply to casting a card with these characteristics
* (and not the face-up card’s characteristics) are applied to casting this card. These
* values are the copiable values of that object’s characteristics. (See rule 613,
* "Interaction of Continuous Effects," and rule 706, "Copying Objects.") Put it onto the
* stack (as a face-down spell with the same characteristics), and pay {3} rather than pay
* its mana cost. This follows the rules for paying alternative costs. You can use morph
* to cast a card from any zone from which you could normally play it. When the spell
* resolves, it enters the battlefield with the same characteristics the spell had. The
* morph effect applies to the face-down object wherever it is, and it ends when the
* permanent is turned face up. #
* 702.36b To cast a card using its morph ability, turn it face down. It becomes
* a 2/2 face-down creature card, with no text, no name, no subtypes, and no
* mana cost. Any effects or prohibitions that would apply to casting a card
* with these characteristics (and not the face-up card’s characteristics) are
* applied to casting this card. These values are the copiable values of that
* object’s characteristics. (See rule 613, "Interaction of Continuous Effects,"
* and rule 706, "Copying Objects.") Put it onto the stack (as a face-down spell
* with the same characteristics), and pay {3} rather than pay its mana cost.
* This follows the rules for paying alternative costs. You can use morph to
* cast a card from any zone from which you could normally play it. When the
* spell resolves, it enters the battlefield with the same characteristics the
* spell had. The morph effect applies to the face-down object wherever it is,
* and it ends when the permanent is turned face up. #
*
* 702.36c You can’t cast a card face down if it doesn’t have morph.
* 702.36c You can’t cast a card face down if it doesn’t have morph.
*
* 702.36d If you have priority, you may turn a face-down permanent you control face up.
* This is a special action; it doesn’t use the stack (see rule 115). To do this, show
* all players what the permanent’s morph cost would be if it were face up, pay that cost,
* then turn the permanent face up. (If the permanent wouldn’t have a morph cost if it
* were face up, it can’t be turned face up this way.) The morph effect on it ends, and
* it regains its normal characteristics. Any abilities relating to the permanent entering
* the battlefield don’t trigger when it’s turned face up and don’t have any effect, because
* the permanent has already entered the battlefield.
* 702.36d If you have priority, you may turn a face-down permanent you control
* face up. This is a special action; it doesn’t use the stack (see rule 115).
* To do this, show all players what the permanent’s morph cost would be if it
* were face up, pay that cost, then turn the permanent face up. (If the
* permanent wouldn’t have a morph cost if it were face up, it can’t be turned
* face up this way.) The morph effect on it ends, and it regains its normal
* characteristics. Any abilities relating to the permanent entering the
* battlefield don’t trigger when it’s turned face up and don’t have any effect,
* because the permanent has already entered the battlefield.
*
* 702.36e See rule 707, "Face-Down Spells and Permanents," for more information on how to
* cast cards with morph.
* 702.36e See rule 707, "Face-Down Spells and Permanents," for more information
* on how to cast cards with morph.
*
* @author LevelX2
*/
public class MorphAbility extends StaticAbility implements AlternativeSourceCosts {
protected static final String ABILITY_KEYWORD = "Morph";
@ -104,9 +105,9 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
protected AlternativeCost2Impl alternateCosts = new AlternativeCost2Impl(ABILITY_KEYWORD, REMINDER_TEXT, new GenericManaCost(3));
protected Costs<Cost> morphCosts;
// needed to check activation status, if card changes zone after casting it
private int zoneChangeCounter = 0;
private boolean megamorph;
private int zoneChangeCounter = 0;
private boolean megamorph;
public MorphAbility(Card card, Cost morphCost) {
this(card, createCosts(morphCost));
}
@ -118,7 +119,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
public MorphAbility(Card card, Costs<Cost> morphCosts) {
this(card, morphCosts, false);
}
public MorphAbility(Card card, Costs<Cost> morphCosts, boolean megamorph) {
super(Zone.HAND, null);
this.morphCosts = morphCosts;
@ -129,9 +130,9 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
sb.append(ABILITY_KEYWORD_MEGA).append(" ");
} else {
sb.append(ABILITY_KEYWORD).append(" ");
}
name = ABILITY_KEYWORD;
for (Cost cost :morphCosts) {
}
name = ABILITY_KEYWORD;
for (Cost cost : morphCosts) {
if (!(cost instanceof ManaCosts)) {
sb.append("- ");
break;
@ -143,10 +144,10 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
} else {
sb.append(REMINDER_TEXT);
}
ruleText = sb.toString();
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(morphCosts, (megamorph ? FaceDownType.MEGAMORPHED :FaceDownType.MORPHED)));
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(morphCosts, (megamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED)));
ability.setWorksFaceDown(true);
ability.setRuleVisible(false);
addSubAbility(ability);
@ -154,12 +155,12 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
}
public MorphAbility(final MorphAbility ability) {
super(ability);
this.zoneChangeCounter = ability.zoneChangeCounter;
this.ruleText = ability.ruleText;
this.alternateCosts = ability.alternateCosts.copy();
this.morphCosts = ability.morphCosts; // can't be changed
this.megamorph = ability.megamorph;
super(ability);
this.zoneChangeCounter = ability.zoneChangeCounter;
this.ruleText = ability.ruleText;
this.alternateCosts = ability.alternateCosts.copy();
this.morphCosts = ability.morphCosts; // can't be changed
this.megamorph = ability.megamorph;
}
private static Costs<Cost> createCosts(Cost cost) {
@ -170,11 +171,11 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
@Override
public MorphAbility copy() {
return new MorphAbility(this);
return new MorphAbility(this);
}
public void resetMorph() {
alternateCosts.reset();
alternateCosts.reset();
zoneChangeCounter = 0;
}
@ -185,7 +186,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
@Override
public boolean isActivated(Ability ability, Game game) {
Card card = game.getCard(sourceId);
if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter +1) {
if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter + 1) {
return alternateCosts.isActivated(game);
}
return false;
@ -205,7 +206,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
this.resetMorph();
spell.setFaceDown(true, game); // so only the back is visible
if (alternateCosts.canPay(ability, sourceId, controllerId, game)) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Cast this card as a 2/2 face-down creature for ").append(getCosts().getText()).append(" ?").toString(), game)) {
if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 face-down creature for " + getCosts().getText() + " ?", ability, game)) {
activateMorph(game);
// change mana costs
ability.getManaCostsToPay().clear();
@ -213,7 +214,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
for (Iterator it = this.alternateCosts.iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
if (cost instanceof ManaCost) {
ability.getManaCostsToPay().add((ManaCost)cost.copy());
ability.getManaCostsToPay().add((ManaCost) cost.copy());
} else {
ability.getCosts().add(cost.copy());
}
@ -231,12 +232,12 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
}
}
}
if (ability.getAbilityType().equals(AbilityType.PLAY_LAND)) {
if (ability.getAbilityType().equals(AbilityType.PLAY_LAND)) {
Player player = game.getPlayer(controllerId);
if (player != null) {
this.resetMorph();
if (alternateCosts.canPay(ability, sourceId, controllerId, game)) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Cast this card as a 2/2 face-down creature for ").append(getCosts().getText()).append(" ?").toString(), game)) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Cast this card as a 2/2 face-down creature for ").append(getCosts().getText()).append(" ?").toString(), ability, game)) {
activateMorph(game);
// change mana costs
ability.getManaCostsToPay().clear();
@ -244,7 +245,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
for (Iterator it = this.alternateCosts.iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
if (cost instanceof ManaCost) {
ability.getManaCostsToPay().add((ManaCost)cost.copy());
ability.getManaCostsToPay().add((ManaCost) cost.copy());
} else {
ability.getCosts().add(cost.copy());
}
@ -292,7 +293,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
public Costs<Cost> getCosts() {
return alternateCosts;
}
public static void setPermanentToFaceDownCreature(MageObject mageObject) {
mageObject.getPower().initValue(2);
mageObject.getToughness().initValue(2);
@ -305,13 +306,9 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
mageObject.getSupertype().clear();
mageObject.getManaCost().clear();
if (mageObject instanceof Permanent) {
((Permanent)mageObject).setExpansionSetCode("");
((Permanent)mageObject).setRarity(Rarity.NA);
((Permanent) mageObject).setExpansionSetCode("");
((Permanent) mageObject).setRarity(Rarity.NA);
}
}
}

View file

@ -159,7 +159,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
Card spellToCast = game.getCard(source.getSourceId());
Player player = game.getPlayer(source.getControllerId());
if (player != null && !CardUtil.isCheckPlayableMode(affectedAbility) &&
player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + "?", game)) {
player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + "?", source, game)) {
Target target = new TargetControlledCreaturePermanent(1,1,filter,true);
player.chooseTarget(Outcome.Sacrifice, target, source, game);
if (!target.isChosen()) {

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities.keyword;
import java.util.Iterator;
@ -50,16 +49,16 @@ import mage.watchers.common.ProwlWatcher;
/**
* 702.74. Prowl #
*
* 702.74a Prowl is a static ability that functions on the stack. "Prowl [cost]" means
* "You may pay [cost] rather than pay this spell's mana cost if a player was dealt combat
* damage this turn by a source that, at the time it dealt that damage, was under your
* control and had any of this spell's creature types." Paying a spell's prowl cost follows
* the rules for paying alternative costs in rules 601.2b and 601.2e-g
*
* 702.74a Prowl is a static ability that functions on the stack. "Prowl [cost]"
* means "You may pay [cost] rather than pay this spell's mana cost if a player
* was dealt combat damage this turn by a source that, at the time it dealt that
* damage, was under your control and had any of this spell's creature types."
* Paying a spell's prowl cost follows the rules for paying alternative costs in
* rules 601.2b and 601.2e-g
*
* @author LevelX2
*/
public class ProwlAbility extends StaticAbility implements AlternativeSourceCosts {
private static final String PROWL_KEYWORD = "Prowl";
@ -77,43 +76,43 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
}
public ProwlAbility(final ProwlAbility ability) {
super(ability);
this.prowlCosts.addAll(ability.prowlCosts);
this.reminderText = ability.reminderText;
super(ability);
this.prowlCosts.addAll(ability.prowlCosts);
this.reminderText = ability.reminderText;
}
@Override
public ProwlAbility copy() {
return new ProwlAbility(this);
return new ProwlAbility(this);
}
public final AlternativeCost2 addProwlCost(String manaString) {
AlternativeCost2 prowlCost = new AlternativeCost2Impl(PROWL_KEYWORD, reminderText, new ManaCostsImpl(manaString));
prowlCosts.add(prowlCost);
return prowlCost;
AlternativeCost2 prowlCost = new AlternativeCost2Impl(PROWL_KEYWORD, reminderText, new ManaCostsImpl(manaString));
prowlCosts.add(prowlCost);
return prowlCost;
}
public void resetProwl() {
for (AlternativeCost2 cost: prowlCosts) {
for (AlternativeCost2 cost : prowlCosts) {
cost.reset();
}
}
@Override
public boolean isActivated(Ability ability, Game game) {
for (AlternativeCost2 cost: prowlCosts) {
if(cost.isActivated(game)) {
for (AlternativeCost2 cost : prowlCosts) {
if (cost.isActivated(game)) {
return true;
}
}
return false;
}
@Override
public boolean isAvailable(Ability source, Game game) {
return true;
}
@Override
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
@ -124,7 +123,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
throw new IllegalArgumentException("Params can't be null");
}
boolean canProwl = false;
for (String subtype: card.getSubtype()) {
for (String subtype : card.getSubtype()) {
if (prowlWatcher.hasSubtypeMadeCombatDamage(ability.getControllerId(), subtype)) {
canProwl = true;
break;
@ -132,9 +131,9 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
}
if (canProwl) {
this.resetProwl();
for (AlternativeCost2 prowlCost: prowlCosts) {
if (prowlCost.canPay(ability, sourceId, controllerId, game) &&
player.chooseUse(Outcome.Benefit, new StringBuilder("Cast for ").append(PROWL_KEYWORD).append(" cost ").append(prowlCost.getText(true)).append(" ?").toString(), game)) {
for (AlternativeCost2 prowlCost : prowlCosts) {
if (prowlCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, new StringBuilder("Cast for ").append(PROWL_KEYWORD).append(" cost ").append(prowlCost.getText(true)).append(" ?").toString(), ability, game)) {
prowlCost.activate();
ability.getManaCostsToPay().clear();
ability.getCosts().clear();
@ -155,23 +154,23 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 prowlCost: prowlCosts) {
if (numberCosts == 0) {
sb.append(prowlCost.getText(false));
remarkText = prowlCost.getReminderText();
} else {
sb.append(" and/or ").append(prowlCost.getText(true));
}
++numberCosts;
}
if (numberCosts == 1) {
StringBuilder sb = new StringBuilder();
int numberCosts = 0;
String remarkText = "";
for (AlternativeCost2 prowlCost : prowlCosts) {
if (numberCosts == 0) {
sb.append(prowlCost.getText(false));
remarkText = prowlCost.getReminderText();
} else {
sb.append(" and/or ").append(prowlCost.getText(true));
}
++numberCosts;
}
if (numberCosts == 1) {
sb.append(" ").append(remarkText);
}
}
return sb.toString();
return sb.toString();
}
@Override
@ -190,12 +189,12 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
private void setReminderText(Card card) {
StringBuilder sb = new StringBuilder("(You may cast this for its prowl cost if you dealt combat damage to a player this turn with a ");
int i = 0;
for (String subtype: card.getSubtype()) {
for (String subtype : card.getSubtype()) {
i++;
sb.append(subtype);
if (card.getSubtype().size() > 1 && i < card.getSubtype().size()) {
sb.append(" or ");
}
}
}
sb.append(".)");
@ -205,7 +204,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
@Override
public Costs<Cost> getCosts() {
Costs<Cost> alterCosts = new CostsImpl<>();
for (AlternativeCost2 aCost: prowlCosts) {
for (AlternativeCost2 aCost : prowlCosts) {
alterCosts.add(aCost.getCost());
}
return alterCosts;

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.abilities.keyword;
import java.util.Iterator;
@ -54,7 +53,6 @@ import mage.players.Player;
*
* @author LevelX2
*/
public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
private static final String keywordText = "Replicate";
@ -62,21 +60,21 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
protected OptionalAdditionalCost additionalCost;
public ReplicateAbility(Card card, String manaString) {
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString));
this.additionalCost.setRepeatable(true);
setRuleAtTheTop(true);
addSubAbility(new ReplicateTriggeredAbility());
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString));
this.additionalCost.setRepeatable(true);
setRuleAtTheTop(true);
addSubAbility(new ReplicateTriggeredAbility());
}
public ReplicateAbility(final ReplicateAbility ability) {
super(ability);
additionalCost = ability.additionalCost;
super(ability);
additionalCost = ability.additionalCost;
}
@Override
public ReplicateAbility copy() {
return new ReplicateAbility(this);
return new ReplicateAbility(this);
}
@Override
@ -119,10 +117,10 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
String times = "";
if (additionalCost.isRepeatable()) {
int numActivations = additionalCost.getActivateCount();
times = Integer.toString(numActivations + 1) + (numActivations == 0 ? " time ":" times ");
times = Integer.toString(numActivations + 1) + (numActivations == 0 ? " time " : " times ");
}
if (additionalCost.canPay(ability, sourceId, controllerId, game) &&
player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), game)) {
if (additionalCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
additionalCost.activate();
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();
@ -140,7 +138,6 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
}
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
@ -179,6 +176,7 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl {
private ReplicateTriggeredAbility(final ReplicateTriggeredAbility ability) {
super(ability);
}
@Override
public ReplicateTriggeredAbility copy() {
return new ReplicateTriggeredAbility(this);
@ -196,7 +194,7 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl {
if (spell instanceof Spell) {
Card card = game.getCard(spell.getSourceId());
if (card != null) {
for (Ability ability: card.getAbilities()) {
for (Ability ability : card.getAbilities()) {
if (ability instanceof ReplicateAbility) {
if (((ReplicateAbility) ability).isActivated()) {
for (Effect effect : this.getEffects()) {
@ -215,7 +213,7 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Replicate <i>(When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)</i>" ;
return "Replicate <i>(When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)</i>";
}
}
@ -239,7 +237,7 @@ class ReplicateCopyEffect extends OneShotEffect {
// reset replicate now so the copies don't report x times Replicate
Card card = game.getCard(spell.getSourceId());
if (card != null) {
for (Ability ability: card.getAbilities()) {
for (Ability ability : card.getAbilities()) {
if (ability instanceof ReplicateAbility) {
if (((ReplicateAbility) ability).isActivated()) {
((ReplicateAbility) ability).resetReplicate();
@ -254,8 +252,9 @@ class ReplicateCopyEffect extends OneShotEffect {
copy.setCopiedSpell(true);
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString());
}
}
return true;
}

View file

@ -118,7 +118,7 @@ class TributeEffect extends OneShotEffect {
sb.append(sourcePermanent.getName());
sb.append(" (add ").append(CardUtil.numberToText(tributeValue)).append(" +1/+1 counter");
sb.append(tributeValue > 1 ? "s":"").append(" to it)?");
if (opponent.chooseUse(outcome, sb.toString(), game)) {
if (opponent.chooseUse(outcome, sb.toString(), source, game)) {
if (!game.isSimulation())
game.informPlayers(opponent.getLogName() + " pays tribute to " + sourcePermanent.getLogName());
game.getState().setValue("tributeValue" + source.getSourceId(), "yes");

View file

@ -111,7 +111,7 @@ class UnleashReplacementEffect extends ReplacementEffectImpl {
Permanent creature = game.getPermanent(event.getTargetId());
Player controller = game.getPlayer(source.getControllerId());
if (creature != null && controller != null) {
if (controller.chooseUse(outcome, "Unleash "+ creature.getName() +"?", game)) {
if (controller.chooseUse(outcome, "Unleash "+ creature.getName() +"?", source, game)) {
if (!game.isSimulation())
game.informPlayers(controller.getLogName() + " unleashes " + creature.getName());
creature.addCounters(CounterType.P1P1.createInstance(), game);

View file

@ -753,7 +753,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
if (name.isEmpty()) {
return GameLog.getNeutralColoredText("face down card");
} else {
return GameLog.getColoredObjectName(this);
return GameLog.getColoredObjectIdName(this);
}
}

View file

@ -1,33 +1,40 @@
/*
* 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.
*/
* 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.game;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.MageItem;
import mage.MageObject;
import mage.abilities.Ability;
@ -44,6 +51,7 @@ import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.constants.Duration;
import mage.constants.MultiplayerAttackOption;
import mage.constants.PlayerAction;
import mage.constants.RangeOfInfluence;
import mage.constants.Zone;
import mage.game.combat.Combat;
@ -66,176 +74,281 @@ import mage.players.PlayerList;
import mage.players.Players;
import mage.util.functions.ApplyToPermanent;
import java.io.Serializable;
import java.util.*;
import mage.constants.PlayerAction;
public interface Game extends MageItem, Serializable {
MatchType getGameType();
int getNumPlayers();
int getLife();
RangeOfInfluence getRangeOfInfluence();
MultiplayerAttackOption getAttackOption();
//game data methods
void loadCards(Set<Card> cards, UUID ownerId);
Collection<Card> getCards();
Object getCustomData();
void setCustomData(Object data);
GameOptions getOptions();
MageObject getObject(UUID objectId);
MageObject getBaseObject(UUID objectId);
MageObject getEmblem(UUID objectId);
UUID getControllerId(UUID objectId);
Permanent getPermanent(UUID permanentId);
Permanent getPermanentOrLKIBattlefield(UUID permanentId);
Map<Zone,HashMap<UUID, MageObject>> getLKI();
Map<Zone, HashMap<UUID, MageObject>> getLKI();
Card getCard(UUID cardId);
Ability getAbility(UUID abilityId, UUID sourceId);
void setZone(UUID objectId, Zone zone);
void addPlayer(Player player, Deck deck) throws GameException;
Player getPlayer(UUID playerId);
Players getPlayers();
PlayerList getPlayerList();
/**
* Returns a Set of opponents in range for the given playerId
*
*
* @param playerId
* @return
* @return
*/
Set<UUID> getOpponents(UUID playerId);
/**
* Checks if the given playerToCheckId is an opponent of player
*
* @param player
*
* @param player
* @param playerToCheckId
* @return
* @return
*/
boolean isOpponent(Player player, UUID playerToCheckId);
Turn getTurn();
Phase getPhase();
Step getStep();
int getTurnNum();
boolean isMainPhase();
boolean canPlaySorcery(UUID playerId);
UUID getActivePlayerId();
UUID getPriorityPlayerId();
boolean gameOver(UUID playerId);
boolean hasEnded();
Battlefield getBattlefield();
SpellStack getStack();
Exile getExile();
Combat getCombat();
GameState getState();
String getWinner();
void setDraw(UUID playerId);
boolean isADraw();
ContinuousEffects getContinuousEffects();
GameStates getGameStates();
void loadGameStates(GameStates states);
Game copy();
boolean isSimulation();
void setSimulation(boolean simulation);
MageObject getLastKnownInformation(UUID objectId, Zone zone);
MageObject getLastKnownInformation(UUID objectId, Zone zone, int zoneChangeCounter);
boolean getShortLivingLKI(UUID objectId, Zone zone);
void rememberLKI(UUID objectId, Zone zone, MageObject object);
void resetLKI();
void resetShortLivingLKI();
void setLosingPlayer(Player player);
Player getLosingPlayer();
void setStateCheckRequired();
boolean getStateCheckRequired();
//client event methods
void addTableEventListener(Listener<TableEvent> listener);
void addPlayerQueryEventListener(Listener<PlayerQueryEvent> listener);
void fireAskPlayerEvent(UUID playerId, String message);
void fireChooseChoiceEvent(UUID playerId, Choice choice);
void fireSelectTargetEvent(UUID playerId, String message, Set<UUID> targets, boolean required, Map<String, Serializable> options);
void fireSelectTargetEvent(UUID playerId, String message, Cards cards, boolean required, Map<String, Serializable> options);
void fireSelectTargetEvent(UUID playerId, String message, List<TriggeredAbility> abilities);
void fireSelectTargetEvent(UUID playerId, String message, List<Permanent> perms, boolean required);
void fireSelectEvent(UUID playerId, String message);
void fireSelectEvent(UUID playerId, String message, Map<String, Serializable> options);
void firePriorityEvent(UUID playerId);
void firePlayManaEvent(UUID playerId, String message);
void firePlayXManaEvent(UUID playerId, String message);
void fireGetChoiceEvent(UUID playerId, String message, MageObject object, List<? extends ActivatedAbility> choices);
void fireGetModeEvent(UUID playerId, String message, Map<UUID, String> modes);
void fireGetAmountEvent(UUID playerId, String message, int min, int max);
void fireChoosePileEvent(UUID playerId, String message, List<? extends Card> pile1, List<? extends Card> pile2);
void fireInformEvent(String message);
void fireStatusEvent(String message, boolean withTime);
void fireUpdatePlayersEvent();
void informPlayers(String message);
void informPlayer(Player player, String message);
void debugMessage(String message);
void fireErrorEvent(String message, Exception ex);
void fireGameEndInfo();
//game event methods
void fireEvent(GameEvent event);
/**
* The events are stored until the resolution of the current effect ends
* and fired then all together (e.g. X lands enter the battlefield from Scapeshift)
* The events are stored until the resolution of the current effect ends and
* fired then all together (e.g. X lands enter the battlefield from
* Scapeshift)
*
* @param event
*/
void addSimultaneousEvent(GameEvent event);
boolean replaceEvent(GameEvent event);
/**
* Creates and fires an damage prevention event
*
* @param damageEvent damage event that will be replaced (instanceof check will be done)
* @param damageEvent damage event that will be replaced (instanceof check
* will be done)
* @param source ability that's the source of the prevention effect
* @param game
* @param amountToPrevent max preventable amount
* @return true prevention was successfull / false prevention was replaced
*/
PreventionEffectData preventDamage(GameEvent damageEvent, Ability source, Game game, int amountToPrevent);
/**
* Creates and fires an damage prevention event
*
* @param event damage event that will be replaced (instanceof check will be done)
* @param event damage event that will be replaced (instanceof check will be
* done)
* @param source ability that's the source of the prevention effect
* @param game
* @param preventAllDamage true if there is no limit to the damage that can be prevented
* @param preventAllDamage true if there is no limit to the damage that can
* be prevented
* @return true prevention was successfull / false prevention was replaced
*/
PreventionEffectData preventDamage(GameEvent event, Ability source, Game game, boolean preventAllDamage);
void start(UUID choosingPlayerId);
void resume();
void pause();
boolean isPaused();
void end();
void cleanUp();
/*
* Gives back the number of cards the player has after the next mulligan
*/
int mulliganDownTo(UUID playerId);
void mulligan(UUID playerId);
void endMulligan(UUID playerId);
// void quit(UUID playerId);
void timerTimeout(UUID playerId);
void idleTimeout(UUID playerId);
void concede(UUID playerId);
void setManaPaymentMode(UUID playerId, boolean autoPayment);
void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted);
void undo(UUID playerId);
void emptyManaPools();
void addEffect(ContinuousEffect continuousEffect, Ability source);
void addEmblem(Emblem emblem, Ability source);
void addEmblem(Emblem emblem, Ability source, UUID toPlayerId);
void addCommander(Commander commander);
void addPermanent(Permanent permanent);
// priority method
void sendPlayerAction(PlayerAction playerAction, UUID playerId);
void sendPlayerAction(PlayerAction playerAction, UUID playerId);
/**
* This version supports copying of copies of any depth.
@ -249,53 +362,74 @@ public interface Game extends MageItem, Serializable {
Permanent copyPermanent(Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier);
Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier);
Card copyCard(Card cardToCopy, Ability source, UUID newController);
void addTriggeredAbility(TriggeredAbility ability);
UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility);
void applyEffects();
boolean checkStateAndTriggered();
void playPriority(UUID activePlayerId, boolean resuming);
boolean endTurn();
int doAction(MageAction action);
//game transaction methods
void saveState(boolean bookmark);
int bookmarkState();
void restoreState(int bookmark, String context);
void removeBookmark(int bookmark);
int getSavedStateSize();
boolean isSaveGame();
void setSaveGame(boolean saveGame);
// game options
void setGameOptions(GameOptions options);
// game times
Date getStartTime();
Date getEndTime();
// game cheats (for tests only)
void cheat(UUID ownerId, Map<Zone, String> commands);
void cheat(UUID ownerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard);
// controlling the behaviour of replacement effects
void setScopeRelevant(boolean scopeRelevant);
public boolean getScopeRelevant();
// players' timers
void initTimer(UUID playerId);
void resumeTimer(UUID playerId);
void pauseTimer(UUID playerId);
int getPriorityTime();
void setPriorityTime(int priorityTime);
UUID getStartingPlayerId();
void saveRollBackGameState();
boolean canRollbackTurns(int turnsToRollback);
void rollbackTurns(int turnsToRollback);
boolean executingRollback();
}
void resumeTimer(UUID playerId);
void pauseTimer(UUID playerId);
int getPriorityTime();
void setPriorityTime(int priorityTime);
UUID getStartingPlayerId();
void saveRollBackGameState();
boolean canRollbackTurns(int turnsToRollback);
void rollbackTurns(int turnsToRollback);
boolean executingRollback();
}

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.game.combat;
import java.io.Serializable;
@ -87,13 +86,13 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
public boolean hasFirstOrDoubleStrike(Game game) {
for (UUID permId: attackers) {
for (UUID permId : attackers) {
Permanent attacker = game.getPermanent(permId);
if (attacker != null && hasFirstOrDoubleStrike(attacker)) {
return true;
}
}
for (UUID permId: blockers) {
for (UUID permId : blockers) {
Permanent blocker = game.getPermanent(permId);
if (blocker != null && hasFirstOrDoubleStrike(blocker)) {
return true;
@ -138,20 +137,18 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (attackers.size() > 0 && (!first || hasFirstOrDoubleStrike(game))) {
if (blockers.isEmpty()) {
unblockedDamage(first, game);
}
else {
} else {
Permanent attacker = game.getPermanent(attackers.get(0));
if (attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId())) {
Player player = game.getPlayer(attacker.getControllerId());
if (player.chooseUse(Outcome.Damage, "Do you wish to assign damage for " + attacker.getLogName() + " as though it weren't blocked?", game)) {
if (player.chooseUse(Outcome.Damage, "Do you wish to assign damage for " + attacker.getLogName() + " as though it weren't blocked?", null, game)) {
blocked = false;
unblockedDamage(first, game);
}
}
if (blockers.size() == 1) {
singleBlockerDamage(first, game);
}
else {
} else {
multiBlockerDamage(first, game);
}
}
@ -162,8 +159,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (blockers.size() > 0 && (!first || hasFirstOrDoubleStrike(game))) {
if (attackers.size() == 1) {
singleAttackerDamage(first, game);
}
else {
} else {
multiAttackerDamage(first, game);
}
}
@ -185,7 +181,8 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
/**
* Determines if permanent can damage in current (First Strike or not) combat damage step
* Determines if permanent can damage in current (First Strike or not)
* combat damage step
*
* @param perm Permanent to check
* @param first First strike or common combat damage step
@ -196,8 +193,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (first) {
// should have first strike or double strike
return hasFirstOrDoubleStrike(perm);
}
// if now not first strike combat
} // if now not first strike combat
else {
if (hasFirstStrike(perm)) {
// if it has first strike in non FS combat damage step
@ -211,7 +207,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
private void unblockedDamage(boolean first, Game game) {
for (UUID attackerId: attackers) {
for (UUID attackerId : attackers) {
Permanent attacker = game.getPermanent(attackerId);
if (canDamage(attacker, first)) {
//20091005 - 510.1c, 702.17c
@ -222,8 +218,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
}
private void singleBlockerDamage(boolean first, Game game) {
//TODO: handle banding
Permanent blocker = game.getPermanent(blockers.get(0));
@ -241,8 +235,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
if (lethalDamage >= damage) {
blocker.markDamage(damage, attacker.getId(), game, true, true);
}
else {
} else {
Player player = game.getPlayer(attacker.getControllerId());
int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game);
blocker.markDamage(damageAssigned, attacker.getId(), game, true, true);
@ -251,13 +244,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
defenderDamage(attacker, damage, game);
}
}
}
else {
} else {
blocker.markDamage(damage, attacker.getId(), game, true, true);
}
}
if (canDamage(blocker, first)) {
if (blocker.getBlocking() == 1) { // blocking several creatures handled separately
if (blocker.getBlocking() == 1) { // blocking several creatures handled separately
attacker.markDamage(blockerDamage, blocker.getId(), game, true, true);
}
}
@ -275,7 +267,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (canDamage(attacker, first)) {
// must be set before attacker damage marking because of effects like Test of Faith
Map<UUID, Integer> blockerPower = new HashMap<>();
for (UUID blockerId: blockerOrder) {
for (UUID blockerId : blockerOrder) {
Permanent blocker = game.getPermanent(blockerId);
if (canDamage(blocker, first)) {
if (blocker.getBlocking() == 1) { // blocking several creatures handled separately
@ -285,7 +277,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
Map<UUID, Integer> assigned = new HashMap<>();
if (blocked) {
for (UUID blockerId: blockerOrder) {
for (UUID blockerId : blockerOrder) {
Permanent blocker = game.getPermanent(blockerId);
int lethalDamage;
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
@ -309,7 +301,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) + damage);
}
}
for (UUID blockerId: blockerOrder) {
for (UUID blockerId : blockerOrder) {
Integer power = blockerPower.get(blockerId);
if (power != null) {
attacker.markDamage(power, blockerId, game, true, true);
@ -320,7 +312,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
blocker.markDamage(entry.getValue(), attacker.getId(), game, true, true);
}
} else {
for (UUID blockerId: blockerOrder) {
for (UUID blockerId : blockerOrder) {
Permanent blocker = game.getPermanent(blockerId);
if (canDamage(blocker, first)) {
attacker.markDamage(getDamageValueFromPermanent(blocker, game), blocker.getId(), game, true, true);
@ -331,11 +323,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
/**
* Damages attacking creatures by a creature that blocked several ones
* Damages only attackers as blocker was damage in {@link #singleBlockerDamage}.
* Damages only attackers as blocker was damage in
* {@link #singleBlockerDamage}.
*
* Handles abilities like "{this} an block any number of creatures.".
*
* @param first
* @param first
* @param game
*/
private void singleAttackerDamage(boolean first, Game game) {
@ -351,7 +344,8 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
/**
* Damages attacking creatures by a creature that blocked several ones
* Damages only attackers as blocker was damage in either {@link #singleBlockerDamage} or {@link #multiBlockerDamage}.
* Damages only attackers as blocker was damage in either
* {@link #singleBlockerDamage} or {@link #multiBlockerDamage}.
*
* Handles abilities like "{this} an block any number of creatures.".
*
@ -368,7 +362,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (canDamage(blocker, first)) {
Map<UUID, Integer> assigned = new HashMap<>();
for (UUID attackerId: attackerOrder) {
for (UUID attackerId : attackerOrder) {
Permanent attacker = game.getPermanent(attackerId);
int lethalDamage;
if (blocker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
@ -398,8 +392,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (defender != null) {
defender.markDamage(amount, attacker.getId(), game, true, true);
}
}
else {
} else {
Player defender = game.getPlayer(defenderId);
defender.damage(amount, attacker.getId(), game, true, true);
}
@ -407,10 +400,10 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
public boolean canBlock(Permanent blocker, Game game) {
// player can't block if another player is attacked
if (!defendingPlayerId.equals(blocker.getControllerId()) ) {
if (!defendingPlayerId.equals(blocker.getControllerId())) {
return false;
}
for (UUID attackerId: attackers) {
for (UUID attackerId : attackers) {
if (!blocker.canBlock(attackerId, game)) {
return false;
}
@ -419,7 +412,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
public void addBlocker(UUID blockerId, UUID playerId, Game game) {
for (UUID attackerId: attackers) {
for (UUID attackerId : attackers) {
if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, attackerId, blockerId, playerId))) {
return;
}
@ -445,10 +438,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (blockerList.size() == 1) {
blockerOrder.add(blockerList.get(0));
break;
}
else {
} else {
List<Permanent> blockerPerms = new ArrayList<>();
for (UUID blockerId: blockerList) {
for (UUID blockerId : blockerList) {
blockerPerms.add(game.getPermanent(blockerId));
}
UUID blockerId = player.chooseBlockerOrder(blockerPerms, this, blockerOrder, game);
@ -469,10 +461,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (attackerList.size() == 1) {
attackerOrder.add(attackerList.get(0));
break;
}
else {
} else {
List<Permanent> attackerPerms = new ArrayList<>();
for (UUID attackerId: attackerList) {
for (UUID attackerId : attackerList) {
attackerPerms.add(game.getPermanent(attackerId));
}
UUID attackerId = player.chooseAttackerOrder(attackerPerms, game);
@ -484,7 +475,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
public int totalAttackerDamage(Game game) {
int total = 0;
for (UUID attackerId: attackers) {
for (UUID attackerId : attackers) {
total += getDamageValueFromPermanent(game.getPermanent(attackerId), game);
}
return total;
@ -517,13 +508,13 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
return;
}
for (UUID blockerId : blockers) {
for (UUID attackerId: attackers) {
for (UUID attackerId : attackers) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, attackerId, blockerId, players.get(blockerId)));
}
}
if(!blockers.isEmpty()) {
for (UUID attackerId: attackers) {
if (!blockers.isEmpty()) {
for (UUID attackerId : attackers) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, attackerId, null));
}
}
@ -536,12 +527,13 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
if (blockersCount == 1) {
List<UUID> toBeRemoved = new ArrayList<>();
for (UUID blockerId: getBlockers()) {
for (UUID blockerId : getBlockers()) {
Permanent blocker = game.getPermanent(blockerId);
if (blocker != null && blocker.getAbilities().containsKey(CantBlockAloneAbility.getInstance().getId())) {
blockWasLegal = false;
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(blocker.getLogName() + " can't block alone. Removing it from combat.");
}
toBeRemoved.add(blockerId);
}
}
@ -567,8 +559,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
blockers.clear();
blockerOrder.clear();
this.blocked = false;
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded.");
}
blockWasLegal = false;
}
// Check if there are to many blockers (maxBlockedBy = 0 means no restrictions)
@ -582,20 +575,23 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
blockers.clear();
blockerOrder.clear();
this.blocked = false;
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(attacker.getLogName())
.append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy())
.append(attacker.getMaxBlockedBy()==1?" creature.":" creatures.")
.append(" Blockers discarded.").toString());
.append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy())
.append(attacker.getMaxBlockedBy() == 1 ? " creature." : " creatures.")
.append(" Blockers discarded.").toString());
}
blockWasLegal = false;
}
}
return blockWasLegal;
}
/**
* There are effects that let creatures assigns combat damage equal to its toughness rather than its power.
* So this method takes this into account to get the value of damage a creature will assign
* There are effects that let creatures assigns combat damage equal to its
* toughness rather than its power. So this method takes this into account
* to get the value of damage a creature will assign
*
* @param permanent
* @param game
@ -610,14 +606,15 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
/**
* There are effects, that set an attacker to be blcoked.
* Therefore this setter can be used.
* @param blocked
* There are effects, that set an attacker to be blcoked. Therefore this
* setter can be used.
*
* @param blocked
*/
public void setBlocked(boolean blocked) {
this.blocked = blocked;
}
@Override
public CombatGroup copy() {
return new CombatGroup(this);

View file

@ -106,7 +106,7 @@ public class Commander implements CommandObject{
@Override
public String getLogName() {
return GameLog.getColoredObjectName(this);
return GameLog.getColoredObjectIdName(this);
}
@Override

View file

@ -106,7 +106,7 @@ public class Emblem implements CommandObject {
@Override
public String getLogName() {
return GameLog.getColoredObjectName(this);
return GameLog.getColoredObjectIdName(this);
}
@Override

View file

@ -1300,7 +1300,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return GameLog.getNeutralColoredText("a creature without name");
}
}
return GameLog.getColoredObjectName(this);
return GameLog.getColoredObjectIdName(this);
}
@Override

View file

@ -354,7 +354,7 @@ public class Spell extends StackObjImpl implements Card {
@Override
public String getLogName() {
return GameLog.getColoredObjectName(card);
return GameLog.getColoredObjectIdName(card);
}
@Override

View file

@ -132,7 +132,7 @@ public class StackAbility extends StackObjImpl implements Ability {
@Override
public String getLogName() {
return GameLog.getColoredObjectName(this);
return GameLog.getColoredObjectIdName(this);
}
@Override

View file

@ -37,38 +37,38 @@ public abstract class StackObjImpl implements StackObject {
public boolean chooseNewTargets(Game game, UUID playerId) {
return chooseNewTargets(game, playerId, false, false, null);
}
/**
* 114.6. Some effects allow a player to change the target(s) of a spell or
* ability, and other effects allow a player to choose new targets for a
* spell or ability.
*
* 114.6a If an effect allows a player to "change the
* target(s)" of a spell or ability, each target can be changed only to
* another legal target. If a target can't be changed to another legal
* target, the original target is unchanged, even if the original target is
* itself illegal by then. If all the targets aren't changed to other legal
* targets, none of them are changed.
*
* 114.6b If an effect allows a player to "change a target" of a
* spell or ability, the process described in rule 114.6a
* is followed, except that only one of those targets may be changed
* (rather than all of them or none of them).
*
* 114.6c If an effect allows a
* player to "change any targets" of a spell or ability, the process
* described in rule 114.6a is followed, except that any number of those
* targets may be changed (rather than all of them or none of them).
*
* 114.6d If an effect allows a player to "choose new targets" for a spell or
* ability, the player may leave any number of the targets unchanged, even
* if those targets would be illegal. If the player chooses to change some
* or all of the targets, the new targets must be legal and must not cause
* any unchanged targets to become illegal.
*
* 114.6e When changing targets or
* choosing new targets for a spell or ability, only the final set of
* targets is evaluated to determine whether the change is legal.
* spell or ability.
*
* 114.6a If an effect allows a player to "change the target(s)" of a spell
* or ability, each target can be changed only to another legal target. If a
* target can't be changed to another legal target, the original target is
* unchanged, even if the original target is itself illegal by then. If all
* the targets aren't changed to other legal targets, none of them are
* changed.
*
* 114.6b If an effect allows a player to "change a target" of a spell or
* ability, the process described in rule 114.6a is followed, except that
* only one of those targets may be changed (rather than all of them or none
* of them).
*
* 114.6c If an effect allows a player to "change any targets" of a spell or
* ability, the process described in rule 114.6a is followed, except that
* any number of those targets may be changed (rather than all of them or
* none of them).
*
* 114.6d If an effect allows a player to "choose new targets" for a spell
* or ability, the player may leave any number of the targets unchanged,
* even if those targets would be illegal. If the player chooses to change
* some or all of the targets, the new targets must be legal and must not
* cause any unchanged targets to become illegal.
*
* 114.6e When changing targets or choosing new targets for a spell or
* ability, only the final set of targets is evaluated to determine whether
* the change is legal.
*
* Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to
* target creature or player and 1 damage to another target creature or
@ -92,10 +92,14 @@ public abstract class StackObjImpl implements StackObject {
* targets will be, the copy is put onto the stack with those targets.
*
* @param game
* @param targetControllerId - player that can/has to change the target of the spell
* @param forceChange - does only work for targets with maximum of one targetId
* @param onlyOneTarget - 114.6b one target must be changed to another target
* @param filterNewTarget restriction for the new target, if null nothing is cheched
* @param targetControllerId - player that can/has to change the target of
* the spell
* @param forceChange - does only work for targets with maximum of one
* targetId
* @param onlyOneTarget - 114.6b one target must be changed to another
* target
* @param filterNewTarget restriction for the new target, if null nothing is
* cheched
* @return
*/
@Override
@ -105,8 +109,8 @@ public abstract class StackObjImpl implements StackObject {
StringBuilder newTargetDescription = new StringBuilder();
// Fused split spells or spells where "Splice on Arcane" was used can have more than one ability
Abilities<Ability> objectAbilities = new AbilitiesImpl<>();
if (this instanceof Spell) {
objectAbilities.addAll(((Spell)this).getSpellAbilities());
if (this instanceof Spell) {
objectAbilities.addAll(((Spell) this).getSpellAbilities());
} else {
objectAbilities.addAll(getAbilities());
}
@ -119,7 +123,7 @@ public abstract class StackObjImpl implements StackObject {
// clear the old target and copy all targets from new target
target.clearChosen();
for (UUID targetId : newTarget.getTargets()) {
target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false);
target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false);
}
}
@ -137,30 +141,30 @@ public abstract class StackObjImpl implements StackObject {
/**
* Handles the change of one target instance of a mode
*
*
* @param targetController - player that can choose the new target
* @param ability
* @param mode
* @param target
* @param forceChange
* @param game
* @return
* @return
*/
private Target chooseNewTarget(Player targetController, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) {
Target newTarget = target.copy();
if (!targetController.getId().equals(getControllerId())) {
newTarget.setTargetController(targetController.getId()); // target controller for the change is different from spell controller
newTarget.setAbilityController(getControllerId());
}
}
newTarget.clearChosen();
for (UUID targetId : target.getTargets()) {
String targetNames = getNamesOftargets(targetId, game);
// change the target?
if (targetNames != null
&& (forceChange || targetController.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) {
&& (forceChange || targetController.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", ability, game))) {
Set<UUID> possibleTargets = target.possibleTargets(this.getSourceId(), getControllerId(), game);
// choose exactly one other target - already targeted objects are not counted
if (forceChange && possibleTargets != null && possibleTargets.size() > 1) { // controller of spell must be used (e.g. TargetOpponent)
if (forceChange && possibleTargets != null && possibleTargets.size() > 1) { // controller of spell must be used (e.g. TargetOpponent)
int iteration = 0;
do {
if (iteration > 0 && !game.isSimulation()) {
@ -168,36 +172,36 @@ public abstract class StackObjImpl implements StackObject {
}
iteration++;
newTarget.clearChosen();
newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game);
// check target restriction
newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game);
// check target restriction
if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(targetController, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
game.informPlayer(targetController, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() + ")");
newTarget.clearChosen();
}
}
}
} while (targetController.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
// choose a new target
// choose a new target
} else {
// build a target definition with exactly one possible target to select that replaces old target
Target tempTarget = target.copy();
if (target instanceof TargetAmount) {
((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId)));
((TargetAmount) tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId)));
}
tempTarget.setMinNumberOfTargets(1);
tempTarget.setMaxNumberOfTargets(1);
if (!targetController.getId().equals(getControllerId())) {
tempTarget.setTargetController(targetController.getId());
tempTarget.setAbilityController(getControllerId());
tempTarget.setAbilityController(getControllerId());
}
boolean again;
do {
again = false;
tempTarget.clearChosen();
if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game)) {
if (targetController.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) {
if (targetController.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", ability, game)) {
// use previous target no target was selected
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
} else {
@ -206,43 +210,41 @@ public abstract class StackObjImpl implements StackObject {
} else {
// if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition
if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) {
if(targetController.isHuman()) {
if (targetController.isHuman()) {
game.informPlayer(targetController, "This target was already selected from origin spell. You can only keep this target!");
again = true;
} else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
} else if (!target.canTarget(getControllerId(), tempTarget.getFirstTarget(), ability, game)) {
if(targetController.isHuman()) {
if (targetController.isHuman()) {
game.informPlayer(targetController, "This target is not valid!");
again = true;
} else {
// keep the old
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
}
} else if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() + ")");
again = true;
}
}
} else {
// valid target was selected, add it to the new target definition
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false);
}
}
} while (again && targetController.isInGame());
}
}
// keep the target
else {
}
} // keep the target
else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
}
}
return newTarget;
}
private String getNamesOftargets(UUID targetId, Game game) {
MageObject object = game.getObject(targetId);
String name = null;
@ -256,5 +258,5 @@ public abstract class StackObjImpl implements StackObject {
}
return name;
}
}

View file

@ -439,7 +439,7 @@ public interface Player extends MageItem, Copyable<Player> {
boolean chooseMulligan(Game game);
boolean chooseUse(Outcome outcome, String message, Game game);
boolean chooseUse(Outcome outcome, String message, Ability source, Game game);
boolean choose(Outcome outcome, Choice choice, Game game);

View file

@ -1326,7 +1326,7 @@ public abstract class PlayerImpl implements Player, Serializable {
int current = 0, last = cards.size();
for (Card card : cards.getCards(game)) {
current++;
sb.append(card.getLogName());
sb.append(GameLog.getColoredObjectName(card));
if (current < last) {
sb.append(", ");
}
@ -1373,7 +1373,7 @@ public abstract class PlayerImpl implements Player, Serializable {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
boolean untap = true;
for (RestrictionEffect effect : game.getContinuousEffects().getApplicableRestrictionEffects(permanent, game).keySet()) {
untap &= effect.canBeUntapped(permanent, game);
untap &= effect.canBeUntapped(permanent, null, game);
}
if (untap) {
canBeUntapped.add(permanent);
@ -1482,7 +1482,7 @@ public abstract class PlayerImpl implements Player, Serializable {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
boolean untap = true;
for (RestrictionEffect effect : game.getContinuousEffects().getApplicableRestrictionEffects(permanent, game).keySet()) {
untap &= effect.canBeUntapped(permanent, game);
untap &= effect.canBeUntapped(permanent, null, game);
}
if (untap) {
permanent.untap(game);
@ -2960,7 +2960,7 @@ public abstract class PlayerImpl implements Player, Serializable {
boolean chooseOrder = false;
if (userData.askMoveToGraveOrder()) {
if (cards.size() > 3) {
chooseOrder = choosingPlayer.chooseUse(Outcome.Neutral, "Would you like to choose the order the cards go to graveyard?", game);
chooseOrder = choosingPlayer.chooseUse(Outcome.Neutral, "Would you like to choose the order the cards go to graveyard?", source, game);
}
}
if (chooseOrder) {

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.util;
import mage.MageObject;
@ -36,64 +35,66 @@ import mage.ObjectColor;
* @author LevelX2
*/
public class GameLog {
static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen
static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate
static final String LOG_COLOR_PLAYER_CONFIRM = "#D2691E"; // Chocolate
static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen
static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate
static final String LOG_COLOR_PLAYER_CONFIRM = "#D2691E"; // Chocolate
static final String LOG_COLOR_GREEN = "#90EE90"; // LightGreen
static final String LOG_COLOR_RED = "#FF6347"; // Tomato
static final String LOG_COLOR_RED = "#FF6347"; // Tomato
static final String LOG_COLOR_BLUE = "#87CEFA"; // LightSkyBlue
static final String LOG_COLOR_BLACK = "#696969"; // DimGray // "#5F9EA0"; // CadetBlue
static final String LOG_COLOR_WHITE = "#F0E68C"; // Khaki
static final String LOG_COLOR_MULTI = "#DAA520"; // GoldenRod
static final String LOG_COLOR_COLORLESS = "#B0C4DE"; // LightSteelBlue
static final String LOG_COLOR_NEUTRAL = "#F0F8FF"; // AliceBlue
static final String LOG_COLOR_WHITE = "#F0E68C"; // Khaki
static final String LOG_COLOR_MULTI = "#DAA520"; // GoldenRod
static final String LOG_COLOR_COLORLESS = "#B0C4DE"; // LightSteelBlue
static final String LOG_COLOR_NEUTRAL = "#F0F8FF"; // AliceBlue
public static String replaceNameByColoredName(MageObject mageObject, String text) {
return text.replaceAll(mageObject.getName(), getColoredObjectName(mageObject));
return text.replaceAll(mageObject.getName(), getColoredObjectIdName(mageObject));
}
public static String getColoredObjectName(MageObject mageObject) {
return "<font color=\'" + getColorName(mageObject.getColor(null)) + "\'>" + mageObject.getName() + " ["+mageObject.getId().toString().substring(0,3) + "]</font>";
public static String getColoredObjectName(MageObject mageObject) {
return "<font color=\'" + getColorName(mageObject.getColor(null)) + "\'>" + mageObject.getName() + "</font>";
}
public static String getNeutralColoredText(String text) {
public static String getColoredObjectIdName(MageObject mageObject) {
return "<font color=\'" + getColorName(mageObject.getColor(null)) + "\'>" + mageObject.getName() + " [" + mageObject.getId().toString().substring(0, 3) + "]</font>";
}
public static String getNeutralColoredText(String text) {
return "<font color=\'" + LOG_COLOR_NEUTRAL + "\'>" + text + "</font>";
}
public static String getColoredPlayerName(String name) {
public static String getColoredPlayerName(String name) {
return "<font color=\'" + LOG_COLOR_PLAYER + "\'>" + name + "</font>";
}
public static String getPlayerRequestColoredText(String name) {
public static String getPlayerRequestColoredText(String name) {
return "<font color=\'" + LOG_COLOR_PLAYER_REQUEST + "\'>" + name + "</font>";
}
public static String getPlayerConfirmColoredText(String name) {
public static String getPlayerConfirmColoredText(String name) {
return "<font color=\'" + LOG_COLOR_PLAYER_CONFIRM + "\'>" + name + "</font>";
}
public static String getSmallSecondLineText(String text) {
public static String getSmallSecondLineText(String text) {
return "<div style='font-size:11pt'>" + text + "</div>";
}
private static String getColorName(ObjectColor objectColor) {
if (objectColor.isMulticolored()) {
return LOG_COLOR_MULTI;
} else if (objectColor.isColorless()){
} else if (objectColor.isColorless()) {
return LOG_COLOR_COLORLESS;
} else if (objectColor.isRed()){
} else if (objectColor.isRed()) {
return LOG_COLOR_RED;
} else if (objectColor.isGreen()){
} else if (objectColor.isGreen()) {
return LOG_COLOR_GREEN;
} else if (objectColor.isBlue()){
} else if (objectColor.isBlue()) {
return LOG_COLOR_BLUE;
} else if (objectColor.isWhite()){
} else if (objectColor.isWhite()) {
return LOG_COLOR_WHITE;
} else {
return LOG_COLOR_BLACK;
return LOG_COLOR_BLACK;
}
}
}

View file

@ -1,18 +1,23 @@
package mage.util;
import mage.Mana;
import mage.ManaSymbol;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaSymbols;
import mage.abilities.mana.*;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.Mana;
import mage.ManaSymbol;
import mage.abilities.Ability;
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaSymbols;
import mage.abilities.mana.BasicManaAbility;
import mage.abilities.mana.BlackManaAbility;
import mage.abilities.mana.BlueManaAbility;
import mage.abilities.mana.GreenManaAbility;
import mage.abilities.mana.ManaAbility;
import mage.abilities.mana.RedManaAbility;
import mage.abilities.mana.WhiteManaAbility;
import mage.cards.Card;
import mage.game.Game;
@ -21,29 +26,32 @@ import mage.game.Game;
*/
public class ManaUtil {
private ManaUtil() {}
private ManaUtil() {
}
/**
* In case the choice of mana to be produced is obvious, let's discard all other abilities.
* In case the choice of mana to be produced is obvious, let's discard all
* other abilities.
*
* Example:
* Pay {W}{R}
* Example: Pay {W}{R}
*
* Land produces {W} or {G}.
* Land produces {W} or {G}.
*
* No need to ask what player wants to choose.
* {W} mana ability should be left only.
* No need to ask what player wants to choose. {W} mana ability should be
* left only.
*
* But we CAN do auto choice only in case we have basic mana abilities.
* Example:
* we should pay {1} and we have Cavern of Souls that can produce {1} or any mana of creature type choice.
* We can't simply auto choose {1} as the second mana ability also makes spell uncounterable.
* Example: we should pay {1} and we have Cavern of Souls that can produce
* {1} or any mana of creature type choice. We can't simply auto choose {1}
* as the second mana ability also makes spell uncounterable.
*
* In case we can't auto choose we'll simply return the useableAbilities map back to caller without any modification.
* In case we can't auto choose we'll simply return the useableAbilities map
* back to caller without any modification.
*
* @param unpaid Mana we need to pay. Can be null (it is for X costs now).
* @param useableAbilities List of mana abilities permanent may produce
* @return List of mana abilities permanent may produce and are reasonable for unpaid mana
* @return List of mana abilities permanent may produce and are reasonable
* for unpaid mana
*/
public static LinkedHashMap<UUID, ManaAbility> tryToAutoPay(ManaCost unpaid, LinkedHashMap<UUID, ManaAbility> useableAbilities) {
@ -55,8 +63,7 @@ public class ManaUtil {
}
}
if (unpaid != null) {
if (unpaid != null) {
ManaSymbols symbols = ManaSymbols.buildFromManaCost(unpaid);
Mana unpaidMana = unpaid.getMana();
@ -339,10 +346,10 @@ public class ManaUtil {
return useableAbilities;
}
/**
* This activates the special button inthe feedback panel of the client
* if there exists special ways to pay the mana (e.g. Delve, Convoke)
* This activates the special button inthe feedback panel of the client if
* there exists special ways to pay the mana (e.g. Delve, Convoke)
*
* @param source ability the mana costs have to be paid for
* @param game
@ -350,17 +357,26 @@ public class ManaUtil {
* @return message to be shown in human players feedback area
*/
public static String addSpecialManaPayAbilities(Ability source, Game game, ManaCost unpaid) {
MageObject baseObject = game.getPermanent(source.getSourceId());
if (baseObject == null) {
baseObject = game.getCard(source.getSourceId());
}
// check for special mana payment possibilities
MageObject mageObject = source.getSourceObject(game);
if (mageObject instanceof Card) {
for (Ability ability :((Card)mageObject).getAbilities(game)) {
MageObject mageObject = source.getSourceObject(game);
if (mageObject instanceof Card) {
for (Ability ability : ((Card) mageObject).getAbilities(game)) {
if (ability instanceof AlternateManaPaymentAbility) {
((AlternateManaPaymentAbility) ability).addSpecialAction(source, game, unpaid);
}
}
return unpaid.getText() + "<div style='font-size:11pt'>" + mageObject.getLogName() + "</div>";
if (baseObject == null) {
baseObject = mageObject;
}
}
if (baseObject != null) {
return unpaid.getText() + "<div style='font-size:11pt'>" + baseObject.getLogName() + "</div>";
} else {
return unpaid.getText();
}
}
}
}

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.watchers.common;
import java.util.HashMap;
@ -45,8 +44,8 @@ import mage.players.Player;
import mage.watchers.Watcher;
/**
* Counts amount of cards drawn this turn by players.
* Asks players about Miracle ability to be activated if it the first card drawn this turn.
* Counts amount of cards drawn this turn by players. Asks players about Miracle
* ability to be activated if it the first card drawn this turn.
*
* @author noxx
*/
@ -99,9 +98,9 @@ public class MiracleWatcher extends Watcher {
Cards cards = new CardsImpl();
cards.add(card);
controller.lookAtCards("Miracle", cards, game);
if (controller.chooseUse(Outcome.Benefit, "Reveal " + card.getLogName() + " to be able to use Miracle?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Reveal " + card.getLogName() + " to be able to use Miracle?", ability, game)) {
controller.revealCards("Miracle", cards, game);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.MIRACLE_CARD_REVEALED, card.getId(), card.getId(),controller.getId()));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.MIRACLE_CARD_REVEALED, card.getId(), card.getId(), controller.getId()));
break;
}
}
@ -110,7 +109,6 @@ public class MiracleWatcher extends Watcher {
}
}
@Override
public void reset() {
amountOfCardsDrawnThisTurn.clear();

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.watchers.common;
import mage.abilities.keyword.SoulbondAbility;
@ -34,7 +33,6 @@ import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
@ -79,7 +77,7 @@ public class SoulbondWatcher extends Watcher {
Cards cards = new CardsImpl();
cards.add(permanent);
controller.lookAtCards("Soulbond", cards, game);
if (controller.chooseUse(Outcome.Benefit, "Use Soulbond?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Use Soulbond?", null, game)) {
TargetControlledPermanent target = new TargetControlledPermanent(filter);
target.setNotTarget(true);
if (target.canChoose(permanent.getId(), controller.getId(), game)) {
@ -88,8 +86,9 @@ public class SoulbondWatcher extends Watcher {
if (chosen != null) {
chosen.setPairedCard(permanent.getId());
permanent.setPairedCard(chosen.getId());
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(" souldbonds ").append(permanent.getLogName()).append(" with ").append(chosen.getName()).toString());
}
}
}
}
@ -110,11 +109,12 @@ public class SoulbondWatcher extends Watcher {
Cards cards = new CardsImpl();
cards.add(chosen);
controller.lookAtCards("Soulbond", cards, game);
if (controller.chooseUse(Outcome.Benefit, "Use Soulbond for recent " + permanent.getLogName() + "?", game)) {
if (controller.chooseUse(Outcome.Benefit, "Use Soulbond for recent " + permanent.getLogName() + "?", null, game)) {
chosen.setPairedCard(permanent.getId());
permanent.setPairedCard(chosen.getId());
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(" souldbonds ").append(permanent.getLogName()).append(" with ").append(chosen.getName()).toString());
}
break;
}
}