From 136988de2911be41dff2562cc9f42bbbf5c7bdb7 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 27 May 2025 21:56:23 -0400 Subject: [PATCH] [FIN] Implement Edgar, King of Figaro, rework coin flips (#13672) * add method for multiple coin flips * [FIN] Implement Edgar, King of Figaro * add extra note * update coin flip logic * add test --- .../src/mage/cards/e/EdgarKingOfFigaro.java | 121 ++++++++++++++++++ .../src/mage/cards/g/GoblinTraprunner.java | 24 ++-- Mage.Sets/src/mage/cards/r/RalZarek.java | 33 ++--- .../src/mage/cards/t/TwoHeadedGiant.java | 20 ++- .../src/mage/cards/y/YusriFortunesFlame.java | 20 +-- Mage.Sets/src/mage/sets/FinalFantasy.java | 2 + .../single/fin/EdgarKingOfFigaroTest.java | 77 +++++++++++ .../java/org/mage/test/player/TestPlayer.java | 5 + .../java/mage/game/events/FlipCoinEvent.java | 9 ++ .../java/mage/game/events/FlipCoinsEvent.java | 25 ++++ .../main/java/mage/game/events/GameEvent.java | 2 +- Mage/src/main/java/mage/players/Player.java | 6 +- .../main/java/mage/players/PlayerImpl.java | 88 ++++++++----- 13 files changed, 345 insertions(+), 87 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java create mode 100644 Mage/src/main/java/mage/game/events/FlipCoinsEvent.java diff --git a/Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java b/Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java new file mode 100644 index 00000000000..00a3b337c9c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java @@ -0,0 +1,121 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.FlipCoinsEvent; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EdgarKingOfFigaro extends CardImpl { + + public EdgarKingOfFigaro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // When Edgar enters, draw a card for each artifact you control. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(ArtifactYouControlCount.instance) + ).addHint(ArtifactYouControlHint.instance)); + + // Two-Headed Coin -- The first time you flip one or more coins each turn, those coins come up heads and you win those flips. + this.addAbility(new SimpleStaticAbility(new EdgarKingOfFigaroEffect()) + .withFlavorWord("Two-Headed Coin"), new EdgarKingOfFigaroWatcher()); + } + + private EdgarKingOfFigaro(final EdgarKingOfFigaro card) { + super(card); + } + + @Override + public EdgarKingOfFigaro copy() { + return new EdgarKingOfFigaro(this); + } +} + +class EdgarKingOfFigaroEffect extends ReplacementEffectImpl { + + EdgarKingOfFigaroEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "the first time you flip one or more coins each turn, " + + "those coins come up heads and you win those flips"; + } + + private EdgarKingOfFigaroEffect(final EdgarKingOfFigaroEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((FlipCoinsEvent) event).setHeadsAndWon(true); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.FLIP_COINS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()) + && !EdgarKingOfFigaroWatcher.checkPlayer(game, source); + } + + @Override + public EdgarKingOfFigaroEffect copy() { + return new EdgarKingOfFigaroEffect(this); + } +} + +class EdgarKingOfFigaroWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + EdgarKingOfFigaroWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COIN_FLIPPED) { + set.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(EdgarKingOfFigaroWatcher.class) + .set + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java b/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java index dceeef319b6..8ab3b7760e0 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java +++ b/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java @@ -10,10 +10,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.token.GoblinToken; -import mage.players.Player; +import java.util.Optional; import java.util.UUID; /** @@ -50,17 +51,16 @@ enum GoblinTraprunnerValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - Player player = game.getPlayer(sourceAbility.getControllerId()); - if (player == null) { - return 0; - } - int count = 0; - for (int i = 0; i < 3; i++) { - if (player.flipCoin(sourceAbility, game, true)) { - count++; - } - } - return count; + return Optional + .ofNullable(sourceAbility) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(player -> player + .flipCoins(sourceAbility, game, 3, true) + .stream() + .mapToInt(x -> x ? 1 : 0) + .sum() + ).orElse(0); } @Override diff --git a/Mage.Sets/src/mage/cards/r/RalZarek.java b/Mage.Sets/src/mage/cards/r/RalZarek.java index df7330f31e0..d1476001c12 100644 --- a/Mage.Sets/src/mage/cards/r/RalZarek.java +++ b/Mage.Sets/src/mage/cards/r/RalZarek.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.effects.Effect; @@ -12,20 +10,22 @@ import mage.abilities.effects.common.UntapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.game.Controllable; import mage.game.Game; import mage.game.turn.TurnMod; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; import mage.target.targetpointer.SecondTargetPointer; +import java.util.Optional; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RalZarek extends CardImpl { @@ -64,7 +64,6 @@ public final class RalZarek extends CardImpl { // -7: Flip five coins. Take an extra turn after this one for each coin that comes up heads. this.addAbility(new LoyaltyAbility(new RalZarekExtraTurnsEffect(), -7)); - } private RalZarek(final RalZarek card) { @@ -95,15 +94,19 @@ class RalZarekExtraTurnsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (int i = 0; i < 5; i++) { - if (controller.flipCoin(source, game, false)) { - game.getState().getTurnMods().add(new TurnMod(source.getControllerId()).withExtraTurn()); - } - } - return true; + int amount = Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(player -> player + .flipCoins(source, game, 5, true) + .stream() + .mapToInt(x -> x ? 1 : 0) + .sum() + ).orElse(0); + for (int i = 0; i < amount; i++) { + game.getState().getTurnMods().add(new TurnMod(source.getControllerId()).withExtraTurn()); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java b/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java index fa65ee2a703..d7dbb6eb88a 100644 --- a/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java +++ b/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -9,17 +8,19 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.MenaceAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.List; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class TwoHeadedGiant extends CardImpl { @@ -69,14 +70,11 @@ class TwoHeadedGiantEffect extends OneShotEffect { if (player == null) { return false; } - boolean head1 = player.flipCoin(source, game, false); - boolean head2 = player.flipCoin(source, game, false); - if (head1 == head2) { - if (head1) { - game.addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source); - } else { - game.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn), source); - } + List flips = player.flipCoins(source, game, 2, false); + if (flips.get(0) == flips.get(1)) { + game.addEffect(new GainAbilitySourceEffect( + flips.get(0) ? DoubleStrikeAbility.getInstance() : new MenaceAbility(), Duration.EndOfTurn + ), source); } return true; } diff --git a/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java b/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java index 5b85ce922c9..ea49683557b 100644 --- a/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java +++ b/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java @@ -3,7 +3,6 @@ package mage.cards.y; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; import mage.abilities.keyword.FlyingAbility; @@ -70,21 +69,16 @@ class YusriFortunesFlameEffect extends OneShotEffect { return false; } int flips = player.getAmount(1, 5, "Choose a number between 1 and 5", source, game); - int wins = 0; - int losses = 0; - for (int i = 0; i < flips; i++) { - if (player.flipCoin(source, game, true)) { - wins++; - } else { - losses++; - } - } + int wins = player + .flipCoins(source, game, flips, true) + .stream() + .mapToInt(x -> x ? 1 : 0) + .sum(); + int losses = flips - wins; player.drawCards(wins, source, game); player.damage(2 * losses, source.getSourceId(), source, game); if (wins >= 5) { - ContinuousEffect effect = new CastFromHandWithoutPayingManaCostEffect(); - effect.setDuration(Duration.EndOfTurn); - game.addEffect(effect, source); + game.addEffect(new CastFromHandWithoutPayingManaCostEffect().setDuration(Duration.EndOfTurn), source); } return true; } diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index 54eaa1c4940..bcf73de44ac 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -168,6 +168,8 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Dragoon's Wyvern", 49, Rarity.COMMON, mage.cards.d.DragoonsWyvern.class)); cards.add(new SetCardInfo("Dreams of Laguna", 50, Rarity.COMMON, mage.cards.d.DreamsOfLaguna.class)); cards.add(new SetCardInfo("Dwarven Castle Guard", 18, Rarity.COMMON, mage.cards.d.DwarvenCastleGuard.class)); + cards.add(new SetCardInfo("Edgar, King of Figaro", 436, Rarity.RARE, mage.cards.e.EdgarKingOfFigaro.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar, King of Figaro", 51, Rarity.RARE, mage.cards.e.EdgarKingOfFigaro.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eject", 52, Rarity.UNCOMMON, mage.cards.e.Eject.class)); cards.add(new SetCardInfo("Elixir", 256, Rarity.UNCOMMON, mage.cards.e.Elixir.class)); cards.add(new SetCardInfo("Emet-Selch, Unsundered", 218, Rarity.MYTHIC, mage.cards.e.EmetSelchUnsundered.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java new file mode 100644 index 00000000000..3a03379f89e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java @@ -0,0 +1,77 @@ +package org.mage.test.cards.single.fin; + +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class EdgarKingOfFigaroTest extends CardTestPlayerBase { + + private static final String edgar = "Edgar, King of Figaro"; + private static final String traprunner = "Goblin Traprunner"; + private static final String swindler = "Tavern Swindler"; + + @Test + public void testTraprunnerThenSwindler() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, traprunner); + addCard(Zone.BATTLEFIELD, playerA, swindler); + + attack(1, playerA, traprunner); + + setFlipCoinResult(playerA, false); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Goblin Token", 3); + assertLife(playerA, 20 - 3); + assertLife(playerB, 20 - 4 - 1 - 1 - 1); + } + + @Test + public void testSwindlerThenTraprunner() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, traprunner); + addCard(Zone.BATTLEFIELD, playerA, swindler); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + + setFlipCoinResult(playerA, false); + setFlipCoinResult(playerA, false); + setFlipCoinResult(playerA, false); + attack(1, playerA, traprunner); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Goblin Token", 0); + assertLife(playerA, 20 - 3 + 6); + assertLife(playerB, 20 - 4); + } + + private static final String giant = "Two-Headed Giant"; + + @Test + public void testTwoHeadedGiant() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, giant); + + attack(1, playerA, giant); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTapped(giant, true); + assertAbility(playerA, giant, DoubleStrikeAbility.getInstance(), true); + assertLife(playerB, 20 - 4 - 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 51496520d7d..f07ab9cceb0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -3794,6 +3794,11 @@ public class TestPlayer implements Player { return computerPlayer.flipCoin(source, game, true); } + @Override + public List flipCoins(Ability source, Game game, int amount, boolean winnable) { + return computerPlayer.flipCoins(source, game, amount, winnable); + } + @Override public boolean flipCoinResult(Game game) { assertAliasSupportInChoices(false); diff --git a/Mage/src/main/java/mage/game/events/FlipCoinEvent.java b/Mage/src/main/java/mage/game/events/FlipCoinEvent.java index 493eb24e651..2194aef0d28 100644 --- a/Mage/src/main/java/mage/game/events/FlipCoinEvent.java +++ b/Mage/src/main/java/mage/game/events/FlipCoinEvent.java @@ -12,6 +12,7 @@ public class FlipCoinEvent extends GameEvent { private boolean result; private final boolean chosen; private final boolean winnable; + private boolean autoWin = false; private int flipCount = 1; public FlipCoinEvent(UUID playerId, Ability source, boolean result, boolean chosen, boolean winnable) { @@ -53,6 +54,14 @@ public class FlipCoinEvent extends GameEvent { this.flipCount = flipCount; } + public void setAutoWin(boolean autoWin) { + this.autoWin = autoWin; + } + + public boolean isAutoWin() { + return autoWin; + } + public CoinFlippedEvent createFlippedEvent() { return new CoinFlippedEvent(playerId, sourceId, flipCount, result, chosen, winnable); } diff --git a/Mage/src/main/java/mage/game/events/FlipCoinsEvent.java b/Mage/src/main/java/mage/game/events/FlipCoinsEvent.java new file mode 100644 index 00000000000..ac9a54199e3 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/FlipCoinsEvent.java @@ -0,0 +1,25 @@ +package mage.game.events; + +import mage.abilities.Ability; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class FlipCoinsEvent extends GameEvent { + + private boolean isHeadsAndWon = false; + + public FlipCoinsEvent(UUID playerId, int amount, Ability source) { + super(EventType.FLIP_COINS, playerId, source, playerId, amount, false); + } + + public void setHeadsAndWon(boolean headsAndWon) { + isHeadsAndWon = headsAndWon; + } + + public boolean isHeadsAndWon() { + return isHeadsAndWon; + } +} diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 547c8239fd1..9a12cc41e44 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -401,7 +401,7 @@ public class GameEvent implements Serializable { SURVEIL, SURVEILED, PROLIFERATE, PROLIFERATED, FATESEALED, - FLIP_COIN, COIN_FLIPPED, + FLIP_COIN, FLIP_COINS, COIN_FLIPPED, REPLACE_ROLLED_DIE, // for Clam-I-Am workaround only ROLL_DIE, DIE_ROLLED, ROLL_DICE, DICE_ROLLED, diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 87258a1ba2d..136e841bcb4 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -533,6 +533,8 @@ public interface Player extends MageItem, Copyable { boolean hasProtectionFrom(MageObject source, Game game); + List flipCoins(Ability source, Game game, int amount, boolean winnable); + boolean flipCoin(Ability source, Game game, boolean winnable); boolean flipCoinResult(Game game); @@ -748,11 +750,12 @@ public interface Player extends MageItem, Copyable { /** * Set the value for X in spells and abilities + * * @param isManaPay helper param for better AI logic */ int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay); - // TODO: rework to use pair's list of effect + ability instead string's map + // TODO: rework to use pair's list of effect + ability instead string's map int chooseReplacementEffect(Map effectsMap, Map objectsMap, Game game); TriggeredAbility chooseTriggeredAbility(List abilities, Game game); @@ -764,7 +767,6 @@ public interface Player extends MageItem, Copyable { void selectBlockers(Ability source, Game game, UUID defendingPlayerId); /** - * * @param source can be null for system actions like define damage */ int getAmount(int min, int max, String message, Ability source, Game game); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 26085e30bdc..0b693a17178 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -3054,44 +3054,66 @@ public abstract class PlayerImpl implements Player, Serializable { */ @Override public boolean flipCoin(Ability source, Game game, boolean winnable) { - boolean chosen = false; - if (winnable) { - chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game); - game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(chosen)); - } - boolean result = this.flipCoinResult(game); - FlipCoinEvent event = new FlipCoinEvent(playerId, source, result, chosen, winnable); - game.replaceEvent(event); - game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(event.getResult()) - + CardUtil.getSourceLogName(game, source)); - if (event.getFlipCount() > 1) { - boolean canChooseHeads = event.getResult(); - boolean canChooseTails = !event.getResult(); - for (int i = 1; i < event.getFlipCount(); i++) { - boolean tempFlip = this.flipCoinResult(game); - canChooseHeads = canChooseHeads || tempFlip; - canChooseTails = canChooseTails || !tempFlip; - game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(tempFlip)); + return flipCoins(source, game, 1, winnable).get(0); + } + + @Override + public List flipCoins(Ability source, Game game, int amount, boolean winnable) { + List results = new ArrayList<>(); + FlipCoinsEvent flipsEvent = new FlipCoinsEvent(this.getId(), amount, source); + game.replaceEvent(flipsEvent); + for (int i = 0; i < flipsEvent.getAmount(); i++) { + if (flipsEvent.isHeadsAndWon()) { + if (winnable) { + game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(true)); + } + game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(true) + CardUtil.getSourceLogName(game, source)); + if (winnable) { + game.informPlayers(getLogName() + " won the flip" + CardUtil.getSourceLogName(game, source)); + } + game.fireEvent(new FlipCoinEvent(playerId, source, true, true, winnable).createFlippedEvent()); + results.add(true); + continue; } - if (canChooseHeads && canChooseTails) { - event.setResult(chooseUse(Outcome.Benefit, "Choose which flip to keep", - (event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null), - "Heads", "Tails", source, game - )); + boolean chosen; + if (winnable) { + chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game); + game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(chosen)); } else { - event.setResult(canChooseHeads); + chosen = false; } - game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult())); - } - if (event.isWinnable()) { - game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip" + boolean result = this.flipCoinResult(game); + FlipCoinEvent event = new FlipCoinEvent(playerId, source, result, chosen, winnable); + game.replaceEvent(event); + game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(event.getResult()) + CardUtil.getSourceLogName(game, source)); + if (event.getFlipCount() > 1) { + boolean canChooseHeads = event.getResult(); + boolean canChooseTails = !event.getResult(); + for (int j = 1; j < event.getFlipCount(); j++) { + boolean tempFlip = this.flipCoinResult(game); + 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 { + event.setResult(canChooseHeads); + } + game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult())); + } + if (event.isWinnable()) { + game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip" + + CardUtil.getSourceLogName(game, source)); + } + game.fireEvent(event.createFlippedEvent()); + results.add(event.isWinnable() ? event.getResult() == event.getChosen() : event.getResult()); } - game.fireEvent(event.createFlippedEvent()); - if (event.isWinnable()) { - return event.getResult() == event.getChosen(); - } - return event.getResult(); + return results; } /**