mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 21:12:04 -08:00
Changed ability handling of modal spells to be able to select the same mode multiple times with different targets.
This commit is contained in:
parent
b18cae5100
commit
4711e0cf99
40 changed files with 488 additions and 421 deletions
|
|
@ -155,7 +155,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
subAbilities.add(subAbility.copy());
|
||||
}
|
||||
}
|
||||
this.modes = ability.modes.copy();
|
||||
this.modes = ability.getModes().copy();
|
||||
this.ruleAtTheTop = ability.ruleAtTheTop;
|
||||
this.ruleVisible = ability.ruleVisible;
|
||||
this.ruleAdditionalCostsVisible = ability.ruleAdditionalCostsVisible;
|
||||
|
|
@ -196,6 +196,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
boolean result = true;
|
||||
//20100716 - 117.12
|
||||
if (checkIfClause(game)) {
|
||||
|
||||
for (Effect effect : getEffects()) {
|
||||
if (effect instanceof OneShotEffect) {
|
||||
boolean effectResult = effect.apply(game, this);
|
||||
|
|
@ -255,9 +256,14 @@ public abstract class AbilityImpl implements Ability {
|
|||
/* 20130201 - 601.2b
|
||||
* If the spell is modal the player announces the mode choice (see rule 700.2).
|
||||
*/
|
||||
if (!modes.choose(game, this)) {
|
||||
if (!getModes().choose(game, this)) {
|
||||
return false;
|
||||
}
|
||||
if (controller.isTestMode()) {
|
||||
if (!controller.addTargets(this, game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getSourceObject(game);
|
||||
|
||||
|
|
@ -274,9 +280,8 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
// TODO: Because all (non targeted) choices have to be done during resolution
|
||||
// this has to be removed, if all using effects are changed
|
||||
for (UUID modeId : this.getModes().getSelectedModes()) {
|
||||
this.getModes().setActiveMode(modeId);
|
||||
if (getChoices().size() > 0 && getChoices().choose(game, this) == false) {
|
||||
for (Mode mode : this.getModes().getSelectedModes()) {
|
||||
if (mode.getChoices().size() > 0 && mode.getChoices().choose(game, this) == false) {
|
||||
logger.debug("activate failed - choice");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -313,8 +318,8 @@ public abstract class AbilityImpl implements Ability {
|
|||
VariableManaCost variableManaCost = handleManaXCosts(game, noMana, controller);
|
||||
String announceString = handleOtherXCosts(game, controller);
|
||||
|
||||
for (UUID modeId : this.getModes().getSelectedModes()) {
|
||||
this.getModes().setActiveMode(modeId);
|
||||
for (Mode mode : this.getModes().getSelectedModes()) {
|
||||
this.getModes().setActiveMode(mode);
|
||||
//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
|
||||
|
|
@ -335,7 +340,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (sourceObject != null && !this.getAbilityType().equals(AbilityType.TRIGGERED)) { // triggered abilities check this already in playerImpl.triggerAbility
|
||||
sourceObject.adjustTargets(this, game);
|
||||
}
|
||||
if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) {
|
||||
if (mode.getTargets().size() > 0 && mode.getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) {
|
||||
if ((variableManaCost != null || announceString != null) && !game.isSimulation()) {
|
||||
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X");
|
||||
}
|
||||
|
|
@ -408,7 +413,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
if (variableManaCost != null) {
|
||||
int xValue = getManaCostsToPay().getX();
|
||||
game.informPlayers(new StringBuilder(controller.getLogName()).append(" announces a value of ").append(xValue).append(" for ").append(variableManaCost.getText()).toString());
|
||||
game.informPlayers(controller.getLogName() + " announces a value of " + xValue + " for " + variableManaCost.getText());
|
||||
}
|
||||
}
|
||||
activated = true;
|
||||
|
|
@ -681,7 +686,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public Effects getEffects() {
|
||||
return modes.getMode().getEffects();
|
||||
return getModes().getMode().getEffects();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -706,7 +711,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public Choices getChoices() {
|
||||
return modes.getMode().getChoices();
|
||||
return getModes().getMode().getChoices();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -781,7 +786,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
|
||||
String ruleStart = sbRule.toString();
|
||||
String text = modes.getText();
|
||||
String text = getModes().getText();
|
||||
String rule;
|
||||
if (!text.isEmpty()) {
|
||||
if (ruleStart.length() > 1) {
|
||||
|
|
@ -873,7 +878,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public Targets getTargets() {
|
||||
return modes.getMode().getTargets();
|
||||
return getModes().getMode().getTargets();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -883,12 +888,12 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public boolean isModal() {
|
||||
return this.modes.size() > 1;
|
||||
return getModes().size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMode(Mode mode) {
|
||||
this.modes.addMode(mode);
|
||||
getModes().addMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -899,10 +904,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
@Override
|
||||
public boolean canChooseTarget(Game game) {
|
||||
int found = 0;
|
||||
for (Mode mode : modes.values()) {
|
||||
for (Mode mode : getModes().values()) {
|
||||
if (mode.getTargets().canChoose(sourceId, controllerId, game)) {
|
||||
found++;
|
||||
if (modes.isEachModeMoreThanOnce()) {
|
||||
if (getModes().isEachModeMoreThanOnce()) {
|
||||
return true;
|
||||
}
|
||||
if (found >= getModes().getMinModes()) {
|
||||
|
|
@ -1037,7 +1042,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
logger.warn("Could get no object: " + this.toString());
|
||||
}
|
||||
return new StringBuilder(" activates: ")
|
||||
.append(object != null ? this.formatRule(modes.getText(), object.getLogName()) : modes.getText())
|
||||
.append(object != null ? this.formatRule(getModes().getText(), object.getLogName()) : getModes().getText())
|
||||
.append(" from ")
|
||||
.append(getMessageText(game)).toString();
|
||||
}
|
||||
|
|
@ -1106,14 +1111,13 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
} else if (object instanceof Spell && ((Spell) object).getSpellAbility().getModes().size() > 1) {
|
||||
Modes spellModes = ((Spell) object).getSpellAbility().getModes();
|
||||
for (UUID modeId : spellModes.getSelectedModes()) {
|
||||
for (Mode selectedMode : spellModes.getSelectedModes()) {
|
||||
int item = 0;
|
||||
for (Mode mode : spellModes.values()) {
|
||||
item++;
|
||||
if (mode.getId().equals(modeId)) {
|
||||
spellModes.setActiveMode(mode.getId());
|
||||
if (mode.getId().equals(selectedMode.getId())) {
|
||||
sb.append(" (mode ").append(item).append(")");
|
||||
sb.append(getTargetDescriptionForLog(getTargets(), game));
|
||||
sb.append(getTargetDescriptionForLog(selectedMode.getTargets(), game));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ import mage.util.CardUtil;
|
|||
*/
|
||||
public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||
|
||||
private UUID modeId;
|
||||
private final ArrayList<UUID> selectedModes = new ArrayList<>();
|
||||
private Mode mode; // the current mode of the selected modes
|
||||
private final ArrayList<Mode> selectedModes = new ArrayList<>();
|
||||
private int minModes;
|
||||
private int maxModes;
|
||||
private TargetController modeChooser;
|
||||
|
|
@ -58,25 +58,40 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
|
||||
|
||||
public Modes() {
|
||||
Mode mode = new Mode();
|
||||
this.mode = new Mode();
|
||||
this.put(mode.getId(), mode);
|
||||
this.modeId = mode.getId();
|
||||
this.minModes = 1;
|
||||
this.maxModes = 1;
|
||||
this.selectedModes.add(modeId);
|
||||
this.selectedModes.add(mode);
|
||||
this.modeChooser = TargetController.YOU;
|
||||
this.eachModeOnlyOnce = false;
|
||||
this.eachModeMoreThanOnce = false;
|
||||
}
|
||||
|
||||
public Modes(final Modes modes) {
|
||||
this.modeId = modes.modeId;
|
||||
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);
|
||||
|
||||
if (modes.size() == 1) {
|
||||
this.mode = values().iterator().next();
|
||||
this.selectedModes.add(mode);
|
||||
} else {
|
||||
// probably there is still a problem with copying modes with the same mode selected multiple times.
|
||||
for (Mode selectedMode : modes.getSelectedModes()) {
|
||||
Mode copiedMode = selectedMode.copy();
|
||||
this.selectedModes.add(copiedMode);
|
||||
if (modes.getSelectedModes().size() == 1) {
|
||||
this.mode = copiedMode;
|
||||
} else {
|
||||
if (selectedMode.equals(modes.getMode())) {
|
||||
this.mode = copiedMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.modeChooser = modes.modeChooser;
|
||||
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
|
||||
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
|
||||
|
|
@ -87,10 +102,21 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return get(modeId);
|
||||
return mode;
|
||||
}
|
||||
|
||||
public ArrayList<UUID> getSelectedModes() {
|
||||
public UUID getModeId(int index) {
|
||||
int idx = 0;
|
||||
for (Mode currentMode : this.values()) {
|
||||
idx++;
|
||||
if (idx == index) {
|
||||
return currentMode.getId();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ArrayList<Mode> getSelectedModes() {
|
||||
return selectedModes;
|
||||
}
|
||||
|
||||
|
|
@ -118,16 +144,9 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
return this.modeChooser;
|
||||
}
|
||||
|
||||
public void setActiveMode(UUID modeId) {
|
||||
if (selectedModes.contains(modeId)) {
|
||||
this.modeId = modeId;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
if (this.containsKey(mode.getId())) {
|
||||
this.modeId = mode.getId();
|
||||
this.selectedModes.add(mode.getId());
|
||||
public void setActiveMode(Mode mode) {
|
||||
if (selectedModes.contains(mode)) {
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +175,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
for (Mode mode : this.values()) {
|
||||
if ((!isEachModeOnlyOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId()))
|
||||
&& mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) {
|
||||
this.selectedModes.add(mode.getId());
|
||||
this.selectedModes.add(mode.copy());
|
||||
}
|
||||
}
|
||||
if (isEachModeOnlyOnce()) {
|
||||
|
|
@ -184,6 +203,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
Player player = game.getPlayer(playerId);
|
||||
|
||||
// player chooses modes manually
|
||||
this.mode = null;
|
||||
while (this.selectedModes.size() < this.getMaxModes()) {
|
||||
Mode choice = player.chooseMode(this, source, game);
|
||||
if (choice == null) {
|
||||
|
|
@ -192,29 +212,38 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
}
|
||||
return this.selectedModes.size() >= this.getMinModes();
|
||||
}
|
||||
setMode(choice);
|
||||
this.selectedModes.add(choice.copy());
|
||||
if (mode == null) {
|
||||
mode = choice;
|
||||
}
|
||||
}
|
||||
if (isEachModeOnlyOnce()) {
|
||||
setAlreadySelectedModes(selectedModes, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.modeId = this.values().iterator().next().getId();
|
||||
this.selectedModes.clear();
|
||||
this.selectedModes.add(modeId);
|
||||
if (mode == null) {
|
||||
this.selectedModes.clear();
|
||||
Mode copiedMode = this.values().iterator().next().copy();
|
||||
this.selectedModes.add(copiedMode);
|
||||
this.setActiveMode(copiedMode);
|
||||
}
|
||||
if (isEachModeOnlyOnce()) {
|
||||
setAlreadySelectedModes(selectedModes, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setAlreadySelectedModes(ArrayList<UUID> selectedModes, Ability source, Game game) {
|
||||
private void setAlreadySelectedModes(ArrayList<Mode> selectedModes, Ability source, Game game) {
|
||||
String key = getKey(source, game);
|
||||
Set<UUID> onceSelectedModes = (Set<UUID>) game.getState().getValue(key);
|
||||
if (onceSelectedModes == null) {
|
||||
onceSelectedModes = new HashSet<>();
|
||||
}
|
||||
onceSelectedModes.addAll(selectedModes);
|
||||
for (Mode mode : selectedModes) {
|
||||
onceSelectedModes.add(mode.getId());
|
||||
}
|
||||
|
||||
game.getState().setValue(key, onceSelectedModes);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@
|
|||
*/
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getTargetId().equals(getSourceId());
|
||||
return event.getTargetId().equals(getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -81,7 +81,7 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (noRule) {
|
||||
return super.getRule();
|
||||
}
|
||||
return (rulePrefix != null ? rulePrefix : "") + "When {this} enters the battlefield, "+ super.getRule();
|
||||
return (rulePrefix != null ? rulePrefix : "") + "When {this} enters the battlefield, " + super.getRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package mage.abilities.effects.common;
|
|||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
|
@ -50,9 +51,8 @@ public class ChangeATargetOfTargetSpellAbilityToSourceEffect extends OneShotEffe
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
for (UUID modeId : sourceAbility.getModes().getSelectedModes()) {
|
||||
sourceAbility.getModes().setActiveMode(modeId);
|
||||
targets.addAll(sourceAbility.getTargets());
|
||||
for (Mode mode : sourceAbility.getModes().getSelectedModes()) {
|
||||
targets.addAll(mode.getTargets());
|
||||
}
|
||||
|
||||
boolean twoTimesTarget = false;
|
||||
|
|
|
|||
|
|
@ -1,43 +1,39 @@
|
|||
/*
|
||||
* 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.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
|
@ -49,7 +45,6 @@ import mage.util.CardUtil;
|
|||
*/
|
||||
public class ExileTargetForSourceEffect extends OneShotEffect {
|
||||
|
||||
|
||||
public ExileTargetForSourceEffect() {
|
||||
super(Outcome.Exile);
|
||||
}
|
||||
|
|
@ -71,11 +66,11 @@ public class ExileTargetForSourceEffect extends OneShotEffect {
|
|||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
|
||||
if (permanent != null) {
|
||||
return controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
|
||||
return controller.moveCardsToExile(permanent, source, game, true, exileId, sourceObject.getIdName());
|
||||
} else {
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
return controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source.getSourceId(), game, game.getState().getZone(card.getId()), true);
|
||||
return controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getIdName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,14 +79,14 @@ public class ExileTargetForSourceEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if(staticText != null && !staticText.isEmpty()) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
|
||||
if (mode.getTargets().isEmpty()) {
|
||||
return "Exile it";
|
||||
return "exile it";
|
||||
} else {
|
||||
return "Exile target " + mode.getTargets().get(0).getTargetName();
|
||||
return "exile target " + mode.getTargets().get(0).getTargetName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,14 @@
|
|||
*/
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
|
@ -70,7 +74,14 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
|
|||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
return controller.moveCards(new CardsImpl(targetPointer.getTargets(game, source)), null, Zone.HAND, source, game);
|
||||
Set<Card> cards = new LinkedHashSet<>();
|
||||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||
MageObject mageObject = game.getObject(targetId);
|
||||
if (mageObject instanceof Card) {
|
||||
cards.add((Card) mageObject);
|
||||
}
|
||||
}
|
||||
return controller.moveCards(cards, Zone.HAND, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -44,7 +44,6 @@ import mage.target.Target;
|
|||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class HeroicAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public HeroicAbility(Effect effect) {
|
||||
|
|
@ -83,19 +82,19 @@ public class HeroicAbility extends TriggeredAbilityImpl {
|
|||
private boolean checkSpell(Spell spell, Game game) {
|
||||
if (spell != null) {
|
||||
SpellAbility sa = spell.getSpellAbility();
|
||||
for(UUID modeId :sa.getModes().getSelectedModes()) {
|
||||
for (Target target : sa.getModes().get(modeId).getTargets()) {
|
||||
for (Mode mode : sa.getModes().getSelectedModes()) {
|
||||
for (Target target : mode.getTargets()) {
|
||||
if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (Effect effect : sa.getModes().get(modeId).getEffects()) {
|
||||
for (Effect effect : mode.getEffects()) {
|
||||
for (UUID targetId : effect.getTargetPointer().getTargets(game, sa)) {
|
||||
if (targetId.equals(this.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
*/
|
||||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Mode;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -52,8 +51,7 @@ public class NumberOfTargetsPredicate implements Predicate<MageObject> {
|
|||
Spell spell = game.getStack().getSpell(input.getId());
|
||||
if (spell != null) {
|
||||
int numberOfTargets = 0;
|
||||
for (UUID modeId : spell.getSpellAbility().getModes().getSelectedModes()) {
|
||||
Mode mode = spell.getSpellAbility().getModes().get(modeId);
|
||||
for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) {
|
||||
for (Target target : mode.getTargets()) {
|
||||
numberOfTargets += target.getTargets().size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,13 +53,12 @@ public class TargetsPermanentPredicate implements ObjectSourcePlayerPredicate<Ob
|
|||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
|
||||
StackObject object = game.getStack().getStackObject(input.getObject().getId());
|
||||
if(object != null) {
|
||||
for(UUID modeId : object.getStackAbility().getModes().getSelectedModes()) {
|
||||
Mode mode = object.getStackAbility().getModes().get(modeId);
|
||||
for(Target target : mode.getTargets()) {
|
||||
for(UUID targetId : target.getTargets()) {
|
||||
if (object != null) {
|
||||
for (Mode mode : object.getStackAbility().getModes().getSelectedModes()) {
|
||||
for (Target target : mode.getTargets()) {
|
||||
for (UUID targetId : target.getTargets()) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(targetId);
|
||||
if(permanent != null && targetFilter.match(permanent, input.getSourceId(), input.getPlayerId(), game)) {
|
||||
if (permanent != null && targetFilter.match(permanent, input.getSourceId(), input.getPlayerId(), game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
for (StackObject spell : stack) {
|
||||
sb.append(spell.getControllerId()).append(spell.getName());
|
||||
sb.append(spell.getStackAbility().toString());
|
||||
for (Mode mode : spell.getStackAbility().getModes().values()) {
|
||||
for (Mode mode : spell.getStackAbility().getModes().getSelectedModes()) {
|
||||
if (!mode.getTargets().isEmpty()) {
|
||||
sb.append("targets");
|
||||
for (Target target : mode.getTargets()) {
|
||||
|
|
@ -367,7 +367,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
for (StackObject spell : stack) {
|
||||
sb.append(spell.getControllerId()).append(spell.getName());
|
||||
sb.append(spell.getStackAbility().toString());
|
||||
for (Mode mode : spell.getStackAbility().getModes().values()) {
|
||||
for (Mode mode : spell.getStackAbility().getModes().getSelectedModes()) {
|
||||
if (!mode.getTargets().isEmpty()) {
|
||||
sb.append("targets");
|
||||
for (Target target : mode.getTargets()) {
|
||||
|
|
@ -709,7 +709,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
|
||||
public void addAbility(Ability ability, MageObject attachedTo) {
|
||||
if (ability instanceof StaticAbility) {
|
||||
for (Mode mode : ability.getModes().values()) {
|
||||
for (Mode mode : ability.getModes().getSelectedModes()) {
|
||||
for (Effect effect : mode.getEffects()) {
|
||||
if (effect instanceof ContinuousEffect) {
|
||||
addEffect((ContinuousEffect) effect, ability);
|
||||
|
|
@ -731,7 +731,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
*/
|
||||
public void addAbility(Ability ability, UUID sourceId, Card attachedTo) {
|
||||
if (ability instanceof StaticAbility) {
|
||||
for (Mode mode : ability.getModes().values()) {
|
||||
for (Mode mode : ability.getModes().getSelectedModes()) {
|
||||
for (Effect effect : mode.getEffects()) {
|
||||
if (effect instanceof ContinuousEffect) {
|
||||
addEffect((ContinuousEffect) effect, sourceId, ability);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import mage.Mana;
|
|||
import mage.ObjectColor;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.AlternativeSourceCosts;
|
||||
import mage.abilities.costs.Cost;
|
||||
|
|
@ -195,9 +196,9 @@ public class Spell extends StackObjImpl implements Card {
|
|||
if (notTargeted || legalParts) {
|
||||
for (SpellAbility spellAbility : this.spellAbilities) {
|
||||
if (spellAbilityHasLegalParts(spellAbility, game)) {
|
||||
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
|
||||
spellAbility.getModes().setActiveMode(modeId);
|
||||
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
|
||||
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
|
||||
spellAbility.getModes().setActiveMode(mode);
|
||||
if (mode.getTargets().stillLegal(spellAbility, game)) {
|
||||
if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
|
||||
updateOptionalCosts(index);
|
||||
}
|
||||
|
|
@ -268,9 +269,8 @@ public class Spell extends StackObjImpl implements Card {
|
|||
|
||||
private boolean hasTargets(SpellAbility spellAbility, Game game) {
|
||||
if (spellAbility.getModes().getSelectedModes().size() > 1) {
|
||||
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
|
||||
spellAbility.getModes().setActiveMode(modeId);
|
||||
if (!spellAbility.getTargets().isEmpty()) {
|
||||
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
|
||||
if (!mode.getTargets().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -285,11 +285,10 @@ public class Spell extends StackObjImpl implements Card {
|
|||
if (spellAbility.getModes().getSelectedModes().size() > 1) {
|
||||
boolean targetedMode = false;
|
||||
boolean legalTargetedMode = false;
|
||||
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
|
||||
spellAbility.getModes().setActiveMode(modeId);
|
||||
if (spellAbility.getTargets().size() > 0) {
|
||||
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
|
||||
if (mode.getTargets().size() > 0) {
|
||||
targetedMode = true;
|
||||
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
|
||||
if (mode.getTargets().stillLegal(spellAbility, game)) {
|
||||
legalTargetedMode = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,14 +86,14 @@ public class StackAbility extends StackObjImpl implements Ability {
|
|||
public StackAbility(Ability ability, UUID controllerId) {
|
||||
this.ability = ability;
|
||||
this.controllerId = controllerId;
|
||||
this.name = new StringBuilder("stack ability (").append(ability.getRule()).append(")").toString();
|
||||
this.name = "stack ability (" + ability.getRule() + ")";
|
||||
}
|
||||
|
||||
public StackAbility(final StackAbility spell) {
|
||||
this.ability = spell.ability.copy();
|
||||
this.controllerId = spell.controllerId;
|
||||
this.name = spell.name;
|
||||
this.expansionSetCode = spell.expansionSetCode;
|
||||
public StackAbility(final StackAbility stackAbility) {
|
||||
this.ability = stackAbility.ability.copy();
|
||||
this.controllerId = stackAbility.controllerId;
|
||||
this.name = stackAbility.name;
|
||||
this.expansionSetCode = stackAbility.expansionSetCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ public abstract class StackObjImpl implements StackObject {
|
|||
}
|
||||
for (Ability ability : objectAbilities) {
|
||||
// Some spells can have more than one mode
|
||||
for (UUID modeId : ability.getModes().getSelectedModes()) {
|
||||
Mode mode = ability.getModes().get(modeId);
|
||||
for (Mode mode : ability.getModes().getSelectedModes()) {
|
||||
ability.getModes().setActiveMode(mode);
|
||||
oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game));
|
||||
for (Target target : mode.getTargets()) {
|
||||
Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game);
|
||||
|
|
|
|||
|
|
@ -787,4 +787,13 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
MatchPlayer getMatchPlayer();
|
||||
|
||||
boolean scry(int value, Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Only used for test player for pre-setting targets
|
||||
*
|
||||
* @param ability
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
boolean addTargets(Ability ability, Game game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2706,7 +2706,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
for (Mode mode : option.getModes().values()) {
|
||||
Ability newOption = option.copy();
|
||||
newOption.getModes().getSelectedModes().clear();
|
||||
newOption.getModes().setMode(mode);
|
||||
newOption.getModes().getSelectedModes().add(mode);
|
||||
newOption.getModes().setActiveMode(mode);
|
||||
if (newOption.getTargets().getUnchosen().size() > 0) {
|
||||
if (newOption.getManaCosts().getVariableCosts().size() > 0) {
|
||||
addVariableXOptions(options, newOption, 0, game);
|
||||
|
|
@ -3448,4 +3449,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addTargets(Ability ability, Game game) {
|
||||
// only used for TestPlayer to preSet Targets
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ import mage.cards.Card;
|
|||
import mage.game.stack.Spell;
|
||||
import mage.target.Target;
|
||||
|
||||
|
||||
/**
|
||||
* @author duncant
|
||||
*/
|
||||
public class TargetAddress {
|
||||
|
||||
protected int spellAbilityIndex;
|
||||
protected UUID mode;
|
||||
protected int targetIndex;
|
||||
|
|
@ -52,8 +52,9 @@ public class TargetAddress {
|
|||
}
|
||||
|
||||
protected static class TargetAddressIterable implements Iterable<TargetAddress> {
|
||||
|
||||
protected final Card card;
|
||||
|
||||
|
||||
public TargetAddressIterable(Card card) {
|
||||
this.card = card;
|
||||
}
|
||||
|
|
@ -64,9 +65,10 @@ public class TargetAddress {
|
|||
}
|
||||
|
||||
protected static class TargetAddressIterator implements Iterator<TargetAddress> {
|
||||
|
||||
protected Iterator<SpellAbility> spellAbilityIterator;
|
||||
protected Integer lastSpellAbilityIndex = null;
|
||||
protected Iterator<UUID> modeIterator = null;
|
||||
protected Iterator<Mode> modeIterator = null;
|
||||
protected Modes modes = null;
|
||||
protected UUID lastMode = null;
|
||||
protected Iterator<Target> targetIterator = null;
|
||||
|
|
@ -88,14 +90,14 @@ public class TargetAddress {
|
|||
public boolean hasNext() {
|
||||
return lastTargetIndex != null;
|
||||
}
|
||||
|
||||
|
||||
public TargetAddress next() {
|
||||
TargetAddress ret = new TargetAddress(lastSpellAbilityIndex,
|
||||
lastMode,
|
||||
lastTargetIndex);
|
||||
lastMode,
|
||||
lastTargetIndex);
|
||||
calcNext();
|
||||
return ret;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
|
|
@ -118,9 +120,9 @@ public class TargetAddress {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (modeIterator != null && modeIterator.hasNext()) {
|
||||
lastMode = modeIterator.next();
|
||||
lastMode = modeIterator.next().getId();
|
||||
targetIterator = modes.get(lastMode).getTargets().iterator();
|
||||
} else {
|
||||
lastMode = null;
|
||||
|
|
@ -145,7 +147,6 @@ public class TargetAddress {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static Iterable<TargetAddress> walk(Card card) {
|
||||
return new TargetAddressIterable(card);
|
||||
}
|
||||
|
|
@ -179,8 +180,8 @@ public class TargetAddress {
|
|||
|
||||
public boolean equals(TargetAddress other) {
|
||||
return spellAbilityIndex == other.spellAbilityIndex
|
||||
&& mode.equals(other.mode)
|
||||
&& targetIndex == other.targetIndex;
|
||||
&& mode.equals(other.mode)
|
||||
&& targetIndex == other.targetIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue