mirror of
https://github.com/magefree/mage.git
synced 2026-01-23 19:59:54 -08:00
Merge remote-tracking branch 'origin/master' into decouple-client
Conflicts: Mage/src/mage/cards/repository/CardRepository.java
This commit is contained in:
commit
1fccbd6b87
339 changed files with 17278 additions and 1002 deletions
|
|
@ -52,6 +52,8 @@ import org.apache.log4j.Logger;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.keyword.EntwineAbility;
|
||||
import mage.constants.SpellAbilityType;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -161,21 +163,35 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
|
||||
@Override
|
||||
public boolean activate(Game game, boolean noMana) {
|
||||
// 20110204 - 700.2
|
||||
/* 20130201 - 601.2b
|
||||
* If the spell is modal the player announces the mode choice (see rule 700.2).
|
||||
*/
|
||||
if (!modes.choose(game, this)) {
|
||||
return false;
|
||||
}
|
||||
//20100716 - 601.2b
|
||||
|
||||
/* 20130201 - 601.2b
|
||||
* If the player wishes to splice any cards onto the spell (see rule 702.45), he
|
||||
* or she reveals those cards in his or her hand.
|
||||
*/
|
||||
if (this.abilityType.equals(AbilityType.SPELL)) {
|
||||
game.getContinuousEffects().applySpliceEffects(this, game);
|
||||
}
|
||||
|
||||
|
||||
Card card = game.getCard(sourceId);
|
||||
if (card != null) {
|
||||
card.adjustChoices(this, game);
|
||||
}
|
||||
if (getChoices().size() > 0 && getChoices().choose(game, this) == false) {
|
||||
logger.debug("activate failed - choice");
|
||||
return false;
|
||||
for (UUID modeId :this.getModes().getSelectedModes()) {
|
||||
this.getModes().setMode(this.getModes().get(modeId));
|
||||
if (getChoices().size() > 0 && getChoices().choose(game, this) == false) {
|
||||
logger.debug("activate failed - choice");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 20121001 - 601.2b
|
||||
// 20130201 - 601.2b
|
||||
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
||||
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
||||
// or her intentions to pay any or all of those costs (see rule 601.2e).
|
||||
|
|
@ -193,38 +209,43 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 20121001 - 601.2b
|
||||
// If the spell has a variable cost that will be paid as it's being cast (such as an {X} in
|
||||
// its mana cost; see rule 107.3), the player announces the value of that variable.
|
||||
VariableManaCost variableManaCost = handleXCosts(game, noMana);
|
||||
|
||||
//20121001 - 601.2c
|
||||
// 601.2c The player announces his or her choice of an appropriate player, object, or zone for
|
||||
// each target the spell requires. A spell may require some targets only if an alternative or
|
||||
// additional cost (such as a buyback or kicker cost), or a particular mode, was chosen for it;
|
||||
// otherwise, the spell is cast as though it did not require those targets. If the spell has a
|
||||
// variable number of targets, the player announces how many targets he or she will choose before
|
||||
// he or she announces those targets. The same target can't be chosen multiple times for any one
|
||||
// instance of the word "target" on the spell. However, if the spell uses the word "target" in
|
||||
// multiple places, the same object, player, or zone can be chosen once for each instance of the
|
||||
// word "target" (as long as it fits the targeting criteria). If any effects say that an object
|
||||
// or player must be chosen as a target, the player chooses targets so that he or she obeys the
|
||||
// maximum possible number of such effects without violating any rules or effects that say that
|
||||
// an object or player can't be chosen as a target. The chosen players, objects, and/or zones
|
||||
// each become a target of that spell. (Any abilities that trigger when those players, objects,
|
||||
// and/or zones become the target of a spell trigger at this point; they'll wait to be put on
|
||||
// the stack until the spell has finished being cast.)
|
||||
if (card != null) {
|
||||
card.adjustTargets(this, game);
|
||||
}
|
||||
if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) {
|
||||
if (variableManaCost != null) {
|
||||
game.informPlayers(new StringBuilder(card.getName()).append(": no valid targets with this value of X").toString());
|
||||
} else {
|
||||
logger.debug("activate failed - target");
|
||||
for (UUID modeId :this.getModes().getSelectedModes()) {
|
||||
this.getModes().setMode(this.getModes().get(modeId));
|
||||
//20121001 - 601.2c
|
||||
// 601.2c The player announces his or her choice of an appropriate player, object, or zone for
|
||||
// each target the spell requires. A spell may require some targets only if an alternative or
|
||||
// additional cost (such as a buyback or kicker cost), or a particular mode, was chosen for it;
|
||||
// otherwise, the spell is cast as though it did not require those targets. If the spell has a
|
||||
// variable number of targets, the player announces how many targets he or she will choose before
|
||||
// he or she announces those targets. The same target can't be chosen multiple times for any one
|
||||
// instance of the word "target" on the spell. However, if the spell uses the word "target" in
|
||||
// multiple places, the same object, player, or zone can be chosen once for each instance of the
|
||||
// word "target" (as long as it fits the targeting criteria). If any effects say that an object
|
||||
// or player must be chosen as a target, the player chooses targets so that he or she obeys the
|
||||
// maximum possible number of such effects without violating any rules or effects that say that
|
||||
// an object or player can't be chosen as a target. The chosen players, objects, and/or zones
|
||||
// each become a target of that spell. (Any abilities that trigger when those players, objects,
|
||||
// and/or zones become the target of a spell trigger at this point; they'll wait to be put on
|
||||
// the stack until the spell has finished being cast.)
|
||||
|
||||
if (card != null) {
|
||||
card.adjustTargets(this, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) {
|
||||
if (variableManaCost != null) {
|
||||
game.informPlayers(new StringBuilder(card != null ? card.getName(): "").append(": no valid targets with this value of X").toString());
|
||||
} else {
|
||||
logger.debug("activate failed - target");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // end modes
|
||||
|
||||
// TODO: Handle optionalCosts at the same time as already OptionalAdditionalSourceCosts are handled.
|
||||
for (Cost cost : optionalCosts) {
|
||||
|
|
|
|||
|
|
@ -243,21 +243,59 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
} else {
|
||||
sb.append("unknown");
|
||||
}
|
||||
if (object instanceof Spell && ((Spell) object).getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) {
|
||||
Spell<?> spell = (Spell<?>) object;
|
||||
int i = 0;
|
||||
for (SpellAbility spellAbility : spell.getSpellAbilities()) {
|
||||
i++;
|
||||
String half;
|
||||
if (i == 1) {
|
||||
half = " left";
|
||||
} else {
|
||||
half = " right";
|
||||
if (object instanceof Spell && ((Spell) object).getSpellAbilities().size() > 1) {
|
||||
if (((Spell) object).getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) {
|
||||
Spell<?> spell = (Spell<?>) object;
|
||||
int i = 0;
|
||||
for (SpellAbility spellAbility : spell.getSpellAbilities()) {
|
||||
i++;
|
||||
String half;
|
||||
if (i == 1) {
|
||||
half = " left";
|
||||
} else {
|
||||
half = " right";
|
||||
}
|
||||
if (spellAbility.getTargets().size() > 0) {
|
||||
sb.append(half).append(" half targeting ");
|
||||
for (Target target: spellAbility.getTargets()) {
|
||||
sb.append(target.getTargetedName(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spellAbility.getTargets().size() > 0) {
|
||||
sb.append(half).append(" half targeting ");
|
||||
for (Target target: spellAbility.getTargets()) {
|
||||
sb.append(target.getTargetedName(game));
|
||||
} else {
|
||||
Spell<?> spell = (Spell<?>) object;
|
||||
int i = 0;
|
||||
for (SpellAbility spellAbility : spell.getSpellAbilities()) {
|
||||
i++;
|
||||
if ( i > 1) {
|
||||
sb.append(" splicing ");
|
||||
if (spellAbility.name.length() > 5 && spellAbility.name.startsWith("Cast ")) {
|
||||
sb.append(spellAbility.name.substring(5));
|
||||
} else {
|
||||
sb.append(spellAbility.name);
|
||||
}
|
||||
}
|
||||
if (spellAbility.getTargets().size() > 0) {
|
||||
for (Target target: spellAbility.getTargets()) {
|
||||
sb.append(" targeting ");
|
||||
sb.append(target.getTargetedName(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (object instanceof Spell && ((Spell) object).getSpellAbility().getModes().size() > 1) {
|
||||
Modes spellModes = ((Spell) object).getSpellAbility().getModes();
|
||||
int item = 0;
|
||||
for (Mode mode : spellModes.values()) {
|
||||
item++;
|
||||
if (spellModes.getSelectedModes().contains(mode.getId())) {
|
||||
spellModes.setMode(mode);
|
||||
sb.append(" (mode ").append(item).append(")");
|
||||
if (getTargets().size() > 0) {
|
||||
sb.append(" targeting ");
|
||||
for (Target target: getTargets()) {
|
||||
sb.append(target.getTargetedName(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,12 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
|
|
@ -40,11 +44,18 @@ import mage.players.Player;
|
|||
public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||
|
||||
private UUID modeId;
|
||||
private Set<UUID> selectedModes = new LinkedHashSet<UUID>();
|
||||
private int minModes;
|
||||
private int maxModes;
|
||||
|
||||
public Modes() {
|
||||
Mode mode = new Mode();
|
||||
this.put(mode.getId(), mode);
|
||||
this.modeId = mode.getId();
|
||||
this.minModes = 1;
|
||||
this.maxModes = 1;
|
||||
this.selectedModes.add(modeId);
|
||||
|
||||
}
|
||||
|
||||
public Modes(Modes modes) {
|
||||
|
|
@ -52,6 +63,9 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
for (Map.Entry<UUID, Mode> entry: modes.entrySet()) {
|
||||
this.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
this.minModes = modes.minModes;
|
||||
this.maxModes = modes.maxModes;
|
||||
this.selectedModes.addAll(modes.selectedModes);
|
||||
}
|
||||
|
||||
public Modes copy() {
|
||||
|
|
@ -62,9 +76,30 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
return get(modeId);
|
||||
}
|
||||
|
||||
public Set<UUID> getSelectedModes() {
|
||||
return selectedModes;
|
||||
}
|
||||
|
||||
public void setMinModes(int minModes) {
|
||||
this.minModes = minModes;
|
||||
}
|
||||
|
||||
public int getMinModes() {
|
||||
return this.minModes;
|
||||
}
|
||||
|
||||
public void setMaxModes(int maxModes) {
|
||||
this.maxModes = maxModes;
|
||||
}
|
||||
|
||||
public int getMaxModes() {
|
||||
return this.maxModes;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
if (this.containsKey(mode.getId())) {
|
||||
this.modeId = mode.getId();
|
||||
this.selectedModes.add(mode.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,40 +109,78 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
|
||||
public boolean choose(Game game, Ability source) {
|
||||
if (this.size() > 1) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Mode choice = player.chooseMode(this, source, game);
|
||||
if (choice == null) {
|
||||
return false;
|
||||
this.selectedModes.clear();
|
||||
// check if mode modifying abilities exist
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability modeModifyingAbility : card.getAbilities()) {
|
||||
if (modeModifyingAbility instanceof OptionalAdditionalModeSourceCosts) {
|
||||
((OptionalAdditionalModeSourceCosts)modeModifyingAbility).addOptionalAdditionalModeCosts(source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if all modes can be activated automatically
|
||||
if (this.size() == this.getMinModes()) {
|
||||
for (Mode mode: this.values()) {
|
||||
if (mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) {
|
||||
this.selectedModes.add(mode.getId());
|
||||
}
|
||||
}
|
||||
return selectedModes.size() > 0;
|
||||
}
|
||||
// player chooses modes manually
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
while (this.selectedModes.size() < this.getMaxModes()) {
|
||||
Mode choice = player.chooseMode(this, source, game);
|
||||
if (choice == null) {
|
||||
return this.selectedModes.size() >= this.getMinModes();
|
||||
}
|
||||
setMode(choice);
|
||||
this.selectedModes.add(choice.getId());
|
||||
}
|
||||
setMode(choice);
|
||||
return true;
|
||||
}
|
||||
this.modeId = this.values().iterator().next().getId();
|
||||
this.selectedModes.add(modeId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
String andOr = "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (this.size() > 1) {
|
||||
sb.append("Choose one - ");
|
||||
if (this.getMinModes() == 1 && this.getMaxModes() == 3) {
|
||||
sb.append("Choose one or more - ");
|
||||
andOr = "; and/or ";
|
||||
}else if (this.getMinModes() == 1 && this.getMaxModes() == 2) {
|
||||
sb.append("Choose one or both - ");
|
||||
andOr = "; and/or ";
|
||||
} else if (this.getMinModes() == 2 && this.getMaxModes() == 2) {
|
||||
sb.append("Choose two - ");
|
||||
andOr = "; or ";
|
||||
} else {
|
||||
sb.append("Choose one - ");
|
||||
andOr = "; or ";
|
||||
}
|
||||
}
|
||||
for (Mode mode: this.values()) {
|
||||
sb.append(mode.getEffects().getText(mode)).append("; or ");
|
||||
sb.append(mode.getEffects().getText(mode));
|
||||
if (this.size() > 1) {
|
||||
if (sb.length() > 2 && sb.substring(sb.length()-2, sb.length()).equals(". ")) {
|
||||
sb.delete(sb.length()-2, sb.length());
|
||||
}
|
||||
sb.append(andOr);
|
||||
}
|
||||
}
|
||||
if (this.size() > 1) {
|
||||
sb.delete(sb.length() - andOr.length(), sb.length());
|
||||
sb.append(".");
|
||||
}
|
||||
sb.delete(sb.length() - 5, sb.length());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getText(String sourceName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (this.size() > 1) {
|
||||
sb.append("Choose one - ");
|
||||
}
|
||||
for (Mode mode: this.values()) {
|
||||
sb.append(mode.getEffects().getText(mode)).append("; or ");
|
||||
}
|
||||
sb.delete(sb.length() - 5, sb.length());
|
||||
String text = sb.toString();
|
||||
String text = getText();
|
||||
text = text.replace("{this}", sourceName);
|
||||
text = text.replace("{source}", sourceName);
|
||||
return text;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import mage.target.targetpointer.FixedTarget;
|
|||
public class BlocksAttachedTriggeredAbility extends TriggeredAbilityImpl<BlocksAttachedTriggeredAbility>{
|
||||
private boolean setFixedTargetPointer;
|
||||
private String attachedDescription;
|
||||
private boolean setFixedTargetPointerToBlocked;
|
||||
|
||||
public BlocksAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional) {
|
||||
this(effect, attachedDescription, optional, false);
|
||||
|
|
@ -29,6 +30,13 @@ public class BlocksAttachedTriggeredAbility extends TriggeredAbilityImpl<BlocksA
|
|||
this.setFixedTargetPointer = setFixedTargetPointer;
|
||||
this.attachedDescription = attachedDescription;
|
||||
}
|
||||
|
||||
public BlocksAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional, boolean setFixedTargetPointer, boolean setFixedTargetPointerToBlocked) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.setFixedTargetPointer = setFixedTargetPointer;
|
||||
this.attachedDescription = attachedDescription;
|
||||
this.setFixedTargetPointerToBlocked = setFixedTargetPointerToBlocked;
|
||||
}
|
||||
|
||||
public BlocksAttachedTriggeredAbility(final BlocksAttachedTriggeredAbility ability) {
|
||||
super(ability);
|
||||
|
|
@ -51,6 +59,11 @@ public class BlocksAttachedTriggeredAbility extends TriggeredAbilityImpl<BlocksA
|
|||
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||
}
|
||||
}
|
||||
if (setFixedTargetPointerToBlocked) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public class EntersBattlefieldAbility extends StaticAbility<EntersBattlefieldAbi
|
|||
/**
|
||||
*
|
||||
* @param effect effect that happens when the permanent enters the battlefield
|
||||
* @param showRule show a rule for this ability
|
||||
* @param showRule show the rule for this ability
|
||||
*/
|
||||
public EntersBattlefieldAbility(Effect effect, Boolean showRule) {
|
||||
this(effect, null, showRule, null, null);
|
||||
|
|
|
|||
|
|
@ -86,11 +86,12 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
this.setTargetPointer = setTargetPointer;
|
||||
}
|
||||
|
||||
public EntersBattlefieldAllTriggeredAbility(EntersBattlefieldAllTriggeredAbility ability) {
|
||||
public EntersBattlefieldAllTriggeredAbility(final EntersBattlefieldAllTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
this.rule = ability.rule;
|
||||
this.controlled = ability.controlled;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -49,10 +49,15 @@ public class OpponentCastsSpellTriggeredAbility extends TriggeredAbilityImpl<Opp
|
|||
}
|
||||
|
||||
public OpponentCastsSpellTriggeredAbility(Effect effect, FilterCard filter, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this(Zone.BATTLEFIELD, effect, filter, optional);
|
||||
}
|
||||
|
||||
public OpponentCastsSpellTriggeredAbility(Zone zone, Effect effect, FilterCard filter, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
||||
public OpponentCastsSpellTriggeredAbility(final OpponentCastsSpellTriggeredAbility ability) {
|
||||
super(ability);
|
||||
filter = ability.filter;
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ public class SpellCastTriggeredAbility extends TriggeredAbilityImpl<SpellCastTri
|
|||
protected boolean rememberSource = false;
|
||||
|
||||
public SpellCastTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.filter = spellCard;
|
||||
this(Zone.BATTLEFIELD, effect, spellCard, optional, false);
|
||||
}
|
||||
|
||||
public SpellCastTriggeredAbility(Effect effect, FilterSpell filter, boolean optional) {
|
||||
|
|
@ -60,7 +59,11 @@ public class SpellCastTriggeredAbility extends TriggeredAbilityImpl<SpellCastTri
|
|||
}
|
||||
|
||||
public SpellCastTriggeredAbility(Effect effect, FilterSpell filter, boolean optional, boolean rememberSource) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this(Zone.BATTLEFIELD, effect, filter, optional, rememberSource);
|
||||
}
|
||||
|
||||
public SpellCastTriggeredAbility(Zone zone, Effect effect, FilterSpell filter, boolean optional, boolean rememberSource) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
this.rememberSource = rememberSource;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.condition.common;
|
||||
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.ColorPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Plopman
|
||||
*/
|
||||
|
||||
|
||||
public class EnchantedCreatureColorCondition implements Condition {
|
||||
|
||||
private FilterPermanent filter = new FilterCreaturePermanent();
|
||||
|
||||
public EnchantedCreatureColorCondition(ObjectColor color){
|
||||
filter.add(new ColorPredicate(color));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent enchantement = game.getPermanent(source.getSourceId());
|
||||
if (enchantement != null) {
|
||||
Permanent creature = game.getPermanent(enchantement.getAttachedTo());
|
||||
if (creature != null) {
|
||||
if(filter.match(creature, source.getId(), enchantement.getControllerId(), game)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
70
Mage/src/mage/abilities/condition/common/ProwlCondition.java
Normal file
70
Mage/src/mage/abilities/condition/common/ProwlCondition.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.keyword.ProwlAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Checks if a the spell was cast with the alternate prowl costs
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class ProwlCondition implements Condition {
|
||||
|
||||
private static ProwlCondition fInstance = null;
|
||||
|
||||
private ProwlCondition() {}
|
||||
|
||||
public static Condition getInstance() {
|
||||
if (fInstance == null) {
|
||||
fInstance = new ProwlCondition();
|
||||
}
|
||||
return fInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability ability: card.getAbilities()) {
|
||||
if (ability instanceof ProwlAbility) {
|
||||
if(((ProwlAbility) ability).isActivated()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public class SourceTappedCondition implements Condition {
|
||||
|
||||
private static SourceTappedCondition fInstance = new SourceTappedCondition();
|
||||
|
||||
public static Condition getInstance() {
|
||||
return fInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
return permanent.isTapped();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -69,11 +69,11 @@ public class SuspendedCondition implements Condition {
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (game.getState().getZone(card.getId()) == Zone.EXILED &&
|
||||
card.getCounters().getCount(CounterType.TIME) > 0) {
|
||||
return true;
|
||||
if (found) {
|
||||
if (game.getState().getZone(card.getId()) == Zone.EXILED &&
|
||||
card.getCounters().getCount(CounterType.TIME) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public interface OptionalAdditionalModeSourceCosts {
|
||||
void addOptionalAdditionalModeCosts(Ability ability, Game game);
|
||||
String getCastMessageSuffix();
|
||||
}
|
||||
|
|
@ -28,16 +28,16 @@
|
|||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -48,7 +48,7 @@ public class ExileFromGraveCost extends CostImpl<ExileFromGraveCost> {
|
|||
public ExileFromGraveCost(TargetCardInYourGraveyard target) {
|
||||
this.addTarget(target);
|
||||
if (target.getMaxNumberOfTargets() > 1) {
|
||||
this.text = "Exile " + target.getMaxNumberOfTargets() + " " + target.getTargetName();
|
||||
this.text = "Exile " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + " " + target.getTargetName();
|
||||
}
|
||||
else {
|
||||
this.text = "Exile " + target.getTargetName();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package mage.abilities.decorator;
|
||||
|
||||
import mage.constants.Duration;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.FixedCondition;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
|
|
@ -84,6 +84,9 @@ public class ConditionalContinousEffect extends ContinuousEffectImpl<Conditional
|
|||
if (!condition.apply(game, source) && effect.getDuration() == Duration.OneUse) {
|
||||
used = true;
|
||||
}
|
||||
if (!condition.apply(game, source) && effect.getDuration() == Duration.Custom) {
|
||||
this.discard();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class MultikickerCount implements DynamicValue {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DynamicValue copy() {
|
||||
public MultikickerCount copy() {
|
||||
return new MultikickerCount();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,24 +49,25 @@ public class SunburstCount implements DynamicValue{
|
|||
@Override
|
||||
public int calculate(Game game, Ability source) {
|
||||
int count = 0;
|
||||
|
||||
StackObject spell = game.getStack().getFirst();
|
||||
if (spell != null && spell instanceof Spell && ((Spell)spell).getSourceId().equals(source.getSourceId())) {
|
||||
Mana mana = ((Spell)spell).getSpellAbility().getManaCostsToPay().getPayment();
|
||||
if(mana.getBlack() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getBlue() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getGreen() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getRed() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getWhite() > 0) {
|
||||
count++;
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject spell = game.getStack().getFirst();
|
||||
if (spell != null && spell instanceof Spell && ((Spell)spell).getSourceId().equals(source.getSourceId())) {
|
||||
Mana mana = ((Spell)spell).getSpellAbility().getManaCostsToPay().getPayment();
|
||||
if(mana.getBlack() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getBlue() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getGreen() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getRed() > 0) {
|
||||
count++;
|
||||
}
|
||||
if(mana.getWhite() > 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
|
|||
|
|
@ -36,11 +36,22 @@ import mage.constants.Layer;
|
|||
import mage.constants.SubLayer;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.keyword.SpliceOntoArcaneAbility;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -58,6 +69,7 @@ public class ContinuousEffects implements Serializable {
|
|||
private ContinuousEffectsList<RestrictionEffect> restrictionEffects = new ContinuousEffectsList<RestrictionEffect>();
|
||||
private ContinuousEffectsList<AsThoughEffect> asThoughEffects = new ContinuousEffectsList<AsThoughEffect>();
|
||||
private ContinuousEffectsList<CostModificationEffect> costModificationEffects = new ContinuousEffectsList<CostModificationEffect>();
|
||||
private ContinuousEffectsList<SpliceCardEffect> spliceCardEffects = new ContinuousEffectsList<SpliceCardEffect>();
|
||||
|
||||
private List<ContinuousEffectsList<?>> allEffectsLists = new ArrayList<ContinuousEffectsList<?>>();
|
||||
|
||||
|
|
@ -88,6 +100,7 @@ public class ContinuousEffects implements Serializable {
|
|||
restrictionEffects = effect.restrictionEffects.copy();
|
||||
asThoughEffects = effect.asThoughEffects.copy();
|
||||
costModificationEffects = effect.costModificationEffects.copy();
|
||||
spliceCardEffects = effect.spliceCardEffects.copy();
|
||||
for (Map.Entry<UUID, UUID> entry : effect.sources.entrySet()) {
|
||||
sources.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
|
@ -103,6 +116,7 @@ public class ContinuousEffects implements Serializable {
|
|||
allEffectsLists.add(restrictionEffects);
|
||||
allEffectsLists.add(asThoughEffects);
|
||||
allEffectsLists.add(costModificationEffects);
|
||||
allEffectsLists.add(spliceCardEffects);
|
||||
}
|
||||
|
||||
public ContinuousEffects copy() {
|
||||
|
|
@ -125,6 +139,7 @@ public class ContinuousEffects implements Serializable {
|
|||
restrictionEffects.removeEndOfCombatEffects();
|
||||
asThoughEffects.removeEndOfCombatEffects();
|
||||
costModificationEffects.removeEndOfCombatEffects();
|
||||
spliceCardEffects.removeEndOfCombatEffects();
|
||||
}
|
||||
|
||||
public void removeEndOfTurnEffects() {
|
||||
|
|
@ -135,6 +150,7 @@ public class ContinuousEffects implements Serializable {
|
|||
restrictionEffects.removeEndOfTurnEffects();
|
||||
asThoughEffects.removeEndOfTurnEffects();
|
||||
costModificationEffects.removeEndOfTurnEffects();
|
||||
spliceCardEffects.removeEndOfTurnEffects();
|
||||
}
|
||||
|
||||
public void removeInactiveEffects(Game game) {
|
||||
|
|
@ -145,6 +161,7 @@ public class ContinuousEffects implements Serializable {
|
|||
restrictionEffects.removeInactiveEffects(game);
|
||||
asThoughEffects.removeInactiveEffects(game);
|
||||
costModificationEffects.removeInactiveEffects(game);
|
||||
spliceCardEffects.removeInactiveEffects(game);
|
||||
}
|
||||
|
||||
public List<ContinuousEffect> getLayeredEffects(Game game) {
|
||||
|
|
@ -156,7 +173,8 @@ public class ContinuousEffects implements Serializable {
|
|||
case WhileInGraveyard:
|
||||
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
|
||||
for (Ability ability: abilities) {
|
||||
if (ability.isInUseableZone(game, null, false)) {
|
||||
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
|
||||
layerEffects.add(effect);
|
||||
break;
|
||||
}
|
||||
|
|
@ -327,6 +345,29 @@ public class ContinuousEffects implements Serializable {
|
|||
|
||||
return costEffects;
|
||||
}
|
||||
/**
|
||||
* Filters out splice effects that are not active.
|
||||
*
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
private List<SpliceCardEffect> getApplicableSpliceCardEffects(Game game, UUID playerId) {
|
||||
List<SpliceCardEffect> spliceEffects = new ArrayList<SpliceCardEffect>();
|
||||
|
||||
for (SpliceCardEffect effect: spliceCardEffects) {
|
||||
HashSet<Ability> abilities = spliceCardEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (ability.getControllerId().equals(playerId) && (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false))) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
spliceEffects.add(effect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spliceEffects;
|
||||
}
|
||||
|
||||
public boolean asThough(UUID objectId, AsThoughEffectType type, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(game);
|
||||
|
|
@ -389,6 +430,68 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all available splice effects to be applied.
|
||||
*
|
||||
* @param abilityToModify
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
public void applySpliceEffects ( Ability abilityToModify, Game game ) {
|
||||
if ( ((SpellAbility) abilityToModify).getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
|
||||
// on a spliced ability of a spell can't be spliced again
|
||||
return;
|
||||
}
|
||||
List<SpliceCardEffect> spliceEffects = getApplicableSpliceCardEffects(game, abilityToModify.getControllerId());
|
||||
// get the applyable splice abilities
|
||||
List<SpliceOntoArcaneAbility> spliceAbilities = new ArrayList<SpliceOntoArcaneAbility>();
|
||||
for (SpliceCardEffect effect : spliceEffects) {
|
||||
HashSet<Ability> abilities = spliceCardEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (effect.applies(abilityToModify, ability, game) ) {
|
||||
spliceAbilities.add((SpliceOntoArcaneAbility) ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if player wants to use splice
|
||||
|
||||
if (spliceAbilities.size() > 0) {
|
||||
Player controller = game.getPlayer(abilityToModify.getControllerId());
|
||||
if (controller.chooseUse(Outcome.Benefit, "Splice a card?", game)) {
|
||||
Cards cardsToReveal = new CardsImpl();
|
||||
do {
|
||||
FilterCard filter = new FilterCard("a card to splice");
|
||||
ArrayList<Predicate<MageObject>> idPredicates = new ArrayList<Predicate<MageObject>>();
|
||||
for (SpliceOntoArcaneAbility ability : spliceAbilities) {
|
||||
idPredicates.add(new CardIdPredicate((ability.getSourceId())));
|
||||
}
|
||||
filter.add(Predicates.or(idPredicates));
|
||||
TargetCardInHand target = new TargetCardInHand(filter);
|
||||
target.setRequired(true);
|
||||
controller.chooseTarget(Outcome.Benefit, target, abilityToModify, game);
|
||||
UUID cardId = target.getFirstTarget();
|
||||
if (cardId != null) {
|
||||
SpliceOntoArcaneAbility selectedAbility = null;
|
||||
for(SpliceOntoArcaneAbility ability :spliceAbilities) {
|
||||
if (ability.getSourceId().equals(cardId)) {
|
||||
selectedAbility = ability;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selectedAbility != null) {
|
||||
SpliceCardEffect spliceEffect = (SpliceCardEffect) selectedAbility.getEffects().get(0);
|
||||
spliceEffect.apply(game, selectedAbility, abilityToModify);
|
||||
cardsToReveal.add(game.getCard(cardId));
|
||||
spliceAbilities.remove(selectedAbility);
|
||||
}
|
||||
}
|
||||
} while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", game));
|
||||
controller.revealCards("Spliced cards", cardsToReveal, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean replaceEvent(GameEvent event, Game game) {
|
||||
boolean caught = false;
|
||||
HashMap<UUID, HashSet<UUID>> consumed = new HashMap<UUID, HashSet<UUID>>();
|
||||
|
|
@ -616,6 +719,10 @@ public class ContinuousEffects implements Serializable {
|
|||
CostModificationEffect newCostModificationEffect = (CostModificationEffect)effect;
|
||||
costModificationEffects.addEffect(newCostModificationEffect, source);
|
||||
break;
|
||||
case SPLICE:
|
||||
SpliceCardEffect newSpliceCardEffect = (SpliceCardEffect)effect;
|
||||
spliceCardEffects.addEffect(newSpliceCardEffect, source);
|
||||
break;
|
||||
default:
|
||||
ContinuousEffect newEffect = (ContinuousEffect)effect;
|
||||
layeredEffects.addEffect(newEffect, source);
|
||||
|
|
|
|||
66
Mage/src/mage/abilities/effects/SpliceCardEffect.java
Normal file
66
Mage/src/mage/abilities/effects/SpliceCardEffect.java
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Represents a {@link ContinuousEffect} that will modify the cost and abilities of a spell
|
||||
* on the stack (splice a card). {@link mage.abilities.Ability Abilities} with this type of effect will be
|
||||
* called once and only once from the {@link mage.abilities.AbilityImpl#activate(mage.game.Game,
|
||||
* boolean) Ability.activate} method before alternative or additional costs are paid.
|
||||
*
|
||||
* @param <T>
|
||||
* @author levelX2
|
||||
*/
|
||||
|
||||
public interface SpliceCardEffect<T extends SpliceCardEffect<T>> extends ContinuousEffect<T> {
|
||||
/**
|
||||
* Called by the {@link ContinuousEffects#costModification(Ability abilityToModify, Game game) ContinuousEffects.costModification}
|
||||
* method.
|
||||
*
|
||||
* @param game The game for which this effect should be applied.
|
||||
* @param source The source ability of this effect.
|
||||
* @param abilityToModify The {@link mage.abilities.SpellAbility} or {@link Ability} which should be modified.
|
||||
* @return
|
||||
*/
|
||||
boolean apply ( Game game, Ability source, Ability abilityToModify );
|
||||
|
||||
/**
|
||||
* Called by the {@link ContinuousEffects#costModification(mage.abilities.Ability, mage.game.Game) ContinuousEffects.costModification}
|
||||
* method.
|
||||
*
|
||||
* @param abilityToModify The ability to possibly modify.
|
||||
* @param source The source ability of this effect.
|
||||
* @param game The game for which this effect shoul dbe applied.
|
||||
* @return
|
||||
*/
|
||||
boolean applies(Ability abilityToModify, Ability source, Game game);
|
||||
}
|
||||
67
Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java
Normal file
67
Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.EffectType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
||||
|
||||
/**
|
||||
* Simple implementation of a {@link SpliceCardEffect} offering simplified
|
||||
* construction to setup the object for use by the mage framework.
|
||||
|
||||
* @author LevelX2
|
||||
*/
|
||||
public abstract class SpliceCardEffectImpl<T extends SpliceCardEffectImpl<T>> extends ContinuousEffectImpl<T> implements SpliceCardEffect<T> {
|
||||
|
||||
public SpliceCardEffectImpl ( Duration duration, Outcome outcome ) {
|
||||
super(duration, outcome);
|
||||
this.effectType = EffectType.SPLICE;
|
||||
}
|
||||
|
||||
public SpliceCardEffectImpl(final SpliceCardEffectImpl<T> effect) {
|
||||
super(effect);
|
||||
this.effectType = effect.effectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden and 'no-op' implementation put in place.
|
||||
*
|
||||
* @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability)
|
||||
*
|
||||
* @param game
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public final boolean apply ( Game game, Ability source ) { return false; }
|
||||
}
|
||||
|
|
@ -1,33 +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.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
|
|
@ -55,12 +55,20 @@ public class CounterTargetEffect extends OneShotEffect<CounterTargetEffect> {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game);
|
||||
boolean countered = false;
|
||||
for (UUID targetId : source.getTargets().get(0).getTargets()) {
|
||||
if (game.getStack().counter(targetId, source.getSourceId(), game)) {
|
||||
countered = true;
|
||||
}
|
||||
}
|
||||
return countered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "Counter target " + mode.getTargets().get(0).getTargetName();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public class DamagePlayersEffect extends OneShotEffect<DamagePlayersEffect> {
|
|||
for (UUID playerId: game.getPlayer(source.getControllerId()).getInRange()) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.damage(amount.calculate(game, source), source.getId(), game, false, true);
|
||||
player.damage(amount.calculate(game, source), source.getSourceId(), game, false, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -86,7 +86,7 @@ public class DamagePlayersEffect extends OneShotEffect<DamagePlayersEffect> {
|
|||
for (UUID playerId: game.getOpponents(source.getControllerId())) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.damage(amount.calculate(game, source), source.getId(), game, false, true);
|
||||
player.damage(amount.calculate(game, source), source.getSourceId(), game, false, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -28,15 +28,14 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.Target;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.target.targetpointer.FirstTargetPointer;
|
||||
|
||||
/**
|
||||
|
|
@ -105,12 +104,14 @@ public class DestroyTargetEffect extends OneShotEffect<DestroyTargetEffect> {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
if (mode.getTargets().size() == 0) {
|
||||
sb.append("destroy that creature"); //TODO add possibility to specify text with targetPointer usage
|
||||
} else if (mode.getTargets().get(0).getNumberOfTargets() == 1)
|
||||
} else if (mode.getTargets().get(0).getNumberOfTargets() == 1) {
|
||||
sb.append("Destroy target ").append(mode.getTargets().get(0).getTargetName());
|
||||
else
|
||||
} else {
|
||||
sb.append("Destroy ").append(mode.getTargets().get(0).getNumberOfTargets()).append(" target ").append(mode.getTargets().get(0).getTargetName());
|
||||
if (noRegen)
|
||||
}
|
||||
if (noRegen) {
|
||||
sb.append(". It can't be regenerated");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public class DiscardHandControllerEffect extends OneShotEffect<DiscardHandControllerEffect> {
|
||||
|
||||
public DiscardHandControllerEffect() {
|
||||
super(Outcome.Discard);
|
||||
this.staticText = "Discard your hand";
|
||||
}
|
||||
|
||||
public DiscardHandControllerEffect(final DiscardHandControllerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscardHandControllerEffect copy() {
|
||||
return new DiscardHandControllerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
for (Card card : player.getHand().getCards(game)) {
|
||||
player.discard(card, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,11 +28,12 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -40,13 +41,29 @@ import mage.players.Player;
|
|||
*/
|
||||
public class DrawDiscardControllerEffect extends OneShotEffect<DrawDiscardControllerEffect> {
|
||||
|
||||
private int cardsToDraw;
|
||||
private int cardsToDiscard;
|
||||
|
||||
public DrawDiscardControllerEffect() {
|
||||
this(1,1);
|
||||
}
|
||||
|
||||
public DrawDiscardControllerEffect(int cardsToDraw, int cardsToDiscard) {
|
||||
super(Outcome.DrawCard);
|
||||
staticText = "Draw a card, then discard a card";
|
||||
this.cardsToDraw = cardsToDraw;
|
||||
this.cardsToDiscard = cardsToDiscard;
|
||||
staticText = new StringBuilder("Draw ")
|
||||
.append(cardsToDraw == 1?"a": CardUtil.numberToText(cardsToDraw))
|
||||
.append(" card").append(cardsToDraw == 1?" ": "s")
|
||||
.append(", then discard ")
|
||||
.append(cardsToDiscard == 1?"a": CardUtil.numberToText(cardsToDiscard))
|
||||
.append(" card").append(cardsToDiscard == 1?" ": "s").toString();
|
||||
}
|
||||
|
||||
public DrawDiscardControllerEffect(final DrawDiscardControllerEffect effect) {
|
||||
super(effect);
|
||||
this.cardsToDraw = effect.cardsToDraw;
|
||||
this.cardsToDiscard = effect.cardsToDiscard;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -58,8 +75,8 @@ public class DrawDiscardControllerEffect extends OneShotEffect<DrawDiscardContro
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
player.drawCards(1, game);
|
||||
player.discard(1, source, game);
|
||||
player.drawCards(cardsToDraw, game);
|
||||
player.discard(cardsToDiscard, source, game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2011 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;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DrawDiscardTargetEffect extends OneShotEffect<DrawDiscardTargetEffect> {
|
||||
|
||||
private int cardsToDraw;
|
||||
private int cardsToDiscard;
|
||||
|
||||
public DrawDiscardTargetEffect() {
|
||||
this(1,1);
|
||||
}
|
||||
|
||||
public DrawDiscardTargetEffect(int cardsToDraw, int cardsToDiscard) {
|
||||
super(Outcome.DrawCard);
|
||||
this.cardsToDraw = cardsToDraw;
|
||||
this.cardsToDiscard = cardsToDiscard;
|
||||
staticText = new StringBuilder("Target player draws ")
|
||||
.append(cardsToDraw == 1?"a": CardUtil.numberToText(cardsToDraw))
|
||||
.append(" card").append(cardsToDraw == 1?" ": "s")
|
||||
.append(", then discard ")
|
||||
.append(cardsToDiscard == 1?"a": CardUtil.numberToText(cardsToDiscard))
|
||||
.append(" card").append(cardsToDiscard == 1?" ": "s").toString();
|
||||
}
|
||||
|
||||
public DrawDiscardTargetEffect(final DrawDiscardTargetEffect effect) {
|
||||
super(effect);
|
||||
this.cardsToDraw = effect.cardsToDraw;
|
||||
this.cardsToDiscard = effect.cardsToDiscard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawDiscardTargetEffect copy() {
|
||||
return new DrawDiscardTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player != null) {
|
||||
player.drawCards(cardsToDraw, game);
|
||||
player.discard(cardsToDiscard, source, game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -86,8 +86,9 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect<PutLibraryInt
|
|||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public class SacrificeTargetEffect extends OneShotEffect<SacrificeTargetEffect>
|
|||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if ("".equals(staticText) && !mode.getTargets().isEmpty()) {
|
||||
if (staticText.isEmpty() && !mode.getTargets().isEmpty()) {
|
||||
if (mode.getTargets().get(0).getNumberOfTargets() == 1) {
|
||||
return "The controller of target " + mode.getTargets().get(0).getTargetName() + " sacrifices it";
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -28,12 +28,13 @@
|
|||
|
||||
package mage.abilities.effects.common.continious;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
|
|
@ -44,6 +45,8 @@ import mage.game.permanent.Permanent;
|
|||
public class GainAbilitySourceEffect extends ContinuousEffectImpl<GainAbilitySourceEffect> {
|
||||
|
||||
protected Ability ability;
|
||||
// shall a card gain the ability (otherwise permanent)
|
||||
private boolean onCard;
|
||||
|
||||
/**
|
||||
* Add ability with Duration.WhileOnBattlefield
|
||||
|
|
@ -54,14 +57,20 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl<GainAbilitySou
|
|||
}
|
||||
|
||||
public GainAbilitySourceEffect(Ability ability, Duration duration) {
|
||||
this(ability, duration, false);
|
||||
}
|
||||
|
||||
public GainAbilitySourceEffect(Ability ability, Duration duration, boolean onCard) {
|
||||
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
||||
this.ability = ability;
|
||||
staticText = "{this} gains \"" + ability.getRule() + "\" " + duration.toString();
|
||||
this.onCard = onCard;
|
||||
}
|
||||
|
||||
public GainAbilitySourceEffect(final GainAbilitySourceEffect effect) {
|
||||
super(effect);
|
||||
this.ability = effect.ability.copy();
|
||||
this.onCard = effect.onCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,10 +80,20 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl<GainAbilitySou
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, game);
|
||||
return true;
|
||||
if (onCard) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
// add ability to card only once
|
||||
card.addAbility(ability);
|
||||
discard();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.target.Target;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.cards.Card;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -48,23 +49,29 @@ import java.util.UUID;
|
|||
public class GainAbilityTargetEffect extends ContinuousEffectImpl<GainAbilityTargetEffect> {
|
||||
|
||||
protected Ability ability;
|
||||
// shall a card gain the ability (otherwise permanent)
|
||||
private boolean onCard;
|
||||
|
||||
public GainAbilityTargetEffect(Ability ability, Duration duration) {
|
||||
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA,
|
||||
ability.getEffects().size() > 0 ? ability.getEffects().get(0).getOutcome() : Outcome.AddAbility);
|
||||
this.ability = ability;
|
||||
this(ability, duration, null);
|
||||
}
|
||||
|
||||
public GainAbilityTargetEffect(Ability ability, Duration duration, String rule) {
|
||||
this(ability, duration, rule, false);
|
||||
}
|
||||
|
||||
public GainAbilityTargetEffect(Ability ability, Duration duration, String rule, boolean onCard) {
|
||||
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA,
|
||||
ability.getEffects().size() > 0 ? ability.getEffects().get(0).getOutcome() : Outcome.AddAbility);
|
||||
this.ability = ability;
|
||||
staticText = rule;
|
||||
this.onCard = onCard;
|
||||
}
|
||||
|
||||
public GainAbilityTargetEffect(final GainAbilityTargetEffect effect) {
|
||||
super(effect);
|
||||
this.ability = effect.ability.copy();
|
||||
this.onCard = effect.onCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -81,11 +88,24 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl<GainAbilityTar
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int affectedTargets = 0;
|
||||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
affectedTargets++;
|
||||
if (onCard) {
|
||||
for (UUID cardId : targetPointer.getTargets(game, source)) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
card.addAbility(ability);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
if (duration.equals(Duration.OneUse)) {
|
||||
discard();
|
||||
}
|
||||
} else {
|
||||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return affectedTargets > 0;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.counters.Counter;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -44,8 +45,9 @@ import mage.players.Player;
|
|||
public class AddCountersSourceEffect extends OneShotEffect<AddCountersSourceEffect> {
|
||||
|
||||
private Counter counter;
|
||||
protected boolean informPlayers;
|
||||
protected DynamicValue amount;
|
||||
private boolean informPlayers;
|
||||
private DynamicValue amount;
|
||||
private boolean putOnCard;
|
||||
|
||||
public AddCountersSourceEffect(Counter counter) {
|
||||
this(counter, false);
|
||||
|
|
@ -55,17 +57,23 @@ public class AddCountersSourceEffect extends OneShotEffect<AddCountersSourceEffe
|
|||
this(counter, new StaticValue(0), informPlayers);
|
||||
}
|
||||
|
||||
public AddCountersSourceEffect(Counter counter, DynamicValue amount, boolean informPlayers) {
|
||||
this(counter, amount, informPlayers, false);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param counter
|
||||
* @param amount this amount will be added to the counter instances
|
||||
* @param informPlayers
|
||||
* @param informPlayers
|
||||
* @param putOnCard - counters have to be put on a card instead of a permanent
|
||||
*/
|
||||
public AddCountersSourceEffect(Counter counter, DynamicValue amount, boolean informPlayers) {
|
||||
public AddCountersSourceEffect(Counter counter, DynamicValue amount, boolean informPlayers, boolean putOnCard) {
|
||||
super(Outcome.Benefit);
|
||||
this.counter = counter.copy();
|
||||
this.informPlayers = informPlayers;
|
||||
this.amount = amount;
|
||||
this.putOnCard = putOnCard;
|
||||
setText();
|
||||
}
|
||||
|
||||
|
|
@ -76,25 +84,45 @@ public class AddCountersSourceEffect extends OneShotEffect<AddCountersSourceEffe
|
|||
}
|
||||
this.informPlayers = effect.informPlayers;
|
||||
this.amount = effect.amount;
|
||||
this.putOnCard = effect.putOnCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
if (counter != null) {
|
||||
Counter newCounter = counter.copy();
|
||||
newCounter.add(amount.calculate(game, source));
|
||||
permanent.addCounters(newCounter, game);
|
||||
if (informPlayers) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(permanent.getName()).toString());
|
||||
if (putOnCard) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
if (counter != null) {
|
||||
Counter newCounter = counter.copy();
|
||||
newCounter.add(amount.calculate(game, source));
|
||||
card.addCounters(newCounter, game);
|
||||
if (informPlayers) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(card.getName()).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
if (counter != null) {
|
||||
Counter newCounter = counter.copy();
|
||||
newCounter.add(amount.calculate(game, source));
|
||||
permanent.addCounters(newCounter, game);
|
||||
if (informPlayers) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(permanent.getName()).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setText() {
|
||||
|
|
|
|||
111
Mage/src/mage/abilities/keyword/DelveAbility.java
Normal file
111
Mage/src/mage/abilities/keyword/DelveAbility.java
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.AdjustingSourceCosts;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 702.64. Delve
|
||||
*
|
||||
* 702.64a Delve is a static ability that functions while the spell that has delve is on
|
||||
* the stack. "Delve" means "As an additional cost to cast this spell, you may exile any
|
||||
* number of cards from your graveyard. Each card exiled this way reduces the cost to cast
|
||||
* this spell by {1}." Using the delve ability follows the rules for paying additional
|
||||
* costs in rules 601.2b and 601.2e-g. #
|
||||
*
|
||||
* 702.64b Multiple instances of delve on the same spell are redundant.
|
||||
*
|
||||
* @author LevelX2
|
||||
*
|
||||
*/
|
||||
|
||||
public class DelveAbility extends SimpleStaticAbility implements AdjustingSourceCosts {
|
||||
|
||||
public DelveAbility() {
|
||||
super(Zone.STACK, null);
|
||||
this.setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
public DelveAbility(final DelveAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DelveAbility copy() {
|
||||
return new DelveAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustCosts(Ability ability, Game game) {
|
||||
Player player = game.getPlayer(controllerId);
|
||||
if (player == null || !(ability instanceof SpellAbility)) {
|
||||
return;
|
||||
}
|
||||
Target target = new TargetCardInYourGraveyard(1, Integer.MAX_VALUE, new FilterCard());
|
||||
target.setTargetName("cards to delve from your graveyard");
|
||||
if (!target.canChoose(sourceId, controllerId, game)) {
|
||||
return;
|
||||
}
|
||||
if (player.chooseUse(Outcome.Detriment, "Delve cards from your graveyard?", game)) {
|
||||
player.chooseTarget(Outcome.Detriment, target, ability, game);
|
||||
if (target.getTargets().size() > 0) {
|
||||
int adjCost = 0;
|
||||
for (UUID cardId: target.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card == null) {
|
||||
continue;
|
||||
}
|
||||
card.moveToExile(null, null, this.getSourceId(), game);
|
||||
++adjCost;
|
||||
}
|
||||
game.informPlayers(new StringBuilder(player.getName()).append(" delved ")
|
||||
.append(adjCost).append(" creature").append(adjCost != 1?"s":"").append(" from his or her graveyard").toString());
|
||||
CardUtil.adjustCost((SpellAbility)ability, adjCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Delve <i>(You may exile any number of cards from your graveyard as you cast this spell. It costs {1} less to cast for each card exiled this way.)</i>";
|
||||
}
|
||||
}
|
||||
|
|
@ -29,14 +29,15 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.EntersTheBattlefieldEvent;
|
||||
|
|
@ -52,18 +53,27 @@ public class EchoAbility extends TriggeredAbilityImpl<EchoAbility> {
|
|||
|
||||
protected UUID lastController;
|
||||
protected boolean echoPaid;
|
||||
protected String manaString;
|
||||
protected Costs echoCosts = new CostsImpl();
|
||||
private boolean manaEcho = true;
|
||||
|
||||
public EchoAbility(String manaString) {
|
||||
super(Zone.BATTLEFIELD, new EchoEffect(new ManaCostsImpl(manaString)), false);
|
||||
this.echoPaid = false;
|
||||
this.manaString = manaString;
|
||||
this.echoCosts.add(new ManaCostsImpl(manaString));
|
||||
}
|
||||
|
||||
public EchoAbility(Cost echoCost) {
|
||||
super(Zone.BATTLEFIELD, new EchoEffect(echoCost), false);
|
||||
this.echoPaid = false;
|
||||
this.echoCosts.add(echoCost);
|
||||
this.manaEcho = false;
|
||||
}
|
||||
|
||||
public EchoAbility(final EchoAbility ability) {
|
||||
super(ability);
|
||||
this.echoPaid = ability.echoPaid;
|
||||
this.manaString = ability.manaString;
|
||||
this.echoCosts = ability.echoCosts.copy();
|
||||
this.manaEcho = ability.manaEcho;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -98,7 +108,15 @@ public class EchoAbility extends TriggeredAbilityImpl<EchoAbility> {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Echo " + manaString + " <i>(At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep," + getEffects().getText(modes.getMode()) + ")</i>";
|
||||
StringBuilder sb = new StringBuilder("Echo");
|
||||
if (manaEcho) {
|
||||
sb.append(" ");
|
||||
} else {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(echoCosts.getText());
|
||||
sb.append(" <i>(At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)</i>");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +155,7 @@ class EchoEffect extends OneShotEffect<EchoEffect> {
|
|||
return new EchoEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
StringBuilder sb = new StringBuilder("sacrifice {this} unless you ");
|
||||
String costText = cost.getText();
|
||||
|
|
@ -145,8 +163,9 @@ class EchoEffect extends OneShotEffect<EchoEffect> {
|
|||
sb.append(costText.substring(0, 1).toLowerCase());
|
||||
sb.append(costText.substring(1));
|
||||
}
|
||||
else
|
||||
else {
|
||||
sb.append("pay ").append(costText);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
|
|
|
|||
160
Mage/src/mage/abilities/keyword/EntwineAbility.java
Normal file
160
Mage/src/mage/abilities/keyword/EntwineAbility.java
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.constants.Outcome;
|
||||
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.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<EntwineAbility> implements OptionalAdditionalModeSourceCosts {
|
||||
|
||||
private static final String keywordText = "Entwine";
|
||||
private static final String reminderText = "<i> (Choose both if you pay the entwine cost.)</i>";
|
||||
protected OptionalAdditionalCost additionalCost;
|
||||
|
||||
public EntwineAbility(String 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);
|
||||
}
|
||||
|
||||
public EntwineAbility(final EntwineAbility ability) {
|
||||
super(ability);
|
||||
additionalCost = ability.additionalCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntwineAbility copy() {
|
||||
return new EntwineAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCost(Cost cost) {
|
||||
if (additionalCost != null) {
|
||||
((Costs) additionalCost).add(cost);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
if (additionalCost != null) {
|
||||
return additionalCost.isActivated();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void resetCosts() {
|
||||
if (additionalCost != null) {
|
||||
additionalCost.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOptionalAdditionalModeCosts(Ability ability, Game game) {
|
||||
if (ability instanceof SpellAbility) {
|
||||
Player player = game.getPlayer(controllerId);
|
||||
if (player != null) {
|
||||
this.resetCosts();
|
||||
if (additionalCost != null) {
|
||||
if (player.chooseUse(Outcome.Benefit,new StringBuilder("Pay ").append(additionalCost.getText(false)).append(" ?").toString(), game)) {
|
||||
additionalCost.activate();
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
ability.getModes().setMinModes(2);
|
||||
ability.getModes().setMaxModes(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (additionalCost != null) {
|
||||
sb.append(additionalCost.getText(false));
|
||||
sb.append(" ").append(additionalCost.getReminderText());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCastMessageSuffix() {
|
||||
if (additionalCost != null) {
|
||||
return additionalCost.getCastSuffixMessage(0);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public String getReminderText() {
|
||||
if (additionalCost != null) {
|
||||
return additionalCost.getReminderText();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ public class InfectAbility extends StaticAbility<InfectAbility> implements MageS
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Infect";
|
||||
return "Infect <i>(This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.)</i>";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ package mage.abilities.keyword;
|
|||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
|
|
@ -41,8 +39,11 @@ import mage.abilities.costs.Costs;
|
|||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
|
|
@ -88,6 +89,7 @@ public class KickerAbility extends StaticAbility<KickerAbility> implements Optio
|
|||
protected String keywordText;
|
||||
protected String reminderText;
|
||||
protected List<OptionalAdditionalCost> kickerCosts = new LinkedList<OptionalAdditionalCost>();
|
||||
private int xManaValue = 0;
|
||||
|
||||
public KickerAbility(String manaString) {
|
||||
this(KICKER_KEYWORD, KICKER_REMINDER_MANA);
|
||||
|
|
@ -112,6 +114,8 @@ public class KickerAbility extends StaticAbility<KickerAbility> implements Optio
|
|||
this.kickerCosts = ability.kickerCosts;
|
||||
this.keywordText = ability.keywordText;
|
||||
this.reminderText = ability.reminderText;
|
||||
this.xManaValue = ability.xManaValue;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -137,6 +141,10 @@ public class KickerAbility extends StaticAbility<KickerAbility> implements Optio
|
|||
}
|
||||
}
|
||||
|
||||
public int getXManaValue() {
|
||||
return xManaValue;
|
||||
}
|
||||
|
||||
public int getKickedCounter() {
|
||||
int counter = 0;
|
||||
for (OptionalAdditionalCost cost: kickerCosts) {
|
||||
|
|
@ -178,7 +186,16 @@ public class KickerAbility extends StaticAbility<KickerAbility> implements Optio
|
|||
for (Iterator it = ((Costs) kickerCost).iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
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)
|
||||
game.informPlayers(new StringBuilder(game.getPlayer(this.controllerId).getName()).append(" announced a value of ").append(xManaValue).append(" for ").append(" kicker X ").toString());
|
||||
ability.getManaCostsToPay().add(new GenericManaCost(xManaValue));
|
||||
} else {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
}
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.StaticAbility;
|
||||
|
|
@ -11,6 +8,9 @@ import mage.abilities.effects.EntersBattlefieldEffect;
|
|||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterArtifactPermanent;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
|
|
@ -42,17 +42,30 @@ public class ModularAbility extends DiesTriggeredAbility {
|
|||
filter.add(new CardTypePredicate(CardType.CREATURE));
|
||||
}
|
||||
private int amount;
|
||||
private boolean sunburst;
|
||||
|
||||
public ModularAbility(Card card, int amount) {
|
||||
this(card, amount, false);
|
||||
}
|
||||
|
||||
public ModularAbility(Card card, int amount, boolean sunburst) {
|
||||
super(new ModularDistributeCounterEffect(), true);
|
||||
this.addTarget(new TargetArtifactPermanent(filter));
|
||||
this.amount = amount;
|
||||
card.addAbility(new ModularStaticAbility(amount));
|
||||
this.sunburst = sunburst;
|
||||
if (sunburst) {
|
||||
Ability ability = new SunburstAbility(card);
|
||||
ability.setRuleVisible(false);
|
||||
card.addAbility(ability);
|
||||
} else {
|
||||
card.addAbility(new ModularStaticAbility(amount));
|
||||
}
|
||||
}
|
||||
|
||||
public ModularAbility(ModularAbility ability) {
|
||||
super(ability);
|
||||
this.amount = ability.amount;
|
||||
this.sunburst = ability.sunburst;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,19 +84,31 @@ public class ModularAbility extends DiesTriggeredAbility {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Modular " + amount + " <i>(This enters the battlefield with " + amount + " +1/+1 counter on it. When it dies, you may put its +1/+1 counters on target artifact creature.)</i>";
|
||||
StringBuilder sb = new StringBuilder("Modular");
|
||||
if (sunburst) {
|
||||
sb.append("-Sunburst <i>(This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. When it dies, you may put its +1/+1 counters on target artifact creature.)</i>");
|
||||
} else {
|
||||
sb.append(" ").append(amount).append(" <i>(This enters the battlefield with ")
|
||||
.append(amount).append(" +1/+1 counter on it. When it dies, you may put its +1/+1 counters on target artifact creature.)</i>");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ModularStaticAbility extends StaticAbility<ModularStaticAbility> {
|
||||
|
||||
private String ruleText;
|
||||
|
||||
public ModularStaticAbility(int amount) {
|
||||
super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount))));
|
||||
ruleText = new StringBuilder("This enters the battlefield with ").append(amount).append(" +1/+1 counter on it.").toString();
|
||||
this.setRuleVisible(false);
|
||||
}
|
||||
|
||||
public ModularStaticAbility(final ModularStaticAbility ability) {
|
||||
super(ability);
|
||||
this.ruleText = ability.ruleText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -93,7 +118,7 @@ class ModularStaticAbility extends StaticAbility<ModularStaticAbility> {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "";
|
||||
return ruleText;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
199
Mage/src/mage/abilities/keyword/ProwlAbility.java
Normal file
199
Mage/src/mage/abilities/keyword/ProwlAbility.java
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.costs.AlternativeCost2;
|
||||
import mage.abilities.costs.AlternativeCost2Impl;
|
||||
import mage.abilities.costs.AlternativeSourceCosts;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
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
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class ProwlAbility extends StaticAbility<ProwlAbility> implements AlternativeSourceCosts {
|
||||
|
||||
private static final String PROWL_KEYWORD = "Prowl";
|
||||
private List<AlternativeCost2> prowlCosts = new LinkedList<AlternativeCost2>();
|
||||
private String reminderText;
|
||||
|
||||
public ProwlAbility(Card card, String manaString) {
|
||||
super(Zone.STACK, null);
|
||||
setRuleAtTheTop(true);
|
||||
name = PROWL_KEYWORD;
|
||||
setReminderText(card);
|
||||
this.addProwlCost(manaString);
|
||||
card.addWatcher(new ProwlWatcher());
|
||||
|
||||
}
|
||||
|
||||
public ProwlAbility(final ProwlAbility ability) {
|
||||
super(ability);
|
||||
this.prowlCosts.addAll(ability.prowlCosts);
|
||||
this.reminderText = ability.reminderText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProwlAbility copy() {
|
||||
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;
|
||||
}
|
||||
|
||||
public void resetProwl() {
|
||||
for (AlternativeCost2 cost: prowlCosts) {
|
||||
cost.reset();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean isActivated() {
|
||||
for (AlternativeCost2 cost: prowlCosts) {
|
||||
if(cost.isActivated()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
|
||||
if (ability instanceof SpellAbility) {
|
||||
Player player = game.getPlayer(controllerId);
|
||||
ProwlWatcher prowlWatcher = (ProwlWatcher) game.getState().getWatchers().get("Prowl");
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
if (player == null || prowlWatcher == null || card == null) {
|
||||
throw new IllegalArgumentException("Params can't be null");
|
||||
}
|
||||
boolean canProwl = false;
|
||||
if (prowlWatcher.getDamagingSubtypes(ability.getControllerId()) != null) {
|
||||
for (String subtype : prowlWatcher.getDamagingSubtypes(ability.getControllerId())) {
|
||||
if (card.getSubtype().contains(subtype)) {
|
||||
canProwl = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canProwl) {
|
||||
this.resetProwl();
|
||||
for (AlternativeCost2 prowlCost: prowlCosts) {
|
||||
if (prowlCost.canPay(sourceId, controllerId, game) &&
|
||||
player.chooseUse(Outcome.Benefit, new StringBuilder("Cast for ").append(PROWL_KEYWORD).append(" cost ").append(prowlCost.getText(true)).append(" ?").toString(), game)) {
|
||||
prowlCost.activate();
|
||||
ability.getManaCostsToPay().clear();
|
||||
ability.getCosts().clear();
|
||||
for (Iterator it = ((Costs) prowlCost).iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return isActivated();
|
||||
}
|
||||
|
||||
@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) {
|
||||
sb.append(" ").append(remarkText);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCastMessageSuffix() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int position = 0;
|
||||
for (AlternativeCost2 cost : prowlCosts) {
|
||||
if (cost.isActivated()) {
|
||||
sb.append(cost.getCastSuffixMessage(position));
|
||||
++position;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
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()) {
|
||||
i++;
|
||||
sb.append(subtype);
|
||||
if (card.getSupertype().size() > 1 && i < card.getSupertype().size()) {
|
||||
sb.append(" or ");
|
||||
}
|
||||
}
|
||||
//private static final String REMINDER_TEXT = "{subtypes}.)";
|
||||
|
||||
reminderText = sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -67,9 +67,7 @@ public class RetraceAbility extends ActivatedAbilityImpl<RetraceAbility> {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
StringBuilder sbRule = new StringBuilder("Retrace");
|
||||
|
||||
return sbRule.toString();
|
||||
return "Retrace <i>(You may cast this card from your graveyard by discarding a land card in addition to paying its other costs.)</i>";
|
||||
}
|
||||
}
|
||||
class RetraceEffect extends OneShotEffect<RetraceEffect> {
|
||||
|
|
|
|||
208
Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java
Normal file
208
Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.SpliceCardEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
|
||||
|
||||
/**
|
||||
* 702.45. Splice
|
||||
*
|
||||
* 702.45a Splice is a static ability that functions while a card is in your hand.
|
||||
* "Splice onto [subtype] [cost]" means "You may reveal this card from your hand
|
||||
* as you cast a [subtype] spell. If you do, copy this card's text box onto that
|
||||
* spell and pay [cost] as an additional cost to cast that spell." Paying a card's
|
||||
* splice cost follows the rules for paying additional costs in rules 601.2b and
|
||||
* 601.2e-g.
|
||||
*
|
||||
* Example: Since the card with splice remains in the player's hand, it can later
|
||||
* be cast normally or spliced onto another spell. It can even be discarded to pay
|
||||
* a "discard a card" cost of the spell it's spliced onto.
|
||||
*
|
||||
* 702.45b You can't choose to use a splice ability if you can't make the required
|
||||
* choices (targets, etc.) for that card's instructions. You can't splice any one
|
||||
* card onto the same spell more than once. If you're splicing more than one card
|
||||
* onto a spell, reveal them all at once and choose the order in which their
|
||||
* instructions will be followed. The instructions on the main spell have to be
|
||||
* followed first.
|
||||
*
|
||||
* 702.45c The spell has the characteristics of the main spell, plus the text boxes
|
||||
* of each of the spliced cards. The spell doesn't gain any other characteristics
|
||||
* (name, mana cost, color, supertypes, card types, subtypes, etc.) of the spliced
|
||||
* cards. Text copied onto the spell that refers to a card by name refers to the spell
|
||||
* on the stack, not the card from which the text was copied.
|
||||
*
|
||||
* Example: Glacial Ray is a red card with splice onto Arcane that reads, "Glacial
|
||||
* Ray deals 2 damage to target creature or player." Suppose Glacial Ray is spliced
|
||||
* onto Reach Through Mists, a blue spell. The spell is still blue, and Reach Through
|
||||
* Mists deals the damage. This means that the ability can target a creature with
|
||||
* protection from red and deal 2 damage to that creature.
|
||||
*
|
||||
* 702.45d Choose targets for the added text normally (see rule 601.2c). Note that a
|
||||
* spell with one or more targets will be countered if all of its targets are illegal
|
||||
* on resolution.
|
||||
*
|
||||
* 702.45e The spell loses any splice changes once it leaves the stack (for example,
|
||||
* when it's countered, it's exiled, or it resolves).
|
||||
*
|
||||
* Rulings
|
||||
*
|
||||
* You must reveal all of the cards you intend to splice at the same time. Each individual card can only be spliced once onto a spell.
|
||||
* If you have more than one card with the same name in your hand, you may splice both of them onto the spell.
|
||||
* A card with a splice ability can't be spliced onto itself because the spell is on the stack (and not in your hand) when you reveal the cards you want to splice onto it.
|
||||
* The target for a card that's spliced onto a spell may be the same as the target chosen for the original spell or for another spliced-on card. (A recent change to the targeting rules allows this, but most other cards are unaffected by the change.)
|
||||
* If you splice a targeted card onto an untargeted spell, the entire spell will be countered if the target isn't legal when the spell resolves.
|
||||
* If you splice an untargeted card onto a targeted spell, the entire spell will be countered if the target isn't legal when the spell resolves.
|
||||
* A spell is countered on resolution only if *all* of its targets are illegal (or the spell is countered by an effect).
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public class SpliceOntoArcaneAbility extends SimpleStaticAbility {
|
||||
|
||||
private static final String KEYWORD_TEXT = "Splice onto Arcane";
|
||||
private Costs spliceCosts = new CostsImpl();
|
||||
private boolean nonManaCosts = false;
|
||||
|
||||
public SpliceOntoArcaneAbility(String manaString) {
|
||||
super(Zone.HAND, new SpliceOntoArcaneEffect());
|
||||
spliceCosts.add(new ManaCostsImpl(manaString));
|
||||
}
|
||||
|
||||
public SpliceOntoArcaneAbility(Cost cost) {
|
||||
super(Zone.HAND, new SpliceOntoArcaneEffect());
|
||||
spliceCosts.add(cost);
|
||||
nonManaCosts = true;
|
||||
}
|
||||
|
||||
public SpliceOntoArcaneAbility(final SpliceOntoArcaneAbility ability) {
|
||||
super(ability);
|
||||
this.spliceCosts = ability.spliceCosts.copy();
|
||||
this.nonManaCosts = ability.nonManaCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleStaticAbility copy() {
|
||||
return new SpliceOntoArcaneAbility(this);
|
||||
}
|
||||
|
||||
public Costs getSpliceCosts() {
|
||||
return spliceCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(KEYWORD_TEXT).append(nonManaCosts?"-":" ");
|
||||
sb.append(spliceCosts.getText()).append(nonManaCosts?". ":" ");
|
||||
sb.append("<i>(As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.)</i>");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SpliceOntoArcaneEffect extends SpliceCardEffectImpl<SpliceOntoArcaneEffect> {
|
||||
|
||||
public SpliceOntoArcaneEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Copy);
|
||||
staticText = "Splice onto Arcane";
|
||||
}
|
||||
|
||||
public SpliceOntoArcaneEffect(final SpliceOntoArcaneEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public SpliceOntoArcaneEffect copy() {
|
||||
return new SpliceOntoArcaneEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Card spliceCard = game.getCard(source.getSourceId());
|
||||
if (spliceCard != null && controller != null) {
|
||||
Spell spell = game.getStack().getSpell(abilityToModify.getId());
|
||||
if (spell != null) {
|
||||
SpellAbility splicedAbility = spliceCard.getSpellAbility().copy();
|
||||
splicedAbility.setSpellAbilityType(SpellAbilityType.SPLICE);
|
||||
splicedAbility.setSourceId(abilityToModify.getSourceId());
|
||||
spell.addSpellAbility(splicedAbility);
|
||||
for (Iterator it = ((SpliceOntoArcaneAbility) source).getSpliceCosts().iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
spell.getSpellAbility().getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
spell.getSpellAbility().getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
MageObject object = game.getObject(abilityToModify.getSourceId());
|
||||
if (object != null && object.getSubtype().contains("Arcane")) {
|
||||
return spliceSpellCanBeActivated(source, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean spliceSpellCanBeActivated(Ability source, Game game) {
|
||||
// check if spell can be activated (protection problem not solved because effect will be used from the base spell?)
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
return card.getSpellAbility().canActivate(source.getControllerId(), game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import mage.abilities.common.EntersBattlefieldAbility;
|
|||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.SunburstCount;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.Counter;
|
||||
|
|
@ -49,12 +50,18 @@ import mage.players.Player;
|
|||
|
||||
public class SunburstAbility extends EntersBattlefieldAbility{
|
||||
|
||||
public SunburstAbility(){
|
||||
private final static String ruleCreature ="Sunburst <i>(This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.)</i>";
|
||||
private final static String ruleNonCreature ="Sunburst <i>(This enters the battlefield with a charge counter on it for each color of mana spent to cast it.)</i>";
|
||||
private boolean isCreature;
|
||||
|
||||
public SunburstAbility(Card card){
|
||||
super(new SunburstEffect(),"");
|
||||
isCreature = card.getCardType().contains(CardType.CREATURE);
|
||||
}
|
||||
|
||||
public SunburstAbility(final SunburstAbility ability){
|
||||
super(ability);
|
||||
this.isCreature = ability.isCreature;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -65,7 +72,7 @@ public class SunburstAbility extends EntersBattlefieldAbility{
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Sunburst";
|
||||
return isCreature ? ruleCreature : ruleNonCreature;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -114,5 +121,4 @@ class SunburstEffect extends OneShotEffect<SunburstEffect> {
|
|||
return new SunburstEffect(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -42,7 +44,6 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.SuspendedCondition;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
|
|
@ -139,44 +140,53 @@ import mage.players.Player;
|
|||
public class SuspendAbility extends ActivatedAbilityImpl<SuspendAbility> {
|
||||
|
||||
private String ruleText;
|
||||
private boolean gainedTemporary;
|
||||
private boolean shortRule;
|
||||
|
||||
/**
|
||||
* Gives the card the SuspendAbility
|
||||
*
|
||||
* @param suspend - amount of time counters
|
||||
* @param cost - null is used for temporary gained suspend ability
|
||||
* @param card - card that has the suspend ability
|
||||
*/
|
||||
public SuspendAbility(int suspend, ManaCost cost, Card card) {
|
||||
this(suspend, cost, card, false);
|
||||
}
|
||||
|
||||
public SuspendAbility(int suspend, ManaCost cost, Card card, boolean shortRule) {
|
||||
super(Zone.HAND, new SuspendExileEffect(suspend), cost);
|
||||
this.usesStack = false;
|
||||
ruleText = new StringBuilder("Suspend ").append(suspend).append(" - ").append(cost.getText())
|
||||
.append(" <i>(Rather than cast this card from your hand, pay ")
|
||||
.append(cost.getText())
|
||||
.append(" and exile it with ")
|
||||
.append(suspend == 1 ? "a time counter":suspend + " time counters")
|
||||
.append(" on it.")
|
||||
.append(" At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.")
|
||||
.append(card.getCardType().contains(CardType.CREATURE)? " If you play it this way and it's a creature, it gains haste until you lose control of it.":"")
|
||||
.append(")</i>")
|
||||
.toString();
|
||||
this.shortRule = shortRule;
|
||||
StringBuilder sb = new StringBuilder("Suspend ");
|
||||
if (cost != null) {
|
||||
sb.append(suspend).append(" - ").append(cost.getText());
|
||||
if (!shortRule) {
|
||||
sb.append(" <i>(Rather than cast this card from your hand, pay ")
|
||||
.append(cost.getText())
|
||||
.append(" and exile it with ")
|
||||
.append(suspend == 1 ? "a time counter":suspend + " time counters")
|
||||
.append(" on it.")
|
||||
.append(" At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.")
|
||||
.append(card.getCardType().contains(CardType.CREATURE)? " If you play it this way and it's a creature, it gains haste until you lose control of it.":"")
|
||||
.append(")</i>");
|
||||
}
|
||||
} else {
|
||||
gainedTemporary = true;
|
||||
}
|
||||
ruleText = sb.toString();
|
||||
if (card.getManaCost().isEmpty()) {
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
// add triggered ability to remove the counter from the card
|
||||
Ability ability = new ConditionalTriggeredAbility(
|
||||
new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), TargetController.YOU, false),
|
||||
SuspendedCondition.getInstance(),
|
||||
"At the beginning of your upkeep, if this card is suspended, remove a time counter from it.");
|
||||
ability.setRuleVisible(false);
|
||||
card.addAbility(ability);
|
||||
// add triggered ability that casts the suspended card, if all counters are removed
|
||||
}
|
||||
card.addAbility(new SuspendBeginningOfUpkeepTriggeredAbility());
|
||||
card.addAbility(new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE)));
|
||||
// if it's a creature card, add Haste ability
|
||||
if (card.getCardType().contains(CardType.CREATURE)) {
|
||||
ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new GainHasteEffect(Duration.WhileOnBattlefield));
|
||||
ability.setRuleVisible(false);
|
||||
card.addAbility(ability);
|
||||
}
|
||||
}
|
||||
|
||||
public SuspendAbility(SuspendAbility ability) {
|
||||
super(ability);
|
||||
this.ruleText = ability.getRule();
|
||||
this.gainedTemporary = ability.gainedTemporary;
|
||||
this.shortRule = ability.shortRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -196,6 +206,10 @@ public class SuspendAbility extends ActivatedAbilityImpl<SuspendAbility> {
|
|||
return ruleText;
|
||||
}
|
||||
|
||||
public boolean isGainedTemporary() {
|
||||
return gainedTemporary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuspendAbility copy() {
|
||||
return new SuspendAbility(this);
|
||||
|
|
@ -252,6 +266,9 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl<SuspendPlayCardAbility
|
|||
|
||||
public SuspendPlayCardAbility(boolean isCreature) {
|
||||
super(Zone.EXILED, new SuspendPlayCardEffect(isCreature));
|
||||
if (isCreature) {
|
||||
this.addEffect(new GainHasteEffect());
|
||||
}
|
||||
setRuleVisible(false);
|
||||
}
|
||||
|
||||
|
|
@ -286,8 +303,7 @@ class SuspendPlayCardEffect extends OneShotEffect<SuspendPlayCardEffect> {
|
|||
|
||||
public SuspendPlayCardEffect(boolean isCreature) {
|
||||
super(Outcome.PutCardInPlay);
|
||||
this.staticText = new StringBuilder("play it without paying its mana cost if able. If you can't, it remains removed from the game")
|
||||
.append(isCreature ? ". If you play it this way and it's a creature, it gains haste until you lose control of it":"").toString();
|
||||
this.staticText = "play it without paying its mana cost if able. If you can't, it remains removed from the game";
|
||||
}
|
||||
|
||||
public SuspendPlayCardEffect(final SuspendPlayCardEffect effect) {
|
||||
|
|
@ -301,9 +317,33 @@ class SuspendPlayCardEffect extends OneShotEffect<SuspendPlayCardEffect> {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (player != null && card != null) {
|
||||
// remove temporary suspend ability (used e.g. for Epochrasite)
|
||||
List<Ability> abilitiesToRemove = new ArrayList<Ability>();
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof SuspendAbility) {
|
||||
if (((SuspendAbility)ability).isGainedTemporary()) {
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!abilitiesToRemove.isEmpty()) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof SuspendBeginningOfUpkeepTriggeredAbility || ability instanceof SuspendPlayCardAbility ) {
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
}
|
||||
// remove the triggered abilities from the game
|
||||
game.getState().resetTriggersForSourceId(card.getId());
|
||||
// remove the continious effects from the game
|
||||
game.getState().getContinuousEffects().removeGainedEffectsForSource(card.getId());
|
||||
// remove the abilities from the card
|
||||
card.getAbilities().removeAll(abilitiesToRemove);
|
||||
}
|
||||
// cast the card for free
|
||||
player.cast(card.getSpellAbility(), game, true);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -314,8 +354,8 @@ class GainHasteEffect extends ContinuousEffectImpl<GainHasteEffect> {
|
|||
|
||||
private UUID suspendController;
|
||||
|
||||
public GainHasteEffect(Duration duration) {
|
||||
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
||||
public GainHasteEffect() {
|
||||
super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
||||
staticText = "If you play it this way and it's a creature, it gains haste until you lose control of it";
|
||||
}
|
||||
|
||||
|
|
@ -336,14 +376,34 @@ class GainHasteEffect extends ContinuousEffectImpl<GainHasteEffect> {
|
|||
}
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
if (suspendController.equals(source.getControllerId()) && !used) { // used stores if the control changed
|
||||
if (suspendController.equals(source.getControllerId())) {
|
||||
permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game);
|
||||
return true;
|
||||
} else {
|
||||
used = true;
|
||||
this.discard();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SuspendBeginningOfUpkeepTriggeredAbility extends ConditionalTriggeredAbility {
|
||||
|
||||
public SuspendBeginningOfUpkeepTriggeredAbility() {
|
||||
super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), TargetController.YOU, false),
|
||||
SuspendedCondition.getInstance(),
|
||||
"At the beginning of your upkeep, if this card is suspended, remove a time counter from it.");
|
||||
this.setRuleVisible(false);
|
||||
|
||||
}
|
||||
|
||||
public SuspendBeginningOfUpkeepTriggeredAbility(final SuspendBeginningOfUpkeepTriggeredAbility effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuspendBeginningOfUpkeepTriggeredAbility copy() {
|
||||
return new SuspendBeginningOfUpkeepTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -28,10 +28,6 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
|
|
@ -39,8 +35,12 @@ import mage.abilities.costs.mana.ManaCosts;
|
|||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.effects.common.continious.GainAbilitySourceEffect;
|
||||
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
|
||||
import mage.abilities.effects.common.continious.GainAbilitySourceEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
|
|
@ -49,11 +49,19 @@ import mage.game.events.ZoneChangeEvent;
|
|||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*
|
||||
*
|
||||
* 702.82. Unearth
|
||||
*
|
||||
* 702.82a Unearth is an activated ability that functions while the card with unearth
|
||||
* is in a graveyard. "Unearth [cost]" means "[Cost]: Return this card from your graveyard
|
||||
* to the battlefield. It gains haste. Exile it at the beginning of the next end step.
|
||||
* If it would leave the battlefield, exile it instead of putting it anywhere else.
|
||||
* Activate this ability only any time you could cast a sorcery."
|
||||
*
|
||||
*/
|
||||
public class UnearthAbility extends ActivatedAbilityImpl<UnearthAbility> {
|
||||
|
||||
protected boolean unearthed;
|
||||
|
||||
public UnearthAbility(ManaCosts costs) {
|
||||
super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), costs);
|
||||
this.timing = TimingRule.SORCERY;
|
||||
|
|
@ -64,7 +72,6 @@ public class UnearthAbility extends ActivatedAbilityImpl<UnearthAbility> {
|
|||
|
||||
public UnearthAbility(final UnearthAbility ability) {
|
||||
super(ability);
|
||||
this.unearthed = ability.unearthed;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -72,13 +79,12 @@ public class UnearthAbility extends ActivatedAbilityImpl<UnearthAbility> {
|
|||
return new UnearthAbility(this);
|
||||
}
|
||||
|
||||
public boolean isUnearthed() {
|
||||
return unearthed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Unearth " + super.getRule();
|
||||
StringBuilder sb = new StringBuilder("Unearth ").append(this.getManaCosts().getText());
|
||||
sb.append(" <i>(").append(this.getManaCosts().getText());
|
||||
sb.append(": Return this card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step or if it would leave the battlefield. Unearth only as a sorcery.)");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -133,8 +139,9 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl<UnearthLeaves
|
|||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) {
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
|
||||
if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED)
|
||||
if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -149,5 +156,4 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl<UnearthLeaves
|
|||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
return apply(game, source);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.mana;
|
||||
|
||||
import mage.Mana;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public class ActivateAsSorceryManaAbility extends SimpleManaAbility {
|
||||
|
||||
public ActivateAsSorceryManaAbility(Zone zone, Mana mana, Cost cost) {
|
||||
super(zone, mana, cost);
|
||||
timing = TimingRule.SORCERY;
|
||||
}
|
||||
|
||||
public ActivateAsSorceryManaAbility(final ActivateAsSorceryManaAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivateAsSorceryManaAbility copy() {
|
||||
return new ActivateAsSorceryManaAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -402,6 +402,9 @@ public abstract class CardImpl<T extends CardImpl<T>> extends MageObjectImpl<T>
|
|||
case LIBRARY:
|
||||
game.getPlayer(ownerId).removeFromLibrary(this, game);
|
||||
break;
|
||||
case EXILED:
|
||||
game.getExile().removeCard(this, game);
|
||||
break;
|
||||
default:
|
||||
logger.warn("moveToExile, not fully implemented: from="+fromZone);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ public abstract class DeckImporter {
|
|||
public DeckCardLists importDeck(String file) {
|
||||
File f = new File(file);
|
||||
DeckCardLists deckList = new DeckCardLists();
|
||||
if (!f.exists()) {
|
||||
logger.warn("Deckfile " + file + " not found.");
|
||||
return deckList;
|
||||
}
|
||||
lineCount = 0;
|
||||
sbMessage.setLength(0);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ public enum EffectType {
|
|||
ASTHOUGH("As Though Effect"),
|
||||
RESTRICTION("Restriction Effect"),
|
||||
REQUIREMENT("Requirement Effect"),
|
||||
COSTMODIFICATION("Cost Modification Effect");
|
||||
COSTMODIFICATION("Cost Modification Effect"),
|
||||
SPLICE("Splice Card Effect");
|
||||
|
||||
private String text;
|
||||
|
||||
|
|
|
|||
42
Mage/src/mage/constants/MatchTimeLimit.java
Normal file
42
Mage/src/mage/constants/MatchTimeLimit.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
* The time per player to have activity in a match.
|
||||
* If time runs out for a player, he looses the currently running game of a match.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum MatchTimeLimit {
|
||||
NONE(0,"None"),
|
||||
MIN__10(600, "10 Minutes"),
|
||||
MIN__15(900, "15 Minutes"),
|
||||
MIN__20(1200, "20 Minutes"),
|
||||
MIN__25(1500, "25 Minutes"),
|
||||
MIN__30(1800, "30 Minutes"),
|
||||
MIN__40(2400, "40 Minutes"),
|
||||
MIN__50(3000, "50 Minutes"),
|
||||
MIN__60(3600, "60 Minutes"),
|
||||
MIN__90(5400, "90 Minutes"),
|
||||
MIN_120(7200, "120 Minutes");
|
||||
|
||||
private int matchSeconds;
|
||||
private String name;
|
||||
|
||||
MatchTimeLimit(int matchSeconds, String name) {
|
||||
this.matchSeconds = matchSeconds;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getTimeLimit() {
|
||||
return matchSeconds;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ package mage.filter.common;
|
|||
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -44,6 +45,13 @@ public class FilterControlledCreaturePermanent extends FilterControlledPermanent
|
|||
public FilterControlledCreaturePermanent(String name) {
|
||||
super(name);
|
||||
this.add(new CardTypePredicate(CardType.CREATURE));
|
||||
}
|
||||
|
||||
|
||||
public FilterControlledCreaturePermanent(String subtype, String name) {
|
||||
super(name);
|
||||
this.add(new CardTypePredicate(CardType.CREATURE));
|
||||
this.add(new SubtypePredicate(subtype));
|
||||
}
|
||||
|
||||
public FilterControlledCreaturePermanent(final FilterControlledCreaturePermanent filter) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ package mage.filter.common;
|
|||
import mage.constants.CardType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -45,6 +46,12 @@ public class FilterCreaturePermanent extends FilterPermanent {
|
|||
public FilterCreaturePermanent(String name) {
|
||||
super(name);
|
||||
this.add(new CardTypePredicate(CardType.CREATURE));
|
||||
}
|
||||
|
||||
public FilterCreaturePermanent(String subtype, String name) {
|
||||
super(name);
|
||||
this.add(new CardTypePredicate(CardType.CREATURE));
|
||||
this.add(new SubtypePredicate(subtype));
|
||||
}
|
||||
|
||||
public FilterCreaturePermanent(final FilterCreaturePermanent filter) {
|
||||
|
|
|
|||
|
|
@ -27,16 +27,16 @@
|
|||
*/
|
||||
package mage.filter.predicate.permanent;
|
||||
|
||||
import mage.cards.Card;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeff
|
||||
*/
|
||||
public class CounterPredicate implements Predicate<Permanent> {
|
||||
public class CounterPredicate implements Predicate<Card> {
|
||||
|
||||
private final CounterType counter;
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ public class CounterPredicate implements Predicate<Permanent> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
public boolean apply(Card input, Game game) {
|
||||
return input.getCounters().containsKey(counter);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@
|
|||
|
||||
package mage.game;
|
||||
|
||||
import mage.constants.MultiplayerAttackOption;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.constants.Zone;
|
||||
import mage.MageItem;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -44,6 +41,10 @@ import mage.cards.Card;
|
|||
import mage.cards.Cards;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.choices.Choice;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.MultiplayerAttackOption;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.combat.Combat;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.events.GameEvent;
|
||||
|
|
@ -65,7 +66,6 @@ import mage.util.functions.ApplyToPermanent;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import mage.constants.Duration;
|
||||
|
||||
public interface Game extends MageItem, Serializable {
|
||||
|
||||
|
|
@ -225,4 +225,11 @@ public interface Game extends MageItem, Serializable {
|
|||
// 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
package mage.game;
|
||||
|
||||
import mage.Constants;
|
||||
import mage.constants.CardType;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
|
|
@ -49,12 +48,14 @@ import mage.actions.impl.MageAction;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.choices.Choice;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterPlaneswalkerPermanent;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
|
|
@ -89,8 +90,6 @@ import java.io.IOException;
|
|||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
|
||||
|
||||
public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializable {
|
||||
|
|
@ -156,6 +155,8 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
|
|||
// used to indicate that currently applied replacement effects have to check for scope relevance (614.12 13/01/18)
|
||||
private boolean scopeRelevant = false;
|
||||
|
||||
private int priorityTime;
|
||||
|
||||
@Override
|
||||
public abstract T copy();
|
||||
|
||||
|
|
@ -199,6 +200,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
|
|||
this.stateCheckRequired = game.stateCheckRequired;
|
||||
this.scorePlayer = game.scorePlayer;
|
||||
this.scopeRelevant = game.scopeRelevant;
|
||||
this.priorityTime = game.priorityTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -590,6 +592,9 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
|
|||
protected void init(UUID choosingPlayerId, GameOptions gameOptions) {
|
||||
for (Player player: state.getPlayers().values()) {
|
||||
player.beginTurn(this);
|
||||
if (priorityTime > 0) {
|
||||
initTimer(player.getId());
|
||||
}
|
||||
}
|
||||
if (startMessage == null || startMessage.isEmpty()) {
|
||||
startMessage = "Game has started";
|
||||
|
|
@ -1060,7 +1065,9 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
|
|||
public void addTriggeredAbility(TriggeredAbility ability) {
|
||||
if (ability instanceof TriggeredManaAbility || ability instanceof DelayedTriggeredManaAbility) {
|
||||
// 20110715 - 605.4
|
||||
ability.resolve(this);
|
||||
Ability manaAbiltiy = ability.copy();
|
||||
manaAbiltiy.activate(this, false);
|
||||
manaAbiltiy.resolve(this);
|
||||
}
|
||||
else {
|
||||
TriggeredAbility newAbility = (TriggeredAbility) ability.copy();
|
||||
|
|
@ -1946,4 +1953,35 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
|
|||
public void setStartMessage(String startMessage) {
|
||||
this.startMessage = startMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initTimer(UUID playerId) {
|
||||
if (priorityTime > 0) {
|
||||
tableEventSource.fireTableEvent(EventType.INIT_TIMER, playerId, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeTimer(UUID playerId) {
|
||||
if (priorityTime > 0) {
|
||||
tableEventSource.fireTableEvent(EventType.RESUME_TIMER, playerId, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseTimer(UUID playerId) {
|
||||
if (priorityTime > 0) {
|
||||
tableEventSource.fireTableEvent(EventType.PAUSE_TIMER, playerId, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriorityTime() {
|
||||
return priorityTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPriorityTime(int priorityTime) {
|
||||
this.priorityTime = priorityTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ import java.util.UUID;
|
|||
public class TableEvent extends EventObject implements ExternalEvent, Serializable {
|
||||
|
||||
public enum EventType {
|
||||
UPDATE, INFO, STATUS, REVEAL, LOOK, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, ERROR
|
||||
UPDATE, INFO, STATUS, REVEAL, LOOK, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, ERROR,
|
||||
INIT_TIMER, RESUME_TIMER, PAUSE_TIMER
|
||||
}
|
||||
|
||||
private Game game;
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ public abstract class MatchImpl implements Match {
|
|||
game.loadCards(matchPlayer.getDeck().getSideboard(), matchPlayer.getPlayer().getId());
|
||||
game.addPlayer(matchPlayer.getPlayer(), matchPlayer.getDeck());
|
||||
}
|
||||
game.setPriorityTime(options.getPriorityTime());
|
||||
}
|
||||
|
||||
protected void shufflePlayers() {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ package mage.game.match;
|
|||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mage.constants.MatchTimeLimit;
|
||||
import mage.constants.MultiplayerAttackOption;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
|
||||
|
|
@ -49,6 +50,10 @@ public class MatchOptions implements Serializable {
|
|||
protected String deckType;
|
||||
protected boolean limited;
|
||||
protected List<String> playerTypes = new ArrayList<String>();
|
||||
/**
|
||||
* Time each player has during the game to play using his\her priority.
|
||||
*/
|
||||
protected MatchTimeLimit matchTimeLimit; // 0 = no priorityTime handling
|
||||
|
||||
public MatchOptions(String name, String gameType) {
|
||||
this.name = name;
|
||||
|
|
@ -90,6 +95,7 @@ public class MatchOptions implements Serializable {
|
|||
public void setFreeMulligans(int freeMulligans) {
|
||||
this.freeMulligans = freeMulligans;
|
||||
}
|
||||
|
||||
public String getGameType() {
|
||||
return gameType;
|
||||
}
|
||||
|
|
@ -117,4 +123,16 @@ public class MatchOptions implements Serializable {
|
|||
public void setLimited(boolean limited) {
|
||||
this.limited = limited;
|
||||
}
|
||||
|
||||
public int getPriorityTime() {
|
||||
if (matchTimeLimit == null) {
|
||||
return MatchTimeLimit.NONE.getTimeLimit();
|
||||
}
|
||||
return matchTimeLimit.getTimeLimit();
|
||||
}
|
||||
|
||||
public void setMatchTimeLimit(MatchTimeLimit matchTimeLimit) {
|
||||
this.matchTimeLimit = matchTimeLimit;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ public class Token extends MageObjectImpl<Token> {
|
|||
protected String description;
|
||||
private UUID lastAddedTokenId;
|
||||
private int tokenType;
|
||||
private int originalCardNumber;
|
||||
private String originalExpansionSetCode;
|
||||
|
||||
public enum Type {
|
||||
FIRST(1),
|
||||
|
|
@ -146,4 +148,21 @@ public class Token extends MageObjectImpl<Token> {
|
|||
public void setTokenType(int tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public int getOriginalCardNumber() {
|
||||
return originalCardNumber;
|
||||
}
|
||||
|
||||
public void setOriginalCardNumber(int originalCardNumber) {
|
||||
this.originalCardNumber = originalCardNumber;
|
||||
}
|
||||
|
||||
public String getOriginalExpansionSetCode() {
|
||||
return originalExpansionSetCode;
|
||||
}
|
||||
|
||||
public void setOriginalExpansionSetCode(String originalExpansionSetCode) {
|
||||
this.originalExpansionSetCode = originalExpansionSetCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,9 +118,21 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
|
||||
|
||||
public boolean activate(Game game, boolean noMana) {
|
||||
if (!spellAbilities.get(0).activate(game, noMana)) {
|
||||
return false;
|
||||
}
|
||||
// if there are more abilities (fused split spell) or first ability added new abilities (splice), activate the additional abilities
|
||||
boolean ignoreAbility = true;
|
||||
boolean payNoMana = noMana;
|
||||
for (SpellAbility spellAbility: spellAbilities) {
|
||||
if (!spellAbility.activate(game, noMana)) {
|
||||
return false;
|
||||
// costs for spliced abilities were added to main spellAbility, so pay no man for spliced abilities
|
||||
payNoMana |= spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE);
|
||||
if (ignoreAbility) {
|
||||
ignoreAbility = false;
|
||||
} else {
|
||||
if (!spellAbility.activate(game, payNoMana)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -138,10 +150,15 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
result = false;
|
||||
boolean legalParts = false;
|
||||
for(SpellAbility spellAbility: this.spellAbilities) {
|
||||
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
|
||||
legalParts = true;
|
||||
updateOptionalCosts(index);
|
||||
result |= spellAbility.resolve(game);
|
||||
for (UUID modeId :spellAbility.getModes().getSelectedModes()) {
|
||||
spellAbility.getModes().setMode(spellAbility.getModes().get(modeId));
|
||||
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
|
||||
legalParts = true;
|
||||
if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
|
||||
updateOptionalCosts(index);
|
||||
}
|
||||
result |= spellAbility.resolve(game);
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
|
@ -226,13 +243,16 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
String name = null;
|
||||
if (object == null) {
|
||||
Player targetPlayer = game.getPlayer(targetId);
|
||||
if (targetPlayer != null) name = targetPlayer.getName();
|
||||
if (targetPlayer != null) {
|
||||
name = targetPlayer.getName();
|
||||
}
|
||||
} else {
|
||||
name = object.getName();
|
||||
}
|
||||
if (name != null && player.chooseUse(spellAbility.getEffects().get(0).getOutcome(), "Change target from " + name + "?", game)) {
|
||||
if (!player.chooseTarget(spellAbility.getEffects().get(0).getOutcome(), newTarget, spellAbility, game))
|
||||
if (!player.chooseTarget(spellAbility.getEffects().get(0).getOutcome(), newTarget, spellAbility, game)) {
|
||||
newTarget.addTarget(targetId, spellAbility, game, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
newTarget.addTarget(targetId, spellAbility, game, false);
|
||||
|
|
@ -345,6 +365,10 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
return card.getOwnerId();
|
||||
}
|
||||
|
||||
public void addSpellAbility(SpellAbility spellAbility) {
|
||||
spellAbilities.add(spellAbility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAbility(Ability ability) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -299,4 +299,18 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*
|
||||
*/
|
||||
void revealFaceDownCard(Card card, Game game);
|
||||
|
||||
/**
|
||||
* Set seconds left to play the game.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
void setPriorityTimeLeft(int timeLeft);
|
||||
|
||||
/**
|
||||
* Returns seconds left to play the game.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getPriorityTimeLeft();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
|
||||
package mage.players;
|
||||
|
||||
import mage.constants.*;
|
||||
import mage.constants.Zone;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.*;
|
||||
|
|
@ -46,10 +44,13 @@ import mage.actions.MageDrawAction;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.constants.*;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
import mage.counters.Counters;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterCreatureForCombat;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
|
|
@ -59,11 +60,13 @@ import mage.game.events.DamagedPlayerEvent;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackAbility;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.net.UserData;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetAmount;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetDiscard;
|
||||
import mage.watchers.common.BloodthirstWatcher;
|
||||
|
|
@ -72,11 +75,6 @@ import org.apache.log4j.Logger;
|
|||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
import mage.cards.SplitCard;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
|
||||
public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Serializable {
|
||||
|
||||
|
|
@ -108,6 +106,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
protected boolean passedTurn;
|
||||
protected int turns;
|
||||
protected int storedBookmark = -1;
|
||||
protected int priorityTimeLeft = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* This indicates that player passed all turns until his own turn starts.
|
||||
|
|
@ -1793,4 +1792,14 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
this.revealCards(name, cards, game);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPriorityTimeLeft(int timeLeft) {
|
||||
priorityTimeLeft = timeLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriorityTimeLeft() {
|
||||
return priorityTimeLeft;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,15 +27,17 @@
|
|||
*/
|
||||
package mage.target.common;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -44,26 +46,67 @@ import java.util.UUID;
|
|||
public class TargetCardInExile extends TargetCard<TargetCardInExile> {
|
||||
|
||||
private UUID zoneId;
|
||||
private boolean allExileZones;
|
||||
|
||||
public TargetCardInExile(FilterCard filter, UUID zoneId) {
|
||||
this(1, 1, filter, zoneId);
|
||||
}
|
||||
|
||||
public TargetCardInExile(int minNumTargets, int maxNumTargets, FilterCard filter, UUID zoneId) {
|
||||
this(minNumTargets, maxNumTargets, filter, zoneId, false);
|
||||
}
|
||||
|
||||
public TargetCardInExile(int minNumTargets, int maxNumTargets, FilterCard filter, UUID zoneId, boolean allExileZones) {
|
||||
super(minNumTargets, maxNumTargets, Zone.EXILED, filter);
|
||||
this.zoneId = zoneId;
|
||||
this.allExileZones = allExileZones;
|
||||
this.targetName = filter.getMessage();
|
||||
}
|
||||
|
||||
public TargetCardInExile(final TargetCardInExile target) {
|
||||
super(target);
|
||||
this.zoneId = target.zoneId;
|
||||
this.allExileZones = target.allExileZones;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
|
||||
if (allExileZones) {
|
||||
Set<UUID> possibleTargets = new HashSet<UUID>();
|
||||
for (Card card : game.getExile().getAllCards(game)) {
|
||||
if (filter.match(card, sourceControllerId, game)) {
|
||||
possibleTargets.add(card.getId());
|
||||
}
|
||||
}
|
||||
return possibleTargets;
|
||||
} else {
|
||||
return super.possibleTargets(sourceId, sourceControllerId, game);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
|
||||
if (allExileZones) {
|
||||
int numberTargets = 0;
|
||||
for(ExileZone exileZone : game.getExile().getExileZones()) {
|
||||
numberTargets += exileZone.count(filter, sourceId, sourceControllerId, game);
|
||||
if (numberTargets >= this.minNumberOfTargets) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return super.canChoose(sourceControllerId, game);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
||||
Card card = game.getCard(id);
|
||||
if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED) {
|
||||
if (allExileZones) {
|
||||
return filter.match(card, source.getControllerId(), game);
|
||||
}
|
||||
ExileZone exile;
|
||||
if (zoneId != null) {
|
||||
exile = game.getExile().getExileZone(zoneId);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
package mage.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -43,6 +44,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
|
|||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.util.functions.CopyFunction;
|
||||
import mage.util.functions.CopyTokenFunction;
|
||||
|
|
@ -365,4 +367,25 @@ public class CardUtil {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and saves a (card + zoneChangeCounter) specific exileId.
|
||||
*
|
||||
*
|
||||
* @param game
|
||||
* @param source - source ability
|
||||
* @return - the specific UUID
|
||||
*/
|
||||
public static UUID getCardExileZoneId(Game game, Ability source) {
|
||||
UUID exileId = null;
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
exileId = (UUID) game.getState().getValue(new StringBuilder("SourceExileZone").append(source.getSourceId()).append(card.getZoneChangeCounter()).toString());
|
||||
if (exileId == null) {
|
||||
exileId = UUID.randomUUID();
|
||||
game.getState().setValue(new StringBuilder("SourceExileZone").append(source.getSourceId()).append(card.getZoneChangeCounter()).toString(), exileId);
|
||||
}
|
||||
}
|
||||
return exileId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ public class CopyTokenFunction implements Function<Token, Card> {
|
|||
for (String type : sourceObj.getSupertype()) {
|
||||
target.getSupertype().add(type);
|
||||
}
|
||||
//target.setExpansionSetCode(source.getExpansionSetCode());
|
||||
target.setOriginalExpansionSetCode(source.getExpansionSetCode());
|
||||
target.setOriginalCardNumber(source.getCardNumber());
|
||||
target.getAbilities().clear();
|
||||
|
||||
for (Ability ability0 : sourceObj.getAbilities()) {
|
||||
|
|
|
|||
97
Mage/src/mage/watchers/common/ProwlWatcher.java
Normal file
97
Mage/src/mage/watchers/common/ProwlWatcher.java
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2011 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.watchers.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.WatcherImpl;
|
||||
|
||||
/**
|
||||
* Watcher stores with which creature subtypes a player made combat damage to
|
||||
* other players during a turn.
|
||||
*
|
||||
* @author LevelX
|
||||
*/
|
||||
public class ProwlWatcher extends WatcherImpl<ProwlWatcher> {
|
||||
|
||||
private Map<UUID, Set<String>> damagingSubtypes = new HashMap<UUID, Set<String>>();
|
||||
|
||||
public ProwlWatcher() {
|
||||
super("Prowl", WatcherScope.GAME);
|
||||
}
|
||||
|
||||
public ProwlWatcher(final ProwlWatcher watcher) {
|
||||
super(watcher);
|
||||
for (Entry<UUID, Set<String>> entry : watcher.damagingSubtypes.entrySet()) {
|
||||
damagingSubtypes.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProwlWatcher copy() {
|
||||
return new ProwlWatcher(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == EventType.DAMAGED_PLAYER) {
|
||||
DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event;
|
||||
if (dEvent.isCombatDamage()) {
|
||||
Permanent creature = game.getPermanent(dEvent.getSourceId());
|
||||
if (creature != null) {
|
||||
Set<String> subtypes = damagingSubtypes.get(creature.getControllerId());
|
||||
if (subtypes == null) {
|
||||
subtypes = new LinkedHashSet<String>();
|
||||
}
|
||||
subtypes.addAll(creature.getSubtype());
|
||||
damagingSubtypes.put(creature.getControllerId(), subtypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
damagingSubtypes.clear();
|
||||
}
|
||||
|
||||
public Set<String> getDamagingSubtypes(UUID playerId) {
|
||||
return damagingSubtypes.get(playerId);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue