This commit is contained in:
Ingmar Goudt 2019-01-17 18:22:19 +01:00
commit dbdd9c00a3
10 changed files with 605 additions and 283 deletions

View file

@ -0,0 +1,163 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.ControlsPermanentsComparedToOpponentsCondition;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.SubType;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetOpponent;
/**
*
* @author jeffwadsworth
*/
public final class ChaosLord extends CardImpl {
public ChaosLord(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}{R}");
this.subtype.add(SubType.HUMAN);
this.power = new MageInt(7);
this.toughness = new MageInt(7);
// First strike
this.addAbility(FirstStrikeAbility.getInstance());
// At the beginning of your upkeep, target opponent gains control of Chaos Lord if the number of permanents is even.
Ability ability = new ChaosLordTriggeredAbility();
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
// Chaos Lord can attack as though it had haste unless it entered the battlefield this turn.
this.addAbility(new SimpleStaticAbility(
Zone.BATTLEFIELD,
new ChaosLordEffect()));
}
private ChaosLord(final ChaosLord card) {
super(card);
}
@Override
public ChaosLord copy() {
return new ChaosLord(this);
}
}
class ChaosLordTriggeredAbility extends BeginningOfUpkeepTriggeredAbility {
public ChaosLordTriggeredAbility() {
super(Zone.BATTLEFIELD,
new GainControlSourceEffect(),
TargetController.YOU,
false);
}
public ChaosLordTriggeredAbility(ChaosLordTriggeredAbility ability) {
super(ability);
}
@Override
public BeginningOfUpkeepTriggeredAbility copy() {
return new ChaosLordTriggeredAbility(this);
}
@Override
public boolean checkInterveningIfClause(Game game) {
Condition condition = new ControlsPermanentsComparedToOpponentsCondition(
ComparisonType.EQUAL_TO,
new FilterPermanent());
Player controller = game.getPlayer(controllerId);
if (controller != null
&& condition.apply(game, this)) {
return super.checkInterveningIfClause(game);
}
return false;
}
@Override
public String getRule() {
return "At the beginning of your upkeep, target opponent gains control of {this} if the number of permanents is even.";
}
}
class GainControlSourceEffect extends ContinuousEffectImpl {
public GainControlSourceEffect() {
super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl);
staticText = "target opponent gains control of {this}";
}
public GainControlSourceEffect(final GainControlSourceEffect effect) {
super(effect);
}
@Override
public GainControlSourceEffect copy() {
return new GainControlSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game);
if (permanent != null) {
return permanent.changeControllerId(source.getFirstTarget(), game);
} else {
discard();
}
return false;
}
}
class ChaosLordEffect extends AsThoughEffectImpl {
public ChaosLordEffect() {
super(AsThoughEffectType.ATTACK_AS_HASTE, Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "Chaos Lord can attack as though it had haste unless it entered the battlefield this turn";
}
public ChaosLordEffect(final ChaosLordEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public ChaosLordEffect copy() {
return new ChaosLordEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Permanent chaosLord = game.getPermanent(objectId);
return chaosLord != null
&& objectId == source.getSourceId()
&& chaosLord.getTurnsOnBattlefield() > 0;
}
}

View file

@ -6,13 +6,13 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SuperType;
import mage.game.Game; import mage.game.Game;
import mage.game.events.FlipCoinEvent; import mage.game.events.FlipCoinEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.players.Player;
import mage.util.CardUtil;
import mage.util.RandomUtil;
import java.util.UUID; import java.util.UUID;
@ -26,7 +26,7 @@ public final class KrarksThumb extends CardImpl {
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
// If you would flip a coin, instead flip two coins and ignore one. // If you would flip a coin, instead flip two coins and ignore one.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KrarksThumbEffect())); this.addAbility(new SimpleStaticAbility(new KrarksThumbEffect()));
} }
private KrarksThumb(final KrarksThumb card) { private KrarksThumb(final KrarksThumb card) {
@ -43,33 +43,22 @@ class KrarksThumbEffect extends ReplacementEffectImpl {
KrarksThumbEffect() { KrarksThumbEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit); super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "If you would flip a coin, instead flip two coins and ignore one"; staticText = "If you would flip a coin, instead flip two coins and ignore one.";
} }
private KrarksThumbEffect(final KrarksThumbEffect effect) { private KrarksThumbEffect(final KrarksThumbEffect effect) {
super(effect); super(effect);
} }
@Override
public KrarksThumbEffect copy() {
return new KrarksThumbEffect(this);
}
@Override @Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player player = game.getPlayer(event.getPlayerId()); FlipCoinEvent flipCoinEvent = (FlipCoinEvent) event;
if (player == null || !player.getId().equals(source.getControllerId())) { flipCoinEvent.setFlipCount(2 * flipCoinEvent.getFlipCount());
return false;
}
FlipCoinEvent flipEvent = (FlipCoinEvent) event;
boolean secondFlip = RandomUtil.nextBoolean();
game.informPlayers(player.getLogName() + " flipped a " + flipEvent.getResultName()
+ " and a " + CardUtil.booleanToFlipName(secondFlip)
);
boolean chosenFlip = player.chooseUse(
Outcome.Benefit, "Choose which coin you want",
(flipEvent.isWinnable() ? "(You chose " + flipEvent.getChosenName() + ")" : null),
flipEvent.getResultName(), CardUtil.booleanToFlipName(secondFlip), source, game
);
if (!chosenFlip) {
flipEvent.setResult(secondFlip);
}
game.informPlayers(player.getLogName() + " chooses to keep " + flipEvent.getResultName());
return false; return false;
} }
@ -87,9 +76,4 @@ class KrarksThumbEffect extends ReplacementEffectImpl {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
return false; return false;
} }
@Override
public KrarksThumbEffect copy() {
return new KrarksThumbEffect(this);
}
} }

View file

@ -12,7 +12,7 @@ import mage.constants.CardType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SupertypePredicate; import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
@ -24,7 +24,7 @@ import java.util.UUID;
public final class PriceOfFame extends CardImpl { public final class PriceOfFame extends CardImpl {
private static final FilterPermanent filter private static final FilterPermanent filter
= new FilterControlledCreaturePermanent("a legendary creature"); = new FilterCreaturePermanent("a legendary creature");
static { static {
filter.add(new SupertypePredicate(SuperType.LEGENDARY)); filter.add(new SupertypePredicate(SuperType.LEGENDARY));

View file

@ -0,0 +1,140 @@
package mage.cards.s;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.DealtDamageAndDiedTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.SacrificeTargetEffect;
import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author jeffwadsworth
*/
public final class Seraph extends CardImpl {
public Seraph(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}");
this.subtype.add(SubType.ANGEL);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever a creature dealt damage by Seraph this turn dies, put that card onto the battlefield under your control at the beginning of the next end step. Sacrifice the creature when you lose control of Seraph.
Effect effect = new CreateDelayedTriggeredAbilityEffect(
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
new SeraphEffect()));
effect.setText("put that card onto the battlefield under your control at the beginning of the next end step. Sacrifice the creature when you lose control of {this}");
this.addAbility(new DealtDamageAndDiedTriggeredAbility(effect));
}
private Seraph(final Seraph card) {
super(card);
}
@Override
public Seraph copy() {
return new Seraph(this);
}
}
class SeraphEffect extends OneShotEffect {
SeraphEffect() {
super(Outcome.Neutral);
staticText = "put that card onto the battlefield under your control. Sacrifice it when you lose control of {this}";
}
SeraphEffect(SeraphEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Card creatureCard = game.getCard(targetPointer.getFirst(game, source));
if (controller != null
&& creatureCard != null
&& game.getState().getZone(creatureCard.getId()) == Zone.GRAVEYARD) { // must be still in the graveyard
controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game, false, false, false, null);
OneShotEffect effect = new SacrificeTargetEffect();
effect.setText("Sacrifice this if Seraph leaves the battlefield or its current controller loses control of it.");
effect.setTargetPointer(new FixedTarget(creatureCard.getId()));
SeraphDelayedTriggeredAbility dTA = new SeraphDelayedTriggeredAbility(effect, source.getSourceId());
game.addDelayedTriggeredAbility(dTA, source);
return true;
}
return false;
}
@Override
public SeraphEffect copy() {
return new SeraphEffect(this);
}
}
class SeraphDelayedTriggeredAbility extends DelayedTriggeredAbility {
UUID seraph;
SeraphDelayedTriggeredAbility(Effect effect, UUID seraph) {
super(effect, Duration.EndOfGame, true);
this.seraph = seraph;
}
SeraphDelayedTriggeredAbility(SeraphDelayedTriggeredAbility ability) {
super(ability);
this.seraph = ability.seraph;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.LOST_CONTROL
|| event.getType() == EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == EventType.LOST_CONTROL
&& event.getSourceId().equals(seraph)) {
return true;
}
if (event.getType() == EventType.ZONE_CHANGE
&& event.getTargetId().equals(seraph)) {
return true;
}
return false;
}
@Override
public SeraphDelayedTriggeredAbility copy() {
return new SeraphDelayedTriggeredAbility(this);
}
@Override
public String getRule() {
return "Control of Seraph was lost, sacrifice this.";
}
}

