mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 13:02:06 -08:00
* Improved handling of asThoughtAs approval by abilities that allows a clear and easy assignment of the approving effect.
This commit is contained in:
parent
0565d32f55
commit
8105d8b26c
117 changed files with 523 additions and 442 deletions
33
Mage/src/main/java/mage/ApprovingObject.java
Normal file
33
Mage/src/main/java/mage/ApprovingObject.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ApprovingObject {
|
||||
|
||||
private final Ability approvingAbility;
|
||||
private final MageObjectReference approvingMageObjectReference;
|
||||
|
||||
public ApprovingObject(Ability source, Game game) {
|
||||
this.approvingAbility = source;
|
||||
this.approvingMageObjectReference = new MageObjectReference(source.getSourceId(), game);
|
||||
}
|
||||
|
||||
public Ability getApprovingAbility() {
|
||||
return approvingAbility;
|
||||
}
|
||||
|
||||
public MageObjectReference getApprovingMageObjectReference() {
|
||||
return approvingMageObjectReference;
|
||||
}
|
||||
|
||||
}
|
||||
15
Mage/src/main/java/mage/MageIdentifier.java
Normal file
15
Mage/src/main/java/mage/MageIdentifier.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package mage;
|
||||
|
||||
/**
|
||||
* Used to identify specific actions/events and to be able to assign them to the
|
||||
* correct watcher or other processing.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum MageIdentifier {
|
||||
GisaAndGeralfWatcher,
|
||||
KaradorGhostChieftainWatcher,
|
||||
KessDissidentMageWatcher,
|
||||
LurrusOfTheDreamDenWatcher,
|
||||
MuldrothaTheGravetideWatcher
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import mage.watchers.Watcher;
|
|||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageIdentifier;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
|
||||
/**
|
||||
|
|
@ -550,4 +551,8 @@ public interface Ability extends Controllable, Serializable {
|
|||
* @return
|
||||
*/
|
||||
boolean isSameInstance(Ability ability);
|
||||
|
||||
MageIdentifier getIdentifier();
|
||||
|
||||
AbilityImpl setIdentifier(MageIdentifier mageIdentifier);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.MageIdentifier;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -73,7 +73,8 @@ public abstract class AbilityImpl implements Ability {
|
|||
protected CostAdjuster costAdjuster = null;
|
||||
protected List<Hint> hints = new ArrayList<>();
|
||||
protected Outcome customOutcome = null; // uses for AI decisions instead effects
|
||||
|
||||
protected MageIdentifier identifier; // used to identify specific ability (e.g. to match with corresponding watcher)
|
||||
|
||||
public AbilityImpl(AbilityType abilityType, Zone zone) {
|
||||
this.id = UUID.randomUUID();
|
||||
this.originalId = id;
|
||||
|
|
@ -123,6 +124,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.hints.add(hint.copy());
|
||||
}
|
||||
this.customOutcome = ability.customOutcome;
|
||||
this.identifier = ability.identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1306,4 +1308,15 @@ public abstract class AbilityImpl implements Ability {
|
|||
|| (this.getOriginalId().equals(ability.getOriginalId()))
|
||||
|| (this.getClass() == ability.getClass() && this.getRule(true).equals(ability.getRule(true)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MageIdentifier getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbilityImpl setIdentifier(MageIdentifier identifier) {
|
||||
this.identifier = identifier;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.constants.TargetController;
|
|||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -16,19 +17,19 @@ public interface ActivatedAbility extends Ability {
|
|||
final class ActivationStatus {
|
||||
|
||||
private final boolean canActivate;
|
||||
private final MageObjectReference permittingObject;
|
||||
private final ApprovingObject approvingObject;
|
||||
|
||||
public ActivationStatus(boolean canActivate, MageObjectReference permittingObject) {
|
||||
public ActivationStatus(boolean canActivate, ApprovingObject approvingObject) {
|
||||
this.canActivate = canActivate;
|
||||
this.permittingObject = permittingObject;
|
||||
this.approvingObject = approvingObject;
|
||||
}
|
||||
|
||||
public boolean canActivate() {
|
||||
return canActivate;
|
||||
}
|
||||
|
||||
public MageObjectReference getPermittingObject() {
|
||||
return permittingObject;
|
||||
public ApprovingObject getApprovingObject() {
|
||||
return approvingObject;
|
||||
}
|
||||
|
||||
public static ActivationStatus getFalse() {
|
||||
|
|
@ -36,12 +37,11 @@ public interface ActivatedAbility extends Ability {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param permittingObjectAbility card or permanent that allows to activate current ability
|
||||
* @param approvingObjectAbility ability that allows to activate/use current ability
|
||||
*/
|
||||
public static ActivationStatus getTrue(Ability permittingObjectAbility, Game game) {
|
||||
MageObject object = permittingObjectAbility == null ? null : permittingObjectAbility.getSourceObject(game);
|
||||
MageObjectReference ref = object == null ? null : new MageObjectReference(object, game);
|
||||
return new ActivationStatus(true, ref);
|
||||
public static ActivationStatus getTrue(Ability approvingObjectAbility, Game game) {
|
||||
ApprovingObject approvingObject = approvingObjectAbility == null ? null : new ApprovingObject(approvingObjectAbility, game);
|
||||
return new ActivationStatus(true, approvingObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mage.abilities;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
|
|
@ -20,6 +19,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -184,7 +184,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
|||
return ActivationStatus.getFalse();
|
||||
}
|
||||
//20091005 - 602.5d/602.5e
|
||||
MageObjectReference permittingObject = game.getContinuousEffects()
|
||||
ApprovingObject approvingObject = game.getContinuousEffects()
|
||||
.asThough(sourceId,
|
||||
AsThoughEffectType.ACTIVATE_AS_INSTANT,
|
||||
this,
|
||||
|
|
@ -192,11 +192,11 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
|||
game);
|
||||
if (timing == TimingRule.INSTANT
|
||||
|| game.canPlaySorcery(playerId)
|
||||
|| null != permittingObject) {
|
||||
|| null != approvingObject) {
|
||||
if (costs.canPay(this, sourceId, playerId, game)
|
||||
&& canChooseTarget(game)) {
|
||||
this.activatorId = playerId;
|
||||
return new ActivationStatus(true, permittingObject);
|
||||
return new ActivationStatus(true, approvingObject);
|
||||
}
|
||||
}
|
||||
return ActivationStatus.getFalse();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.ApprovingObject;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -25,15 +25,15 @@ public class PlayLandAbility extends ActivatedAbilityImpl {
|
|||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, playerId, game);
|
||||
if (!controlsAbility(playerId, game) && null == permittingObject) {
|
||||
ApprovingObject approvingObject = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, playerId, game);
|
||||
if (!controlsAbility(playerId, game) && null == approvingObject) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
//20091005 - 114.2a
|
||||
return new ActivationStatus(game.isActivePlayer(playerId)
|
||||
&& game.getPlayer(playerId).canPlayLand()
|
||||
&& game.canPlaySorcery(playerId),
|
||||
permittingObject);
|
||||
approvingObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mage.abilities;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
|
|
@ -16,6 +15,7 @@ import mage.players.Player;
|
|||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -82,8 +82,8 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
return ActivationStatus.getFalse();
|
||||
}
|
||||
// fix for Gitaxian Probe and casting opponent's spells
|
||||
MageObjectReference permittingSource = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, playerId, game);
|
||||
if (permittingSource == null) {
|
||||
ApprovingObject approvingObject = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, playerId, game);
|
||||
if (approvingObject == null) {
|
||||
Card card = game.getCard(sourceId);
|
||||
if (!(card != null && card.isOwnedBy(playerId))) {
|
||||
return ActivationStatus.getFalse();
|
||||
|
|
@ -118,7 +118,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
return ActivationStatus.getFalse();
|
||||
|
||||
} else {
|
||||
return new ActivationStatus(canChooseTarget(game), permittingSource);
|
||||
return new ActivationStatus(canChooseTarget(game), approvingObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import java.io.Serializable;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -507,7 +508,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @return sourceId of the permitting effect if any exists otherwise returns
|
||||
* null
|
||||
*/
|
||||
public MageObjectReference asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
|
||||
public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
|
||||
if (!asThoughEffectsList.isEmpty()) {
|
||||
UUID idToCheck;
|
||||
|
|
@ -536,7 +537,7 @@ public class ContinuousEffects implements Serializable {
|
|||
if (affectedAbility == null) {
|
||||
// applies to own ability (one effect can be used in multiple abilities)
|
||||
if (effect.applies(idToCheck, ability, controllerId, game)) {
|
||||
return new MageObjectReference(ability.getSourceObject(game), game);
|
||||
return new ApprovingObject(ability, game);
|
||||
}
|
||||
} else {
|
||||
// applies to affected ability
|
||||
|
|
@ -547,7 +548,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
if (effect.applies(idToCheck, affectedAbility, ability, game, controllerId)) {
|
||||
return new MageObjectReference(ability.getSourceObject(game), game);
|
||||
return new ApprovingObject(ability, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.Set;
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -73,7 +74,7 @@ public class CastCardFromOutsideTheGameEffect extends OneShotEffect {
|
|||
if (player.choose(Outcome.Benefit, filteredCards, target, game)) {
|
||||
Card card = player.getSideboard().get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
player.cast(card.getSpellAbility(), game, true, new ApprovingObject(source, game));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import mage.target.common.TargetControlledCreaturePermanent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* FAQ 2013/01/11
|
||||
|
|
@ -120,7 +121,7 @@ class CipherStoreEffect extends OneShotEffect {
|
|||
SpellAbility ability = copyCard.getSpellAbility();
|
||||
// remove the cipher effect from the copy
|
||||
ability.getEffects().removeIf(effect -> effect instanceof CipherEffect);
|
||||
controller.cast(ability, game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
controller.cast(ability, game, true, new ApprovingObject(source, game));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import mage.util.CardUtil;
|
|||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -67,7 +68,7 @@ public class HideawayPlayEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
|
||||
if (!controller.playCard(card, game, true, true, new MageObjectReference(source.getSourceObject(game), game))) {
|
||||
if (!controller.playCard(card, game, true, true, new ApprovingObject(source, game))) {
|
||||
if (card.getZoneChangeCounter(game) == zcc) {
|
||||
card.setFaceDown(true, game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
|
|
@ -38,7 +39,7 @@ public class PlayTargetWithoutPayingManaEffect extends OneShotEffect {
|
|||
&& target != null) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + target.getId(), Boolean.TRUE);
|
||||
Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(target, game, true),
|
||||
game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + target.getId(), null);
|
||||
return cardWasCast;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
|
@ -87,7 +88,7 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
|
|||
if (cardToCast != null) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE);
|
||||
controller.cast(controller.chooseAbilityForCast(cardToCast, game, true),
|
||||
game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -109,7 +110,7 @@ class CascadeEffect extends OneShotEffect {
|
|||
if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + '?', source, game)) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
|
||||
controller.cast(controller.chooseAbilityForCast(card, game, true),
|
||||
game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
|
|
@ -20,6 +19,7 @@ import mage.game.stack.Spell;
|
|||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* 702.33. Madness
|
||||
|
|
@ -219,7 +219,7 @@ class MadnessCastEffect extends OneShotEffect {
|
|||
castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS);
|
||||
costRef.clear();
|
||||
costRef.add(madnessCost);
|
||||
return owner.cast(castByMadness, game, false, new MageObjectReference(source.getSourceObject(game), game));
|
||||
return owner.cast(castByMadness, game, false, new ApprovingObject(source, game));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
|
|
@ -144,7 +145,7 @@ class MiracleEffect extends OneShotEffect {
|
|||
// replace with the new cost
|
||||
costRef.clear();
|
||||
costRef.add(miracleCosts);
|
||||
controller.cast(abilityToCast, game, false, new MageObjectReference(source.getSourceObject(game), game));
|
||||
controller.cast(abilityToCast, game, false, new ApprovingObject(source, game));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import mage.game.stack.Spell;
|
|||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* This ability has no effect by default and will always return false on the
|
||||
|
|
@ -189,7 +190,7 @@ class ReboundCastSpellFromExileEffect extends OneShotEffect {
|
|||
&& reboundCard != null) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + reboundCard.getId(), Boolean.TRUE);
|
||||
Boolean cardWasCast = player.cast(player.chooseAbilityForCast(reboundCard, game, true),
|
||||
game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + reboundCard.getId(), null);
|
||||
return cardWasCast;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -104,7 +105,7 @@ class RippleEffect extends OneShotEffect {
|
|||
while (player.canRespond() && cards.count(sameNameFilter, game) > 0 && player.choose(Outcome.PlayForFree, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
player.cast(card.getSpellAbility(), game, true, new ApprovingObject(source, game));
|
||||
cards.remove(card);
|
||||
}
|
||||
target1.clearChosen();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpecialAction;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -27,6 +26,7 @@ import mage.target.targetpointer.FixedTarget;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* 502.59. Suspend
|
||||
|
|
@ -369,7 +369,7 @@ class SuspendPlayCardEffect extends OneShotEffect {
|
|||
// cast the card for free
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
|
||||
Boolean cardWasCast = player.cast(player.chooseAbilityForCast(card, game, true),
|
||||
game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
|
||||
if (cardWasCast) {
|
||||
if (card.isCreature()) {
|
||||
|
|
|
|||
|
|
@ -971,5 +971,5 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package mage.game.events;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageIdentifier;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -27,7 +29,7 @@ public class GameEvent implements Serializable {
|
|||
protected String data;
|
||||
protected Zone zone;
|
||||
protected List<UUID> appliedEffects = new ArrayList<>();
|
||||
protected MageObjectReference reference; // e.g. the permitting object for casting a spell from non hand zone
|
||||
protected ApprovingObject approvingObject; // e.g. the approving object for casting a spell from non hand zone
|
||||
protected UUID customEventType = null;
|
||||
|
||||
public enum EventType {
|
||||
|
|
@ -359,8 +361,8 @@ public class GameEvent implements Serializable {
|
|||
this(type, null, targetId, sourceId, playerId, 0, false);
|
||||
}
|
||||
|
||||
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, MageObjectReference reference) {
|
||||
this(type, null, targetId, sourceId, playerId, 0, false, reference);
|
||||
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, ApprovingObject approvingObject) {
|
||||
this(type, null, targetId, sourceId, playerId, 0, false, approvingObject);
|
||||
}
|
||||
|
||||
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
|
||||
|
|
@ -383,8 +385,8 @@ public class GameEvent implements Serializable {
|
|||
return new GameEvent(type, targetId, sourceId, playerId);
|
||||
}
|
||||
|
||||
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, MageObjectReference reference) {
|
||||
return new GameEvent(type, targetId, sourceId, playerId, reference);
|
||||
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, ApprovingObject approvingObject) {
|
||||
return new GameEvent(type, targetId, sourceId, playerId, approvingObject);
|
||||
}
|
||||
|
||||
public static GameEvent getEvent(EventType type, UUID targetId, UUID playerId) {
|
||||
|
|
@ -423,7 +425,7 @@ public class GameEvent implements Serializable {
|
|||
}
|
||||
|
||||
private GameEvent(EventType type, UUID customEventType,
|
||||
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, MageObjectReference reference) {
|
||||
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, ApprovingObject approvingObject) {
|
||||
this.type = type;
|
||||
this.customEventType = customEventType;
|
||||
this.targetId = targetId;
|
||||
|
|
@ -431,7 +433,7 @@ public class GameEvent implements Serializable {
|
|||
this.amount = amount;
|
||||
this.playerId = playerId;
|
||||
this.flag = flag;
|
||||
this.reference = reference;
|
||||
this.approvingObject = approvingObject;
|
||||
}
|
||||
|
||||
public EventType getType() {
|
||||
|
|
@ -501,12 +503,17 @@ public class GameEvent implements Serializable {
|
|||
this.zone = zone;
|
||||
}
|
||||
|
||||
public MageObjectReference getAdditionalReference() {
|
||||
return reference;
|
||||
/**
|
||||
* Returns possibly approving object that allowed the creation of the event.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ApprovingObject getAdditionalReference() {
|
||||
return approvingObject;
|
||||
}
|
||||
|
||||
public void setAdditionalReference(MageObjectReference additionalReference) {
|
||||
this.reference = additionalReference;
|
||||
public void setAdditionalReference(ApprovingObject approvingObject) {
|
||||
this.approvingObject = approvingObject;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -546,4 +553,14 @@ public class GameEvent implements Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasApprovingIdentifier(MageIdentifier identifier) {
|
||||
if (approvingObject == null) {
|
||||
return false;
|
||||
}
|
||||
if (identifier == null) {
|
||||
return false;
|
||||
}
|
||||
return identifier.equals(approvingObject.getApprovingAbility().getIdentifier());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import java.util.ArrayList;
|
|||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageIdentifier;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -690,4 +691,15 @@ public class StackAbility extends StackObjImpl implements Ability {
|
|||
|| (this.getOriginalId().equals(ability.getOriginalId()))
|
||||
|| (this.getClass() == ability.getClass() && this.getRule().equals(ability.getRule()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MageIdentifier getIdentifier() {
|
||||
return ability.getIdentifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbilityImpl setIdentifier(MageIdentifier identifier) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.players;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageItem;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
|
|
@ -105,9 +106,9 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
/**
|
||||
* Can the player pay life for spells or activated abilities
|
||||
*
|
||||
*
|
||||
* @param Ability
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
boolean canPayLifeCost(Ability Ability);
|
||||
|
||||
|
|
@ -319,7 +320,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
int drawCards(int num, UUID sourceId, Game game, List<UUID> appliedEffects);
|
||||
|
||||
boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference reference);
|
||||
boolean cast(SpellAbility ability, Game game, boolean noMana, ApprovingObject approvingObject);
|
||||
|
||||
SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana);
|
||||
|
||||
|
|
@ -367,17 +368,17 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
/**
|
||||
* Plays a card if possible
|
||||
*
|
||||
* @param card the card that can be cast
|
||||
* @param card the card that can be cast
|
||||
* @param game
|
||||
* @param noMana if it's a spell i can be cast without paying mana
|
||||
* @param ignoreTiming if it's cast during the resolution of another spell
|
||||
* no sorcery or play land timing restriction are
|
||||
* checked. For a land it has to be the turn of the
|
||||
* player playing that card.
|
||||
* @param reference mage object that allows to play the card
|
||||
* @param noMana if it's a spell i can be cast without paying mana
|
||||
* @param ignoreTiming if it's cast during the resolution of another
|
||||
* spell no sorcery or play land timing restriction
|
||||
* are checked. For a land it has to be the turn of
|
||||
* the player playing that card.
|
||||
* @param approvingObject reference to the ability that allows to play the card
|
||||
* @return
|
||||
*/
|
||||
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference);
|
||||
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, ApprovingObject approvingObject);
|
||||
|
||||
/**
|
||||
* @param card the land card to play
|
||||
|
|
@ -941,13 +942,15 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
/**
|
||||
* Set the mana colors the user can pay with 2 life instead
|
||||
* @param colors
|
||||
*
|
||||
* @param colors
|
||||
*/
|
||||
void addPhyrexianToColors(FilterMana colors);
|
||||
|
||||
/**
|
||||
* Mana colors the player can pay instead with 2 life
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
FilterMana getPhyrexianColors();
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ import java.io.Serializable;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
public abstract class PlayerImpl implements Player, Serializable {
|
||||
|
||||
|
|
@ -1136,7 +1137,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference) {
|
||||
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, ApprovingObject approvingObject) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1144,7 +1145,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (card.isLand()) {
|
||||
result = playLand(card, game, ignoreTiming);
|
||||
} else {
|
||||
result = cast(card.getSpellAbility(), game, noMana, reference);
|
||||
result = cast(card.getSpellAbility(), game, noMana, approvingObject);
|
||||
}
|
||||
if (!result) {
|
||||
game.informPlayer(this, "You can't play " + card.getIdName() + '.');
|
||||
|
|
@ -1156,11 +1157,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
* @param originalAbility
|
||||
* @param game
|
||||
* @param noMana cast it without paying mana costs
|
||||
* @param permittingObject which object permitted the cast
|
||||
* @param approvingObject which object approved the cast
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference permittingObject) {
|
||||
public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, ApprovingObject approvingObject) {
|
||||
if (game == null || originalAbility == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1178,7 +1179,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
Card card = game.getCard(ability.getSourceId());
|
||||
if (card != null) {
|
||||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL,
|
||||
ability.getId(), ability.getSourceId(), playerId, permittingObject), ability)) {
|
||||
ability.getId(), ability.getSourceId(), playerId, approvingObject), ability)) {
|
||||
int bookmark = game.bookmarkState();
|
||||
setStoredBookmark(bookmark); // move global bookmark to current state (if you activated mana before then you can't rollback it)
|
||||
Zone fromZone = game.getState().getZone(card.getMainCard().getId());
|
||||
|
|
@ -1213,11 +1214,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
clearCastSourceIdManaCosts(); // TODO: test multiple alternative cost for different cards as same time
|
||||
|
||||
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL,
|
||||
spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
|
||||
spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, approvingObject);
|
||||
game.fireEvent(event);
|
||||
if (spell.activate(game, noMana)) {
|
||||
event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST,
|
||||
spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
|
||||
spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, approvingObject);
|
||||
event.setZone(fromZone);
|
||||
game.fireEvent(event);
|
||||
if (!game.isSimulation()) {
|
||||
|
|
@ -1281,19 +1282,19 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
//20091005 - 305.1
|
||||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND,
|
||||
card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
|
||||
card.getId(), card.getId(), playerId, activationStatus.getApprovingObject()))) {
|
||||
// int bookmark = game.bookmarkState();
|
||||
// land events must return original zone (uses for commander watcher)
|
||||
Zone cardZoneBefore = game.getState().getZone(card.getId());
|
||||
GameEvent landEventBefore = GameEvent.getEvent(GameEvent.EventType.PLAY_LAND,
|
||||
card.getId(), card.getId(), playerId, activationStatus.getPermittingObject());
|
||||
card.getId(), card.getId(), playerId, activationStatus.getApprovingObject());
|
||||
landEventBefore.setZone(cardZoneBefore);
|
||||
game.fireEvent(landEventBefore);
|
||||
|
||||
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
|
||||
landsPlayed++;
|
||||
GameEvent landEventAfter = GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED,
|
||||
card.getId(), card.getId(), playerId, activationStatus.getPermittingObject());
|
||||
card.getId(), card.getId(), playerId, activationStatus.getApprovingObject());
|
||||
landEventAfter.setZone(cardZoneBefore);
|
||||
game.fireEvent(landEventAfter);
|
||||
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
|
||||
|
|
@ -1439,7 +1440,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
result = playManaAbility((ActivatedManaAbilityImpl) ability.copy(), game);
|
||||
break;
|
||||
case SPELL:
|
||||
result = cast((SpellAbility) ability, game, false, activationStatus.getPermittingObject());
|
||||
result = cast((SpellAbility) ability, game, false, activationStatus.getApprovingObject());
|
||||
break;
|
||||
default:
|
||||
result = playAbility(ability.copy(), game);
|
||||
|
|
@ -3145,7 +3146,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
addPhyrexianLikePayOptions(abilityOptions, availableMana, game);
|
||||
}
|
||||
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(),
|
||||
ApprovingObject approvingObject = game.getContinuousEffects().asThough(ability.getSourceId(),
|
||||
AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
|
||||
for (Mana mana : abilityOptions) {
|
||||
if (mana.count() == 0) {
|
||||
|
|
@ -3158,7 +3159,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
//
|
||||
// add tests for non any color like Sunglasses of Urza
|
||||
if (permittingObject != null && mana.count() <= avail.count()) {
|
||||
if (approvingObject != null && mana.count() <= avail.count()) {
|
||||
return true;
|
||||
}
|
||||
if (avail instanceof ConditionalMana && !((ConditionalMana) avail).apply(ability, game, getId(), ability.getManaCosts())) {
|
||||
|
|
@ -3427,17 +3428,17 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
continue;
|
||||
}
|
||||
|
||||
MageObjectReference permittingObject;
|
||||
ApprovingObject approvingObject;
|
||||
if (isPlaySpell || isPlayLand) {
|
||||
// play hand from non hand zone
|
||||
permittingObject = game.getContinuousEffects().asThough(object.getId(),
|
||||
approvingObject = game.getContinuousEffects().asThough(object.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, this.getId(), game);
|
||||
} else {
|
||||
// other abilities from direct zones
|
||||
permittingObject = null;
|
||||
approvingObject = null;
|
||||
}
|
||||
|
||||
boolean canActivateAsHandZone = permittingObject != null
|
||||
boolean canActivateAsHandZone = approvingObject != null
|
||||
|| (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
|
||||
boolean possibleToPlay = false;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue