diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml index 3f176653a65..b8d21660c54 100644 --- a/forge-ai/pom.xml +++ b/forge-ai/pom.xml @@ -6,7 +6,7 @@ forge forge - 1.6.33-SNAPSHOT + 1.6.34-SNAPSHOT forge-ai diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index c9eb6634f57..bec6e19c4d6 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -2323,7 +2323,7 @@ public class ComputerUtil { return chosen; } - public static Object vote(Player ai, List options, SpellAbility sa, Multimap votes) { + public static Object vote(Player ai, List options, SpellAbility sa, Multimap votes, Player forPlayer) { final Card source = sa.getHostCard(); final Player controller = source.getController(); final Game game = controller.getGame(); diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 04a9f303832..56d592d5b8e 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -513,8 +513,8 @@ public class PlayerControllerAi extends PlayerController { } @Override - public Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes) { - return ComputerUtil.vote(player, options, sa, votes); + public Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer) { + return ComputerUtil.vote(player, options, sa, votes, forPlayer); } @Override diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 27700e2455b..f5f4435c491 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -1295,6 +1295,26 @@ public class SpecialCardAi { } } + // Timmerian Fiends + public static class TimmerianFiends { + public static boolean consider(final Player ai, final SpellAbility sa) { + final Card targeted = sa.getParentTargetingCard().getTargetCard(); + if (targeted == null) { + return false; + } + + if (targeted.isCreature()) { + if (ComputerUtil.aiLifeInDanger(ai, true, 0)) { + return true; // do it, hoping to save a valuable potential blocker etc. + } + return ComputerUtilCard.evaluateCreature(targeted) >= 200; // might need tweaking + } else { + // TODO: this currently compares purely by CMC. To be somehow improved, especially for stuff like the Power Nine etc. + return ComputerUtilCard.evaluatePermanentList(new CardCollection(targeted)) >= 3; + } + } + } + // Volrath's Shapeshifter public static class VolrathsShapeshifter { public static boolean consider(final Player ai, final SpellAbility sa) { diff --git a/forge-ai/src/main/java/forge/ai/ability/MillAi.java b/forge-ai/src/main/java/forge/ai/ability/MillAi.java index fba432c9d48..52f0e8cda25 100644 --- a/forge-ai/src/main/java/forge/ai/ability/MillAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/MillAi.java @@ -1,15 +1,10 @@ package forge.ai.ability; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - import com.google.common.collect.Lists; import com.google.common.collect.Maps; - import forge.ai.ComputerUtil; import forge.ai.ComputerUtilMana; +import forge.ai.SpecialCardAi; import forge.ai.SpellAbilityAi; import forge.game.ability.AbilityUtils; import forge.game.card.Card; @@ -24,6 +19,11 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + public class MillAi extends SpellAbilityAi { @Override @@ -196,6 +196,10 @@ public class MillAi extends SpellAbilityAi { */ @Override public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + if ("TimmerianFiends".equals(sa.getParam("AILogic"))) { + return SpecialCardAi.TimmerianFiends.consider(player, sa); + } + return true; } diff --git a/forge-ai/src/main/java/forge/ai/ability/VoteAi.java b/forge-ai/src/main/java/forge/ai/ability/VoteAi.java index 58aab03ecbb..33d23dd3b22 100644 --- a/forge-ai/src/main/java/forge/ai/ability/VoteAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/VoteAi.java @@ -46,6 +46,12 @@ public class VoteAi extends SpellAbilityAi { @Override public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map params) { + if (params.containsKey("Voter")) { + Player p = (Player)params.get("Voter"); + if (p.isOpponentOf(player)) { + return min; + } + } if (sa.getActivatingPlayer().isOpponentOf(player)) { return min; } diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 9c13b0d69aa..763d968dc05 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -6,7 +6,7 @@ forge forge - 1.6.33-SNAPSHOT + 1.6.34-SNAPSHOT forge-core diff --git a/forge-core/src/main/java/forge/item/IPaperCard.java b/forge-core/src/main/java/forge/item/IPaperCard.java index 0c14064e059..53ea1d066e4 100644 --- a/forge-core/src/main/java/forge/item/IPaperCard.java +++ b/forge-core/src/main/java/forge/item/IPaperCard.java @@ -6,6 +6,7 @@ import forge.card.CardRarity; import forge.card.CardRules; import forge.card.CardType.CoreType; import forge.card.MagicColor; +import forge.util.PredicateCard; import forge.util.PredicateString; import org.apache.commons.lang3.StringUtils; @@ -60,6 +61,8 @@ public interface IPaperCard extends InventoryItem { return new PredicateNames(what); } + public static PredicateCards cards(final List what) { return new PredicateCards(what); } + private static final class PredicateColor implements Predicate { private final byte operand; @@ -161,6 +164,25 @@ public interface IPaperCard extends InventoryItem { } } + private static final class PredicateCards extends PredicateCard { + private final List operand; + + @Override + public boolean apply(final PaperCard card) { + for (final PaperCard element : this.operand) { + if (this.op(card, element)) { + return true; + } + } + return false; + } + + private PredicateCards(final List operand) { + super(StringOp.EQUALS); + this.operand = operand; + } + } + /** * Pre-built predicates are stored here to allow their re-usage and * easier access from code. diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index 896e8d6de6b..833ee7e6bf7 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -566,12 +566,8 @@ public class BoosterGenerator { toAdd = IPaperCard.Predicates.printedInSets(sets); } else if (operator.startsWith("fromSheet(") && invert) { String sheetName = StringUtils.strip(operator.substring(9), "()\" "); - Iterable src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList(); - List cardNames = Lists.newArrayList(); - for (PaperCard card : src) { - cardNames.add(card.getName()); - } - toAdd = IPaperCard.Predicates.names(Lists.newArrayList(cardNames)); + Iterable cards = StaticData.instance().getPrintSheets().get(sheetName).toFlatList(); + toAdd = IPaperCard.Predicates.cards(Lists.newArrayList(cards)); } if (toAdd == null) { diff --git a/forge-core/src/main/java/forge/util/PredicateCard.java b/forge-core/src/main/java/forge/util/PredicateCard.java new file mode 100644 index 00000000000..308b34f68ca --- /dev/null +++ b/forge-core/src/main/java/forge/util/PredicateCard.java @@ -0,0 +1,83 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2020 Jamin W. Collins + * + * 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.util; + +import com.google.common.base.Predicate; +import forge.item.PaperCard; + +/** + * Special predicate class to perform string operations. + * + * @param + * the generic type + */ +public abstract class PredicateCard implements Predicate { + /** Possible operators for string operands. */ + public enum StringOp { + /** The EQUALS. */ + EQUALS, + } + + /** The operator. */ + private final StringOp operator; + + /** + * Op. + * + * @param op1 + * the op1 + * @param op2 + * the op2 + * @return true, if successful + */ + protected final boolean op(final PaperCard op1, final PaperCard op2) { + switch (this.getOperator()) { + case EQUALS: + return op1.equals(op2); + default: + return false; + } + } + + /** + * Instantiates a new predicate string. + * + * @param operator + * the operator + */ + public PredicateCard(final StringOp operator) { + this.operator = operator; + } + + /** + * @return the operator + */ + public StringOp getOperator() { + return operator; + } + + public static PredicateCard equals(final PaperCard what) { + return new PredicateCard(StringOp.EQUALS) { + @Override + public boolean apply(PaperCard subject) { + return op(subject, what); + } + }; + } + +} diff --git a/forge-game/pom.xml b/forge-game/pom.xml index 117c4abbebd..e9cf2c327e8 100644 --- a/forge-game/pom.xml +++ b/forge-game/pom.xml @@ -6,7 +6,7 @@ forge forge - 1.6.33-SNAPSHOT + 1.6.34-SNAPSHOT forge-game diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 849e2de4a0d..2a49bae9d59 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -914,4 +914,17 @@ public class Game { } return false; } + + public Player getControlVote() { + Player result = null; + long maxValue = 0; + for (Player p : getPlayers()) { + Long v = p.getHighestControlVote(); + if (v != null && v > maxValue) { + maxValue = v; + result = p; + } + } + return result; + } } diff --git a/forge-game/src/main/java/forge/game/StaticEffect.java b/forge-game/src/main/java/forge/game/StaticEffect.java index dc9d51094a0..d6ce1813d7e 100644 --- a/forge-game/src/main/java/forge/game/StaticEffect.java +++ b/forge-game/src/main/java/forge/game/StaticEffect.java @@ -6,12 +6,12 @@ * 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 . */ @@ -37,7 +37,7 @@ import com.google.common.collect.Maps; *

