mirror of
https://github.com/correl/mage.git
synced 2024-11-14 11:09:31 +00:00
Implement The Ring Tempts You mechanic (#10320)
* remove skip * initial implementation of the ring mechanic * some changes * rework ring-bearer choosing * [LTR] Implement Call of the Ring * update ring-bearer condition
This commit is contained in:
parent
d56c148118
commit
3503513c4e
19 changed files with 508 additions and 48 deletions
71
Mage.Sets/src/mage/cards/c/CallOfTheRing.java
Normal file
71
Mage.Sets/src/mage/cards/c/CallOfTheRing.java
Normal file
|
@ -0,0 +1,71 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.keyword.TheRingTemptsYouEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class CallOfTheRing extends CardImpl {
|
||||
|
||||
public CallOfTheRing(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
|
||||
|
||||
// At the beginning of your upkeep, the Ring tempts you.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
|
||||
new TheRingTemptsYouEffect(), TargetController.YOU, false
|
||||
));
|
||||
|
||||
// Whenever you choose a creature as your Ring-bearer, you may pay 2 life. If you do, draw a card.
|
||||
this.addAbility(new CallOfTheRingTriggeredAbility());
|
||||
}
|
||||
|
||||
private CallOfTheRing(final CallOfTheRing card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallOfTheRing copy() {
|
||||
return new CallOfTheRing(this);
|
||||
}
|
||||
}
|
||||
|
||||
class CallOfTheRingTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
CallOfTheRingTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new PayLifeCost(2)));
|
||||
setTriggerPhrase("Whenever you choose a creature as your Ring-bearer, ");
|
||||
}
|
||||
|
||||
private CallOfTheRingTriggeredAbility(final CallOfTheRingTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallOfTheRingTriggeredAbility copy() {
|
||||
return new CallOfTheRingTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.RING_BEARER_CHOSEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return isControlledBy(event.getPlayerId());
|
||||
}
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.SourceIsRingBearerCondition;
|
||||
import mage.abilities.decorator.ConditionalRequirementEffect;
|
||||
import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect;
|
||||
import mage.abilities.effects.keyword.TheRingTemptsYouEffect;
|
||||
|
@ -15,7 +14,6 @@ import mage.constants.SubType;
|
|||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -46,7 +44,7 @@ public final class FrodoBaggins extends CardImpl {
|
|||
|
||||
// As long as Frodo is your Ring-bearer, it must be blocked if able.
|
||||
this.addAbility(new SimpleStaticAbility(new ConditionalRequirementEffect(
|
||||
new MustBeBlockedByAtLeastOneSourceEffect(), FrodoBagginsCondition.instance,
|
||||
new MustBeBlockedByAtLeastOneSourceEffect(), SourceIsRingBearerCondition.instance,
|
||||
"as long as {this} is your Ring-bearer, it must be blocked if able"
|
||||
)));
|
||||
}
|
||||
|
@ -60,13 +58,3 @@ public final class FrodoBaggins extends CardImpl {
|
|||
return new FrodoBaggins(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum FrodoBagginsCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// TODO: Implement this
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.TemptedByTheRingWatcher;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -80,7 +81,6 @@ enum FrodoSauronsBaneCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// TODO: Implement when mechanic is known
|
||||
return false;
|
||||
return TemptedByTheRingWatcher.getCount(source.getControllerId(), game) >= 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.InvertCondition;
|
||||
import mage.abilities.condition.common.SourceIsRingBearerCondition;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||
|
@ -38,7 +40,7 @@ public final class SauronTheNecromancer extends CardImpl {
|
|||
this.toughness = new MageInt(4);
|
||||
|
||||
// Menace
|
||||
this.addAbility(new MenaceAbility());
|
||||
this.addAbility(new MenaceAbility(false));
|
||||
|
||||
// Whenever Sauron, the Necromancer attacks, exile target creature card from your graveyard. Create a tapped and attacking token that's a copy of that card, except it's a 3/3 black Wraith with menace. At the beginning of the next end step, exile that token unless Sauron is your Ring-bearer.
|
||||
Ability ability = new AttacksTriggeredAbility(new SauronTheNecromancerEffect());
|
||||
|
@ -56,18 +58,10 @@ public final class SauronTheNecromancer extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
enum SauronTheNecromancerCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// TODO: Implement this
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class SauronTheNecromancerEffect extends OneShotEffect {
|
||||
|
||||
private static final Condition condition = new InvertCondition(SourceIsRingBearerCondition.instance);
|
||||
|
||||
SauronTheNecromancerEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile target creature card from your graveyard. Create a tapped and attacking " +
|
||||
|
@ -105,7 +99,7 @@ class SauronTheNecromancerEffect extends OneShotEffect {
|
|||
effect.apply(game, source);
|
||||
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new ConditionalOneShotEffect(
|
||||
new ExileTargetEffect(), SauronTheNecromancerCondition.instance,
|
||||
new ExileTargetEffect(), condition,
|
||||
"exile that token unless {this} is your Ring-bearer"
|
||||
).setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game))
|
||||
), source);
|
||||
|
|
|
@ -4,12 +4,8 @@ import mage.cards.ExpansionSet;
|
|||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet {
|
||||
|
||||
private static final List<String> unfinished = Arrays.asList("Bilbo, Retired Burglar", "Call of the Ring", "Frodo Baggins", "Frodo, Sauron's Bane", "Gollum, Patient Plotter", "Samwise the Stouthearted");
|
||||
private static final TheLordOfTheRingsTalesOfMiddleEarth instance = new TheLordOfTheRingsTalesOfMiddleEarth();
|
||||
|
||||
public static TheLordOfTheRingsTalesOfMiddleEarth getInstance() {
|
||||
|
@ -24,6 +20,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet {
|
|||
|
||||
cards.add(new SetCardInfo("Aragorn and Arwen, Wed", 287, Rarity.MYTHIC, mage.cards.a.AragornAndArwenWed.class));
|
||||
cards.add(new SetCardInfo("Bilbo, Retired Burglar", 403, Rarity.UNCOMMON, mage.cards.b.BilboRetiredBurglar.class));
|
||||
cards.add(new SetCardInfo("Call of the Ring", 79, Rarity.RARE, mage.cards.c.CallOfTheRing.class));
|
||||
cards.add(new SetCardInfo("Forest", 280, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS));
|
||||
cards.add(new SetCardInfo("Frodo Baggins", 404, Rarity.UNCOMMON, mage.cards.f.FrodoBaggins.class));
|
||||
cards.add(new SetCardInfo("Frodo, Sauron's Bane", 18, Rarity.RARE, mage.cards.f.FrodoSauronsBane.class));
|
||||
|
@ -45,7 +42,5 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Trailblazer's Boots", 398, Rarity.RARE, mage.cards.t.TrailblazersBoots.class));
|
||||
cards.add(new SetCardInfo("Wizard's Rockets", 400, Rarity.COMMON, mage.cards.w.WizardsRockets.class));
|
||||
cards.add(new SetCardInfo("You Cannot Pass!", 38, Rarity.UNCOMMON, mage.cards.y.YouCannotPass.class));
|
||||
|
||||
cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); // remove when mechanic is implemented
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4361,6 +4361,21 @@ public class TestPlayer implements Player {
|
|||
return computerPlayer.getPhyrexianColors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getRingBearerId() {
|
||||
return computerPlayer.getRingBearerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permanent getRingBearer(Game game) {
|
||||
return computerPlayer.getRingBearer(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chooseRingBearer(Game game) {
|
||||
computerPlayer.chooseRingBearer(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||
assertAliasSupportInChoices(false);
|
||||
|
|
|
@ -1419,6 +1419,20 @@ public class PlayerStub implements Player {
|
|||
return (new FilterMana());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getRingBearerId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permanent getRingBearer(Game game) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chooseRingBearer(Game game) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserData getControllingPlayersUserData(Game game) {
|
||||
return null;
|
||||
|
|
|
@ -4,7 +4,8 @@ import mage.MageObjectReference;
|
|||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
@ -16,7 +17,7 @@ import mage.util.CardUtil;
|
|||
*/
|
||||
public class AttacksCreatureYouControlTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
protected final FilterControlledCreaturePermanent filter;
|
||||
protected final FilterPermanent filter;
|
||||
protected final boolean setTargetPointer;
|
||||
protected boolean once = false;
|
||||
|
||||
|
@ -25,25 +26,29 @@ public class AttacksCreatureYouControlTriggeredAbility extends TriggeredAbilityI
|
|||
}
|
||||
|
||||
public AttacksCreatureYouControlTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(effect, optional, new FilterControlledCreaturePermanent());
|
||||
this(effect, optional, StaticFilters.FILTER_CONTROLLED_CREATURE);
|
||||
}
|
||||
|
||||
public AttacksCreatureYouControlTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) {
|
||||
this(effect, optional, new FilterControlledCreaturePermanent(), setTargetPointer);
|
||||
this(effect, optional, StaticFilters.FILTER_CONTROLLED_CREATURE, setTargetPointer);
|
||||
}
|
||||
|
||||
public AttacksCreatureYouControlTriggeredAbility(Effect effect, boolean optional, FilterControlledCreaturePermanent filter) {
|
||||
public AttacksCreatureYouControlTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) {
|
||||
this(effect, optional, filter, false);
|
||||
}
|
||||
|
||||
public AttacksCreatureYouControlTriggeredAbility(Effect effect, boolean optional, FilterControlledCreaturePermanent filter, boolean setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
public AttacksCreatureYouControlTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) {
|
||||
this(Zone.BATTLEFIELD, effect, optional, filter, setTargetPointer);
|
||||
}
|
||||
|
||||
public AttacksCreatureYouControlTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase("Whenever " + CardUtil.addArticle(filter.getMessage()) + " attacks, ");
|
||||
}
|
||||
|
||||
public AttacksCreatureYouControlTriggeredAbility(AttacksCreatureYouControlTriggeredAbility ability) {
|
||||
private AttacksCreatureYouControlTriggeredAbility(final AttacksCreatureYouControlTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum SourceIsRingBearerCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return Optional
|
||||
.ofNullable(source.getSourcePermanentIfItStillExists(game))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(permanent -> permanent.isControlledBy(source.getControllerId()))
|
||||
.map(permanent -> permanent.isRingBearer(game))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{this} is your Ring-bearer";
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ public class TheRingTemptsYouEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// TODO: Implement when we know what the mechanic does
|
||||
return false;
|
||||
game.temptWithTheRing(source.getControllerId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -406,6 +406,8 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
|||
|
||||
void ventureIntoDungeon(UUID playerId, boolean undercity);
|
||||
|
||||
void temptWithTheRing(UUID playerId);
|
||||
|
||||
/**
|
||||
* Tells whether the current game has day or night, defaults to false
|
||||
*/
|
||||
|
@ -552,8 +554,8 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
|||
/**
|
||||
* Function to call for a player to take the initiative.
|
||||
*
|
||||
* @param source The ability granting initiative.
|
||||
* @param initiativeId UUID of the player taking the initiative
|
||||
* @param source The ability granting initiative.
|
||||
* @param initiativeId UUID of the player taking the initiative
|
||||
*/
|
||||
void takeInitiative(Ability source, UUID initiativeId);
|
||||
|
||||
|
@ -681,6 +683,6 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
|||
void setGameStopped(boolean gameStopped);
|
||||
|
||||
boolean isGameStopped();
|
||||
|
||||
|
||||
boolean isTurnOrderReversed();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import mage.game.combat.Combat;
|
|||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.command.*;
|
||||
import mage.game.command.dungeons.UndercityDungeon;
|
||||
import mage.game.command.emblems.TheRingEmblem;
|
||||
import mage.game.events.*;
|
||||
import mage.game.events.TableEvent.EventType;
|
||||
import mage.game.mulligan.Mulligan;
|
||||
|
@ -563,6 +564,34 @@ public abstract class GameImpl implements Game {
|
|||
fireEvent(GameEvent.getEvent(GameEvent.EventType.VENTURED, playerId, null, playerId));
|
||||
}
|
||||
|
||||
private TheRingEmblem getOrCreateTheRing(UUID playerId) {
|
||||
TheRingEmblem emblem = state
|
||||
.getCommand()
|
||||
.stream()
|
||||
.filter(TheRingEmblem.class::isInstance)
|
||||
.map(TheRingEmblem.class::cast)
|
||||
.filter(commandObject -> commandObject.isControlledBy(playerId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (emblem != null) {
|
||||
return emblem;
|
||||
}
|
||||
TheRingEmblem newEmblem = new TheRingEmblem(playerId);
|
||||
state.addCommandObject(newEmblem);
|
||||
return newEmblem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void temptWithTheRing(UUID playerId) {
|
||||
Player player = getPlayer(playerId);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
player.chooseRingBearer(this);
|
||||
getOrCreateTheRing(playerId).addNextAbility(this);
|
||||
fireEvent(GameEvent.getEvent(GameEvent.EventType.TEMPTED_BY_RING, playerId, null, playerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDayNight() {
|
||||
return state.isHasDayNight();
|
||||
|
@ -1302,6 +1331,7 @@ public abstract class GameImpl implements Game {
|
|||
newWatchers.add(new EndStepCountWatcher());
|
||||
newWatchers.add(new CommanderPlaysCountWatcher()); // commander plays count uses in non commander games by some cards
|
||||
newWatchers.add(new CreaturesDiedWatcher());
|
||||
newWatchers.add(new TemptedByTheRingWatcher());
|
||||
|
||||
// runtime check - allows only GAME scope (one watcher per game)
|
||||
newWatchers.forEach(watcher -> {
|
||||
|
|
191
Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java
Normal file
191
Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java
Normal file
|
@ -0,0 +1,191 @@
|
|||
package mage.game.command.emblems;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
|
||||
import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.DrawDiscardControllerEffect;
|
||||
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
|
||||
import mage.abilities.effects.common.SacrificeTargetEffect;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.watchers.common.TemptedByTheRingWatcher;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class TheRingEmblem extends Emblem {
|
||||
private static final FilterPermanent filter = new FilterControlledPermanent("your Ring-bearer");
|
||||
|
||||
static {
|
||||
filter.add(TheRingEmblemPredicate.instance);
|
||||
}
|
||||
|
||||
public TheRingEmblem(UUID controllerId) {
|
||||
super();
|
||||
this.setName("The Ring");
|
||||
this.setExpansionSetCodeForImage("LTR");
|
||||
this.setControllerId(controllerId);
|
||||
}
|
||||
|
||||
public void addNextAbility(Game game) {
|
||||
Ability ability;
|
||||
switch (TemptedByTheRingWatcher.getCount(this.getControllerId(), game)) {
|
||||
case 0:
|
||||
// Your Ring-bearer is legendary and can't be blocked by creatures with greater power.
|
||||
ability = new SimpleStaticAbility(Zone.COMMAND, new TheRingEmblemLegendaryEffect());
|
||||
ability.addEffect(new TheRingEmblemEvasionEffect());
|
||||
break;
|
||||
case 1:
|
||||
// Whenever your Ring-bearer attacks, draw a card, then discard a card.
|
||||
ability = new AttacksCreatureYouControlTriggeredAbility(
|
||||
Zone.COMMAND,
|
||||
new DrawDiscardControllerEffect(1, 1),
|
||||
false, filter, false
|
||||
).setTriggerPhrase("Whenever your Ring-bearer attacks, ");
|
||||
break;
|
||||
case 2:
|
||||
// Whenever your Ring-bearer becomes blocked by a creature, that creature's controller sacrifices it at end of combat.
|
||||
ability = new TheRingEmblemTriggeredAbility();
|
||||
break;
|
||||
case 3:
|
||||
// Whenever your Ring-bearer deals combat damage to a player, each opponent loses 3 life.
|
||||
ability = new DealsDamageToAPlayerAllTriggeredAbility(
|
||||
Zone.COMMAND, new LoseLifeOpponentsEffect(3), filter, false,
|
||||
SetTargetPointer.NONE, true, false
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
this.getAbilities().add(ability);
|
||||
ability.setSourceId(this.getId());
|
||||
ability.setControllerId(this.getControllerId());
|
||||
game.getState().addAbility(ability, this);
|
||||
}
|
||||
}
|
||||
|
||||
enum TheRingEmblemPredicate implements Predicate<Permanent> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return input.isRingBearer(game);
|
||||
}
|
||||
}
|
||||
|
||||
class TheRingEmblemLegendaryEffect extends ContinuousEffectImpl {
|
||||
|
||||
TheRingEmblemLegendaryEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
|
||||
staticText = "your Ring-bearer is legendary";
|
||||
}
|
||||
|
||||
private TheRingEmblemLegendaryEffect(final TheRingEmblemLegendaryEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRingEmblemLegendaryEffect copy() {
|
||||
return new TheRingEmblemLegendaryEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = Optional
|
||||
.ofNullable(game.getPlayer(source.getControllerId()))
|
||||
.filter(Objects::nonNull)
|
||||
.map(player -> player.getRingBearer(game))
|
||||
.orElse(null);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
permanent.addSuperType(SuperType.LEGENDARY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class TheRingEmblemEvasionEffect extends RestrictionEffect {
|
||||
|
||||
TheRingEmblemEvasionEffect() {
|
||||
super(Duration.WhileOnBattlefield);
|
||||
staticText = "and can't be blocked by creatures with greater power";
|
||||
}
|
||||
|
||||
private TheRingEmblemEvasionEffect(final TheRingEmblemEvasionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRingEmblemEvasionEffect copy() {
|
||||
return new TheRingEmblemEvasionEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
return permanent.isControlledBy(source.getControllerId())
|
||||
&& permanent.isRingBearer(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) {
|
||||
return blocker.getPower().getValue() <= attacker.getPower().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
class TheRingEmblemTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
TheRingEmblemTriggeredAbility() {
|
||||
super(Zone.COMMAND, new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new SacrificeTargetEffect())));
|
||||
}
|
||||
|
||||
private TheRingEmblemTriggeredAbility(final TheRingEmblemTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRingEmblemTriggeredAbility copy() {
|
||||
return new TheRingEmblemTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.CREATURE_BLOCKED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent attacker = game.getPermanent(event.getTargetId());
|
||||
Permanent blocker = game.getPermanent(event.getSourceId());
|
||||
if (attacker == null
|
||||
|| blocker == null
|
||||
|| attacker.isControlledBy(getControllerId())
|
||||
|| !attacker.isRingBearer(game)) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setTargetPointer(new FixedTarget(blocker, game));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever your Ring-bearer becomes blocked by a creature, " +
|
||||
"that creature's controller sacrifices it at end of combat.";
|
||||
}
|
||||
}
|
|
@ -493,6 +493,7 @@ public class GameEvent implements Serializable {
|
|||
ROOM_ENTERED,
|
||||
VENTURE, VENTURED,
|
||||
DUNGEON_COMPLETED,
|
||||
TEMPTED_BY_RING, RING_BEARER_CHOSEN,
|
||||
REMOVED_FROM_COMBAT, // targetId id of permanent removed from combat
|
||||
FORETOLD, // targetId id of card foretold
|
||||
FORETELL, // targetId id of card foretell playerId id of the controller
|
||||
|
|
|
@ -419,6 +419,8 @@ public interface Permanent extends Card, Controllable {
|
|||
|
||||
boolean isManifested();
|
||||
|
||||
boolean isRingBearer(Game game);
|
||||
|
||||
@Override
|
||||
Permanent copy();
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ import mage.players.Player;
|
|||
import mage.target.TargetPlayer;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.RandomUtil;
|
||||
import mage.util.ThreadLocalStringBuilder;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
@ -1813,6 +1812,12 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.secondSideCard = card;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRingBearer(Game game) {
|
||||
Player player = game.getPlayer(getControllerId());
|
||||
return player != null && this.equals(player.getRingBearer(game));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fight(Permanent fightTarget, Ability source, Game game) {
|
||||
return this.fight(fightTarget, source, game, true);
|
||||
|
|
|
@ -1080,6 +1080,12 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
FilterMana getPhyrexianColors();
|
||||
|
||||
UUID getRingBearerId();
|
||||
|
||||
Permanent getRingBearer(Game game);
|
||||
|
||||
void chooseRingBearer(Game game);
|
||||
|
||||
/**
|
||||
* Function to query if the player has strictChooseMode enabled. Only the test player can have it.
|
||||
* Function is added here so that the test suite project does not have to be imported into the client/server project.
|
||||
|
|
|
@ -58,6 +58,7 @@ import mage.target.TargetAmount;
|
|||
import mage.target.TargetCard;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetDiscard;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.GameLog;
|
||||
|
@ -176,6 +177,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// mana colors the player can handle like Phyrexian mana
|
||||
protected FilterMana phyrexianColors;
|
||||
|
||||
protected UUID ringBearerId = null;
|
||||
|
||||
// Used during available mana calculation to give back possible available net mana from triggered mana abilities (No need to copy)
|
||||
protected final List<List<Mana>> availableTriggeredManaList = new ArrayList<>();
|
||||
|
||||
|
@ -285,6 +288,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
this.payManaMode = player.payManaMode;
|
||||
this.phyrexianColors = player.getPhyrexianColors() != null ? player.phyrexianColors.copy() : null;
|
||||
this.ringBearerId = player.ringBearerId;
|
||||
for (Designation object : player.designations) {
|
||||
this.designations.add(object.copy());
|
||||
}
|
||||
|
@ -372,6 +376,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
this.phyrexianColors = player.getPhyrexianColors() != null ? player.getPhyrexianColors().copy() : null;
|
||||
|
||||
this.ringBearerId = player.getRingBearerId();
|
||||
|
||||
this.designations.clear();
|
||||
for (Designation object : player.getDesignations()) {
|
||||
this.designations.add(object.copy());
|
||||
|
@ -5127,6 +5133,62 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return this.phyrexianColors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getRingBearerId() {
|
||||
return ringBearerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permanent getRingBearer(Game game) {
|
||||
if (ringBearerId == null) {
|
||||
return null;
|
||||
}
|
||||
Permanent bearer = game.getPermanent(ringBearerId);
|
||||
if (bearer != null && bearer.isControlledBy(getId())) {
|
||||
return bearer;
|
||||
}
|
||||
ringBearerId = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chooseRingBearer(Game game) {
|
||||
Permanent currentBearer = getRingBearer(game);
|
||||
int creatureCount = game.getBattlefield().count(
|
||||
StaticFilters.FILTER_CONTROLLED_CREATURE, getId(), null, game
|
||||
);
|
||||
boolean mustChoose;
|
||||
if (currentBearer == null) {
|
||||
if (creatureCount > 0) {
|
||||
mustChoose = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (currentBearer.isCreature(game)) {
|
||||
if (creatureCount > 1) {
|
||||
mustChoose = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (creatureCount > 0) {
|
||||
mustChoose = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (!mustChoose && !chooseUse(Outcome.Neutral, "Choose a new Ring-bearer?", null, game)) {
|
||||
return;
|
||||
}
|
||||
TargetPermanent target = new TargetControlledCreaturePermanent();
|
||||
target.setNotTarget(true);
|
||||
target.withChooseHint("to be your Ring-bearer");
|
||||
choose(Outcome.Neutral, target, null, game);
|
||||
UUID newBearerId = target.getFirstTarget();
|
||||
if (game.getPermanent(newBearerId) != null) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.RING_BEARER_CHOSEN, newBearerId, null, getId()));
|
||||
this.ringBearerId = newBearerId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana) {
|
||||
return card.getSpellAbility();
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package mage.watchers.common;
|
||||
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.util.CardUtil;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class TemptedByTheRingWatcher extends Watcher {
|
||||
|
||||
private final Map<UUID, Integer> map = new HashMap<>();
|
||||
|
||||
public TemptedByTheRingWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
switch (event.getType()) {
|
||||
case TEMPTED_BY_RING:
|
||||
map.compute(event.getPlayerId(), CardUtil::setOrIncrementValue);
|
||||
return;
|
||||
case BEGINNING_PHASE_PRE:
|
||||
if (game.getTurnNum() == 1) {
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
}
|
||||
|
||||
public static int getCount(UUID playerId, Game game) {
|
||||
return game
|
||||
.getState()
|
||||
.getWatcher(TemptedByTheRingWatcher.class)
|
||||
.map
|
||||
.getOrDefault(playerId, 0);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue