Added framework method for copying a StackAbility without casting it.

Modified the effects doing so with the new method.
This commit is contained in:
emerald000 2016-04-17 23:55:11 -04:00
parent b9ab16d945
commit 8823839a42
34 changed files with 158 additions and 313 deletions

View file

@ -419,7 +419,9 @@ public class SimulatedPlayer2 extends ComputerPlayer {
if (options.isEmpty()) {
logger.debug("simulating -- triggered ability:" + ability);
game.getStack().push(new StackAbility(ability, playerId));
ability.activate(game, false);
if (ability.activate(game, false) && ability.isUsesStack()) {
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
}
game.applyEffects();
game.getPlayers().resetPassed();
} else {
@ -439,7 +441,9 @@ public class SimulatedPlayer2 extends ComputerPlayer {
protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) {
Game sim = game.copy();
sim.getStack().push(new StackAbility(ability, playerId));
ability.activate(sim, false);
if (ability.activate(sim, false) && ability.isUsesStack()) {
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
}
sim.applyEffects();
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
logger.debug("simulating -- node #:" + SimulationNode2.getCount() + " triggered ability option");

View file

@ -47,6 +47,7 @@ import mage.choices.Choice;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackAbility;
import mage.players.Player;
@ -168,6 +169,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
if (ability.isUsesStack()) {
game.getStack().push(new StackAbility(ability, playerId));
if (ability.activate(game, false)) {
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
actionCount++;
return true;
}

View file

@ -28,6 +28,8 @@
package mage.player.ai;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbility;
@ -42,9 +44,6 @@ import mage.game.stack.StackAbility;
import mage.target.Target;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
*
* @author BetaSteward_at_googlemail.com
@ -239,7 +238,9 @@ public class SimulatedPlayer extends ComputerPlayer {
if (logger.isDebugEnabled())
logger.debug("simulating -- triggered ability:" + ability);
game.getStack().push(new StackAbility(ability, playerId));
ability.activate(game, false);
if (ability.activate(game, false) && ability.isUsesStack()) {
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
}
game.applyEffects();
game.getPlayers().resetPassed();
}
@ -258,6 +259,9 @@ public class SimulatedPlayer extends ComputerPlayer {
Game sim = game.copy();
sim.getStack().push(new StackAbility(ability, playerId));
ability.activate(sim, false);
if (ability.activate(sim, false) && ability.isUsesStack()) {
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
}
sim.applyEffects();
SimulationNode newNode = new SimulationNode(parent, sim, playerId);
logger.debug(indent(newNode.getDepth()) + "simulating -- node #:" + SimulationNode.getCount() + " triggered ability option");

View file

@ -28,22 +28,18 @@
package mage.sets.alarareborn;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.mageobject.MulticoloredPredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
/**
*
@ -65,8 +61,9 @@ public class ClovenCasting extends CardImpl {
this.expansionSetCode = "ARB";
// Whenever you cast a multicolored instant or sorcery spell, you may pay {1}. If you do, copy that spell. You may choose new targets for the copy.
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new ClovenCastingEffect(), new GenericManaCost(1)), filter, true, true));
Effect effect = new CopyTargetSpellEffect();
effect.setText("copy that spell. You may choose new targets for the copy");
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new GenericManaCost(1)), filter, true, true));
}
public ClovenCasting(final ClovenCasting card) {
@ -78,38 +75,3 @@ public class ClovenCasting extends CardImpl {
return new ClovenCasting(this);
}
}
class ClovenCastingEffect extends OneShotEffect {
public ClovenCastingEffect() {
super(Outcome.Copy);
staticText = "copy that spell. You may choose new targets for the copy";
}
public ClovenCastingEffect(final ClovenCastingEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
Player player = game.getPlayer(source.getControllerId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(player.getLogName() + " copies " + activateMessage);
return true;
}
return false;
}
@Override
public ClovenCastingEffect copy() {
return new ClovenCastingEffect(this);
}
}

View file

@ -196,7 +196,7 @@ class ZadaHedronGrinderEffect extends OneShotEffect {
}
}
}
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, copy.getId(), spell.getId(), source.getControllerId()));
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);

View file

@ -29,17 +29,15 @@ package mage.sets.commander;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
@ -49,9 +47,6 @@ import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
/**
*
@ -82,10 +77,12 @@ public class RikuOfTwoReflections extends CardImpl {
this.toughness = new MageInt(2);
// Whenever you cast an instant or sorcery spell, you may pay {U}{R}. If you do, copy that spell. You may choose new targets for the copy.
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new RikuOfTwoReflectionsCopyEffect(), new ManaCostsImpl("{U}{R}")), filter, false, true));
Effect effect = new CopyTargetSpellEffect();
effect.setText("copy that spell. You may choose new targets for the copy");
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new ManaCostsImpl("{U}{R}")), filter, false, true));
// Whenever another nontoken creature enters the battlefield under your control, you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield.
Effect effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(),
effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(),
new ManaCostsImpl("{G}{U}"), "Put a token that's a copy of that creature onto the battlefield?");
effect.setText("you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield");
this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filterPermanent, false, SetTargetPointer.PERMANENT, null));
@ -100,38 +97,3 @@ public class RikuOfTwoReflections extends CardImpl {
return new RikuOfTwoReflections(this);
}
}
class RikuOfTwoReflectionsCopyEffect extends OneShotEffect {
public RikuOfTwoReflectionsCopyEffect() {
super(Outcome.Copy);
staticText = "copy that spell. You may choose new targets for the copy";
}
public RikuOfTwoReflectionsCopyEffect(final RikuOfTwoReflectionsCopyEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());;
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
Player player = game.getPlayer(source.getControllerId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(player.getLogName() + " copies " + activateMessage);
return true;
}
return false;
}
@Override
public RikuOfTwoReflectionsCopyEffect copy() {
return new RikuOfTwoReflectionsCopyEffect(this);
}
}

View file

@ -28,18 +28,15 @@
package mage.sets.commander;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ChooseNewTargetsTargetEffect;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.filter.FilterStackObject;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetStackObject;
/**
@ -61,9 +58,11 @@ public class WildRicochet extends CardImpl {
this.expansionSetCode = "CMD";
// You may choose new targets for target instant or sorcery spell. Then copy that spell. You may choose new targets for the copy.
this.getSpellAbility().addEffect(new WildRicochetEffect());
this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect());
Effect effect = new CopyTargetSpellEffect();
effect.setText("Then copy that spell. You may choose new targets for the copy");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addTarget(new TargetStackObject(filter));
}
public WildRicochet(final WildRicochet card) {
@ -75,37 +74,3 @@ public class WildRicochet extends CardImpl {
return new WildRicochet(this);
}
}
class WildRicochetEffect extends OneShotEffect {
public WildRicochetEffect() {
super(Outcome.Neutral);
staticText = "You may choose new targets for target instant or sorcery spell. Then copy that spell. You may choose new targets for the copy";
}
public WildRicochetEffect(final WildRicochetEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(source.getFirstTarget());
Player you = game.getPlayer(source.getControllerId());
if (spell != null && you != null && you.chooseUse(Outcome.Benefit, "Do you wish to choose new targets for " + spell.getName() + "?", source, game)) {
spell.chooseNewTargets(game, you.getId());
}
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());;
game.getStack().push(copy);
if (you != null && you.chooseUse(Outcome.Benefit, "Do you wish to choose new targets for the copied " + spell.getName() + "?", source, game)) {
return copy.chooseNewTargets(game, you.getId());
}
}
return false;
}
@Override
public WildRicochetEffect copy() {
return new WildRicochetEffect(this);
}
}

View file

@ -45,6 +45,7 @@ import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
@ -106,14 +107,14 @@ class CopySourceSpellEffect extends OneShotEffect {
if (controller != null) {
Spell spell = game.getStack().getSpell(source.getSourceId());
if (spell != null) {
Spell spellCopy = spell.copySpell(source.getControllerId());;
game.getStack().push(spellCopy);
spellCopy.chooseNewTargets(game, controller.getId());
String activateMessage = spellCopy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (stackObjectCopy != null && stackObjectCopy instanceof Spell) {
String activateMessage = ((Spell) stackObjectCopy).getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(controller.getLogName() + " copies " + activateMessage);
}
game.informPlayers(controller.getLogName() + " copies " + activateMessage);
return true;
}
}

View file

@ -151,9 +151,7 @@ class CurseOfEchoesEffect extends OneShotEffect {
if (!playerId.equals(spell.getControllerId())) {
Player player = game.getPlayer(playerId);
if (player.chooseUse(Outcome.Copy, chooseMessage, source, game)) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, playerId);
spell.createCopyOnStack(game, source, player.getId(), true);
}
}
}

View file

@ -40,6 +40,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetSpell;
@ -99,17 +100,17 @@ class IncreasingVengeanceEffect extends OneShotEffect {
if (controller != null) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString());
StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (stackObjectCopy != null && stackObjectCopy instanceof Spell) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(((Spell) stackObjectCopy).getActivatedMessage(game)).toString());
}
Spell sourceSpell = (Spell) game.getStack().getStackObject(source.getSourceId());
if (sourceSpell != null) {
if (sourceSpell.getFromZone() == Zone.GRAVEYARD) {
copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString());
stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (stackObjectCopy != null && stackObjectCopy instanceof Spell) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(((Spell) stackObjectCopy).getActivatedMessage(game)).toString());
}
}
}
return true;

View file

@ -31,10 +31,9 @@ import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterSpell;
@ -43,9 +42,7 @@ import mage.filter.predicate.ObjectPlayerPredicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetSpell;
@ -56,7 +53,7 @@ import mage.target.TargetSpell;
*/
public class MirrorSheen extends CardImpl {
private static final FilterSpell filter = new FilterSpell();
private static final FilterSpell filter = new FilterSpell("instant or sorcery spell that targets you");
static {
filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY)));
@ -68,7 +65,7 @@ public class MirrorSheen extends CardImpl {
this.expansionSetCode = "EVE";
// {1}{UR}{UR}: Copy target instant or sorcery spell that targets you. You may choose new targets for the copy.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MirrorSheenEffect(), new ManaCostsImpl("{1}{U/R}{U/R}"));
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyTargetSpellEffect(), new ManaCostsImpl("{1}{U/R}{U/R}"));
ability.addTarget(new TargetSpell(filter));
this.addAbility(ability);
@ -84,41 +81,6 @@ public class MirrorSheen extends CardImpl {
}
}
class MirrorSheenEffect extends OneShotEffect {
public MirrorSheenEffect() {
super(Outcome.Copy);
staticText = "Copy target instant or sorcery spell that targets you. You may choose new targets for the copy";
}
public MirrorSheenEffect(final MirrorSheenEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(source.getFirstTarget());
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
Player player = game.getPlayer(source.getControllerId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(player.getLogName() + " copies " + activateMessage);
return true;
}
return false;
}
@Override
public MirrorSheenEffect copy() {
return new MirrorSheenEffect(this);
}
}
class TargetYouPredicate implements ObjectPlayerPredicate<ObjectPlayer<StackObject>> {
@Override

View file

@ -102,7 +102,7 @@ class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (!(stackAbility.getStackAbility() instanceof ManaAbility)) {
Effect effect = this.getEffects().get(0);
effect.setValue("stackAbility", stackAbility.getStackAbility());
effect.setValue("stackAbility", stackAbility);
return true;
}
}
@ -133,21 +133,11 @@ class CopyActivatedAbilityEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Ability ability = (Ability) getValue("stackAbility");
StackAbility ability = (StackAbility) getValue("stackAbility");
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (ability != null && controller != null && sourcePermanent != null) {
Ability newAbility = ability.copy();
newAbility.newId();
game.getStack().push(new StackAbility(newAbility, source.getControllerId()));
if (newAbility.getTargets().size() > 0) {
if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) {
newAbility.getTargets().clearChosen();
if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) {
return false;
}
}
}
ability.createCopyOnStack(game, source, source.getControllerId(), true);
game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString());
return true;
}

