diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 9fc00f539a0..9fdbda1f9eb 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -338,13 +338,10 @@ public class GameAction { } // update state for view - copied.updateStateForView(); if (copied.isMerged()) { - if (copied.getMergedToCard() != null) { - copied.getMergedToCard().updateStateForView(); - } else { - copied.getMergedCards().get(0).updateStateForView(); - } + copied.getMergedToCard().updateStateForView(); + } else { + copied.updateStateForView(); } if (fromBattlefield) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java index 2f931ec2dab..6f7c023be24 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java @@ -47,17 +47,17 @@ public class MutateEffect extends SpellAbilityEffect { ); final boolean putOnTop = (topCard == host); + host.setMergedToCard(target); + // If first time mutate, add target first. + if (target.getMergedCards().isEmpty()) { + target.addMergedCard(target); + } if (putOnTop) { - host.addMergedCard(target); - host.addMergedCards(target.getMergedCards()); - target.clearMergedCards(); - target.setMergedToCard(host); + target.addMergedCardToTop(host); } else { target.addMergedCard(host); - host.setMergedToCard(target); } - // First remove current mutated states if (target.getMutatedTimestamp() != -1) { target.removeCloneState(target.getMutatedTimestamp()); @@ -66,27 +66,21 @@ public class MutateEffect extends SpellAbilityEffect { // 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) { - // Re-register triggers for target card - game.getTriggerHandler().clearActiveTriggers(target, null); - game.getTriggerHandler().registerActiveTrigger(target, false); + final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(target, sa); + target.addCloneState(mutatedStates, ts); + target.setMutatedTimestamp(ts); } + // Re-register triggers for target card + game.getTriggerHandler().clearActiveTriggers(target, null); + game.getTriggerHandler().registerActiveTrigger(target, false); game.getAction().moveToPlay(host, p, sa); - if (topCard == host) { - CardFactory.migrateTopCard(host, target); - } else { - host.setTapped(target.isTapped()); - host.setFlipped(target.isFlipped()); - } - topCard.setTimesMutated(topCard.getTimesMutated() + 1); + host.setTapped(target.isTapped()); + host.setFlipped(target.isFlipped()); + target.setTimesMutated(target.getTimesMutated() + 1); - game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(topCard), false); + game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(target), false); } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index e345d723114..7cc2284851d 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1010,8 +1010,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final void addMergedCard(final Card c) { mergedCards = view.addCard(mergedCards, c, TrackableProperty.MergedCards); } - public final void addMergedCards(final Iterable cards) { - mergedCards = view.addCards(mergedCards, cards, TrackableProperty.MergedCards); + public final void addMergedCardToTop(final Card c) { + mergedCards.add(0, c); + view.setCards(mergedCards, mergedCards, TrackableProperty.MergedCards); } public final void removeMergedCard(final Card c) { mergedCards = view.removeCard(mergedCards, c, TrackableProperty.MergedCards); @@ -1028,7 +1029,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public final boolean isMerged() { - return !getMergedCards().isEmpty() || getMergedToCard() != null; + return getMergedToCard() != null; } public final boolean isMutated() { @@ -3740,9 +3741,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (tapped == tapped0) { return; } tapped = tapped0; view.updateTapped(this); - for (final Card c : getMergedCards()) { - c.setTapped(tapped0); - } } public final void tap() { diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 370a7e40ede..9c74b51ca8c 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -823,49 +823,23 @@ public class CardFactory { } public static CardCloneStates getMutatedCloneStates(final Card card, final CardTraitBase sa) { - final CardStateName state = card.getCurrentStateName(); + final Card top = card.getMergedCards().get(0); + final CardStateName state = top.getCurrentStateName(); final CardState ret = new CardState(card, state); - ret.copyFrom(card.getState(state, true), false); + ret.copyFrom(top.getState(state, true), false); + boolean first = true; for (final Card c : card.getMergedCards()) { + if (first) { + first = false; + continue; + } ret.addAbilitiesFrom(c.getCurrentState(), false); } - final CardCloneStates result = new CardCloneStates(card, sa); + final CardCloneStates result = new CardCloneStates(top, 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 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 diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index 6aa3db8dae3..556fb7e4809 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -470,7 +470,7 @@ public class PlayerView extends GameEntityView { void updateZone(PlayerZone zone) { TrackableProperty prop = getZoneProp(zone.getZoneType()); if (prop == null) { return; } - set(prop, CardView.getCollection(zone.getCards(false, false))); + set(prop, CardView.getCollection(zone.getCards(false, true))); //update delirium if (ZoneType.Graveyard == zone.getZoneType()) diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java b/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java index 9044ee0ef14..291c02b1253 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java @@ -685,21 +685,6 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen } toPanel.getAttachedPanels().clear(); - // Treat merged cards like attached cards - if (card.hasMergedCards()) { - final Iterable merged = card.getMergedCards(); - for (final CardView c : merged) { - final CardPanel cardC = getCardPanel(c.getId()); - if (cardC != null) { - if (cardC.getAttachedToPanel() != toPanel) { - cardC.setAttachedToPanel(toPanel); - needLayoutRefresh = true; //ensure layout refreshed if any attachments change - } - toPanel.getAttachedPanels().add(cardC); - } - } - } - if (card.hasCardAttachments()) { final Iterable enchants = card.getAttachedCards(); for (final CardView e : enchants) { @@ -714,13 +699,11 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen } } - // Treat merged cards like attached cards CardPanel attachedToPanel; - if (card.getMergedTo() != null) { - attachedToPanel = getCardPanel(card.getMergedTo().getId()); - } else if (card.getAttachedTo() != null) { + if (card.getAttachedTo() != null) { attachedToPanel = getCardPanel(card.getAttachedTo().getId()); - } else { + } + else { attachedToPanel = null; } if (toPanel.getAttachedToPanel() != attachedToPanel) { diff --git a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java index 5205234cc08..6af3cc4c31f 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java @@ -62,7 +62,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH } if (isVisible()) { //only revalidate if currently visible revalidate(); - + if (newCardPanel != null) { //if new cards added, ensure first new card is scrolled into view scrollIntoView(newCardPanel); } @@ -286,17 +286,6 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH attachedPanels.clear(); - // Treat merged cards like attached cards - if (card.hasMergedCards()) { - final Iterable merged = card.getMergedCards(); - for (final CardView c : merged) { - final CardAreaPanel cardC = CardAreaPanel.get(c); - if (cardC != null) { - attachedPanels.add(cardC); - } - } - } - if (card.hasCardAttachments()) { final Iterable enchants = card.getAttachedCards(); for (final CardView e : enchants) { @@ -306,11 +295,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH } } } - - if (card.getMergedTo() != null ) { - setAttachedToPanel(CardAreaPanel.get(card.getMergedTo())); - } - else if (card.getAttachedTo() != null) { + + if (card.getAttachedTo() != null) { setAttachedToPanel(CardAreaPanel.get(card.getAttachedTo())); } else { diff --git a/forge-gui/res/cardsfolder/d/dreamtail_heron.txt b/forge-gui/res/cardsfolder/d/dreamtail_heron.txt index 48b9d16d0f0..39098c06480 100644 --- a/forge-gui/res/cardsfolder/d/dreamtail_heron.txt +++ b/forge-gui/res/cardsfolder/d/dreamtail_heron.txt @@ -6,4 +6,4 @@ K:Mutate:3 U K:Flying T:Mode$ Mutates | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever this creature mutates, draw a card. SVar:TrigDraw:DB$ Draw | NumCards$ 1 -Oracle:Mutate {3}{U} (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.)\nFlying\nWhenever this creature mutates, draw a card. \ No newline at end of file +Oracle:Mutate {3}{U} (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.)\nFlying\nWhenever this creature mutates, draw a card. diff --git a/forge-gui/res/cardsfolder/i/insatiable_hemophage.txt b/forge-gui/res/cardsfolder/i/insatiable_hemophage.txt index 6d728ee65eb..05f7a5776e2 100644 --- a/forge-gui/res/cardsfolder/i/insatiable_hemophage.txt +++ b/forge-gui/res/cardsfolder/i/insatiable_hemophage.txt @@ -8,4 +8,4 @@ T:Mode$ Mutates | ValidCard$ Card.Self | Execute$ TrigLoseLife | TriggerDescript 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. \ No newline at end of file +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. diff --git a/forge-gui/res/cardsfolder/p/parcelbeast.txt b/forge-gui/res/cardsfolder/p/parcelbeast.txt index 62d45a87269..6bbb6ea9390 100644 --- a/forge-gui/res/cardsfolder/p/parcelbeast.txt +++ b/forge-gui/res/cardsfolder/p/parcelbeast.txt @@ -4,4 +4,4 @@ Types:Creature Elemental Beast PT:2/4 K:Mutate:G U A:AB$ Dig | Cost$ 1 T | DigNum$ 1 | ChangeNum$ 1 | ChangeValid$ Land | Optional$ True | DestinationZone$ Battlefield | DestinationZone2$ Hand | StackDescription$ SpellDescription | SpellDescription$ Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand. -Oracle:Mutate {G}{U} (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.)\n{1}, {T}: Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand. \ No newline at end of file +Oracle:Mutate {G}{U} (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.)\n{1}, {T}: Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand. diff --git a/forge-gui/res/cardsfolder/p/pollywog_symbiote.txt b/forge-gui/res/cardsfolder/p/pollywog_symbiote.txt new file mode 100644 index 00000000000..55811c758fa --- /dev/null +++ b/forge-gui/res/cardsfolder/p/pollywog_symbiote.txt @@ -0,0 +1,10 @@ +Name:Pollywog Symbiote +ManaCost:1 U +Types:Creature Frog +PT:1/3 +S:Mode$ ReduceCost | ValidCard$ Creature.withMutate | Type$ Spell | Activator$ You | Amount$ 1 | Description$ Each creature spell you cast costs {1} less to cast if it has mutate. +T:Mode$ SpellCast | ValidCard$ Creature.withMutate | ValidActivatingPlayer$ You | Execute$ TrigLoot | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a creature spell, if it has mutate, draw a card, then discard a card. +SVar:TrigLoot:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBDiscard +SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose +SVar:BuffedBy:Card.withMutate +Oracle:Each creature spell you cast costs {1} less to cast if it has mutate.\nWhenever you cast a creature spell, if it has mutate, draw a card, then discard a card. diff --git a/forge-gui/res/cardsfolder/v/vulpikeet.txt b/forge-gui/res/cardsfolder/v/vulpikeet.txt index e8c991b1e1e..430aea82acd 100644 --- a/forge-gui/res/cardsfolder/v/vulpikeet.txt +++ b/forge-gui/res/cardsfolder/v/vulpikeet.txt @@ -6,4 +6,4 @@ K:Mutate:2 W K:Flying T:Mode$ Mutates | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever this creature mutates, put a +1/+1 counter on it. SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredCardLKICopy | CounterType$ P1P1 | CounterNum$ 1 -Oracle:Mutate {2}{W} (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.)\nFlying\nWhenever this creature mutates, put a +1/+1 counter on it. \ No newline at end of file +Oracle:Mutate {2}{W} (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.)\nFlying\nWhenever this creature mutates, put a +1/+1 counter on it.