* StaticEffect class. *

- * + * * @author Forge * @version $Id$ */ @@ -72,7 +72,7 @@ public class StaticEffect { /** * setTimestamp TODO Write javadoc for this method. - * + * * @param t * a long */ @@ -82,7 +82,7 @@ public class StaticEffect { /** * getTimestamp. TODO Write javadoc for this method. - * + * * @return a long */ public final long getTimestamp() { @@ -93,7 +93,7 @@ public class StaticEffect { *

* Getter for the field source. *

- * + * * @return a {@link forge.game.card.Card} object. */ public final Card getSource() { @@ -104,7 +104,7 @@ public class StaticEffect { *

* Getter for the field affectedCards. *

- * + * * @return a {@link forge.CardList} object. */ public final CardCollectionView getAffectedCards() { @@ -115,7 +115,7 @@ public class StaticEffect { *

* Setter for the field affectedCards. *

- * + * * @param list * a {@link forge.CardList} object. */ @@ -125,7 +125,7 @@ public class StaticEffect { /** * Gets the affected players. - * + * * @return the affected players */ public final List getAffectedPlayers() { @@ -134,7 +134,7 @@ public class StaticEffect { /** * Sets the affected players. - * + * * @param list * the new affected players */ @@ -144,7 +144,7 @@ public class StaticEffect { /** * setParams. TODO Write javadoc for this method. - * + * * @param params * a HashMap */ @@ -154,7 +154,7 @@ public class StaticEffect { /** * Gets the params. - * + * * @return the params */ public final Map getParams() { @@ -171,13 +171,12 @@ public class StaticEffect { /** * Undo everything that was changed by this effect. - * + * * @return a {@link CardCollectionView} of all affected cards. */ final CardCollectionView remove() { final CardCollectionView affectedCards = getAffectedCards(); final List affectedPlayers = getAffectedPlayers(); - //final Map params = getParams(); String changeColorWordsTo = null; @@ -245,6 +244,10 @@ public class StaticEffect { p.removeMaxLandPlays(getTimestamp()); p.removeMaxLandPlaysInfinite(getTimestamp()); + + p.removeControlVote(getTimestamp()); + p.removeAdditionalVote(getTimestamp()); + p.removeAdditionalOptionalVote(getTimestamp()); } // modify the affected card diff --git a/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java index 442fe3c5340..2595c792dec 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java @@ -70,7 +70,7 @@ public class DebuffEffect extends SpellAbilityEffect { for (final Card tgtC : getTargetCards(sa)) { final List addedKW = Lists.newArrayList(); final List removedKW = Lists.newArrayList(); - if (tgtC.isInPlay() && tgtC.canBeTargetedBy(sa)) { + if (tgtC.isInPlay() && (!sa.usesTargeting() || tgtC.canBeTargetedBy(sa))) { if (sa.hasParam("AllSuffixKeywords")) { String suffix = sa.getParam("AllSuffixKeywords"); for (final KeywordInterface kw : tgtC.getKeywords()) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/VoteEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VoteEffect.java index 769e758388c..cc572a9c5fd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/VoteEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/VoteEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -9,9 +10,9 @@ import forge.game.ability.AbilityKey; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import forge.game.Game; import forge.game.ability.AbilityFactory; @@ -19,10 +20,7 @@ import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardLists; -import forge.game.card.CardPredicates; import forge.game.player.Player; -import forge.game.player.PlayerCollection; -import forge.game.player.PlayerPredicates; import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; @@ -67,33 +65,29 @@ public class VoteEffect extends SpellAbilityEffect { return; } - // starting with the activator - int pSize = tgtPlayers.size(); Player activator = sa.getActivatingPlayer(); - while (tgtPlayers.contains(activator) && !activator.equals(Iterables.getFirst(tgtPlayers, null))) { - tgtPlayers.add(pSize - 1, tgtPlayers.remove(0)); + + // starting with the activator + int aidx = tgtPlayers.indexOf(activator); + if (aidx != -1) { + Collections.rotate(tgtPlayers, -aidx); } + ListMultimap votes = ArrayListMultimap.create(); - Player voter = null; - - PlayerCollection voters = game.getPlayers().filter(PlayerPredicates.hasKeyword("You choose how each player votes this turn.")); - - if (voters.size() > 1) { - List illusions = CardLists.filter(voters.getCardsIn(ZoneType.Command), CardPredicates.nameEquals("Illusion of Choice Effect")); - voter = Collections.max(illusions, CardPredicates.compareByTimestamp()).getController(); - } else if (voters.size() == 1) { - voter = voters.get(0); - } + Player voter = game.getControlVote(); for (final Player p : tgtPlayers) { - int voteAmount = p.getKeywords().getAmount("You get an additional vote.") + 1; - int optionalVotes = p.getKeywords().getAmount("You may vote an additional time."); - voteAmount += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyAdditionalVotesDoYouWant"), 0, optionalVotes); + int voteAmount = p.getAdditionalVotesAmount() + 1; + int optionalVotes = p.getAdditionalOptionalVotesAmount(); Player realVoter = voter == null ? p : voter; + Map params = Maps.newHashMap(); + params.put("Voter", realVoter); + voteAmount += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyAdditionalVotesDoYouWant"), 0, optionalVotes, params); + for (int i = 0; i < voteAmount; i++) { - Object result = realVoter.getController().vote(sa, host + Localizer.getInstance().getMessage("lblVote") + ":", voteType, votes); + Object result = realVoter.getController().vote(sa, host + Localizer.getInstance().getMessage("lblVote") + ":", voteType, votes, p); votes.put(result, p); host.getGame().getAction().nofityOfValue(sa, p, result + "\r\n" + Localizer.getInstance().getMessage("lblCurrentVote") + ":" + votes, p); @@ -104,34 +98,49 @@ public class VoteEffect extends SpellAbilityEffect { runParams.put(AbilityKey.AllVotes, votes); game.getTriggerHandler().runTrigger(TriggerType.Vote, runParams, false); - List subAbs = Lists.newArrayList(); - final List mostVotes = getMostVotes(votes); - if (sa.hasParam("Tied") && mostVotes.size() > 1) { - subAbs.add(sa.getParam("Tied")); - } else if (sa.hasParam("VoteSubAbility")) { - for (final Object o : mostVotes) { - host.addRemembered(o); - } - subAbs.add(sa.getParam("VoteSubAbility")); - } else { - for (Object type : mostVotes) { - subAbs.add(sa.getParam("Vote" + type.toString())); - } - } - if (sa.hasParam("StoreVoteNum")) { - for (final Object type : voteType) { - host.setSVar("VoteNum" + type, "Number$" + votes.get(type).size()); - } - } else { - for (final String subAb : subAbs) { - final SpellAbility action = AbilityFactory.getAbility(host.getSVar(subAb), host); + if (sa.hasParam("EachVote")) { + for (Map.Entry> e : votes.asMap().entrySet()) { + final SpellAbility action = AbilityFactory.getAbility(host, sa.getParam("Vote" + e.getKey().toString())); + action.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) action).setParent(sa); - AbilityUtils.resolve(action); + + for (Player p : e.getValue()) { + host.addRemembered(p); + AbilityUtils.resolve(action); + host.removeRemembered(p); + } + } + } else { + List subAbs = Lists.newArrayList(); + final List mostVotes = getMostVotes(votes); + if (sa.hasParam("Tied") && mostVotes.size() > 1) { + subAbs.add(sa.getParam("Tied")); + } else if (sa.hasParam("VoteSubAbility")) { + for (final Object o : mostVotes) { + host.addRemembered(o); + } + subAbs.add(sa.getParam("VoteSubAbility")); + } else { + for (Object type : mostVotes) { + subAbs.add(sa.getParam("Vote" + type.toString())); + } + } + if (sa.hasParam("StoreVoteNum")) { + for (final Object type : voteType) { + host.setSVar("VoteNum" + type, "Number$" + votes.get(type).size()); + } + } else { + for (final String subAb : subAbs) { + final SpellAbility action = AbilityFactory.getAbility(host, subAb); + action.setActivatingPlayer(sa.getActivatingPlayer()); + ((AbilitySub) action).setParent(sa); + AbilityUtils.resolve(action); + } + } + if (sa.hasParam("VoteSubAbility")) { + host.clearRemembered(); } - } - if (sa.hasParam("VoteSubAbility")) { - host.clearRemembered(); } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 1d278409976..f2ab81b38b9 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -3938,7 +3938,9 @@ public class Card extends GameEntity implements Comparable { keywordsGrantedByTextChanges.add(newKw); } } - addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, false, timestamp, true); + if (!addKeywords.isEmpty() || !removeKeywords.isEmpty()) { + addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, false, timestamp, true); + } } private void updateKeywordsOnRemoveChangedText(final KeywordsChange k) { diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 93e53b59b3d..f21c28a6d60 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -162,6 +162,10 @@ public class Player extends GameEntity implements Comparable { private Card blessingEffect = null; private Card keywordEffect = null; + private Map additionalVotes = Maps.newHashMap(); + private Map additionalOptionalVotes = Maps.newHashMap(); + private SortedSet controlVotes = Sets.newTreeSet(); + private final AchievementTracker achievementTracker = new AchievementTracker(); private final PlayerView view; @@ -3067,4 +3071,86 @@ public class Player extends GameEntity implements Comparable { this.updateZoneForView(com); return keywordEffect; } + + public void addAdditionalVote(long timestamp, int value) { + additionalVotes.put(timestamp, value); + getView().updateAdditionalVote(this); + getGame().fireEvent(new GameEventPlayerStatsChanged(this, false)); + } + + public void removeAdditionalVote(long timestamp) { + if (additionalVotes.remove(timestamp) != null) { + getView().updateAdditionalVote(this); + getGame().fireEvent(new GameEventPlayerStatsChanged(this, false)); + } + } + + public int getAdditionalVotesAmount() { + int value = 0; + for (Integer i : additionalVotes.values()) { + value += i; + } + return value; + } + + public void addAdditionalOptionalVote(long timestamp, int value) { + additionalOptionalVotes.put(timestamp, value); + getView().updateOptionalAdditionalVote(this); + getGame().fireEvent(new GameEventPlayerStatsChanged(this, false)); + } + public void removeAdditionalOptionalVote(long timestamp) { + if (additionalOptionalVotes.remove(timestamp) != null) { + getView().updateOptionalAdditionalVote(this); + getGame().fireEvent(new GameEventPlayerStatsChanged(this, false)); + } + } + + public int getAdditionalOptionalVotesAmount() { + int value = 0; + for (Integer i : additionalOptionalVotes.values()) { + value += i; + } + return value; + } + + public boolean addControlVote(long timestamp) { + if (controlVotes.add(timestamp)) { + updateControlVote(); + return true; + } + return false; + } + public boolean removeControlVote(long timestamp) { + if (controlVotes.remove(timestamp)) { + updateControlVote(); + return true; + } + return false; + } + + void updateControlVote() { + // need to update all players because it can't know + Player control = getGame().getControlVote(); + for (Player pl : getGame().getPlayers()) { + pl.getView().updateControlVote(pl.equals(control)); + getGame().fireEvent(new GameEventPlayerStatsChanged(pl, false)); + } + } + + public Set getControlVote() { + return controlVotes; + } + + public void setControlVote(Set value) { + controlVotes.clear(); + controlVotes.addAll(value); + updateControlVote(); + } + + public Long getHighestControlVote() { + if (controlVotes.isEmpty()) { + return null; + } + return controlVotes.last(); + } } 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 62d57539212..ec79ece50b7 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -166,7 +166,7 @@ public abstract class PlayerController { return chooseSomeType(kindOfType, sa, validTypes, invalidTypes, false); } - public abstract Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes); + public abstract Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer); public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question); public abstract CardCollectionView getCardsToMulligan(Player firstPlayer); diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index 6aa49b215b0..2da3dac4bf6 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -271,6 +271,27 @@ public class PlayerView extends GameEntityView { set(TrackableProperty.NumDrawnThisTurn, p.getNumDrawnThisTurn()); } + public int getAdditionalVote() { + return get(TrackableProperty.AdditionalVote); + } + public void updateAdditionalVote(Player p) { + set(TrackableProperty.AdditionalVote, p.getAdditionalVotesAmount()); + } + + public int getOptionalAdditionalVote() { + return get(TrackableProperty.OptionalAdditionalVote); + } + public void updateOptionalAdditionalVote(Player p) { + set(TrackableProperty.OptionalAdditionalVote, p.getAdditionalOptionalVotesAmount()); + } + + public boolean getControlVote() { + return get(TrackableProperty.ControlVotes); + } + public void updateControlVote(boolean val) { + set(TrackableProperty.ControlVotes, val); + } + public ImmutableMultiset getKeywords() { return get(TrackableProperty.Keywords); } @@ -497,6 +518,19 @@ public class PlayerView extends GameEntityView { details.add(Localizer.getInstance().getMessage("lblCardDrawnThisTurnHas", String.valueOf(getNumDrawnThisTurn()))); details.add(Localizer.getInstance().getMessage("lblDamagepreventionHas", String.valueOf(getPreventNextDamage()))); + int v = getAdditionalVote(); + if (v > 0) { + details.add(Localizer.getInstance().getMessage("lblAdditionalVotes", String.valueOf(v))); + } + v = getOptionalAdditionalVote(); + if (v > 0) { + details.add(Localizer.getInstance().getMessage("lblOptionalAdditionalVotes", String.valueOf(v))); + } + + if (getControlVote()) { + details.add(Localizer.getInstance().getMessage("lblControlsVote")); + } + if (getIsExtraTurn()) { details.add(Localizer.getInstance().getMessage("lblIsExtraTurn")); } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index f8442310ec0..421edc80b60 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -181,15 +181,9 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone layers.add(StaticAbilityLayer.MODIFYPT); } - if (hasParam("AddHiddenKeyword")) { - layers.add(StaticAbilityLayer.RULES); - } - - if (hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")) { - layers.add(StaticAbilityLayer.RULES); - } - - if (hasParam("AdjustLandPlays")) { + if (hasParam("AddHiddenKeyword") + || hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount") + || hasParam("AdjustLandPlays") || hasParam("ControlVote") || hasParam("AdditionalVote") || hasParam("AdditionalOptionalVote")) { layers.add(StaticAbilityLayer.RULES); } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index f0b0813bfc3..f6d8a9b6984 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -513,6 +513,20 @@ public final class StaticAbilityContinuous { } } + if (params.containsKey("ControlVote")) { + p.addControlVote(se.getTimestamp()); + } + if (params.containsKey("AdditionalVote")) { + String mhs = params.get("AdditionalVote"); + int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb); + p.addAdditionalVote(se.getTimestamp(), add); + } + if (params.containsKey("AdditionalOptionalVote")) { + String mhs = params.get("AdditionalOptionalVote"); + int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb); + p.addAdditionalOptionalVote(se.getTimestamp(), add); + } + if (params.containsKey("RaiseMaxHandSize")) { String rmhs = params.get("RaiseMaxHandSize"); int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb); diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 2660337d82f..039085ee9d0 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -134,6 +134,9 @@ public enum TrackableProperty { HasUnlimitedLandPlay(TrackableTypes.BooleanType), NumLandThisTurn(TrackableTypes.IntegerType), NumDrawnThisTurn(TrackableTypes.IntegerType), + AdditionalVote(TrackableTypes.IntegerType), + OptionalAdditionalVote(TrackableTypes.IntegerType), + ControlVotes(TrackableTypes.BooleanType), Keywords(TrackableTypes.KeywordCollectionViewType, FreezeMode.IgnoresFreeze), Commander(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), CommanderCast(TrackableTypes.IntegerMapType), diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml index 9afc4dcc9ab..854f9159678 100644 --- a/forge-gui-android/pom.xml +++ b/forge-gui-android/pom.xml @@ -19,7 +19,7 @@ forge forge - 1.6.33-SNAPSHOT + 1.6.34-SNAPSHOT forge-gui-android diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index c2bb8f2388f..e1fa8d95747 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -4,7 +4,7 @@ forge forge - 1.6.33-SNAPSHOT + 1.6.34-SNAPSHOT forge-gui-desktop @@ -213,7 +213,7 @@ com.akathist.maven.plugins.launch4j launch4j-maven-plugin - 1.5.2 + 1.7.25 l4j-gui @@ -235,7 +235,10 @@ 1.8.0 - 1024 + 4096 + + -Dfile.encoding=UTF-8 + @@ -367,7 +370,7 @@ com.akathist.maven.plugins.launch4j launch4j-maven-plugin - 1.5.2 + 1.7.25 l4j-gui @@ -389,7 +392,10 @@ 1.8.0 - 1024 + 4096 + + -Dfile.encoding=UTF-8 + @@ -564,7 +570,7 @@