Changed ability handling of modal spells to be able to select the same mode multiple times with different targets.

This commit is contained in:
LevelX2 2015-11-14 01:56:56 +01:00
parent b18cae5100
commit 4711e0cf99
40 changed files with 488 additions and 421 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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