unfinished = Arrays.asList("Baithook Angler", "Hook-Haunt Drifter", "Beloved Beggar", "Generous Soul", "Chaplain of Alms", "Chapel Shieldgeist", "Covert Cutpurse", "Covetous Geist", "Covetous Castaway", "Ghostly Castigator", "Dennick, Pious Apprentice", "Dennick, Pious Apparition", "Devoted Grafkeeper", "Departed Soulkeeper", "Galedrifter", "Waildrifter", "Lunarch Veteran", "Luminous Phantom", "Malevolent Hermit", "Benevolent Geist", "Mourning Patrol", "Morning Apparition", "Overwhelmed Archivist", "Archive Haunt", "Phantom Carriage", "Shipwreck Sifters", "Thraben Exorcism", "Unblinking Observer");
private static final InnistradMidnightHunt instance = new InnistradMidnightHunt();
public static InnistradMidnightHunt getInstance() {
@@ -40,6 +40,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 1, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class));
cards.add(new SetCardInfo("Ambitious Farmhand", 2, Rarity.UNCOMMON, mage.cards.a.AmbitiousFarmhand.class));
cards.add(new SetCardInfo("Angelfire Ignition", 209, Rarity.RARE, mage.cards.a.AngelfireIgnition.class));
+ cards.add(new SetCardInfo("Angelic Enforcer", 17, Rarity.MYTHIC, mage.cards.a.AngelicEnforcer.class));
cards.add(new SetCardInfo("Arcane Infusion", 210, Rarity.UNCOMMON, mage.cards.a.ArcaneInfusion.class));
cards.add(new SetCardInfo("Archive Haunt", 68, Rarity.UNCOMMON, mage.cards.a.ArchiveHaunt.class));
cards.add(new SetCardInfo("Ardent Elementalist", 128, Rarity.COMMON, mage.cards.a.ArdentElementalist.class));
@@ -50,6 +51,8 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Augur of Autumn", 168, Rarity.RARE, mage.cards.a.AugurOfAutumn.class));
cards.add(new SetCardInfo("Awoken Demon", 100, Rarity.COMMON, mage.cards.a.AwokenDemon.class));
cards.add(new SetCardInfo("Baithook Angler", 42, Rarity.COMMON, mage.cards.b.BaithookAngler.class));
+ cards.add(new SetCardInfo("Baneblade Scoundrel", 85, Rarity.UNCOMMON, mage.cards.b.BanebladeScoundrel.class));
+ cards.add(new SetCardInfo("Baneclaw Marauder", 85, Rarity.UNCOMMON, mage.cards.b.BaneclawMarauder.class));
cards.add(new SetCardInfo("Bat Whisperer", 86, Rarity.COMMON, mage.cards.b.BatWhisperer.class));
cards.add(new SetCardInfo("Benevolent Geist", 61, Rarity.RARE, mage.cards.b.BenevolentGeist.class));
cards.add(new SetCardInfo("Bereaved Survivor", 4, Rarity.UNCOMMON, mage.cards.b.BereavedSurvivor.class));
@@ -97,6 +100,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Creeping Inn", 264, Rarity.MYTHIC, mage.cards.c.CreepingInn.class));
cards.add(new SetCardInfo("Croaking Counterpart", 215, Rarity.RARE, mage.cards.c.CroakingCounterpart.class));
cards.add(new SetCardInfo("Crossroads Candleguide", 253, Rarity.COMMON, mage.cards.c.CrossroadsCandleguide.class));
+ cards.add(new SetCardInfo("Curse of Leeches", 94, Rarity.RARE, mage.cards.c.CurseOfLeeches.class));
cards.add(new SetCardInfo("Curse of Shaken Faith", 134, Rarity.RARE, mage.cards.c.CurseOfShakenFaith.class));
cards.add(new SetCardInfo("Curse of Silence", 15, Rarity.RARE, mage.cards.c.CurseOfSilence.class));
cards.add(new SetCardInfo("Curse of Surveillance", 46, Rarity.RARE, mage.cards.c.CurseOfSurveillance.class));
@@ -132,6 +136,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Ecstatic Awakener", 100, Rarity.COMMON, mage.cards.e.EcstaticAwakener.class));
cards.add(new SetCardInfo("Electric Revelation", 135, Rarity.COMMON, mage.cards.e.ElectricRevelation.class));
cards.add(new SetCardInfo("Embodiment of Flame", 141, Rarity.UNCOMMON, mage.cards.e.EmbodimentOfFlame.class));
+ cards.add(new SetCardInfo("Enduring Angel", 17, Rarity.MYTHIC, mage.cards.e.EnduringAngel.class));
cards.add(new SetCardInfo("Evolving Wilds", 261, Rarity.COMMON, mage.cards.e.EvolvingWilds.class));
cards.add(new SetCardInfo("Fading Hope", 51, Rarity.UNCOMMON, mage.cards.f.FadingHope.class));
cards.add(new SetCardInfo("Faithful Mending", 221, Rarity.UNCOMMON, mage.cards.f.FaithfulMending.class));
@@ -196,6 +201,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Kessig Naturalist", 231, Rarity.UNCOMMON, mage.cards.k.KessigNaturalist.class));
cards.add(new SetCardInfo("Lambholt Harrier", 145, Rarity.COMMON, mage.cards.l.LambholtHarrier.class));
cards.add(new SetCardInfo("Larder Zombie", 58, Rarity.COMMON, mage.cards.l.LarderZombie.class));
+ cards.add(new SetCardInfo("Leeching Lurker", 94, Rarity.RARE, mage.cards.l.LeechingLurker.class));
cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 59, Rarity.MYTHIC, mage.cards.l.LierDiscipleOfTheDrowned.class));
cards.add(new SetCardInfo("Liesa, Forgotten Archangel", 232, Rarity.RARE, mage.cards.l.LiesaForgottenArchangel.class));
cards.add(new SetCardInfo("Light Up the Night", 146, Rarity.RARE, mage.cards.l.LightUpTheNight.class));
@@ -203,6 +209,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Lord of the Forsaken", 110, Rarity.MYTHIC, mage.cards.l.LordOfTheForsaken.class));
cards.add(new SetCardInfo("Lord of the Ulvenwald", 231, Rarity.UNCOMMON, mage.cards.l.LordOfTheUlvenwald.class));
cards.add(new SetCardInfo("Loyal Gryff", 26, Rarity.UNCOMMON, mage.cards.l.LoyalGryff.class));
+ cards.add(new SetCardInfo("Ludevic, Necrogenius", 233, Rarity.RARE, mage.cards.l.LudevicNecrogenius.class));
cards.add(new SetCardInfo("Luminous Phantom", 27, Rarity.COMMON, mage.cards.l.LuminousPhantom.class));
cards.add(new SetCardInfo("Lunar Frenzy", 147, Rarity.UNCOMMON, mage.cards.l.LunarFrenzy.class));
cards.add(new SetCardInfo("Lunarch Veteran", 27, Rarity.COMMON, mage.cards.l.LunarchVeteran.class));
@@ -230,6 +237,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Novice Occultist", 117, Rarity.COMMON, mage.cards.n.NoviceOccultist.class));
cards.add(new SetCardInfo("Obsessive Astronomer", 152, Rarity.UNCOMMON, mage.cards.o.ObsessiveAstronomer.class));
cards.add(new SetCardInfo("Odric's Outrider", 29, Rarity.UNCOMMON, mage.cards.o.OdricsOutrider.class));
+ cards.add(new SetCardInfo("Olag, Ludevic's Hubris", 233, Rarity.RARE, mage.cards.o.OlagLudevicsHubris.class));
cards.add(new SetCardInfo("Old Stickfingers", 234, Rarity.RARE, mage.cards.o.OldStickfingers.class));
cards.add(new SetCardInfo("Olivia's Midnight Ambush", 118, Rarity.COMMON, mage.cards.o.OliviasMidnightAmbush.class));
cards.add(new SetCardInfo("Ominous Roost", 65, Rarity.UNCOMMON, mage.cards.o.OminousRoost.class));
@@ -314,6 +322,7 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Tavern Ruffian", 163, Rarity.COMMON, mage.cards.t.TavernRuffian.class));
cards.add(new SetCardInfo("Tavern Smasher", 163, Rarity.COMMON, mage.cards.t.TavernSmasher.class));
cards.add(new SetCardInfo("Teferi, Who Slows the Sunset", 245, Rarity.MYTHIC, mage.cards.t.TeferiWhoSlowsTheSunset.class));
+ cards.add(new SetCardInfo("The Celestus", 252, Rarity.RARE, mage.cards.t.TheCelestus.class));
cards.add(new SetCardInfo("The Meathook Massacre", 112, Rarity.MYTHIC, mage.cards.t.TheMeathookMassacre.class));
cards.add(new SetCardInfo("Thermo-Alchemist", 164, Rarity.UNCOMMON, mage.cards.t.ThermoAlchemist.class));
cards.add(new SetCardInfo("Thraben Exorcism", 39, Rarity.COMMON, mage.cards.t.ThrabenExorcism.class));
@@ -321,10 +330,13 @@ public final class InnistradMidnightHunt extends ExpansionSet {
cards.add(new SetCardInfo("Tireless Hauler", 203, Rarity.COMMON, mage.cards.t.TirelessHauler.class));
cards.add(new SetCardInfo("Tovolar's Huntmaster", 204, Rarity.RARE, mage.cards.t.TovolarsHuntmaster.class));
cards.add(new SetCardInfo("Tovolar's Packleader", 204, Rarity.RARE, mage.cards.t.TovolarsPackleader.class));
+ cards.add(new SetCardInfo("Tovolar, Dire Overlord", 246, Rarity.RARE, mage.cards.t.TovolarDireOverlord.class));
+ cards.add(new SetCardInfo("Tovolar, the Midnight Scourge", 246, Rarity.RARE, mage.cards.t.TovolarTheMidnightScourge.class));
cards.add(new SetCardInfo("Triskaidekaphile", 81, Rarity.RARE, mage.cards.t.Triskaidekaphile.class));
cards.add(new SetCardInfo("Turn the Earth", 205, Rarity.UNCOMMON, mage.cards.t.TurnTheEarth.class));
cards.add(new SetCardInfo("Unblinking Observer", 82, Rarity.COMMON, mage.cards.u.UnblinkingObserver.class));
cards.add(new SetCardInfo("Unnatural Growth", 206, Rarity.RARE, mage.cards.u.UnnaturalGrowth.class));
+ cards.add(new SetCardInfo("Unnatural Moonrise", 247, Rarity.UNCOMMON, mage.cards.u.UnnaturalMoonrise.class));
cards.add(new SetCardInfo("Unruly Mob", 40, Rarity.COMMON, mage.cards.u.UnrulyMob.class));
cards.add(new SetCardInfo("Untamed Pup", 187, Rarity.UNCOMMON, mage.cards.u.UntamedPup.class));
cards.add(new SetCardInfo("Vadrik, Astral Archmage", 248, Rarity.RARE, mage.cards.v.VadrikAstralArchmage.class));
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java
new file mode 100644
index 0000000000..6525e7c000
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java
@@ -0,0 +1,349 @@
+package org.mage.test.cards.abilities.keywords;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.game.permanent.Permanent;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author TheElk801
+ */
+public class DayNightTest extends CardTestPlayerBase {
+
+ private static final String ruffian = "Tavern Ruffian";
+ private static final String smasher = "Tavern Smasher";
+ private static final String moonmist = "Moonmist";
+ private static final String outcasts = "Grizzled Outcasts";
+ private static final String wantons = "Krallenhorde Wantons";
+ private static final String immerwolf = "Immerwolf";
+ private static final String bolt = "Lightning Bolt";
+ private static final String curse = "Curse of Leeches";
+ private static final String lurker = "Leeching Lurker";
+ private static final String vandal = "Brimstone Vandal";
+
+ private void assertDayNight(boolean daytime) {
+ Assert.assertTrue("It should not be neither day nor night", currentGame.hasDayNight());
+ Assert.assertTrue("It should be " + (daytime ? "day" : "night"), currentGame.checkDayNight(daytime));
+ Assert.assertFalse("It should not be " + (daytime ? "night" : "day"), currentGame.checkDayNight(!daytime));
+ }
+
+ private void assertRuffianSmasher(boolean daytime) {
+ assertDayNight(daytime);
+ if (daytime) {
+ assertPowerToughness(playerA, ruffian, 2, 5);
+ assertPermanentCount(playerA, smasher, 0);
+ } else {
+ assertPermanentCount(playerA, ruffian, 0);
+ assertPowerToughness(playerA, smasher, 6, 5);
+ }
+ }
+
+ private void setDayNight(int turn, PhaseStep phaseStep, boolean daytime) {
+ runCode("set game to " + (daytime ? "day" : "night"), turn, phaseStep, playerA, (i, p, game) -> game.setDaytime(daytime));
+ }
+
+ @Test
+ public void testRegularDay() {
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(true);
+ }
+
+ @Test
+ public void testNightbound() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(false);
+ }
+
+ @Test
+ public void testDayToNightTransform() {
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+ setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, false);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(false);
+ }
+
+ @Test
+ public void testNightToDayTransform() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+ setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, true);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(true);
+ }
+
+ @Test
+ public void testMoonmistFails() {
+ addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+ addCard(Zone.BATTLEFIELD, playerA, ruffian);
+ addCard(Zone.BATTLEFIELD, playerA, outcasts);
+ addCard(Zone.HAND, playerA, moonmist);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, moonmist);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(true);
+ assertPermanentCount(playerA, outcasts, 0);
+ assertPowerToughness(playerA, wantons, 7, 7);
+ }
+
+ @Test
+ public void testImmerwolfPreventsTransformation() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+ addCard(Zone.BATTLEFIELD, playerA, immerwolf);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+ setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, true);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(true);
+ assertPowerToughness(playerA, smasher, 6 + 1, 5 + 1);
+ assertPermanentCount(playerA, ruffian, 0);
+ }
+
+ @Test
+ public void testImmerwolfRemoved() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
+ addCard(Zone.BATTLEFIELD, playerA, immerwolf);
+ addCard(Zone.HAND, playerA, bolt);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+ setDayNight(1, PhaseStep.BEGIN_COMBAT, true);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_COMBAT);
+ execute();
+
+ assertDayNight(true);
+ assertPowerToughness(playerA, smasher, 6 + 1, 5 + 1);
+ assertPermanentCount(playerA, ruffian, 0);
+
+ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bolt, immerwolf);
+
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(true);
+ }
+
+ @Test
+ public void testNoSpellsBecomesNight() {
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+ addCard(Zone.HAND, playerA, ruffian);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertRuffianSmasher(true);
+
+ setStopAt(2, PhaseStep.END_TURN);
+ execute();
+
+ assertRuffianSmasher(true);
+
+ setStopAt(3, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(false);
+ }
+
+ @Test
+ public void testTwoSpellsBecomesDay() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
+ addCard(Zone.HAND, playerA, ruffian);
+ addCard(Zone.HAND, playerA, bolt);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian);
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertLife(playerB, 20 - 3);
+ assertGraveyardCount(playerA, bolt, 1);
+ assertRuffianSmasher(false);
+
+ setStopAt(2, PhaseStep.END_TURN);
+ execute();
+
+ assertRuffianSmasher(true);
+
+ setStopAt(3, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertRuffianSmasher(false);
+ }
+
+ @Test
+ public void testCurseOfLeechesRegular() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, curse);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(true);
+ Permanent permanent = getPermanent(curse);
+ Assert.assertTrue("Curse is attached to playerB", permanent.isAttachedTo(playerB.getId()));
+ assertPermanentCount(playerA, lurker, 0);
+ }
+
+ @Test
+ public void testCurseOfLeechesNightbound() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, curse);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(false);
+ assertPermanentCount(playerA, curse, 0);
+ assertPermanentCount(playerA, lurker, 1);
+ }
+
+ @Test
+ public void testCurseOfLeechesDayToNight() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, curse);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB);
+ setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, false);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(false);
+ assertPermanentCount(playerA, curse, 0);
+ assertPermanentCount(playerA, lurker, 1);
+ }
+
+ @Test
+ public void testCurseOfLeechesNightToDay() {
+ currentGame.setDaytime(false);
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, curse);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB);
+ setChoice(playerA, playerB.getName());
+ setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, true);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(true);
+ Permanent permanent = getPermanent(curse);
+ Assert.assertTrue("Curse is attached to playerB", permanent.isAttachedTo(playerB.getId()));
+ assertPermanentCount(playerA, lurker, 0);
+ }
+
+ @Test
+ public void testBrimstoneVandalBecomeDay() {
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+ addCard(Zone.HAND, playerA, vandal);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vandal);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(true);
+ assertLife(playerB, 20);
+ }
+
+ @Test
+ public void testBrimstoneVandalTrigger() {
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+ addCard(Zone.HAND, playerA, bolt, 2);
+ addCard(Zone.HAND, playerA, vandal);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vandal);
+
+ setStrictChooseMode(true);
+ setStopAt(3, PhaseStep.UPKEEP);
+ execute();
+
+ assertDayNight(false);
+ assertLife(playerB, 20 - 1);
+
+ castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
+ castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
+
+ setStopAt(4, PhaseStep.UPKEEP);
+ execute();
+ assertAllCommandsUsed();
+
+ assertDayNight(true);
+ assertLife(playerB, 20 - 1 - 3 - 3 - 1);
+ }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java
index 5329f99df1..9ef18543e7 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java
@@ -1,15 +1,13 @@
package org.mage.test.cards.abilities.keywords;
+import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
-import mage.game.permanent.Permanent;
-import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
- *
* @author LevelX2
*/
public class TransformTest extends CardTestPlayerBase {
@@ -133,7 +131,7 @@ public class TransformTest extends CardTestPlayerBase {
* 4G Creature - Human Shaman Whenever a permanent you control transforms
* into a non-Human creature, put a 2/2 green Wolf creature token onto the
* battlefield.
- *
+ *
* Reported bug: "It appears to trigger either when a non-human creature
* transforms OR when a creature transforms from a non-human into a human
* (as in when a werewolf flips back to the sun side), rather than when a
@@ -171,24 +169,51 @@ public class TransformTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Startled Awake"); // SORCERY {2}{U}{U}"
addCard(Zone.BATTLEFIELD, playerA, "Island", 9);
- castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake");
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake", playerB);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}{U}");
+
+ setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
+ assertAllCommandsUsed();
assertGraveyardCount(playerB, 13);
assertGraveyardCount(playerA, "Startled Awake", 0);
assertPermanentCount(playerA, "Persistent Nightmare", 1); // Night-side card of Startled Awake
- Permanent nightmare = getPermanent("Persistent Nightmare", playerA);
- Assert.assertTrue("Has to have creature card type", nightmare.isCreature(currentGame));
- Assert.assertFalse("Has not to have sorcery card type", nightmare.isSorcery(currentGame));
+ assertType("Persistent Nightmare", CardType.CREATURE, true);
+ assertType("Persistent Nightmare", CardType.SORCERY, false);
+ }
+
+ @Test
+ public void testStartledAwakeMoonmist() {
+ addCard(Zone.HAND, playerA, "Startled Awake");
+ addCard(Zone.HAND, playerA, "Moonmist");
+ addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 11);
+ addCard(Zone.BATTLEFIELD, playerA, "Maskwood Nexus");
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake", playerB);
+
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}{U}");
+
+ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Moonmist");
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertGraveyardCount(playerB, 13);
+ assertGraveyardCount(playerA, "Startled Awake", 0);
+ assertPermanentCount(playerA, "Persistent Nightmare", 1); // Night-side card of Startled Awake
+ assertType("Persistent Nightmare", CardType.CREATURE, true);
+ assertType("Persistent Nightmare", CardType.SORCERY, false);
}
/**
* When copy token of Lambholt Pacifist transforms with "its transform
* ability", I see below error. Then rollback.
- *
+ *
* 701.25a Only permanents represented by double-faced cards can transform.
* (See rule 711, “Double-Faced Cards.”) If a spell or ability instructs a
* player to transform any permanent that isn‘t represented by a
@@ -221,7 +246,7 @@ public class TransformTest extends CardTestPlayerBase {
/**
* Mirror Mockery copies the front face of a Transformed card rather than
* the current face.
- *
+ *
* It's worth pointing out that my opponent cast Mirror Mockery the previous
* turn - after it had transformed. I should have included the part of the
* log that showed that Mirror Mockery was applied to the Unimpeded
@@ -280,11 +305,13 @@ public class TransformTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Wastes", 3);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion");
+ waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
- activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}{C}", "Archangel Avacyn", "Whenever a non-Angel creature you control dies");
+ activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}{C}", "Archangel Avacyn");
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
execute();
+ assertAllCommandsUsed();
assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
@@ -317,9 +344,9 @@ public class TransformTest extends CardTestPlayerBase {
* was on stack, my opponent used Displacer's ability targeting Huntmaster.
* That ability resolved and Huntmaster still transformed like it never left
* the battlefield.
- *
+ *
* http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=20014&p=210533#p210513
- *
+ *
* The transform effect on the stack should fizzle. The card brought back
* from Exile should be a new object unless I am interpreting the rules
* incorrectly. The returned permanent uses the same GUID.
@@ -356,12 +383,13 @@ public class TransformTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Ravager of the Fells", 0);
assertPermanentCount(playerA, "Huntmaster of the Fells", 1);
- assertPowerToughness(playerA, "Huntmaster of the Fells", 2, 2);
+ assertPowerToughness(playerA, "Huntmaster of the Fells", 2, 2);
assertTappedCount("Plains", true, 2);
assertTappedCount("Wastes", true, 1);
}
- @Test
+
+ @Test
public void testHuntmasterTransformed() {
// Whenever this creature enters the battlefield or transforms into Huntmaster of the Fells, create a 2/2 green Wolf creature token and you gain 2 life.
// At the beginning of each upkeep, if no spells were cast last turn, transform Huntmaster of the Fells.
@@ -387,15 +415,16 @@ public class TransformTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Ravager of the Fells", 0);
assertPermanentCount(playerA, "Huntmaster of the Fells", 1);
assertPowerToughness(playerA, "Huntmaster of the Fells", 2, 2);
-
+
}
+
/**
* Having cast Phantasmal Image copying my opponent's flipped Thing in the
* Ice, I was left with a 0/4 Awoken Horror.
- *
+ *
* https://github.com/magefree/mage/issues/5893
- *
+ *
* The transform effect on the stack should fizzle. The card brought back
* from Exile should be a new object unless I am interpreting the rules
* incorrectly. The returned permanent uses the same GUID.
@@ -440,4 +469,32 @@ public class TransformTest extends CardTestPlayerBase {
}
+ @Test
+ public void testMoonmistDelver() {
+ addCard(Zone.BATTLEFIELD, playerA, "Island");
+ addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+ addCard(Zone.HAND, playerA, "Delver of Secrets");
+ addCard(Zone.HAND, playerA, "Moonmist", 2);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delver of Secrets");
+ waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Moonmist");
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.BEGIN_COMBAT);
+ execute();
+
+ assertPermanentCount(playerA, "Delver of Secrets", 0);
+ assertPermanentCount(playerA, "Insectile Aberration", 1);
+
+ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Moonmist");
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertAllCommandsUsed();
+
+ assertPermanentCount(playerA, "Delver of Secrets", 1);
+ assertPermanentCount(playerA, "Insectile Aberration", 0);
+ }
}
diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java
index 63cb477b0c..725c671925 100644
--- a/Mage/src/main/java/mage/abilities/Ability.java
+++ b/Mage/src/main/java/mage/abilities/Ability.java
@@ -531,6 +531,10 @@ public interface Ability extends Controllable, Serializable {
*/
Permanent getSourcePermanentOrLKI(Game game);
+ void setSourcePermanentTransformCount(Game game);
+
+ boolean checkTransformCount(Permanent permanent, Game game);
+
String getTargetDescription(Targets targets, Game game);
void setCanFizzle(boolean canFizzle);
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 4430621822..e9cdf56991 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -80,6 +80,7 @@ public abstract class AbilityImpl implements Ability {
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)
protected String appendToRule = null;
+ protected int sourcePermanentTransformCount = 0;
public AbilityImpl(AbilityType abilityType, Zone zone) {
this.id = UUID.randomUUID();
@@ -135,6 +136,7 @@ public abstract class AbilityImpl implements Ability {
this.identifier = ability.identifier;
this.activated = ability.activated;
this.appendToRule = ability.appendToRule;
+ this.sourcePermanentTransformCount = ability.sourcePermanentTransformCount;
}
@Override
@@ -246,6 +248,7 @@ public abstract class AbilityImpl implements Ability {
if (getSourceObjectZoneChangeCounter() == 0) {
setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId()));
}
+ setSourcePermanentTransformCount(game);
/* 20130201 - 601.2b
* If the player wishes to splice any cards onto the spell (see rule 702.45), he
@@ -1292,6 +1295,24 @@ public abstract class AbilityImpl implements Ability {
return sourceObjectZoneChangeCounter;
}
+ @Override
+ public void setSourcePermanentTransformCount(Game game) {
+ Permanent permanent = getSourcePermanentOrLKI(game);
+ if (permanent != null) {
+ this.sourcePermanentTransformCount = permanent.getTransformCount();
+ }
+ }
+
+ @Override
+ public boolean checkTransformCount(Permanent permanent, Game game) {
+ if (permanent == null
+ || !permanent.getId().equals(sourceId)
+ || permanent.getZoneChangeCounter(game) != sourceObjectZoneChangeCounter) {
+ return true;
+ }
+ return permanent.getTransformCount() == sourcePermanentTransformCount;
+ }
+
@Override
public boolean canFizzle() {
return canFizzle;
diff --git a/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java b/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java
index ac555d784e..80ec12f0dc 100644
--- a/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java
@@ -2,17 +2,18 @@ package mage.abilities.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
+import mage.abilities.hint.common.DayNightHint;
import mage.constants.Outcome;
import mage.game.Game;
/**
* @author TheElk801
- * TODO: this is just a placeholder for the actual ability
*/
public class BecomeDayAsEntersAbility extends EntersBattlefieldAbility {
public BecomeDayAsEntersAbility() {
super(new BecomeDayEffect());
+ this.addHint(DayNightHint.instance);
}
private BecomeDayAsEntersAbility(final BecomeDayAsEntersAbility ability) {
@@ -33,7 +34,7 @@ public class BecomeDayAsEntersAbility extends EntersBattlefieldAbility {
class BecomeDayEffect extends OneShotEffect {
BecomeDayEffect() {
- super(Outcome.Benefit);
+ super(Outcome.Neutral);
}
private BecomeDayEffect(final BecomeDayEffect effect) {
@@ -47,6 +48,10 @@ class BecomeDayEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- return true;
+ if (!game.hasDayNight()) {
+ game.setDaytime(true);
+ return true;
+ }
+ return false;
}
}
diff --git a/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java
index e955649af1..237805acbb 100644
--- a/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java
@@ -8,7 +8,6 @@ import mage.game.events.GameEvent;
/**
* @author TheElk801
- * TODO: this is just a placeholder for the actual ability
*/
public class BecomesDayOrNightTriggeredAbility extends TriggeredAbilityImpl {
@@ -26,7 +25,7 @@ public class BecomesDayOrNightTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
- return false;
+ return event.getType() == GameEvent.EventType.BECOMES_DAY_NIGHT;
}
@Override
diff --git a/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java
index 536d4c10a0..1915abb7ec 100644
--- a/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java
@@ -11,7 +11,7 @@ import mage.game.Game;
public class WerewolfBackTriggeredAbility extends BeginningOfUpkeepTriggeredAbility {
public WerewolfBackTriggeredAbility() {
- super(new TransformSourceEffect(false), TargetController.ANY, false);
+ super(new TransformSourceEffect(), TargetController.ANY, false);
}
private WerewolfBackTriggeredAbility(final WerewolfBackTriggeredAbility ability) {
diff --git a/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java
index 70d91b1a37..033cb06681 100644
--- a/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java
@@ -11,7 +11,7 @@ import mage.game.Game;
public class WerewolfFrontTriggeredAbility extends BeginningOfUpkeepTriggeredAbility {
public WerewolfFrontTriggeredAbility() {
- super(new TransformSourceEffect(true), TargetController.ANY, false);
+ super(new TransformSourceEffect(), TargetController.ANY, false);
}
private WerewolfFrontTriggeredAbility(final WerewolfFrontTriggeredAbility ability) {
diff --git a/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java b/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java
index 29e5c709d6..11a6cd4bb5 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java
@@ -6,14 +6,13 @@ import mage.game.Game;
/**
* @author TheElk801
- * TODO: Implement this
*/
public enum NightCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
- return false;
+ return game.checkDayNight(false);
}
@Override
diff --git a/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java
index 13e8b95d1f..329cab439e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java
@@ -1,43 +1,23 @@
-
package mage.abilities.effects.common;
-import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
-import mage.game.permanent.PermanentCard;
/**
- *
* @author nantuko
*/
public class TransformSourceEffect extends OneShotEffect {
- private boolean withoutTrigger;
- private boolean fromDayToNight;
-
- /**
- * @param fromDayToNight Defines whether we transform from "day" side to
- * "night" or vice versa.
- */
- public TransformSourceEffect(boolean fromDayToNight) {
- this(fromDayToNight, false);
- }
-
- public TransformSourceEffect(boolean fromDayToNight, boolean withoutTrigger) {
+ public TransformSourceEffect() {
super(Outcome.Transform);
- this.withoutTrigger = withoutTrigger;
- this.fromDayToNight = fromDayToNight;
staticText = "transform {this}";
}
public TransformSourceEffect(final TransformSourceEffect effect) {
super(effect);
- this.withoutTrigger = effect.withoutTrigger;
- this.fromDayToNight = effect.fromDayToNight;
}
@Override
@@ -47,37 +27,8 @@ public class TransformSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- MageObject sourceObject = source.getSourceObjectIfItStillExists(game); // Transform only if it's the same object as the effect was put on the stack
- if (sourceObject instanceof Permanent) {
- Permanent sourcePermanent = (Permanent) sourceObject;
- if (sourcePermanent.canTransform(source, game)) {
- // check not to transform twice the same side
- if (sourcePermanent.isTransformed() != fromDayToNight) {
- if (withoutTrigger) {
- sourcePermanent.setTransformed(fromDayToNight);
- } else {
- if (sourcePermanent.isTransformed()) {
- Card orgCard = game.getCard(source.getSourceId());
- sourcePermanent.getPower().modifyBaseValue(orgCard.getPower().getValue());
- sourcePermanent.getToughness().modifyBaseValue(orgCard.getToughness().getValue());
- }
- sourcePermanent.transform(game);
- }
- if (!game.isSimulation()) {
- if (fromDayToNight) {
- if (sourcePermanent.getSecondCardFace() != null) {
- if (sourcePermanent instanceof PermanentCard) {
- game.informPlayers(((PermanentCard) sourcePermanent).getCard().getLogName() + " transforms into " + sourcePermanent.getSecondCardFace().getLogName());
- }
- }
- } else {
- game.informPlayers(sourcePermanent.getSecondCardFace().getLogName() + " transforms into " + sourcePermanent.getLogName());
- }
- }
- }
- }
- }
- return true;
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
+ // check not to transform twice the same side
+ return permanent != null && permanent.transform(source, game);
}
-
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java
deleted file mode 100644
index b128ae0108..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package mage.abilities.effects.common;
-
-import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.effects.OneShotEffect;
-import mage.constants.Outcome;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.game.permanent.PermanentCard;
-import mage.target.Target;
-import mage.util.CardUtil;
-
-/**
- *
- * @author LevelX2
- */
-public class TransformTargetEffect extends OneShotEffect {
-
- private boolean withoutTrigger;
-
- public TransformTargetEffect() {
- this(true);
- }
-
- public TransformTargetEffect(boolean withoutTrigger) {
- super(Outcome.Transform);
- this.withoutTrigger = withoutTrigger;
- }
-
- public TransformTargetEffect(final TransformTargetEffect effect) {
- super(effect);
- this.withoutTrigger = effect.withoutTrigger;
- }
-
- @Override
- public TransformTargetEffect copy() {
- return new TransformTargetEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
- if (permanent != null) {
- if (permanent.canTransform(source, game)) {
- // check not to transform twice the same side
- if (withoutTrigger) {
- permanent.setTransformed(!permanent.isTransformed());
- } else {
- permanent.transform(game);
- }
- if (!game.isSimulation()) {
- if (permanent.isTransformed()) {
- if (permanent.getSecondCardFace() != null) {
- if (permanent instanceof PermanentCard) {
- game.informPlayers(((PermanentCard) permanent).getCard().getLogName() + " transforms into " + permanent.getSecondCardFace().getLogName());
- }
- }
- } else {
- game.informPlayers(permanent.getSecondCardFace().getLogName() + " transforms into " + permanent.getLogName());
- }
- }
- }
-
- return true;
- }
- return false;
- }
-
- @Override
- public String getText(Mode mode) {
- if (staticText != null && !staticText.isEmpty()) {
- return staticText;
- }
- if (mode.getTargets().isEmpty()) {
- return "transform target";
- }
- Target target = mode.getTargets().get(0);
- if (target.getMaxNumberOfTargets() > 1) {
- if (target.getMaxNumberOfTargets() == target.getNumberOfTargets()) {
- return "transform " + CardUtil.numberToText(target.getNumberOfTargets()) + " target " + target.getTargetName();
- } else {
- return "transform up to " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + " target " + target.getTargetName();
- }
- } else {
- return "transform target " + mode.getTargets().get(0).getTargetName();
- }
- }
-}
diff --git a/Mage/src/main/java/mage/abilities/hint/common/DayNightHint.java b/Mage/src/main/java/mage/abilities/hint/common/DayNightHint.java
new file mode 100644
index 0000000000..a4b56be021
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/hint/common/DayNightHint.java
@@ -0,0 +1,40 @@
+package mage.abilities.hint.common;
+
+import mage.abilities.Ability;
+import mage.abilities.hint.Hint;
+import mage.game.Game;
+import mage.watchers.common.CastSpellLastTurnWatcher;
+
+/**
+ * @author TheElk801
+ */
+public enum DayNightHint implements Hint {
+ instance;
+
+ @Override
+ public String getText(Game game, Ability ability) {
+ if (!game.hasDayNight()) {
+ return "It's neither day nor night.";
+ }
+ boolean isDay = game.checkDayNight(true);
+ int spellsThisTurn = game
+ .getState()
+ .getWatcher(CastSpellLastTurnWatcher.class)
+ .getActivePlayerThisTurnCount();
+ StringBuilder sb = new StringBuilder("It's currently ");
+ sb.append(isDay ? "day" : "night");
+ sb.append(", active player has cast ");
+ sb.append(spellsThisTurn);
+ sb.append(" spells this turn. It will ");
+ sb.append((isDay ? spellsThisTurn == 0 : spellsThisTurn >= 2) ? "" : "not");
+ sb.append(" become ");
+ sb.append(isDay ? "night" : "day");
+ sb.append(" next turn.");
+ return sb.toString();
+ }
+
+ @Override
+ public DayNightHint copy() {
+ return this;
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/hint/common/NightHint.java b/Mage/src/main/java/mage/abilities/hint/common/NightHint.java
deleted file mode 100644
index afc7f2631d..0000000000
--- a/Mage/src/main/java/mage/abilities/hint/common/NightHint.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package mage.abilities.hint.common;
-
-import mage.abilities.Ability;
-import mage.abilities.condition.common.NightCondition;
-import mage.abilities.hint.ConditionHint;
-import mage.abilities.hint.Hint;
-import mage.game.Game;
-
-/**
- * @author TheElk801
- */
-public enum NightHint implements Hint {
- instance;
- private static final Hint hint = new ConditionHint(
- NightCondition.instance, "It's currently night"
- );
-
- @Override
- public String getText(Game game, Ability ability) {
- return hint.getText(game, ability);
- }
-
- @Override
- public Hint copy() {
- return this;
- }
-}
diff --git a/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java b/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java
index 1e8761d6b4..619210a997 100644
--- a/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java
@@ -1,16 +1,20 @@
package mage.abilities.keyword;
+import mage.abilities.Ability;
import mage.abilities.StaticAbility;
-import mage.constants.Zone;
+import mage.abilities.effects.ContinuousEffectImpl;
+import mage.abilities.hint.common.DayNightHint;
+import mage.constants.*;
+import mage.game.Game;
/**
* @author TheElk801
- * TODO: Implement this
*/
public class DayboundAbility extends StaticAbility {
public DayboundAbility() {
- super(Zone.BATTLEFIELD, null);
+ super(Zone.BATTLEFIELD, new DayboundEffect());
+ this.addHint(DayNightHint.instance);
}
private DayboundAbility(final DayboundAbility ability) {
@@ -27,3 +31,27 @@ public class DayboundAbility extends StaticAbility {
return new DayboundAbility(this);
}
}
+
+class DayboundEffect extends ContinuousEffectImpl {
+
+ DayboundEffect() {
+ super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
+ }
+
+ private DayboundEffect(final DayboundEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public DayboundEffect copy() {
+ return new DayboundEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ if (!game.hasDayNight()) {
+ game.setDaytime(true);
+ }
+ return true;
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java b/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java
index 804b63c83c..5269cd301b 100644
--- a/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java
@@ -1,16 +1,21 @@
package mage.abilities.keyword;
+import mage.abilities.Ability;
import mage.abilities.StaticAbility;
-import mage.constants.Zone;
+import mage.abilities.effects.ContinuousEffectImpl;
+import mage.abilities.hint.common.DayNightHint;
+import mage.cards.Card;
+import mage.constants.*;
+import mage.game.Game;
/**
* @author TheElk801
- * TODO: Implement this
*/
public class NightboundAbility extends StaticAbility {
public NightboundAbility() {
- super(Zone.BATTLEFIELD, null);
+ super(Zone.BATTLEFIELD, new NightboundEffect());
+ this.addHint(DayNightHint.instance);
}
private NightboundAbility(final NightboundAbility ability) {
@@ -26,4 +31,34 @@ public class NightboundAbility extends StaticAbility {
public NightboundAbility copy() {
return new NightboundAbility(this);
}
+
+ public static boolean checkCard(Card card, Game game) {
+ return game.checkDayNight(false)
+ && card.getSecondCardFace() != null
+ && card.getSecondCardFace().getAbilities().containsClass(NightboundAbility.class);
+ }
+}
+
+class NightboundEffect extends ContinuousEffectImpl {
+
+ NightboundEffect() {
+ super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
+ }
+
+ private NightboundEffect(final NightboundEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public NightboundEffect copy() {
+ return new NightboundEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ if (!game.hasDayNight()) {
+ game.setDaytime(false);
+ }
+ return true;
+ }
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
index 7017d0cd06..37223ad614 100644
--- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
@@ -64,7 +64,6 @@ public class TransformAbility extends SimpleStaticAbility {
}
permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
- permanent.setTransformable(sourceCard.isTransformable());
}
}
diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java
index c00bffaf70..5db0ca7208 100644
--- a/Mage/src/main/java/mage/cards/Card.java
+++ b/Mage/src/main/java/mage/cards/Card.java
@@ -70,8 +70,6 @@ public interface Card extends MageObject {
boolean isTransformable();
- void setTransformable(boolean transformable);
-
Card getSecondCardFace();
boolean isNightCard();
diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java
index e20db01632..a06431ed07 100644
--- a/Mage/src/main/java/mage/cards/CardImpl.java
+++ b/Mage/src/main/java/mage/cards/CardImpl.java
@@ -43,7 +43,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
protected String tokenSetCode;
protected String tokenDescriptor;
protected Rarity rarity;
- protected boolean transformable;
protected Class> secondSideCardClazz;
protected Card secondSideCard;
protected boolean nightCard;
@@ -121,7 +120,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
tokenDescriptor = card.tokenDescriptor;
rarity = card.rarity;
- transformable = card.transformable;
secondSideCardClazz = card.secondSideCardClazz;
secondSideCard = null; // will be set on first getSecondCardFace call if card has one
nightCard = card.nightCard;
@@ -618,12 +616,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
@Override
public boolean isTransformable() {
- return this.transformable;
- }
-
- @Override
- public void setTransformable(boolean transformable) {
- this.transformable = transformable;
+ return this.secondSideCardClazz != null || this.nightCard;
}
@Override
diff --git a/Mage/src/main/java/mage/cards/mock/MockCard.java b/Mage/src/main/java/mage/cards/mock/MockCard.java
index 0a6b411344..1e94fe8d55 100644
--- a/Mage/src/main/java/mage/cards/mock/MockCard.java
+++ b/Mage/src/main/java/mage/cards/mock/MockCard.java
@@ -63,7 +63,6 @@ public class MockCard extends CardImpl {
this.flipCard = card.isFlipCard();
- this.transformable = card.isDoubleFaced();
this.nightCard = card.isNightCard();
if (card.getSecondSideName() != null && !card.getSecondSideName().isEmpty()) {
this.secondSideCard = new MockCard(CardRepository.instance.findCardWPreferredSet(card.getSecondSideName(), card.getSetCode(), false));
diff --git a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java
index d0b3b86113..de5e116b60 100644
--- a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java
+++ b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java
@@ -37,7 +37,6 @@ public class MockSplitCard extends SplitCard {
this.color = card.getColor();
this.flipCard = card.isFlipCard();
- this.transformable = card.isDoubleFaced();
this.nightCard = card.isNightCard();
if (card.getSecondSideName() != null && !card.getSecondSideName().isEmpty()) {
this.secondSideCard = new MockCard(CardRepository.instance.findCardWPreferredSet(card.getSecondSideName(), card.getSetCode(), false));
diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java
index 9cbdbf01d5..1ec9f2439f 100644
--- a/Mage/src/main/java/mage/game/Game.java
+++ b/Mage/src/main/java/mage/game/Game.java
@@ -386,6 +386,26 @@ public interface Game extends MageItem, Serializable, Copyable {
void ventureIntoDungeon(UUID playerId);
+ /**
+ * Tells whether the current game has day or night, defaults to false
+ */
+ boolean hasDayNight();
+
+ /**
+ * Sets game to day or night, sets hasDayNight to true
+ *
+ * @param daytime day is true, night is false
+ */
+ void setDaytime(boolean daytime);
+
+ /**
+ * Returns true if hasDayNight is true and parameter matches current day/night value
+ * Returns false if hasDayNight is false
+ *
+ * @param daytime day is true, night is false
+ */
+ boolean checkDayNight(boolean daytime);
+
/**
* Adds a permanent to the battlefield
*
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 71cb83352f..d21094e545 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -14,10 +14,7 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.PreventionEffectData;
import mage.abilities.effects.common.CopyEffect;
import mage.abilities.effects.common.InfoEffect;
-import mage.abilities.keyword.BestowAbility;
-import mage.abilities.keyword.CompanionAbility;
-import mage.abilities.keyword.MorphAbility;
-import mage.abilities.keyword.TransformAbility;
+import mage.abilities.keyword.*;
import mage.abilities.mana.DelayedTriggeredManaAbility;
import mage.abilities.mana.TriggeredManaAbility;
import mage.actions.impl.MageAction;
@@ -552,6 +549,35 @@ public abstract class GameImpl implements Game {
fireEvent(GameEvent.getEvent(GameEvent.EventType.VENTURED, playerId, null, playerId));
}
+ @Override
+ public boolean hasDayNight() {
+ return state.isHasDayNight();
+ }
+
+ @Override
+ public void setDaytime(boolean daytime) {
+ if (!state.isHasDayNight()) {
+ informPlayers("It has become " + (daytime ? "day" : "night"));
+ }
+ if (!state.setDaytime(daytime)) {
+ return;
+ }
+ // TODO: add day/night sound effect
+ informPlayers("It has become " + (daytime ? "day" : "night"));
+ fireEvent(GameEvent.getEvent(GameEvent.EventType.BECOMES_DAY_NIGHT, null, null, null));
+ for (Permanent permanent : state.getBattlefield().getAllPermanents()) {
+ if ((daytime && permanent.getAbilities(this).containsClass(NightboundAbility.class))
+ || (!daytime && permanent.getAbilities(this).containsClass(DayboundAbility.class))) {
+ permanent.transform(null, this, true);
+ }
+ }
+ }
+
+ @Override
+ public boolean checkDayNight(boolean daytime) {
+ return state.isHasDayNight() && state.isDaytime() == daytime;
+ }
+
@Override
public UUID getOwnerId(UUID objectId) {
return getOwnerId(getObject(objectId));
@@ -1933,6 +1959,9 @@ public abstract class GameImpl implements Game {
if (newAbility.getSourceObjectZoneChangeCounter() == 0) {
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId()));
}
+ if (!(newAbility instanceof DelayedTriggeredAbility)) {
+ newAbility.setSourcePermanentTransformCount(this);
+ }
newAbility.setTriggerEvent(triggeringEvent);
state.addTriggeredAbility(newAbility);
}
@@ -1949,6 +1978,7 @@ public abstract class GameImpl implements Game {
newAbility.newId();
if (source != null) {
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId()));
+ newAbility.setSourcePermanentTransformCount(this);
}
newAbility.initOnAdding(this);
// ability.init is called as the ability triggeres not now.
@@ -2585,6 +2615,17 @@ public abstract class GameImpl implements Game {
}
}
+ // Daybound/Nightbound permanents should be transformed according to day/night
+ // This is not a state-based action but it's unclear where else to put it
+ if (hasDayNight()) {
+ for (Permanent permanent : getBattlefield().getAllActivePermanents()) {
+ if ((permanent.getAbilities(this).containsClass(DayboundAbility.class) && !state.isDaytime())
+ || (permanent.getAbilities(this).containsClass(NightboundAbility.class) && state.isDaytime())) {
+ somethingHappened = permanent.transform(null, this, true) || somethingHappened;
+ }
+ }
+ }
+
//TODO: implement the rest
return somethingHappened;
}
@@ -2757,6 +2798,8 @@ public abstract class GameImpl implements Game {
@Override
public void informPlayers(String message) {
+ // Uncomment to print game messages
+ // System.out.println(message.replaceAll("\\<.*?\\>", ""));
if (simulation) {
return;
}
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index 491c26b587..1bb704405f 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -105,6 +105,8 @@ public class GameState implements Serializable, Copyable {
private final Map usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>();
private Set commandersToStay = new HashSet<>(); // commanders that do not go back to command zone
private boolean manaBurn = false;
+ private boolean hasDayNight = false;
+ private boolean isDaytime = true;
private int applyEffectsCounter; // Upcounting number of each applyEffects execution
@@ -193,6 +195,8 @@ public class GameState implements Serializable, Copyable {
state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter)
-> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy()));
this.commandersToStay.addAll(state.commandersToStay);
+ this.hasDayNight = state.hasDayNight;
+ this.isDaytime = state.isDaytime;
}
public void clearOnGameRestart() {
@@ -280,6 +284,8 @@ public class GameState implements Serializable, Copyable {
state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter)
-> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy()));
this.commandersToStay = state.commandersToStay;
+ this.hasDayNight = state.hasDayNight;
+ this.isDaytime = state.isDaytime;
}
@Override
@@ -872,7 +878,7 @@ public class GameState implements Serializable, Copyable {
for (Map.Entry> entry : eventsByKey.entrySet()) {
Set movedCards = new LinkedHashSet<>();
Set movedTokens = new LinkedHashSet<>();
- for (Iterator it = entry.getValue().iterator(); it.hasNext();) {
+ for (Iterator it = entry.getValue().iterator(); it.hasNext(); ) {
GameEvent event = it.next();
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
UUID targetId = castEvent.getTargetId();
@@ -946,8 +952,8 @@ public class GameState implements Serializable, Copyable {
* span
*
* @param ability
- * @param sourceId - if source object can be moved between zones then you
- * must set it here (each game cycle clear all source related triggers)
+ * @param sourceId - if source object can be moved between zones then you
+ * must set it here (each game cycle clear all source related triggers)
* @param attachedTo
*/
public void addAbility(Ability ability, UUID sourceId, MageObject attachedTo) {
@@ -1153,8 +1159,8 @@ public class GameState implements Serializable, Copyable {
* @param attachedTo
* @param ability
* @param copyAbility copies non MageSingleton abilities before adding to
- * state (allows to have multiple instances in one object, e.g. false param
- * will simulate keyword/singleton)
+ * state (allows to have multiple instances in one object, e.g. false param
+ * will simulate keyword/singleton)
*/
public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) {
checkWrongDynamicAbilityUsage(attachedTo, ability);
@@ -1413,6 +1419,21 @@ public class GameState implements Serializable, Copyable {
return manaBurn;
}
+ boolean isHasDayNight() {
+ return hasDayNight;
+ }
+
+ boolean setDaytime(boolean daytime) {
+ boolean flag = this.hasDayNight && this.isDaytime != daytime;
+ this.hasDayNight = true;
+ this.isDaytime = daytime;
+ return flag;
+ }
+
+ boolean isDaytime() {
+ return isDaytime;
+ }
+
@Override
public String toString() {
return CardUtil.getTurnInfo(this);
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index 69c5415482..8a5f93b38f 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -334,7 +334,7 @@ public class GameEvent implements Serializable {
UNTAP, UNTAPPED,
FLIP, FLIPPED,
UNFLIP, UNFLIPPED,
- TRANSFORM, TRANSFORMED,
+ TRANSFORM, TRANSFORMING, TRANSFORMED,
ADAPT,
BECOMES_MONSTROUS,
/* BECOMES_EXERTED
@@ -356,6 +356,7 @@ public class GameEvent implements Serializable {
*/
BECOME_MONARCH,
BECOMES_MONARCH,
+ BECOMES_DAY_NIGHT,
MEDITATED,
PHASE_OUT, PHASED_OUT,
PHASE_IN, PHASED_IN,
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index 65b4704253..b91b9f740a 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -47,12 +47,16 @@ public interface Permanent extends Card, Controllable {
boolean flip(Game game);
- boolean transform(Game game);
+ boolean transform(Ability source, Game game);
+
+ boolean transform(Ability source, Game game, boolean ignoreDayNight);
boolean isTransformed();
void setTransformed(boolean value);
+ int getTransformCount();
+
boolean isPhasedIn();
boolean isPhasedOutIndirectly();
@@ -289,15 +293,6 @@ public interface Permanent extends Card, Controllable {
*/
boolean canUseActivatedAbilities(Game game);
- /**
- * Checks by restriction effects if the permanent can transform
- *
- * @param ability the ability that causes the transform
- * @param game
- * @return true - permanent can transform
- */
- boolean canTransform(Ability ability, Game game);
-
boolean removeFromCombat(Game game);
boolean removeFromCombat(Game game, boolean withInfo);
@@ -413,5 +408,4 @@ public interface Permanent extends Card, Controllable {
}
return getAttachedTo().equals(otherId);
}
-
}
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java
index 8b813625aa..13c4dc60f5 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java
@@ -5,6 +5,7 @@ import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
+import mage.abilities.keyword.NightboundAbility;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.cards.LevelerCard;
@@ -70,7 +71,8 @@ public class PermanentCard extends PermanentImpl {
maxLevelCounters = ((LevelerCard) card).getMaxLevelCounters();
}
if (isTransformable()) {
- if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null) {
+ if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null
+ || NightboundAbility.checkCard(this, game)) {
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId(), null);
setTransformed(true);
TransformAbility.transform(this, getSecondCardFace(), game, null);
@@ -132,14 +134,10 @@ public class PermanentCard extends PermanentImpl {
this.cardNumber = card.getCardNumber();
this.usesVariousArt = card.getUsesVariousArt();
- this.transformable = card.isTransformable();
- if (this.transformable) {
- this.nightCard = card.isNightCard();
- if (!this.nightCard) {
- this.secondSideCard = card.getSecondCardFace();
- this.secondSideCardClazz = this.secondSideCard.getClass();
- }
+ if (card.getSecondCardFace() != null) {
+ this.secondSideCardClazz = card.getSecondCardFace().getClass();
}
+ this.nightCard = card.isNightCard();
this.flipCard = card.isFlipCard();
this.flipCardName = card.getFlipCardName();
}
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index 26b10e2d08..f27244bd08 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -103,6 +103,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
protected List markedDamage;
protected int markedLifelink;
protected int timesLoyaltyUsed = 0;
+ protected int transformCount = 0;
protected Map info;
protected int createOrder;
@@ -168,6 +169,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.pairedPermanent = permanent.pairedPermanent;
this.bandedCards.addAll(permanent.bandedCards);
this.timesLoyaltyUsed = permanent.timesLoyaltyUsed;
+ this.transformCount = permanent.transformCount;
this.morphed = permanent.morphed;
this.manifested = permanent.manifested;
@@ -562,16 +564,46 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
- public boolean transform(Game game) {
- if (transformable) {
- if (!replaceEvent(EventType.TRANSFORM, game)) {
- setTransformed(!transformed);
- game.applyEffects();
- game.addSimultaneousEvent(GameEvent.getEvent(GameEvent.EventType.TRANSFORMED, getId(), getControllerId()));
- return true;
- }
+ public boolean transform(Ability source, Game game) {
+ return this.transform(source, game, false);
+ }
+
+ private boolean checkDayNightBound() {
+ return this.getAbilities().containsClass(DayboundAbility.class)
+ || this.getAbilities().containsClass(NightboundAbility.class);
+ }
+
+ private Card getOtherFace() {
+ return transformed ? this.getMainCard() : this.getMainCard().getSecondCardFace();
+ }
+
+ @Override
+ public boolean transform(Ability source, Game game, boolean ignoreDayNight) {
+ if (!this.isTransformable()
+ || (!ignoreDayNight && this.checkDayNightBound())
+ || this.getOtherFace().isInstantOrSorcery()
+ || (source != null && !source.checkTransformCount(this, game))
+ || this.replaceEvent(EventType.TRANSFORM, game)) {
+ return false;
}
- return false;
+ if (this.transformed) {
+ Card orgCard = this.getMainCard();
+ this.getPower().modifyBaseValue(orgCard.getPower().getValue());
+ this.getToughness().modifyBaseValue(orgCard.getToughness().getValue());
+ }
+ game.informPlayers(this.getLogName() + " transforms into " + this.getOtherFace().getLogName()
+ + CardUtil.getSourceLogName(game, source, this.getId()));
+ this.setTransformed(!this.transformed);
+ this.transformCount++;
+ game.applyEffects();
+ this.replaceEvent(EventType.TRANSFORMING, game);
+ game.addSimultaneousEvent(GameEvent.getEvent(EventType.TRANSFORMED, this.getId(), this.getControllerId()));
+ return true;
+ }
+
+ @Override
+ public int getTransformCount() {
+ return transformCount;
}
@Override
@@ -1406,21 +1438,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return true;
}
- @Override
- public boolean canTransform(Ability source, Game game) {
- if (transformable) {
- for (Map.Entry> entry : game.getContinuousEffects().getApplicableRestrictionEffects(this, game).entrySet()) {
- RestrictionEffect effect = entry.getKey();
- for (Ability ability : entry.getValue()) {
- if (!effect.canTransform(this, ability, game, true)) {
- return false;
- }
- }
- }
- }
- return transformable;
- }
-
@Override
public void setAttacking(boolean attacking) {
this.attacking = attacking;
@@ -1750,5 +1767,4 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
detachAllAttachments(game);
return successfullyMoved;
}
-
}
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index 6738fd8f1a..cec9147374 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -907,11 +907,6 @@ public class Spell extends StackObjectImpl implements Card {
throw new UnsupportedOperationException("Unsupported operation");
}
- @Override
- public void setTransformable(boolean value) {
- throw new UnsupportedOperationException("Unsupported operation");
- }
-
@Override
public int getZoneChangeCounter(Game game) {
// spell's zcc can't be changed after put to stack
diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java
index e9493bcb31..bfddba81ce 100644
--- a/Mage/src/main/java/mage/game/stack/StackAbility.java
+++ b/Mage/src/main/java/mage/game/stack/StackAbility.java
@@ -576,6 +576,16 @@ public class StackAbility extends StackObjectImpl implements Ability {
return ability.getSourcePermanentOrLKI(game);
}
+ @Override
+ public void setSourcePermanentTransformCount(Game game) {
+ ability.setSourcePermanentTransformCount(game);
+ }
+
+ @Override
+ public boolean checkTransformCount(Permanent permanent, Game game) {
+ return ability.checkTransformCount(permanent, game);
+ }
+
@Override
public int getZoneChangeCounter(Game game) {
return game.getState().getZoneChangeCounter(getSourceId());
diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java
index 80410f1dba..577eb98824 100644
--- a/Mage/src/main/java/mage/game/turn/Turn.java
+++ b/Mage/src/main/java/mage/game/turn/Turn.java
@@ -1,10 +1,5 @@
package mage.game.turn;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.UUID;
import mage.abilities.Ability;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
@@ -17,6 +12,12 @@ import mage.game.stack.StackObject;
import mage.players.Player;
import mage.util.ThreadLocalStringBuilder;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
/**
* @author BetaSteward_at_googlemail.com
*/
@@ -95,7 +96,7 @@ public class Turn implements Serializable {
* @param activePlayer
* @return true if turn is skipped
*/
- public boolean play(Game game, Player activePlayer) {
+ public boolean play(Game game, Player activePlayer) {
// uncomment this to trace triggered abilities and/or continous effects
// TraceUtil.traceTriggeredAbilities(game);
// game.getState().getContinuousEffects().traceContinuousEffects(game);
@@ -121,24 +122,25 @@ public class Turn implements Serializable {
if (game.isPaused() || game.checkIfGameIsOver()) {
return false;
}
- if (!isEndTurnRequested() || phase.getType() == TurnPhase.END) {
- currentPhase = phase;
- game.fireEvent(new PhaseChangedEvent(activePlayer.getId(), null));
- if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) {
- if (phase.play(game, activePlayer.getId())) {
- if (game.executingRollback()) {
- return false;
- }
- //20091005 - 500.4/703.4n
- game.emptyManaPools(null);
- game.saveState(false);
-
- //20091005 - 500.8
- while (playExtraPhases(game, phase.getType())) {
- }
- }
- }
+ if (isEndTurnRequested() && phase.getType() != TurnPhase.END) {
+ continue;
}
+ currentPhase = phase;
+ game.fireEvent(new PhaseChangedEvent(activePlayer.getId(), null));
+ if (game.getState().getTurnMods().skipPhase(
+ activePlayer.getId(), currentPhase.getType()
+ ) || !phase.play(game, activePlayer.getId())) {
+ continue;
+ }
+ if (game.executingRollback()) {
+ return false;
+ }
+ //20091005 - 500.4/703.4n
+ game.emptyManaPools(null);
+ game.saveState(false);
+
+ //20091005 - 500.8
+ while (playExtraPhases(game, phase.getType())) ;
}
return false;
}
diff --git a/Mage/src/main/java/mage/game/turn/UntapStep.java b/Mage/src/main/java/mage/game/turn/UntapStep.java
index d9c0c28f38..658fba2190 100644
--- a/Mage/src/main/java/mage/game/turn/UntapStep.java
+++ b/Mage/src/main/java/mage/game/turn/UntapStep.java
@@ -2,14 +2,15 @@
package mage.game.turn;
-import java.util.UUID;
import mage.constants.PhaseStep;
import mage.game.Game;
import mage.game.events.GameEvent.EventType;
import mage.players.Player;
+import mage.watchers.common.CastSpellLastTurnWatcher;
+
+import java.util.UUID;
/**
- *
* @author BetaSteward_at_googlemail.com
*/
public class UntapStep extends Step {
@@ -28,6 +29,7 @@ public class UntapStep extends Step {
@Override
public void beginStep(Game game, UUID activePlayerId) {
super.beginStep(game, activePlayerId);
+ handleDayNight(game);
Player activePlayer = game.getPlayer(activePlayerId);
//20091005 - 502.1/703.4a
activePlayer.phasing(game);
@@ -41,4 +43,18 @@ public class UntapStep extends Step {
return new UntapStep(this);
}
+ private void handleDayNight(Game game) {
+ if (!game.hasDayNight() || game.getTurnNum() <= 1) {
+ return;
+ }
+ int previousSpells = game
+ .getState()
+ .getWatcher(CastSpellLastTurnWatcher.class)
+ .getActivePlayerPrevTurnCount();
+ if (game.checkDayNight(true) && previousSpells == 0) {
+ game.setDaytime(false);
+ } else if (game.checkDayNight(false) && previousSpells >= 2) {
+ game.setDaytime(true);
+ }
+ }
}
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 4b626d74db..1d4cbe1560 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -2063,11 +2063,11 @@ public abstract class PlayerImpl implements Player, Serializable {
game.informPlayers(this.getLogName() + " loses " + event.getAmount() + " life"
+ (atCombat ? " at combat" : "") + CardUtil.getSourceLogName(game, " from ", needId, "", ""));
}
- if (amount > 0) {
+ if (event.getAmount() > 0) {
game.fireEvent(new GameEvent(GameEvent.EventType.LOST_LIFE,
- playerId, source, playerId, amount, atCombat));
+ playerId, source, playerId, event.getAmount(), atCombat));
}
- return amount;
+ return event.getAmount();
}
return 0;
}
diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java
index 402ecd4419..02c273d532 100644
--- a/Mage/src/main/java/mage/util/CardUtil.java
+++ b/Mage/src/main/java/mage/util/CardUtil.java
@@ -1408,5 +1408,4 @@ public final class CardUtil {
effect.apply(game, source);
return true;
}
-
}
diff --git a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java
index c2fe4644a8..5a6ec9fbec 100644
--- a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java
@@ -16,6 +16,8 @@ public class CastSpellLastTurnWatcher extends Watcher {
private final Map amountOfSpellsCastOnPrevTurn = new HashMap<>();
private final Map amountOfSpellsCastOnCurrentTurn = new HashMap<>();
private final List spellsCastThisTurnInOrder = new ArrayList<>();
+ private int activePlayerPrevTurnCount = 0;
+ private int activePlayerThisTurnCount = 0;
public CastSpellLastTurnWatcher() {
super(WatcherScope.GAME);
@@ -29,7 +31,9 @@ public class CastSpellLastTurnWatcher extends Watcher {
if (playerId != null) {
amountOfSpellsCastOnCurrentTurn.putIfAbsent(playerId, 0);
amountOfSpellsCastOnCurrentTurn.compute(playerId, (k, a) -> a + 1);
-
+ }
+ if (game.isActivePlayer(playerId)) {
+ activePlayerThisTurnCount++;
}
}
}
@@ -41,6 +45,8 @@ public class CastSpellLastTurnWatcher extends Watcher {
amountOfSpellsCastOnPrevTurn.putAll(amountOfSpellsCastOnCurrentTurn);
amountOfSpellsCastOnCurrentTurn.clear();
spellsCastThisTurnInOrder.clear();
+ activePlayerPrevTurnCount = activePlayerThisTurnCount;
+ activePlayerThisTurnCount = 0;
}
public Map getAmountOfSpellsCastOnPrevTurn() {
@@ -69,4 +75,12 @@ public class CastSpellLastTurnWatcher extends Watcher {
}
return 0;
}
+
+ public int getActivePlayerPrevTurnCount() {
+ return activePlayerPrevTurnCount;
+ }
+
+ public int getActivePlayerThisTurnCount() {
+ return activePlayerThisTurnCount;
+ }
}