Implemented Twinning Staff

This commit is contained in:
Evan Kranzler 2020-04-23 17:36:26 -04:00
parent 4e5e00d2be
commit 7522c0a049
15 changed files with 179 additions and 82 deletions

View file

@ -1,7 +1,6 @@
package mage.cards.e; package mage.cards.e;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Abilities; import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl; import mage.abilities.AbilitiesImpl;
@ -16,22 +15,23 @@ import mage.abilities.keyword.LevelerCardBuilder;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.cards.LevelerCard; import mage.cards.LevelerCard;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.target.TargetSpell; import mage.target.TargetSpell;
import java.util.UUID;
/** /**
*
* @author North * @author North
*/ */
public final class EchoMage extends LevelerCard { public final class EchoMage extends LevelerCard {
public EchoMage(UUID ownerId, CardSetInfo setInfo) { public EchoMage(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}");
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD); this.subtype.add(SubType.WIZARD);
@ -94,8 +94,7 @@ class EchoMageEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) { if (spell != null) {
spell.createCopyOnStack(game, source, source.getControllerId(), true); spell.createCopyOnStack(game, source, source.getControllerId(), true, 2);
spell.createCopyOnStack(game, source, source.getControllerId(), true);
return true; return true;
} }
return false; return false;

View file

@ -125,8 +125,8 @@ class FinaleOfPromiseEffect extends OneShotEffect {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(Card::getName) .map(Card::getName)
.collect(Collectors.joining(" -> ")); .collect(Collectors.joining(" -> "));
if (!controller.chooseUse(Outcome.Detriment, "Cast cards by choose order: " if (!controller.chooseUse(Outcome.Detriment, "Cast cards by choose order: "
+ cardsOrder + "?", "Finale of Promise", + cardsOrder + "?", "Finale of Promise",
"Use that order", "Reverse", source, game)) { "Use that order", "Reverse", source, game)) {
Collections.reverse(cardsToCast); Collections.reverse(cardsToCast);
} }
@ -154,8 +154,7 @@ class FinaleOfPromiseEffect extends OneShotEffect {
if (card != null) { if (card != null) {
Spell spell = game.getStack().getSpell(card.getId()); Spell spell = game.getStack().getSpell(card.getId());
if (spell != null) { if (spell != null) {
spell.createCopyOnStack(game, source, controller.getId(), true); spell.createCopyOnStack(game, source, controller.getId(), true, 2);
spell.createCopyOnStack(game, source, controller.getId(), true);
game.informPlayers(controller.getLogName() + " copies " + spell.getName() + " twice."); game.informPlayers(controller.getLogName() + " copies " + spell.getName() + " twice.");
} }
} }

View file

@ -1,7 +1,6 @@
package mage.cards.i; package mage.cards.i;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
@ -18,8 +17,9 @@ import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.target.TargetSpell; import mage.target.TargetSpell;
import java.util.UUID;
/** /**
*
* @author BetaSteward * @author BetaSteward
*/ */
public final class IncreasingVengeance extends CardImpl { public final class IncreasingVengeance extends CardImpl {
@ -34,7 +34,7 @@ public final class IncreasingVengeance extends CardImpl {
} }
public IncreasingVengeance(UUID ownerId, CardSetInfo setInfo) { public IncreasingVengeance(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{R}");
// Copy target instant or sorcery spell you control. If this spell was cast from a graveyard, copy that spell twice instead. You may choose new targets for the copies. // Copy target instant or sorcery spell you control. If this spell was cast from a graveyard, copy that spell twice instead. You may choose new targets for the copies.
this.getSpellAbility().addEffect(new IncreasingVengeanceEffect()); this.getSpellAbility().addEffect(new IncreasingVengeanceEffect());
@ -69,26 +69,23 @@ class IncreasingVengeanceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller == null) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); return false;
if (spell != null) {
StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (stackObjectCopy instanceof Spell) {
game.informPlayers(controller.getLogName() + ((Spell) stackObjectCopy).getActivatedMessage(game));
}
Spell sourceSpell = (Spell) game.getStack().getStackObject(source.getSourceId());
if (sourceSpell != null) {
if (sourceSpell.getFromZone() == Zone.GRAVEYARD) {
stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (stackObjectCopy instanceof Spell) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(((Spell) stackObjectCopy).getActivatedMessage(game)).toString());
}
}
}
return true;
}
} }
return false; Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell == null) {
return false;
}
Spell sourceSpell = (Spell) game.getSpell(source.getSourceId());
int copies = 1;
if (sourceSpell != null && sourceSpell.getFromZone() == Zone.GRAVEYARD) {
copies++;
}
StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true, copies);
if (stackObjectCopy instanceof Spell) {
game.informPlayers(controller.getLogName() + ((Spell) stackObjectCopy).getActivatedMessage(game));
}
return true;
} }
@Override @Override

