mirror of
https://github.com/correl/mage.git
synced 2024-12-29 19:15:06 +00:00
Improving implementation of cards which use voting (WIP) (#7566)
* created interface for handling voting * created class for two choice votes, refactored a card to use it * refactored all cards which use two choice votes * updated VoteHandler to an abstract class to encapsulate more of its functions * refactored cards which vote for more than two things * [CNS] Implemented Brago's Representative * [CN2] Implemented Ballot Broker * [CN2] Implemented Illusion of Choice * [CNS] Implemented Grudge Keeper * added vote outcomes * updated implementation of Illusion of Choice to work correctly in multiples * added test for voting * updated implementation of extra votes * simplified vote message handling * Improved names, additional comments * Votes: fixed not working getMostVoted * Votes: added final vote results to game logs; * Votes: added additional info for the vote choices; * Votes: added vote step info in choose dialogs, added AI support example for Tyrant's Choice; Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
parent
991f154cd7
commit
1cbbcddcc6
32 changed files with 1615 additions and 786 deletions
77
Mage.Sets/src/mage/cards/b/BallotBroker.java
Normal file
77
Mage.Sets/src/mage/cards/b/BallotBroker.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
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.game.events.GameEvent;
|
||||
import mage.game.events.VoteEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BallotBroker extends CardImpl {
|
||||
|
||||
public BallotBroker(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.ADVISOR);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// While voting, you may vote an additional time.
|
||||
this.addAbility(new SimpleStaticAbility(new BallotBrokerReplacementEffect()));
|
||||
}
|
||||
|
||||
private BallotBroker(final BallotBroker card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BallotBroker copy() {
|
||||
return new BallotBroker(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BallotBrokerReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
BallotBrokerReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
staticText = "while voting, you may vote an additional time";
|
||||
}
|
||||
|
||||
private BallotBrokerReplacementEffect(final BallotBrokerReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BallotBrokerReplacementEffect copy() {
|
||||
return new BallotBrokerReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.VOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return source.isControlledBy(event.getTargetId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((VoteEvent) event).incrementOptionalExtraVotes();
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostOpponentsEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
|
@ -17,14 +15,15 @@ import mage.constants.TargetController;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class BiteOfTheBlackRose extends CardImpl {
|
||||
|
||||
public BiteOfTheBlackRose(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}");
|
||||
|
||||
// Will of the council - Starting with you, each player votes for sickness or psychosis. If sickness gets more votes, creatures your opponents control get -2/-2 until end of turn. If psychosis gets more votes or the vote is tied, each opponent discards two cards.
|
||||
this.getSpellAbility().addEffect(new BiteOfTheBlackRoseEffect());
|
||||
|
@ -44,10 +43,13 @@ class BiteOfTheBlackRoseEffect extends OneShotEffect {
|
|||
|
||||
BiteOfTheBlackRoseEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, each player votes for sickness or psychosis. If sickness gets more votes, creatures your opponents control get -2/-2 until end of turn. If psychosis gets more votes or the vote is tied, each opponent discards two cards";
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, " +
|
||||
"each player votes for sickness or psychosis. If sickness gets more votes, " +
|
||||
"creatures your opponents control get -2/-2 until end of turn. " +
|
||||
"If psychosis gets more votes or the vote is tied, each opponent discards two cards";
|
||||
}
|
||||
|
||||
BiteOfTheBlackRoseEffect(final BiteOfTheBlackRoseEffect effect) {
|
||||
private BiteOfTheBlackRoseEffect(final BiteOfTheBlackRoseEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -59,29 +61,24 @@ class BiteOfTheBlackRoseEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int sicknessCount = 0;
|
||||
int psychosisCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.ExtraTurn, "Choose sickness?", source, game)) {
|
||||
sicknessCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for sickness");
|
||||
} else {
|
||||
psychosisCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for psychosis");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sicknessCount > psychosisCount) {
|
||||
ContinuousEffect effect = new BoostOpponentsEffect(-2, -2, Duration.EndOfTurn);
|
||||
game.addEffect(effect, source);
|
||||
} else {
|
||||
new DiscardEachPlayerEffect(StaticValue.get(2), false, TargetController.OPPONENT).apply(game, source);
|
||||
}
|
||||
return true;
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Outcome.Detriment - AI will discard a card all the time (Psychosis choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Sickness (-2/-2)", "Psychosis (discard cards)", Outcome.Detriment);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int sicknessCount = vote.getVoteCount(true);
|
||||
int psychosisCount = vote.getVoteCount(false);
|
||||
if (sicknessCount > psychosisCount) {
|
||||
// sickness
|
||||
game.addEffect(new BoostOpponentsEffect(-2, -2, Duration.EndOfTurn), source);
|
||||
} else {
|
||||
// psychosis or tied
|
||||
new DiscardEachPlayerEffect(StaticValue.get(2), false, TargetController.OPPONENT).apply(game, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
77
Mage.Sets/src/mage/cards/b/BragosRepresentative.java
Normal file
77
Mage.Sets/src/mage/cards/b/BragosRepresentative.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
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.game.events.GameEvent;
|
||||
import mage.game.events.VoteEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BragosRepresentative extends CardImpl {
|
||||
|
||||
public BragosRepresentative(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.ADVISOR);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// While voting, you get an additional vote.
|
||||
this.addAbility(new SimpleStaticAbility(new BragosRepresentativeReplacementEffect()));
|
||||
}
|
||||
|
||||
private BragosRepresentative(final BragosRepresentative card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BragosRepresentative copy() {
|
||||
return new BragosRepresentative(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BragosRepresentativeReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
BragosRepresentativeReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
staticText = "while voting, you get an additional vote";
|
||||
}
|
||||
|
||||
private BragosRepresentativeReplacementEffect(final BragosRepresentativeReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BragosRepresentativeReplacementEffect copy() {
|
||||
return new BragosRepresentativeReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.VOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return source.isControlledBy(event.getTargetId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((VoteEvent) event).incrementExtraVotes();
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,24 +1,23 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CouncilsDilemmaVoteEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.SacrificeOpponentsEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JRHerlehy
|
||||
* @author JRHerlehy, TheElk801
|
||||
*/
|
||||
public final class CapitalPunishment extends CardImpl {
|
||||
|
||||
|
@ -26,7 +25,7 @@ public final class CapitalPunishment extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}");
|
||||
|
||||
// <i>Council's dilemma</i> — Starting with you, each player votes for death or taxes. Each opponent sacrifices a creature for each death vote and discards a card for each taxes vote.
|
||||
this.getSpellAbility().addEffect(new CapitalPunishmentDilemmaEffect());
|
||||
this.getSpellAbility().addEffect(new CapitalPunishmentEffect());
|
||||
}
|
||||
|
||||
private CapitalPunishment(final CapitalPunishment card) {
|
||||
|
@ -39,45 +38,42 @@ public final class CapitalPunishment extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class CapitalPunishmentDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||
class CapitalPunishmentEffect extends OneShotEffect {
|
||||
|
||||
public CapitalPunishmentDilemmaEffect() {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for death or taxes. Each opponent sacrifices a creature for each death vote and discards a card for each taxes vote";
|
||||
CapitalPunishmentEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for death or taxes. " +
|
||||
"Each opponent sacrifices a creature for each death vote and discards a card for each taxes vote";
|
||||
}
|
||||
|
||||
public CapitalPunishmentDilemmaEffect(final CapitalPunishmentDilemmaEffect effect) {
|
||||
private CapitalPunishmentEffect(final CapitalPunishmentEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
//If no controller, exit out here and do not vote.
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.vote("death", "taxes", controller, game, source);
|
||||
|
||||
//Death Votes
|
||||
if (voteOneCount > 0) {
|
||||
Effect sacrificeEffect = new SacrificeOpponentsEffect(voteOneCount, StaticFilters.FILTER_CONTROLLED_CREATURE);
|
||||
sacrificeEffect.apply(game, source);
|
||||
}
|
||||
|
||||
//Taxes Votes
|
||||
if (voteTwoCount > 0) {
|
||||
Effect discardEffect = new DiscardEachPlayerEffect(StaticValue.get(voteTwoCount), false, TargetController.OPPONENT);
|
||||
discardEffect.apply(game, source);
|
||||
}
|
||||
|
||||
return true;
|
||||
public CapitalPunishmentEffect copy() {
|
||||
return new CapitalPunishmentEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CapitalPunishmentDilemmaEffect copy() {
|
||||
return new CapitalPunishmentDilemmaEffect(this);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// Outcome.Detriment - AI will discard a card all the time (taxes choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Death (sacrifice creature)", "Taxes (discard card)", Outcome.Detriment);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int deathCount = vote.getVoteCount(true);
|
||||
int taxesCount = vote.getVoteCount(false);
|
||||
if (deathCount > 0) {
|
||||
new SacrificeOpponentsEffect(
|
||||
deathCount, StaticFilters.FILTER_CONTROLLED_CREATURE
|
||||
).apply(game, source);
|
||||
}
|
||||
if (taxesCount > 0) {
|
||||
new DiscardEachPlayerEffect(
|
||||
StaticValue.get(taxesCount), false, TargetController.OPPONENT
|
||||
).apply(game, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,39 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DestroyAllEffect;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
* @author fireshoes, TheElk801
|
||||
*/
|
||||
public final class CoercivePortal extends CardImpl {
|
||||
|
||||
public CoercivePortal(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
|
||||
|
||||
// Will of the council - At the beginning of your upkeep, starting with you, each player votes for carnage or homage. If carnage gets more votes, sacrifice Coercive Portal and destroy all nonland permanents. If homage gets more votes or the vote is tied, draw a card.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CoercivePortalEffect(), TargetController.YOU, false));
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
|
||||
Zone.BATTLEFIELD, new CoercivePortalEffect(), TargetController.YOU,
|
||||
false, false, "<i>Will of the council</i> — " +
|
||||
"At the beginning of your upkeep, starting with you, each player votes for carnage or homage. " +
|
||||
"If carnage gets more votes, sacrifice {this} and destroy all nonland permanents. " +
|
||||
"If homage gets more votes or the vote is tied, draw a card"
|
||||
));
|
||||
}
|
||||
|
||||
private CoercivePortal(final CoercivePortal card) {
|
||||
|
@ -43,10 +50,9 @@ class CoercivePortalEffect extends OneShotEffect {
|
|||
|
||||
CoercivePortalEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — At the beginning of your upkeep, starting with you, each player votes for carnage or homage. If carnage gets more votes, sacrifice Coercive Portal and destroy all nonland permanents. If homage gets more votes or the vote is tied, draw a card";
|
||||
}
|
||||
|
||||
CoercivePortalEffect(final CoercivePortalEffect effect) {
|
||||
private CoercivePortalEffect(final CoercivePortalEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -57,30 +63,27 @@ class CoercivePortalEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int carnageCount = 0;
|
||||
int homageCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.DestroyPermanent, "Choose carnage?", source, game)) {
|
||||
carnageCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for carnage");
|
||||
} else {
|
||||
homageCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for homage");
|
||||
}
|
||||
}
|
||||
// Outcome.Detriment - AI will draw a card all the time (Homage choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Carnage (sacrifice and destroy)", "Homage (draw a card)", Outcome.Detriment);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int carnageCount = vote.getVoteCount(true);
|
||||
int homageCount = vote.getVoteCount(false);
|
||||
if (carnageCount > homageCount) {
|
||||
// carnage
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent != null && permanent.isControlledBy(source.getControllerId())) {
|
||||
permanent.sacrifice(source, game);
|
||||
}
|
||||
if (carnageCount > homageCount) {
|
||||
new SacrificeSourceEffect().apply(game, source);
|
||||
new DestroyAllEffect(new FilterNonlandPermanent()).apply(game, source);
|
||||
} else {
|
||||
controller.drawCards(1, source, game);
|
||||
new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_NON_LAND).apply(game, source);
|
||||
} else {
|
||||
// homage or tied
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
player.drawCards(1, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
@ -10,6 +9,7 @@ import mage.abilities.keyword.ProtectionAbility;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.choices.VoteHandler;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
|
@ -17,12 +17,13 @@ import mage.constants.SubType;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Styxo
|
||||
* @author Styxo, TheElk801
|
||||
*/
|
||||
public final class CouncilGuardian extends CardImpl {
|
||||
|
||||
|
@ -35,8 +36,9 @@ public final class CouncilGuardian extends CardImpl {
|
|||
this.toughness = new MageInt(5);
|
||||
|
||||
// Will of the council - When Council Guardian enters the battlefield, starting with you, each player votes for blue, black, red, or green. Council Guardian gains protection from each color with the most votes or tied for most votes.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new CouncilsGuardianEffect(), false, "<i>Will of the council</i> — "));
|
||||
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new CouncilsGuardianEffect(), false, "<i>Will of the council</i> — "
|
||||
));
|
||||
}
|
||||
|
||||
private CouncilGuardian(final CouncilGuardian card) {
|
||||
|
@ -51,12 +53,13 @@ public final class CouncilGuardian extends CardImpl {
|
|||
|
||||
class CouncilsGuardianEffect extends OneShotEffect {
|
||||
|
||||
public CouncilsGuardianEffect() {
|
||||
CouncilsGuardianEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "starting with you, each player votes for blue, black, red, or green. {this} gains protection from each color with the most votes or tied for most votes";
|
||||
this.staticText = "starting with you, each player votes for blue, black, red, or green. " +
|
||||
"{this} gains protection from each color with the most votes or tied for most votes";
|
||||
}
|
||||
|
||||
public CouncilsGuardianEffect(final CouncilsGuardianEffect effect) {
|
||||
private CouncilsGuardianEffect(final CouncilsGuardianEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -67,45 +70,39 @@ class CouncilsGuardianEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
ChoiceColor choice = new ChoiceColor();
|
||||
choice.getChoices().remove("White");
|
||||
if (controller != null) {
|
||||
Map<ObjectColor, Integer> chosenColors = new HashMap<>(2);
|
||||
int maxCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
choice.clearChoice();
|
||||
if (player.choose(Outcome.Detriment, choice, game)) {
|
||||
ObjectColor color = choice.getColor();
|
||||
if (color != null) {
|
||||
if (chosenColors.containsKey(color)) {
|
||||
int count = chosenColors.get(color) + 1;
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
}
|
||||
chosenColors.put(color, count);
|
||||
} else {
|
||||
if (maxCount == 0) {
|
||||
maxCount = 1;
|
||||
}
|
||||
chosenColors.put(color, 1);
|
||||
}
|
||||
game.informPlayers(player.getLogName() + " has chosen " + color.getDescription() + '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CouncilGuardianVote vote = new CouncilGuardianVote();
|
||||
vote.doVotes(source, game);
|
||||
|
||||
for (Map.Entry<ObjectColor, Integer> entry : chosenColors.entrySet()) {
|
||||
if (entry.getValue() == maxCount) {
|
||||
ObjectColor color = entry.getKey();
|
||||
game.addEffect(new GainAbilitySourceEffect(ProtectionAbility.from(color), Duration.Custom), source);
|
||||
}
|
||||
for (String color : vote.getMostVoted()) {
|
||||
if (color == null) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
game.addEffect(new GainAbilitySourceEffect(
|
||||
ProtectionAbility.from(ChoiceColor.getColorFromString(color)), Duration.Custom
|
||||
), source);
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class CouncilGuardianVote extends VoteHandler<String> {
|
||||
|
||||
@Override
|
||||
protected Set<String> getPossibleVotes(Ability source, Game game) {
|
||||
return new LinkedHashSet<>(Arrays.asList("Blue", "Black", "Red", "Green"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) {
|
||||
ChoiceColor choice = new ChoiceColor();
|
||||
choice.getChoices().remove("White");
|
||||
choice.setSubMessage(voteInfo);
|
||||
decidingPlayer.choose(Outcome.AIDontUseIt, choice, game); // TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
return choice.getChoice();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String voteName(String vote) {
|
||||
return vote;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.choices.VoteHandler;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
|
@ -18,11 +18,14 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
* @author emerald000, TheElk801
|
||||
*/
|
||||
public final class CouncilsJudgment extends CardImpl {
|
||||
|
||||
|
@ -47,10 +50,11 @@ class CouncilsJudgmentEffect extends OneShotEffect {
|
|||
|
||||
CouncilsJudgmentEffect() {
|
||||
super(Outcome.Exile);
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, each player votes for a nonland permanent you don't control. Exile each permanent with the most votes or tied for most votes";
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, each player votes for a " +
|
||||
"nonland permanent you don't control. Exile each permanent with the most votes or tied for most votes";
|
||||
}
|
||||
|
||||
CouncilsJudgmentEffect(final CouncilsJudgmentEffect effect) {
|
||||
private CouncilsJudgmentEffect(final CouncilsJudgmentEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -61,48 +65,48 @@ class CouncilsJudgmentEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Map<Permanent, Integer> chosenCards = new HashMap<>(2);
|
||||
int maxCount = 0;
|
||||
FilterNonlandPermanent filter = new FilterNonlandPermanent("a nonland permanent " + controller.getLogName() + " doesn't control");
|
||||
filter.add(Predicates.not(new ControllerIdPredicate(controller.getId())));
|
||||
//Players each choose a legal permanent
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
Target target = new TargetNonlandPermanent(filter);
|
||||
target.setNotTarget(true);
|
||||
if (player.choose(Outcome.Exile, target, source.getSourceId(), game)) {
|
||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||
if (permanent != null) {
|
||||
if (chosenCards.containsKey(permanent)) {
|
||||
int count = chosenCards.get(permanent) + 1;
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
}
|
||||
chosenCards.put(permanent, count);
|
||||
} else {
|
||||
if (maxCount == 0) {
|
||||
maxCount = 1;
|
||||
}
|
||||
chosenCards.put(permanent, 1);
|
||||
}
|
||||
game.informPlayers(player.getLogName() + " has chosen: " + permanent.getLogName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Exile the card(s) with the most votes.
|
||||
for (Entry<Permanent, Integer> entry : chosenCards.entrySet()) {
|
||||
if (entry.getValue() == maxCount) {
|
||||
Permanent permanent = entry.getKey();
|
||||
controller.moveCardToExileWithInfo(permanent, null, "", source, game, Zone.BATTLEFIELD, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
Player player = game.getPlayer(source.getSourceId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
CouncilsJudgmentVote vote = new CouncilsJudgmentVote(player);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
Cards cards = new CardsImpl();
|
||||
vote.getMostVoted().stream().forEach(cards::add);
|
||||
return player.moveCards(cards, Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
class CouncilsJudgmentVote extends VoteHandler<Permanent> {
|
||||
|
||||
private final FilterPermanent filter;
|
||||
|
||||
CouncilsJudgmentVote(Player controller) {
|
||||
this.filter = new FilterNonlandPermanent("nonland permanent not controlled by " + controller.getName());
|
||||
this.filter.add(Predicates.not(new ControllerIdPredicate(controller.getId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Permanent> getPossibleVotes(Ability source, Game game) {
|
||||
// too much permanentns on battlefield, so no need to show full list here
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permanent playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) {
|
||||
if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) < 1) {
|
||||
return null;
|
||||
}
|
||||
TargetPermanent target = new TargetPermanent(1, filter);
|
||||
target.withChooseHint(voteInfo + " (to exile)");
|
||||
target.setNotTarget(true);
|
||||
decidingPlayer.choose(Outcome.Exile, target, source.getSourceId(), game);
|
||||
return game.getPermanent(target.getFirstTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String voteName(Permanent vote) {
|
||||
return vote.getIdName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import mage.MageInt;
|
||||
|
@ -6,29 +5,31 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.*;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.VoteHandler;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, TheElk801
|
||||
*/
|
||||
public final class CustodiSquire extends CardImpl {
|
||||
|
||||
public CustodiSquire(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
this.subtype.add(SubType.CLERIC);
|
||||
this.power = new MageInt(3);
|
||||
|
@ -37,7 +38,9 @@ public final class CustodiSquire extends CardImpl {
|
|||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
// Will of the council - When Custodi Squire enters the battlefield, starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. Return each card with the most votes or tied for most votes to your hand.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new CustodiSquireVoteEffect(), false, true));
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new CustodiSquireVoteEffect(), false, true
|
||||
));
|
||||
}
|
||||
|
||||
private CustodiSquire(final CustodiSquire card) {
|
||||
|
@ -52,20 +55,14 @@ public final class CustodiSquire extends CardImpl {
|
|||
|
||||
class CustodiSquireVoteEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("artifact, creature, or enchantment card from your graveyard");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(),
|
||||
CardType.CREATURE.getPredicate(),
|
||||
CardType.ENCHANTMENT.getPredicate()));
|
||||
}
|
||||
|
||||
CustodiSquireVoteEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — When {this} enters the battlefield, starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. Return each card with the most votes or tied for most votes to your hand";
|
||||
this.staticText = "<i>Will of the council</i> — When {this} enters the battlefield, " +
|
||||
"starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. " +
|
||||
"Return each card with the most votes or tied for most votes to your hand";
|
||||
}
|
||||
|
||||
CustodiSquireVoteEffect(final CustodiSquireVoteEffect effect) {
|
||||
private CustodiSquireVoteEffect(final CustodiSquireVoteEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -76,47 +73,52 @@ class CustodiSquireVoteEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Cards possibleCards = new CardsImpl();
|
||||
possibleCards.addAll(controller.getGraveyard().getCards(filter, game));
|
||||
if (!possibleCards.isEmpty()) {
|
||||
Map<UUID, Integer> cardCounter = new HashMap<>();
|
||||
TargetCard target = new TargetCard(1, 1, Zone.GRAVEYARD, filter);
|
||||
int maxCount = 1;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
target.clearChosen();
|
||||
player.chooseTarget(outcome, possibleCards, target, source, game);
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
if (card != null) {
|
||||
game.informPlayers(player.getLogName() + " voted for " + card.getLogName());
|
||||
if (!cardCounter.containsKey(target.getFirstTarget())) {
|
||||
cardCounter.put(target.getFirstTarget(), 1);
|
||||
} else {
|
||||
int count = cardCounter.get(target.getFirstTarget()) + 1;
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
}
|
||||
cardCounter.put(target.getFirstTarget(), count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Cards cardsToMove = new CardsImpl();
|
||||
for (UUID uuid : possibleCards) {
|
||||
if (cardCounter.containsKey(uuid)) {
|
||||
if (cardCounter.get(uuid) == maxCount) {
|
||||
cardsToMove.add(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.moveCards(cardsToMove, Zone.HAND, source, game);
|
||||
}
|
||||
return true;
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
CustodiSquireVote vote = new CustodiSquireVote();
|
||||
vote.doVotes(source, game);
|
||||
|
||||
return player.moveCards(vote.getMostVoted(), Zone.HAND, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
class CustodiSquireVote extends VoteHandler<Card> {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("artifact, creature, or enchantment card");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.or(
|
||||
CardType.ARTIFACT.getPredicate(),
|
||||
CardType.CREATURE.getPredicate(),
|
||||
CardType.ENCHANTMENT.getPredicate()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Card> getPossibleVotes(Ability source, Game game) {
|
||||
// too much permanentns on battlefield, so no need to show full list here
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null || controller.getGraveyard().count(
|
||||
filter, source.getSourceId(), source.getControllerId(), game
|
||||
) < 1) {
|
||||
return null;
|
||||
}
|
||||
TargetCardInGraveyard target = new TargetCardInGraveyard(filter);
|
||||
target.withChooseHint(voteInfo + " (from graveyard to hand)");
|
||||
target.setNotTarget(true);
|
||||
decidingPlayer.choose(Outcome.ReturnToHand, controller.getGraveyard(), target, game);
|
||||
return controller.getGraveyard().get(target.getFirstTarget(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String voteName(Card vote) {
|
||||
return vote.getIdName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
package mage.cards.e;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.common.CouncilsDilemmaVoteEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileSpellEffect;
|
||||
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.card.OwnerIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.turn.TurnMod;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JRHerlehy
|
||||
* @author JRHerlehy, TheElk801
|
||||
*/
|
||||
public final class Expropriate extends CardImpl {
|
||||
|
||||
|
@ -31,7 +34,7 @@ public final class Expropriate extends CardImpl {
|
|||
|
||||
// <i>Council's dilemma</i> — Starting with you, each player votes for time or money. For each time vote,
|
||||
// take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it. Exile Expropriate
|
||||
this.getSpellAbility().addEffect(new ExpropriateDilemmaEffect());
|
||||
this.getSpellAbility().addEffect(new ExpropriateEffect());
|
||||
this.getSpellAbility().addEffect(ExileSpellEffect.getInstance());
|
||||
}
|
||||
|
||||
|
@ -45,138 +48,69 @@ public final class Expropriate extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class ExpropriateDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||
class ExpropriateEffect extends OneShotEffect {
|
||||
|
||||
private ArrayList<UUID> choiceTwoVoters = new ArrayList<>();
|
||||
|
||||
public ExpropriateDilemmaEffect() {
|
||||
ExpropriateEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it";
|
||||
staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for time or money. " +
|
||||
"For each time vote, take an extra turn after this one. For each money vote, " +
|
||||
"choose a permanent owned by the voter and gain control of it";
|
||||
}
|
||||
|
||||
public ExpropriateDilemmaEffect(final ExpropriateDilemmaEffect effect) {
|
||||
private ExpropriateEffect(final ExpropriateEffect effect) {
|
||||
super(effect);
|
||||
this.choiceTwoVoters.addAll(effect.choiceTwoVoters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpropriateEffect copy() {
|
||||
return new ExpropriateEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
//If not controller, exit out here and do not vote.
|
||||
if (controller == null) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.vote("time", "money", controller, game, source);
|
||||
// Outcome.Detriment - AI will gain control all the time (Money choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Time (extra turn)", "Money (gain control)", Outcome.Detriment);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
//Time Votes
|
||||
if (voteOneCount > 0) {
|
||||
this.turnsForTimeVote(voteOneCount, controller, game, source);
|
||||
}
|
||||
|
||||
//Money Votes
|
||||
if (voteTwoCount > 0) {
|
||||
this.controlForMoneyVote(controller, game, source);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void turnsForTimeVote(int timeCount, Player controller, Game game, Ability source) {
|
||||
if (timeCount == 1) {
|
||||
game.informPlayers(controller.getLogName() + " will take an extra turn");
|
||||
} else {
|
||||
game.informPlayers(controller.getLogName() + " will take " + timeCount + " extra turns");
|
||||
}
|
||||
do {
|
||||
// extra turn
|
||||
int timeCount = vote.getVoteCount(true);
|
||||
for (int i = 0; i < timeCount; i++) {
|
||||
game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), false));
|
||||
timeCount--;
|
||||
} while (timeCount > 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void controlForMoneyVote(Player controller, Game game, Ability source) {
|
||||
List<Permanent> chosenCards = new ArrayList<>();
|
||||
|
||||
for (UUID playerId : choiceTwoVoters) {
|
||||
FilterPermanent filter = new FilterPermanent("permanent owned by " + game.getPlayer(playerId).getName());
|
||||
filter.add(new OwnerIdPredicate(playerId));
|
||||
|
||||
Target target = new TargetPermanent(filter);
|
||||
// gain control
|
||||
if (vote.getVoteCount(false) < 1) {
|
||||
return true;
|
||||
}
|
||||
List<Permanent> toSteal = new ArrayList<>();
|
||||
for (UUID playerId : vote.getVotedFor(false)) {
|
||||
int moneyCount = vote.getVotes(playerId).stream().mapToInt(x -> x ? 0 : 1).sum();
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
filter.add(new ControllerIdPredicate(playerId));
|
||||
moneyCount = Math.min(game.getBattlefield().count(
|
||||
filter, source.getSourceId(), source.getControllerId(), game
|
||||
), moneyCount);
|
||||
if (moneyCount == 0) {
|
||||
continue;
|
||||
}
|
||||
TargetPermanent target = new TargetPermanent(moneyCount, filter);
|
||||
target.setNotTarget(true);
|
||||
|
||||
if (controller != null
|
||||
&& controller.chooseTarget(Outcome.GainControl, target, source, game)) {
|
||||
Permanent targetPermanent = game.getPermanent(target.getFirstTarget());
|
||||
|
||||
if (targetPermanent != null) {
|
||||
chosenCards.add(targetPermanent);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (controller != null) {
|
||||
for (Permanent permanent : chosenCards) {
|
||||
ContinuousEffect effect = new ExpropriateControlEffect(controller.getId());
|
||||
effect.setTargetPointer(new FixedTarget(permanent, game));
|
||||
game.addEffect(effect, source);
|
||||
game.informPlayers(controller.getLogName() + " gained control of " + permanent.getIdName() + " owned by " + game.getPlayer(permanent.getOwnerId()).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void vote(String choiceOne, String choiceTwo, Player controller, Game game, Ability source) {
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.Vote,
|
||||
"Choose " + choiceOne + " or " + choiceTwo + "?",
|
||||
source.getRule(), choiceOne, choiceTwo, source, game)) {
|
||||
voteOneCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for " + choiceOne);
|
||||
} else {
|
||||
voteTwoCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for " + choiceTwo);
|
||||
choiceTwoVoters.add(player.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpropriateDilemmaEffect copy() {
|
||||
return new ExpropriateDilemmaEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ExpropriateControlEffect extends ContinuousEffectImpl {
|
||||
|
||||
private final UUID controllerId;
|
||||
|
||||
public ExpropriateControlEffect(UUID controllerId) {
|
||||
super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl);
|
||||
this.controllerId = controllerId;
|
||||
}
|
||||
|
||||
public ExpropriateControlEffect(final ExpropriateControlEffect effect) {
|
||||
super(effect);
|
||||
this.controllerId = effect.controllerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpropriateControlEffect copy() {
|
||||
return new ExpropriateControlEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
if (permanent == null || controllerId == null) {
|
||||
this.discard();
|
||||
} else {
|
||||
permanent.changeControllerId(controllerId, game, source);
|
||||
player.choose(Outcome.GainControl, target, source.getSourceId(), game);
|
||||
target.getTargets()
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(toSteal::add);
|
||||
}
|
||||
game.addEffect(new GainControlTargetEffect(
|
||||
Duration.Custom, true, source.getControllerId()
|
||||
).setTargetPointer(new FixedTargets(toSteal, game)), source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
112
Mage.Sets/src/mage/cards/g/GrudgeKeeper.java
Normal file
112
Mage.Sets/src/mage/cards/g/GrudgeKeeper.java
Normal file
|
@ -0,0 +1,112 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.VotedEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class GrudgeKeeper extends CardImpl {
|
||||
|
||||
public GrudgeKeeper(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
|
||||
|
||||
this.subtype.add(SubType.ZOMBIE);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Whenever players finish voting, each opponent who voted for a choice you didn't vote for loses 2 life.
|
||||
this.addAbility(new GrudgeKeeperTriggeredAbility());
|
||||
}
|
||||
|
||||
private GrudgeKeeper(final GrudgeKeeper card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrudgeKeeper copy() {
|
||||
return new GrudgeKeeper(this);
|
||||
}
|
||||
}
|
||||
|
||||
class GrudgeKeeperTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
GrudgeKeeperTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, null, false);
|
||||
}
|
||||
|
||||
private GrudgeKeeperTriggeredAbility(final GrudgeKeeperTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.VOTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
VotedEvent votedEvent = (VotedEvent) event;
|
||||
this.getEffects().clear();
|
||||
this.addEffect(new GrudgeKeeperEffect(votedEvent.getDidntVote(getControllerId())));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrudgeKeeperTriggeredAbility copy() {
|
||||
return new GrudgeKeeperTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever players finish voting, each opponent who voted for a choice you didn't vote for loses 2 life.";
|
||||
}
|
||||
}
|
||||
|
||||
class GrudgeKeeperEffect extends OneShotEffect {
|
||||
|
||||
private final Set<UUID> playerIds = new HashSet<>();
|
||||
|
||||
GrudgeKeeperEffect(Set<UUID> playerIds) {
|
||||
super(Outcome.Benefit);
|
||||
this.playerIds.addAll(playerIds);
|
||||
}
|
||||
|
||||
private GrudgeKeeperEffect(final GrudgeKeeperEffect effect) {
|
||||
super(effect);
|
||||
this.playerIds.addAll(effect.playerIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrudgeKeeperEffect copy() {
|
||||
return new GrudgeKeeperEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID playerId : playerIds) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null && player.hasOpponent(source.getControllerId(), game)) {
|
||||
player.loseLife(2, game, source, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
67
Mage.Sets/src/mage/cards/i/IllusionOfChoice.java
Normal file
67
Mage.Sets/src/mage/cards/i/IllusionOfChoice.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
package mage.cards.i;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class IllusionOfChoice extends CardImpl {
|
||||
|
||||
public IllusionOfChoice(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
|
||||
|
||||
// You choose how each player votes this turn.
|
||||
this.getSpellAbility().addEffect(new IllusionOfChoiceReplacementEffect());
|
||||
|
||||
// Draw a card.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
|
||||
}
|
||||
|
||||
private IllusionOfChoice(final IllusionOfChoice card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IllusionOfChoice copy() {
|
||||
return new IllusionOfChoice(this);
|
||||
}
|
||||
}
|
||||
|
||||
class IllusionOfChoiceReplacementEffect extends ContinuousRuleModifyingEffectImpl {
|
||||
|
||||
IllusionOfChoiceReplacementEffect() {
|
||||
super(Duration.EndOfTurn, Outcome.Benefit);
|
||||
staticText = "you choose how each player votes this turn";
|
||||
}
|
||||
|
||||
private IllusionOfChoiceReplacementEffect(final IllusionOfChoiceReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IllusionOfChoiceReplacementEffect copy() {
|
||||
return new IllusionOfChoiceReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.VOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
event.setPlayerId(source.getControllerId());
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
package mage.cards.l;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CouncilsDilemmaVoteEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
|
@ -16,11 +14,11 @@ import mage.counters.CounterType;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.SoldierToken;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JRHerlehy
|
||||
* @author JRHerlehy, TheElk801
|
||||
*/
|
||||
public final class LieutenantsOfTheGuard extends CardImpl {
|
||||
|
||||
|
@ -35,7 +33,9 @@ public final class LieutenantsOfTheGuard extends CardImpl {
|
|||
// <i>Council's dilemma</i> — When Lieutenants of the Guard enters the battlefield, starting with you,
|
||||
// each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard for each
|
||||
// strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new LieutenantsOfTheGuardDilemmaEffect(), false, "<i>Council's dilemma</i> — "));
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new LieutenantsOfTheGuardEffect(), false, "<i>Council's dilemma</i> — "
|
||||
));
|
||||
}
|
||||
|
||||
private LieutenantsOfTheGuard(final LieutenantsOfTheGuard card) {
|
||||
|
@ -48,47 +48,40 @@ public final class LieutenantsOfTheGuard extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||
class LieutenantsOfTheGuardEffect extends OneShotEffect {
|
||||
|
||||
public LieutenantsOfTheGuardDilemmaEffect() {
|
||||
LieutenantsOfTheGuardEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "starting with you, each player votes for strength or numbers. Put a +1/+1 counter on {this} for each strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote.";
|
||||
this.staticText = "starting with you, each player votes for strength or numbers. " +
|
||||
"Put a +1/+1 counter on {this} for each strength vote " +
|
||||
"and create a 1/1 white Soldier creature token for each numbers vote.";
|
||||
}
|
||||
|
||||
public LieutenantsOfTheGuardDilemmaEffect(final LieutenantsOfTheGuardDilemmaEffect effect) {
|
||||
private LieutenantsOfTheGuardEffect(final LieutenantsOfTheGuardEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
//If no controller, exit out here and do not vote.
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.vote("strength", "numbers", controller, game, source);
|
||||
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
|
||||
//Strength Votes
|
||||
//If strength received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counters on it.
|
||||
if (voteOneCount > 0 && permanent != null) {
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount), source.getControllerId(), source, game);
|
||||
}
|
||||
|
||||
//Numbers Votes
|
||||
if (voteTwoCount > 0) {
|
||||
Effect tokenEffect = new CreateTokenEffect(new SoldierToken(), voteTwoCount);
|
||||
tokenEffect.apply(game, source);
|
||||
}
|
||||
|
||||
return true;
|
||||
public LieutenantsOfTheGuardEffect copy() {
|
||||
return new LieutenantsOfTheGuardEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LieutenantsOfTheGuardDilemmaEffect copy() {
|
||||
return new LieutenantsOfTheGuardDilemmaEffect(this);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// Outcome.Benefit - AI will boost all the time (Strength choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Strength (+1/+1 counter)", "Numbers (1/1 token)", Outcome.Benefit);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int strengthCount = vote.getVoteCount(true);
|
||||
int numbersCount = vote.getVoteCount(false);
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (strengthCount > 0 && permanent != null) {
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(strengthCount), source.getControllerId(), source, game);
|
||||
}
|
||||
if (numbersCount > 0) {
|
||||
new SoldierToken().putOntoBattlefield(numbersCount, game, source, source.getControllerId());
|
||||
}
|
||||
return strengthCount + numbersCount > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
package mage.cards.m;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DestroyAllEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
* @author fireshoes, TheElk801
|
||||
*/
|
||||
public final class MagisterOfWorth extends CardImpl {
|
||||
|
||||
|
@ -38,9 +41,7 @@ public final class MagisterOfWorth extends CardImpl {
|
|||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Will of the council - When Magister of Worth enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than Magister of Worth.
|
||||
Effect effect = new MagisterOfWorthVoteEffect();
|
||||
effect.setText("Will of the council - When Magister of Worth enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than Magister of Worth");
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(effect, false, true));
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new MagisterOfWorthEffect(), false, true));
|
||||
}
|
||||
|
||||
private MagisterOfWorth(final MagisterOfWorth card) {
|
||||
|
@ -53,83 +54,65 @@ public final class MagisterOfWorth extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class MagisterOfWorthVoteEffect extends OneShotEffect {
|
||||
class MagisterOfWorthEffect extends OneShotEffect {
|
||||
|
||||
MagisterOfWorthVoteEffect() {
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent();
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
MagisterOfWorthEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — When {this} enters the battlefield, starting with you, each player votes for grace or condemnation. If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. If condemnation gets more votes or the vote is tied, destroy all creatures other than {this}.";
|
||||
staticText = "<i>Will of the council</i> — When {this} enters the battlefield, " +
|
||||
"starting with you, each player votes for grace or condemnation. " +
|
||||
"If grace gets more votes, each player returns each creature card from their graveyard to the battlefield. " +
|
||||
"If condemnation gets more votes or the vote is tied, destroy all creatures other than {this}.";
|
||||
}
|
||||
|
||||
MagisterOfWorthVoteEffect(final MagisterOfWorthVoteEffect effect) {
|
||||
private MagisterOfWorthEffect(final MagisterOfWorthEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagisterOfWorthVoteEffect copy() {
|
||||
return new MagisterOfWorthVoteEffect(this);
|
||||
public MagisterOfWorthEffect copy() {
|
||||
return new MagisterOfWorthEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int graceCount = 0;
|
||||
int condemnationCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.DestroyPermanent, "Choose grace?", source, game)) {
|
||||
graceCount++;
|
||||
game.informPlayers(player.getLogName() + " has chosen: grace");
|
||||
} else {
|
||||
condemnationCount++;
|
||||
game.informPlayers(player.getLogName() + " has chosen: condemnation");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (graceCount > condemnationCount) {
|
||||
new MagisterOfWorthReturnFromGraveyardEffect().apply(game, source);
|
||||
} else {
|
||||
FilterPermanent filter = new FilterCreaturePermanent("creatures other than {this}");
|
||||
filter.add(AnotherPredicate.instance);
|
||||
new DestroyAllEffect(filter).apply(game, source);
|
||||
}
|
||||
return true;
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Outcome.Benefit - AI will return from graveyard all the time (Grace choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Grace (return from graveyard)", "Condemnation (destroy all)", Outcome.Benefit);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int graceCount = vote.getVoteCount(true);
|
||||
int condemnationCount = vote.getVoteCount(false);
|
||||
if (condemnationCount >= graceCount) {
|
||||
return new DestroyAllEffect(filter).apply(game, source);
|
||||
}
|
||||
|
||||
// grace win - each player returns each creature card from their graveyard to the battlefield
|
||||
Cards cards = new CardsImpl();
|
||||
game.getState()
|
||||
.getPlayersInRange(source.getControllerId(), game)
|
||||
.stream()
|
||||
.map(game::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Player::getGraveyard)
|
||||
.map(g -> g.getCards(game))
|
||||
.flatMap(Collection::stream)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(MageObject::isCreature)
|
||||
.forEach(cards::add);
|
||||
return controller.moveCards(
|
||||
cards.getCards(game), Zone.BATTLEFIELD, source, game,
|
||||
false, false, true, null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MagisterOfWorthReturnFromGraveyardEffect extends OneShotEffect {
|
||||
|
||||
public MagisterOfWorthReturnFromGraveyardEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
staticText = "each player returns each creature card from their graveyard to the battlefield";
|
||||
}
|
||||
|
||||
public MagisterOfWorthReturnFromGraveyardEffect(final MagisterOfWorthReturnFromGraveyardEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
if (controller != null && sourceObject != null) {
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.moveCards(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game), Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagisterOfWorthReturnFromGraveyardEffect copy() {
|
||||
return new MagisterOfWorthReturnFromGraveyardEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,41 +1,42 @@
|
|||
|
||||
package mage.cards.m;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CouncilsDilemmaVoteEffect;
|
||||
import mage.abilities.effects.common.DrawDiscardControllerEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JRHerlehy
|
||||
* @author JRHerlehy, TheElk801
|
||||
*/
|
||||
public final class MessengerJays extends CardImpl {
|
||||
|
||||
public MessengerJays(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}");
|
||||
|
||||
|
||||
this.subtype.add(SubType.BIRD);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// <i>Council's dilemma — When Messenger Jays enters the battlefield, starting with you, each player votes for feather or quill. Put a +1/+1 counter on Messenger Jays for each feather vote and draw a card for each quill vote. For each card drawn this way, discard a card.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new MessengerJaysDilemmaEffect(), false, "<i>Council's dilemma</i> — "));
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new MessengerJaysEffect(), false, "<i>Council's dilemma</i> — "
|
||||
));
|
||||
}
|
||||
|
||||
private MessengerJays(final MessengerJays card) {
|
||||
|
@ -48,45 +49,44 @@ public final class MessengerJays extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class MessengerJaysDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||
class MessengerJaysEffect extends OneShotEffect {
|
||||
|
||||
public MessengerJaysDilemmaEffect() {
|
||||
MessengerJaysEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "starting with you, each player votes for feather or quill. Put a +1/+1 counter on {this} for each feather vote and draw a card for each quill vote. For each card drawn this way, discard a card.";
|
||||
staticText = "starting with you, each player votes for feather or quill. " +
|
||||
"Put a +1/+1 counter on {this} for each feather vote " +
|
||||
"and draw a card for each quill vote. For each card drawn this way, discard a card.";
|
||||
}
|
||||
|
||||
public MessengerJaysDilemmaEffect(final MessengerJaysDilemmaEffect effect) {
|
||||
private MessengerJaysEffect(final MessengerJaysEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
//If no controller, exit out here and do not vote.
|
||||
if (controller == null) return false;
|
||||
|
||||
this.vote("feather", "quill", controller, game, source);
|
||||
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
|
||||
//Feathers Votes
|
||||
//If feathers received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counter on it.
|
||||
if (voteOneCount > 0 && permanent != null)
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount), source.getControllerId(), source, game);
|
||||
|
||||
//Quill Votes
|
||||
//Only let the controller loot the appropriate amount of cards if it was voted for.
|
||||
if (voteTwoCount > 0) {
|
||||
Effect lootCardsEffect = new DrawDiscardControllerEffect(voteTwoCount, voteTwoCount);
|
||||
lootCardsEffect.apply(game, source);
|
||||
}
|
||||
|
||||
return true;
|
||||
public MessengerJaysEffect copy() {
|
||||
return new MessengerJaysEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessengerJaysDilemmaEffect copy() {
|
||||
return new MessengerJaysDilemmaEffect(this);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// Outcome.Benefit - AI will boost all the time (Feather choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Feather (+1/+1 counter)", "Quill (draw a card)", Outcome.Benefit);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int featherCount = vote.getVoteCount(true);
|
||||
int quillCount = vote.getVoteCount(false);
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (featherCount > 0 && permanent != null) {
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(featherCount), source.getControllerId(), source, game);
|
||||
}
|
||||
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (quillCount > 0 && player != null) {
|
||||
int drawn = player.drawCards(quillCount, source, game);
|
||||
player.discard(drawn, false, false, source, game);
|
||||
}
|
||||
|
||||
return featherCount + quillCount > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
|
||||
package mage.cards.o;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CouncilsDilemmaVoteEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JRHerlehy
|
||||
* @author JRHerlehy, TheElk801
|
||||
*/
|
||||
public final class OrchardElemental extends CardImpl {
|
||||
|
||||
public OrchardElemental(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}");
|
||||
|
||||
|
||||
this.subtype.add(SubType.ELEMENTAL);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// <i>Council's dilemma</i> &mdash When Orchard Elemental enters the battlefield, starting with you, each player votes for sprout or harvest. Put two +1/+1 counters on Orchard Elemental for each sprout vote. You gain 3 life for each harvest vote.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new OrchardElementalDilemmaEffect(), false, "<i>Council's dilemma</i> — "));
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new OrchardElementalEffect(), false, "<i>Council's dilemma</i> — "
|
||||
));
|
||||
}
|
||||
|
||||
private OrchardElemental(final OrchardElemental card) {
|
||||
|
@ -45,43 +45,42 @@ public final class OrchardElemental extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class OrchardElementalDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||
class OrchardElementalEffect extends OneShotEffect {
|
||||
|
||||
public OrchardElementalDilemmaEffect() {
|
||||
OrchardElementalEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "starting with you, each player votes for sprout or harvest. Put two +1/+1 counters on Orchard Elemental for each sprout vote. You gain 3 life for each harvest vote";
|
||||
staticText = "starting with you, each player votes for sprout or harvest. " +
|
||||
"Put two +1/+1 counters on {this} for each sprout vote. You gain 3 life for each harvest vote";
|
||||
}
|
||||
|
||||
public OrchardElementalDilemmaEffect(final OrchardElementalDilemmaEffect effect) {
|
||||
private OrchardElementalEffect(final OrchardElementalEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
if (controller == null) return false;
|
||||
|
||||
this.vote("sprout", "harvest", controller, game, source);
|
||||
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
|
||||
//Sprout Votes
|
||||
//If sprout received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counter on it.
|
||||
if (voteOneCount > 0 && permanent != null)
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount * 2), source.getControllerId(), source, game);
|
||||
|
||||
//Harvest Votes
|
||||
if (voteTwoCount > 0) {
|
||||
Effect gainLifeEffect = new GainLifeEffect(voteTwoCount * 3);
|
||||
gainLifeEffect.apply(game, source);
|
||||
}
|
||||
|
||||
return true;
|
||||
public OrchardElementalEffect copy() {
|
||||
return new OrchardElementalEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrchardElementalDilemmaEffect copy() {
|
||||
return new OrchardElementalDilemmaEffect(this);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// Outcome.Benefit - AI will boost all the time (Sprout choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Sprout (two +1/+1 counters)", "Harvest (3 life)", Outcome.Benefit);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int sproutCount = vote.getVoteCount(true);
|
||||
int harvestCount = vote.getVoteCount(false);
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (sproutCount > 0 && permanent != null) {
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(2 * sproutCount), source.getControllerId(), source, game);
|
||||
}
|
||||
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (harvestCount > 0 && player != null) {
|
||||
player.gainLife(3 * harvestCount, game, source);
|
||||
}
|
||||
|
||||
return sproutCount + harvestCount > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
* @author emerald000, TheElk801
|
||||
*/
|
||||
public final class PleaForPower extends CardImpl {
|
||||
|
||||
public PleaForPower(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}");
|
||||
|
||||
// Will of the council - Starting with you, each player votes for time or knowledge. If time gets more votes, take an extra turn after this one. If knowledge gets more votes or the vote is tied, draw three cards.
|
||||
this.getSpellAbility().addEffect(new PleaForPowerEffect());
|
||||
|
@ -39,10 +39,12 @@ class PleaForPowerEffect extends OneShotEffect {
|
|||
|
||||
PleaForPowerEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, each player votes for time or knowledge. If time gets more votes, take an extra turn after this one. If knowledge gets more votes or the vote is tied, draw three cards";
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, " +
|
||||
"each player votes for time or knowledge. If time gets more votes, take an extra turn after this one. " +
|
||||
"If knowledge gets more votes or the vote is tied, draw three cards";
|
||||
}
|
||||
|
||||
PleaForPowerEffect(final PleaForPowerEffect effect) {
|
||||
private PleaForPowerEffect(final PleaForPowerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -54,28 +56,21 @@ class PleaForPowerEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int timeCount = 0;
|
||||
int knowledgeCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.ExtraTurn, "Choose time?", source, game)) {
|
||||
timeCount++;
|
||||
game.informPlayers(player.getLogName() + " has chosen: time");
|
||||
} else {
|
||||
knowledgeCount++;
|
||||
game.informPlayers(player.getLogName() + " has chosen: knowledge");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timeCount > knowledgeCount) {
|
||||
new AddExtraTurnControllerEffect().apply(game, source);
|
||||
} else {
|
||||
controller.drawCards(3, source, game);
|
||||
}
|
||||
return true;
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Outcome.Detriment - AI will draw cards all the time (Knowledge choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Time (extra turn)", "Knowledge (draw 3 cards)", Outcome.Detriment);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int timeCount = vote.getVoteCount(true);
|
||||
int knowledgeCount = vote.getVoteCount(false);
|
||||
if (timeCount > knowledgeCount) {
|
||||
return new AddExtraTurnControllerEffect().apply(game, source);
|
||||
} else {
|
||||
return controller.drawCards(3, source, game) > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.CouncilsDilemmaVoteEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterPermanentCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JRHerlehy
|
||||
* @author JRHerlehy, TheElk801
|
||||
*/
|
||||
public final class SelvalasStampede extends CardImpl {
|
||||
|
||||
|
@ -23,7 +23,7 @@ public final class SelvalasStampede extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}");
|
||||
|
||||
// <i>Council's dilemma</i> &mdash Starting with you, each player votes for wild or free. Reveal cards from the top of your library until you reveal a creature card for each wild vote. Put those creature cards onto the battlefield, then shuffle the rest into your library. You may put a permanent card from your hand onto the battlefield for each free vote.
|
||||
this.getSpellAbility().addEffect(new SelvalasStampedeDilemmaEffect());
|
||||
this.getSpellAbility().addEffect(new SelvalasStampedeEffect());
|
||||
}
|
||||
|
||||
private SelvalasStampede(final SelvalasStampede card) {
|
||||
|
@ -36,63 +36,70 @@ public final class SelvalasStampede extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class SelvalasStampedeDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||
class SelvalasStampedeEffect extends OneShotEffect {
|
||||
|
||||
public SelvalasStampedeDilemmaEffect() {
|
||||
super(Outcome.PutCardInPlay);
|
||||
this.staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for wild or free. Reveal cards from the top of your library until you reveal a creature card for each wild vote. Put those creature cards onto the battlefield, then shuffle the rest into your library. "
|
||||
+ "You may put a permanent card from your hand onto the battlefield for each free vote";
|
||||
SelvalasStampedeEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for wild or free. " +
|
||||
"Reveal cards from the top of your library until you reveal a creature card for each wild vote. " +
|
||||
"Put those creature cards onto the battlefield, then shuffle the rest into your library. " +
|
||||
"You may put a permanent card from your hand onto the battlefield for each free vote";
|
||||
}
|
||||
|
||||
public SelvalasStampedeDilemmaEffect(final SelvalasStampedeDilemmaEffect effect) {
|
||||
private SelvalasStampedeEffect(final SelvalasStampedeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
//If no controller, exit here and do not vote.
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.vote("wild", "free", controller, game, source);
|
||||
|
||||
//Wild Votes
|
||||
if (voteOneCount > 0) {
|
||||
Cards revealedCards = new CardsImpl();
|
||||
Cards toBattlefield = new CardsImpl();
|
||||
for (Card card : controller.getLibrary().getCards(game)) {
|
||||
revealedCards.add(card);
|
||||
if (card.isCreature()) {
|
||||
toBattlefield.add(card);
|
||||
if (toBattlefield.size() == voteOneCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.revealCards(source, revealedCards, game);
|
||||
controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game);
|
||||
revealedCards.removeAll(toBattlefield);
|
||||
if (!revealedCards.isEmpty()) {
|
||||
controller.shuffleLibrary(source, game);
|
||||
}
|
||||
}
|
||||
|
||||
//Free Votes
|
||||
if (voteTwoCount > 0) {
|
||||
TargetCardInHand target = new TargetCardInHand(0, voteTwoCount, new FilterPermanentCard("permanent cards"));
|
||||
if (controller.choose(outcome, target, source.getSourceId(), game)) {
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
public SelvalasStampedeEffect copy() {
|
||||
return new SelvalasStampedeEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelvalasStampedeDilemmaEffect copy() {
|
||||
return new SelvalasStampedeDilemmaEffect(this);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Outcome.Detriment - AI will use library will the time (Free choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Wild (from library to battlefield)", "Free (from hand to battlefield)", Outcome.Detriment);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int wildCount = vote.getVoteCount(true);
|
||||
int freeCount = vote.getVoteCount(false);
|
||||
|
||||
// Reveal cards from the top of your library until you reveal a creature card for each wild vote.
|
||||
// Put those creature cards onto the battlefield, then shuffle the rest into your library.
|
||||
Cards toReveal = new CardsImpl();
|
||||
Cards creatureCards = new CardsImpl();
|
||||
for (Card card : player.getLibrary().getCards(game)) {
|
||||
if (creatureCards.size() >= wildCount) {
|
||||
break;
|
||||
}
|
||||
if (card.isCreature()) {
|
||||
creatureCards.add(card);
|
||||
}
|
||||
toReveal.add(card);
|
||||
}
|
||||
if (toReveal.size() > 0) {
|
||||
player.revealCards(source, toReveal, game);
|
||||
}
|
||||
if (creatureCards.size() > 0) {
|
||||
player.moveCards(creatureCards, Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
player.shuffleLibrary(source, game);
|
||||
|
||||
// You may put a permanent card from your hand onto the battlefield for each free vote
|
||||
if (freeCount > 0) {
|
||||
TargetCardInHand target = new TargetCardInHand(0, freeCount, StaticFilters.FILTER_CARD_PERMANENT);
|
||||
player.choose(Outcome.PutCreatureInPlay, player.getHand(), target, game);
|
||||
creatureCards.clear();
|
||||
creatureCards.addAll(target.getTargets());
|
||||
player.moveCards(creatureCards, Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
|
||||
return wildCount + freeCount > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CopyTargetSpellEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
* @author fireshoes, TheElk801
|
||||
*/
|
||||
public final class SplitDecision extends CardImpl {
|
||||
|
||||
public SplitDecision(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
|
||||
|
||||
// Will of the council - Choose target instant or sorcery spell. Starting with you, each player votes for denial or duplication. If denial gets more votes, counter the spell. If duplication gets more votes or the vote is tied, copy the spell. You may choose new targets for the copy.
|
||||
this.getSpellAbility().addEffect(new SplitDecisionEffect());
|
||||
|
@ -41,11 +41,14 @@ public final class SplitDecision extends CardImpl {
|
|||
class SplitDecisionEffect extends OneShotEffect {
|
||||
|
||||
SplitDecisionEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — Choose target instant or sorcery spell. Starting with you, each player votes for denial or duplication. If denial gets more votes, counter the spell. If duplication gets more votes or the vote is tied, copy the spell. You may choose new targets for the copy";
|
||||
super(Outcome.Removal); // cause AI votes for counter all the time so it must it target opponent's spell, not own
|
||||
this.staticText = "<i>Will of the council</i> — Choose target instant or sorcery spell. " +
|
||||
"Starting with you, each player votes for denial or duplication. If denial gets more votes, " +
|
||||
"counter the spell. If duplication gets more votes or the vote is tied, copy the spell. " +
|
||||
"You may choose new targets for the copy";
|
||||
}
|
||||
|
||||
SplitDecisionEffect(final SplitDecisionEffect effect) {
|
||||
private SplitDecisionEffect(final SplitDecisionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -56,28 +59,26 @@ class SplitDecisionEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int denialCount = 0;
|
||||
int duplicationCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.ExtraTurn, "Choose denial?", source, game)) {
|
||||
denialCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for denial");
|
||||
} else {
|
||||
duplicationCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for duplication");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (denialCount > duplicationCount) {
|
||||
return game.getStack().counter(getTargetPointer().getFirst(game, source), source, game);
|
||||
} else {
|
||||
return new CopyTargetSpellEffect().apply(game, source);
|
||||
}
|
||||
Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
|
||||
if (spell == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Outcome.Benefit - AI will use counter all the time (Denial choice)
|
||||
// TODO: add AI hint logic in the choice method, see Tyrant's Choice as example
|
||||
TwoChoiceVote vote = new TwoChoiceVote(
|
||||
"Denial (counter " + spell.getIdName() + ")",
|
||||
"Duplication (copy " + spell.getIdName() + ")",
|
||||
Outcome.Benefit
|
||||
);
|
||||
vote.doVotes(source, game);
|
||||
|
||||
int denialCount = vote.getVoteCount(true);
|
||||
int duplicationCount = vote.getVoteCount(false);
|
||||
if (denialCount > duplicationCount) {
|
||||
return game.getStack().counter(spell.getId(), source, game);
|
||||
} else {
|
||||
return new CopyTargetSpellEffect().apply(game, source);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
|
||||
import mage.abilities.effects.common.SacrificeOpponentsEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.TwoChoiceVote;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
* @author fireshoes, TheElk801
|
||||
*/
|
||||
public final class TyrantsChoice extends CardImpl {
|
||||
|
||||
public TyrantsChoice(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
|
||||
|
||||
// Will of the council - Starting with you, each player votes for death or torture. If death gets more votes, each opponent sacrifices a creature. If torture gets more votes or the vote is tied, each opponent loses 4 life.
|
||||
this.getSpellAbility().addEffect(new TyrantsChoiceEffect());
|
||||
|
@ -40,10 +41,13 @@ class TyrantsChoiceEffect extends OneShotEffect {
|
|||
|
||||
TyrantsChoiceEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, each player votes for death or torture. If death gets more votes, each opponent sacrifices a creature. If torture gets more votes or the vote is tied, each opponent loses 4 life";
|
||||
this.staticText = "<i>Will of the council</i> — Starting with you, " +
|
||||
"each player votes for death or torture. If death gets more votes, " +
|
||||
"each opponent sacrifices a creature. If torture gets more votes " +
|
||||
"or the vote is tied, each opponent loses 4 life";
|
||||
}
|
||||
|
||||
TyrantsChoiceEffect(final TyrantsChoiceEffect effect) {
|
||||
private TyrantsChoiceEffect(final TyrantsChoiceEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -54,55 +58,24 @@ class TyrantsChoiceEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int deathCount = 0;
|
||||
int tortureCount = 0;
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.Sacrifice, "Choose death?", source, game)) {
|
||||
deathCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for death");
|
||||
} else {
|
||||
tortureCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for torture");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deathCount > tortureCount) {
|
||||
new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT).apply(game, source);
|
||||
TwoChoiceVote vote = new TwoChoiceVote("Death (sacrifice a creature)", "Torture (lose 4 life)", Outcome.Benefit);
|
||||
vote.doVotes(source, game, (voteHandler, aiPlayer, aiDecidingPlayer, aiSource, aiGame) -> {
|
||||
// ai hint
|
||||
if (aiSource.isControlledBy(aiDecidingPlayer.getId())) {
|
||||
// best for controller - lose life
|
||||
return Boolean.FALSE;
|
||||
} else {
|
||||
new TyrantsChoiceLoseLifeEffect().apply(game, source);
|
||||
// best for opponent - sacrifice
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
int deathCount = vote.getVoteCount(true);
|
||||
int tortureCount = vote.getVoteCount(false);
|
||||
if (deathCount > tortureCount) {
|
||||
return new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT).apply(game, source);
|
||||
} else {
|
||||
return new LoseLifeOpponentsEffect(4).apply(game, source);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class TyrantsChoiceLoseLifeEffect extends OneShotEffect {
|
||||
|
||||
public TyrantsChoiceLoseLifeEffect() {
|
||||
super(Outcome.Damage);
|
||||
staticText = "Each opponent loses 2 life";
|
||||
}
|
||||
|
||||
public TyrantsChoiceLoseLifeEffect(final TyrantsChoiceLoseLifeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
|
||||
game.getPlayer(opponentId).loseLife(4, game, source, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TyrantsChoiceLoseLifeEffect copy() {
|
||||
return new TyrantsChoiceLoseLifeEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ public final class Conspiracy extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Basandra, Battle Seraph", 184, Rarity.RARE, mage.cards.b.BasandraBattleSeraph.class));
|
||||
cards.add(new SetCardInfo("Bite of the Black Rose", 26, Rarity.UNCOMMON, mage.cards.b.BiteOfTheBlackRose.class));
|
||||
cards.add(new SetCardInfo("Boldwyr Intimidator", 137, Rarity.UNCOMMON, mage.cards.b.BoldwyrIntimidator.class));
|
||||
cards.add(new SetCardInfo("Brago's Representative", 14, Rarity.COMMON, mage.cards.b.BragosRepresentative.class));
|
||||
cards.add(new SetCardInfo("Brago, King Eternal", 41, Rarity.RARE, mage.cards.b.BragoKingEternal.class));
|
||||
cards.add(new SetCardInfo("Brainstorm", 91, Rarity.COMMON, mage.cards.b.Brainstorm.class));
|
||||
cards.add(new SetCardInfo("Breakthrough", 92, Rarity.UNCOMMON, mage.cards.b.Breakthrough.class));
|
||||
|
@ -90,6 +91,7 @@ public final class Conspiracy extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Grenzo, Dungeon Warden", 47, Rarity.RARE, mage.cards.g.GrenzoDungeonWarden.class));
|
||||
cards.add(new SetCardInfo("Grenzo's Cutthroat", 32, Rarity.COMMON, mage.cards.g.GrenzosCutthroat.class));
|
||||
cards.add(new SetCardInfo("Grixis Illusionist", 99, Rarity.COMMON, mage.cards.g.GrixisIllusionist.class));
|
||||
cards.add(new SetCardInfo("Grudge Keeper", 28, Rarity.COMMON, mage.cards.g.GrudgeKeeper.class));
|
||||
cards.add(new SetCardInfo("Guardian Zendikon", 71, Rarity.COMMON, mage.cards.g.GuardianZendikon.class));
|
||||
cards.add(new SetCardInfo("Heartless Hidetsugu", 144, Rarity.RARE, mage.cards.h.HeartlessHidetsugu.class));
|
||||
cards.add(new SetCardInfo("Heckling Fiends", 145, Rarity.UNCOMMON, mage.cards.h.HecklingFiends.class));
|
||||
|
|
|
@ -33,6 +33,7 @@ public final class ConspiracyTakeTheCrown extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Altar's Reap", 127, Rarity.COMMON, mage.cards.a.AltarsReap.class));
|
||||
cards.add(new SetCardInfo("Ascended Lawmage", 198, Rarity.UNCOMMON, mage.cards.a.AscendedLawmage.class));
|
||||
cards.add(new SetCardInfo("Avatar of Woe", 128, Rarity.MYTHIC, mage.cards.a.AvatarOfWoe.class));
|
||||
cards.add(new SetCardInfo("Ballot Broker", 13, Rarity.COMMON, mage.cards.b.BallotBroker.class));
|
||||
cards.add(new SetCardInfo("Beast Within", 174, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class));
|
||||
cards.add(new SetCardInfo("Berserk", 175, Rarity.MYTHIC, mage.cards.b.Berserk.class));
|
||||
cards.add(new SetCardInfo("Besmirch", 49, Rarity.UNCOMMON, mage.cards.b.Besmirch.class));
|
||||
|
@ -121,6 +122,7 @@ public final class ConspiracyTakeTheCrown extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Hundred-Handed One", 93, Rarity.RARE, mage.cards.h.HundredHandedOne.class));
|
||||
cards.add(new SetCardInfo("Hurly-Burly", 165, Rarity.COMMON, mage.cards.h.HurlyBurly.class));
|
||||
cards.add(new SetCardInfo("Ill-Tempered Cyclops", 166, Rarity.COMMON, mage.cards.i.IllTemperedCyclops.class));
|
||||
cards.add(new SetCardInfo("Illusion of Choice", 31, Rarity.UNCOMMON, mage.cards.i.IllusionOfChoice.class));
|
||||
cards.add(new SetCardInfo("Infest", 139, Rarity.UNCOMMON, mage.cards.i.Infest.class));
|
||||
cards.add(new SetCardInfo("Inquisition of Kozilek", 140, Rarity.RARE, mage.cards.i.InquisitionOfKozilek.class));
|
||||
cards.add(new SetCardInfo("Into the Void", 112, Rarity.UNCOMMON, mage.cards.i.IntoTheVoid.class));
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
package org.mage.test.multiplayer;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestCommander4PlayersWithAIHelps;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class VotingTest extends CardTestCommander4PlayersWithAIHelps {
|
||||
|
||||
// Player order: A -> D -> C -> B
|
||||
|
||||
// Council’s dilemma — When Lieutenants of the Guard enters the battlefield, starting with you,
|
||||
// each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard
|
||||
// for each strength vote and create a 1/1 white Soldier creature token for each numbers vote.
|
||||
private static final String lieutenant = "Lieutenants of the Guard";
|
||||
|
||||
// While voting, you get an additional vote. (The votes can be for different choices or for the same choice.)
|
||||
private static final String rep = "Brago's Representative";
|
||||
|
||||
// While voting, you may vote an additional time. (The votes can be for different choices or for the same choice.)
|
||||
private static final String broker = "Ballot Broker";
|
||||
|
||||
// You choose how each player votes this turn.
|
||||
private static final String illusion = "Illusion of Choice";
|
||||
|
||||
// TODO: add test with broker
|
||||
// rulues:
|
||||
// The ability only affects spells and abilities that use the word “vote.” Other cards that involve choices,
|
||||
// such as Archangel of Strife, are unaffected.
|
||||
// (2016-08-23)
|
||||
|
||||
// Whenever players finish voting, each opponent who voted for a choice you didn’t vote for loses 2 life.
|
||||
private static final String keeper = "Grudge Keeper";
|
||||
|
||||
// Will of the council - Starting with you, each player votes for death or torture. If death gets more votes,
|
||||
// each opponent sacrifices a creature. If torture gets more votes or the vote is tied, each opponent loses 4 life.
|
||||
private static final String tyrant = "Tyrant's Choice";
|
||||
|
||||
private void setChoices(String choice) {
|
||||
setChoices(choice, choice, choice, choice);
|
||||
}
|
||||
|
||||
private void setChoices(String choiceA, String choiceB, String choiceC, String choiceD) {
|
||||
setChoice(playerA, choiceA);
|
||||
setChoice(playerB, choiceB);
|
||||
setChoice(playerC, choiceC);
|
||||
setChoice(playerD, choiceD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LieutenantsOfTheGuard_1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoices("Yes");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 6, 6);
|
||||
assertPermanentCount(playerA, "Soldier", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LieutenantsOfTheGuard_2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoices("No");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 2, 2);
|
||||
assertPermanentCount(playerA, "Soldier", 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LieutenantsOfTheGuard_3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoices("Yes", "Yes", "No", "No");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 4, 4);
|
||||
assertPermanentCount(playerA, "Soldier", 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_TyrantsChoice_AI_Normal() {
|
||||
addCard(Zone.HAND, playerA, tyrant); // {1}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
// ai play
|
||||
// opponents must have more votes so final result is sacrifice (best for opponents)
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tyrant);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerC);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerD);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, tyrant, 1);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
assertLife(playerC, 20);
|
||||
assertLife(playerD, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO: fix after merge, see player.isComputer
|
||||
public void test_TyrantsChoice_AI_UnderControl() {
|
||||
addCard(Zone.HAND, playerA, tyrant); // {1}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, illusion, 1); // {U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
|
||||
|
||||
// prepare vote control
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkGraveyardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion, 1);
|
||||
|
||||
// ai play
|
||||
// you control the opponents, so votes result must be lose life (best for controller)
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tyrant);
|
||||
checkStackSize("before resolve", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerC);
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerD);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, tyrant, 1);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 4);
|
||||
assertLife(playerC, 20 - 4);
|
||||
assertLife(playerD, 20 - 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_BragosRepresentative() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerA, rep);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoice(playerA, "Yes");
|
||||
setChoices("Yes", "Yes", "No", "No");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 5, 5);
|
||||
assertPermanentCount(playerA, "Soldier", 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_BallotBroker() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerA, broker);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoices("Yes", "Yes", "No", "No");
|
||||
setChoice(playerA, "Yes"); // to have an additional vote
|
||||
setChoice(playerA, "No"); // the additional vote
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 4, 4);
|
||||
assertPermanentCount(playerA, "Soldier", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_IllusionOfChoice_Single() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Tundra", 6);
|
||||
addCard(Zone.HAND, playerA, illusion);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoice(playerA, "Yes", 4);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 6, 6);
|
||||
assertPermanentCount(playerA, "Soldier", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_IllusionOfChoice_WithBragosRepresentative() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Tundra", 6);
|
||||
addCard(Zone.BATTLEFIELD, playerB, rep);
|
||||
addCard(Zone.HAND, playerA, illusion);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoice(playerA, "Yes", 5);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 7, 7);
|
||||
assertPermanentCount(playerA, "Soldier", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_IllusionOfChoice_Double() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Tundra", 6);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island");
|
||||
addCard(Zone.HAND, playerA, illusion);
|
||||
addCard(Zone.HAND, playerB, illusion);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, illusion);
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, illusion);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoice(playerB, "Yes", 4);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 6, 6);
|
||||
assertPermanentCount(playerA, "Soldier", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GrudgeKeeper_1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerA, keeper);
|
||||
addCard(Zone.BATTLEFIELD, playerB, rep);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoice(playerB, "Yes");
|
||||
setChoices("Yes", "No", "No", "No");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 4, 4);
|
||||
assertPermanentCount(playerA, "Soldier", 3);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 2);
|
||||
assertLife(playerC, 20 - 2);
|
||||
assertLife(playerD, 20 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GrudgeKeeper_2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerA, keeper);
|
||||
addCard(Zone.BATTLEFIELD, playerA, rep);
|
||||
addCard(Zone.BATTLEFIELD, playerB, rep);
|
||||
addCard(Zone.HAND, playerA, lieutenant);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lieutenant);
|
||||
setChoice(playerA, "No");
|
||||
setChoice(playerB, "No");
|
||||
setChoices("Yes");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, lieutenant, 6, 6);
|
||||
assertPermanentCount(playerA, "Soldier", 2);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
assertLife(playerC, 20);
|
||||
assertLife(playerD, 20);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.mage.test.serverside.base;
|
||||
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import org.mage.test.player.TestComputerPlayer7;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
|
||||
/**
|
||||
* See more details in CardTestPlayerBaseWithAIHelps
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public abstract class CardTestCommander4PlayersWithAIHelps extends CardTestCommander4Players {
|
||||
|
||||
@Override
|
||||
protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) {
|
||||
// use same RangeOfInfluence.ALL as CardTestCommander4Players do
|
||||
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, RangeOfInfluence.ALL, 6));
|
||||
testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands
|
||||
return testPlayer;
|
||||
}
|
||||
}
|
|
@ -5,9 +5,7 @@ import org.mage.test.player.TestComputerPlayerMonteCarlo;
|
|||
import org.mage.test.player.TestPlayer;
|
||||
|
||||
/**
|
||||
* Base class but with Monte Carlo computer player to test single AI commands (it's different from full AI simulation from CardTestPlayerBaseAI):
|
||||
* 1. AI don't play normal priorities (you must use ai*** commands to play it);
|
||||
* 2. AI will choose in non strict mode (it's simulated ComputerPlayerMCTS, not simple ComputerPlayer from basic tests)
|
||||
* See more details in CardTestPlayerBaseWithAIHelps
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author JRHerlehy
|
||||
*/
|
||||
public abstract class CouncilsDilemmaVoteEffect extends OneShotEffect {
|
||||
|
||||
protected int voteOneCount = 0, voteTwoCount = 0;
|
||||
|
||||
public CouncilsDilemmaVoteEffect(Outcome outcome) {
|
||||
super(outcome);
|
||||
}
|
||||
|
||||
public CouncilsDilemmaVoteEffect(final CouncilsDilemmaVoteEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
protected void vote(String choiceOne, String choiceTwo, Player controller, Game game, Ability source) {
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.Vote,
|
||||
"Choose " + choiceOne + " or " + choiceTwo + "?",
|
||||
source.getRule(), choiceOne, choiceTwo, source, game)) {
|
||||
voteOneCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for " + choiceOne);
|
||||
} else {
|
||||
voteTwoCount++;
|
||||
game.informPlayers(player.getLogName() + " has voted for " + choiceTwo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -63,11 +63,15 @@ public class ChoiceColor extends ChoiceImpl {
|
|||
}
|
||||
|
||||
public ObjectColor getColor() {
|
||||
if (choice == null) {
|
||||
return getColorFromString(choice);
|
||||
}
|
||||
|
||||
public static ObjectColor getColorFromString(String colorString) {
|
||||
if (colorString == null) {
|
||||
return null;
|
||||
}
|
||||
ObjectColor color = new ObjectColor();
|
||||
switch (choice) {
|
||||
switch (colorString) {
|
||||
case "Black":
|
||||
color.setBlack(true);
|
||||
break;
|
||||
|
|
41
Mage/src/main/java/mage/choices/TwoChoiceVote.java
Normal file
41
Mage/src/main/java/mage/choices/TwoChoiceVote.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package mage.choices;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class TwoChoiceVote extends VoteHandler<Boolean> {
|
||||
|
||||
private final String choice1;
|
||||
private final String choice2;
|
||||
private final Outcome outcome;
|
||||
|
||||
public TwoChoiceVote(String choice1, String choice2, Outcome outcome) {
|
||||
this.choice1 = choice1;
|
||||
this.choice2 = choice2;
|
||||
this.outcome = outcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Boolean> getPossibleVotes(Ability source, Game game) {
|
||||
return new LinkedHashSet<>(Arrays.asList(Boolean.TRUE, Boolean.FALSE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game) {
|
||||
return decidingPlayer.chooseUse(outcome, voteInfo, null, choice1, choice2, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String voteName(Boolean vote) {
|
||||
return (vote ? choice1 : choice2);
|
||||
}
|
||||
}
|
183
Mage/src/main/java/mage/choices/VoteHandler.java
Normal file
183
Mage/src/main/java/mage/choices/VoteHandler.java
Normal file
|
@ -0,0 +1,183 @@
|
|||
package mage.choices;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.VoteEvent;
|
||||
import mage.game.events.VotedEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public abstract class VoteHandler<T> {
|
||||
|
||||
protected final Map<UUID, List<T>> playerMap = new HashMap<>();
|
||||
protected VoteHandlerAI<T> aiVoteHint = null;
|
||||
|
||||
public void doVotes(Ability source, Game game) {
|
||||
doVotes(source, game, null);
|
||||
}
|
||||
|
||||
public void doVotes(Ability source, Game game, VoteHandlerAI<T> aiVoteHint) {
|
||||
this.aiVoteHint = aiVoteHint;
|
||||
this.playerMap.clear();
|
||||
int stepCurrent = 0;
|
||||
int stepTotal = game.getState().getPlayersInRange(source.getControllerId(), game).size();
|
||||
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
||||
stepCurrent++;
|
||||
VoteEvent event = new VoteEvent(playerId, source);
|
||||
game.replaceEvent(event);
|
||||
Player player = game.getPlayer(event.getTargetId());
|
||||
Player decidingPlayer = game.getPlayer(event.getPlayerId());
|
||||
if (player == null || decidingPlayer == null) {
|
||||
continue;
|
||||
}
|
||||
int voteCount = event.getExtraVotes() + event.getOptionalExtraVotes() + 1;
|
||||
for (int i = 0; i < voteCount; i++) {
|
||||
// Decision for extra choice goes from original player, not from deciding.
|
||||
// Rules from Illusion of Choice:
|
||||
// If another player controls Ballot Broker, that player first takes their “normal” vote
|
||||
// with you choosing the result, then that player decides whether they are taking the
|
||||
// additional vote. If there is an additional vote, you again choose the result.
|
||||
// (2016-08-23)
|
||||
|
||||
// Outcome.Benefit - AI must use extra vote all the time
|
||||
if (i > event.getExtraVotes() && !player.chooseUse(
|
||||
Outcome.Benefit, "Use an extra vote?", source, game
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String stepName = (i > 0 ? "extra step" : "step");
|
||||
String voteInfo = String.format("Vote, %s %d of %d", stepName, stepCurrent, stepTotal);
|
||||
T vote;
|
||||
if (!decidingPlayer.isHuman() && !decidingPlayer.isTestMode() && this.aiVoteHint != null) {
|
||||
// TODO: add isComputer after PR
|
||||
// ai choose
|
||||
vote = this.aiVoteHint.makeChoice(this, player, decidingPlayer, source, game);
|
||||
} else {
|
||||
// human choose
|
||||
vote = playerChoose(voteInfo, player, decidingPlayer, source, game);
|
||||
}
|
||||
if (vote == null) {
|
||||
continue;
|
||||
}
|
||||
String message = voteInfo + ": " + player.getName() + " voted for " + voteName(vote);
|
||||
if (!Objects.equals(player, decidingPlayer)) {
|
||||
message += " (chosen by " + decidingPlayer.getName() + ')';
|
||||
}
|
||||
game.informPlayers(message);
|
||||
this.playerMap.computeIfAbsent(playerId, x -> new ArrayList<>()).add(vote);
|
||||
}
|
||||
}
|
||||
|
||||
// show final results to players
|
||||
|
||||
Map<T, Integer> totalVotes = new LinkedHashMap<>();
|
||||
// fill by possible choices
|
||||
this.getPossibleVotes(source, game).forEach(vote -> {
|
||||
totalVotes.putIfAbsent(vote, 0);
|
||||
});
|
||||
// fill by real choices
|
||||
playerMap.entrySet()
|
||||
.stream()
|
||||
.flatMap(votesList -> votesList.getValue().stream())
|
||||
.forEach(vote -> {
|
||||
totalVotes.compute(vote, (u, i) -> i == null ? 1 : Integer.sum(i, 1));
|
||||
});
|
||||
|
||||
Set<T> winners = this.getMostVoted();
|
||||
String totalVotesStr = totalVotes.entrySet()
|
||||
.stream()
|
||||
.map(entry -> (winners.contains(entry.getKey()) ? " -win- " : " -lose- ") + voteName(entry.getKey()) + ": " + entry.getValue())
|
||||
.sorted()
|
||||
.collect(Collectors.joining("<br>"));
|
||||
game.informPlayers("Vote results:<br>" + totalVotesStr);
|
||||
|
||||
game.fireEvent(new VotedEvent(source, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return possible votes. Uses for info only (final results).
|
||||
*
|
||||
* @param source
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
protected abstract Set<T> getPossibleVotes(Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Choose dialog for voting. Another player can choose it (example: Illusion of Choice)
|
||||
*
|
||||
* @param player
|
||||
* @param decidingPlayer
|
||||
* @param source
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
protected abstract T playerChoose(String voteInfo, Player player, Player decidingPlayer, Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Show readable choice name
|
||||
*
|
||||
* @param vote
|
||||
* @return
|
||||
*/
|
||||
protected abstract String voteName(T vote);
|
||||
|
||||
public List<T> getVotes(UUID playerId) {
|
||||
return playerMap.computeIfAbsent(playerId, x -> new ArrayList<>());
|
||||
}
|
||||
|
||||
public int getVoteCount(T vote) {
|
||||
return playerMap
|
||||
.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(vote::equals)
|
||||
.mapToInt(x -> x ? 1 : 0)
|
||||
.sum();
|
||||
}
|
||||
|
||||
public List<UUID> getVotedFor(T vote) {
|
||||
return playerMap
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue() != null && entry.getValue().contains(vote))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Set<T> getMostVoted() {
|
||||
Map<T, Integer> map = new HashMap<>();
|
||||
playerMap
|
||||
.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(t -> map.compute(t, (s, i) -> i == null ? 1 : Integer.sum(i, 1)));
|
||||
int max = map.values().stream().mapToInt(x -> x).max().orElse(0);
|
||||
return map
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getValue() >= max)
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Set<UUID> getDidntVote(UUID playerId) {
|
||||
if (playerMap.computeIfAbsent(playerId, x -> new ArrayList<>()).isEmpty()) {
|
||||
return playerMap.keySet();
|
||||
}
|
||||
return playerMap
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getValue() != null && !e.getValue().isEmpty())
|
||||
.filter(e -> !e.getValue().stream().allMatch(playerMap.get(playerId)::contains))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
23
Mage/src/main/java/mage/choices/VoteHandlerAI.java
Normal file
23
Mage/src/main/java/mage/choices/VoteHandlerAI.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package mage.choices;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface VoteHandlerAI<T> {
|
||||
|
||||
/**
|
||||
* AI choosing hints for votes
|
||||
*
|
||||
* @param voteHandler voting handler for choosing
|
||||
* @param aiPlayer player who must choose
|
||||
* @param aiDecidingPlayer real player who make a choice (cab be changed by another effect, example: Illusion of Choice)
|
||||
* @param aiSource
|
||||
* @param aiGame
|
||||
*/
|
||||
T makeChoice(VoteHandler<T> voteHandler, Player aiPlayer, Player aiDecidingPlayer, Ability aiSource, Game aiGame);
|
||||
}
|
|
@ -437,6 +437,14 @@ public class GameEvent implements Serializable {
|
|||
//combat events
|
||||
COMBAT_DAMAGE_APPLIED,
|
||||
SELECTED_ATTACKER, SELECTED_BLOCKER,
|
||||
/* voting
|
||||
targetId player who voting
|
||||
sourceId sourceId of the effect doing the voting
|
||||
playerId player who deciding about voting, can be changed by replace events
|
||||
amount not used for this event
|
||||
flag not used for this event
|
||||
*/
|
||||
VOTE, VOTED,
|
||||
//custom events
|
||||
CUSTOM_EVENT
|
||||
}
|
||||
|
|
34
Mage/src/main/java/mage/game/events/VoteEvent.java
Normal file
34
Mage/src/main/java/mage/game/events/VoteEvent.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
package mage.game.events;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class VoteEvent extends GameEvent {
|
||||
|
||||
private int extraVotes = 0; // example: you get an additional vote
|
||||
private int optionalExtraVotes = 0; // example: you may vote an additional time
|
||||
|
||||
public VoteEvent(UUID playerId, Ability source) {
|
||||
super(EventType.VOTE, playerId, source, playerId);
|
||||
}
|
||||
|
||||
public void incrementExtraVotes() {
|
||||
extraVotes++;
|
||||
}
|
||||
|
||||
public void incrementOptionalExtraVotes() {
|
||||
optionalExtraVotes++;
|
||||
}
|
||||
|
||||
public int getExtraVotes() {
|
||||
return extraVotes;
|
||||
}
|
||||
|
||||
public int getOptionalExtraVotes() {
|
||||
return optionalExtraVotes;
|
||||
}
|
||||
}
|
24
Mage/src/main/java/mage/game/events/VotedEvent.java
Normal file
24
Mage/src/main/java/mage/game/events/VotedEvent.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
package mage.game.events;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.choices.VoteHandler;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class VotedEvent extends GameEvent {
|
||||
|
||||
private final VoteHandler voteHandler;
|
||||
|
||||
public VotedEvent(Ability source, VoteHandler voteHandler) {
|
||||
super(EventType.VOTED, source.getSourceId(), source, source.getControllerId());
|
||||
this.voteHandler = voteHandler;
|
||||
}
|
||||
|
||||
public Set<UUID> getDidntVote(UUID playerId) {
|
||||
return voteHandler.getDidntVote(playerId);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue