diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 186e811d94f..f420cecf3e8 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1,20 +1,11 @@ package forge.ai; -import java.security.InvalidParameterException; -import java.util.*; - -import forge.game.keyword.Keyword; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; - import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; - import forge.LobbyPlayer; import forge.ai.ability.ProtectAi; import forge.card.CardStateName; @@ -25,48 +16,26 @@ import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; import forge.deck.DeckSection; -import forge.game.Game; -import forge.game.GameEntity; -import forge.game.GameObject; -import forge.game.GameType; +import forge.game.*; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.ability.effects.CharmEffect; -import forge.game.card.Card; -import forge.game.card.CardCollection; -import forge.game.card.CardCollectionView; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates; +import forge.game.card.*; import forge.game.card.CardPredicates.Presets; -import forge.game.card.CardUtil; -import forge.game.card.CardView; -import forge.game.card.CounterType; import forge.game.combat.Combat; import forge.game.cost.Cost; import forge.game.cost.CostEnlist; import forge.game.cost.CostPart; import forge.game.cost.CostPartMana; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.mana.Mana; import forge.game.mana.ManaConversionMatrix; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; -import forge.game.player.DelayedReveal; -import forge.game.player.Player; -import forge.game.player.PlayerActionConfirmMode; -import forge.game.player.PlayerController; -import forge.game.player.PlayerView; +import forge.game.player.*; import forge.game.replacement.ReplacementEffect; -import forge.game.spellability.Ability; -import forge.game.spellability.AbilityStatic; -import forge.game.spellability.AbilitySub; -import forge.game.spellability.LandAbility; -import forge.game.spellability.OptionalCost; -import forge.game.spellability.OptionalCostValue; -import forge.game.spellability.Spell; -import forge.game.spellability.SpellAbility; -import forge.game.spellability.SpellAbilityStackInstance; -import forge.game.spellability.TargetChoices; +import forge.game.spellability.*; import forge.game.trigger.WrappedAbility; import forge.game.zone.ZoneType; import forge.item.PaperCard; @@ -75,6 +44,12 @@ import forge.util.ITriggerEvent; import forge.util.MyRandom; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import java.security.InvalidParameterException; +import java.util.*; /** @@ -606,6 +581,12 @@ public class PlayerControllerAi extends PlayerController { return Aggregates.random(sectors); } + @Override + public PlanarDice choosePDRollToIgnore(List rolls) { + //TODO create AI logic for this + return Aggregates.random(rolls); + } + @Override public boolean mulliganKeepHand(Player firstPlayer, int cardsToReturn) { return !ComputerUtil.wantMulligan(player, cardsToReturn); diff --git a/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java b/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java index 09d3859a5c5..ca686734f15 100644 --- a/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java @@ -134,7 +134,7 @@ public class RollPlanarDiceAi extends SpellAbilityAi { decideToRoll = false; } - if (ai.getGame().getPhaseHandler().getPlanarDiceRolledthisTurn() >= maxActivations) { + if (ai.getGame().getPhaseHandler().getPlanarDiceSpecialActionThisTurn() >= maxActivations) { decideToRoll = false; } diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index b4796df622c..5f4887dc52a 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -212,6 +212,13 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView, if (ArrayUtils.contains(valids, o)) { return true; } + } else if (o instanceof PlanarDice) { + for (String s : valids) { + PlanarDice valid = PlanarDice.smartValueOf(s); + if (((PlanarDice) o).name().equals(valid.name())) { + return true; + } + } } return false; diff --git a/forge-game/src/main/java/forge/game/GlobalRuleChange.java b/forge-game/src/main/java/forge/game/GlobalRuleChange.java index 8c7e3e535db..fbaff413308 100644 --- a/forge-game/src/main/java/forge/game/GlobalRuleChange.java +++ b/forge-game/src/main/java/forge/game/GlobalRuleChange.java @@ -28,9 +28,8 @@ public enum GlobalRuleChange { noNight ("It can't become night."), onlyOneBlocker ("No more than one creature can block each combat."), onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."), - onlyTwoBlockers ("No more than two creatures can block each combat."), - blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll."); - + onlyTwoBlockers ("No more than two creatures can block each combat."); + private final String ruleText; GlobalRuleChange(String text) { diff --git a/forge-game/src/main/java/forge/game/PlanarDice.java b/forge-game/src/main/java/forge/game/PlanarDice.java index d767ddb8c97..7c549469c00 100644 --- a/forge-game/src/main/java/forge/game/PlanarDice.java +++ b/forge-game/src/main/java/forge/game/PlanarDice.java @@ -1,15 +1,16 @@ package forge.game; -import java.util.Arrays; -import java.util.Map; - import com.google.common.collect.ImmutableList; - +import com.google.common.collect.Lists; import forge.game.ability.AbilityKey; -import forge.game.event.GameEventRollDie; import forge.game.player.Player; +import forge.game.replacement.ReplacementType; import forge.game.trigger.TriggerType; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + /** * Represents the planar dice for Planechase games. * @@ -20,36 +21,68 @@ public enum PlanarDice { Blank; public static PlanarDice roll(Player roller, PlanarDice riggedResult) { - PlanarDice res = Blank; - int i = forge.util.MyRandom.getRandom().nextInt(6); - // Play the die roll sound - roller.getGame().fireEvent(new GameEventRollDie()); - roller.roll(); - if (riggedResult != null) - res = riggedResult; - else if (i == 0) - res = Planeswalk; - else if (i == 1) - res = Chaos; + final Game game = roller.getGame(); + int rolls = 1; + int ignore = 0; + final Map repParams = AbilityKey.mapFromAffected(roller); + repParams.put(AbilityKey.Number, rolls); + repParams.put(AbilityKey.Ignore, ignore); + + switch (game.getReplacementHandler().run(ReplacementType.RollPlanarDice, repParams)) { + case NotReplaced: + break; + case Updated: { + rolls = (int) repParams.get(AbilityKey.Number); + ignore = (int) repParams.get(AbilityKey.Ignore); + break; + } + } + + List results = Lists.newArrayList(); + for (int r = 0; r < rolls; r++) { + PlanarDice thisRoll = Blank; + int i = forge.util.MyRandom.getRandom().nextInt(6); + roller.roll(); + if (riggedResult != null) + thisRoll = riggedResult; + else if (i == 0) + thisRoll = Planeswalk; + else if (i == 1) + thisRoll = Chaos; + results.add(thisRoll); + } + + for (int ig = 0; ig < ignore; ig++) { + results.remove(roller.getController().choosePDRollToIgnore(results)); + } + PlanarDice res = results.get(0); PlanarDice trigRes = res; - if(roller.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.blankIsChaos) - && res == Blank) - { - trigRes = Chaos; + final Map resRepParams = AbilityKey.mapFromAffected(roller); + resRepParams.put(AbilityKey.Result, res); + + switch (game.getReplacementHandler().run(ReplacementType.PlanarDiceResult, resRepParams)) { + case NotReplaced: + break; + case Updated: { + trigRes = (PlanarDice) resRepParams.get(AbilityKey.Result); + break; + } } Map runParams = AbilityKey.mapFromPlayer(roller); runParams.put(AbilityKey.Result, trigRes); - roller.getGame().getTriggerHandler().runTrigger(TriggerType.PlanarDice, runParams,false); + game.getTriggerHandler().runTrigger(TriggerType.PlanarDice, runParams,false); // Also run normal RolledDie and RolledDieOnce triggers - runParams = AbilityKey.mapFromPlayer(roller); - runParams.put(AbilityKey.Sides, 6); - runParams.put(AbilityKey.Result, 0); - roller.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false); + for (int r = 0; r < rolls; r++) { + runParams = AbilityKey.mapFromPlayer(roller); + runParams.put(AbilityKey.Sides, 6); + runParams.put(AbilityKey.Result, 0); + roller.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false); + } runParams = AbilityKey.mapFromPlayer(roller); runParams.put(AbilityKey.Sides, 6); diff --git a/forge-game/src/main/java/forge/game/ability/AbilityKey.java b/forge-game/src/main/java/forge/game/ability/AbilityKey.java index c86ad5e205a..9dd6cccaa9b 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityKey.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityKey.java @@ -74,6 +74,7 @@ public enum AbilityKey { FirstTime("FirstTime"), Fizzle("Fizzle"), FoundSearchingLibrary("FoundSearchingLibrary"), + Ignore("Ignore"), IsCombat("IsCombat"), // TODO confirm that this and IsCombatDamage can be merged IsCombatDamage("IsCombatDamage"), IsDamage("IsDamage"), diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 7b435dad1a8..a4b39e47d1f 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -2690,8 +2690,8 @@ public class AbilityUtils { return doXMath(game.getStack().getSpellsCastThisTurn().size() - 1, expr, c, ctb); } - if (sq[0].startsWith("RolledThisTurn")) { - return game.getPhaseHandler().getPlanarDiceRolledthisTurn(); + if (sq[0].startsWith("PlanarDiceSpecialActionThisTurn")) { + return game.getPhaseHandler().getPlanarDiceSpecialActionThisTurn(); } if (sq[0].contains("CardTypes")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReplaceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReplaceEffect.java index ef4b78cca1b..2eca5cf041d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReplaceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReplaceEffect.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import forge.game.GameObject; +import forge.game.PlanarDice; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -41,6 +42,8 @@ public class ReplaceEffect extends SpellAbilityEffect { if (list.size() > 0) { params.put(varName, list.get(0)); } + } else if ("PlanarDice".equals(type)) { + params.put(varName, PlanarDice.smartValueOf(varValue)); } else { params.put(varName, AbilityUtils.calculateAmount(card, varValue, sa)); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RollPlanarDiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RollPlanarDiceEffect.java index 45ad6b68eb1..61677c90bce 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RollPlanarDiceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RollPlanarDiceEffect.java @@ -19,17 +19,19 @@ public class RollPlanarDiceEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { - boolean countedTowardsCost = !sa.hasParam("NotCountedTowardsCost"); final Player activator = sa.getActivatingPlayer(); final Game game = activator.getGame(); - if(countedTowardsCost) { - game.getPhaseHandler().incPlanarDiceRolledthisTurn(); + if (game.getActivePlanes() == null) { // not a planechase game, nothing happens + return; + } + if (sa.hasParam("SpecialAction")) { + game.getPhaseHandler().incPlanarDiceSpecialActionThisTurn(); } - PlanarDice result = PlanarDice.roll(activator, null); // Play the die roll sound - activator.getGame().fireEvent(new GameEventRollDie()); - String message = Localizer.getInstance().getMessage("lblPlayerRolledResult", activator.getName(), result.toString()); + game.fireEvent(new GameEventRollDie()); + PlanarDice result = PlanarDice.roll(activator, null); + String message = Localizer.getInstance().getMessage("lblPlanarDiceResult", result.toString()); game.getAction().notifyOfValue(sa, activator, message, null); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 56110cfc56d..4e1cc6a80ee 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -341,23 +341,21 @@ public class CardFactory { } private static void buildPlaneAbilities(Card card) { - StringBuilder triggerSB = new StringBuilder(); - triggerSB.append("Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Secondary$ True | "); - triggerSB.append("TriggerDescription$ Whenever you roll Planeswalk, put this card on the bottom of its owner's planar deck face down, "); - triggerSB.append("then move the top card of your planar deck off that planar deck and turn it face up"); + String trigger = "Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Secondary$ True | " + + "TriggerDescription$ Whenever you roll the Planeswalker symbol on the planar die, planeswalk."; String rolledWalk = "DB$ Planeswalk"; - Trigger planesWalkTrigger = TriggerHandler.parseTrigger(triggerSB.toString(), card, true); + Trigger planesWalkTrigger = TriggerHandler.parseTrigger(trigger, card, true); planesWalkTrigger.setOverridingAbility(AbilityFactory.getAbility(rolledWalk, card)); card.addTrigger(planesWalkTrigger); - StringBuilder saSB = new StringBuilder(); - saSB.append("AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | Activator$ Player | ActivationZone$ Command | "); - saSB.append("SpellDescription$ Roll the planar dice. X is equal to the amount of times the planar die has been rolled this turn."); + String specialA = "ST$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | Activator$ Player | SpecialAction$ True" + + " | ActivationZone$ Command | SpellDescription$ Roll the planar dice. X is equal to the number of " + + "times you have previously taken this action this turn."; - SpellAbility planarRoll = AbilityFactory.getAbility(saSB.toString(), card); - planarRoll.setSVar("X", "Count$RolledThisTurn"); + SpellAbility planarRoll = AbilityFactory.getAbility(specialA, card); + planarRoll.setSVar("X", "Count$PlanarDiceSpecialActionThisTurn"); card.addSpellAbility(planarRoll); } diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index b1be209c6ff..b0e37ace58b 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -91,7 +91,7 @@ public class PhaseHandler implements java.io.Serializable { private int nUpkeepsThisGame = 0; private int nCombatsThisTurn = 0; private int nMain2sThisTurn = 0; - private int planarDiceRolledthisTurn = 0; + private int planarDiceSpecialActionThisTurn = 0; private transient Player playerTurn = null; private transient Player playerPreviousTurn = null; @@ -522,7 +522,7 @@ public class PhaseHandler implements java.io.Serializable { final Map runParams = AbilityKey.mapFromPlayer(playerTurn); game.getTriggerHandler().runTrigger(TriggerType.TurnBegin, runParams, false); } - planarDiceRolledthisTurn = 0; + planarDiceSpecialActionThisTurn = 0; // Play the End Turn sound game.fireEvent(new GameEventTurnEnded()); break; @@ -1212,11 +1212,11 @@ public class PhaseHandler implements java.io.Serializable { onPhaseBegin(); } - public int getPlanarDiceRolledthisTurn() { - return planarDiceRolledthisTurn; + public int getPlanarDiceSpecialActionThisTurn() { + return planarDiceSpecialActionThisTurn; } - public void incPlanarDiceRolledthisTurn() { - planarDiceRolledthisTurn++; + public void incPlanarDiceSpecialActionThisTurn() { + planarDiceSpecialActionThisTurn++; } public String debugPrintState(boolean hasPriority) { diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 3eb731d1a5f..c881605e79c 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import forge.game.*; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -19,13 +20,7 @@ import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; import forge.deck.DeckSection; -import forge.game.Game; -import forge.game.GameEntity; -import forge.game.GameObject; import forge.game.GameOutcome.AnteResult; -import forge.game.GameType; -import forge.game.GameView; -import forge.game.Match; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; @@ -192,6 +187,8 @@ public abstract class PlayerController { return chooseSector(assignee, ai, sectors); } + public abstract PlanarDice choosePDRollToIgnore(List rolls); + public abstract Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer); public abstract CardCollectionView getCardsToMulligan(Player firstPlayer); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacePlanarDiceResult.java b/forge-game/src/main/java/forge/game/replacement/ReplacePlanarDiceResult.java new file mode 100644 index 00000000000..6a9e938aca2 --- /dev/null +++ b/forge-game/src/main/java/forge/game/replacement/ReplacePlanarDiceResult.java @@ -0,0 +1,56 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.replacement; + +import forge.game.ability.AbilityKey; +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +import java.util.Map; + +public class ReplacePlanarDiceResult extends ReplacementEffect { + + /** + * Instantiates a new replace roll planar dice. + * + * @param params the params + * @param host the host + */ + public ReplacePlanarDiceResult(final Map params, final Card host, final boolean intrinsic) { + super(params, host, intrinsic); + } + + /* (non-Javadoc) + * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) + */ + @Override + public boolean canReplace(Map runParams) { + if (!matchesValidParam("ValidRoll", runParams.get(AbilityKey.Result))) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.Map, forge.card.spellability.SpellAbility) + */ + @Override + public void setReplacingObjects(Map runParams, SpellAbility sa) { + sa.setReplacingObject(AbilityKey.Result, runParams.get(AbilityKey.Result)); + } +} diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceRollPlanarDice.java b/forge-game/src/main/java/forge/game/replacement/ReplaceRollPlanarDice.java new file mode 100644 index 00000000000..e7cdebfde0e --- /dev/null +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceRollPlanarDice.java @@ -0,0 +1,57 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.replacement; + +import forge.game.ability.AbilityKey; +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +import java.util.Map; + +public class ReplaceRollPlanarDice extends ReplacementEffect { + + /** + * Instantiates a new replace roll planar dice. + * + * @param params the params + * @param host the host + */ + public ReplaceRollPlanarDice(final Map params, final Card host, final boolean intrinsic) { + super(params, host, intrinsic); + } + + /* (non-Javadoc) + * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) + */ + @Override + public boolean canReplace(Map runParams) { + if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Affected))) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.Map, forge.card.spellability.SpellAbility) + */ + @Override + public void setReplacingObjects(Map runParams, SpellAbility sa) { + sa.setReplacingObject(AbilityKey.Number, runParams.get(AbilityKey.Number)); + sa.setReplacingObject(AbilityKey.Ignore, runParams.get(AbilityKey.Ignore)); + } +} diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementType.java b/forge-game/src/main/java/forge/game/replacement/ReplacementType.java index 27d8e6cb73a..ae1cca0ff0f 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementType.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementType.java @@ -33,8 +33,10 @@ public enum ReplacementType { LoseMana(ReplaceLoseMana.class), Mill(ReplaceMill.class), Moved(ReplaceMoved.class), + PlanarDiceResult(ReplacePlanarDiceResult.class), ProduceMana(ReplaceProduceMana.class), Proliferate(ReplaceProliferate.class), + RollPlanarDice(ReplaceRollPlanarDice.class), Scry(ReplaceScry.class), SetInMotion(ReplaceSetInMotion.class), Surveil(ReplaceSurveil.class), diff --git a/forge-game/src/main/java/forge/util/MessageUtil.java b/forge-game/src/main/java/forge/util/MessageUtil.java index bbd640321bd..a0b83fd47dd 100644 --- a/forge-game/src/main/java/forge/util/MessageUtil.java +++ b/forge-game/src/main/java/forge/util/MessageUtil.java @@ -68,6 +68,7 @@ public class MessageUtil { case Protection: return Localizer.getInstance().getMessage("lblPlayerChooseValue", choser, value); case RollDice: + case RollPlanarDice: case PutCounter:// For Clay Golem cost text return value; case Vote: diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index d9d649f6492..4741ce0ba17 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -20,10 +20,7 @@ import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; import forge.deck.DeckSection; -import forge.game.Game; -import forge.game.GameEntity; -import forge.game.GameObject; -import forge.game.GameType; +import forge.game.*; import forge.game.ability.AbilityUtils; import forge.game.card.*; import forge.game.combat.Combat; @@ -503,6 +500,11 @@ public class PlayerControllerForTests extends PlayerController { return chooseItem(sectors); } + @Override + public PlanarDice choosePDRollToIgnore(List rolls) { + return Aggregates.random(rolls); + } + @Override public Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer) { return chooseItem(options); diff --git a/forge-gui/res/cardsfolder/c/chaotic_aether.txt b/forge-gui/res/cardsfolder/c/chaotic_aether.txt index d021a86a7f3..ecfb11a932b 100644 --- a/forge-gui/res/cardsfolder/c/chaotic_aether.txt +++ b/forge-gui/res/cardsfolder/c/chaotic_aether.txt @@ -1,10 +1,11 @@ Name:Chaotic Aether ManaCost:no cost Types:Phenomenon -T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ Aether | TriggerDescription$ When you encounter CARDNAME, each blank roll of the planar dice is a {CHAOS} roll until a player planeswalks away from a plane. (Then planeswalk away from this phenomenon) -SVar:Aether:DB$ Effect | Name$ Chaotic Aether Effect | StaticAbilities$ STBlankIsChaos | Triggers$ TPWAway | SubAbility$ PWAway -SVar:PWAway:DB$ Planeswalk | Cost$ 0 -SVar:STBlankIsChaos:Mode$ Continuous | EffectZone$ Command | GlobalRule$ Each blank roll of the planar dice is a {CHAOS} roll. -SVar:TPWAway:Mode$ PlaneswalkedFrom | ValidCard$ Plane | Execute$ ExileSelf | Static$ True | TriggerDescription$ Until a player planeswalks away from a plane, each blank roll of the planar dice is a {CHAOS} roll. +T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ Aether | TriggerDescription$ When you encounter CARDNAME, each blank roll of the planar dice is a {CHAOS} roll until a player planeswalks away from a plane. (Then planeswalk away from this phenomenon.) +SVar:Aether:DB$ Effect | ReplacementEffects$ BlankIsChaos | Triggers$ TPWAway | SubAbility$ PWAway +SVar:PWAway:DB$ Planeswalk +SVar:BlankIsChaos:Event$ PlanarDiceResult | ValidRoll$ Blank | ReplaceWith$ REChaos | Description$ Each blank roll of the planar die is a {CHAOS} roll until a player planeswalks away from a plane. +SVar:REChaos:DB$ ReplaceEffect | VarName$ Result | VarValue$ Chaos | VarType$ PlanarDice +SVar:TPWAway:Mode$ PlaneswalkedFrom | ValidCard$ Plane | Execute$ ExileSelf | Static$ True SVar:ExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile -Oracle:When you encounter Chaotic Aether, each blank roll of the planar die is a CHAOS roll until a player planeswalks away from a plane. (Then planeswalk away from this phenomenon.) +Oracle:When you encounter Chaotic Aether, each blank roll of the planar die is a {CHAOS} roll until a player planeswalks away from a plane. (Then planeswalk away from this phenomenon.) diff --git a/forge-gui/res/cardsfolder/k/krosa.txt b/forge-gui/res/cardsfolder/k/krosa.txt index 075896c07ff..4db4f3f0550 100644 --- a/forge-gui/res/cardsfolder/k/krosa.txt +++ b/forge-gui/res/cardsfolder/k/krosa.txt @@ -1,7 +1,7 @@ Name:Krosa ManaCost:no cost Types:Plane Dominaria -S:Mode$ Continuous | EffectZone$ Command | Affected$ Creature | AddPower$ 2 | AddToughness$ 2 | Description$ All Creatures get +2/+2. +S:Mode$ Continuous | EffectZone$ Command | Affected$ Creature | AddPower$ 2 | AddToughness$ 2 | Description$ All creatures get +2/+2. T:Mode$ PlanarDice | Result$ Chaos | OptionalDecider$ You | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, you may add {W}{U}{B}{R}{G}. SVar:RolledChaos:DB$ Mana | Produced$ W U B R G SVar:AIRollPlanarDieParams:Mode$ Always | RollInMain1$ True diff --git a/forge-gui/res/cardsfolder/upcoming/ichor_elixir.txt b/forge-gui/res/cardsfolder/upcoming/ichor_elixir.txt new file mode 100644 index 00000000000..1f9bf0bc560 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ichor_elixir.txt @@ -0,0 +1,8 @@ +Name:Ichor Elixir +ManaCost:4 +Types:Artifact +R:Event$ RollPlanarDice | ValidPlayer$ You | ReplaceWith$ PlusRoll | Description$ If you would roll one or more planar dice, instead roll that many planar dice plus one and ignore one. +SVar:PlusRoll:DB$ ReplaceEffect | VarName$ Number | VarValue$ ReplaceCount$Number/Plus.1 | SubAbility$ IgnoreRoll +SVar:IgnoreRoll:DB$ ReplaceEffect | VarName$ Ignore | VarValue$ ReplaceCount$Ignore/Plus.1 +A:AB$ Mana | Cost$ T | Produced$ C | Amount$ 2 | SpellDescription$ Add {C}{C}. +Oracle:If you would roll one or more planar dice, instead roll that many planar dice plus one and ignore one.\n{T}: Add {C}{C}. diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index 18ce1365c42..a1a87f77eda 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1351,6 +1351,7 @@ lblLibrary=Bibliothek lblGraveyard=Friedhof lblAssignSectorCreature=Weise {0} einem Bereich zu lblChooseSectorEffect=Wähle Bereich +lblChooseRollIgnore=Choose a roll to ignore lblTop=Oben drauf lblBottom=Unten drunter lblNColorManaFromCard={0} {1} Mana von {2} @@ -2042,8 +2043,10 @@ lblDoyouWantShuffleTheLibrary=Möchtest du deine Bibliothek mischen? lblDoYouWantRepeatProcessAgain=Möchtest du den Vorgang wiederholen? #RevealHandEffect.java lblDoYouWantRevealYourHand=Möchtest du deine Handkarten offen vorzeigen? +#RollDiceEffect.java +lblPlayerRolledResult={0} würfelt {1} #RollPlanarDiceEffect.java -lblPlayerRolledResult={0} würfelte {1}? +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=Möchtest du die Echokosten zahlen lblPayEcho=Zahle Echokosten diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 0d78c2880c2..7981bf21a17 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -1357,6 +1357,7 @@ lblLibrary=Library lblGraveyard=Graveyard lblAssignSectorCreature=Assign {0} to a sector lblChooseSectorEffect=Choose a sector +lblChooseRollIgnore=Choose a roll to ignore lblTop=Top lblBottom=Bottom lblNColorManaFromCard={0} {1} mana from {2} @@ -2048,8 +2049,10 @@ lblDoyouWantShuffleTheLibrary=Do you want to shuffle the library? lblDoYouWantRepeatProcessAgain=Do you want to repeat this process again? #RevealHandEffect.java lblDoYouWantRevealYourHand=Do you want to reveal your hand? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0} rolled {1} +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=Do you want to pay Echo lblPayEcho=Pay Echo diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 5690bd39e1b..df8348733f2 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -1352,6 +1352,7 @@ lblLibrary=Biblioteca lblGraveyard=Cementerio lblAssignSectorCreature=Assign {0} to a sector lblChooseSectorEffect=Choose a sector +lblChooseRollIgnore=Choose a roll to ignore lblTop=Superior lblBottom=Fondo lblNColorManaFromCard={0} {1} maná de {2} @@ -2043,8 +2044,10 @@ lblDoyouWantShuffleTheLibrary=¿Quieres barajar la biblioteca? lblDoYouWantRepeatProcessAgain=¿Quiere repetir este proceso de nuevo? #RevealHandEffect.java lblDoYouWantRevealYourHand=¿Quieres descubrir tu mano? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0} lanzó {1} +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=¿Quieres pagar Eco lblPayEcho=Pagar Eco diff --git a/forge-gui/res/languages/fr-FR.properties b/forge-gui/res/languages/fr-FR.properties index ac8542fb386..c5de9a17174 100644 --- a/forge-gui/res/languages/fr-FR.properties +++ b/forge-gui/res/languages/fr-FR.properties @@ -1294,7 +1294,7 @@ lblError=Erreur lblWinGame=Gagner la partie lblSetLifetoWhat=Mettre la vie à quoi ? lblSetLifeforWhichPlayer=Définir la vie pour quel joueur -kglChoosePermanentstoTap=Choisir les permanents à engager +lblChoosePermanentstoTap=Choisir les permanents à engager lblChoosePermanentstoUntap=Choisir les permanents à dégager lblWhichTypeofCounter=Quel type de marqueur ? lblHowManyCounters=Combien de marqueurs ? @@ -1311,7 +1311,7 @@ lblSelectOrderForSimultaneousAbilities=Sélectionner l''ordre pour les capacité lblReorderSimultaneousAbilities=Réorganiser les capacités simultanées lblResolveFirst=Résoudre en premier lblMoveCardstoToporBbottomofLibrary=Déplacer les cartes en haut ou en bas de la bibliothèque -lblSelectCardsToBeOutOnTheBottomOfYourLibrary=S\u00e9lectionnez les cartes \u00e0 mettre en bas de votre biblioth\u00e8que +lblSelectCardsToBeOutOnTheBottomOfYourLibrary=Sélectionnez les cartes à mettre en bas de votre bibliothèque lblCardsToPutOnTheBottom=Cartes à mettre en bas lblArrangeCardsToBePutOnTopOfYourLibrary=Disposer les cartes à mettre au-dessus de votre bibliothèque lblTopOfLibrary=Haut de la bibliothèque @@ -1353,18 +1353,20 @@ lblThereNoCardInPlayerZone=Il n''y a pas de cartes dans {0} {1} lblPutCardsOnTheTopLibraryOrGraveyard=Placer {0} au-dessus de la bibliothèque ou du cimetière ? lblLibrary=Bibliothèque lblGraveyard=Cimetière -lblHaut=Haut -lblBas=Bas +lblAssignSectorCreature=Assign {0} to a sectorlblChooseSectorEffect=Choose a sector +lblChooseRollIgnore=Choose a roll to ignore +lblTop=Haut +lblBottom=Bas lblNColorManaFromCard={0} {1} mana de {2} lblPayManaFromManaPool=Payer le mana du pool de mana lblChooseATargetType=Choisir un type {0} lblUntap=Dégager lblOdds=Cote -lblPairs=Pairs +lblEvens=Pairs lblLeaveTapped=Laisser tapé lblUntapAndSkipThisTurn=Dégager (et sauter ce tour) -lblGauche=Gauche -lblDroite=Droite +lblLeft=Gauche +lblRight=Droite lblAddCounter=Ajouter un marqueur lblRemoveCounter=Supprimer le marqueur lblWinTheFlip=gagner le flip @@ -2045,8 +2047,10 @@ lblDoyouWantShuffleTheLibrary=Voulez-vous mélanger la bibliothèque ? lblDoYouWantRepeatProcessAgain=Voulez-vous répéter ce processus à nouveau ? #RevealHandEffect.java lblDoYouWantRevealYourHand=Voulez-vous révéler votre main ? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0} a lancé {1} +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=Voulez-vous payer Echo lblPayEcho=Écho de paiement diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index 50ba4593e23..aa848adf02f 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -1352,6 +1352,7 @@ lblLibrary=Grimorio lblGraveyard=Cimitero lblAssignSectorCreature=Assign {0} to a sector lblChooseSectorEffect=Choose a sector +lblChooseRollIgnore=Choose a roll to ignore lblTop=Cima lblBottom=Fondo lblNColorManaFromCard={0} {1} mana da {2} @@ -2042,8 +2043,10 @@ lblDoyouWantShuffleTheLibrary=Vuoi mescolare il grimorio? lblDoYouWantRepeatProcessAgain=Vuoi ripetere nuovamente questo procedimento? #RevealHandEffect.java lblDoYouWantRevealYourHand=Vuoi rivelare la tua mano? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0} ha ottenuto {1} col dado +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=Vuoi pagare Eco lblPayEcho=Paga Eco diff --git a/forge-gui/res/languages/ja-JP.properties b/forge-gui/res/languages/ja-JP.properties index b7a029458d8..661db100946 100644 --- a/forge-gui/res/languages/ja-JP.properties +++ b/forge-gui/res/languages/ja-JP.properties @@ -1353,6 +1353,7 @@ lblLibrary=ライブラリー lblGraveyard=墓地 lblAssignSectorCreature=Assign {0} to a sector lblChooseSectorEffect=Choose a sector +lblChooseRollIgnore=Choose a roll to ignore lblTop=タップ lblBottom=ボトム lblNColorManaFromCard={2}から {0} {1} のマナ @@ -2042,8 +2043,10 @@ lblDoyouWantShuffleTheLibrary=ライブラリーをシャッフルしますか lblDoYouWantRepeatProcessAgain=この手順をもう一度しますか? #RevealHandEffect.java lblDoYouWantRevealYourHand=手札を公開しますか? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0}が {1}をロールしました +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=エコーコストを支払いますか: lblPayEcho=エコーコストを支払います diff --git a/forge-gui/res/languages/pt-BR.properties b/forge-gui/res/languages/pt-BR.properties index db94200c18e..c6d36de1dca 100644 --- a/forge-gui/res/languages/pt-BR.properties +++ b/forge-gui/res/languages/pt-BR.properties @@ -1383,6 +1383,7 @@ lblLibrary=Grimório lblGraveyard=Cemitério lblAssignSectorCreature=Assign {0} to a sector lblChooseSectorEffect=Choose a sector +lblChooseRollIgnore=Choose a roll to ignore lblTop=Topo lblBottom=Fundo lblNColorManaFromCard={0} {1} mana de {2} @@ -2104,8 +2105,10 @@ lblDoyouWantShuffleTheLibrary=Quer embaralhar o grimório? lblDoYouWantRepeatProcessAgain=Quer repetir este processo novamente? #RevealHandEffect.java lblDoYouWantRevealYourHand=Você quer revelar sua mão? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0} tirou {1} +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=Quer pagar pelo Eco lblPayEcho=Pagar Eco diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 6b947171c4e..ea3a0dd2b05 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -1354,6 +1354,7 @@ lblLibrary=牌库 lblGraveyard=坟场 lblAssignSectorCreature=将{0}分配给一部分 lblChooseSectorEffect=选择一部分 +lblChooseRollIgnore=Choose a roll to ignore lblTop=顶 lblBottom=底 lblNColorManaFromCard={2}产{0}个{1}法术力 @@ -2046,8 +2047,10 @@ lblDoyouWantShuffleTheLibrary=你想要洗这个牌库吗? lblDoYouWantRepeatProcessAgain=你是否想再次重复这个过程? #RevealHandEffect.java lblDoYouWantRevealYourHand=你想展示你的手牌吗? -#RollPlanarDiceEffect.java +#RollDiceEffect.java lblPlayerRolledResult={0}掷骰结果为{1} +#RollPlanarDiceEffect.java +lblPlanarDiceResult=Planar dice result: {0} #SacrificeEffect.java lblDoYouWantPayEcho=你想支付返响费用 lblPayEcho=支付返响费用 diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 2193ab759f1..53e7386d85e 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1391,6 +1391,11 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return getGui().one(prompt, sectors); } + @Override + public PlanarDice choosePDRollToIgnore(List rolls) { + return getGui().one(Localizer.getInstance().getMessage("lblChooseRollIgnore"), rolls); + } + @Override public Object vote(final SpellAbility sa, final String prompt, final List options, final ListMultimap votes, Player forPlayer) {