diff --git a/Mage.Common/src/mage/view/AbilityPickerView.java b/Mage.Common/src/mage/view/AbilityPickerView.java index 7426fbf8b50..9f800dd7951 100644 --- a/Mage.Common/src/mage/view/AbilityPickerView.java +++ b/Mage.Common/src/mage/view/AbilityPickerView.java @@ -34,6 +34,8 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.Modes; /** * @@ -50,6 +52,10 @@ public class AbilityPickerView implements Serializable { } } + public AbilityPickerView(Map modes) { + this.choices = modes; + } + public Map getChoices() { return choices; } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 0013e29f572..1c0b181e819 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -44,6 +44,8 @@ import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.Modes; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; @@ -860,6 +862,13 @@ public class ComputerPlayer> extends PlayerImpl i return 0; } + @Override + public Mode chooseMode(Modes modes, Ability source, Game game) { + logger.debug("chooseMode"); + //TODO: improve this; + return modes.get(0); + } + @Override public TriggeredAbility chooseTriggeredAbility(TriggeredAbilities abilities, Game game) { logger.debug("chooseTriggeredAbility"); diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 28be0128aa9..fd4f8ae2f77 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -29,6 +29,7 @@ package mage.player.human; import java.io.Serializable; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -41,6 +42,8 @@ import mage.Constants.Zone; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.Modes; import mage.abilities.SpecialAction; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; @@ -564,6 +567,30 @@ public class HumanPlayer extends PlayerImpl { activateAbility(abilities.get(response.getUUID()), game); } } + + @Override + public Mode chooseMode(Modes modes, Ability source, Game game) { + if (modes.size() > 1) { + MageObject obj = game.getObject(source.getSourceId()); + Map modeMap = new HashMap(); + for (Mode mode: modes.values()) { + String modeText = mode.getEffects().getText(source); + if (obj != null) + modeText = modeText.replace("{source}", obj.getName()); + modeMap.put(mode.getId(), modeText); + } + game.fireGetModeEvent(playerId, "Choose Mode", modeMap); + waitForResponse(); + if (response.getUUID() != null) { + for (Mode mode: modes.values()) { + if (mode.getId().equals(response.getUUID())) + return mode; + } + } + return null; + } + return modes.getMode(); + } @Override public void setResponseString(String responseString) { diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index 53cd8af9690..3872e13ae24 100644 Binary files a/Mage.Server/plugins/mage-player-ai.jar and b/Mage.Server/plugins/mage-player-ai.jar differ diff --git a/Mage.Server/plugins/mage-player-human.jar b/Mage.Server/plugins/mage-player-human.jar index 0e500cf9295..5e53215ca9f 100644 Binary files a/Mage.Server/plugins/mage-player-human.jar and b/Mage.Server/plugins/mage-player-human.jar differ diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 614da0a6507..c53665fcdd9 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -50,6 +50,7 @@ import java.util.zip.GZIPOutputStream; import mage.Constants.Zone; import mage.abilities.Ability; +import mage.abilities.Modes; import mage.cards.Card; import mage.cards.Cards; import mage.cards.decks.Deck; @@ -155,6 +156,9 @@ public class GameController implements GameCallback { case CHOOSE_ABILITY: chooseAbility(event.getPlayerId(), event.getAbilities()); break; + case CHOOSE_MODE: + chooseMode(event.getPlayerId(), event.getModes()); + break; case CHOOSE: choose(event.getPlayerId(), event.getMessage(), event.getChoices()); break; @@ -352,6 +356,12 @@ public class GameController implements GameCallback { informOthers(playerId); } + private synchronized void chooseMode(UUID playerId, Map modes) throws MageException { + if (gameSessions.containsKey(playerId)) + gameSessions.get(playerId).chooseAbility(new AbilityPickerView(modes)); + informOthers(playerId); + } + private synchronized void choose(UUID playerId, String message, Set choices) throws MageException { if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).choose(message, choices); diff --git a/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java b/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java index 6ab963feac3..71f8c59c015 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java +++ b/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java @@ -95,7 +95,7 @@ class FinestHourAbility extends TriggeredAbilityImpl { if (event.getType() == EventType.DECLARED_ATTACKERS) { if (game.getCombat().attacksAlone()) { this.addTarget(new TargetCreaturePermanent()); - this.targets.get(0).add(game.getCombat().getAttackers().get(0), game); + getTargets().get(0).add(game.getCombat().getAttackers().get(0), game); return true; } } diff --git a/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java b/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java index e4a27f40f1e..4e0fc1e76f5 100644 --- a/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java +++ b/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java @@ -83,7 +83,7 @@ class FontOfMythosAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DRAW_STEP_PRE) { this.addTarget(new TargetPlayer()); - this.targets.get(0).add(event.getPlayerId(),game); + getTargets().get(0).add(event.getPlayerId(),game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/conflux/WallOfReverence.java b/Mage.Sets/src/mage/sets/conflux/WallOfReverence.java index 6c28d99d77f..6e9f3e75bfd 100644 --- a/Mage.Sets/src/mage/sets/conflux/WallOfReverence.java +++ b/Mage.Sets/src/mage/sets/conflux/WallOfReverence.java @@ -101,7 +101,7 @@ class WallOfReverenceTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DRAW_STEP_PRE) { this.addTarget(new TargetPlayer()); - this.targets.get(0).add(event.getPlayerId(), game); + getTargets().get(0).add(event.getPlayerId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/magic2011/LilianasCaress.java b/Mage.Sets/src/mage/sets/magic2011/LilianasCaress.java index 779eeb92d74..77ad0d16b26 100644 --- a/Mage.Sets/src/mage/sets/magic2011/LilianasCaress.java +++ b/Mage.Sets/src/mage/sets/magic2011/LilianasCaress.java @@ -83,7 +83,7 @@ class LilianasCaressAbility extends TriggeredAbilityImpl public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DISCARDED_CARD && game.getOpponents(controllerId).contains(event.getPlayerId())) { this.addTarget(new TargetPlayer()); - this.targets.get(0).add(event.getPlayerId(), game); + getTargets().get(0).add(event.getPlayerId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/magic2011/WildEvocation.java b/Mage.Sets/src/mage/sets/magic2011/WildEvocation.java index 4b3b8d194b2..3e7ad50aaf1 100644 --- a/Mage.Sets/src/mage/sets/magic2011/WildEvocation.java +++ b/Mage.Sets/src/mage/sets/magic2011/WildEvocation.java @@ -89,7 +89,7 @@ class WildEvocationAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DRAW_STEP_PRE) { this.addTarget(new TargetPlayer()); - this.targets.get(0).add(event.getPlayerId(), game); + getTargets().get(0).add(event.getPlayerId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/Slagstorm.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/Slagstorm.java new file mode 100644 index 00000000000..af60c22b636 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/Slagstorm.java @@ -0,0 +1,101 @@ +/* + * 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.sets.mirrodinbesieged; + +import java.util.UUID; + +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageAllEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Loki + */ +public class Slagstorm extends CardImpl { + + public Slagstorm (UUID ownerId) { + super(ownerId, 75, "Slagstorm", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); + this.expansionSetCode = "MBS"; + this.color.setRed(true); + this.getSpellAbility().addEffect(new DamageAllEffect(3, FilterCreaturePermanent.getDefault())); + Mode mode = new Mode(); + mode.getEffects().add(new SlagstormEffect()); + this.getSpellAbility().addMode(mode); + } + + public Slagstorm (final Slagstorm card) { + super(card); + } + + @Override + public Slagstorm copy() { + return new Slagstorm(this); + } + +} + + +class SlagstormEffect extends OneShotEffect { + SlagstormEffect() { + super(Constants.Outcome.Damage); + } + + SlagstormEffect(final SlagstormEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId: game.getPlayer(source.getControllerId()).getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null) + player.damage(3, source.getId(), game, false, true); + } + return true; + } + + @Override + public SlagstormEffect copy() { + return new SlagstormEffect(this); + } + + @Override + public String getText(Ability source) { + return "Slagstorm deals 3 damage to each player"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/newphyrexia/SuturePriest.java b/Mage.Sets/src/mage/sets/newphyrexia/SuturePriest.java index 4300c7ade4e..add44a7858f 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/SuturePriest.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/SuturePriest.java @@ -100,7 +100,7 @@ class SuturePriestFirstTriggeredAbility extends TriggeredAbilityImpl if (event.getType() == EventType.DECLARED_ATTACKERS && game.getActivePlayerId().equals(this.controllerId) ) { if (game.getCombat().attacksAlone()) { this.addTarget(new TargetCreaturePermanent()); - this.targets.get(0).add(game.getCombat().getAttackers().get(0), game); + getTargets().get(0).add(game.getCombat().getAttackers().get(0), game); return true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/HammerOfRuin.java b/Mage.Sets/src/mage/sets/worldwake/HammerOfRuin.java index e61ebf93e91..ef5b85b8134 100644 --- a/Mage.Sets/src/mage/sets/worldwake/HammerOfRuin.java +++ b/Mage.Sets/src/mage/sets/worldwake/HammerOfRuin.java @@ -99,7 +99,7 @@ class HammerOfRuinTriggeredAbility extends TriggeredAbilityImpl> implements Ability { protected Costs costs; protected ArrayList alternativeCosts = new ArrayList(); protected Costs optionalCosts; - protected Targets targets; - protected Choices choices; - protected Effects effects; + protected Modes modes; protected Zone zone; protected String name; protected boolean usesStack = true; @@ -92,9 +90,7 @@ public abstract class AbilityImpl> implements Ability { this.manaCostsToPay = new ManaCostsImpl(); this.costs = new CostsImpl(); this.optionalCosts = new CostsImpl(); - this.effects = new Effects(); - this.targets = new Targets(); - this.choices = new Choices(); + this.modes = new Modes(); } public AbilityImpl(final AbilityImpl ability) { @@ -113,9 +109,7 @@ public abstract class AbilityImpl> implements Ability { for (AlternativeCost cost: ability.alternativeCosts) { this.alternativeCosts.add((AlternativeCost)cost.copy()); } - this.targets = ability.targets.copy(); - this.choices = ability.choices.copy(); - this.effects = ability.effects.copy(); + this.modes = ability.modes.copy(); } @Override @@ -152,13 +146,16 @@ public abstract class AbilityImpl> implements Ability { @Override public boolean activate(Game game, boolean noMana) { + // 20110204 - 700.2 + if (!modes.choose(game, this)) + return false; //20100716 - 601.2b - if (choices.size() > 0 && choices.choose(game, this) == false) { + if (getChoices().size() > 0 && getChoices().choose(game, this) == false) { logger.debug("activate failed - choice"); return false; } //20100716 - 601.2b - if (targets.size() > 0 && targets.chooseTargets(effects.get(0).getOutcome(), this.controllerId, this, game) == false) { + if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) { logger.debug("activate failed - target"); return false; } @@ -258,13 +255,13 @@ public abstract class AbilityImpl> implements Ability { @Override public Effects getEffects() { - return effects; + return modes.getMode().getEffects(); } @Override public Effects getEffects(EffectType effectType) { Effects typedEffects = new Effects(); - for (Effect effect: effects) { + for (Effect effect: getEffects()) { if (effect.getEffectType() == effectType) { typedEffects.add(effect); } @@ -274,7 +271,7 @@ public abstract class AbilityImpl> implements Ability { @Override public Choices getChoices() { - return choices; + return modes.getMode().getChoices(); } @Override @@ -323,7 +320,7 @@ public abstract class AbilityImpl> implements Ability { } } - sbRule.append(effects.getText(this)); + sbRule.append(modes.getText(this)); return sbRule.toString(); } @@ -371,34 +368,49 @@ public abstract class AbilityImpl> implements Ability { @Override public void addEffect(Effect effect) { if (effect != null) { - this.effects.add(effect); + getEffects().add(effect); } } @Override public void addTarget(Target target) { if (target != null) { - this.targets.add(target); + getTargets().add(target); } } @Override public void addChoice(Choice choice) { if (choice != null) { - this.choices.add(choice); + getChoices().add(choice); } } @Override public Targets getTargets() { - return this.targets; + return modes.getMode().getTargets(); } @Override public UUID getFirstTarget() { - return targets.getFirstTarget(); + return getTargets().getFirstTarget(); } + @Override + public boolean isModal() { + return this.modes.size() > 1; + } + + @Override + public void addMode(Mode mode) { + this.modes.addMode(mode); + } + + @Override + public Modes getModes() { + return modes; + } + @Override public String toString() { return getRule(); diff --git a/Mage/src/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/mage/abilities/ActivatedAbilityImpl.java index 6bd5134c5e8..2b18e56ebe3 100644 --- a/Mage/src/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/mage/abilities/ActivatedAbilityImpl.java @@ -148,7 +148,7 @@ public abstract class ActivatedAbilityImpl> ex return false; //20091005 - 602.5d/602.5e if (timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)) { - if (costs.canPay(sourceId, controllerId, game) && targets.canChoose(sourceId, playerId, game)) { + if (costs.canPay(sourceId, controllerId, game) && getTargets().canChoose(sourceId, playerId, game)) { return true; } } @@ -174,9 +174,9 @@ public abstract class ActivatedAbilityImpl> ex protected String getMessageText(Game game) { StringBuilder sb = new StringBuilder(); sb.append(game.getObject(this.sourceId).getName()); - if (this.targets.size() > 0) { + if (getTargets().size() > 0) { sb.append(" targeting "); - for (Target target: targets) { + for (Target target: getTargets()) { sb.append(target.getTargetedName(game)); } } diff --git a/Mage/src/mage/abilities/Mode.java b/Mage/src/mage/abilities/Mode.java new file mode 100644 index 00000000000..dcabed11204 --- /dev/null +++ b/Mage/src/mage/abilities/Mode.java @@ -0,0 +1,79 @@ +/* + * 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; + +import java.util.UUID; +import mage.abilities.effects.Effects; +import mage.choices.Choices; +import mage.target.Targets; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Mode { + + protected UUID id; + protected Targets targets; + protected Choices choices; + protected Effects effects; + + public Mode() { + this.id = UUID.randomUUID(); + this.targets = new Targets(); + this.choices = new Choices(); + this.effects = new Effects(); + } + + public Mode(Mode mode) { + this.id = mode.id; + this.targets = mode.targets.copy(); + this.choices = mode.choices.copy(); + this.effects = mode.effects.copy(); + } + + public Mode copy() { + return new Mode(this); + } + + public UUID getId() { + return id; + } + + public Targets getTargets() { + return targets; + } + + public Choices getChoices() { + return choices; + } + + public Effects getEffects() { + return effects; + } +} diff --git a/Mage/src/mage/abilities/Modes.java b/Mage/src/mage/abilities/Modes.java new file mode 100644 index 00000000000..e0925648c81 --- /dev/null +++ b/Mage/src/mage/abilities/Modes.java @@ -0,0 +1,109 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.UUID; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Modes extends HashMap { + + private UUID modeId; + + public Modes() { + Mode mode = new Mode(); + this.put(mode.getId(), mode); + this.modeId = mode.getId(); + } + + public Modes(Modes modes) { + this.modeId = modes.modeId; + for (Mode mode: modes.values()) { + this.put(mode.getId(), mode.copy()); + } + } + + public Modes copy() { + return new Modes(this); + } + + public Mode getMode() { + return get(modeId); + } + + public void setMode(Mode mode) { + if (this.containsKey(mode.getId())) + this.modeId = mode.getId(); + } + + public void addMode(Mode mode) { + this.put(mode.getId(), 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; + setMode(choice); + return true; + } + this.modeId = this.values().iterator().next().getId(); + return true; + } + + public String getText(Ability source) { + StringBuilder sb = new StringBuilder(); + sb.append("Choose one - "); + for (Mode mode: this.values()) { + sb.append(mode.getEffects().getText(source)).append("; or "); + } + sb.delete(sb.length() - 5, sb.length()); + return sb.toString(); + } + + public String getText(Ability source, String sourceName) { + StringBuilder sb = new StringBuilder(); + sb.append("Choose one - "); + for (Mode mode: this.values()) { + sb.append(mode.getEffects().getText(source)).append("; or "); + } + sb.delete(sb.length() - 5, sb.length()); + String text = sb.toString(); + text = text.replace("{this}", sourceName); + text = text.replace("{source}", sourceName); + return text; + } + +} diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index 647927f40c5..d50a488e78b 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -61,7 +61,7 @@ public class SpellAbility extends ActivatedAbilityImpl { object.getAbilities().containsKey(FlashAbility.getInstance().getId()) || game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST, game) || game.canPlaySorcery(playerId))) { - if (costs.canPay(sourceId, controllerId, game) && targets.canChoose(sourceId, playerId, game)) { + if (costs.canPay(sourceId, controllerId, game) && getTargets().canChoose(sourceId, playerId, game)) { return true; } } @@ -82,8 +82,8 @@ public class SpellAbility extends ActivatedAbilityImpl { } public void clear() { - this.choices.clearChosen(); - this.targets.clearChosen(); + getChoices().clearChosen(); + getTargets().clearChosen(); this.manaCosts.clearPaid(); this.costs.clearPaid(); } diff --git a/Mage/src/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/mage/abilities/TriggeredAbilityImpl.java index c7a12b59b1a..edccf881320 100644 --- a/Mage/src/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/mage/abilities/TriggeredAbilityImpl.java @@ -88,7 +88,7 @@ public abstract class TriggeredAbilityImpl> ex sb.append("Use ").append(this.getRule()).append("ability"); } sb.append("?"); - if (!player.chooseUse(this.effects.get(0).getOutcome(), sb.toString(), game)) { + if (!player.chooseUse(getEffects().get(0).getOutcome(), sb.toString(), game)) { return false; } } diff --git a/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java b/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java index 0677f55cff7..d0ac1651c75 100644 --- a/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java @@ -52,11 +52,11 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean activate(Game game, boolean noMana) { Player player = game.getPlayer(this.getControllerId()); - String message = "Use kicker - " + this.effects.get(0).getText(this) + "?"; + String message = "Use kicker - " + getEffects().get(0).getText(this) + "?"; Card card = game.getCard(sourceId); // replace by card name or just plain "this" message = message.replace("{this}", card == null ? "this" : card.getName()); - if (player.chooseUse(this.effects.get(0).getOutcome(), message, game)) { + if (player.chooseUse(getEffects().get(0).getOutcome(), message, game)) { game.bookmarkState(); if (super.activate(game, noMana)) { game.removeLastBookmark(); @@ -101,7 +101,7 @@ public class KickerAbility extends StaticAbility { } if (costs.size() > 0) sb.append(costs.getText()); - sb.append(":").append(effects.getText(this)); + sb.append(":").append(modes.getText(this)); if (replaces) sb.append(" instead"); return sb.toString(); diff --git a/Mage/src/mage/abilities/keyword/MultikickerAbility.java b/Mage/src/mage/abilities/keyword/MultikickerAbility.java index 9ebeaed0f6c..ce33acbb49b 100644 --- a/Mage/src/mage/abilities/keyword/MultikickerAbility.java +++ b/Mage/src/mage/abilities/keyword/MultikickerAbility.java @@ -87,7 +87,7 @@ public class MultikickerAbility extends KickerAbility { } if (costs.size() > 0) sb.append(costs.getText()); - sb.append(":").append(effects.getText(this)); + sb.append(":").append(modes.getText(this)); if (replaces) sb.append(" instead"); return sb.toString(); diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 01be8de434f..14c1fcbb83b 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -42,6 +42,7 @@ import mage.MageItem; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.Modes; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; import mage.abilities.effects.ContinuousEffect; @@ -128,6 +129,7 @@ public interface Game extends MageItem, Serializable { public void firePlayManaEvent(UUID playerId, String message); public void firePlayXManaEvent(UUID playerId, String message); public void fireGetChoiceEvent(UUID playerId, String message, Collection choices); + public void fireGetModeEvent(UUID playerId, String message, Map modes); public void fireGetAmountEvent(UUID playerId, String message, int min, int max); public void fireInformEvent(String message); public void fireUpdatePlayersEvent(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 5043bcad608..2c7bba16794 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -785,6 +785,11 @@ public abstract class GameImpl> implements Game, Serializa playerQueryEventSource.chooseAbility(playerId, message, choices); } + @Override + public void fireGetModeEvent(UUID playerId, String message, Map modes) { + playerQueryEventSource.chooseMode(playerId, message, modes); + } + @Override public void fireSelectTargetEvent(UUID playerId, String message, Set targets, boolean required, Map options) { playerQueryEventSource.target(playerId, message, targets, required, options); diff --git a/Mage/src/mage/game/events/PlayerQueryEvent.java b/Mage/src/mage/game/events/PlayerQueryEvent.java index a06615f7dbe..8b5cfd8775f 100644 --- a/Mage/src/mage/game/events/PlayerQueryEvent.java +++ b/Mage/src/mage/game/events/PlayerQueryEvent.java @@ -33,6 +33,7 @@ import java.util.*; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; +import mage.abilities.Modes; import mage.abilities.TriggeredAbilities; import mage.cards.Card; import mage.cards.Cards; @@ -46,7 +47,7 @@ import mage.game.permanent.Permanent; public class PlayerQueryEvent extends EventObject implements ExternalEvent, Serializable { public enum QueryType { - ASK, CHOOSE, CHOOSE_ABILITY, PICK_TARGET, PICK_ABILITY, SELECT, PLAY_MANA, PLAY_X_MANA, AMOUNT, LOOK, PICK_CARD, CONSTRUCT + ASK, CHOOSE, CHOOSE_ABILITY, CHOOSE_MODE, PICK_TARGET, PICK_ABILITY, SELECT, PLAY_MANA, PLAY_X_MANA, AMOUNT, LOOK, PICK_CARD, CONSTRUCT } private String message; @@ -63,6 +64,7 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri private int max; private Deck deck; private Map options; + private Map modes; private PlayerQueryEvent(UUID playerId, String message, Collection abilities, Set choices, Set targets, Cards cards, QueryType queryType, int min, int max, boolean required, Map options) { this(playerId, message, abilities, choices, targets, cards, queryType, min, max, required); @@ -110,6 +112,14 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri this.required = required; } + private PlayerQueryEvent(UUID playerId, String message, Map modes) { + super(playerId); + this.queryType = QueryType.CHOOSE_MODE; + this.message = message; + this.playerId = playerId; + this.modes = modes; + } + public static PlayerQueryEvent askEvent(UUID playerId, String message) { return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.ASK, 0, 0, false); } @@ -117,6 +127,11 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri public static PlayerQueryEvent chooseAbilityEvent(UUID playerId, String message, Collection choices) { return new PlayerQueryEvent(playerId, message, choices, null, null, null, QueryType.CHOOSE_ABILITY, 0, 0, false); } + + public static PlayerQueryEvent chooseModeEvent(UUID playerId, String message, Map modes) { + return new PlayerQueryEvent(playerId, message, modes); + } + public static PlayerQueryEvent chooseEvent(UUID playerId, String message, Set choices) { return new PlayerQueryEvent(playerId, message, null, choices, null, null, QueryType.CHOOSE, 0, 0, false); } @@ -225,4 +240,8 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri public Map getOptions() { return options; } + + public Map getModes() { + return modes; + } } diff --git a/Mage/src/mage/game/events/PlayerQueryEventSource.java b/Mage/src/mage/game/events/PlayerQueryEventSource.java index 9301f140147..3e10123dfda 100644 --- a/Mage/src/mage/game/events/PlayerQueryEventSource.java +++ b/Mage/src/mage/game/events/PlayerQueryEventSource.java @@ -32,6 +32,8 @@ import java.io.Serializable; import java.util.*; import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.Modes; import mage.abilities.TriggeredAbilities; import mage.cards.Card; import mage.cards.Cards; @@ -63,6 +65,10 @@ public class PlayerQueryEventSource implements EventSource, Se dispatcher.fireEvent(PlayerQueryEvent.chooseAbilityEvent(playerId, message, choices)); } + public void chooseMode(UUID playerId, String message, Map modes) { + dispatcher.fireEvent(PlayerQueryEvent.chooseModeEvent(playerId, message, modes)); + } + public void target(UUID playerId, String message, Set targets, boolean required) { dispatcher.fireEvent(PlayerQueryEvent.targetEvent(playerId, message, targets, required)); } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 55f9f27ffb3..92c52ce7360 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -30,6 +30,8 @@ package mage.game.stack; import java.util.ArrayList; import mage.Constants.AbilityType; +import mage.abilities.Mode; +import mage.abilities.Modes; import mage.abilities.costs.AlternativeCost; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; @@ -288,9 +290,7 @@ public class StackAbility implements StackObject, Ability { } @Override - public void addOptionalCost(Cost cost) { - throw new UnsupportedOperationException("Not supported yet."); - } + public void addOptionalCost(Cost cost) {} @Override public boolean checkIfClause(Game game) { @@ -298,11 +298,22 @@ public class StackAbility implements StackObject, Ability { } @Override - public void newId() { - throw new UnsupportedOperationException("Not supported yet."); - } + public void newId() {} public Ability getStackAbility() { return ability; } + + @Override + public boolean isModal() { + return ability.isModal(); + } + + @Override + public void addMode(Mode mode) {} + + @Override + public Modes getModes() { + return ability.getModes(); + } } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index c0a14267df0..3e5eb98c26f 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -39,6 +39,8 @@ import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.Modes; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; @@ -156,6 +158,7 @@ public interface Player extends MageItem, Copyable { public abstract boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game); public abstract int chooseEffect(List rEffects, Game game); public abstract TriggeredAbility chooseTriggeredAbility(TriggeredAbilities abilities, Game game); + public abstract Mode chooseMode(Modes modes, Ability source, Game game); public abstract void selectAttackers(Game game); public abstract void selectBlockers(Game game); public abstract UUID chooseBlockerOrder(List blockers, Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 213a9ef1e78..1641c2b6646 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -46,6 +46,7 @@ import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; import mage.abilities.PlayLandAbility; import mage.abilities.SpecialAction; import mage.abilities.SpellAbility; @@ -936,8 +937,10 @@ public abstract class PlayerImpl> implements Player, Ser @Override public List getPlayableOptions(Ability ability, Game game) { List options = new ArrayList(); - - if (ability.getTargets().getUnchosen().size() > 0) + + if (ability.isModal()) + addModeOptions(options, ability, game); + else if (ability.getTargets().getUnchosen().size() > 0) addTargetOptions(options, ability, 0, game); else if (ability.getChoices().getUnchosen().size() > 0) addChoiceOptions(options, ability, 0, game); @@ -947,6 +950,21 @@ public abstract class PlayerImpl> implements Player, Ser return options; } + private void addModeOptions(List options, Ability option, Game game) { + for (Mode mode: option.getModes().values()) { + Ability newOption = option.copy(); + newOption.getModes().setMode(mode); + if (option.getTargets().getUnchosen().size() > 0) + addTargetOptions(options, option, 0, game); + else if (option.getChoices().getUnchosen().size() > 0) + addChoiceOptions(options, option, 0, game); + else if (option.getCosts().getTargets().getUnchosen().size() > 0) + addCostTargetOptions(options, option, 0, game); + else + options.add(newOption); + } + } + private void addTargetOptions(List options, Ability option, int targetNum, Game game) { for (UUID targetId: option.getTargets().getUnchosen().get(targetNum).possibleTargets(option.getSourceId(), playerId, game)) { Ability newOption = option.copy();