From 86dad5e54f671236456211812d3d8f77fda76583 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 17 May 2022 20:18:18 -0400 Subject: [PATCH] [CLB] Implemented White Plume Adventurer --- .../src/main/java/mage/view/PlayerView.java | 6 + .../mage/cards/w/WhitePlumeAdventurer.java | 108 +++++++++++ .../CommanderLegendsBattleForBaldursGate.java | 1 + .../common/TakeTheInitiativeEffect.java | 32 ++++ .../keyword/VentureIntoTheDungeonEffect.java | 2 +- .../mage/designations/DesignationType.java | 5 +- .../java/mage/designations/Initiative.java | 179 ++++++++++++++++++ Mage/src/main/java/mage/game/Game.java | 6 +- Mage/src/main/java/mage/game/GameImpl.java | 29 ++- Mage/src/main/java/mage/game/GameState.java | 11 ++ .../command/dungeons/UndercityDungeon.java | 152 +++++++++++++++ .../main/java/mage/game/events/GameEvent.java | 1 + Utils/mtg-cards-data.txt | 1 + 13 files changed, 524 insertions(+), 9 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/w/WhitePlumeAdventurer.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/TakeTheInitiativeEffect.java create mode 100644 Mage/src/main/java/mage/designations/Initiative.java create mode 100644 Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java diff --git a/Mage.Common/src/main/java/mage/view/PlayerView.java b/Mage.Common/src/main/java/mage/view/PlayerView.java index 30784ba083b..9703f20509e 100644 --- a/Mage.Common/src/main/java/mage/view/PlayerView.java +++ b/Mage.Common/src/main/java/mage/view/PlayerView.java @@ -54,6 +54,7 @@ public class PlayerView implements Serializable { private final boolean passedAllTurns; // F9 private final boolean passedUntilEndStepBeforeMyTurn; // F11 private final boolean monarch; + private final boolean initiative; private final List designationNames = new ArrayList<>(); public PlayerView(Player player, GameState state, Game game, UUID createdForPlayerId, UUID watcherUserId) { @@ -153,6 +154,7 @@ public class PlayerView implements Serializable { this.passedUntilStackResolved = player.getPassedUntilStackResolved(); this.passedUntilEndStepBeforeMyTurn = player.getPassedUntilEndStepBeforeMyTurn(); this.monarch = player.getId().equals(game.getMonarchId()); + this.initiative = player.getId().equals(game.getInitiativeId()); for (Designation designation : player.getDesignations()) { this.designationNames.add(designation.getName()); } @@ -309,6 +311,10 @@ public class PlayerView implements Serializable { return monarch; } + public boolean isInitiative() { + return initiative; + } + public List getDesignationNames() { return designationNames; } diff --git a/Mage.Sets/src/mage/cards/w/WhitePlumeAdventurer.java b/Mage.Sets/src/mage/cards/w/WhitePlumeAdventurer.java new file mode 100644 index 00000000000..ee4c2a7ac45 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhitePlumeAdventurer.java @@ -0,0 +1,108 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CompletedDungeonCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TakeTheInitiativeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.watchers.common.CompletedDungeonWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WhitePlumeAdventurer extends CardImpl { + + public WhitePlumeAdventurer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When White Plume Adventurer enters battlefield, you take the initiative. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TakeTheInitiativeEffect())); + + // At the beginning of each opponent's upkeep, untap a creature you control. If you've completed a dungeon, untap all creatures you control instead. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new WhitePlumeAdventurerEffect(), TargetController.OPPONENT, false + ).addHint(CompletedDungeonCondition.getHint()), new CompletedDungeonWatcher()); + } + + private WhitePlumeAdventurer(final WhitePlumeAdventurer card) { + super(card); + } + + @Override + public WhitePlumeAdventurer copy() { + return new WhitePlumeAdventurer(this); + } +} + +class WhitePlumeAdventurerEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("tapped creature you control"); + + static { + filter.add(TappedPredicate.TAPPED); + } + + WhitePlumeAdventurerEffect() { + super(Outcome.Benefit); + staticText = "untap a creature you control. If you've completed a dungeon, " + + "untap all creatures you control instead"; + } + + private WhitePlumeAdventurerEffect(final WhitePlumeAdventurerEffect effect) { + super(effect); + } + + @Override + public WhitePlumeAdventurerEffect copy() { + return new WhitePlumeAdventurerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!game.getBattlefield().contains(filter, source, game, 1)) { + return false; + } + if (CompletedDungeonWatcher.checkPlayer(source.getControllerId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getControllerId(), source, game + )) { + permanent.untap(game); + } + return true; + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + player.choose(outcome, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.untap(game); + } +} diff --git a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java index f5249c92105..fa5c9ec0acf 100644 --- a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java +++ b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java @@ -34,6 +34,7 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet { cards.add(new SetCardInfo("Sea of Clouds", 360, Rarity.RARE, mage.cards.s.SeaOfClouds.class)); cards.add(new SetCardInfo("Spire Garden", 361, Rarity.RARE, mage.cards.s.SpireGarden.class)); cards.add(new SetCardInfo("Wand of Wonder", 204, Rarity.RARE, mage.cards.w.WandOfWonder.class)); + cards.add(new SetCardInfo("White Plume Adventurer", 49, Rarity.RARE, mage.cards.w.WhitePlumeAdventurer.class)); cards.add(new SetCardInfo("Zevlor, Elturel Exile", 296, Rarity.RARE, mage.cards.z.ZevlorElturelExile.class)); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TakeTheInitiativeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TakeTheInitiativeEffect.java new file mode 100644 index 00000000000..8576b6bebd0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/TakeTheInitiativeEffect.java @@ -0,0 +1,32 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public class TakeTheInitiativeEffect extends OneShotEffect { + + public TakeTheInitiativeEffect() { + super(Outcome.Benefit); + staticText = "you take the initiative"; + } + + private TakeTheInitiativeEffect(final TakeTheInitiativeEffect effect) { + super(effect); + } + + @Override + public TakeTheInitiativeEffect copy() { + return new TakeTheInitiativeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.takeInitiative(source, source.getControllerId()); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/VentureIntoTheDungeonEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/VentureIntoTheDungeonEffect.java index 9e4a9c682ba..a467f6cfeb6 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/VentureIntoTheDungeonEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/VentureIntoTheDungeonEffect.java @@ -26,7 +26,7 @@ public class VentureIntoTheDungeonEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - game.ventureIntoDungeon(source.getControllerId()); + game.ventureIntoDungeon(source.getControllerId(), false); return true; } } diff --git a/Mage/src/main/java/mage/designations/DesignationType.java b/Mage/src/main/java/mage/designations/DesignationType.java index ff0e98b9dd7..17db4717b24 100644 --- a/Mage/src/main/java/mage/designations/DesignationType.java +++ b/Mage/src/main/java/mage/designations/DesignationType.java @@ -1,13 +1,12 @@ - package mage.designations; /** - * * @author LevelX2 */ public enum DesignationType { THE_MONARCH("The Monarch"), - CITYS_BLESSING("City's Blessing"); + CITYS_BLESSING("City's Blessing"), + THE_INITIATIVE("The Initiative"); private final String text; diff --git a/Mage/src/main/java/mage/designations/Initiative.java b/Mage/src/main/java/mage/designations/Initiative.java new file mode 100644 index 00000000000..71727a344e3 --- /dev/null +++ b/Mage/src/main/java/mage/designations/Initiative.java @@ -0,0 +1,179 @@ +package mage.designations; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.events.DamagedPlayerBatchEvent; +import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class Initiative extends Designation { + + public Initiative() { + super(DesignationType.THE_INITIATIVE, "CLB"); + + // Whenever one or more creatures a player controls deals combat damage to you, that player takes the initiative. + this.addAbility(new InitiativeDamageTriggeredAbility()); + + // Whenever you take the initiative and at the beginning of your upkeep, venture into Undercity. + this.addAbility(new InitiativeVentureTriggeredAbility()); + } + + private Initiative(final Initiative card) { + super(card); + } + + @Override + public Initiative copy() { + return new Initiative(this); + } +} + +class InitiativeDamageTriggeredAbility extends TriggeredAbilityImpl { + + InitiativeDamageTriggeredAbility() { + super(Zone.ALL, new InitiativeTakeEffect()); + } + + private InitiativeDamageTriggeredAbility(final InitiativeDamageTriggeredAbility ability) { + super(ability); + } + + @Override + public InitiativeDamageTriggeredAbility copy() { + return new InitiativeDamageTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(game.getInitiativeId())) { + return false; + } + DamagedPlayerBatchEvent dEvent = (DamagedPlayerBatchEvent) event; + UUID playerId = dEvent + .getEvents() + .stream() + .map(GameEvent::getSourceId) + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .findFirst() + .orElse(null); + if (playerId == null) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(playerId)); + return true; + } + + @Override + public String getRule() { + return "Whenever one or more creatures a player controls deals combat damage to you, that player takes the initiative."; + } +} + +class InitiativeTakeEffect extends OneShotEffect { + + InitiativeTakeEffect() { + super(Outcome.Benefit); + } + + private InitiativeTakeEffect(final InitiativeTakeEffect effect) { + super(effect); + } + + @Override + public InitiativeTakeEffect copy() { + return new InitiativeTakeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.takeInitiative(source, getTargetPointer().getFirst(game, source)); + return true; + } +} + +class InitiativeVentureTriggeredAbility extends TriggeredAbilityImpl { + + InitiativeVentureTriggeredAbility() { + super(Zone.ALL, new InitiativeUndercityEffect()); + } + + private InitiativeVentureTriggeredAbility(final InitiativeVentureTriggeredAbility ability) { + super(ability); + } + + @Override + public InitiativeVentureTriggeredAbility copy() { + return new InitiativeVentureTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE + || event.getType() == GameEvent.EventType.TOOK_INITIATIVE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + UUID playerId; + switch (event.getType()) { + case UPKEEP_STEP_PRE: + if (!game.isActivePlayer(game.getInitiativeId())) { + return false; + } + playerId = game.getActivePlayerId(); + break; + case TOOK_INITIATIVE: + playerId = event.getPlayerId(); + break; + default: + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(playerId)); + return true; + } + + @Override + public String getRule() { + return "Whenever you take the initiative and at the beginning of your upkeep, venture into Undercity."; + } +} + +class InitiativeUndercityEffect extends OneShotEffect { + + InitiativeUndercityEffect() { + super(Outcome.Benefit); + } + + private InitiativeUndercityEffect(final InitiativeUndercityEffect effect) { + super(effect); + } + + @Override + public InitiativeUndercityEffect copy() { + return new InitiativeUndercityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.ventureIntoDungeon(getTargetPointer().getFirst(game, source), true); + return true; + } +} diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 1aa9eb4afb3..178c31a8fdb 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -386,7 +386,7 @@ public interface Game extends MageItem, Serializable, Copyable { Dungeon addDungeon(Dungeon dungeon, UUID playerId); - void ventureIntoDungeon(UUID playerId); + void ventureIntoDungeon(UUID playerId, boolean undercity); /** * Tells whether the current game has day or night, defaults to false @@ -519,6 +519,10 @@ public interface Game extends MageItem, Serializable, Copyable { void setMonarchId(Ability source, UUID monarchId); + UUID getInitiativeId(); + + void takeInitiative(Ability source, UUID initiativeId); + int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable); int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable, List appliedEffects); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 0fc53f56a88..13bd3f3ffd9 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -26,6 +26,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.counters.Counters; import mage.designations.Designation; +import mage.designations.Initiative; import mage.designations.Monarch; import mage.filter.Filter; import mage.filter.FilterCard; @@ -36,6 +37,7 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.combat.Combat; import mage.game.command.*; +import mage.game.command.dungeons.UndercityDungeon; import mage.game.events.*; import mage.game.events.TableEvent.EventType; import mage.game.mulligan.Mulligan; @@ -536,21 +538,24 @@ public abstract class GameImpl implements Game { )); } - private Dungeon getOrCreateDungeon(UUID playerId) { + private Dungeon getOrCreateDungeon(UUID playerId, boolean undercity) { Dungeon dungeon = this.getPlayerDungeon(playerId); if (dungeon != null && dungeon.hasNextRoom()) { return dungeon; } removeDungeon(dungeon); - return this.addDungeon(Dungeon.selectDungeon(playerId, this), playerId); + return this.addDungeon(undercity ? new UndercityDungeon() : Dungeon.selectDungeon(playerId, this), playerId); } @Override - public void ventureIntoDungeon(UUID playerId) { + public void ventureIntoDungeon(UUID playerId, boolean undercity) { + if (playerId == null) { + return; + } if (replaceEvent(GameEvent.getEvent(GameEvent.EventType.VENTURE, playerId, null, playerId))) { return; } - this.getOrCreateDungeon(playerId).moveToNextRoom(playerId, this); + this.getOrCreateDungeon(playerId, undercity).moveToNextRoom(playerId, this); fireEvent(GameEvent.getEvent(GameEvent.EventType.VENTURED, playerId, null, playerId)); } @@ -3691,6 +3696,22 @@ public abstract class GameImpl implements Game { } } + @Override + public UUID getInitiativeId() { + return getState().getInitiativeId(); + } + + @Override + public void takeInitiative(Ability source, UUID initiativeId) { + if (getInitiativeId() == null) { + getState().addDesignation(new Initiative(), this, initiativeId); + } else if (!getInitiativeId().equals(initiativeId)) { + getState().setInitiativeId(initiativeId); + } + informPlayers(getPlayer(initiativeId).getLogName() + " takes the initiative"); + fireEvent(new GameEvent(GameEvent.EventType.TOOK_INITIATIVE, initiativeId, source, initiativeId)); + } + @Override public int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable) { return damagePlayerOrPlaneswalker(playerOrWalker, damage, attackerId, source, game, combatDamage, preventable, null); diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 1bb704405fa..4da2e7772e6 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -76,6 +76,7 @@ public class GameState implements Serializable, Copyable { private UUID priorityPlayerId; // player that has currently priority private UUID playerByOrderId; // player that has currently priority private UUID monarchId; // player that is the monarch + private UUID initiativeId; // player that has the initiative private SpellStack stack; private Command command; private boolean isPlaneChase; @@ -144,6 +145,7 @@ public class GameState implements Serializable, Copyable { this.priorityPlayerId = state.priorityPlayerId; this.playerByOrderId = state.playerByOrderId; this.monarchId = state.monarchId; + this.initiativeId = state.initiativeId; this.turn = state.turn.copy(); this.stack = state.stack.copy(); @@ -249,6 +251,7 @@ public class GameState implements Serializable, Copyable { this.playerByOrderId = state.playerByOrderId; this.priorityPlayerId = state.priorityPlayerId; this.monarchId = state.monarchId; + this.initiativeId = state.initiativeId; this.stack = state.stack; this.command = state.command; this.isPlaneChase = state.isPlaneChase; @@ -482,6 +485,14 @@ public class GameState implements Serializable, Copyable { this.monarchId = monarchId; } + public UUID getInitiativeId() { + return initiativeId; + } + + public void setInitiativeId(UUID initiativeId) { + this.initiativeId = initiativeId; + } + public UUID getChoosingPlayerId() { return choosingPlayerId; } diff --git a/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java b/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java new file mode 100644 index 00000000000..c02f9837963 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java @@ -0,0 +1,152 @@ +package mage.game.command.dungeons; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.command.Dungeon; +import mage.game.command.DungeonRoom; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SkeletonToken; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.RandomUtil; + +/** + * @author TheElk801 + */ +public class UndercityDungeon extends Dungeon { + + public UndercityDungeon() { + super("Undercity", "CLB"); + + DungeonRoom secretEntrance = new DungeonRoom( + "Secret Entrance", + new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND) + ) + ); + + DungeonRoom forge = new DungeonRoom( + "Forge", new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + forge.addTarget(new TargetCreaturePermanent()); + + DungeonRoom lostWell = new DungeonRoom( + "Lost Well", new ScryEffect(2, false) + ); + + DungeonRoom trap = new DungeonRoom("Trap!", new LoseLifeTargetEffect(5)); + trap.addTarget(new TargetPlayer()); + + DungeonRoom arena = new DungeonRoom("Arena", new GoadTargetEffect()); + arena.addTarget(new TargetCreaturePermanent()); + + DungeonRoom stash = new DungeonRoom("Stash", new CreateTokenEffect(new TreasureToken())); + + DungeonRoom archives = new DungeonRoom("Archives", new DrawCardSourceControllerEffect(1)); + + DungeonRoom catacombs = new DungeonRoom("Catacombs", new CreateTokenEffect(new SkeletonToken())); + + DungeonRoom throneOfTheDeadThree = new DungeonRoom("Throne of the Dead Three", new ThroneOfTheDeadThreeEffect()); + + secretEntrance.addNextRoom(forge); + secretEntrance.addNextRoom(lostWell); + forge.addNextRoom(trap); + forge.addNextRoom(arena); + lostWell.addNextRoom(arena); + lostWell.addNextRoom(stash); + trap.addNextRoom(archives); + arena.addNextRoom(archives); + arena.addNextRoom(catacombs); + archives.addNextRoom(throneOfTheDeadThree); + catacombs.addNextRoom(throneOfTheDeadThree); + + this.addRoom(secretEntrance); + this.addRoom(forge); + this.addRoom(lostWell); + this.addRoom(trap); + this.addRoom(arena); + this.addRoom(stash); + this.addRoom(archives); + this.addRoom(catacombs); + this.addRoom(throneOfTheDeadThree); + } + + private UndercityDungeon(final UndercityDungeon dungeon) { + super(dungeon); + } + + public UndercityDungeon copy() { + return new UndercityDungeon(this); + } +} + +class ThroneOfTheDeadThreeEffect extends OneShotEffect { + + ThroneOfTheDeadThreeEffect() { + super(Outcome.Benefit); + staticText = "Reveal the top ten cards of your library. Put a creature card from among them onto the " + + "battlefield with three +1/+1 counters on it. It gains hexproof until your next turn. Then shuffle."; + } + + private ThroneOfTheDeadThreeEffect(final ThroneOfTheDeadThreeEffect effect) { + super(effect); + } + + @Override + public ThroneOfTheDeadThreeEffect copy() { + return new ThroneOfTheDeadThreeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 10)); + player.revealCards(source, cards, game); + Card card; + switch (cards.count(StaticFilters.FILTER_CARD_CREATURE, game)) { + case 0: + card = null; + break; + case 1: + card = RandomUtil.randomFromCollection(cards.getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + break; + default: + TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE); + player.choose(outcome, cards, target, game); + card = cards.get(target.getFirstTarget(), game); + } + if (card != null) { + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + game.addEffect(new GainAbilityTargetEffect(HexproofAbility.getInstance()) + .setTargetPointer(new FixedTarget(permanent, game)), source); + } + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 68e7bfcf676..5ac5560d7de 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -362,6 +362,7 @@ public class GameEvent implements Serializable { */ BECOME_MONARCH, BECOMES_MONARCH, + TOOK_INITIATIVE, BECOMES_DAY_NIGHT, MEDITATED, PHASE_OUT, PHASED_OUT, diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index a9911956fc5..8a59d6c73ae 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -44184,6 +44184,7 @@ Swamp|Streets of New Capenna|266|C||Basic Land - Swamp|||({T}: Add {B}.)| Mountain|Streets of New Capenna|268|C||Basic Land - Mountain|||({T}: Add {R}.)| Forest|Streets of New Capenna|270|C||Basic Land - Forest|||({T}: Add {G}.)| Faceless One|Commander Legends: Battle for Baldur's Gate|1|C|{5}|Legendary Enchantment Creature - Background|3|3|If Faceless One is your commander, choose a color before the game begins. Faceless One is the chosen color.$Choose a Background| +White Plume Adventurer|Commander Legends: Battle for Baldur's Gate|49|R|{2}{W}|Creature - Orc Cleric|3|3|When White Plume Adventurer enters battlefield, you take the initiative.$At the beginning of each opponent's upkeep, untap a creature you control. If you've completed a dungeon, untap all creatures you control instead.| Ancient Brass Dragon|Commander Legends: Battle for Baldur's Gate|111|M|{5}{B}{B}|Creature - Elder Dragon|7|6|Flying$Whenever Ancient Brass Dragon deals combat damage to a player, roll a d20. When you do, put any number of target creature cards with mana value X or less from graveyards onto the battlefield under your control, where X is the result.| Elder Brain|Commander Legends: Battle for Baldur's Gate|125|R|{5}{B}{B}|Creature - Horror|6|6|Menace$Whenever Elder Brain attacks a player, exile all cards from that player's hand, then they draw that many cards. You may play lands and cast spells from among the exiled cards for as long as they remain exiled. If you cast a spell this way, you may spend mana as though it were mana of any color to cast it.| Fireball|Commander Legends: Battle for Baldur's Gate|175|U|{X}{R}|Sorcery|||This spell costs {1} more to cast for each target beyond the first.$Fireball deals X damage divided evenly, rounded down, among any number of targets.|