View file

@ -122,8 +122,7 @@ class RepeatedReverberationEffect extends OneShotEffect {
if (controller == null || sourcePermanent == null) { if (controller == null || sourcePermanent == null) {
return false; return false;
} }
stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); stackAbility.createCopyOnStack(game, source, source.getControllerId(), true, 2);
stackAbility.createCopyOnStack(game, source, source.getControllerId(), true);
game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied loyalty ability twice"); game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied loyalty ability twice");
return true; return true;
} }

View file

@ -22,7 +22,6 @@ import mage.watchers.Watcher;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
/** /**
@ -114,9 +113,7 @@ class ThousandYearStormEffect extends OneShotEffect {
numberOfCopies = (int) game.getState().getValue(stateSearchId); numberOfCopies = (int) game.getState().getValue(stateSearchId);
} }
if (numberOfCopies > 0) { if (numberOfCopies > 0) {
for (int i = 0; i < numberOfCopies; i++) { spell.createCopyOnStack(game, source, source.getControllerId(), true, numberOfCopies);
spell.createCopyOnStack(game, source, source.getControllerId(), true);
}
} }
return true; return true;
} }

View file

@ -0,0 +1,92 @@
package mage.cards.t;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.filter.FilterSpell;
import mage.filter.common.FilterInstantOrSorcerySpell;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.TargetSpell;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class TwinningStaff extends CardImpl {
private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery spell you control");
static {
filter.add(TargetController.YOU.getControllerPredicate());
}
public TwinningStaff(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// If you would copy a spell one or more times, instead copy it that many times plus an additional time. You may choose new targets for the additional copy.
this.addAbility(new SimpleStaticAbility(new TwinningStaffEffect()));
// {7}, {T}: Copy target instant or sorcery spell you control. You may choose new targets for the copy.
Ability ability = new SimpleActivatedAbility(new CopyTargetSpellEffect(), new GenericManaCost(7));
ability.addCost(new TapSourceCost());
ability.addTarget(new TargetSpell(filter));
this.addAbility(ability);
}
private TwinningStaff(final TwinningStaff card) {
super(card);
}
@Override
public TwinningStaff copy() {
return new TwinningStaff(this);
}
}
class TwinningStaffEffect extends ReplacementEffectImpl {
TwinningStaffEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "If you would copy a spell one or more times, " +
"instead copy it that many times plus an additional time. " +
"You may choose new targets for the additional copy.";
}
private TwinningStaffEffect(final TwinningStaffEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmount(event.getAmount() + 1);
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COPY_STACKOBJECT;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(source.getControllerId())
&& game.getSpellOrLKIStack(event.getTargetId()) != null;
}
@Override
public TwinningStaffEffect copy() {
return new TwinningStaffEffect(this);
}
}

View file

@ -322,6 +322,7 @@ public final class Commander2020Edition extends ExpansionSet {
cards.add(new SetCardInfo("Tribute to the Wild", 193, Rarity.UNCOMMON, mage.cards.t.TributeToTheWild.class)); cards.add(new SetCardInfo("Tribute to the Wild", 193, Rarity.UNCOMMON, mage.cards.t.TributeToTheWild.class));
cards.add(new SetCardInfo("Trygon Predator", 232, Rarity.UNCOMMON, mage.cards.t.TrygonPredator.class)); cards.add(new SetCardInfo("Trygon Predator", 232, Rarity.UNCOMMON, mage.cards.t.TrygonPredator.class));
cards.add(new SetCardInfo("Trynn, Champion of Freedom", 1, Rarity.MYTHIC, mage.cards.t.TrynnChampionOfFreedom.class)); cards.add(new SetCardInfo("Trynn, Champion of Freedom", 1, Rarity.MYTHIC, mage.cards.t.TrynnChampionOfFreedom.class));
cards.add(new SetCardInfo("Twinning Staff", 70, Rarity.RARE, mage.cards.t.TwinningStaff.class));
cards.add(new SetCardInfo("Ukkima, Stalking Shadow", 17, Rarity.MYTHIC, mage.cards.u.UkkimaStalkingShadow.class)); cards.add(new SetCardInfo("Ukkima, Stalking Shadow", 17, Rarity.MYTHIC, mage.cards.u.UkkimaStalkingShadow.class));
cards.add(new SetCardInfo("Unburial Rites", 139, Rarity.UNCOMMON, mage.cards.u.UnburialRites.class)); cards.add(new SetCardInfo("Unburial Rites", 139, Rarity.UNCOMMON, mage.cards.u.UnburialRites.class));
cards.add(new SetCardInfo("Unclaimed Territory", 320, Rarity.UNCOMMON, mage.cards.u.UnclaimedTerritory.class)); cards.add(new SetCardInfo("Unclaimed Territory", 320, Rarity.UNCOMMON, mage.cards.u.UnclaimedTerritory.class));

View file

@ -101,9 +101,7 @@ class CommanderStormEffect extends OneShotEffect {
return false; return false;
} }
game.informPlayers(spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : "")); game.informPlayers(spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : ""));
for (int i = 0; i < stormCount; i++) { spell.createCopyOnStack(game, source, source.getControllerId(), true, stormCount);
spell.createCopyOnStack(game, source, source.getControllerId(), true);
}
return true; return true;
} }

View file

@ -16,7 +16,6 @@ import mage.game.stack.StackObject;
import mage.watchers.common.GravestormWatcher; import mage.watchers.common.GravestormWatcher;
/** /**
*
* @author emerald000 * @author emerald000
*/ */
public class GravestormAbility extends TriggeredAbilityImpl { public class GravestormAbility extends TriggeredAbilityImpl {
@ -75,7 +74,7 @@ class GravestormEffect extends OneShotEffect {
MageObjectReference spellRef = (MageObjectReference) this.getValue("GravestormSpellRef"); MageObjectReference spellRef = (MageObjectReference) this.getValue("GravestormSpellRef");
if (spellRef != null) { if (spellRef != null) {
GravestormWatcher watcher = game.getState().getWatcher(GravestormWatcher.class); GravestormWatcher watcher = game.getState().getWatcher(GravestormWatcher.class);
if(watcher != null) { if (watcher != null) {
int gravestormCount = watcher.getGravestormCount(); int gravestormCount = watcher.getGravestormCount();
if (gravestormCount > 0) { if (gravestormCount > 0) {
Spell spell = (Spell) this.getValue("GravestormSpell"); Spell spell = (Spell) this.getValue("GravestormSpell");
@ -83,9 +82,7 @@ class GravestormEffect extends OneShotEffect {
if (!game.isSimulation()) { if (!game.isSimulation()) {
game.informPlayers("Gravestorm: " + spell.getName() + " will be copied " + gravestormCount + " time" + (gravestormCount > 1 ? "s" : "")); game.informPlayers("Gravestorm: " + spell.getName() + " will be copied " + gravestormCount + " time" + (gravestormCount > 1 ? "s" : ""));
} }
for (int i = 0; i < gravestormCount; i++) { spell.createCopyOnStack(game, source, source.getControllerId(), true, gravestormCount);
spell.createCopyOnStack(game, source, source.getControllerId(), true);
}
} }
} }
return true; return true;

View file

@ -218,12 +218,7 @@ class ReplicateCopyEffect extends OneShotEffect {
} }
} }
// create the copies // create the copies
for (int i = 0; i < replicateCount; i++) { StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true, replicateCount);
StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (newStackObject instanceof Spell && !game.isSimulation()) {
game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game));
}
}
return true; return true;
} }

