* Fixed a bug of spell copy that caused that added spliced spells were not copied.

This commit is contained in:
LevelX2 2016-02-14 13:42:46 +01:00
parent 1835671f3d
commit 6726f48669
25 changed files with 198 additions and 199 deletions

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.ArrayList;
@ -54,14 +53,15 @@ import mage.util.TargetAddress;
/**
* @author duncant
* @param <T>
*/
public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> extends OneShotEffect {
protected final FilterInPlay<T> filter;
public CopySpellForEachItCouldTargetEffect(FilterInPlay<T> filter) {
super(Outcome.Copy);
this.staticText = "copy the spell for each other "+filter.getMessage()+" that spell could target. Each copy targets a different one";
this.staticText = "copy the spell for each other " + filter.getMessage() + " that spell could target. Each copy targets a different one";
this.filter = filter;
}
@ -71,10 +71,12 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
}
protected abstract Spell getSpell(Game game, Ability source);
protected abstract boolean changeTarget(Target target, Game game, Ability source);
protected abstract void modifyCopy(Spell copy, Game game, Ability source);
protected void modifyCopy(Spell copy, T newTarget, Game game, Ability source){
protected void modifyCopy(Spell copy, T newTarget, Game game, Ability source) {
modifyCopy(copy, game, source);
}
@ -97,18 +99,17 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
targetsToBeChanged.add(addr);
}
}
if (targetsToBeChanged.size() < 1) {
return false;
}
// collect objects that can be targeted
Spell copy = spell.copySpell();
copy.setCopiedSpell(true);
Spell copy = spell.copySpell(source.getControllerId());
modifyCopy(copy, game, source);
Target sampleTarget = targetsToBeChanged.iterator().next().getTarget(copy);
sampleTarget.setNotTarget(true);
Map<UUID, Map<UUID, Spell>> playerTargetCopyMap = new HashMap<>();
for (UUID objId : sampleTarget.possibleTargets(controller.getId(), game)) {
MageItem obj = game.getObject(objId);
@ -116,9 +117,7 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
obj = game.getPlayer(objId);
}
if (obj != null) {
copy = spell.copySpell();
copy.setCopiedSpell(true);
copy = spell.copySpell(source.getControllerId());
try {
modifyCopy(copy, (T) obj, game, source);
if (!filter.match((T) obj, game)) {
@ -174,7 +173,7 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
// shortcut if there's only one possible target remaining
if (targetCopyMap.size() > 1
&& target.canChoose(spell.getId(), player.getId(), game)) {
&& target.canChoose(spell.getId(), player.getId(), game)) {
player.choose(Outcome.Neutral, target, spell.getId(), game);
}
Collection<UUID> chosenIds = target.getTargets();
@ -205,8 +204,6 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
}
}
class CompoundFilter<T extends MageItem> extends FilterImpl<T> implements FilterInPlay<T> {
protected final FilterInPlay<T> filterA;
@ -228,7 +225,7 @@ class CompoundFilter<T extends MageItem> extends FilterImpl<T> implements Filter
public boolean match(T obj, Game game) {
return (filterA == null
|| !filterA.match(obj, game))
&& (filterB == null
&& (filterB == null
|| !filterB.match(obj, game));
}
@ -236,36 +233,35 @@ class CompoundFilter<T extends MageItem> extends FilterImpl<T> implements Filter
public boolean match(T obj, UUID sourceId, UUID playerId, Game game) {
return (filterA == null
|| !filterA.match(obj, sourceId, playerId, game))
&& (filterB == null
&& (filterB == null
|| !filterB.match(obj, sourceId, playerId, game));
}
@Override
public CompoundFilter copy() {
return new CompoundFilter(filterA == null ? null : filterA.copy(),
filterB == null ? null : filterB.copy(),
message);
filterB == null ? null : filterB.copy(),
message);
}
}
class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
protected final FilterInPlay<T> additionalFilter;
protected final Target originalTarget;
protected static final Integer minNumberOfTargets = null;
protected static final Integer maxNumberOfTargets = null;
protected static final Zone zone = null;
public TargetWithAdditionalFilter(final TargetWithAdditionalFilter target){
this(target.originalTarget, target.additionalFilter, false);
public TargetWithAdditionalFilter(final TargetWithAdditionalFilter target) {
this(target.originalTarget, target.additionalFilter, false);
}
public TargetWithAdditionalFilter(Target originalTarget, FilterInPlay<T> additionalFilter){
public TargetWithAdditionalFilter(Target originalTarget, FilterInPlay<T> additionalFilter) {
this(originalTarget, additionalFilter, false);
}
public TargetWithAdditionalFilter(Target originalTarget, FilterInPlay<T> additionalFilter, boolean notTarget){
public TargetWithAdditionalFilter(Target originalTarget, FilterInPlay<T> additionalFilter, boolean notTarget) {
originalTarget = originalTarget.copy();
originalTarget.clearChosen();
this.originalTarget = originalTarget;
@ -283,7 +279,7 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
public int getMaxNumberOfTargets() {
return originalTarget.getMaxNumberOfTargets();
}
@Override
public void setMinNumberOfTargets(int minNumberOfTargets) {
originalTarget.setMinNumberOfTargets(minNumberOfTargets);
@ -305,57 +301,53 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
if (obj == null) {
obj = game.getPlayer(id);
}
try {
return obj != null
&& originalTarget.canTarget(id, game)
&& additionalFilter.match((T) obj, game);
&& originalTarget.canTarget(id, game)
&& additionalFilter.match((T) obj, game);
} catch (ClassCastException e) {
return false;
}
}
@Override
public boolean canTarget(UUID id, Ability source, Game game) {
MageItem obj = game.getObject(id);
if (obj == null) {
obj = game.getPlayer(id);
}
try {
return obj != null
&& originalTarget.canTarget(id, source, game)
&& additionalFilter.match((T) obj, source.getSourceId(), source.getControllerId(), game);
&& originalTarget.canTarget(id, source, game)
&& additionalFilter.match((T) obj, source.getSourceId(), source.getControllerId(), game);
} catch (ClassCastException e) {
return false;
}
}
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
MageItem obj = game.getObject(id);
if (obj == null) {
obj = game.getPlayer(id);
}
try {
return obj != null
&& originalTarget.canTarget(controllerId, id, source, game)
&& additionalFilter.match((T) obj, source.getSourceId(), controllerId, game);
&& originalTarget.canTarget(controllerId, id, source, game)
&& additionalFilter.match((T) obj, source.getSourceId(), controllerId, game);
} catch (ClassCastException e) {
return false;
}
}
@Override
public FilterInPlay<T> getFilter() {
return new CompoundFilter((FilterInPlay<T>) originalTarget.getFilter(), additionalFilter, originalTarget.getFilter().getMessage());
}
@Override
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
int remainingTargets = getNumberOfTargets() - targets.size();
@ -371,19 +363,19 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
}
try {
if (!targets.containsKey(objId)
&& obj != null
&& additionalFilter.match((T) obj, sourceId, sourceControllerId, game)) {
&& obj != null
&& additionalFilter.match((T) obj, sourceId, sourceControllerId, game)) {
count++;
if (count >= remainingTargets) {
return true;
}
}
} catch (ClassCastException e) {}
} catch (ClassCastException e) {
}
}
return false;
}
@Override
public boolean canChoose(UUID sourceControllerId, Game game) {
int remainingTargets = getNumberOfTargets() - targets.size();
@ -399,19 +391,19 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
}
try {
if (!targets.containsKey(objId)
&& obj != null
&& additionalFilter.match((T) obj, game)) {
&& obj != null
&& additionalFilter.match((T) obj, game)) {
count++;
if (count >= remainingTargets) {
return true;
}
}
} catch (ClassCastException e) {}
} catch (ClassCastException e) {
}
}
return false;
}
@Override
public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
Set<UUID> ret = new HashSet<>();
@ -422,15 +414,15 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
}
try {
if (obj != null
&& additionalFilter.match((T) obj, sourceId, sourceControllerId, game)) {
&& additionalFilter.match((T) obj, sourceId, sourceControllerId, game)) {
ret.add(id);
}
} catch (ClassCastException e) {}
} catch (ClassCastException e) {
}
}
return ret;
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) {
Set<UUID> ret = new HashSet<>();
@ -441,15 +433,15 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
}
try {
if (obj != null
&& additionalFilter.match((T) obj, game)) {
&& additionalFilter.match((T) obj, game)) {
ret.add(id);
}
} catch (ClassCastException e) {}
} catch (ClassCastException e) {
}
}
return ret;
}
@Override
public TargetWithAdditionalFilter copy() {
return new TargetWithAdditionalFilter(this);
@ -458,7 +450,7 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
@Override
public String getTargetedName(Game game) {
StringBuilder sb = new StringBuilder();
for (UUID targetId: getTargets()) {
for (UUID targetId : getTargets()) {
MageObject object = game.getObject(targetId);
if (object != null) {
sb.append(object.getLogName()).append(" ");

View file

@ -57,9 +57,7 @@ public class CopyTargetSpellEffect extends OneShotEffect {
spell = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK);
}
if (spell != null) {
Spell copy = spell.copySpell();
copy.setControllerId(source.getControllerId());
copy.setCopiedSpell(true);
Spell copy = spell.copySpell(source.getControllerId());;
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
Player player = game.getPlayer(source.getControllerId());

View file

@ -51,9 +51,7 @@ public class EpicEffect extends OneShotEffect {
if (controller != null) {
StackObject stackObject = game.getStack().getStackObject(source.getId());
Spell spell = (Spell) stackObject;
spell = spell.copySpell();
spell.setCopiedSpell(true);
spell.setControllerId(source.getControllerId());
spell = spell.copySpell(source.getControllerId());
// Remove Epic effect from the spell
Effect epicEffect = null;
for (Effect effect : spell.getSpellAbility().getEffects()) {

View file

@ -292,9 +292,7 @@ class ConspireEffect extends OneShotEffect {
if (controller != null && conspiredSpell != null) {
Card card = game.getCard(conspiredSpell.getSourceId());
if (card != null) {
Spell copy = conspiredSpell.copySpell();
copy.setControllerId(source.getControllerId());
copy.setCopiedSpell(true);
Spell copy = conspiredSpell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation()) {

View file

@ -24,8 +24,7 @@
* 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 mage.MageObjectReference;
@ -42,8 +41,6 @@ import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.watchers.common.GravestormWatcher;
/**
*
* @author emerald000
@ -57,6 +54,7 @@ public class GravestormAbility extends TriggeredAbilityImpl {
private GravestormAbility(final GravestormAbility ability) {
super(ability);
}
@Override
public GravestormAbility copy() {
return new GravestormAbility(this);
@ -84,7 +82,7 @@ public class GravestormAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Gravestorm <i>(When you cast this spell, copy it for each permanent put into a graveyard this turn. You may choose new targets for the copies.</i>)" ;
return "Gravestorm <i>(When you cast this spell, copy it for each permanent put into a graveyard this turn. You may choose new targets for the copies.</i>)";
}
}
@ -111,9 +109,7 @@ class GravestormEffect extends OneShotEffect {
game.informPlayers("Gravestorm: " + spell.getName() + " will be copied " + gravestormCount + " time" + (gravestormCount > 1 ? "s" : ""));
}
for (int i = 0; i < gravestormCount; i++) {
Spell copy = spell.copySpell();
copy.setControllerId(source.getControllerId());
copy.setCopiedSpell(true);
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
}

View file

@ -247,9 +247,7 @@ class ReplicateCopyEffect extends OneShotEffect {
}
// create the copies
for (int i = 0; i < replicateCount; i++) {
Spell copy = spell.copySpell();
copy.setControllerId(source.getControllerId());
copy.setCopiedSpell(true);
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation()) {

View file

@ -24,8 +24,7 @@
* 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 mage.MageObjectReference;
@ -42,8 +41,6 @@ import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.watchers.common.CastSpellLastTurnWatcher;
/**
*
* @author Plopman
@ -57,6 +54,7 @@ public class StormAbility extends TriggeredAbilityImpl {
private StormAbility(final StormAbility ability) {
super(ability);
}
@Override
public StormAbility copy() {
return new StormAbility(this);
@ -84,7 +82,7 @@ public class StormAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Storm <i>(When you cast this spell, copy it for each spell cast before it this turn. You may choose new targets for the copies.)</i>" ;
return "Storm <i>(When you cast this spell, copy it for each spell cast before it this turn. You may choose new targets for the copies.)</i>";
}
}
@ -108,12 +106,10 @@ class StormEffect extends OneShotEffect {
Spell spell = (Spell) this.getValue("StormSpell");
if (spell != null) {
if (!game.isSimulation()) {
game.informPlayers("Storm: " + spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ?"s":""));
game.informPlayers("Storm: " + spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : ""));
}
for (int i = 0; i < stormCount; i++) {
Spell copy = spell.copySpell();
copy.setControllerId(source.getControllerId());
copy.setCopiedSpell(true);
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
}

View file

@ -139,10 +139,8 @@ public class Spell extends StackObjImpl implements Card {
payNoMana |= spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE);
if (ignoreAbility) {
ignoreAbility = false;
} else {
if (!spellAbility.activate(game, payNoMana)) {
return false;
}
} else if (!spellAbility.activate(game, payNoMana)) {
return false;
}
}
return true;
@ -630,9 +628,21 @@ public class Spell extends StackObjImpl implements Card {
return new Spell(this);
}
public Spell copySpell() {
// replaced card.copy by copy (card content should no longer be changed)
return new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone);
public Spell copySpell(UUID newController) {
Spell copy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone);
boolean firstDone = false;
for (SpellAbility spellAbility : this.getSpellAbilities()) {
if (!firstDone) {
firstDone = true;
continue;
}
SpellAbility newAbility = spellAbility.copy(); // e.g. spliced spell
newAbility.newId();
copy.addSpellAbility(newAbility);
}
copy.setCopy(true);
copy.setControllerId(newController);
return copy;
}
@Override