mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 21:42:07 -08:00
Token's zone change counter (ZCC) improves:
* Now token's zcc uses same logic as card's zcc: enters to battlefield with +1 zcc instead +0 zcc * It can improve support of copied spells that store zcc related data (bug example: lost kicked status for copied spell/token); * Kicker abilities - improved support with copied creature spells (example: Verazol, the Split Current, #7431, #7433); * Refactor: simplified kicker code;
This commit is contained in:
parent
3eb57eade9
commit
f38639e1db
24 changed files with 262 additions and 177 deletions
|
|
@ -20,11 +20,14 @@ import mage.game.MageObjectAttribute;
|
|||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.SubTypes;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class MageObjectImpl implements MageObject {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MageObjectImpl.class);
|
||||
|
||||
protected UUID objectId;
|
||||
|
||||
protected String name;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package mage.abilities.condition.common;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
|
||||
|
|
@ -18,22 +17,7 @@ public enum KickedCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card == null) {
|
||||
// if permanent spell was copied then it enters with sourceId = PermanentToken instead Card (example: Lithoform Engine)
|
||||
card = game.getPermanentEntering(source.getSourceId());
|
||||
}
|
||||
|
||||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
if (((KickerAbility) ability).isKicked(game, source)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return KickerAbility.getSourceObjectKickedCount(game, source) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import mage.game.stack.Spell;
|
|||
|
||||
|
||||
/**
|
||||
* Kicker {X}
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum GetKickerXValue implements DynamicValue {
|
||||
|
|
@ -23,7 +25,7 @@ public enum GetKickerXValue implements DynamicValue {
|
|||
// only one X value per card possible
|
||||
// kicker can be calls multiple times (use getKickedCounter)
|
||||
|
||||
int finalValue = 0;
|
||||
int countX = 0;
|
||||
Spell spell = game.getSpellOrLKIStack(sourceAbility.getSourceId());
|
||||
if (spell != null && spell.getSpellAbility() != null) {
|
||||
int xValue = spell.getSpellAbility().getManaCostsToPay().getX();
|
||||
|
|
@ -39,13 +41,13 @@ public enum GetKickerXValue implements DynamicValue {
|
|||
if (haveVarCost) {
|
||||
int kickedCount = ((KickerAbility) ability).getKickedCounter(game, sourceAbility);
|
||||
if (kickedCount > 0) {
|
||||
finalValue += kickedCount * xValue;
|
||||
countX += kickedCount * xValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalValue;
|
||||
return countX;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Find permanent/spell kicked stats, can be used in ETB effects.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum MultikickerCount implements DynamicValue {
|
||||
|
|
@ -16,16 +16,7 @@ public enum MultikickerCount implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
int count = 0;
|
||||
Card card = game.getCard(sourceAbility.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
count += ((KickerAbility) ability).getKickedCounter(game, sourceAbility);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
return KickerAbility.getSourceObjectKickedCount(game, sourceAbility);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class AttachEffect extends OneShotEffect {
|
||||
|
|
@ -37,10 +37,10 @@ public class AttachEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (sourcePermanent != null) {
|
||||
// if it activating on the stack then allow +1 zcc
|
||||
int zcc = game.getState().getZoneChangeCounter(sourcePermanent.getId());
|
||||
if (zcc == source.getSourceObjectZoneChangeCounter()
|
||||
|| zcc == source.getSourceObjectZoneChangeCounter() + 1
|
||||
|| zcc == source.getSourceObjectZoneChangeCounter() + 2) {
|
||||
if (zcc == CardUtil.getActualSourceObjectZoneChangeCounter(game, source)
|
||||
|| zcc == CardUtil.getActualSourceObjectZoneChangeCounter(game, source) + 1) {
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
return permanent.addAttachment(source.getSourceId(), source, game);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
|
|
@ -45,7 +45,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
* Otherwise, the spell is cast as if it did not have those targets. See rule
|
||||
* 601.2c.
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
public class KickerAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
|
||||
|
||||
|
|
@ -203,17 +203,25 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
* @return
|
||||
*/
|
||||
public static String getActivationKey(Ability source, Game game) {
|
||||
// must use ZCC from the moment of spell's ability activation
|
||||
int zcc = source.getSourceObjectZoneChangeCounter();
|
||||
if (zcc == 0) {
|
||||
// if ability is not activated yet (example: triggered ability checking the kicker conditional)
|
||||
zcc = game.getState().getZoneChangeCounter(source.getSourceId());
|
||||
}
|
||||
// Kicker activates in STACK zone so all zcc must be from "stack moment"
|
||||
// Use cases:
|
||||
// * resolving spell have same zcc (example: check kicker status in sorcery/instant);
|
||||
// * copied spell have same zcc as source spell (see Spell.copySpell and zcc sync);
|
||||
// * creature/token from resolved spell have +1 zcc after moved to battlefield (example: check kicker status in ETB triggers/effects);
|
||||
|
||||
// triggers or activated abilities moves to stack and card's ZCC is changed -- so you must use workaround to find spell's zcc
|
||||
if (source.getAbilityType() == AbilityType.TRIGGERED || source.getAbilityType() == AbilityType.ACTIVATED) {
|
||||
// find object info from the source ability (it can be a permanent or a spell on stack, on the moment of trigger/resolve)
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
Zone sourceObjectZone = game.getState().getZone(sourceObject.getId());
|
||||
int zcc = CardUtil.getActualSourceObjectZoneChangeCounter(game, source);
|
||||
|
||||
// find "stack moment" zcc:
|
||||
// * permanent cards enters from STACK to BATTLEFIELD (+1 zcc)
|
||||
// * permanent tokens enters from OUTSIDE to BATTLEFIELD (+1 zcc, see prepare code in TokenImpl.putOntoBattlefieldHelper)
|
||||
// * spells and copied spells resolves on STACK (zcc not changes)
|
||||
if (sourceObjectZone != Zone.STACK) {
|
||||
--zcc;
|
||||
}
|
||||
|
||||
return zcc + "";
|
||||
}
|
||||
|
||||
|
|
@ -308,4 +316,44 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find spell's kicked stats. Must be used on stack only, e.g. for SPELL_CAST events
|
||||
*
|
||||
* @param game
|
||||
* @param spellId
|
||||
* @return
|
||||
*/
|
||||
public static int getSpellKickedCount(Game game, UUID spellId) {
|
||||
int count = 0;
|
||||
Spell spell = game.getSpellOrLKIStack(spellId);
|
||||
if (spell != null) {
|
||||
for (Ability ability : spell.getAbilities(game)) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
count += ((KickerAbility) ability).getKickedCounter(game, spell.getSpellAbility());
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find source object's kicked stats. Can be used in any places, e.g. in ETB effects
|
||||
*
|
||||
* @param game
|
||||
* @param abilityToCheck
|
||||
* @return
|
||||
*/
|
||||
public static int getSourceObjectKickedCount(Game game, Ability abilityToCheck) {
|
||||
MageObject sourceObject = abilityToCheck.getSourceObject(game);
|
||||
int count = 0;
|
||||
if (sourceObject instanceof Card) {
|
||||
for (Ability ability : ((Card) sourceObject).getAbilities(game)) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
count += ((KickerAbility) ability).getKickedCounter(game, abilityToCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
|
|||
|
||||
@Override
|
||||
public Counters getCounters(Game game) {
|
||||
return leftHalfCard.getCounters(game.getState());
|
||||
return getCounters(game.getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import mage.constants.SuperType;
|
|||
import mage.constants.TargetController;
|
||||
import mage.filter.common.*;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.KickedPredicate;
|
||||
import mage.filter.predicate.mageobject.KickedSpellPredicate;
|
||||
import mage.filter.predicate.mageobject.MulticoloredPredicate;
|
||||
import mage.filter.predicate.permanent.AnotherPredicate;
|
||||
import mage.filter.predicate.permanent.AttackingPredicate;
|
||||
|
|
@ -646,7 +646,7 @@ public final class StaticFilters {
|
|||
public static final FilterSpell FILTER_SPELL_KICKED_A = new FilterSpell("a kicked spell");
|
||||
|
||||
static {
|
||||
FILTER_SPELL_KICKED_A.add(KickedPredicate.instance);
|
||||
FILTER_SPELL_KICKED_A.add(KickedSpellPredicate.instance);
|
||||
FILTER_SPELL_KICKED_A.setLockedFilter(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum KickedPredicate implements Predicate<MageObject> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(MageObject input, Game game) {
|
||||
Spell spell = game.getSpell(input.getId());
|
||||
if (spell == null) {
|
||||
return false;
|
||||
}
|
||||
for (Ability ability : spell.getAbilities()) {
|
||||
if (ability instanceof KickerAbility
|
||||
&& ((KickerAbility) ability).getKickedCounter(game, spell.getSpellAbility()) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Kicked";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Find spell's kicked stats.
|
||||
* <p>
|
||||
* Warning, must be used for SPELL_CAST events only
|
||||
* (if you need kicked stats in ETB effects then search object's abilities instead spell,
|
||||
* see MultikickerCount as example)
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum KickedSpellPredicate implements Predicate<MageObject> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(MageObject input, Game game) {
|
||||
return KickerAbility.getSpellKickedCount(game, input.getId()) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Kicked";
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ import mage.util.Copyable;
|
|||
import mage.util.ThreadLocalStringBuilder;
|
||||
import mage.watchers.Watcher;
|
||||
import mage.watchers.Watchers;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
|
@ -52,6 +53,7 @@ import static java.util.Collections.emptyList;
|
|||
*/
|
||||
public class GameState implements Serializable, Copyable<GameState> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(GameState.class);
|
||||
private static final ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(1024);
|
||||
|
||||
public static final String COPIED_FROM_CARD_KEY = "CopiedFromCard";
|
||||
|
|
@ -1225,9 +1227,10 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
|
||||
public void updateZoneChangeCounter(UUID objectId) {
|
||||
Integer value = getZoneChangeCounter(objectId);
|
||||
int value = getZoneChangeCounter(objectId);
|
||||
value++;
|
||||
this.zoneChangeCounter.put(objectId, value);
|
||||
setZoneChangeCounter(objectId, value);
|
||||
|
||||
// card is changing zone so clear state
|
||||
if (cardState.containsKey(objectId)) {
|
||||
this.cardState.get(objectId).clear();
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ public final class ZonesHandler {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!game.replaceEvent(event)) {
|
||||
Zone fromZone = event.getFromZone();
|
||||
if (event.getToZone() == Zone.BATTLEFIELD) {
|
||||
|
|
@ -382,6 +383,11 @@ public final class ZonesHandler {
|
|||
}
|
||||
}
|
||||
if (success) {
|
||||
// change ZCC on real enter
|
||||
// warning, tokens creation code uses same zcc logic as cards (+1 zcc on enter to battlefield)
|
||||
// so if you want to change zcc logic here (but I know you don't) then change token code
|
||||
// too in TokenImpl.putOntoBattlefieldHelper
|
||||
// KickerTest do many tests for token's zcc
|
||||
if (event.getToZone() == Zone.BATTLEFIELD && event.getTarget() != null) {
|
||||
event.getTarget().updateZoneChangeCounter(game, event);
|
||||
} else if (!(card instanceof Permanent)) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.abilities.costs.mana.ManaCost;
|
|||
import mage.cards.Card;
|
||||
import mage.constants.EmptyNames;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.token.Token;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -119,6 +120,14 @@ public class PermanentToken extends PermanentImpl {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
|
||||
// token must change zcc on enters to battlefield (like cards do with stack),
|
||||
// so it can keep abilities settings synced with copied spell/card
|
||||
// example: kicker ability of copied creature spell
|
||||
super.updateZoneChangeCounter(game, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card getMainCard() {
|
||||
// token don't have game card, so return itself
|
||||
|
|
|
|||
|
|
@ -198,51 +198,68 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
|
|||
|
||||
Token token = event.getToken();
|
||||
int amount = event.getAmount();
|
||||
|
||||
List<Permanent> permanents = new ArrayList<>();
|
||||
List<Permanent> permanentsEntered = new ArrayList<>();
|
||||
|
||||
String setCode = token instanceof TokenImpl ? ((TokenImpl) token).getSetCode(game, event.getSourceId()) : null;
|
||||
|
||||
List<Permanent> needTokens = new ArrayList<>();
|
||||
List<Permanent> allowedTokens = new ArrayList<>();
|
||||
|
||||
// prepare tokens to enter
|
||||
for (int i = 0; i < amount; i++) {
|
||||
PermanentToken newToken = new PermanentToken(token, event.getPlayerId(), setCode, game); // use event.getPlayerId() because it can be replaced by replacement effect
|
||||
game.getState().addCard(newToken);
|
||||
permanents.add(newToken);
|
||||
game.getPermanentsEntering().put(newToken.getId(), newToken);
|
||||
newToken.setTapped(tapped);
|
||||
// use event.getPlayerId() as controller cause it can be replaced by replacement effect
|
||||
PermanentToken newPermanent = new PermanentToken(token, event.getPlayerId(), setCode, game);
|
||||
game.getState().addCard(newPermanent);
|
||||
needTokens.add(newPermanent);
|
||||
game.getPermanentsEntering().put(newPermanent.getId(), newPermanent);
|
||||
newPermanent.setTapped(tapped);
|
||||
|
||||
ZoneChangeEvent emptyEvent = new ZoneChangeEvent(newPermanent, newPermanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD);
|
||||
// tokens zcc must simulate card's zcc too keep copied card/spell settings
|
||||
// (example: etb's kicker ability of copied creature spell, see tests with Deathforge Shaman)
|
||||
newPermanent.updateZoneChangeCounter(game, emptyEvent);
|
||||
}
|
||||
|
||||
// check ETB effects
|
||||
game.setScopeRelevant(true);
|
||||
for (Permanent permanent : permanents) {
|
||||
for (Permanent permanent : needTokens) {
|
||||
if (permanent.entersBattlefield(source, game, Zone.OUTSIDE, true)) {
|
||||
permanentsEntered.add(permanent);
|
||||
allowedTokens.add(permanent);
|
||||
} else {
|
||||
game.getPermanentsEntering().remove(permanent.getId());
|
||||
}
|
||||
}
|
||||
game.setScopeRelevant(false);
|
||||
|
||||
// put allowed tokens to play
|
||||
int createOrder = game.getState().getNextPermanentOrderNumber();
|
||||
for (Permanent permanent : permanentsEntered) {
|
||||
for (Permanent permanent : allowedTokens) {
|
||||
game.addPermanent(permanent, createOrder);
|
||||
permanent.setZone(Zone.BATTLEFIELD, game);
|
||||
game.getPermanentsEntering().remove(permanent.getId());
|
||||
|
||||
// keep tokens ids
|
||||
if (token instanceof TokenImpl) {
|
||||
((TokenImpl) token).lastAddedTokenIds.add(permanent.getId());
|
||||
((TokenImpl) token).lastAddedTokenId = permanent.getId();
|
||||
}
|
||||
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD));
|
||||
|
||||
// created token events
|
||||
ZoneChangeEvent zccEvent = new ZoneChangeEvent(permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD);
|
||||
game.addSimultaneousEvent(zccEvent);
|
||||
if (permanent instanceof PermanentToken && created) {
|
||||
game.addSimultaneousEvent(new CreatedTokenEvent(source, (PermanentToken) permanent));
|
||||
}
|
||||
|
||||
// must attack
|
||||
if (attacking && game.getCombat() != null && game.getActivePlayerId().equals(permanent.getControllerId())) {
|
||||
game.getCombat().addAttackingCreature(permanent.getId(), game, attackedPlayer);
|
||||
}
|
||||
|
||||
// game logs
|
||||
if (created) {
|
||||
game.informPlayers(controller.getLogName() + " creates a " + permanent.getLogName() + " token");
|
||||
} else {
|
||||
game.informPlayers(permanent.getLogName() + " enters the battlefield as a token under " + controller.getLogName() + "'s control'");
|
||||
}
|
||||
|
||||
}
|
||||
game.getState().applyEffects(game); // Needed to do it here without LKIReset i.e. do get SwordOfTheMeekTest running correctly.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import mage.util.CardUtil;
|
|||
import mage.util.GameLog;
|
||||
import mage.util.ManaUtil;
|
||||
import mage.util.SubTypes;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -43,6 +44,8 @@ import java.util.*;
|
|||
*/
|
||||
public class Spell extends StackObjImpl implements Card {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Spell.class);
|
||||
|
||||
private final List<SpellAbility> spellAbilities = new ArrayList<>();
|
||||
private final List<Card> spellCards = new ArrayList<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -1149,4 +1149,24 @@ public final class CardUtil {
|
|||
}
|
||||
return CardUtil.getSourceLogName(game, " (source: ", source, ")", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find actual ZCC of source object, works in any moment (even before source ability activated)
|
||||
* <p>
|
||||
* Use case for usage: if you want to get actual object ZCC before ability resolve
|
||||
* (ability gets zcc after resolve/activate/trigger only -- ?wtf workaround to targets setup I think?)
|
||||
*
|
||||
* @param game
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
public static int getActualSourceObjectZoneChangeCounter(Game game, Ability source) {
|
||||
// current object zcc, find from source object (it can be permanent or spell on stack)
|
||||
int zcc = source.getSourceObjectZoneChangeCounter();
|
||||
if (zcc == 0) {
|
||||
// if ability is not activated yet then use current object's zcc (example: triggered etb ability checking the kicker conditional)
|
||||
zcc = game.getState().getZoneChangeCounter(source.getSourceId());
|
||||
}
|
||||
return zcc;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue