From 4ddb73e8d2f08ec1483e308f11d6e87aefee9a35 Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 6 Mar 2018 22:13:04 +0300 Subject: [PATCH 01/17] - When copying deck to clipboard, do not attempt to check the validity of zone sizes to avoid situations when exceptional, but legal deck compositions (such as two partner Commanders) are not allowed / not fully copied. --- .../java/forge/deckchooser/FDeckViewer.java | 27 ++++++++----------- .../src/forge/deck/FDeckViewer.java | 11 +++----- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java index e97535ff489..a28f4a94202 100644 --- a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java +++ b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java @@ -184,23 +184,18 @@ public class FDeckViewer extends FDialog { } deckList.append(s.toString()).append(": "); sectionCards = new TreeMap<>(); - if (s.isSingleCard()) { - deckList.append(cp.get(0).getName()).append(nl); + deckList.append(nl); + for (final Entry ev : cp) { + cardName = ev.getKey().toString(); + if (sectionCards.containsKey(cardName)) { + sectionCards.put(cardName, (int)sectionCards.get(cardName) + ev.getValue()); + } + else { + sectionCards.put(cardName, ev.getValue()); + } } - else { - deckList.append(nl); - for (final Entry ev : cp) { - cardName = ev.getKey().toString(); - if (sectionCards.containsKey(cardName)) { - sectionCards.put(cardName, (int)sectionCards.get(cardName) + ev.getValue()); - } - else { - sectionCards.put(cardName, ev.getValue()); - } - } - for (final Entry ev: sectionCards.entrySet()) { - deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl); - } + for (final Entry ev: sectionCards.entrySet()) { + deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl); } deckList.append(nl); } diff --git a/forge-gui-mobile/src/forge/deck/FDeckViewer.java b/forge-gui-mobile/src/forge/deck/FDeckViewer.java index ea38152df37..33d8fdc0895 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckViewer.java +++ b/forge-gui-mobile/src/forge/deck/FDeckViewer.java @@ -93,14 +93,9 @@ public class FDeckViewer extends FScreen { continue; } deckList.append(s.toString()).append(": "); - if (s.isSingleCard()) { - deckList.append(cp.get(0).getName()).append(nl); - } - else { - deckList.append(nl); - for (final Entry ev : cp) { - deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl); - } + deckList.append(nl); + for (final Entry ev : cp) { + deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl); } deckList.append(nl); } From fcd392eec12681d0a6731551cc179e4c374ba893 Mon Sep 17 00:00:00 2001 From: austinio7116 Date: Tue, 6 Mar 2018 19:50:36 +0000 Subject: [PATCH 02/17] Added Masters 25 to draft blockdata for drafting and corrected Konming Sleeping Dragon quotes and rarity of Akroma --- forge-gui/res/blockdata/blocks.txt | 3 ++- forge-gui/res/draft/rankings.txt | 4 ++-- forge-gui/res/editions/Masters 25.txt | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/forge-gui/res/blockdata/blocks.txt b/forge-gui/res/blockdata/blocks.txt index 067404b320b..23d74962bd4 100644 --- a/forge-gui/res/blockdata/blocks.txt +++ b/forge-gui/res/blockdata/blocks.txt @@ -72,4 +72,5 @@ Kaladesh, 3/6/KLD, KLD AER Modern Masters 2017, 3/6/M15, MM3 Amonkhet, 3/6/AKH, AKH HOU Ixalan, 3/6/XLN, XLN RIX -Iconic Masters, 3/6/XLN, IMA \ No newline at end of file +Iconic Masters, 3/6/XLN, IMA +Masters 25, 3/6/XLN, A25 \ No newline at end of file diff --git a/forge-gui/res/draft/rankings.txt b/forge-gui/res/draft/rankings.txt index 573704b6d22..4ed0aaffd6f 100644 --- a/forge-gui/res/draft/rankings.txt +++ b/forge-gui/res/draft/rankings.txt @@ -1,7 +1,7 @@ #1|Jace, the Mind Sculptor|M|A25 #2|Niv-Mizzet, the Firemind|R|A25 #3|Gisela, Blade of Goldnight|M|A25 -#4|Akroma, Angel of Fury|R|A25 +#4|Akroma, Angel of Fury|M|A25 #5|Laquatus's Champion|R|A25 #6|Master of the Wild Hunt|M|A25 #7|Prossh, Skyraider of Kher|M|A25 @@ -70,7 +70,7 @@ #70|Regrowth|U|A25 #71|Nicol Bolas|R|A25 #72|Treasure Keeper|U|A25 -#73|Kongming, Sleeping Dragon|U|A25 +#73|Kongming, "Sleeping Dragon"|U|A25 #74|Court Hussar|C|A25 #75|Exclude|U|A25 #76|Disfigure|C|A25 diff --git a/forge-gui/res/editions/Masters 25.txt b/forge-gui/res/editions/Masters 25.txt index b6e4bef4002..29e566785bd 100644 --- a/forge-gui/res/editions/Masters 25.txt +++ b/forge-gui/res/editions/Masters 25.txt @@ -28,7 +28,7 @@ FoilChanceInBooster=100 17 C Griffin Protector 18 U Karona's Zealot 19 C Knight of the Skyward Eye -20 U Kongming, Sleeping Dragon +20 U Kongming, "Sleeping Dragon" 21 U Kor Firewalker 22 C Loyal Sentry 23 R Luminarch Ascension @@ -127,7 +127,7 @@ FoilChanceInBooster=100 116 U Zombify 117 U Zulaport Cutthroat 118 C Act of Treason -119 R Akroma, Angel of Fury +119 M Akroma, Angel of Fury 120 C Balduvian Horde 121 R Ball Lightning 122 R Blood Moon From 93dc3338c1f3617b2f702864281c31011f8926c1 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 17:33:23 +0100 Subject: [PATCH 03/17] SpellAbility: isValid check for root --- .../main/java/forge/game/spellability/SpellAbility.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 4416815c7a7..92aea120f1b 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1491,19 +1491,20 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public final boolean isValid(final String restriction, final Player sourceController, final Card source, SpellAbility spellAbility) { // Inclusive restrictions are Card types final String[] incR = restriction.split("\\.", 2); + SpellAbility root = getRootAbility(); if (incR[0].equals("Spell")) { - if (!isSpell()) { + if (!root.isSpell()) { return false; } } else if (incR[0].equals("Triggered")) { - if (!isTrigger()) { + if (!root.isTrigger()) { return false; } } else if (incR[0].equals("Activated")) { - if (!(this instanceof AbilityActivated)) { + if (!(this instanceof AbilityActivated) && !(root instanceof AbilityActivated)) { return false; } } From e25247d0e5bb4e4df943b1a847f8a7d105208372 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:56:24 +0100 Subject: [PATCH 04/17] SpellAbility: add IsTargeting property, fixed additional abilities for clone --- .../src/main/java/forge/game/ForgeScript.java | 12 ++++++++++++ .../forge/game/spellability/SpellAbility.java | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 7ba06ea9a79..8cc37fb3987 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -179,6 +179,18 @@ public class ForgeScript { if (!sa.hasParam("Equip")) { return false; } + } else if (property.startsWith("IsTargeting")) { + String k[] = property.split(" ", 2); + boolean found = false; + for (GameObject o : AbilityUtils.getDefinedObjects(source, k[1], spellAbility)) { + if (sa.isTargeting(o)) { + found = true; + break; + } + } + if (!found) { + return false; + } } return true; diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 92aea120f1b..0fde5be036d 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -816,6 +816,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (manaPart != null) { clone.manaPart = new AbilityManaPart(host, mapParams); } + + // clear maps for copy, the values will be added later + clone.additionalAbilities = Maps.newHashMap(); + clone.additionalAbilityLists = Maps.newHashMap(); // run special copy Ability to make a deep copy CardFactory.copySpellAbility(this, clone, host, lki); } catch (final CloneNotSupportedException e) { @@ -1486,6 +1490,17 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return topSA.getHostCard().isValid(tgt.getValidTgts(), getActivatingPlayer(), getHostCard(), this); } + public boolean isTargeting(GameObject o) { + if (getTargets().isTargeting(o)) { + return true; + } + SpellAbility p = getParent(); + if (p != null) { + return p.isTargeting(o); + } + return false; + } + // Takes one argument like Permanent.Blue+withFlying @Override public final boolean isValid(final String restriction, final Player sourceController, final Card source, SpellAbility spellAbility) { From 7b8e92ace2b211dd46a12c0c0690343a702ac608 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sat, 3 Mar 2018 18:51:39 +0100 Subject: [PATCH 05/17] CardFactoryUtil: use AdditionalAbilityList with better check for Fabricate --- .../ability/effects/ChooseGenericEffect.java | 9 ++++---- .../java/forge/game/card/CardFactoryUtil.java | 22 +++++++++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java index 1d52dd622e4..7cb835542b6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java @@ -7,7 +7,6 @@ import forge.game.card.Card; import forge.game.event.GameEventCardModeChosen; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.util.MyRandom; import java.util.List; @@ -34,14 +33,16 @@ public class ChooseGenericEffect extends SpellAbilityEffect { final SpellAbility fallback = sa.getAdditionalAbility("FallbackAbility"); final List tgtPlayers = getDefinedPlayersOrTargeted(sa); - final TargetRestrictions tgt = sa.getTargetRestrictions(); for (final Player p : tgtPlayers) { // determine if any of the choices are not valid List saToRemove = Lists.newArrayList(); for (SpellAbility saChoice : abilities) { - if ("Player.IsRemembered".equals(saChoice.getParam("Defined")) && saChoice.hasParam("UnlessCost")) { + if (!saChoice.getRestrictions().checkOtherRestrictions(host, saChoice, sa.getActivatingPlayer()) ) { + saToRemove.add(saChoice); + } else if (saChoice.hasParam("UnlessCost") && + "Player.IsRemembered".equals(saChoice.getParam("Defined"))) { String unlessCost = saChoice.getParam("UnlessCost"); // Sac a permanent in presence of Sigarda, Host of Herons // TODO: generalize this by testing if the unless cost can be paid @@ -55,7 +56,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect { } abilities.removeAll(saToRemove); - if (tgt != null && sa.getTargets().isTargeting(p) && !p.canBeTargetedBy(sa)) { + if (sa.usesTargeting() && sa.getTargets().isTargeting(p) && !p.canBeTargetedBy(sa)) { continue; } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index a090562c9a2..e0fe3eb7d13 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2350,11 +2350,12 @@ public class CardFactoryUtil { final String name = StringUtils.join(k); final String trigStr = "Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield " - + " | Execute$ " + name + "Choose | ValidCard$ Card.Self | Secondary$ True" + + " | ValidCard$ Card.Self | Secondary$ True" + " | TriggerDescription$ Fabricate " + n + " (" + inst.getReminderText() + ")"; - final String choose = "DB$ GenericChoice | Choices$ DB" + name + "Counter,DB" + name + "Token | ConditionPresent$ Card.StrictlySelf | SubAbility$ DB" + name + "Token2 | AILogic$ " + name; - final String counter = "DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ " + n + " | SpellDescription$ Put " + final String choose = "DB$ GenericChoice | AILogic$ " + name; + final String counter = "DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ " + n + + " | IsPresent$ Card.StrictlySelf | SpellDescription$ Put " + Lang.nounWithNumeral(n, "+1/+1 counter") + " on it."; final String token = "DB$ Token | TokenAmount$ " + n + " | TokenName$ Servo | TokenTypes$ Artifact,Creature,Servo" + " | TokenOwner$ You | TokenColors$ Colorless | TokenPower$ 1 | TokenToughness$ 1" @@ -2363,10 +2364,15 @@ public class CardFactoryUtil { final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic); - card.setSVar(name + "Choose", choose); - card.setSVar("DB" + name + "Counter", counter); - card.setSVar("DB" + name + "Token", token); - card.setSVar("DB" + name + "Token2", token + " | ConditionPresent$ Card.StrictlySelf | ConditionCompare$ EQ0"); + SpellAbility saChoose = AbilityFactory.getAbility(choose, card); + + List list = Lists.newArrayList(); + list.add((AbilitySub)AbilityFactory.getAbility(counter, card)); + list.add((AbilitySub)AbilityFactory.getAbility(token, card)); + saChoose.setAdditionalAbilityList("Choices", list); + saChoose.setIntrinsic(intrinsic); + + trigger.setOverridingAbility(saChoose); inst.addTrigger(trigger); } else if (keyword.startsWith("Fading")) { @@ -3367,6 +3373,8 @@ public class CardFactoryUtil { // extra part for the Damage Prevention keywords if (keyword.startsWith("Prevent all ")) { + // TODO add intrinsic warning + boolean isCombat = false; boolean from = false; boolean to = false; From 1e7755b87742c6f477919a33a6c8516c6afab04a Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:50:45 +0100 Subject: [PATCH 06/17] CloneEffect: setOriginalHost instead of setHostCard --- .../src/main/java/forge/game/ability/effects/CloneEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java index d35ec37f7e6..977db72b19d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java @@ -149,7 +149,7 @@ public class CloneEffect extends SpellAbilityEffect { // set the host card for copied spellabilities for (final SpellAbility newSa : tgtCard.getSpellAbilities()) { - newSa.setHostCard(cardToCopy); + newSa.setOriginalHost(cardToCopy); } // restore name if it should be unchanged From 2e33b3162c2c019c2f3cb40332477d04e899333e Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:52:29 +0100 Subject: [PATCH 07/17] AbilityUtils: fixed getSpellAbilities if sa is null --- forge-game/src/main/java/forge/game/ability/AbilityUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3361436670b..cb8ae2e50e4 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1205,7 +1205,7 @@ public class AbilityUtils { final SpellAbility sa) { final FCollection sas = new FCollection(); final String defined = (def == null) ? "Self" : applyAbilityTextChangeEffects(def, sa); // default to Self - final Game game = sa.getActivatingPlayer().getGame(); + final Game game = card.getGame(); SpellAbility s = null; From 59063bc1945d6812dadc684119b31053750c380f Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:53:17 +0100 Subject: [PATCH 08/17] CardTraitBase: use GameObject so it works with SpellAbility too --- forge-game/src/main/java/forge/game/CardTraitBase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index 822b185df46..6951c2be1bb 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -170,8 +170,8 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { * @return a boolean. */ public static boolean matchesValid(final Object o, final String[] valids, final Card srcCard) { - if (o instanceof GameEntity) { - final GameEntity c = (GameEntity) o; + if (o instanceof GameObject) { + final GameObject c = (GameObject) o; return c.isValid(valids, srcCard.getController(), srcCard, null); } From 58bba637ca3578ee90905b5e872a466fc7a3351a Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:57:35 +0100 Subject: [PATCH 09/17] SimulateMatch: fixed simulateSingleMatch --- .../src/main/java/forge/view/SimulateMatch.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index be176d263d7..586e7c0231f 100644 --- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java +++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java @@ -160,11 +160,11 @@ public class SimulateMatch { - private static void simulateSingleMatch(Match mc, int iGame, boolean outputGamelog) { - StopWatch sw = new StopWatch(); + private static void simulateSingleMatch(final Match mc, int iGame, boolean outputGamelog) { + final StopWatch sw = new StopWatch(); sw.start(); - Game g1 = mc.createGame(); + final Game g1 = mc.createGame(); // will run match in the same thread long startTime = System.currentTimeMillis(); From f76c60e867ce6d30ad9c622352bfe030db8bb794 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:58:21 +0100 Subject: [PATCH 10/17] CommanderDeckGenerator: fixed getCommanderDecks --- forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java b/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java index 52af7d5b7d2..fc08fb0d616 100644 --- a/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java @@ -22,7 +22,7 @@ import java.util.Map; * Created by maustin on 09/05/2017. */ public class CommanderDeckGenerator extends DeckProxy implements Comparable { - public static List getCommanderDecks(DeckFormat format, boolean isForAi, boolean isCardGen){ + public static List getCommanderDecks(final DeckFormat format, boolean isForAi, boolean isCardGen){ ItemPool uniqueCards; if(isCardGen){ uniqueCards = new ItemPool(PaperCard.class); From a47c3cf004fec1ce33537d74522bbc0b5e7f8264 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:59:00 +0100 Subject: [PATCH 11/17] SetStateEffect: check for StoredTransform svar --- .../main/java/forge/game/ability/effects/SetStateEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index 4732a185cd9..91695cd6580 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -66,7 +66,7 @@ public class SetStateEffect extends SpellAbilityEffect { continue; } - if ("Transform".equals(mode) && tgt.equals(host)) { + if ("Transform".equals(mode) && tgt.equals(host) && sa.hasSVar("StoredTransform")) { // If want to Transform, and host is trying to transform self, skip if not in alignment boolean skip = tgt.getTransformedTimestamp() != Long.parseLong(sa.getSVar("StoredTransform")); // Clear SVar from SA so it doesn't get reused accidentally From c5c3591471f76b1189e62e13ee94c289537d432a Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 11:59:48 +0100 Subject: [PATCH 12/17] TokenEffect: use sa instead of root --- .../java/forge/game/ability/effects/TokenEffect.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java index ee15c6f7b2c..2181ed28ba6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java @@ -18,9 +18,7 @@ package forge.game.ability.effects; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import forge.StaticData; import forge.card.MagicColor; @@ -281,10 +279,10 @@ public class TokenEffect extends SpellAbilityEffect { if (prototype == null) { tokens = tokenInfo.makeTokenWithMultiplier(controller, finalAmount, cause != null); grantHiddenKeywords(tokens); - grantSvars(tokens, root); - grantAbilities(tokens, root); - grantTriggers(tokens, root); - grantStatics(tokens, root); + grantSvars(tokens, sa); + grantAbilities(tokens, sa); + grantTriggers(tokens, sa); + grantStatics(tokens, sa); } else { tokens = TokenInfo.makeTokensFromPrototype(prototype, controller, finalAmount, cause != null); } @@ -352,7 +350,6 @@ public class TokenEffect extends SpellAbilityEffect { } private String determineTokenColor(Card host) { - Set colorSet = new HashSet<>(); final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length); for (int i = 0; i < substitutedColors.length; i++) { if (substitutedColors[i].equals("ChosenColor")) { From 65d65c9e5fea92a1a2835c618ffbfe8c4b7a9392 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 4 Mar 2018 20:40:57 +0100 Subject: [PATCH 13/17] Treasure map: update code using Branch to remove trigger --- .../cardsfolder/t/treasure_map_treasure_cove.txt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/forge-gui/res/cardsfolder/t/treasure_map_treasure_cove.txt b/forge-gui/res/cardsfolder/t/treasure_map_treasure_cove.txt index 99f4383977e..f4193ed8f05 100644 --- a/forge-gui/res/cardsfolder/t/treasure_map_treasure_cove.txt +++ b/forge-gui/res/cardsfolder/t/treasure_map_treasure_cove.txt @@ -2,16 +2,13 @@ Name:Treasure Map ManaCost:2 Types:Artifact A:AB$ Scry | Cost$ 1 T | ScryNum$ 1 | SubAbility$ DBLandmark | SpellDescription$ Scry 1. Put a landmark counter on CARDNAME. Then if there are three or more landmark counters on it, remove those counters, transform CARDNAME, and create three colorless Treasure artifact tokens with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool." -SVar:DBLandmark:DB$ PutCounter | Defined$ Self | CounterType$ LANDMARK | CounterNum$ 1 | SubAbility$ DBStoreSVar -SVar:DBStoreSVar:DB$ StoreSVar | SVar$ FoundTreasure | Type$ Number | Expression$ 1 | ConditionCheckSVar$ XMarksTheSpot | ConditionSVarCompare$ GE1 | References$ XMarksTheSpot,FoundTreasure | SubAbility$ DBRemoveCtrs -SVar:DBRemoveCtrs:DB$ RemoveCounter | Defined$ Self | CounterType$ LANDMARK | CounterNum$ 3 | ConditionCheckSVar$ FoundTreasure | ConditionSVarCompare$ GE1 | References$ FoundTreasure | SubAbility$ DBTreasureTokens -SVar:DBTreasureTokens:DB$ Token | TokenAmount$ 3 | TokenName$ Treasure | TokenTypes$ Artifact,Treasure | TokenOwner$ You | TokenColors$ Colorless | TokenImage$ c treasure | TokenAbilities$ ABTreasureMana | TokenAltImages$ c_treasure2,c_treasure3,c_treasure4 | ConditionCheckSVar$ FoundTreasure | ConditionSVarCompare$ GE1 | SubAbility$ DBTransform | References$ FoundTreasure -SVar:DBTransform:DB$ SetState | Defined$ Self | Mode$ Transform | ConditionCheckSVar$ FoundTreasure | ConditionSVarCompare$ GE1 | References$ FoundTreasure +SVar:DBLandmark:DB$ PutCounter | Defined$ Self | CounterType$ LANDMARK | CounterNum$ 1 | SubAbility$ DBBranch +SVar:DBBranch:DB$ Branch | BranchConditionSVar$ XMarksTheSpot | References$ XMarksTheSpot | TrueSubAbility$ DBRemoveCtrs +SVar:DBRemoveCtrs:DB$ RemoveCounter | Defined$ Self | CounterType$ LANDMARK | CounterNum$ All | SubAbility$ DBTransform +SVar:DBTransform:DB$ SetState | Defined$ Self | Mode$ Transform | SubAbility$ DBTreasureTokens +SVar:DBTreasureTokens:DB$ Token | TokenAmount$ 3 | TokenName$ Treasure | TokenTypes$ Artifact,Treasure | TokenOwner$ You | TokenColors$ Colorless | TokenImage$ c treasure | TokenAbilities$ ABTreasureMana | TokenAltImages$ c_treasure2,c_treasure3,c_treasure4 | References$ ABTreasureMana SVar:ABTreasureMana:AB$ Mana | Cost$ T Sac<1/CARDNAME> | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool. SVar:XMarksTheSpot:Count$Valid Card.Self+counters_GE3_LANDMARK -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | Execute$ DBInitSVar | Static$ True -SVar:DBInitSVar:DB$ StoreSVar | SVar$ FoundTreasure | Type$ Number | Expression$ 0 | References$ FoundTreasure -SVar:FoundTreasure:Number$0 AlternateMode:DoubleFaced DeckHas:Ability$Token SVar:Picture:http://www.wizards.com/global/images/magic/general/treasure_map.jpg From b88365e4753903c28e16e00ce827a2d455c09003 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Wed, 7 Mar 2018 07:04:00 +0100 Subject: [PATCH 14/17] SpellAbility fixed some changes --- .../main/java/forge/game/spellability/SpellAbility.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 0fde5be036d..a8a1e2b3cfd 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1495,10 +1495,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return true; } SpellAbility p = getParent(); - if (p != null) { - return p.isTargeting(o); - } - return false; + return p != null && p.isTargeting(o); } // Takes one argument like Permanent.Blue+withFlying @@ -1519,7 +1516,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } } else if (incR[0].equals("Activated")) { - if (!(this instanceof AbilityActivated) && !(root instanceof AbilityActivated)) { + if (!(root instanceof AbilityActivated)) { return false; } } From d9df1243b6c003f99c065c5787332bcd7f791686 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Wed, 7 Mar 2018 07:04:57 +0100 Subject: [PATCH 15/17] mark effect as final --- .../java/forge/game/ability/effects/RegenerateBaseEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java index 5114885e70f..e84307bb801 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java @@ -22,7 +22,7 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect { final Game game = hostCard.getGame(); // create Effect for Regeneration - Card eff = createEffect( + final Card eff = createEffect( hostCard, sa.getActivatingPlayer(), hostCard.getName() + "'s Regeneration", hostCard.getImageKey()); eff.addRemembered(list); From d9913ffb1ecc7e5b4401660c065aa1fe2531326f Mon Sep 17 00:00:00 2001 From: "Jamin W. Collins" Date: Tue, 6 Mar 2018 17:26:51 -0700 Subject: [PATCH 16/17] broadcast message on player leaving, like joins Signed-off-by: Jamin W. Collins --- forge-gui/src/main/java/forge/net/server/FServerManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forge-gui/src/main/java/forge/net/server/FServerManager.java b/forge-gui/src/main/java/forge/net/server/FServerManager.java index 0344a0219ea..ed71661eff8 100644 --- a/forge-gui/src/main/java/forge/net/server/FServerManager.java +++ b/forge-gui/src/main/java/forge/net/server/FServerManager.java @@ -323,8 +323,10 @@ public final class FServerManager { @Override public void channelInactive(final ChannelHandlerContext ctx) throws Exception { final RemoteClient client = clients.remove(ctx.channel()); + final String username = client.getUsername(); localLobby.disconnectPlayer(client.getIndex()); - broadcast(new LogoutEvent(client.getUsername())); + broadcast(new MessageEvent(String.format("%s left the room", username))); + broadcast(new LogoutEvent(username)); super.channelInactive(ctx); } } From 7615f333e159dcdb7c40ccbceb6d2ec1161f99be Mon Sep 17 00:00:00 2001 From: "Jamin W. Collins" Date: Tue, 6 Mar 2018 17:39:39 -0700 Subject: [PATCH 17/17] disclose and enforce DEV_MODE state in network play Signed-off-by: Jamin W. Collins --- .../java/forge/screens/home/PlayerPanel.java | 44 +++++++++++ .../main/java/forge/screens/home/VLobby.java | 13 +++- .../home/settings/CSubmenuPreferences.java | 6 ++ .../src/main/java/forge/match/GameLobby.java | 4 + .../src/main/java/forge/match/LobbySlot.java | 15 +++- .../net/event/UpdateLobbyPlayerEvent.java | 76 ++++++++++++++----- .../java/forge/net/server/FServerManager.java | 4 + 7 files changed, 142 insertions(+), 20 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java b/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java index 50a4ae31fcd..2a54a8b6efd 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java @@ -98,6 +98,10 @@ public class PlayerPanel extends FPanel { private final FLabel vgdSelectorBtn = new FLabel.ButtonBuilder().text("Select a Vanguard avatar").build(); private final FLabel vgdLabel; + private FCheckBox chkDevMode; + + private boolean allowNetworking; + private final VLobby lobby; public PlayerPanel(final VLobby lobby, final boolean allowNetworking, final int index, final LobbySlot slot, final boolean mayEdit, final boolean mayControl) { super(); @@ -106,6 +110,7 @@ public class PlayerPanel extends FPanel { this.index = index; this.mayEdit = mayEdit; this.mayControl = mayControl; + this.allowNetworking = allowNetworking; this.deckLabel = lobby.newLabel("Deck:"); this.scmLabel = lobby.newLabel("Scheme deck:"); @@ -179,6 +184,11 @@ public class PlayerPanel extends FPanel { } }); + if (isNetworkHost()) { + createDevModeButton(); + this.add(chkDevMode); + } + this.type = slot == null ? LobbySlotType.LOCAL : slot.getType(); this.setPlayerName(slot == null ? "" : slot.getName()); this.setAvatarIndex(slot == null ? 0 : slot.getAvatarIndex()); @@ -186,6 +196,10 @@ public class PlayerPanel extends FPanel { update(); } + boolean isNetworkHost() { + return this.allowNetworking && this.index == 0; + } + void update() { avatarLabel.setEnabled(mayEdit); avatarLabel.setIcon(FSkin.getAvatars().get(Integer.valueOf(type == LobbySlotType.OPEN ? -1 : avatarIndex))); @@ -200,6 +214,10 @@ public class PlayerPanel extends FPanel { chkReady.setVisible(type == LobbySlotType.LOCAL || type == LobbySlotType.REMOTE); chkReady.setEnabled(mayEdit); + if (chkDevMode != null) { + chkDevMode.setEnabled(mayEdit); + } + closeBtn.setVisible(mayRemove); if (mayRemove) { @@ -585,6 +603,23 @@ public class PlayerPanel extends FPanel { }); } + private void createDevModeButton() { + chkDevMode = new FCheckBox("Dev Mode"); + + chkDevMode.addActionListener(new ActionListener() { + @Override public final void actionPerformed(final ActionEvent e) { + final boolean toggle = chkDevMode.isSelected(); + prefs.setPref(FPref.DEV_MODE_ENABLED, String.valueOf(toggle)); + ForgePreferences.DEV_MODE = toggle; + + // ensure that preferences panel reflects the change + prefs.save(); + + lobby.setDevMode(index); + } + }); + } + /** * @param index */ @@ -747,6 +782,15 @@ public class PlayerPanel extends FPanel { chkReady.setSelected(isReady); } + public boolean isDevMode() { + return chkDevMode != null && chkDevMode.isSelected(); + } + public void setIsDevMode(final boolean isDevMode) { + if (chkDevMode != null) { + chkDevMode.setSelected(isDevMode); + } + } + public void setMayEdit(final boolean mayEdit) { this.mayEdit = mayEdit; } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java index 0cbfbfc6590..02732999231 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -275,6 +275,7 @@ public class VLobby implements ILobbyView { commanderDeckChooser.restoreSavedState(); tinyLeaderDeckChooser.restoreSavedState(); if (i == 0) { + slot.setIsDevMode(prefs.getPrefBoolean(FPref.DEV_MODE_ENABLED)); changePlayerFocus(0); } isNewPanel = true; @@ -286,6 +287,7 @@ public class VLobby implements ILobbyView { panel.setAvatarIndex(slot.getAvatarIndex()); panel.setTeam(slot.getTeam()); panel.setIsReady(slot.isReady()); + panel.setIsDevMode(slot.isDevMode()); panel.setIsArchenemy(slot.isArchenemy()); panel.setUseAiSimulation(slot.getAiOptions().contains(AIOption.USE_SIMULATION)); panel.setMayEdit(lobby.mayEdit(i)); @@ -331,6 +333,15 @@ public class VLobby implements ILobbyView { firePlayerChangeListener(index); changePlayerFocus(index); } + void setDevMode(final int index) { + // clear ready for everyone + for (int i = 0; i < activePlayersNum; i++) { + final PlayerPanel panel = playerPanels.get(i); + panel.setIsReady(false); + firePlayerChangeListener(i); + } + changePlayerFocus(index); + } void firePlayerChangeListener(final int index) { if (playerChangeListener != null) { playerChangeListener.update(index, getSlot(index)); @@ -361,7 +372,7 @@ public class VLobby implements ILobbyView { private UpdateLobbyPlayerEvent getSlot(final int index) { final PlayerPanel panel = playerPanels.get(index); - return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.getAiOptions()); + return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions()); } /** Builds the actual deck panel layouts for each player. diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index 883c5843003..21c5ba5e069 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -8,6 +8,7 @@ import forge.game.GameLogEntryType; import forge.gui.framework.FScreen; import forge.gui.framework.ICDoc; import forge.model.FModel; +import forge.net.server.FServerManager; import forge.player.GamePlayerUtil; import forge.properties.ForgeConstants; import forge.properties.ForgePreferences; @@ -63,6 +64,11 @@ public enum CSubmenuPreferences implements ICDoc { @Override public void itemStateChanged(final ItemEvent arg0) { if (updating) { return; } + // prevent changing DEV_MODE while network game running + if (FServerManager.getInstance().isMatchActive()) { + System.out.println("Can't change DEV_MODE while a network match is in progress!"); + return; + } final boolean toggle = view.getCbDevMode().isSelected(); prefs.setPref(FPref.DEV_MODE_ENABLED, String.valueOf(toggle)); diff --git a/forge-gui/src/main/java/forge/match/GameLobby.java b/forge-gui/src/main/java/forge/match/GameLobby.java index 097705ed545..4599efce415 100644 --- a/forge-gui/src/main/java/forge/match/GameLobby.java +++ b/forge-gui/src/main/java/forge/match/GameLobby.java @@ -60,6 +60,10 @@ public abstract class GameLobby implements IHasGameType { return allowNetworking; } + public final boolean isMatchActive() { + return hostedMatch != null && hostedMatch.isMatchOver() == false; + } + public void setListener(final IUpdateable listener) { this.listener = listener; } diff --git a/forge-gui/src/main/java/forge/match/LobbySlot.java b/forge-gui/src/main/java/forge/match/LobbySlot.java index 462ca739f78..9deb226fe04 100644 --- a/forge-gui/src/main/java/forge/match/LobbySlot.java +++ b/forge-gui/src/main/java/forge/match/LobbySlot.java @@ -10,7 +10,7 @@ import forge.deck.Deck; import forge.net.event.UpdateLobbyPlayerEvent; public final class LobbySlot implements Serializable { - private static final long serialVersionUID = 6918205436608794289L; + private static final long serialVersionUID = 9203252798721142264L; private LobbySlotType type; private String name; @@ -18,6 +18,7 @@ public final class LobbySlot implements Serializable { private int team; private boolean isArchenemy; private boolean isReady; + private boolean isDevMode; private Deck deck; private ImmutableSet aiOptions; @@ -28,6 +29,7 @@ public final class LobbySlot implements Serializable { this.team = team; this.isArchenemy = isArchenemy; this.isReady = isReady; + this.isDevMode = false; this.setAiOptions(aiOptions); } @@ -57,6 +59,10 @@ public final class LobbySlot implements Serializable { setIsReady(data.getReady().booleanValue()); changed = true; } + if (data.getDevMode() != null) { + setIsDevMode(data.getDevMode().booleanValue()); + changed = true; + } if (data.getAiOptions() != null) { setAiOptions(data.getAiOptions()); changed = true; @@ -112,6 +118,13 @@ public final class LobbySlot implements Serializable { this.isReady = isReady; } + public boolean isDevMode() { + return isDevMode; + } + public void setIsDevMode(final boolean isDevMode) { + this.isDevMode = isDevMode; + } + public Deck getDeck() { return deck; } diff --git a/forge-gui/src/main/java/forge/net/event/UpdateLobbyPlayerEvent.java b/forge-gui/src/main/java/forge/net/event/UpdateLobbyPlayerEvent.java index 8e8045eb8b7..59f0eb7baa1 100644 --- a/forge-gui/src/main/java/forge/net/event/UpdateLobbyPlayerEvent.java +++ b/forge-gui/src/main/java/forge/net/event/UpdateLobbyPlayerEvent.java @@ -11,39 +11,76 @@ import forge.match.LobbySlotType; import forge.net.server.RemoteClient; public final class UpdateLobbyPlayerEvent implements NetEvent { - private static final long serialVersionUID = -5073305607515425968L; + private static final long serialVersionUID = -7354695008599789571L; + + private LobbySlotType type = null; + private String name = null; + private int avatarIndex = -1; + private int team = -1; + private Boolean isArchenemy = null; + private Boolean isReady = null; + private Boolean isDevMode = null; + private Deck deck = null; + private DeckSection section = null; + private CardPool cards = null; + private Set aiOptions = null; - private final LobbySlotType type; - private final String name; - private final int avatarIndex; - private final int team; - private final Boolean isArchenemy; - private final Boolean isReady; - private final Deck deck; - private final DeckSection section; - private final CardPool cards; - private final Set aiOptions; public static UpdateLobbyPlayerEvent create(final LobbySlotType type, final String name, final int avatarIndex, final int team, final boolean isArchenemy, final boolean isReady, final Set aiOptions) { - return new UpdateLobbyPlayerEvent(type, name, avatarIndex, team, isArchenemy, isReady, null, null, null, aiOptions); + return new UpdateLobbyPlayerEvent(type, name, avatarIndex, team, isArchenemy, isReady, aiOptions); + } + public static UpdateLobbyPlayerEvent create(final LobbySlotType type, final String name, final int avatarIndex, final int team, final boolean isArchenemy, final boolean isReady, final boolean isDevMode, final Set aiOptions) { + return new UpdateLobbyPlayerEvent(type, name, avatarIndex, team, isArchenemy, isReady, isDevMode, aiOptions); } public static UpdateLobbyPlayerEvent deckUpdate(final Deck deck) { - return new UpdateLobbyPlayerEvent(null, null, -1, -1, null, null, deck, null, null, null); + return new UpdateLobbyPlayerEvent(deck); } public static UpdateLobbyPlayerEvent deckUpdate(final DeckSection section, final CardPool cards) { - return new UpdateLobbyPlayerEvent(null, null, -1, -1, null, null, null, section, cards, null); + return new UpdateLobbyPlayerEvent(section, cards); } - private UpdateLobbyPlayerEvent(final LobbySlotType type, final String name, final int avatarIndex, final int team, final Boolean isArchenemy, final Boolean isReady, final Deck deck, final DeckSection section, final CardPool cards, final Set aiOptions) { + private UpdateLobbyPlayerEvent(final Deck deck) { + this.deck = deck; + } + + private UpdateLobbyPlayerEvent(final DeckSection section, final CardPool cards) { + this.section = section; + this.cards = cards; + } + + private UpdateLobbyPlayerEvent( + final LobbySlotType type, + final String name, + final int avatarIndex, + final int team, + final boolean isArchenemy, + final boolean isReady, + final Set aiOptions) { this.type = type; this.name = name; this.avatarIndex = avatarIndex; this.team = team; this.isArchenemy = isArchenemy; this.isReady = isReady; - this.deck = deck; - this.section = section; - this.cards = cards; + this.aiOptions = aiOptions; + } + + private UpdateLobbyPlayerEvent( + final LobbySlotType type, + final String name, + final int avatarIndex, + final int team, + final boolean isArchenemy, + final boolean isReady, + final boolean isDevMode, + final Set aiOptions) { + this.type = type; + this.name = name; + this.avatarIndex = avatarIndex; + this.team = team; + this.isArchenemy = isArchenemy; + this.isReady = isReady; + this.isDevMode = isDevMode; this.aiOptions = aiOptions; } @@ -69,6 +106,9 @@ public final class UpdateLobbyPlayerEvent implements NetEvent { public Boolean getReady() { return isReady; } + public Boolean getDevMode() { + return isDevMode; + } public Deck getDeck() { return deck; } diff --git a/forge-gui/src/main/java/forge/net/server/FServerManager.java b/forge-gui/src/main/java/forge/net/server/FServerManager.java index ed71661eff8..c5af180361c 100644 --- a/forge-gui/src/main/java/forge/net/server/FServerManager.java +++ b/forge-gui/src/main/java/forge/net/server/FServerManager.java @@ -185,6 +185,10 @@ public final class FServerManager { this.localLobby = lobby; } + public boolean isMatchActive() { + return this.localLobby != null && this.localLobby.isMatchActive(); + } + public void setLobbyListener(final ILobbyListener listener) { this.lobbyListener = listener; }