View file

@ -106,10 +106,8 @@ class ChainLightningEffect extends OneShotEffect {
if (cost.pay(source, game, source.getSourceId(), affectedPlayer.getId(), false, null)) {
Spell spell = game.getStack().getSpell(source.getSourceId());
if (spell != null) {
Spell copy = spell.copySpell(affectedPlayer.getId());
game.getStack().push(copy);
copy.chooseNewTargets(game, affectedPlayer.getId());
game.informPlayers(affectedPlayer.getLogName() + " copies " + copy.getName() + ".");
spell.createCopyOnStack(game, source, affectedPlayer.getId(), true);
game.informPlayers(affectedPlayer.getLogName() + " copies " + spell.getName() + ".");
}
}
}

View file

@ -39,6 +39,7 @@ import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetSpell;
@ -96,6 +97,7 @@ class ForkEffect extends OneShotEffect {
copy.getColor(game).setRed(true);
game.getStack().push(copy);
copy.chooseNewTargets(game, controller.getId());
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, copy.getId(), spell.getId(), source.getControllerId()));
return true;
}
return false;

View file

@ -96,7 +96,7 @@ class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (stackAbility != null && !(stackAbility.getStackAbility() instanceof ManaAbility)) {
Effect effect = this.getEffects().get(0);
effect.setValue("stackAbility", stackAbility.getStackAbility());
effect.setValue("stackAbility", stackAbility);
return true;
}
}
@ -132,21 +132,11 @@ class RingsOfBrighthearthEffect extends OneShotEffect {
if (player != null) {
if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? If you do, copy that ability. You may choose new targets for the copy.", source, game)) {
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
Ability ability = (Ability) getValue("stackAbility");
StackAbility ability = (StackAbility) getValue("stackAbility");
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (ability != null && controller != null) {
Ability newAbility = ability.copy();
newAbility.newId();
game.getStack().push(new StackAbility(newAbility, source.getControllerId()));
if (newAbility.getTargets().size() > 0) {
if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) {
newAbility.getTargets().clearChosen();
if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) {
return false;
}
}
}
ability.createCopyOnStack(game, source, source.getControllerId(), true);
game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString());
return true;
}

View file

@ -135,9 +135,7 @@ class HiveMindEffect extends OneShotEffect {
if (spell != null && player != null) {
for (UUID playerId : game.getState().getPlayersInRange(player.getId(), game)) {
if (!playerId.equals(spell.getControllerId())) {
Spell copy = spell.copySpell(playerId);
game.getStack().push(copy);
copy.chooseNewTargets(game, playerId);
spell.createCopyOnStack(game, source, playerId, true);
}
}
return true;

View file

@ -92,21 +92,10 @@ class StrionicResonatorEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source));
if (stackAbility != null) {
Ability ability = (Ability) stackAbility.getStackAbility();
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (ability != null && controller != null && sourcePermanent != null) {
Ability newAbility = ability.copy();
newAbility.newId();
game.getStack().push(new StackAbility(newAbility, source.getControllerId()));
if (newAbility.getTargets().size() > 0) {
if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) {
newAbility.getTargets().clearChosen();
if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) {
return false;
}
}
}
if (controller != null && sourcePermanent != null) {
stackAbility.createCopyOnStack(game, source, source.getControllerId(), true);
game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString());
return true;
}

View file

@ -107,7 +107,7 @@ class KurkeshOnakkeAncientTriggeredAbility extends TriggeredAbilityImpl {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (!(stackAbility.getStackAbility() instanceof ManaAbility)) {
Effect effect = this.getEffects().get(0);
effect.setValue("stackAbility", stackAbility.getStackAbility());
effect.setValue("stackAbility", stackAbility);
return true;
}
}
@ -144,21 +144,11 @@ class KurkeshOnakkeAncientEffect extends OneShotEffect {
if (player != null) {
if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? If you do, copy that ability. You may choose new targets for the copy.", source, game)) {
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) {
Ability ability = (Ability) getValue("stackAbility");
StackAbility ability = (StackAbility) getValue("stackAbility");
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (ability != null && controller != null) {
Ability newAbility = ability.copy();
newAbility.newId();
game.getStack().push(new StackAbility(newAbility, source.getControllerId()));
if (newAbility.getTargets().size() > 0) {
if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) {
newAbility.getTargets().clearChosen();
if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) {
return false;
}
}
}
ability.createCopyOnStack(game, source, source.getControllerId(), true);
game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString());
return true;
}

View file

@ -104,22 +104,21 @@ class PsychicRebuttalEffect extends OneShotEffect {
if (controller == null) {
return false;
}
StackObject stackObject = game.getState().getStack().getStackObject(getTargetPointer().getFirst(game, source));
if (stackObject != null) {
game.getStack().counter(stackObject.getId(), source.getSourceId(), game);
Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
if (spell != null) {
game.getStack().counter(spell.getId(), source.getSourceId(), game);
if (SpellMasteryCondition.getInstance().apply(game, source)
&& controller.chooseUse(Outcome.PlayForFree, "Copy " + stackObject.getName() + " (you may choose new targets for the copy)?", source, game)) {
&& controller.chooseUse(Outcome.PlayForFree, "Copy " + spell.getName() + " (you may choose new targets for the copy)?", source, game)) {
Spell copy = ((Spell) stackObject).copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
Player player = game.getPlayer(source.getControllerId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (newStackObject != null && newStackObject instanceof Spell) {
String activateMessage = ((Spell) newStackObject).getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(controller.getLogName() + activateMessage);
}
game.informPlayers(player.getLogName() + activateMessage);
}
return true;

View file

@ -41,6 +41,7 @@ import mage.filter.common.FilterControlledLandPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetNonlandPermanent;
@ -103,14 +104,14 @@ class ChainOfVaporEffect extends OneShotEffect {
if (player.chooseUse(outcome, "Copy the spell?", source, game)) {
Spell spell = game.getStack().getSpell(source.getSourceId());
if (spell != null) {
Spell copy = spell.copySpell(player.getId());
game.getStack().push(copy);
copy.chooseNewTargets(game, player.getId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
StackObject newStackObject = spell.createCopyOnStack(game, source, player.getId(), true);
if (newStackObject != null && newStackObject instanceof Spell) {
String activateMessage = ((Spell) newStackObject).getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(player.getLogName() + " " + activateMessage);
}
game.informPlayers(player.getLogName() + " " + activateMessage);
}
}
}

View file

@ -130,11 +130,8 @@ class EchoMageEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) {
for (int i = 0; i < 2; i++) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
}
spell.createCopyOnStack(game, source, source.getControllerId(), true);
spell.createCopyOnStack(game, source, source.getControllerId(), true);
return true;
}
return false;

View file

@ -44,6 +44,7 @@ import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetSpell;
@ -104,15 +105,15 @@ class MeletisCharlatanCopyTargetSpellEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, spell.getControllerId());
StackObject newStackObject = spell.createCopyOnStack(game, source, spell.getControllerId(), true);
Player player = game.getPlayer(spell.getControllerId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
if (player != null && newStackObject != null && newStackObject instanceof Spell) {
String activateMessage = ((Spell) newStackObject).getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
game.informPlayers(player.getLogName() + " copies " + activateMessage);
}
game.informPlayers(player.getLogName() + " copies " + activateMessage);;
return true;
}
return false;

View file

@ -48,6 +48,7 @@ import mage.constants.Outcome;
import mage.constants.RangeOfInfluence;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackAbility;
import mage.player.ai.ComputerPlayer;
@ -173,6 +174,7 @@ public class RandomPlayer extends ComputerPlayer {
if (ability.isUsesStack()) {
game.getStack().push(new StackAbility(ability, playerId));
if (ability.activate(game, false)) {
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
actionCount++;
return true;
}

View file

@ -34,6 +34,7 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
/**
@ -57,16 +58,16 @@ public class CopyTargetSpellEffect extends OneShotEffect {
spell = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK);
}
if (spell != null) {
Spell copy = spell.copySpell(source.getControllerId());;
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true);
Player player = game.getPlayer(source.getControllerId());
String activateMessage = copy.getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + activateMessage);
if (player != null && newStackObject != null && newStackObject instanceof Spell) {
String activateMessage = ((Spell) newStackObject).getActivatedMessage(game);
if (activateMessage.startsWith(" casts ")) {
activateMessage = activateMessage.substring(6);
}
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + activateMessage);
}
}
return true;
}

View file

@ -140,10 +140,7 @@ class EpicPushEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
if (spell != null) {
// don't change the targets of the in the origin copied spell
Spell copySpell = spell.copy();
game.getStack().push(copySpell);
copySpell.chooseNewTargets(game, source.getControllerId());
spell.createCopyOnStack(game, source, source.getControllerId(), true);
return true;
}

View file

@ -52,6 +52,7 @@ import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
@ -292,11 +293,9 @@ class ConspireEffect extends OneShotEffect {
if (controller != null && conspiredSpell != null) {
Card card = game.getCard(conspiredSpell.getSourceId());
if (card != null) {
Spell copy = conspiredSpell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game));
StackObject newStackObject = conspiredSpell.createCopyOnStack(game, source, source.getControllerId(), true);
if (newStackObject != null && newStackObject instanceof Spell && !game.isSimulation()) {
game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game));
}
return true;
}

View file

@ -109,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(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
spell.createCopyOnStack(game, source, source.getControllerId(), true);
}
}
}

View file

@ -247,11 +247,9 @@ class ReplicateCopyEffect extends OneShotEffect {
}
// create the copies
for (int i = 0; i < replicateCount; i++) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game));
StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true);
if (newStackObject != null && newStackObject instanceof Spell && !game.isSimulation()) {
game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game));
}
}
return true;

View file

@ -109,9 +109,7 @@ class StormEffect extends OneShotEffect {
game.informPlayers("Storm: " + spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : ""));
}
for (int i = 0; i < stormCount; i++) {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
spell.createCopyOnStack(game, source, source.getControllerId(), true);
}
}
}

View file

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

View file

@ -56,6 +56,8 @@ import mage.constants.ZoneDetail;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
@ -873,4 +875,14 @@ public class Spell extends StackObjImpl implements Card {
throw new UnsupportedOperationException("Not supported for Spell");
}
@Override
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) {
Spell copy = this.copySpell(newControllerId);
game.getStack().push(copy);
if (chooseNewTargets) {
copy.chooseNewTargets(game, newControllerId);
}
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId));
return copy;
}
}

View file

@ -580,4 +580,21 @@ public class StackAbility extends StackObjImpl implements Ability {
public void setCanFizzle(boolean canFizzle) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) {
Ability newAbility = this.copy();
newAbility.newId();
StackAbility newStackAbility = new StackAbility(newAbility, newControllerId);
game.getStack().push(newStackAbility);
if (chooseNewTargets && newAbility.getTargets().size() > 0) {
Player controller = game.getPlayer(newControllerId);
if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) {
newAbility.getTargets().clearChosen();
newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), newControllerId, newAbility, false, game);
}
}
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId));
return newStackAbility;
}
}

View file

@ -50,6 +50,8 @@ public interface StackObject extends MageObject, Controllable {
// int getConvertedManaCost();
boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget);
StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets);
@Override
StackObject copy();

View file

@ -1239,6 +1239,9 @@ public abstract class PlayerImpl implements Player, Serializable {
if (!ability.isUsesStack()) {
ability.resolve(game);
}
else {
game.fireEvent(new GameEvent(EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId()));
}
game.removeBookmark(bookmark);
return true;
}