Add TimesMutated counter. Refactor how mutate get abilities

This commit is contained in:
Lyu Zong-Hong
2021-02-08 20:56:15 +09:00
parent 87330ac828
commit 610d712b11
7 changed files with 108 additions and 63 deletions

View File

@@ -3,8 +3,8 @@ package forge.game.ability.effects;
import java.util.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.CardStateName;
import forge.game.Game;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
@@ -13,41 +13,9 @@ import forge.game.card.*;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.util.Lang;
public class MutateEffect extends SpellAbilityEffect {
private void migrateTopCard(final Card host, final Card target) {
// Copy all status from target card and migrate all counters
// Also update all reference of target card to new top card
// TODO: find out all necessary status that should be copied
host.setTapped(target.isTapped());
host.setSickness(target.isFirstTurnControlled());
host.setFlipped(target.isFlipped());
host.setDamage(target.getDamage());
host.setMonstrous(target.isMonstrous());
host.setRenowned(target.isRenowned());
// Migrate counters
Map<CounterType, Integer> counters = target.getCounters();
if (!counters.isEmpty()) {
host.setCounters(Maps.newHashMap(counters));
}
target.clearCounters();
// Migrate attached cards
CardCollectionView attached = target.getAttachedCards();
for (final Card c : attached) {
c.setEntityAttachedTo(host);
}
target.setAttachedCards(null);
host.setAttachedCards(attached);
// TODO: move all remembered, imprinted objects to new top card
// and possibly many other needs to be migrated.
}
@Override
public void resolve(SpellAbility sa) {
final Player p = sa.getActivatingPlayer();
@@ -89,33 +57,34 @@ public class MutateEffect extends SpellAbilityEffect {
host.setMergedToCard(target);
}
// Now the top card always have all abilities from bottom cards
// First remove current mutated states
if (target.getMutatedTimestamp() != -1) {
target.removeCloneState(target.getMutatedTimestamp());
target.setMutatedTimestamp(-1);
}
// Now add all abilities from bottom cards
final Long ts = game.getNextTimestamp();
if (topCard.getCurrentStateName() != CardStateName.FaceDown) {
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(topCard, sa);
topCard.addCloneState(mutatedStates, ts);
topCard.setMutatedTimestamp(ts);
}
if (topCard == target) {
final CardCloneStates cloneStates = CardFactory.getCloneStates(target, target, sa);
final CardState targetState = cloneStates.get(target.getCurrentStateName());
final CardState newState = host.getCurrentState();
targetState.addAbilitiesFrom(newState, false);
target.addCloneState(cloneStates, ts);
// Re-register triggers for target card
game.getTriggerHandler().clearActiveTriggers(target, null);
game.getTriggerHandler().registerActiveTrigger(target, false);
} else {
final CardCloneStates cloneStates = CardFactory.getCloneStates(host, host, sa);
final CardState newState = cloneStates.get(host.getCurrentStateName());
final CardState targetState = target.getCurrentState();
newState.addAbilitiesFrom(targetState, false);
host.addCloneState(cloneStates, ts);
}
game.getAction().moveToPlay(host, p, sa);
if (topCard == host) {
migrateTopCard(host, target);
CardFactory.migrateTopCard(host, target);
} else {
host.setTapped(target.isTapped());
host.setFlipped(target.isFlipped());
}
topCard.setTimesMutated(topCard.getTimesMutated() + 1);
game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(topCard), false);
}

View File

@@ -185,6 +185,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private long bestowTimestamp = -1;
private long transformedTimestamp = 0;
private long mutatedTimestamp = -1;
private int timesMutated = 0;
private boolean tributed = false;
private boolean embalmed = false;
private boolean eternalized = false;
@@ -822,7 +824,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
public boolean isCloned() {
return !clonedStates.isEmpty();
return !clonedStates.isEmpty() && clonedStates.lastEntry().getKey() != mutatedTimestamp;
}
public final CardCollectionView getDevouredCards() {
@@ -1029,6 +1031,24 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return !getMergedCards().isEmpty() || getMergedToCard() != null;
}
public final boolean isMutated() {
return mutatedTimestamp != -1;
}
public final long getMutatedTimestamp() {
return mutatedTimestamp;
}
public final void setMutatedTimestamp(final long t) {
mutatedTimestamp = t;
}
public final int getTimesMutated() {
return timesMutated;
}
public final void setTimesMutated(final int t) {
timesMutated = t;
}
public final String getFlipResult(final Player flipper) {
if (flipResult == null) {
return null;

View File

@@ -20,6 +20,8 @@ package forge.game.card;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ImageKeys;
import forge.StaticData;
import forge.card.*;
@@ -820,4 +822,50 @@ public class CardFactory {
return result;
}
public static CardCloneStates getMutatedCloneStates(final Card card, final CardTraitBase sa) {
final CardStateName state = card.getCurrentStateName();
final CardState ret = new CardState(card, state);
ret.copyFrom(card.getState(state, true), false);
for (final Card c : card.getMergedCards()) {
ret.addAbilitiesFrom(c.getCurrentState(), false);
}
final CardCloneStates result = new CardCloneStates(card, sa);
result.put(state, ret);
return result;
}
public static void migrateTopCard(final Card host, final Card target) {
// Copy all status from target card and migrate all counters
// Also update all reference of target card to new top card
// TODO: find out all necessary status that should be copied
host.setTapped(target.isTapped());
host.setSickness(target.isFirstTurnControlled());
host.setFlipped(target.isFlipped());
host.setDamage(target.getDamage());
host.setTimesMutated(target.getTimesMutated());
host.setMonstrous(target.isMonstrous());
host.setRenowned(target.isRenowned());
// Migrate counters
Map<CounterType, Integer> counters = target.getCounters();
if (!counters.isEmpty()) {
host.setCounters(Maps.newHashMap(counters));
}
target.clearCounters();
// Migrate attached cards
CardCollectionView attached = target.getAttachedCards();
for (final Card c : attached) {
c.setEntityAttachedTo(host);
}
target.setAttachedCards(null);
host.setAttachedCards(attached);
// TODO: move all remembered, imprinted objects to new top card
// and possibly many other needs to be migrated.
}
} // end class AbstractCardFactory

View File

@@ -1319,6 +1319,9 @@ public class CardFactoryUtil {
if (sq[0].contains("TimesPseudokicked")) {
return doXMath(c.getPseudoKickerMagnitude(), m, c);
}
if (sq[0].contains("TimesMutated")) {
return doXMath(c.getTimesMutated(), m, c);
}
// Count$IfCastInOwnMainPhase.<numMain>.<numNotMain> // 7/10
if (sq[0].contains("IfCastInOwnMainPhase")) {

View File

@@ -595,17 +595,11 @@ public class CardState extends GameObject implements IHasSVars {
}
}
staticAbilities.clear();
for (StaticAbility sa : source.staticAbilities) {
if (sa.isIntrinsic()) {
staticAbilities.add(sa.copy(card, lki));
}
}
// Not sure if this is needed
if (lki && source.loyaltyRep != null) {
this.loyaltyRep = source.loyaltyRep.copy(card, lki);
}
}
public CardState copy(final Card host, CardStateName name, final boolean lki) {

View File

@@ -62,7 +62,7 @@ public class PlayerZoneBattlefield extends PlayerZone {
super.add(c, position, latestState);
if (trigger && !c.isMerged()) {
if (trigger) {
c.setSickness(true); // summoning sickness
c.runComesIntoPlayCommands();
}

View File

@@ -0,0 +1,11 @@
Name:Insatiable Hemophage
ManaCost:3 B
Types:Creature Nightmare
PT:3/3
K:Mutate:2 B
K:Deathtouch
T:Mode$ Mutates | ValidCard$ Card.Self | Execute$ TrigLoseLife | TriggerDescription$ Whenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.
SVar:TrigLoseLife:DB$ LoseLife | Defined$ Opponent | LifeAmount$ X | References$ X | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X
SVar:X:Count$TimesMutated
Oracle:Mutate {2}{B} (If you cast this spell for its mutate cost, put it over or under target non-Human creature you own. They mutate into the creature on top plus all abilities from under it.)\nDeathtouch\nWhenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.