From 00411b4a9bddf045f4772c31709d31dbf2930b55 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 2 Aug 2020 14:01:58 +0400 Subject: [PATCH] * Regeneration abilities improved: * Fixed that regeneration shield isn't added to permanent on aura sacrifice cost (example: Stamina, Carapace, see #2221); * Fixed that regeneration shields accumulated in attached aura instead permanent (bug example: re-attached aura gives old shields to new permanent, see #6846); * Added card hint with regeneration shields amount (#6846); --- .../mage/client/combat/CombatManager.java | 2 +- Mage.Sets/src/mage/cards/c/Carapace.java | 7 +- .../cards/replacement/RegenerateTest.java | 341 +++++++++++++++++- .../common/RegenerateAttachedEffect.java | 70 +--- .../common/RegenerateSourceEffect.java | 83 +++-- .../common/RegenerateTargetEffect.java | 2 +- .../mage/game/permanent/PermanentImpl.java | 5 +- 7 files changed, 411 insertions(+), 99 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java index 6d980d9c4e..4e121976c9 100644 --- a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java +++ b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java @@ -82,7 +82,7 @@ public enum CombatManager { private void drawDefender(CombatGroupView group, MagePermanent attackerCard, UUID gameId) { UUID defenderId = group.getDefenderId(); if (defenderId != null) { - // if attacker was blocked then use another allow color + // if attacker was blocked then use another arrow color Color attackColor = group.getBlockers().isEmpty() ? ARROW_COLOR_ATTACKER : ARROW_COLOR_BLOCKED_ATTACKER; parentPoint = getParentPoint(attackerCard); PlayAreaPanel p = MageFrame.getGamePlayers(gameId).get(defenderId); diff --git a/Mage.Sets/src/mage/cards/c/Carapace.java b/Mage.Sets/src/mage/cards/c/Carapace.java index 9c36e39dd8..d315ba38a3 100644 --- a/Mage.Sets/src/mage/cards/c/Carapace.java +++ b/Mage.Sets/src/mage/cards/c/Carapace.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -16,15 +14,16 @@ import mage.constants.*; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Galatolol */ public final class Carapace extends CardImpl { public Carapace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); - + this.subtype.add(SubType.AURA); // Enchant creature diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java index d02ac24547..fe310095d5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java @@ -1,43 +1,370 @@ - package org.mage.test.cards.replacement; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public class RegenerateTest extends CardTestPlayerBase { @Test - public void testRegenerateAbilityGainedByEnchantment() { + public void test_GainedByEnchantment() { addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus"); addCard(Zone.BATTLEFIELD, playerB, "Sagu Archer"); addCard(Zone.HAND, playerB, "Molting Snakeskin"); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Molting Snakeskin", "Sagu Archer"); attack(2, playerB, "Sagu Archer"); block(2, playerA, "Underworld Cerberus", "Sagu Archer"); activateAbility(2, PhaseStep.DECLARE_BLOCKERS, playerB, "{2}{B}: Regenerate {this}."); + setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); - // attacker has to regenerat because of damage - + // attacker has to regenerate because of damage + assertPermanentCount(playerA, "Underworld Cerberus", 1); assertPermanentCount(playerB, "Sagu Archer", 1); assertPermanentCount(playerB, "Molting Snakeskin", 1); + } + @Test + public void test_Source_Normal() { + // {B}: Regenerate Drudge Skeletons. (The next time this creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.BATTLEFIELD, playerA, "Drudge Skeletons", 1); // 1/1 + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // damage 1 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + setChoice(playerA, "Drudge Skeletons"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + // damage 2 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + // damage 3 - die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Source_Blinked() { + // {B}: Regenerate Drudge Skeletons. (The next time this creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.BATTLEFIELD, playerA, "Drudge Skeletons", 1); // 1/1 + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Cloudshift", 1); // {W} instant + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // blink and reset regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive blinked", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + // damage - die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Target_Normal() { + // {G}, {T}, Discard a card: Regenerate target creature. + addCard(Zone.BATTLEFIELD, playerA, "Rushwood Herbalist", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Forest", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // damage 1 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setChoice(playerA, "Rushwood Herbalist"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 2 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 3 - die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Target_Blinked() { + // {G}, {T}, Discard a card: Regenerate target creature. + addCard(Zone.BATTLEFIELD, playerA, "Rushwood Herbalist", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Forest", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Cloudshift", 1); // {W} instant + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // blink and reset regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive blinked", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage - die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Normal() { + // Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.) + // {G}: Regenerate enchanted creature. (The next time that creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.HAND, playerA, "Regeneration", 1); // {1}{G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // cast + 2 activates + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // attach + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Regeneration", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // damage 1 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setChoice(playerA, "Regeneration"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 2 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 3 - die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Blinked() { + // Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.) + // {G}: Regenerate enchanted creature. (The next time that creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.HAND, playerA, "Regeneration", 1); // {1}{G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // cast + 2 activates + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Cloudshift", 1); // {W} instant + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // attach + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Regeneration", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // blink and reset regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive blinked", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage - die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Moved() { + // regen shields must be accumulated in permanent, not aura + + // Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.) + // {G}: Regenerate enchanted creature. (The next time that creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.HAND, playerA, "Regeneration", 1); // {1}{G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // cast + 2 activates + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 permanent 1 + addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair", 1); // 2/1 permanent 2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Attach target Aura you control to target creature. + addCard(Zone.HAND, playerA, "Aura Finesse", 1); // {U} instant + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + // attach to first + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Regeneration", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // re-attach to second + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aura Finesse"); + addTarget(playerA, "Regeneration"); // aura to move + addTarget(playerA, "Kitesail Corsair"); // attach to permanent 2 + + // damage to one - have regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setChoice(playerA, "Regeneration"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage to second -- die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Kitesail Corsair"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Sacrifice() { + // if you sacrifice aura to enable regen then it must works + + // Enchant creature + // Enchanted creature gets +0/+2. + // Sacrifice Carapace: Regenerate enchanted creature. + addCard(Zone.HAND, playerA, "Carapace", 1); // {G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // attach + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Carapace", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPT("boosted", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2, 2 + 2); + + // try to kill (boost lost after regen activate, so need only 1 bolt) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + // activate regen before kill + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice", TestPlayer.NO_TARGET, "Cast Lightning Bolt", StackClause.WHILE_ON_STACK); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // permanent must be alive + checkGraveyardCount("aura sacrificed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Carapace", 1); + checkPermanentCount("permanent alive", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java index 8416880e53..6d16d6415b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java @@ -1,63 +1,49 @@ package mage.abilities.effects.common; import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.OneShotEffect; import mage.constants.AttachmentType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; import java.util.Locale; -import java.util.UUID; /** * @author jeff */ -public class RegenerateAttachedEffect extends ReplacementEffectImpl { +public class RegenerateAttachedEffect extends OneShotEffect { protected AttachmentType attachmentType; public RegenerateAttachedEffect(AttachmentType attachmentType) { - super(Duration.EndOfTurn, Outcome.Regenerate); + super(Outcome.Regenerate); this.attachmentType = attachmentType; this.setText(); } public RegenerateAttachedEffect(final RegenerateAttachedEffect effect) { super(effect); + this.attachmentType = effect.attachmentType; } @Override public boolean apply(Game game, Ability source) { - //20110204 - 701.11 - Permanent permanent = game.getPermanent(source.getSourceId()); + // must use lki cause attachment can be sacrificed to activate regen + Permanent attachment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (attachment == null) { + return false; + } + Permanent permanent = game.getPermanent(attachment.getAttachedTo()); if (permanent == null) { return false; } - Permanent equipped = game.getPermanent(permanent.getAttachedTo()); - if (equipped != null && equipped.regenerate(source, game)) { - this.used = true; - return true; - } - return false; - } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - return; - } - Permanent equipped = game.getPermanent(permanent.getAttachedTo()); - if (equipped == null) { - return; - } - - RegenerateSourceEffect.initRegenerationInfoWhileAttached(game, source, equipped.getId()); + RegenerateTargetEffect regenEffect = new RegenerateTargetEffect(); + regenEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(regenEffect, source); + return true; } @Override @@ -65,32 +51,6 @@ public class RegenerateAttachedEffect extends ReplacementEffectImpl { return new RegenerateAttachedEffect(this); } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return apply(game, source); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DESTROY_PERMANENT; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - //20110204 - 701.11c - event.getAmount() is used to signal if regeneration is allowed - Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null) { - Permanent equipped = game.getPermanent(equipment.getAttachedTo()); - if (equipped != null) { - UUID equippedID = equipped.getId(); - if (event.getAmount() == 0 && event.getTargetId().equals(equippedID) && !this.used) { - return true; - } - } - } - return false; - } - private void setText() { staticText = "Regenerate " + attachmentType.verb().toLowerCase(Locale.ENGLISH) + " creature"; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java index fabdd62d2f..a50e13c8e1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java @@ -1,10 +1,9 @@ package mage.abilities.effects.common; import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.AttachedToPermanentCondition; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.hint.HintUtils; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; @@ -15,7 +14,7 @@ import mage.util.CardUtil; import java.util.UUID; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class RegenerateSourceEffect extends ReplacementEffectImpl { @@ -50,7 +49,7 @@ public class RegenerateSourceEffect extends ReplacementEffectImpl { public void init(Ability source, Game game) { super.init(source, game); - RegenerateSourceEffect.initRegenerationInfo(game, source, source.getSourceId()); + RegenerateSourceEffect.initRegenerationShieldInfo(game, source, source.getSourceId()); } @Override @@ -83,44 +82,70 @@ public class RegenerateSourceEffect extends ReplacementEffectImpl { && !this.used; } - public static void initRegenerationInfo(Game game, Ability source, UUID permanentId) { - // enable regen info + /** + * Add info about new regen shield. + * Warning, it's a workaround to show regen shields info, real effects will be use replacement logic. + * + * @param game + * @param source + * @param permanentId + */ + public static void initRegenerationShieldInfo(Game game, Ability source, UUID permanentId) { Permanent permanent = game.getPermanent(permanentId); if (permanent != null) { - game.getState().setValue(CardUtil.getCardZoneString("RegenerationActivated", permanent.getId(), game), Boolean.TRUE); + // add one regen shield + RegenerateSourceEffect.incRegenerationShieldsAmount(game, permanent.getId()); + // add regen info InfoEffect.addCardHintToPermanent(game, source, permanent, - new ConditionHint(RegeneratingCanBeUsedCondition.instance, "Permanent will be regenerated instead destroy"), - Duration.EndOfTurn - ); + RegenerationShieldsHint.instance, Duration.EndOfTurn); } } - public static void initRegenerationInfoWhileAttached(Game game, Ability source, UUID permanentId) { - // enable regen info for attached permanent - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - game.getState().setValue(CardUtil.getCardZoneString("RegenerationActivated", permanent.getId(), game), Boolean.TRUE); - - InfoEffect.addCardHintToPermanentConditional(game, source, permanent, - new ConditionHint(RegeneratingCanBeUsedCondition.instance, "Permanent will be regenerated instead destroy"), - Duration.EndOfTurn, - new AttachedToPermanentCondition(permanent.getId()) - ); + public static int getRegenerationShieldsAmount(Game game, UUID permanentId) { + // info must be reset on new turn + Integer amount = (Integer) game.getState().getValue( + CardUtil.getCardZoneString("RegenerationShieldsAmount_turn" + game.getTurnNum(), permanentId, game)); + if (amount != null) { + return amount; } + return 0; + } + + private static void setRegenerationShieldsAmount(Game game, UUID permanentId, int amount) { + game.getState().setValue( + CardUtil.getCardZoneString("RegenerationShieldsAmount_turn" + game.getTurnNum(), permanentId, game), amount); + } + + public static int incRegenerationShieldsAmount(Game game, UUID permanentId) { + int amount = getRegenerationShieldsAmount(game, permanentId) + 1; + setRegenerationShieldsAmount(game, permanentId, amount); + return amount; + } + + public static int decRegenerationShieldsAmount(Game game, UUID permanentId) { + int amount = Math.max(0, getRegenerationShieldsAmount(game, permanentId) - 1); + setRegenerationShieldsAmount(game, permanentId, amount); + return amount; } } -enum RegeneratingCanBeUsedCondition implements Condition { +enum RegenerationShieldsHint implements Hint { instance; @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - Boolean shieldActivated = (Boolean) game.getState().getValue(CardUtil.getCardZoneString("RegenerationActivated", permanent.getId(), game)); - return shieldActivated != null && shieldActivated; + public String getText(Game game, Ability ability) { + int amount = RegenerateSourceEffect.getRegenerationShieldsAmount(game, ability.getSourceId()); + String info = "Regeneration shields: " + amount + " (permanent will be regenerated instead destroy)"; + if (amount > 0) { + return HintUtils.prepareText(info, null, HintUtils.HINT_ICON_GOOD); + } else { + return HintUtils.prepareText(info, null, HintUtils.HINT_ICON_BAD); } - return false; + } + + @Override + public Hint copy() { + return instance; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java index 7dff540d19..f672ce1164 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java @@ -41,7 +41,7 @@ public class RegenerateTargetEffect extends ReplacementEffectImpl { public void init(Ability source, Game game) { super.init(source, game); - RegenerateSourceEffect.initRegenerationInfo(game, source, targetPointer.getFirst(game, source)); + RegenerateSourceEffect.initRegenerationShieldInfo(game, source, targetPointer.getFirst(game, source)); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index ccb0d99854..829e75df52 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -10,6 +10,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.RegenerateSourceEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.HintUtils; import mage.abilities.keyword.*; @@ -1179,8 +1180,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.removeFromCombat(game); this.removeAllDamage(game); - // remove regen info - game.getState().setValue(CardUtil.getCardZoneString("RegenerationActivated", this.getId(), game), Boolean.FALSE); + // remove one regen shield + RegenerateSourceEffect.decRegenerationShieldsAmount(game, this.getId()); game.fireEvent(GameEvent.getEvent(EventType.REGENERATED, objectId, source.getSourceId(), controllerId)); return true;