View file

@ -16,7 +16,6 @@ import mage.watchers.common.CastSpellLastTurnWatcher;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
*
* @author Plopman * @author Plopman
*/ */
public class StormAbility extends TriggeredAbilityImpl { public class StormAbility extends TriggeredAbilityImpl {
@ -83,13 +82,11 @@ class StormEffect extends OneShotEffect {
if (!game.isSimulation()) { 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.createCopyOnStack(game, source, source.getControllerId(), true, stormCount);
spell.createCopyOnStack(game, source, source.getControllerId(), true);
}
} }
} }
} else { } else {
Logger.getLogger(StormEffect.class).fatal("CastSpellLastTurnWatcher not found. game = " +game.getGameType().toString()); Logger.getLogger(StormEffect.class).fatal("CastSpellLastTurnWatcher not found. game = " + game.getGameType().toString());
} }
return true; return true;
} }

View file

@ -150,7 +150,7 @@ public class GameEvent implements Serializable {
SPELL_CAST, SPELL_CAST,
ACTIVATE_ABILITY, ACTIVATED_ABILITY, ACTIVATE_ABILITY, ACTIVATED_ABILITY,
TRIGGERED_ABILITY, TRIGGERED_ABILITY,
COPIED_STACKOBJECT, COPY_STACKOBJECT,COPIED_STACKOBJECT,
/* ADD_MANA /* ADD_MANA
targetId id of the ability that added the mana targetId id of the ability that added the mana
sourceId sourceId of the ability that added the mana sourceId sourceId of the ability that added the mana

View file

@ -1,6 +1,5 @@
package mage.game.stack; package mage.game.stack;
import java.util.*;
import mage.MageInt; import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.Mana; import mage.Mana;
@ -32,6 +31,8 @@ import mage.players.Player;
import mage.util.GameLog; import mage.util.GameLog;
import mage.util.SubTypeList; import mage.util.SubTypeList;
import java.util.*;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -992,13 +993,25 @@ public class Spell extends StackObjImpl implements Card {
@Override @Override
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) {
Spell copy = this.copySpell(newControllerId); return createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1);
game.getState().setZone(copy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental }
game.getStack().push(copy);
if (chooseNewTargets) { @Override
copy.chooseNewTargets(game, newControllerId); public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) {
Spell copy = null;
GameEvent gameEvent = GameEvent.getEvent(EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount);
if (game.replaceEvent(gameEvent)) {
return null;
}
for (int i = 0; i < gameEvent.getAmount(); i++) {
copy = this.copySpell(newControllerId);
game.getState().setZone(copy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental
game.getStack().push(copy);
if (chooseNewTargets) {
copy.chooseNewTargets(game, newControllerId);
}
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId));
} }
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId));
return copy; return copy;
} }

View file

@ -573,19 +573,30 @@ public class StackAbility extends StackObjImpl implements Ability {
@Override @Override
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) {
Ability newAbility = this.copy(); return createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1);
newAbility.newId(); }
StackAbility newStackAbility = new StackAbility(newAbility, newControllerId);
game.getStack().push(newStackAbility); public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) {
if (chooseNewTargets && !newAbility.getTargets().isEmpty()) { StackAbility newStackAbility = null;
Player controller = game.getPlayer(newControllerId); GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount);
Outcome outcome = newAbility.getEffects().getOutcome(newAbility); if (game.replaceEvent(gameEvent)) {
if (controller.chooseUse(outcome, "Choose new targets?", source, game)) { return null;
newAbility.getTargets().clearChosen(); }
newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false); for (int i = 0; i < gameEvent.getAmount(); i++) {
} Ability newAbility = this.copy();
newAbility.newId();
newStackAbility = new StackAbility(newAbility, newControllerId);
game.getStack().push(newStackAbility);
if (chooseNewTargets && !newAbility.getTargets().isEmpty()) {
Player controller = game.getPlayer(newControllerId);
Outcome outcome = newAbility.getEffects().getOutcome(newAbility);
if (controller.chooseUse(outcome, "Choose new targets?", source, game)) {
newAbility.getTargets().clearChosen();
newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false);
}
}
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId));
} }
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId));
return newStackAbility; return newStackAbility;
} }

View file

@ -1,7 +1,5 @@
package mage.game.stack; package mage.game.stack;
import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.constants.Zone; import mage.constants.Zone;
@ -10,6 +8,8 @@ import mage.filter.FilterPermanent;
import mage.game.Controllable; import mage.game.Controllable;
import mage.game.Game; import mage.game.Game;
import java.util.UUID;
public interface StackObject extends MageObject, Controllable { public interface StackObject extends MageObject, Controllable {
boolean resolve(Game game); boolean resolve(Game game);
@ -22,13 +22,15 @@ public interface StackObject extends MageObject, Controllable {
Ability getStackAbility(); Ability getStackAbility();
// int getConvertedManaCost(); // int getConvertedManaCost();
boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget); boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget);
StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets); StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets);
StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount);
boolean isTargetChanged(); boolean isTargetChanged();
void setTargetChanged(boolean targetChanged); void setTargetChanged(boolean targetChanged);
@Override @Override