View file

@ -368,6 +368,7 @@ public final class FifthEdition extends ExpansionSet {
cards.add(new SetCardInfo("Seasinger", 121, Rarity.UNCOMMON, mage.cards.s.Seasinger.class)); cards.add(new SetCardInfo("Seasinger", 121, Rarity.UNCOMMON, mage.cards.s.Seasinger.class));
cards.add(new SetCardInfo("Segovian Leviathan", 122, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class)); cards.add(new SetCardInfo("Segovian Leviathan", 122, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class));
cards.add(new SetCardInfo("Sengir Autocrat", 193, Rarity.RARE, mage.cards.s.SengirAutocrat.class)); cards.add(new SetCardInfo("Sengir Autocrat", 193, Rarity.RARE, mage.cards.s.SengirAutocrat.class));
cards.add(new SetCardInfo("Seraph", 59, Rarity.RARE, mage.cards.s.Seraph.class));
cards.add(new SetCardInfo("Serpent Generator", 397, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); cards.add(new SetCardInfo("Serpent Generator", 397, Rarity.RARE, mage.cards.s.SerpentGenerator.class));
cards.add(new SetCardInfo("Serra Bestiary", 60, Rarity.UNCOMMON, mage.cards.s.SerraBestiary.class)); cards.add(new SetCardInfo("Serra Bestiary", 60, Rarity.UNCOMMON, mage.cards.s.SerraBestiary.class));
cards.add(new SetCardInfo("Serra Paladin", 61, Rarity.UNCOMMON, mage.cards.s.SerraPaladin.class)); cards.add(new SetCardInfo("Serra Paladin", 61, Rarity.UNCOMMON, mage.cards.s.SerraPaladin.class));

View file

@ -62,6 +62,7 @@ public final class IceAge extends ExpansionSet {
cards.add(new SetCardInfo("Caribou Range", 11, Rarity.RARE, mage.cards.c.CaribouRange.class)); cards.add(new SetCardInfo("Caribou Range", 11, Rarity.RARE, mage.cards.c.CaribouRange.class));
cards.add(new SetCardInfo("Celestial Sword", 314, Rarity.RARE, mage.cards.c.CelestialSword.class)); cards.add(new SetCardInfo("Celestial Sword", 314, Rarity.RARE, mage.cards.c.CelestialSword.class));
cards.add(new SetCardInfo("Centaur Archer", 282, Rarity.UNCOMMON, mage.cards.c.CentaurArcher.class)); cards.add(new SetCardInfo("Centaur Archer", 282, Rarity.UNCOMMON, mage.cards.c.CentaurArcher.class));
cards.add(new SetCardInfo("Chaos Lord", 178, Rarity.RARE, mage.cards.c.ChaosLord.class));
cards.add(new SetCardInfo("Chaos Moon", 179, Rarity.RARE, mage.cards.c.ChaosMoon.class)); cards.add(new SetCardInfo("Chaos Moon", 179, Rarity.RARE, mage.cards.c.ChaosMoon.class));
cards.add(new SetCardInfo("Chub Toad", 229, Rarity.COMMON, mage.cards.c.ChubToad.class)); cards.add(new SetCardInfo("Chub Toad", 229, Rarity.COMMON, mage.cards.c.ChubToad.class));
cards.add(new SetCardInfo("Circle of Protection: Black", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); cards.add(new SetCardInfo("Circle of Protection: Black", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class));
@ -287,6 +288,7 @@ public final class IceAge extends ExpansionSet {
cards.add(new SetCardInfo("Scaled Wurm", 262, Rarity.COMMON, mage.cards.s.ScaledWurm.class)); cards.add(new SetCardInfo("Scaled Wurm", 262, Rarity.COMMON, mage.cards.s.ScaledWurm.class));
cards.add(new SetCardInfo("Sea Spirit", 95, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); cards.add(new SetCardInfo("Sea Spirit", 95, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class));
cards.add(new SetCardInfo("Seizures", 159, Rarity.COMMON, mage.cards.s.Seizures.class)); cards.add(new SetCardInfo("Seizures", 159, Rarity.COMMON, mage.cards.s.Seizures.class));
cards.add(new SetCardInfo("Seraph", 51, Rarity.RARE, mage.cards.s.Seraph.class));
cards.add(new SetCardInfo("Shambling Strider", 263, Rarity.COMMON, mage.cards.s.ShamblingStrider.class)); cards.add(new SetCardInfo("Shambling Strider", 263, Rarity.COMMON, mage.cards.s.ShamblingStrider.class));
cards.add(new SetCardInfo("Shatter", 216, Rarity.COMMON, mage.cards.s.Shatter.class)); cards.add(new SetCardInfo("Shatter", 216, Rarity.COMMON, mage.cards.s.Shatter.class));
cards.add(new SetCardInfo("Shield Bearer", 52, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); cards.add(new SetCardInfo("Shield Bearer", 52, Rarity.COMMON, mage.cards.s.ShieldBearer.class));

View file

@ -175,6 +175,7 @@ public final class MastersEdition extends ExpansionSet {
cards.add(new SetCardInfo("Scryb Sprites", 128, Rarity.COMMON, mage.cards.s.ScrybSprites.class)); cards.add(new SetCardInfo("Scryb Sprites", 128, Rarity.COMMON, mage.cards.s.ScrybSprites.class));
cards.add(new SetCardInfo("Seasinger", 49, Rarity.UNCOMMON, mage.cards.s.Seasinger.class)); cards.add(new SetCardInfo("Seasinger", 49, Rarity.UNCOMMON, mage.cards.s.Seasinger.class));
cards.add(new SetCardInfo("Sea Sprite", 48, Rarity.COMMON, mage.cards.s.SeaSprite.class)); cards.add(new SetCardInfo("Sea Sprite", 48, Rarity.COMMON, mage.cards.s.SeaSprite.class));
cards.add(new SetCardInfo("Seraph", 26, Rarity.RARE, mage.cards.s.Seraph.class));
cards.add(new SetCardInfo("Serendib Efreet", 50, Rarity.RARE, mage.cards.s.SerendibEfreet.class)); cards.add(new SetCardInfo("Serendib Efreet", 50, Rarity.RARE, mage.cards.s.SerendibEfreet.class));
cards.add(new SetCardInfo("Serpent Generator", 164, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); cards.add(new SetCardInfo("Serpent Generator", 164, Rarity.RARE, mage.cards.s.SerpentGenerator.class));
cards.add(new SetCardInfo("Shambling Strider", 129, Rarity.COMMON, mage.cards.s.ShamblingStrider.class)); cards.add(new SetCardInfo("Shambling Strider", 129, Rarity.COMMON, mage.cards.s.ShamblingStrider.class));

View file

@ -11,6 +11,7 @@ public class FlipCoinEvent extends GameEvent {
private boolean result; private boolean result;
private final boolean chosen; private final boolean chosen;
private final boolean winnable; private final boolean winnable;
private int flipCount = 1;
public FlipCoinEvent(UUID playerId, UUID sourceId, boolean result, boolean chosen, boolean winnable) { public FlipCoinEvent(UUID playerId, UUID sourceId, boolean result, boolean chosen, boolean winnable) {
super(EventType.FLIP_COIN, playerId, sourceId, playerId); super(EventType.FLIP_COIN, playerId, sourceId, playerId);
@ -43,6 +44,14 @@ public class FlipCoinEvent extends GameEvent {
return winnable; return winnable;
} }
public int getFlipCount() {
return flipCount;
}
public void setFlipCount(int flipCount) {
this.flipCount = flipCount;
}
public CoinFlippedEvent getFlippedEvent() { public CoinFlippedEvent getFlippedEvent() {
return new CoinFlippedEvent(playerId, sourceId, result, chosen, winnable); return new CoinFlippedEvent(playerId, sourceId, result, chosen, winnable);
} }

View file

@ -74,7 +74,7 @@ public interface Player extends MageItem, Copyable<Player> {
void setLife(int life, Game game, UUID sourceId); void setLife(int life, Game game, UUID sourceId);
/** /**
* @param amount amount of life loss * @param amount amount of life loss
* @param game * @param game
* @param atCombat was the source combat damage * @param atCombat was the source combat damage
* @return * @return
@ -347,7 +347,7 @@ public interface Player extends MageItem, Copyable<Player> {
* @param target * @param target
* @param game * @param game
* @param targetPlayerId player whose library will be searched * @param targetPlayerId player whose library will be searched
* @param triggerEvents whether searching will trigger any game events * @param triggerEvents whether searching will trigger any game events
* @return true if search was successful * @return true if search was successful
*/ */
boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId, boolean triggerEvents); boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId, boolean triggerEvents);
@ -355,6 +355,7 @@ public interface Player extends MageItem, Copyable<Player> {
/** /**
* Reveals all players' libraries. Useful for abilities like Jace, Architect of Thought's -8 * Reveals all players' libraries. Useful for abilities like Jace, Architect of Thought's -8
* that have effects that require information from all libraries. * that have effects that require information from all libraries.
*
* @param source * @param source
* @param game * @param game
* @return * @return
@ -366,23 +367,23 @@ public interface Player extends MageItem, Copyable<Player> {
/** /**
* Plays a card if possible * Plays a card if possible
* *
* @param card the card that can be cast * @param card the card that can be cast
* @param game * @param game
* @param noMana if it's a spell i can be cast without paying mana * @param noMana if it's a spell i can be cast without paying mana
* @param ignoreTiming if it's cast during the resolution of another spell * @param ignoreTiming if it's cast during the resolution of another spell
* no sorcery or play land timing restriction are checked. For a land it has * no sorcery or play land timing restriction are checked. For a land it has
* to be the turn of the player playing that card. * to be the turn of the player playing that card.
* @param reference mage object that allows to play the card * @param reference mage object that allows to play the card
* @return * @return
*/ */
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference); boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference);
/** /**
* @param card the land card to play * @param card the land card to play
* @param game * @param game
* @param ignoreTiming false - it won't be checked if the stack is empty and * @param ignoreTiming false - it won't be checked if the stack is empty and
* you are able to play a Sorcery. It's still checked, if you are able to * you are able to play a Sorcery. It's still checked, if you are able to
* play a land concerning the number of lands you already played. * play a land concerning the number of lands you already played.
* @return * @return
*/ */
boolean playLand(Card card, Game game, boolean ignoreTiming); boolean playLand(Card card, Game game, boolean ignoreTiming);
@ -528,11 +529,11 @@ public interface Player extends MageItem, Copyable<Player> {
/** /**
* Moves the cards from cards to the bottom of the players library. * Moves the cards from cards to the bottom of the players library.
* *
* @param cards - list of cards that have to be moved * @param cards - list of cards that have to be moved
* @param game - game * @param game - game
* @param anyOrder - true if player can determine the order of the cards * @param anyOrder - true if player can determine the order of the cards
* else random order * else random order
* @param source - source ability * @param source - source ability
* @return * @return
*/ */
boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder); boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
@ -553,10 +554,10 @@ public interface Player extends MageItem, Copyable<Player> {
/** /**
* Moves the cards from cards to the top of players library. * Moves the cards from cards to the top of players library.
* *
* @param cards - list of cards that have to be moved * @param cards - list of cards that have to be moved
* @param game - game * @param game - game
* @param anyOrder - true if player can determine the order of the cards * @param anyOrder - true if player can determine the order of the cards
* @param source - source ability * @param source - source ability
* @return * @return
*/ */
boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder); boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
@ -582,8 +583,8 @@ public interface Player extends MageItem, Copyable<Player> {
/** /**
* Choose the order in which blockers get damage assigned to * Choose the order in which blockers get damage assigned to
* *
* @param blockers list of blockers where to choose the next one from * @param blockers list of blockers where to choose the next one from
* @param combatGroup the concerning combat group * @param combatGroup the concerning combat group
* @param blockerOrder the already set order of blockers * @param blockerOrder the already set order of blockers
* @param game * @param game
* @return blocker next to add to the blocker order * @return blocker next to add to the blocker order
@ -722,11 +723,11 @@ public interface Player extends MageItem, Copyable<Player> {
* @param toZone * @param toZone
* @param source * @param source
* @param game * @param game
* @param tapped the cards are tapped on the battlefield * @param tapped the cards are tapped on the battlefield
* @param faceDown the cards are face down in the to zone * @param faceDown the cards are face down in the to zone
* @param byOwner the card is moved (or put onto battlefield) by the owner * @param byOwner the card is moved (or put onto battlefield) by the owner
* of the card and if target zone is battlefield controls the permanent * of the card and if target zone is battlefield controls the permanent
* (instead of the controller of the source) * (instead of the controller of the source)
* @param appliedEffects * @param appliedEffects
* @return * @return
*/ */
@ -762,7 +763,7 @@ public interface Player extends MageItem, Copyable<Player> {
* list of applied effects is not saved * list of applied effects is not saved
* *
* @param card * @param card
* @param exileId exile zone id (optional) * @param exileId exile zone id (optional)
* @param exileName name of exile zone (optional) * @param exileName name of exile zone (optional)
* @param sourceId * @param sourceId
* @param game * @param game
@ -804,7 +805,7 @@ public interface Player extends MageItem, Copyable<Player> {
* @param sourceId * @param sourceId
* @param game * @param game
* @param fromZone if null, this info isn't postet * @param fromZone if null, this info isn't postet
* @param toTop to the top of the library else to the bottom * @param toTop to the top of the library else to the bottom
* @param withName show the card name in the log * @param withName show the card name in the log
* @return * @return
*/ */
@ -829,10 +830,10 @@ public interface Player extends MageItem, Copyable<Player> {
* without mana (null) or the mana set to manaCosts instead of its normal * without mana (null) or the mana set to manaCosts instead of its normal
* mana costs. * mana costs.
* *
* @param sourceId the source that can be cast without mana * @param sourceId the source that can be cast without mana
* @param manaCosts alternate ManaCost, null if it can be cast without mana * @param manaCosts alternate ManaCost, null if it can be cast without mana
* cost * cost
* @param costs alternate other costs you need to pay * @param costs alternate other costs you need to pay
*/ */
void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs); void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs);

View file

@ -2573,13 +2573,34 @@ public abstract class PlayerImpl implements Player, Serializable {
boolean chosen = false; boolean chosen = false;
if (winnable) { if (winnable) {
chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game); chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game);
game.informPlayers(getLogName() + " chose " + (chosen ? "heads." : "tails.")); game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(chosen));
} }
boolean result = RandomUtil.nextBoolean(); boolean result = RandomUtil.nextBoolean();
FlipCoinEvent event = new FlipCoinEvent(playerId, source.getSourceId(), result, chosen, winnable); FlipCoinEvent event = new FlipCoinEvent(playerId, source.getSourceId(), result, chosen, winnable);
event.addAppliedEffects(appliedEffects); event.addAppliedEffects(appliedEffects);
game.replaceEvent(event); game.replaceEvent(event);
game.informPlayers(getLogName() + " got " + (event.getResult() ? "heads" : "tails")); game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(event.getResult()));
if (event.getFlipCount() > 1) {
boolean canChooseHeads = event.getResult();
boolean canChooseTails = !event.getResult();
for (int i = 1; i < event.getFlipCount(); i++) {
boolean tempFlip = RandomUtil.nextBoolean();
canChooseHeads = canChooseHeads || tempFlip;
canChooseTails = canChooseTails || !tempFlip;
game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(tempFlip));
}
if (canChooseHeads && canChooseTails) {
event.setResult(chooseUse(Outcome.Benefit, "Choose which flip to keep",
(event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null),
"Heads", "Tails", source, game
));
} else if (canChooseHeads) {
event.setResult(true);
} else {
event.setResult(false);
}
game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult()));
}
if (event.isWinnable()) { if (event.isWinnable()) {
game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip"); game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip");
} }