added modal abilities + Slagstorm

This commit is contained in:
BetaSteward 2011-07-20 13:42:27 -04:00
parent 3a0b73cee9
commit 14d863a8f2
38 changed files with 484 additions and 60 deletions

View file

@ -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<UUID, String> modes) {
this.choices = modes;
}
public Map<UUID, String> getChoices() {
return choices;
}

View file

@ -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<T extends ComputerPlayer<T>> extends PlayerImpl<T> 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");

View file

@ -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<HumanPlayer> {
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<UUID, String> modeMap = new HashMap<UUID, String>();
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) {

View file

@ -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<UUID, String> 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<String> choices) throws MageException {
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).choose(message, choices);

View file

@ -95,7 +95,7 @@ class FinestHourAbility extends TriggeredAbilityImpl<FinestHourAbility> {
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;
}
}

View file

@ -83,7 +83,7 @@ class FontOfMythosAbility extends TriggeredAbilityImpl<FontOfMythosAbility> {
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;

View file

@ -101,7 +101,7 @@ class WallOfReverenceTriggeredAbility extends TriggeredAbilityImpl<WallOfReveren
@Override
public String getRule() {
return "At the beginning of your end step, " + effects.getText(this);
return "At the beginning of your end step, " + modes.getText(this);
}
}

View file

@ -82,7 +82,7 @@ class HowlingMineAbility extends TriggeredAbilityImpl<HowlingMineAbility> {
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;

View file

@ -83,7 +83,7 @@ class LilianasCaressAbility extends TriggeredAbilityImpl<LilianasCaressAbility>
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;

View file

@ -89,7 +89,7 @@ class WildEvocationAbility extends TriggeredAbilityImpl<WildEvocationAbility> {
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;

View file

@ -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<Slagstorm> {
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> {
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";
}
}

View file

@ -100,7 +100,7 @@ class SuturePriestFirstTriggeredAbility extends TriggeredAbilityImpl<SuturePries
@Override
public String getRule() {
return "Whenever another creature enters the battlefield under your control, " + effects.getText(this);
return "Whenever another creature enters the battlefield under your control, " + modes.getText(this);
}
}

View file

@ -114,7 +114,7 @@ class SwordOfWarAndPeaceAbility extends TriggeredAbilityImpl<SwordOfWarAndPeaceA
DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event;
Permanent p = game.getPermanent(event.getSourceId());
if (damageEvent.isCombatDamage() && p != null && p.getAttachments().contains(this.getSourceId())) {
this.targets.get(0).add(event.getPlayerId(), game);
getTargets().get(0).add(event.getPlayerId(), game);
return true;
}
}

View file

@ -115,7 +115,7 @@ class SwordOfBodyAndMindAbility extends TriggeredAbilityImpl<SwordOfBodyAndMindA
DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event;
Permanent p = game.getPermanent(event.getSourceId());
if (damageEvent.isCombatDamage() && p != null && p.getAttachments().contains(this.getSourceId())) {
this.targets.get(0).add(event.getPlayerId(), game);
getTargets().get(0).add(event.getPlayerId(), game);
return true;
}
}

View file

@ -96,7 +96,7 @@ class RafiqOfTheManyAbility extends TriggeredAbilityImpl<RafiqOfTheManyAbility>
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;
}
}

View file

@ -99,7 +99,7 @@ class HammerOfRuinTriggeredAbility extends TriggeredAbilityImpl<HammerOfRuinTrig
DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event;
Permanent p = game.getPermanent(event.getSourceId());
if (damageEvent.isCombatDamage() && p != null && p.getAttachments().contains(this.getSourceId())) {
FilterPermanent filter = (FilterPermanent) targets.get(0).getFilter();
FilterPermanent filter = (FilterPermanent)getTargets().get(0).getFilter();
filter.getControllerId().add(event.getPlayerId());
return true;
}

View file

@ -330,4 +330,11 @@ public interface Ability extends Serializable {
* @return A new copy of this ability.
*/
public Ability copy();
public boolean isModal();
public void addMode(Mode mode);
public Modes getModes();
}

View file

@ -73,9 +73,7 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
protected Costs<Cost> costs;
protected ArrayList<AlternativeCost> alternativeCosts = new ArrayList<AlternativeCost>();
protected Costs<Cost> 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<T extends AbilityImpl<T>> implements Ability {
this.manaCostsToPay = new ManaCostsImpl<ManaCost>();
this.costs = new CostsImpl<Cost>();
this.optionalCosts = new CostsImpl<Cost>();
this.effects = new Effects();
this.targets = new Targets();
this.choices = new Choices();
this.modes = new Modes();
}
public AbilityImpl(final AbilityImpl<T> ability) {
@ -113,9 +109,7 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> 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<T extends AbilityImpl<T>> 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<T extends AbilityImpl<T>> 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<T extends AbilityImpl<T>> implements Ability {
@Override
public Choices getChoices() {
return choices;
return modes.getMode().getChoices();
}
@Override
@ -323,7 +320,7 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
}
}
sbRule.append(effects.getText(this));
sbRule.append(modes.getText(this));
return sbRule.toString();
}
@ -371,34 +368,49 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> 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();

View file

@ -148,7 +148,7 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> 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<T extends ActivatedAbilityImpl<T>> 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));
}
}

View file

@ -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;
}
}

View file

@ -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<UUID, Mode> {
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;
}
}

View file

@ -61,7 +61,7 @@ public class SpellAbility extends ActivatedAbilityImpl<SpellAbility> {
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<SpellAbility> {
}
public void clear() {
this.choices.clearChosen();
this.targets.clearChosen();
getChoices().clearChosen();
getTargets().clearChosen();
this.manaCosts.clearPaid();
this.costs.clearPaid();
}

View file

@ -88,7 +88,7 @@ public abstract class TriggeredAbilityImpl<T extends TriggeredAbilityImpl<T>> 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;
}
}

View file

@ -52,11 +52,11 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl<Begi
public String getRule() {
switch (targetController) {
case YOU:
return "At the beginning of your upkeep, " + effects.getText(this);
return "At the beginning of your upkeep, " + getEffects().getText(this);
case OPPONENT:
return "At the beginning of each opponent's upkeep, " + effects.getText(this);
return "At the beginning of each opponent's upkeep, " + getEffects().getText(this);
case ANY:
return "At the beginning of each player's upkeep, " + effects.getText(this);
return "At the beginning of each player's upkeep, " + getEffects().getText(this);
}
return "";
}

View file

@ -58,9 +58,9 @@ public class DealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbility
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == EventType.DAMAGED_PLAYER && event.getSourceId().equals(this.sourceId)) {
this.targets.clear();
getTargets().clear();
this.addTarget(new TargetPlayer());
this.targets.get(0).add(event.getPlayerId(), game);
getTargets().get(0).add(event.getPlayerId(), game);
return true;
}
return false;

View file

@ -29,6 +29,6 @@ public class AtTheEndOfCombatDelayedTriggeredAbility extends DelayedTriggeredAbi
@Override
public String getRule() {
return "At end of combat, " + effects.getText(this);
return "At end of combat, " + modes.getText(this);
}
}

View file

@ -21,7 +21,7 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl<Conditiona
public ConditionalTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
super(ability.getZone(), null);
this.ability = ability;
this.effects = ability.getEffects();
this.modes = ability.getModes();
this.condition = condition;
this.text = text;
}
@ -43,8 +43,8 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl<Conditiona
ability.setSourceId(this.getSourceId());
if (ability.checkTrigger(event, game)) {
if (condition.apply(game, this)) {
this.targets.clear();
this.targets.addAll(ability.getTargets());
getTargets().clear();
getTargets().addAll(ability.getTargets());
return true;
}
}

View file

@ -63,11 +63,11 @@ public class KickerAbility extends StaticAbility<KickerAbility> {
@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<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();

View file

@ -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();

View file

@ -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<? extends ActivatedAbility> choices);
public void fireGetModeEvent(UUID playerId, String message, Map<UUID, String> modes);
public void fireGetAmountEvent(UUID playerId, String message, int min, int max);
public void fireInformEvent(String message);
public void fireUpdatePlayersEvent();

View file

@ -785,6 +785,11 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
playerQueryEventSource.chooseAbility(playerId, message, choices);
}
@Override
public void fireGetModeEvent(UUID playerId, String message, Map<UUID, String> modes) {
playerQueryEventSource.chooseMode(playerId, message, modes);
}
@Override
public void fireSelectTargetEvent(UUID playerId, String message, Set<UUID> targets, boolean required, Map<String, Serializable> options) {
playerQueryEventSource.target(playerId, message, targets, required, options);

View file

@ -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<String, Serializable> options;
private Map<UUID, String> modes;
private PlayerQueryEvent(UUID playerId, String message, Collection<? extends Ability> abilities, Set<String> choices, Set<UUID> targets, Cards cards, QueryType queryType, int min, int max, boolean required, Map<String, Serializable> 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<UUID, String> 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<? extends ActivatedAbility> 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<UUID, String> modes) {
return new PlayerQueryEvent(playerId, message, modes);
}
public static PlayerQueryEvent chooseEvent(UUID playerId, String message, Set<String> 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<String, Serializable> getOptions() {
return options;
}
public Map<UUID, String> getModes() {
return modes;
}
}

View file

@ -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<PlayerQueryEvent>, Se
dispatcher.fireEvent(PlayerQueryEvent.chooseAbilityEvent(playerId, message, choices));
}
public void chooseMode(UUID playerId, String message, Map<UUID, String> modes) {
dispatcher.fireEvent(PlayerQueryEvent.chooseModeEvent(playerId, message, modes));
}
public void target(UUID playerId, String message, Set<UUID> targets, boolean required) {
dispatcher.fireEvent(PlayerQueryEvent.targetEvent(playerId, message, targets, required));
}

View file

@ -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();
}
}

View file

@ -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<Player> {
public abstract boolean playXMana(VariableManaCost cost, ManaCosts<ManaCost> costs, Game game);
public abstract int chooseEffect(List<ReplacementEffect> 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<Permanent> blockers, Game game);

View file

@ -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<T extends PlayerImpl<T>> implements Player, Ser
@Override
public List<Ability> getPlayableOptions(Ability ability, Game game) {
List<Ability> options = new ArrayList<Ability>();
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<T extends PlayerImpl<T>> implements Player, Ser
return options;
}
private void addModeOptions(List<Ability> 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<Ability> options, Ability option, int targetNum, Game game) {
for (UUID targetId: option.getTargets().getUnchosen().get(targetNum).possibleTargets(option.getSourceId(), playerId, game)) {
Ability newOption = option.copy();