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); } 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/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; 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/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 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); 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 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")) { 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; 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..a8a1e2b3cfd 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,24 +1490,33 @@ 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(); + return p != null && p.isTargeting(o); + } + // Takes one argument like Permanent.Blue+withFlying @Override 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 (!(root instanceof AbilityActivated)) { return false; } } 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-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-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index 5b901753a8f..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,7 +160,7 @@ public class SimulateMatch { - private static void simulateSingleMatch(Match mc, int iGame, boolean outputGamelog) { + private static void simulateSingleMatch(final Match mc, int iGame, boolean outputGamelog) { final StopWatch sw = new StopWatch(); sw.start(); 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); } 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/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 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 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); 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 0344a0219ea..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; } @@ -323,8 +327,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); } }