mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -561,4 +561,12 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
|
||||
// this does overwrite the original MapParams
|
||||
this.originalMapParams = Maps.newHashMap(this.mapParams);
|
||||
}
|
||||
|
||||
protected void copyHelper(CardTraitBase copy, Card host) {
|
||||
copy.originalMapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.mapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.sVars = Maps.newHashMap(sVars);
|
||||
// dont use setHostCard to not trigger the not copied parts yet
|
||||
copy.hostCard = host;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.game;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Predicate<GameObject> interface.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
*/
|
||||
public final class GameObjectPredicates {
|
||||
|
||||
public static final Predicate<GameObject> restriction(final String[] restrictions, final Player sourceController, final Card source, final SpellAbility spellAbility) {
|
||||
return new Predicate<GameObject>() {
|
||||
@Override
|
||||
public boolean apply(final GameObject c) {
|
||||
return (c != null) && c.isValid(restrictions, sourceController, source, spellAbility);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class DamageAllEffect extends DamageBaseEffect {
|
||||
|
||||
if (!usedDamageMap) {
|
||||
preventMap.triggerPreventDamage(false);
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
||||
|
||||
preventMap.clear();
|
||||
damageMap.clear();
|
||||
|
||||
@@ -166,7 +166,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
if (!usedDamageMap) {
|
||||
preventMap.triggerPreventDamage(false);
|
||||
// non combat damage cause lifegain there
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
||||
|
||||
preventMap.clear();
|
||||
damageMap.clear();
|
||||
@@ -215,7 +215,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
if (!usedDamageMap) {
|
||||
preventMap.triggerPreventDamage(false);
|
||||
// non combat damage cause lifegain there
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
||||
|
||||
preventMap.clear();
|
||||
damageMap.clear();
|
||||
|
||||
@@ -136,7 +136,7 @@ public class DamageEachEffect extends DamageBaseEffect {
|
||||
|
||||
if (!usedDamageMap) {
|
||||
preventMap.triggerPreventDamage(false);
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
||||
|
||||
preventMap.clear();
|
||||
damageMap.clear();
|
||||
|
||||
@@ -25,7 +25,7 @@ public class DamageResolveEffect extends SpellAbilityEffect {
|
||||
}
|
||||
// non combat damage cause lifegain there
|
||||
if (damageMap != null) {
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, sa.getHostCard().getGame(), sa);
|
||||
damageMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class FightEffect extends DamageBaseEffect {
|
||||
|
||||
if (!usedDamageMap) {
|
||||
preventMap.triggerPreventDamage(false);
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, fighterA.getGame(), sa);
|
||||
|
||||
preventMap.clear();
|
||||
damageMap.clear();
|
||||
|
||||
@@ -236,7 +236,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
sa.getPreventMap().triggerPreventDamage(false);
|
||||
sa.setPreventMap(null);
|
||||
// non combat damage cause lifegain there
|
||||
sa.getDamageMap().triggerDamageDoneOnce(false, sa);
|
||||
sa.getDamageMap().triggerDamageDoneOnce(false, game, sa);
|
||||
sa.setDamageMap(null);
|
||||
}
|
||||
if (sa.hasParam("ChangeZoneTable")) {
|
||||
|
||||
@@ -4,13 +4,17 @@
|
||||
package forge.game.card;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ForwardingTable;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObjectPredicates;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -44,7 +48,7 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
public void triggerDamageDoneOnce(boolean isCombat, final SpellAbility sa) {
|
||||
public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) {
|
||||
// Source -> Targets
|
||||
for (Map.Entry<Card, Map<GameEntity, Integer>> e : this.rowMap().entrySet()) {
|
||||
final Card sourceLKI = e.getKey();
|
||||
@@ -59,7 +63,7 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
runParams.put(AbilityKey.DamageAmount, sum);
|
||||
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
||||
|
||||
sourceLKI.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
|
||||
|
||||
if (sourceLKI.hasKeyword(Keyword.LIFELINK)) {
|
||||
sourceLKI.getController().gainLife(sum, sourceLKI, sa);
|
||||
@@ -80,9 +84,14 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
runParams.put(AbilityKey.DamageAmount, sum);
|
||||
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
||||
|
||||
ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
runParams.put(AbilityKey.DamageMap, new CardDamageMap(this));
|
||||
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.DamageAll, runParams, false);
|
||||
}
|
||||
/**
|
||||
* special put logic, sum the values
|
||||
@@ -98,4 +107,29 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
public int filteredAmount(String validSource, String validTarget, Card host, SpellAbility sa) {
|
||||
int result = 0;
|
||||
|
||||
Set<Card> filteredSource = null;
|
||||
Set<GameEntity> filteredTarget = null;
|
||||
if (validSource != null) {
|
||||
filteredSource = Sets.newHashSet(Iterables.filter(rowKeySet(), GameObjectPredicates.restriction(validSource.split(","), host.getController(), host, sa)));
|
||||
}
|
||||
if (validTarget != null) {
|
||||
filteredTarget = Sets.newHashSet(Iterables.filter(columnKeySet(), GameObjectPredicates.restriction(validTarget.split(","), host.getController(), host, sa)));
|
||||
}
|
||||
|
||||
for (Table.Cell<Card, GameEntity, Integer> c : cellSet()) {
|
||||
if (filteredSource != null && !filteredSource.contains(c.getRowKey())) {
|
||||
continue;
|
||||
}
|
||||
if (filteredTarget != null && !filteredTarget.contains(c.getColumnKey())) {
|
||||
continue;
|
||||
}
|
||||
result += c.getValue();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -836,7 +836,7 @@ public class Combat {
|
||||
|
||||
// Run the trigger to deal combat damage once
|
||||
// LifeLink for Combat Damage at this place
|
||||
dealtDamageTo.triggerDamageDoneOnce(true, null);
|
||||
dealtDamageTo.triggerDamageDoneOnce(true, game, null);
|
||||
dealtDamageTo.clear();
|
||||
|
||||
counterTable.triggerCountersPutAll(game);
|
||||
|
||||
@@ -74,7 +74,7 @@ public class CostDamage extends CostPart {
|
||||
payer.addDamage(decision.c, source, damageMap, preventMap, table, sa);
|
||||
|
||||
preventMap.triggerPreventDamage(false);
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, source.getGame(), sa);
|
||||
table.triggerCountersPutAll(payer.getGame());
|
||||
|
||||
preventMap.clear();
|
||||
|
||||
@@ -1708,7 +1708,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
if (land.isFaceDown()) {
|
||||
land.turnFaceUp();
|
||||
}
|
||||
game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
|
||||
final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
|
||||
game.updateLastStateForCard(c);
|
||||
|
||||
// play a sound
|
||||
game.fireEvent(new GameEventLandPlayed(this, land));
|
||||
|
||||
@@ -164,9 +164,8 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
*/
|
||||
public final ReplacementEffect copy(final Card host, final boolean lki) {
|
||||
final ReplacementEffect res = (ReplacementEffect) clone();
|
||||
for (String key : getSVars()) {
|
||||
res.setSVar(key, getSVar(key));
|
||||
}
|
||||
|
||||
copyHelper(res, host);
|
||||
|
||||
final SpellAbility sa = this.getOverridingAbility();
|
||||
if (sa != null) {
|
||||
@@ -182,8 +181,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
res.setOtherChoices(null);
|
||||
}
|
||||
|
||||
res.setHostCard(host);
|
||||
|
||||
res.setActiveZone(validHostZones);
|
||||
res.setLayer(getLayer());
|
||||
return res;
|
||||
|
||||
@@ -870,13 +870,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
clone.view = new SpellAbilityView(clone);
|
||||
|
||||
// dont use setHostCard to not trigger the not copied parts yet
|
||||
clone.hostCard = host;
|
||||
|
||||
copyHelper(clone, host);
|
||||
if (!lki && host != null && host.getGame() != null) {
|
||||
host.getGame().addSpellAbility(clone);
|
||||
}
|
||||
// need to clone the maps too so they can be changed
|
||||
clone.originalMapParams = Maps.newHashMap(this.originalMapParams);
|
||||
clone.mapParams = Maps.newHashMap(this.mapParams);
|
||||
|
||||
clone.triggeringObjects = AbilityKey.newMap(this.triggeringObjects);
|
||||
|
||||
@@ -902,7 +900,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
// clear maps for copy, the values will be added later
|
||||
clone.additionalAbilities = Maps.newHashMap();
|
||||
clone.additionalAbilityLists = Maps.newHashMap();
|
||||
clone.sVars = Maps.newHashMap();
|
||||
// run special copy Ability to make a deep copy
|
||||
CardFactory.copySpellAbility(this, clone, host, activ, lki);
|
||||
} catch (final CloneNotSupportedException e) {
|
||||
|
||||
@@ -819,13 +819,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
clone = (StaticAbility) clone();
|
||||
clone.id = lki ? id : nextId();
|
||||
|
||||
// dont use setHostCard to not trigger the not copied parts yet
|
||||
clone.hostCard = host;
|
||||
// need to clone the maps too so they can be changed
|
||||
clone.originalMapParams = Maps.newHashMap(this.originalMapParams);
|
||||
clone.mapParams = Maps.newHashMap(this.mapParams);
|
||||
|
||||
clone.sVars = Maps.newHashMap(this.sVars);
|
||||
copyHelper(clone, host);
|
||||
|
||||
clone.layers = this.generateLayer();
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import forge.game.zone.ZoneType;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.util.TextUtil;
|
||||
@@ -523,9 +522,7 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
public final Trigger copy(Card newHost, boolean lki) {
|
||||
final Trigger copy = (Trigger) clone();
|
||||
|
||||
copy.originalMapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.mapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.setHostCard(newHost);
|
||||
copyHelper(copy, newHost);
|
||||
|
||||
if (getOverridingAbility() != null) {
|
||||
copy.setOverridingAbility(getOverridingAbility().copy(newHost, lki));
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package forge.game.trigger;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class TriggerDamageAll extends Trigger {
|
||||
|
||||
public TriggerDamageAll(Map<String, String> params, Card host, boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (hasParam("CombatDamage")) {
|
||||
if (getParam("CombatDamage").equals("True")) {
|
||||
if (!((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
|
||||
return false;
|
||||
}
|
||||
} else if (getParam("CombatDamage").equals("False")) {
|
||||
if (((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
|
||||
return filterTable(table) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
|
||||
|
||||
sa.setTriggeringObject(AbilityKey.DamageAmount, filterTable(table));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImportantStackObjects(SpellAbility sa) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(Localizer.getInstance().getMessage("lblAmount")).append(": ").append(sa.getTriggeringObject(AbilityKey.DamageAmount));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private int filterTable(CardDamageMap table) {
|
||||
return table.filteredAmount(getParam("ValidSource"), getParam("ValidTarget"), getHostCard(), null);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ public enum TriggerType {
|
||||
CounterRemovedOnce(TriggerCounterRemovedOnce.class),
|
||||
Crewed(TriggerCrewed.class),
|
||||
Cycled(TriggerCycled.class),
|
||||
DamageAll(TriggerDamageAll.class),
|
||||
DamageDealtOnce(TriggerDamageDealtOnce.class),
|
||||
DamageDone(TriggerDamageDone.class),
|
||||
DamageDoneOnce(TriggerDamageDoneOnce.class),
|
||||
|
||||
@@ -17,39 +17,11 @@
|
||||
*/
|
||||
package forge.screens.match;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
import javax.swing.event.PopupMenuListener;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.ImageCache;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.Singletons;
|
||||
import forge.StaticData;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.*;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.card.CardStateName;
|
||||
import forge.control.KeyboardShortcuts;
|
||||
@@ -71,51 +43,26 @@ import forge.game.phase.PhaseType;
|
||||
import forge.game.player.DelayedReveal;
|
||||
import forge.game.player.IHasIcon;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.SpellAbilityView;
|
||||
import forge.game.spellability.StackItemView;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.FNetOverlay;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiDialog;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.SOverlayUtils;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.framework.SDisplayUtil;
|
||||
import forge.gui.framework.SLayoutIO;
|
||||
import forge.gui.framework.VEmptyDoc;
|
||||
import forge.gui.*;
|
||||
import forge.gui.framework.*;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.match.AbstractGuiGame;
|
||||
import forge.menus.IMenuProvider;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.match.controllers.CAntes;
|
||||
import forge.screens.match.controllers.CCombat;
|
||||
import forge.screens.match.controllers.CDetailPicture;
|
||||
import forge.screens.match.controllers.CDev;
|
||||
import forge.screens.match.controllers.CDock;
|
||||
import forge.screens.match.controllers.CLog;
|
||||
import forge.screens.match.controllers.CPrompt;
|
||||
import forge.screens.match.controllers.CStack;
|
||||
import forge.screens.match.controllers.*;
|
||||
import forge.screens.match.menus.CMatchUIMenus;
|
||||
import forge.screens.match.views.VField;
|
||||
import forge.screens.match.views.VHand;
|
||||
import forge.toolbox.FButton;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FOptionPane;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.toolbox.*;
|
||||
import forge.toolbox.FSkin.SkinImage;
|
||||
import forge.toolbox.FTextArea;
|
||||
import forge.toolbox.imaging.FImagePanel;
|
||||
import forge.toolbox.imaging.FImagePanel.AutoSizeImageMode;
|
||||
import forge.toolbox.imaging.FImageUtil;
|
||||
@@ -123,15 +70,26 @@ import forge.toolbox.special.PhaseIndicator;
|
||||
import forge.toolbox.special.PhaseLabel;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.util.Localizer;
|
||||
import forge.view.FView;
|
||||
import forge.view.arcane.CardPanel;
|
||||
import forge.view.arcane.FloatingZone;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
import javax.swing.event.PopupMenuListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Constructs instance of match UI controller, used as a single point of
|
||||
* top-level control for child UIs. Tasks targeting the view of individual
|
||||
@@ -459,6 +417,8 @@ public final class CMatchUI
|
||||
|
||||
@Override
|
||||
public Iterable<PlayerZoneUpdate> tempShowZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
|
||||
List<PlayerZoneUpdate> updatedPlayerZones = Lists.newArrayList();
|
||||
|
||||
for (final PlayerZoneUpdate update : zonesToUpdate) {
|
||||
final PlayerView player = update.getPlayer();
|
||||
for (final ZoneType zone : update.getZones()) {
|
||||
@@ -467,7 +427,9 @@ public final class CMatchUI
|
||||
break;
|
||||
case Hand: // controller hand always shown
|
||||
if (controller != player) {
|
||||
FloatingZone.show(this,player,zone);
|
||||
if (FloatingZone.show(this,player,zone)) {
|
||||
updatedPlayerZones.add(update);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Library:
|
||||
@@ -475,14 +437,16 @@ public final class CMatchUI
|
||||
case Exile:
|
||||
case Flashback:
|
||||
case Command:
|
||||
FloatingZone.show(this,player,zone);
|
||||
if (FloatingZone.show(this,player,zone)) {
|
||||
updatedPlayerZones.add(update);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return zonesToUpdate; //pfps should return only the newly shown zones
|
||||
return updatedPlayerZones;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1086,21 +1050,30 @@ public final class CMatchUI
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
|
||||
if (zones.size() == 1) {
|
||||
switch (zones.iterator().next()) {
|
||||
case Battlefield:
|
||||
case Hand:
|
||||
return true; //don't actually need to open anything, but indicate that zone can be opened
|
||||
default:
|
||||
return false;
|
||||
public PlayerZoneUpdates openZones(PlayerView controller, final Collection<ZoneType> zones, final Map<PlayerView, Object> playersWithTargetables) {
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final PlayerView view : playersWithTargetables.keySet()) {
|
||||
for(final ZoneType zone : zones) {
|
||||
if (zone.equals(ZoneType.Battlefield) || zone.equals(ZoneType.Hand)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zone.equals(ZoneType.Stack)) {
|
||||
// TODO: Remove this if we have ever have a Stack zone that's displayable for Counters
|
||||
continue;
|
||||
}
|
||||
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(view, zone));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
tempShowZones(controller, zonesToUpdate);
|
||||
return zonesToUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
|
||||
public void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates) {
|
||||
hideZones(playerView, playerZoneUpdates);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -61,6 +61,9 @@ import forge.util.Localizer;
|
||||
*/
|
||||
public class FSkin {
|
||||
|
||||
public static final int SYMBOL_WIDTH = 13;
|
||||
public static final int SYMBOL_HEIGHT = 13;
|
||||
|
||||
/**
|
||||
* Retrieves a color from this skin's color map.
|
||||
*
|
||||
@@ -77,7 +80,7 @@ public class FSkin {
|
||||
*
|
||||
* @param clr0 {@link java.awt.Color}
|
||||
* @param step int
|
||||
* @return {@link java.awt.Color}
|
||||
* @return {@link java.awt.CFaceolor}
|
||||
*/
|
||||
public static Color stepColor(final Color clr0, final int step) {
|
||||
int r = clr0.getRed();
|
||||
@@ -1006,7 +1009,7 @@ public class FSkin {
|
||||
|
||||
private static void addEncodingSymbol(final String key, final FSkinProp skinProp) {
|
||||
final String path = ForgeConstants.CACHE_SYMBOLS_DIR + "/" + key.replace("/", "") + ".png";
|
||||
getImage(skinProp).save(path, 13, 13);
|
||||
getImage(skinProp).save(path, SYMBOL_WIDTH, SYMBOL_HEIGHT);
|
||||
}
|
||||
|
||||
public static String encodeSymbols(String str, final boolean formatReminderText) {
|
||||
@@ -1023,7 +1026,7 @@ public class FSkin {
|
||||
//format mana symbols to display as icons
|
||||
pattern = "\\{([A-Z0-9]+)\\}|\\{([A-Z0-9]+)/([A-Z0-9]+)\\}"; //fancy pattern needed so "/" can be omitted from replacement
|
||||
try {
|
||||
replacement = "<img src=\"" + new File(ForgeConstants.CACHE_SYMBOLS_DIR + "/$1$2$3.png").toURI().toURL().toString() + "\">";
|
||||
replacement = "<img src=\"" + new File(ForgeConstants.CACHE_SYMBOLS_DIR + "/$1$2$3.png").toURI().toURL().toString() + "\" width=" + SYMBOL_WIDTH + " height=" + SYMBOL_HEIGHT + ">";
|
||||
str = str.replaceAll(pattern, replacement);
|
||||
} catch (final MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -17,18 +17,6 @@
|
||||
*/
|
||||
package forge.view.arcane;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.Timer;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.game.card.CardView;
|
||||
import forge.gui.framework.SDisplayUtil;
|
||||
@@ -39,10 +27,19 @@ import forge.screens.match.CMatchUI;
|
||||
import forge.toolbox.FMouseAdapter;
|
||||
import forge.toolbox.FScrollPane;
|
||||
import forge.toolbox.MouseTriggerEvent;
|
||||
//import forge.util.collect.FCollectionView;
|
||||
import forge.view.FDialog;
|
||||
import forge.view.FFrame;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
//import forge.util.collect.FCollectionView;
|
||||
|
||||
// show some cards in a new window
|
||||
public abstract class FloatingCardArea extends CardArea {
|
||||
|
||||
@@ -67,18 +64,20 @@ public abstract class FloatingCardArea extends CardArea {
|
||||
getWindow().setFocusableWindowState(false); // should probably do this earlier
|
||||
getWindow().setVisible(true);
|
||||
}
|
||||
|
||||
protected void hideWindow() {
|
||||
onShow();
|
||||
getWindow().setFocusableWindowState(false); // should probably do this earlier
|
||||
getWindow().setVisible(false);
|
||||
getWindow().dispose(); //pfps so that old content does not show up
|
||||
getWindow().dispose(); //pfps so that old content does not show up
|
||||
}
|
||||
|
||||
protected void showOrHideWindow() {
|
||||
if (getWindow().isVisible()) {
|
||||
hideWindow();
|
||||
} else {
|
||||
showWindow();
|
||||
}
|
||||
if (getWindow().isVisible()) {
|
||||
hideWindow();
|
||||
} else {
|
||||
showWindow();
|
||||
}
|
||||
}
|
||||
protected void onShow() {
|
||||
if (!hasBeenShown) {
|
||||
@@ -98,24 +97,31 @@ public abstract class FloatingCardArea extends CardArea {
|
||||
if (hasBeenShown || locLoaded) { return; }
|
||||
super.setLocationRelativeTo(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b0) {
|
||||
if (isVisible() == b0) { return; }
|
||||
if (isVisible() == b0) {
|
||||
return;
|
||||
}
|
||||
if (!b0 && hasBeenShown && locPref != null) {
|
||||
//update preference before hiding window, as otherwise its location will be 0,0
|
||||
prefs.setPref(locPref,
|
||||
getX() + COORD_DELIM + getY() + COORD_DELIM +
|
||||
getWidth() + COORD_DELIM + getHeight());
|
||||
getWidth() + COORD_DELIM + getHeight());
|
||||
//don't call prefs.save(), instead allowing them to be saved when match ends
|
||||
}
|
||||
if (b0) {
|
||||
doRefresh(); // force a refresh before showing to pick up any changes when hidden
|
||||
doRefresh(); // force a refresh before showing to pick up any changes when hidden
|
||||
hasBeenShown = true;
|
||||
}
|
||||
}
|
||||
super.setVisible(b0);
|
||||
}
|
||||
};
|
||||
|
||||
public boolean isVisible() {
|
||||
return window.isVisible();
|
||||
}
|
||||
|
||||
protected FDialog getWindow() {
|
||||
return window;
|
||||
}
|
||||
|
||||
@@ -17,27 +17,26 @@
|
||||
*/
|
||||
package forge.view.arcane;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.WindowConstants;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.match.CMatchUI;
|
||||
import forge.toolbox.FScrollPane;
|
||||
import forge.toolbox.FMouseAdapter;
|
||||
import forge.toolbox.FScrollPane;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.util.Lang;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FloatingZone extends FloatingCardArea {
|
||||
private static final long serialVersionUID = 1927906492186378596L;
|
||||
|
||||
@@ -46,18 +45,44 @@ public class FloatingZone extends FloatingCardArea {
|
||||
private static int getKey(final PlayerView player, final ZoneType zone) {
|
||||
return 40 * player.getId() + zone.hashCode();
|
||||
}
|
||||
|
||||
public static void showOrHide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
|
||||
final FloatingZone cardArea = _init(matchUI, player, zone);
|
||||
cardArea.showOrHideWindow();
|
||||
}
|
||||
public static void show(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
|
||||
|
||||
public static boolean show(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
|
||||
final FloatingZone cardArea = _init(matchUI, player, zone);
|
||||
cardArea.showWindow();
|
||||
|
||||
if (cardArea.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
cardArea.showWindow();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
public static void hide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
|
||||
|
||||
public static boolean hide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
|
||||
final FloatingZone cardArea = _init(matchUI, player, zone);
|
||||
cardArea.hideWindow();
|
||||
|
||||
if (!cardArea.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
cardArea.hideWindow();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static FloatingZone _init(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
|
||||
final int key = getKey(player, zone);
|
||||
FloatingZone cardArea = floatingAreas.get(key);
|
||||
@@ -69,10 +94,12 @@ public class FloatingZone extends FloatingCardArea {
|
||||
}
|
||||
return cardArea;
|
||||
}
|
||||
|
||||
public static CardPanel getCardPanel(final CMatchUI matchUI, final CardView card) {
|
||||
final FloatingZone window = _init(matchUI, card.getController(), card.getZone());
|
||||
return window.getCardPanel(card.getId());
|
||||
}
|
||||
|
||||
public static void refresh(final PlayerView player, final ZoneType zone) {
|
||||
FloatingZone cardArea = floatingAreas.get(getKey(player, zone));
|
||||
if (cardArea != null) {
|
||||
@@ -82,21 +109,23 @@ public class FloatingZone extends FloatingCardArea {
|
||||
|
||||
//refresh flashback zone when graveyard, library, or exile zones updated
|
||||
switch (zone) {
|
||||
case Graveyard:
|
||||
case Library:
|
||||
case Exile:
|
||||
refresh(player, ZoneType.Flashback);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case Graveyard:
|
||||
case Library:
|
||||
case Exile:
|
||||
refresh(player, ZoneType.Flashback);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeAll() {
|
||||
for (final FloatingZone cardArea : floatingAreas.values()) {
|
||||
cardArea.window.setVisible(false);
|
||||
}
|
||||
floatingAreas.clear();
|
||||
}
|
||||
|
||||
public static void refreshAll() {
|
||||
for (final FloatingZone cardArea : floatingAreas.values()) {
|
||||
cardArea.refresh();
|
||||
@@ -110,23 +139,23 @@ public class FloatingZone extends FloatingCardArea {
|
||||
protected FCollection<CardView> cardList;
|
||||
|
||||
private final Comparator<CardView> comp = new Comparator<CardView>() {
|
||||
@Override
|
||||
public int compare(CardView lhs, CardView rhs) {
|
||||
if ( !getMatchUI().mayView(lhs) ) {
|
||||
return ( getMatchUI().mayView(rhs) ) ? 1 : 0 ;
|
||||
} else if ( !getMatchUI().mayView(rhs) ) {
|
||||
return -1;
|
||||
} else {
|
||||
return lhs.getName().compareTo(rhs.getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public int compare(CardView lhs, CardView rhs) {
|
||||
if (!getMatchUI().mayView(lhs)) {
|
||||
return (getMatchUI().mayView(rhs)) ? 1 : 0;
|
||||
} else if (!getMatchUI().mayView(rhs)) {
|
||||
return -1;
|
||||
} else {
|
||||
return lhs.getName().compareTo(rhs.getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected Iterable<CardView> getCards() {
|
||||
Iterable<CardView> zoneCards = player.getCards(zone);
|
||||
if ( zoneCards != null ) {
|
||||
if (zoneCards != null) {
|
||||
cardList = new FCollection<>(zoneCards);
|
||||
if ( sortedByName ) {
|
||||
if (sortedByName) {
|
||||
Collections.sort(cardList, comp);
|
||||
}
|
||||
return cardList;
|
||||
@@ -138,28 +167,28 @@ public class FloatingZone extends FloatingCardArea {
|
||||
private FloatingZone(final CMatchUI matchUI, final PlayerView player0, final ZoneType zone0) {
|
||||
super(matchUI, new FScrollPane(false, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER));
|
||||
window.add(getScrollPane(), "grow, push");
|
||||
window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); //pfps so that old content does not reappear?
|
||||
window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); //pfps so that old content does not reappear?
|
||||
getScrollPane().setViewportView(this);
|
||||
setOpaque(false);
|
||||
switch (zone0) {
|
||||
case Exile:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_EXILE));
|
||||
break;
|
||||
case Graveyard:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_GRAVEYARD));
|
||||
break;
|
||||
case Hand:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_HAND));
|
||||
break;
|
||||
case Library:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_LIBRARY));
|
||||
break;
|
||||
case Flashback:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_FLASHBACK));
|
||||
break;
|
||||
default:
|
||||
locPref = null;
|
||||
break;
|
||||
case Exile:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_EXILE));
|
||||
break;
|
||||
case Graveyard:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_GRAVEYARD));
|
||||
break;
|
||||
case Hand:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_HAND));
|
||||
break;
|
||||
case Library:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_LIBRARY));
|
||||
break;
|
||||
case Flashback:
|
||||
window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_FLASHBACK));
|
||||
break;
|
||||
default:
|
||||
locPref = null;
|
||||
break;
|
||||
}
|
||||
zone = zone0;
|
||||
setPlayer(player0);
|
||||
@@ -167,19 +196,20 @@ public class FloatingZone extends FloatingCardArea {
|
||||
}
|
||||
|
||||
private void toggleSorted() {
|
||||
sortedByName = !sortedByName;
|
||||
setTitle();
|
||||
refresh();
|
||||
// revalidation does not appear to be necessary here
|
||||
getWindow().repaint();
|
||||
sortedByName = !sortedByName;
|
||||
setTitle();
|
||||
refresh();
|
||||
// revalidation does not appear to be necessary here
|
||||
getWindow().repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShow() {
|
||||
super.onShow();
|
||||
super.onShow();
|
||||
if (!hasBeenShown) {
|
||||
getWindow().getTitleBar().addMouseListener(new FMouseAdapter() {
|
||||
@Override public final void onRightClick(final MouseEvent e) {
|
||||
@Override
|
||||
public final void onRightClick(final MouseEvent e) {
|
||||
toggleSorted();
|
||||
}
|
||||
});
|
||||
@@ -188,34 +218,36 @@ public class FloatingZone extends FloatingCardArea {
|
||||
|
||||
private void setTitle() {
|
||||
title = Lang.getPossessedObject(player.getName(), zone.name()) + " (%d)" +
|
||||
( sortedByName ? " - sorted by name (right click in title to not sort)" : " (right click in title to sort)" ) ;
|
||||
(sortedByName ? " - sorted by name (right click in title to not sort)" : " (right click in title to sort)");
|
||||
}
|
||||
|
||||
private void setPlayer(PlayerView player0) {
|
||||
if (player == player0) { return; }
|
||||
if (player == player0) {
|
||||
return;
|
||||
}
|
||||
player = player0;
|
||||
setTitle();
|
||||
setTitle();
|
||||
|
||||
boolean isAi = player0.isAI();
|
||||
switch (zone) {
|
||||
case Exile:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_EXILE : FPref.ZONE_LOC_HUMAN_EXILE;
|
||||
break;
|
||||
case Graveyard:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_GRAVEYARD : FPref.ZONE_LOC_HUMAN_GRAVEYARD;
|
||||
break;
|
||||
case Hand:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_HAND : FPref.ZONE_LOC_HUMAN_HAND;
|
||||
break;
|
||||
case Library:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_LIBRARY : FPref.ZONE_LOC_HUMAN_LIBRARY;
|
||||
break;
|
||||
case Flashback:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_FLASHBACK : FPref.ZONE_LOC_HUMAN_FLASHBACK;
|
||||
break;
|
||||
default:
|
||||
locPref = null;
|
||||
break;
|
||||
case Exile:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_EXILE : FPref.ZONE_LOC_HUMAN_EXILE;
|
||||
break;
|
||||
case Graveyard:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_GRAVEYARD : FPref.ZONE_LOC_HUMAN_GRAVEYARD;
|
||||
break;
|
||||
case Hand:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_HAND : FPref.ZONE_LOC_HUMAN_HAND;
|
||||
break;
|
||||
case Library:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_LIBRARY : FPref.ZONE_LOC_HUMAN_LIBRARY;
|
||||
break;
|
||||
case Flashback:
|
||||
locPref = isAi ? FPref.ZONE_LOC_AI_FLASHBACK : FPref.ZONE_LOC_HUMAN_FLASHBACK;
|
||||
break;
|
||||
default:
|
||||
locPref = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.assets.FSkinImage;
|
||||
@@ -44,6 +43,7 @@ import forge.match.AbstractGuiGame;
|
||||
import forge.match.HostedMatch;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.match.views.VAssignDamage;
|
||||
@@ -307,40 +307,51 @@ public class MatchController extends AbstractGuiGame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
|
||||
public PlayerZoneUpdates openZones(PlayerView controller, final Collection<ZoneType> zones, final Map<PlayerView, Object> playersWithTargetables) {
|
||||
PlayerZoneUpdates updates = new PlayerZoneUpdates();
|
||||
if (zones.size() == 1) {
|
||||
final ZoneType zoneType = zones.iterator().next();
|
||||
switch (zoneType) {
|
||||
case Battlefield:
|
||||
case Command:
|
||||
players.clear(); //clear since no zones need to be restored
|
||||
return true; //Battlefield is always open
|
||||
playersWithTargetables.clear(); //clear since no zones need to be restored
|
||||
default:
|
||||
//open zone tab for given zone if needed
|
||||
boolean result = true;
|
||||
for (final PlayerView player : players.keySet()) {
|
||||
for (final PlayerView player : playersWithTargetables.keySet()) {
|
||||
final VPlayerPanel playerPanel = view.getPlayerPanel(player);
|
||||
players.put(player, playerPanel.getSelectedTab()); //backup selected tab before changing it
|
||||
playersWithTargetables.put(player, playerPanel.getSelectedTab()); //backup selected tab before changing it
|
||||
final InfoTab zoneTab = playerPanel.getZoneTab(zoneType);
|
||||
if (zoneTab == null) {
|
||||
result = false;
|
||||
} else {
|
||||
ZoneType previousZone = playerPanel.getZoneByInfoTab(playerPanel.getSelectedTab());
|
||||
updates.add(new PlayerZoneUpdate(player, previousZone));
|
||||
if (zoneTab != null) {
|
||||
playerPanel.setSelectedTab(zoneTab);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return updates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
|
||||
for (final Entry<PlayerView, Object> player : playersToRestoreZonesFor.entrySet()) {
|
||||
final VPlayerPanel playerPanel = view.getPlayerPanel(player.getKey());
|
||||
if (player.getValue() == null || player.getValue() instanceof InfoTab) {
|
||||
playerPanel.setSelectedTab((InfoTab) player.getValue());
|
||||
public void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates) {
|
||||
for(PlayerZoneUpdate update : playerZoneUpdates) {
|
||||
PlayerView player = update.getPlayer();
|
||||
|
||||
ZoneType zone = null;
|
||||
for(ZoneType type : update.getZones()) {
|
||||
zone = type;
|
||||
break;
|
||||
}
|
||||
|
||||
final VPlayerPanel playerPanel = view.getPlayerPanel(player);
|
||||
if (zone == null) {
|
||||
playerPanel.hideSelectedTab();
|
||||
continue;
|
||||
}
|
||||
|
||||
final InfoTab zoneTab = playerPanel.getZoneTab(zone);
|
||||
playerPanel.setSelectedTab(zoneTab);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,7 +392,7 @@ public class MatchController extends AbstractGuiGame {
|
||||
|
||||
@Override
|
||||
public void hideZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
|
||||
view.hideZones(controller, zonesToUpdate);
|
||||
view.hideZones(controller, zonesToUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -102,18 +102,32 @@ public class VPlayerPanel extends FContainer {
|
||||
return zoneTabs.get(zoneType);
|
||||
}
|
||||
|
||||
public ZoneType getZoneByInfoTab(InfoTab tab) {
|
||||
for(ZoneType zone : zoneTabs.keySet()) {
|
||||
if (zoneTabs.get(zone).equals(tab)) {
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setSelectedZone(ZoneType zoneType) {
|
||||
setSelectedTab(zoneTabs.get(zoneType));
|
||||
}
|
||||
|
||||
public void hideSelectedTab() {
|
||||
if (selectedTab != null) {
|
||||
selectedTab.displayArea.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedTab(InfoTab selectedTab0) {
|
||||
if (selectedTab == selectedTab0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedTab != null) {
|
||||
selectedTab.displayArea.setVisible(false);
|
||||
}
|
||||
hideSelectedTab();
|
||||
|
||||
selectedTab = selectedTab0;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import forge.menu.FDropDown;
|
||||
import forge.menu.FMenuItem;
|
||||
import forge.menu.FMenuTab;
|
||||
import forge.menu.FPopupMenu;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.screens.match.MatchController;
|
||||
import forge.screens.match.TargetingOverlay;
|
||||
import forge.toolbox.FCardPanel;
|
||||
@@ -55,6 +56,7 @@ public class VStack extends FDropDown {
|
||||
private StackInstanceDisplay activeItem;
|
||||
private StackItemView activeStackInstance;
|
||||
private Map<PlayerView, Object> playersWithValidTargets;
|
||||
private PlayerZoneUpdates restorablePlayerZones = null;
|
||||
|
||||
private int stackSize;
|
||||
|
||||
@@ -70,6 +72,8 @@ public class VStack extends FDropDown {
|
||||
private void revealTargetZones() {
|
||||
if (activeStackInstance == null) { return; }
|
||||
|
||||
PlayerView player = MatchController.instance.getCurrentPlayer();
|
||||
|
||||
final Set<ZoneType> zones = new HashSet<>();
|
||||
playersWithValidTargets = new HashMap<>();
|
||||
for (final CardView c : activeStackInstance.getTargetCards()) {
|
||||
@@ -79,14 +83,15 @@ public class VStack extends FDropDown {
|
||||
}
|
||||
}
|
||||
if (zones.isEmpty() || playersWithValidTargets.isEmpty()) { return; }
|
||||
MatchController.instance.openZones(zones, playersWithValidTargets);
|
||||
restorablePlayerZones = MatchController.instance.openZones(player, zones, playersWithValidTargets);
|
||||
}
|
||||
|
||||
//restore old zones when active stack instance changes
|
||||
private void restoreOldZones() {
|
||||
if (playersWithValidTargets == null) { return; }
|
||||
MatchController.instance.restoreOldZones(playersWithValidTargets);
|
||||
playersWithValidTargets = null;
|
||||
if (restorablePlayerZones == null) { return; }
|
||||
PlayerView player = MatchController.instance.getCurrentPlayer();
|
||||
MatchController.instance.restoreOldZones(player, restorablePlayerZones);
|
||||
restorablePlayerZones = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#Add one announcement per line
|
||||
Theros Beyond Death Prerelease!
|
||||
"Prerelease" limited mode for latest sets
|
||||
We believe the issue with 1.8.0_211 or greater have been resolved. Let us know if you are still on the latest version and things are better now.
|
||||
Bunches of bug fixes,
|
||||
Continued work on Translations
|
||||
[b]Forge now requires Java 8 (or newer). You will not be able to start the game if you are not yet running Java 8.[/b]
|
||||
For some reason Oracle hates Forge and version 1.8.0_211 does bad things with Forge for unknown reasons. Downgrade to 202 for a beter time.
|
||||
https://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase8-2177648.html
|
||||
We have a Discord server for hanging out with Forge devs and other Forge fans. Feel free to [url=https://discord.gg/3v9JCVr]jump on in and say hi[/url]!
|
||||
@@ -33,6 +33,7 @@ tjtillman
|
||||
tojammot
|
||||
torridus
|
||||
Xyx
|
||||
Zimtente
|
||||
Zuchinni
|
||||
|
||||
(If you think your name should be on this list, add it with your next contribution)
|
||||
@@ -1556,6 +1556,39 @@ Evolving Wilds
|
||||
13 Swamp|M20
|
||||
|
||||
[ELD Secret Cards]
|
||||
Garruk, Cursed Huntsman|ELD|2
|
||||
Oko, Thief of Crowns|ELD|2
|
||||
The Royal Scions|ELD|2
|
||||
Ardenvale Tactician|ELD|2
|
||||
Faerie Guidemother|ELD|2
|
||||
Giant Killer|ELD|2
|
||||
Lonesome Unicorn|ELD|2
|
||||
Realm-Cloaked Giant|ELD|2
|
||||
Shepherd of the Flock|ELD|2
|
||||
Silverflame Squire|ELD|2
|
||||
Animating Faerie|ELD|2
|
||||
Brazen Borrower|ELD|2
|
||||
Fae of Wishes|ELD|2
|
||||
Hypnotic Sprite|ELD|2
|
||||
Merfolk Secretkeeper|ELD|2
|
||||
Queen of Ice|ELD|2
|
||||
Foulmire Knight|ELD|2
|
||||
Murderous Rider|ELD|2
|
||||
Order of Midnight|ELD|2
|
||||
Reaper of Night|ELD|2
|
||||
Smitten Swordmaster|ELD|2
|
||||
Bonecrusher Giant|ELD|2
|
||||
Embereth Shieldbreaker|ELD|2
|
||||
Merchant of the Vale|ELD|2
|
||||
Rimrock Knight|ELD|2
|
||||
Beanstalk Giant|ELD|2
|
||||
Curious Pair|ELD|2
|
||||
Flaxen Intruder|ELD|2
|
||||
Garenbrig Carver|ELD|2
|
||||
Lovestruck Beast|ELD|2
|
||||
Rosethorn Acolyte|ELD|2
|
||||
Tuinvale Treefolk|ELD|2
|
||||
Oakhame Ranger|ELD|2
|
||||
Kenrith, the Returned King
|
||||
Rowan, Fearless Sparkmage
|
||||
Garrison Griffin
|
||||
@@ -1587,6 +1620,70 @@ Syr Gwyn, Hero of Ashvale
|
||||
Arcane Signet
|
||||
Tome of Legends
|
||||
Command Tower
|
||||
Acclaimed Contender|ELD|2
|
||||
Charming Prince|ELD|2
|
||||
The Circle of Loyalty|ELD|2
|
||||
Happily Ever After|ELD|2
|
||||
Harmonious Archon|ELD|2
|
||||
Hushbringer|ELD|2
|
||||
Linden, the Steadfast Queen|ELD|2
|
||||
Worthy Knight|ELD|2
|
||||
Emry, Lurker of the Loch|ELD|2
|
||||
Folio of Fancies|ELD|2
|
||||
Gadwick, the Wizened|ELD|2
|
||||
The Magic Mirror|ELD|2
|
||||
Midnight Clock|ELD|2
|
||||
Mirrormade|ELD|2
|
||||
Stolen by the Fae|ELD|2
|
||||
Vantress Gargoyle|ELD|2
|
||||
Ayara, First of Locthwain|ELD|2
|
||||
Blacklance Paragon|ELD|2
|
||||
The Cauldron of Eternity|ELD|2
|
||||
Clackbridge Troll|ELD|2
|
||||
Oathsworn Knight|ELD|2
|
||||
Piper of the Swarm|ELD|2
|
||||
Rankle, Master of Pranks|ELD|2
|
||||
Wishclaw Talisman|ELD|2
|
||||
Witch's Vengeance|ELD|2
|
||||
Embercleave|ELD|2
|
||||
Fervent Champion|ELD|2
|
||||
Fires of Invention|ELD|2
|
||||
Irencrag Feat|ELD|2
|
||||
Irencrag Pyromancer|ELD|2
|
||||
Opportunistic Dragon|ELD|2
|
||||
Robber of the Rich|ELD|2
|
||||
Sundering Stroke|ELD|2
|
||||
Torbran, Thane of Red Fell|ELD|2
|
||||
Feasting Troll King|ELD|2
|
||||
Gilded Goose|ELD|2
|
||||
The Great Henge|ELD|2
|
||||
Once Upon A Time|ELD|2
|
||||
Questing Beast|ELD|2
|
||||
Return of the Wildspeaker|ELD|2
|
||||
Wicked Wolf|ELD|2
|
||||
Wildborn Preserver|ELD|2
|
||||
Yorvo, Lord of Garenbrig|ELD|2
|
||||
Dance of the Manse|ELD|2
|
||||
Doom Foretold|ELD|2
|
||||
Escape to the Wilds|ELD|2
|
||||
Faeburrow Elder|ELD|2
|
||||
Lochmere Serpent|ELD|2
|
||||
Outlaws' Merriment|ELD|2
|
||||
Stormfist Crusader|ELD|2
|
||||
Sorcerous Spyglass|ELD|2
|
||||
Stonecoil Serpent|ELD|2
|
||||
Castle Ardenvale|ELD|2
|
||||
Castle Embereth|ELD|2
|
||||
Castle Garenbrig|ELD|2
|
||||
Castle Locthwain|ELD|2
|
||||
Castle Vantress|ELD|2
|
||||
Fabled Passage|ELD|2
|
||||
Piper of the Swarm|ELD|3
|
||||
Glass Casket|ELD|2
|
||||
Slaying Fire|ELD|2
|
||||
Kenrith's Transformation|ELD|2
|
||||
Improbable Alliance|ELD|2
|
||||
Inspiring Veteran|ELD|2
|
||||
|
||||
[THB Secret Cards]
|
||||
Athreos, Shroud-Veiled
|
||||
@@ -1627,6 +1724,7 @@ Eidolon of Obstruction|THB|2
|
||||
Heliod's Intervention|THB|2
|
||||
Idyllic Tutor|THB|2
|
||||
Shatter the Sky|THB|2
|
||||
Taranika, Akroan Veteran|THB|2
|
||||
Ashiok's Erasure|THB|2
|
||||
Nadir Kraken|THB|2
|
||||
Protean Thaumaturge|THB|2
|
||||
@@ -1675,3 +1773,9 @@ Temple of Deceit|THB|2
|
||||
Temple of Enlightenment|THB|2
|
||||
Temple of Malice|THB|2
|
||||
Temple of Plenty|THB|2
|
||||
Arasta of the Endless Web|THB|3
|
||||
Alseid of Life's Bounty|THB|2
|
||||
Thirst For Meaning|THB|2
|
||||
Gray Merchant of Asphodel|THB|2
|
||||
Thrill of Possibility|THB|2
|
||||
Wolfwillow Haven|THB|2
|
||||
@@ -2,7 +2,7 @@ Name:Hapatra, Vizier of Poisons
|
||||
ManaCost:B G
|
||||
Types:Legendary Creature Human Cleric
|
||||
PT:2/2
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, you may put a -1/-1 counter on target creature.
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | CombatDamage$ True | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, you may put a -1/-1 counter on target creature.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ M1M1 | CounterNum$ 1 | IsCurse$ True
|
||||
T:Mode$ CounterAddedOnce | ValidCard$ Creature | ValidSource$ You | CounterType$ M1M1 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you put one or more -1/-1 counters on a creature, create a 1/1 green Snake creature token with deathtouch.
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_snake_deathtouch | TokenOwner$ You | LegacyImage$ g 1 1 snake deathtouch akh
|
||||
|
||||
8
forge-gui/res/cardsfolder/m/mindblade_render.txt
Normal file
8
forge-gui/res/cardsfolder/m/mindblade_render.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Mindblade Render
|
||||
ManaCost:1 B
|
||||
Types:Creature Azra Warrior
|
||||
PT:1/3
|
||||
T:Mode$ DamageAll | ValidSource$ Creature.Warrior | ValidTarget$ Player.Opponent | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever your opponents are dealt combat damage, if any of that damage was dealt by a Warrior, you draw a card and you lose 1 life.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife
|
||||
SVar:DBLoseLife:DB$LoseLife | Defined$ You | LifeAmount$ 1
|
||||
Oracle: Whenever your opponents are dealt combat damage, if any of that damage was dealt by a Warrior, you draw a card and you lose 1 life.
|
||||
@@ -1,5 +1,5 @@
|
||||
Name:Mire in Misery
|
||||
ManaCost:1 B
|
||||
Types:Sorcery
|
||||
A:SP$ Sacrifice | Cost$ 1 B | Valid$ Creature,Enchantment | SacMessage$ Creature or Enchantment | Defined$ Player.Opponent | SpellDescription$ Each opponent sacrifices a creature or enchantment.
|
||||
A:SP$ Sacrifice | Cost$ 1 B | SacValid$ Creature,Enchantment | SacMessage$ Creature or Enchantment | Defined$ Player.Opponent | SpellDescription$ Each opponent sacrifices a creature or enchantment.
|
||||
Oracle:Each opponent sacrifices a creature or enchantment.
|
||||
|
||||
61
forge-gui/res/editions/Secret Lair Drop Series.txt
Normal file
61
forge-gui/res/editions/Secret Lair Drop Series.txt
Normal file
@@ -0,0 +1,61 @@
|
||||
[metadata]
|
||||
Code=SLD
|
||||
Date=2019-12-02
|
||||
Name=Secret Lair Drop Series
|
||||
MciCode=sld
|
||||
Type=Other
|
||||
|
||||
[cards]
|
||||
1 R Snow-Covered Plains
|
||||
2 R Snow-Covered Island
|
||||
3 R Snow-Covered Swamp
|
||||
4 R Snow-Covered Mountain
|
||||
5 R Snow-Covered Forest
|
||||
6 R Bloodghast
|
||||
7 R Golgari Thug
|
||||
8 R Life from the Loam
|
||||
9 M Reaper King
|
||||
10 M Sliver Overlord
|
||||
11 M The Ur-Dragon
|
||||
12 M Bitterblossom
|
||||
17 R Goblin Bushwhacker
|
||||
18 R Goblin Sharpshooter
|
||||
19 R Goblin King
|
||||
20 R Goblin Lackey
|
||||
21 R Goblin Piledriver
|
||||
22 R Leonin Warleader
|
||||
23 R Regal Caracal
|
||||
24 R Qasali Slingers
|
||||
25 M Arahbo, Roar of the World
|
||||
26 M Mirri, Weatherlight Duelist
|
||||
29 R Serum Visions
|
||||
30 R Serum Visions
|
||||
31 R Serum Visions
|
||||
32 R Serum Visions
|
||||
33 R Ink-Eyes, Servant of Oni
|
||||
34 R Marrow-Gnawer
|
||||
35 R Pack Rat
|
||||
36 R Rat Colony
|
||||
68 M Heliod, God of the Sun
|
||||
69 M Karametra, God of Harvests
|
||||
70 M Iroas, God of Victory
|
||||
71 M Thassa, God of the Sea
|
||||
72 M Ephara, God of the Polis
|
||||
73 M Kruphix, God of Horizons
|
||||
74 M Erebos, God of the Dead
|
||||
75 M Phenax, God of Deception
|
||||
76 M Athreos, God of Passage
|
||||
77 M Purphoros, God of the Forge
|
||||
78 M Mogis, God of Slaughter
|
||||
79 M Keranos, God of Storms
|
||||
80 M Nylea, God of the Hunt
|
||||
81 M Xenagos, God of Revels
|
||||
82 M Pharika, God of Affliction
|
||||
|
||||
[tokens]
|
||||
b_1_1_faerie_rogue_flying
|
||||
b_1_1_faerie_rogue_flying
|
||||
b_1_1_faerie_rogue_flying
|
||||
b_1_1_faerie_rogue_flying
|
||||
w_1_1_cat_lifelink
|
||||
w_1_1_cat_lifelink
|
||||
25
forge-gui/res/editions/Secret Lair Promos.txt
Normal file
25
forge-gui/res/editions/Secret Lair Promos.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
[metadata]
|
||||
Code=PSLD
|
||||
Date=2019-12-02
|
||||
Name=Secret Lair Promos
|
||||
MciCode=sld
|
||||
Type=Other
|
||||
|
||||
[cards]
|
||||
503 M Gideon Blackblade
|
||||
504 U Teyo, the Shieldmage
|
||||
505 U The Wanderer
|
||||
506 R Jace, Wielder of Mysteries
|
||||
514 R Sarkhan the Masterless
|
||||
516 U Arlinn, Voice of the Pack
|
||||
520 R Ajani, the Greathearted
|
||||
521 R Domri, Anarch of Bolas
|
||||
522 M Nicol Bolas, Dragon-God
|
||||
523 R Ral, Storm Conduit
|
||||
524 R Sorin, Vengeful Bloodlord
|
||||
525 R Tamiyo, Collector of Tales
|
||||
526 R Teferi, Time Raveler
|
||||
527 U Angrath, Captain of Chaos
|
||||
528 U Ashiok, Dream Render
|
||||
530 U Huatli, the Sun's Heart
|
||||
533 U Nahiri, Storm of Stone
|
||||
@@ -263,21 +263,25 @@ Prerelease=6 Boosters, 1 RareMythic+
|
||||
252 L Swamp
|
||||
253 L Mountain
|
||||
254 L Forest
|
||||
#255 M Elspeth, Sun's Nemesis
|
||||
#256 M Ashiok, Nightmare Muse
|
||||
#257 M Calix, Destiny's Hand
|
||||
#258 U Daxos, Blessed by the Sun
|
||||
#259 M Heliod, Sun-Crowned
|
||||
#260 U Callaphe, Beloved of the Sea
|
||||
#261 M Thassa, Deep-Dwelling
|
||||
#262 M Erebos, Bleak-Hearted
|
||||
#263 U Tymaret, Chosen from Death
|
||||
#264 U Anax, Hardened in the Forge
|
||||
#265 M Purphoros, Bronze-Blooded
|
||||
#266 M Nylea, Keen-Eyed
|
||||
#267 U Renata, Called to the Hunt
|
||||
#268 M Klothys, God of Destiny
|
||||
#Statue borderless planeswalkers
|
||||
255 M Elspeth, Sun's Nemesis
|
||||
256 M Ashiok, Nightmare Muse
|
||||
#Constellation Gods and Demigods
|
||||
257 M Calix, Destiny's Hand
|
||||
258 U Daxos, Blessed by the Sun
|
||||
259 M Heliod, Sun-Crowned
|
||||
260 U Callaphe, Beloved of the Sea
|
||||
261 M Thassa, Deep-Dwelling
|
||||
262 M Erebos, Bleak-Hearted
|
||||
263 U Tymaret, Chosen from Death
|
||||
264 U Anax, Hardened in the Forge
|
||||
265 M Purphoros, Bronze-Blooded
|
||||
266 M Nylea, Keen-Eyed
|
||||
267 U Renata, Called to the Hunt
|
||||
268 M Klothys, God of Destiny
|
||||
#Buy-A-Box Promo
|
||||
269 M Athreos, Shroud-Veiled
|
||||
#Planeswalker Deck Cards
|
||||
270 M Elspeth, Undaunted Hero
|
||||
271 U Eidolon of Inspiration
|
||||
272 R Elspeth's Devotee
|
||||
@@ -296,6 +300,7 @@ Prerelease=6 Boosters, 1 RareMythic+
|
||||
285 L Mountain
|
||||
286 L Forest
|
||||
287 L Forest
|
||||
#Theme Booster Exclusive Rares
|
||||
288 R Grasping Giant
|
||||
289 R Victory's Envoy
|
||||
290 R Sphinx Mindbreaker
|
||||
@@ -306,59 +311,69 @@ Prerelease=6 Boosters, 1 RareMythic+
|
||||
295 R Terror of Mount Velus
|
||||
296 R Ironscale Hydra
|
||||
297 R Treeshaker Chimera
|
||||
#298 R Archon of Sun's Grace
|
||||
#299 R Eidolon of Obstruction
|
||||
#300 R Heliod's Intervention
|
||||
#301 R Idyllic Tutor
|
||||
#302 R Shatter the Sky
|
||||
#304 R Ashiok's Erasure
|
||||
#305 R Nadir Kraken
|
||||
#306 R Protean Thaumaturge
|
||||
#307 R Thassa's Intervention
|
||||
#308 R Thassa's Oracle
|
||||
#309 R Thryx, the Sudden Storm
|
||||
#310 R Wavebreak Hippocamp
|
||||
#311 R Aphemia, the Cacophony
|
||||
#312 R Eat to Extinction
|
||||
#313 R Erebos's Intervention
|
||||
#314 R Gravebreaker Lamia
|
||||
#315 R Nightmare Shepherd
|
||||
#316 R Treacherous Blessing
|
||||
#317 R Woe Strider
|
||||
#318 M Ox of Agonas
|
||||
#319 R Phoenix of Ash
|
||||
#320 R Purphoros's Intervention
|
||||
#321 R Storm Herald
|
||||
#322 R Storm's Wrath
|
||||
#323 R Tectonic Giant
|
||||
#324 R Underworld Breach
|
||||
#325 R Arasta of the Endless Web
|
||||
#326 R Dryad of the Ilysian Grove
|
||||
#327 R Mantle of the Wolf
|
||||
#328 R Nessian Boar
|
||||
#329 R Nylea's Intervention
|
||||
#330 M Nyxbloom Ancient
|
||||
#331 R Setessan Champion
|
||||
#332 R Allure of the Unknown
|
||||
#333 R Atris, Oracle of Half-Truths
|
||||
#334 R Bronzehide Lion
|
||||
#335 R Dalakos, Crafter of Wonders
|
||||
#336 R Dream Trawler
|
||||
#337 R Enigmatic Incarnation
|
||||
#338 R Gallia of the Endless Dance
|
||||
#339 R Haktos the Unscarred
|
||||
#340 M Kroxa, Titan of Death's Hunger
|
||||
#341 R Kunoros, Hound of Athreos
|
||||
#342 M Polukranos, Unchained
|
||||
#343 M Uro, Titan of Nature's Wrath
|
||||
#344 R Nyx Lotus
|
||||
#345 R Shadowspear
|
||||
#346 R Labyrinth of Skophos
|
||||
#347 R Temple of Abandon
|
||||
#348 R Temple of Deceit
|
||||
#349 R Temple of Enlightenment
|
||||
#350 R Temple of Malice
|
||||
#351 R Temple of Plenty
|
||||
#Borderless art rares and mythics
|
||||
298 R Archon of Sun's Grace
|
||||
299 R Eidolon of Obstruction
|
||||
300 R Heliod's Intervention
|
||||
301 R Idyllic Tutor
|
||||
302 R Shatter the Sky
|
||||
303 R Taranika, Akroan Veteran
|
||||
304 R Ashiok's Erasure
|
||||
305 R Nadir Kraken
|
||||
306 R Protean Thaumaturge
|
||||
307 R Thassa's Intervention
|
||||
308 R Thassa's Oracle
|
||||
309 R Thryx, the Sudden Storm
|
||||
310 R Wavebreak Hippocamp
|
||||
311 R Aphemia, the Cacophony
|
||||
312 R Eat to Extinction
|
||||
313 R Erebos's Intervention
|
||||
314 R Gravebreaker Lamia
|
||||
315 R Nightmare Shepherd
|
||||
316 R Treacherous Blessing
|
||||
317 R Woe Strider
|
||||
318 M Ox of Agonas
|
||||
319 R Phoenix of Ash
|
||||
320 R Purphoros's Intervention
|
||||
321 R Storm Herald
|
||||
322 R Storm's Wrath
|
||||
323 R Tectonic Giant
|
||||
324 R Underworld Breach
|
||||
325 R Arasta of the Endless Web
|
||||
326 R Dryad of the Ilysian Grove
|
||||
327 R Mantle of the Wolf
|
||||
328 R Nessian Boar
|
||||
329 R Nylea's Intervention
|
||||
330 M Nyxbloom Ancient
|
||||
331 R Setessan Champion
|
||||
332 R Allure of the Unknown
|
||||
333 R Atris, Oracle of Half-Truths
|
||||
334 R Bronzehide Lion
|
||||
335 R Dalakos, Crafter of Wonders
|
||||
336 R Dream Trawler
|
||||
337 R Enigmatic Incarnation
|
||||
338 R Gallia of the Endless Dance
|
||||
339 R Haktos the Unscarred
|
||||
340 M Kroxa, Titan of Death's Hunger
|
||||
341 R Kunoros, Hound of Athreos
|
||||
342 M Polukranos, Unchained
|
||||
343 M Uro, Titan of Nature's Wrath
|
||||
344 R Nyx Lotus
|
||||
345 R Shadowspear
|
||||
346 R Labyrinth of Skophos
|
||||
347 R Temple of Abandon
|
||||
348 R Temple of Deceit
|
||||
349 R Temple of Enlightenment
|
||||
350 R Temple of Malice
|
||||
351 R Temple of Plenty
|
||||
#Bundle promo
|
||||
R Arasta of the Endless Web
|
||||
#Promo Pack
|
||||
U Alseid of Life's Bounty
|
||||
C Thirst For Meaning
|
||||
U Gray Merchant of Asphodel
|
||||
C Thrill of Possibility
|
||||
U Wolfwillow Haven
|
||||
|
||||
[tokens]
|
||||
b_2_2_zombie
|
||||
|
||||
@@ -279,7 +279,44 @@ Prerelease=6 Boosters, 1 RareMythic+
|
||||
267 L Forest
|
||||
268 L Forest
|
||||
269 L Forest
|
||||
#Borderless Planeswalkers
|
||||
270 M Garruk, Cursed Huntsman
|
||||
271 M Oko, Thief of Crowns
|
||||
272 M The Royal Scions
|
||||
#Storybook Frames
|
||||
C Ardenvale Tactician
|
||||
C Faerie Guidemother
|
||||
R Giant Killer
|
||||
C Lonesome Unicorn
|
||||
M Realm-Cloaked Giant
|
||||
U Shepherd of the Flock
|
||||
C Silverflame Squire
|
||||
U Animating Faerie
|
||||
M Brazen Borrower
|
||||
R Fae of Wishes
|
||||
U Hypnotic Sprite
|
||||
C Merfolk Secretkeeper
|
||||
C Queen of Ice
|
||||
U Foulmire Knight
|
||||
R Murderous Rider
|
||||
U Order of Midnight
|
||||
C Reaper of Night
|
||||
C Smitten Swordmaster
|
||||
R Bonecrusher Giant
|
||||
U Embereth Shieldbreaker
|
||||
C Merchant of the Vale
|
||||
C Rimrock Knight
|
||||
U Beanstalk Giant
|
||||
C Curious Pair
|
||||
U Flaxen Intruder
|
||||
C Garenbrig Carver
|
||||
R Lovestruck Beast
|
||||
C Rosethorn Acolyte
|
||||
C Tuinvale Treefolk
|
||||
U Oakhame Ranger
|
||||
#Buy-A-Box Promo
|
||||
303 M Kenrith, the Returned King
|
||||
#Planeswalker Deck Cards
|
||||
304 M Rowan, Fearless Sparkmage
|
||||
305 C Garrison Griffin
|
||||
306 U Rowan's Battleguard
|
||||
@@ -310,6 +347,73 @@ Prerelease=6 Boosters, 1 RareMythic+
|
||||
331 C Arcane Signet
|
||||
332 R Tome of Legends
|
||||
333 C Command Tower
|
||||
#Borderless art rares and mythics
|
||||
R Acclaimed Contender
|
||||
R Charming Prince
|
||||
M The Circle of Loyalty
|
||||
R Happily Ever After
|
||||
M Harmonious Archon
|
||||
R Hushbringer
|
||||
R Linden, the Steadfast Queen
|
||||
R Worthy Knight
|
||||
R Emry, Lurker of the Loch
|
||||
R Folio of Fancies
|
||||
R Gadwick, the Wizened
|
||||
M The Magic Mirror
|
||||
R Midnight Clock
|
||||
R Mirrormade
|
||||
R Stolen by the Fae
|
||||
R Vantress Gargoyle
|
||||
R Ayara, First of Locthwain
|
||||
R Blacklance Paragon
|
||||
M The Cauldron of Eternity
|
||||
R Clackbridge Troll
|
||||
R Oathsworn Knight
|
||||
R Piper of the Swarm
|
||||
M Rankle, Master of Pranks
|
||||
R Wishclaw Talisman
|
||||
R Witch's Vengeance
|
||||
M Embercleave
|
||||
R Fervent Champion
|
||||
R Fires of Invention
|
||||
R Irencrag Feat
|
||||
R Irencrag Pyromancer
|
||||
R Opportunistic Dragon
|
||||
M Robber of the Rich
|
||||
R Sundering Stroke
|
||||
R Torbran, Thane of Red Fell
|
||||
R Feasting Troll King
|
||||
R Gilded Goose
|
||||
M The Great Henge
|
||||
R Once Upon A Time
|
||||
M Questing Beast
|
||||
R Return of the Wildspeaker
|
||||
R Wicked Wolf
|
||||
R Wildborn Preserver
|
||||
R Yorvo, Lord of Garenbrig
|
||||
R Dance of the Manse
|
||||
R Doom Foretold
|
||||
R Escape to the Wilds
|
||||
R Faeburrow Elder
|
||||
R Lochmere Serpent
|
||||
M Outlaws' Merriment
|
||||
R Stormfist Crusader
|
||||
R Sorcerous Spyglass
|
||||
R Stonecoil Serpent
|
||||
R Castle Ardenvale
|
||||
R Castle Embereth
|
||||
R Castle Garenbrig
|
||||
R Castle Locthwain
|
||||
R Castle Vantress
|
||||
R Fabled Passage
|
||||
#Bundle promo
|
||||
R Piper of the Swarm
|
||||
#Promo Pack
|
||||
U Glass Casket
|
||||
U Slaying Fire
|
||||
U Kenrith's Transformation
|
||||
U Improbable Alliance
|
||||
U Inspiring Veteran
|
||||
|
||||
[tokens]
|
||||
w_0_1_goat
|
||||
|
||||
48
forge-gui/res/editions/Ultimate Box Topper.txt
Normal file
48
forge-gui/res/editions/Ultimate Box Topper.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
[metadata]
|
||||
Code=PUMA
|
||||
Date=2018-12-07
|
||||
Name=Ultimate Box Topper - Ultimate Masters
|
||||
MciCode=uma
|
||||
Type=Other
|
||||
|
||||
[cards]
|
||||
1 M Emrakul, the Aeons Torn
|
||||
2 M Karn Liberated
|
||||
3 M Kozilek, Butcher of Truth
|
||||
4 M Ulamog, the Infinite Gyre
|
||||
5 M Snapcaster Mage
|
||||
6 M Temporal Manipulation
|
||||
7 M Bitterblossom
|
||||
8 M Demonic Tutor
|
||||
9 M Goryo's Vengeance
|
||||
10 M Liliana of the Veil
|
||||
11 M Mikaeus, the Unhallowed
|
||||
12 M Reanimate
|
||||
13 M Tasigur, the Golden Fang
|
||||
14 M Balefire Dragon
|
||||
15 M Through the Breach
|
||||
16 M Eternal Witness
|
||||
17 M Life from the Loam
|
||||
18 M Noble Hierarch
|
||||
19 M Tarmogoyf
|
||||
20 M Vengevine
|
||||
21 M Gaddock Teeg
|
||||
22 M Leovold, Emissary of Trest
|
||||
23 M Lord of Extinction
|
||||
24 M Maelstrom Pulse
|
||||
25 M Sigarda, Host of Herons
|
||||
26 M Fulminator Mage
|
||||
27 M Kitchen Finks
|
||||
28 M Engineered Explosives
|
||||
29 M Mana Vault
|
||||
30 M Platinum Emperion
|
||||
31 M Ancient Tomb
|
||||
32 M Cavern of Souls
|
||||
33 M Celestial Colonnade
|
||||
34 M Creeping Tar Pit
|
||||
35 M Dark Depths
|
||||
36 M Karakas
|
||||
37 M Lavaclaw Reaches
|
||||
38 M Raging Ravine
|
||||
39 M Stirring Wildwood
|
||||
40 M Urborg, Tomb of Yawgmoth
|
||||
@@ -3,5 +3,5 @@ Name:Brawl
|
||||
Order:101
|
||||
Type:Casual
|
||||
Subtype:Commander
|
||||
Sets:GRN, RNA, WAR, M20, ELD
|
||||
Banned:Oko, Thief of Crowns
|
||||
Sets:GRN, RNA, WAR, M20, ELD, THB
|
||||
Banned:Sorcerous Spyglass;Oko, Thief of Crowns
|
||||
|
||||
20
forge-gui/res/puzzle/PS_THB3.pzl
Normal file
20
forge-gui/res/puzzle/PS_THB3.pzl
Normal file
@@ -0,0 +1,20 @@
|
||||
[metadata]
|
||||
Name:Possibility Storm - Theros Beyond Death #03
|
||||
URL:https://i0.wp.com/www.possibilitystorm.com/wp-content/uploads/2020/02/146.-THB3-1-scaled.jpg
|
||||
Goal:Win
|
||||
Turns:1
|
||||
Difficulty:Mythic
|
||||
Description:Win this turn. Assume any cards drawn are not relevant to solving the puzzle. Assume your opponent has no cards in hand.
|
||||
[state]
|
||||
humanlife=20
|
||||
ailife=17
|
||||
turn=1
|
||||
activeplayer=human
|
||||
activephase=MAIN1
|
||||
humanhand=Gingerbrute;Purphoros, Bronze-Blooded;Nyxborn Brute;Mire's Grasp;Omen of the Dead
|
||||
humanlibrary=Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt
|
||||
humangraveyard=Rotting Regisaur;Erebos, Bleak-Hearted;Kroxa, Titan of Death's Hunger
|
||||
humanbattlefield=Lazav, the Multifarious;Protean Thaumaturge;Bloodmist Infiltrator;The Royal Scions|Counters:LOYALTY=3;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs
|
||||
aibattlefield=Cerulean Drake;Cerulean Drake;Archon of Sun's Grace
|
||||
aiprecast=Archon of Sun's Grace:TrigToken;Archon of Sun's Grace:TrigToken
|
||||
removesummoningsickness=true
|
||||
92
forge-gui/res/quest/precons/Adaptive Enchantment.dck
Normal file
92
forge-gui/res/quest/precons/Adaptive Enchantment.dck
Normal file
@@ -0,0 +1,92 @@
|
||||
[metadata]
|
||||
Name=Adaptive Enchantment
|
||||
[shop]
|
||||
WinsToUnlock=0
|
||||
Credits=3600
|
||||
MinDifficulty=0
|
||||
MaxDifficulty=5
|
||||
[metadata]
|
||||
Description=Estrid wears many faces, only occasionally wearing her own. Her arsenal of enchanted masks lets her shift seamlessly between the powers and fighting styles of countless creatures to adapt to enemy tactics.
|
||||
Set=C18
|
||||
Image=adaptive_enchantment.jpg
|
||||
[Main]
|
||||
1 Ajani's Chosen|C18
|
||||
1 Archetype of Imagination|C18
|
||||
1 Arixmethes, Slumbering Isle|C18
|
||||
1 Aura Gnarlid|C18
|
||||
1 Azorius Chancery|C18
|
||||
1 Bant Charm|C18
|
||||
1 Bear Umbra|C18
|
||||
1 Blossoming Sands|C18
|
||||
1 Boon Satyr|C18
|
||||
1 Bruna, Light of Alabaster|C18
|
||||
1 Celestial Archon|C18
|
||||
1 Cold-Eyed Selkie|C18
|
||||
1 Command Tower|C18
|
||||
1 Creeping Renaissance|C18
|
||||
1 Dawn's Reflection|C18
|
||||
1 Daxos of Meletis|C18
|
||||
1 Dictate of Kruphix|C18
|
||||
1 Dismantling Blow|C18
|
||||
1 Eel Umbra|C18
|
||||
1 Eidolon of Blossoms|C18
|
||||
1 Elderwood Scion|C18
|
||||
1 Empyrial Storm|C18
|
||||
1 Enchantress's Presence|C18
|
||||
1 Epic Proportions|C18
|
||||
1 Estrid's Invocation|C18
|
||||
1 Estrid, the Masked+|C18
|
||||
1 Ever-Watching Threshold|C18
|
||||
1 Evolving Wilds|C18
|
||||
1 Fertile Ground|C18
|
||||
1 Finest Hour|C18
|
||||
8 Forest|C18
|
||||
1 Forge of Heroes|C18
|
||||
1 Genesis Storm|C18
|
||||
1 Ground Seal|C18
|
||||
1 Heavenly Blademaster|C18
|
||||
1 Herald of the Pantheon|C18
|
||||
1 Hydra Omnivore|C18
|
||||
6 Island|C18
|
||||
1 Kestia, the Cultivator|C18
|
||||
1 Krosan Verge|C18
|
||||
1 Kruphix's Insight|C18
|
||||
1 Loyal Drake|C18
|
||||
1 Loyal Guardian|C18
|
||||
1 Loyal Unicorn|C18
|
||||
1 Martial Coup|C18
|
||||
1 Meandering River|C18
|
||||
1 Mosswort Bridge|C18
|
||||
1 Myth Unbound|C18
|
||||
1 Nylea's Colossus|C18
|
||||
1 Octopus Umbra|C18
|
||||
1 Overgrowth|C18
|
||||
1 Phyrexian Rebirth|C18
|
||||
9 Plains|C18
|
||||
1 Ravenous Slime|C18
|
||||
1 Reclamation Sage|C18
|
||||
1 Righteous Authority|C18
|
||||
1 Sage's Reverie|C18
|
||||
1 Seaside Citadel|C18
|
||||
1 Selesnya Sanctuary|C18
|
||||
1 Sigil of the Empty Throne|C18
|
||||
1 Silent Sentinel|C18
|
||||
1 Simic Growth Chamber|C18
|
||||
1 Snake Umbra|C18
|
||||
1 Sol Ring|C18
|
||||
1 Soul Snare|C18
|
||||
1 Spawning Grounds|C18
|
||||
1 Terramorphic Expanse|C18
|
||||
1 Thornwood Falls|C18
|
||||
1 Tranquil Cove|C18
|
||||
1 Tranquil Expanse|C18
|
||||
1 Tuvasa the Sunlit|C18
|
||||
1 Unflinching Courage|C18
|
||||
1 Unquestioned Authority|C18
|
||||
1 Vow of Flight|C18
|
||||
1 Vow of Wildness|C18
|
||||
1 Whitewater Naiads|C18
|
||||
1 Wild Growth|C18
|
||||
1 Winds of Rath|C18
|
||||
1 Woodland Stream|C18
|
||||
1 Yavimaya Enchantress|C18
|
||||
@@ -6,7 +6,7 @@ Credits=1200
|
||||
MinDifficulty=0
|
||||
MaxDifficulty=5
|
||||
[metadata]
|
||||
Description=Theros Beyond Death U/B Planeswalker Deck
|
||||
Description=Ashiok has power to torment foes by conjuring their darkest memories, fears, and regrets. With Ashiok’s deck, you’ll fill your graveyard along with your opponent’s for massive value as you slowly drive them insane.
|
||||
Set=THB
|
||||
Image=ashiok_sculptor_of_fears.jpg
|
||||
[Main]
|
||||
|
||||
@@ -6,7 +6,7 @@ Credits=1200
|
||||
MinDifficulty=0
|
||||
MaxDifficulty=5
|
||||
[metadata]
|
||||
Description=Theros Beyond Death W Planeswalker Deck
|
||||
Description=Elspeth is a renowned warrior, who has defeated everything from gods to death itself. With Elspeth’s deck, you’ll build up a battalion of devoted soldiers and lead them fearlessly to a glorious victory.
|
||||
Set=THB
|
||||
Image=elspeth_undaunted_hero.jpg
|
||||
[Main]
|
||||
|
||||
87
forge-gui/res/quest/precons/Exquisite Invention.dck
Normal file
87
forge-gui/res/quest/precons/Exquisite Invention.dck
Normal file
@@ -0,0 +1,87 @@
|
||||
[metadata]
|
||||
Name=Exquisite Invention
|
||||
[shop]
|
||||
WinsToUnlock=0
|
||||
Credits=3600
|
||||
MinDifficulty=0
|
||||
MaxDifficulty=5
|
||||
[metadata]
|
||||
Description=Saheeli Rai is a brilliant inventor who specializes in creating lifelike animated constructs as beautiful as they are deadly. Her dazzling intellect and mastery over metal make her a formidable adversary.
|
||||
Set=C18
|
||||
Image=exquisite_invention.jpg
|
||||
[Main]
|
||||
1 Aether Gale|C18
|
||||
1 Ancient Stone Idol|C18
|
||||
1 Blasphemous Act|C18
|
||||
1 Blinkmoth Urn|C18
|
||||
1 Bosh, Iron Golem|C18
|
||||
1 Brudiclad, Telchor Engineer|C18
|
||||
1 Buried Ruin|C18
|
||||
1 Chaos Warp|C18
|
||||
1 Chief of the Foundry|C18
|
||||
1 Command Tower|C18
|
||||
1 Commander's Sphere|C18
|
||||
1 Coveted Jewel|C18
|
||||
1 Darksteel Citadel|C18
|
||||
1 Darksteel Juggernaut|C18
|
||||
1 Dreamstone Hedron|C18
|
||||
1 Duplicant|C18
|
||||
1 Echo Storm|C18
|
||||
1 Enchanter's Bane|C18
|
||||
1 Endless Atlas|C18
|
||||
1 Etherium Sculptor|C18
|
||||
1 Forge of Heroes|C18
|
||||
1 Foundry of the Consuls|C18
|
||||
1 Geode Golem|C18
|
||||
1 Great Furnace|C18
|
||||
1 Hedron Archive|C18
|
||||
1 Hellkite Igniter|C18
|
||||
1 Highland Lake|C18
|
||||
1 Inkwell Leviathan|C18
|
||||
1 Into the Roil|C18
|
||||
15 Island|C18
|
||||
1 Izzet Boilerworks|C18
|
||||
1 Izzet Guildgate|C18
|
||||
1 Izzet Signet|C18
|
||||
1 Loyal Apprentice|C18
|
||||
1 Loyal Drake|C18
|
||||
1 Magmaquake|C18
|
||||
1 Magnifying Glass|C18
|
||||
1 Maverick Thopterist|C18
|
||||
1 Mimic Vat|C18
|
||||
1 Mind Stone|C18
|
||||
1 Mirrorworks|C18
|
||||
12 Mountain|C18
|
||||
1 Myr Battlesphere|C18
|
||||
1 Pilgrim's Eye|C18
|
||||
1 Prismatic Lens|C18
|
||||
1 Prototype Portal|C18
|
||||
1 Psychosis Crawler|C18
|
||||
1 Retrofitter Foundry|C18
|
||||
1 Reverse Engineer|C18
|
||||
1 Saheeli's Artistry|C18
|
||||
1 Saheeli's Directive|C18
|
||||
1 Saheeli, the Gifted+|C18
|
||||
1 Scrabbling Claws|C18
|
||||
1 Scuttling Doom Engine|C18
|
||||
1 Seat of the Synod|C18
|
||||
1 Sharding Sphinx|C18
|
||||
1 Sol Ring|C18
|
||||
1 Soul of New Phyrexia|C18
|
||||
1 Steel Hellkite|C18
|
||||
1 Swiftfoot Boots|C18
|
||||
1 Swiftwater Cliffs|C18
|
||||
1 Tawnos, Urza's Apprentice|C18
|
||||
1 Thirst for Knowledge|C18
|
||||
1 Thopter Assembly|C18
|
||||
1 Thopter Engineer|C18
|
||||
1 Thopter Spy Network|C18
|
||||
1 Tidings|C18
|
||||
1 Treasure Nabber|C18
|
||||
1 Unstable Obelisk|C18
|
||||
1 Unwinding Clock|C18
|
||||
1 Varchild, Betrayer of Kjeldor|C18
|
||||
1 Vedalken Humiliator|C18
|
||||
1 Vessel of Endless Rest|C18
|
||||
1 Whirler Rogue|C18
|
||||
1 Worn Powerstone|C18
|
||||
97
forge-gui/res/quest/precons/Nature's Vengeance.dck
Normal file
97
forge-gui/res/quest/precons/Nature's Vengeance.dck
Normal file
@@ -0,0 +1,97 @@
|
||||
[metadata]
|
||||
Name=Nature's Vengeance
|
||||
[shop]
|
||||
WinsToUnlock=0
|
||||
Credits=3600
|
||||
MinDifficulty=0
|
||||
MaxDifficulty=5
|
||||
[metadata]
|
||||
Description=Lord Windgrace saw his entire world devastated by the detonation of a magical device, instilling in him a hatred of artifacts. Now he commands nature to rise and join his furious war against synthetic abominations.
|
||||
Set=C18
|
||||
Image=natures_vengeance.jpg
|
||||
[Main]
|
||||
1 Acidic Slime|C18
|
||||
1 Akoum Refuge|C18
|
||||
1 Avenger of Zendikar|C18
|
||||
1 Baloth Woodcrasher|C18
|
||||
1 Barren Moor|C18
|
||||
1 Blighted Woodland|C18
|
||||
1 Bloodtracker|C18
|
||||
1 Bojuka Bog|C18
|
||||
1 Borderland Explorer|C18
|
||||
1 Budoka Gardener|C18
|
||||
1 Centaur Vinecrasher|C18
|
||||
1 Chain Reaction|C18
|
||||
1 Charnelhoard Wurm|C18
|
||||
1 Command Tower|C18
|
||||
1 Consign to Dust|C18
|
||||
1 Crash of Rhino Beetles|C18
|
||||
1 Cultivate|C18
|
||||
1 Deathreap Ritual|C18
|
||||
1 Decimate|C18
|
||||
1 Emissary of Grudges|C18
|
||||
1 Evolving Wilds|C18
|
||||
1 Explore|C18
|
||||
1 Explosive Vegetation|C18
|
||||
1 Far Wanderings|C18
|
||||
1 Farhaven Elf|C18
|
||||
1 Flameblast Dragon|C18
|
||||
7 Forest|C18
|
||||
1 Forge of Heroes|C18
|
||||
1 Forgotten Cave|C18
|
||||
1 Fury Storm|C18
|
||||
1 Gaze of Granite|C18
|
||||
1 Golgari Rot Farm|C18
|
||||
1 Grapple with the Past|C18
|
||||
1 Grim Backwoods|C18
|
||||
1 Grisly Salvage|C18
|
||||
1 Gruul Turf|C18
|
||||
1 Gyrus, Waker of Corpses|C18
|
||||
1 Harrow|C18
|
||||
1 Haunted Fengraf|C18
|
||||
1 Hunting Wilds|C18
|
||||
1 Jund Panorama|C18
|
||||
1 Jungle Hollow|C18
|
||||
1 Kazandu Refuge|C18
|
||||
1 Khalni Garden|C18
|
||||
1 Khalni Heart Expedition|C18
|
||||
1 Lavalanche|C18
|
||||
1 Lord Windgrace+|C18
|
||||
1 Loyal Apprentice|C18
|
||||
1 Loyal Guardian|C18
|
||||
1 Loyal Subordinate|C18
|
||||
1 Moldgraf Monstrosity|C18
|
||||
1 Moonlight Bargain|C18
|
||||
5 Mountain|C18
|
||||
1 Mountain Valley|C18
|
||||
1 Myriad Landscape|C18
|
||||
1 Nesting Dragon|C18
|
||||
1 Putrefy|C18
|
||||
1 Rakdos Carnarium|C18
|
||||
1 Rampaging Baloths|C18
|
||||
1 Reality Scramble|C18
|
||||
1 Retreat to Hagra|C18
|
||||
1 Rocky Tar Pit|C18
|
||||
1 Rubblehulk|C18
|
||||
1 Ruinous Path|C18
|
||||
1 Sakura-Tribe Elder|C18
|
||||
1 Savage Lands|C18
|
||||
1 Savage Twister|C18
|
||||
1 Scute Mob|C18
|
||||
1 Seer's Sundial|C18
|
||||
1 Sol Ring|C18
|
||||
1 Soul of Innistrad|C18
|
||||
1 Stitch Together|C18
|
||||
6 Swamp|C18
|
||||
1 Temple of the False God|C18
|
||||
1 Terramorphic Expanse|C18
|
||||
1 Thantis, the Warweaver|C18
|
||||
1 Tranquil Thicket|C18
|
||||
1 Turntimber Sower|C18
|
||||
1 Warped Landscape|C18
|
||||
1 Whiptongue Hydra|C18
|
||||
1 Windgrace's Judgment|C18
|
||||
1 Worm Harvest|C18
|
||||
1 Xantcha, Sleeper Agent|C18
|
||||
1 Yavimaya Elder|C18
|
||||
1 Zendikar Incarnate|C18
|
||||
99
forge-gui/res/quest/precons/Subjective Reality.dck
Normal file
99
forge-gui/res/quest/precons/Subjective Reality.dck
Normal file
@@ -0,0 +1,99 @@
|
||||
[metadata]
|
||||
Name=Subjective Reality
|
||||
[shop]
|
||||
WinsToUnlock=0
|
||||
Credits=3600
|
||||
MinDifficulty=0
|
||||
MaxDifficulty=5
|
||||
[metadata]
|
||||
Description=Aminatou may be a child, but she possesses the wisdom of many lifetimes and the power to manipulate reality itself. She can effortlessly alter a person's entire destiny – for better or for worse – and leave no trace of her interference.
|
||||
Set=C18
|
||||
Image=subjective_reality.jpg
|
||||
[Main]
|
||||
1 Adarkar Valkyrie|C18
|
||||
1 Aethermage's Touch|C18
|
||||
1 Akroma's Vengeance|C18
|
||||
1 Aminatou's Augury|C18
|
||||
1 Aminatou, the Fateshifter+|C18
|
||||
1 Arcane Sanctum|C18
|
||||
1 Army of the Damned|C18
|
||||
1 Azorius Chancery|C18
|
||||
1 Azorius Guildgate|C18
|
||||
1 Azorius Signet|C18
|
||||
1 Banishing Stroke|C18
|
||||
1 Barren Moor|C18
|
||||
1 Boreas Charger|C18
|
||||
1 Brainstorm|C18
|
||||
1 Cloudform|C18
|
||||
1 Command Tower|C18
|
||||
1 Commander's Sphere|C18
|
||||
1 Conundrum Sphinx|C18
|
||||
1 Crib Swap|C18
|
||||
1 Crystal Ball|C18
|
||||
1 Devastation Tide|C18
|
||||
1 Dimir Aqueduct|C18
|
||||
1 Dimir Guildgate|C18
|
||||
1 Dimir Signet|C18
|
||||
1 Dismal Backwater|C18
|
||||
1 Djinn of Wishes|C18
|
||||
1 Dream Cache|C18
|
||||
1 Duskmantle Seer|C18
|
||||
1 Enigma Sphinx|C18
|
||||
1 Entreat the Angels|C18
|
||||
1 Entreat the Dead|C18
|
||||
1 Esper Charm|C18
|
||||
1 Forge of Heroes|C18
|
||||
1 Forsaken Sanctuary|C18
|
||||
1 Geode Golem|C18
|
||||
1 Halimar Depths|C18
|
||||
1 High Priest of Penance|C18
|
||||
5 Island|C18
|
||||
1 Isolated Watchtower|C18
|
||||
1 Jeskai Infiltrator|C18
|
||||
1 Jwar Isle Refuge|C18
|
||||
1 Lightform|C18
|
||||
1 Lonely Sandbar|C18
|
||||
1 Loyal Subordinate|C18
|
||||
1 Loyal Unicorn|C18
|
||||
1 Magus of the Balance|C18
|
||||
1 Meandering River|C18
|
||||
1 Mind Stone|C18
|
||||
1 Mortify|C18
|
||||
1 Mortuary Mire|C18
|
||||
1 Mulldrifter|C18
|
||||
1 New Benalia|C18
|
||||
1 Night Incarnate|C18
|
||||
1 Ninja of the Deep Hours|C18
|
||||
1 Orzhov Basilica|C18
|
||||
1 Orzhov Guildgate|C18
|
||||
1 Orzhov Signet|C18
|
||||
1 Phyrexian Delver|C18
|
||||
1 Pilgrim's Eye|C18
|
||||
8 Plains|C18
|
||||
1 Ponder|C18
|
||||
1 Portent|C18
|
||||
1 Predict|C18
|
||||
1 Primordial Mist|C18
|
||||
1 Return to Dust|C18
|
||||
1 Scoured Barrens|C18
|
||||
1 Secluded Steppe|C18
|
||||
1 Seer's Lantern|C18
|
||||
1 Sejiri Refuge|C18
|
||||
1 Serra Avatar|C18
|
||||
1 Sigiled Starfish|C18
|
||||
1 Silent-Blade Oni|C18
|
||||
1 Skull Storm|C18
|
||||
1 Sol Ring|C18
|
||||
1 Sower of Discord|C18
|
||||
1 Sphinx of Jwar Isle|C18
|
||||
1 Sphinx of Uthuun|C18
|
||||
1 Submerged Boneyard|C18
|
||||
3 Swamp|C18
|
||||
1 Telling Time|C18
|
||||
1 Terminus|C18
|
||||
1 Tranquil Cove|C18
|
||||
1 Treasure Hunt|C18
|
||||
1 Utter End|C18
|
||||
1 Varina, Lich Queen|C18
|
||||
1 Yennett, Cryptic Sovereign|C18
|
||||
1 Yuriko, the Tiger's Shadow|C18
|
||||
@@ -1,11 +1,6 @@
|
||||
package forge.interfaces;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
@@ -22,9 +17,14 @@ import forge.game.spellability.SpellAbilityView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface IGuiGame {
|
||||
void setGameView(GameView gameView);
|
||||
GameView getGameView();
|
||||
@@ -107,7 +107,6 @@ public interface IGuiGame {
|
||||
* @return null if choices is missing, empty, or if the users' choices are
|
||||
* empty; otherwise, returns the first item in the List returned by
|
||||
* getChoices.
|
||||
* @see #getChoices(String, int, int, Object...)
|
||||
*/
|
||||
<T> T oneOrNone(String message, List<T> choices);
|
||||
|
||||
@@ -158,8 +157,8 @@ public interface IGuiGame {
|
||||
|
||||
void setCard(CardView card);
|
||||
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
|
||||
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
|
||||
PlayerZoneUpdates openZones(PlayerView controller, Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||
void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates);
|
||||
void setHighlighted(PlayerView pv, boolean b);
|
||||
void setUsedToPay(CardView card, boolean value);
|
||||
void setSelectables(final Iterable<CardView> cards);
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
package forge.match.input;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.FThreads;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSelectManyBase<T> {
|
||||
private static final long serialVersionUID = -6609493252672573139L;
|
||||
@@ -31,31 +30,32 @@ public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSele
|
||||
}
|
||||
|
||||
public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView<T> validChoices0, final SpellAbility sa0) {
|
||||
super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()),sa0);
|
||||
super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()), sa0);
|
||||
validChoices = validChoices0;
|
||||
if (min > validChoices.size()) { // pfps does this really do anything useful??
|
||||
System.out.println(String.format("Trying to choose at least %d things from a list with only %d things!", min, validChoices.size()));
|
||||
}
|
||||
ArrayList<CardView> vCards = new ArrayList<>();
|
||||
for ( T c : validChoices0 ) {
|
||||
if ( c instanceof Card ) {
|
||||
vCards.add(((Card)c).getView()) ;
|
||||
}
|
||||
}
|
||||
getController().getGui().setSelectables(vCards);
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final GameEntity c : validChoices) {
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ;
|
||||
if ( cz != null ) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(),cz.getZoneType()));
|
||||
}
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
getController().getGui().updateZones(zonesToUpdate);
|
||||
zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate);
|
||||
ArrayList<CardView> vCards = new ArrayList<>();
|
||||
for (T c : validChoices0) {
|
||||
if (c instanceof Card) {
|
||||
vCards.add(((Card) c).getView());
|
||||
}
|
||||
});
|
||||
}
|
||||
getController().getGui().setSelectables(vCards);
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final GameEntity c : validChoices) {
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null;
|
||||
if (cz != null) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(), cz.getZoneType()));
|
||||
}
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getController().getGui().updateZones(zonesToUpdate);
|
||||
zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(), zonesToUpdate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package forge.match.input;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.ApiType;
|
||||
@@ -18,13 +12,18 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.TextUtil;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.FThreads;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
private final List<Card> choices;
|
||||
@@ -47,16 +46,17 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
this.tgt = sa.getTargetRestrictions();
|
||||
this.sa = sa;
|
||||
this.mandatory = mandatory;
|
||||
controller.getGui().setSelectables(CardView.getCollection(choices));
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final Card c : choices) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(c.getZone().getPlayer().getView(),c.getZone().getZoneType()));
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
controller.getGui().updateZones(zonesToUpdate);
|
||||
controller.getGui().setSelectables(CardView.getCollection(choices));
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final Card c : choices) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(c.getZone().getPlayer().getView(), c.getZone().getZoneType()));
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
controller.getGui().updateZones(zonesToUpdate);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
package forge.net;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
@@ -22,11 +14,19 @@ import forge.game.spellability.SpellAbilityView;
|
||||
import forge.interfaces.IGameController;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.match.NextGameDecision;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.ReflectionUtil;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The methods that can be sent through this protocol.
|
||||
*/
|
||||
@@ -75,8 +75,8 @@ public enum ProtocolMethod {
|
||||
clearSelectables (Mode.SERVER),
|
||||
refreshField (Mode.SERVER),
|
||||
// TODO case "setPlayerAvatar":
|
||||
openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
||||
restoreOldZones (Mode.SERVER, Void.TYPE, Map/*PlayerView,Object*/.class),
|
||||
openZones (Mode.SERVER, PlayerZoneUpdates.class, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
||||
restoreOldZones (Mode.SERVER, Void.TYPE, PlayerView.class, PlayerZoneUpdates.class),
|
||||
isUiSetToSkipPhase (Mode.SERVER, Boolean.TYPE, PlayerView.class, PhaseType.class),
|
||||
setRememberedActions(Mode.SERVER, Void.TYPE),
|
||||
nextRememberedAction(Mode.SERVER, Void.TYPE),
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package forge.net.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
@@ -23,9 +18,14 @@ import forge.match.AbstractGuiGame;
|
||||
import forge.net.GameProtocolSender;
|
||||
import forge.net.ProtocolMethod;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class NetGuiGame extends AbstractGuiGame {
|
||||
|
||||
private final GameProtocolSender sender;
|
||||
@@ -283,14 +283,14 @@ public class NetGuiGame extends AbstractGuiGame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
|
||||
public PlayerZoneUpdates openZones(PlayerView controller, final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
|
||||
updateGameView();
|
||||
return sendAndWait(ProtocolMethod.openZones, zones, players);
|
||||
return sendAndWait(ProtocolMethod.openZones, controller, zones, players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
|
||||
send(ProtocolMethod.restoreOldZones, playersToRestoreZonesFor);
|
||||
public void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates) {
|
||||
send(ProtocolMethod.restoreOldZones, playerView, playerZoneUpdates);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -133,8 +133,8 @@ public class HumanPlaySpellAbility {
|
||||
}
|
||||
|
||||
if (ability.isAbility() && ability instanceof AbilityActivated) {
|
||||
final Map<String, String> params = Maps.newHashMap();
|
||||
params.put("ManaColorConversion", "Additive");
|
||||
final Map<String, String> params = Maps.newHashMap();
|
||||
params.put("ManaColorConversion", "Additive");
|
||||
|
||||
for (KeywordInterface inst : c.getKeywords()) {
|
||||
String keyword = inst.getOriginal();
|
||||
@@ -249,6 +249,8 @@ public class HumanPlaySpellAbility {
|
||||
final Game game = ability.getActivatingPlayer().getGame();
|
||||
|
||||
if (fromZone != null) { // and not a copy
|
||||
ability.getHostCard().setCastSA(null);
|
||||
ability.getHostCard().setCastFrom(null);
|
||||
// add back to where it came from
|
||||
game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null, null);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,15 @@ public class PlayerZoneUpdate implements Serializable {
|
||||
private final Set<ZoneType> zones;
|
||||
|
||||
public PlayerZoneUpdate(final PlayerView player, final ZoneType zone) {
|
||||
if (player == null || zone == null) {
|
||||
if (player == null ) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.player = player;
|
||||
this.zones = EnumSet.of(zone);
|
||||
if (zone != null) {
|
||||
this.zones = EnumSet.of(zone);
|
||||
} else {
|
||||
this.zones = EnumSet.noneOf(ZoneType.class);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerView getPlayer() {
|
||||
@@ -30,13 +34,13 @@ public class PlayerZoneUpdate implements Serializable {
|
||||
|
||||
void addZone(final ZoneType zone) {
|
||||
if (zone == null) {
|
||||
throw new NullPointerException();
|
||||
return;
|
||||
}
|
||||
zones.add(zone);
|
||||
}
|
||||
void add(final PlayerZoneUpdate other) {
|
||||
if (other == null) {
|
||||
throw new NullPointerException();
|
||||
return;
|
||||
}
|
||||
zones.addAll(other.getZones());
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class TargetSelection {
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<ZoneType> zone = tgt.getZone();
|
||||
final List<ZoneType> zones = tgt.getZone();
|
||||
final boolean mandatory = tgt.getMandatory() && hasCandidates;
|
||||
|
||||
final boolean choiceResult;
|
||||
@@ -110,7 +110,7 @@ public class TargetSelection {
|
||||
final GameObject choice = Aggregates.random(candidates);
|
||||
return ability.getTargets().add(choice);
|
||||
}
|
||||
else if (zone.size() == 1 && zone.get(0) == ZoneType.Stack) {
|
||||
else if (zones.size() == 1 && zones.get(0) == ZoneType.Stack) {
|
||||
// If Zone is Stack, the choices are handled slightly differently.
|
||||
// Handle everything inside function due to interaction with StackInstance
|
||||
return this.chooseCardFromStack(mandatory);
|
||||
@@ -152,12 +152,15 @@ public class TargetSelection {
|
||||
for (Card card : validTargets) {
|
||||
playersWithValidTargets.put(PlayerView.get(card.getController()), null);
|
||||
}
|
||||
if (controller.getGui().openZones(zone, playersWithValidTargets)) {
|
||||
|
||||
PlayerView playerView = controller.getLocalPlayerView();
|
||||
PlayerZoneUpdates playerZoneUpdates = controller.getGui().openZones(playerView, zones, playersWithValidTargets);
|
||||
if (!zones.contains(ZoneType.Stack)) {
|
||||
InputSelectTargets inp = new InputSelectTargets(controller, validTargets, ability, mandatory);
|
||||
inp.showAndWait();
|
||||
choiceResult = !inp.hasCancelled();
|
||||
bTargetingDone = inp.hasPressedOk();
|
||||
controller.getGui().restoreOldZones(playersWithValidTargets);
|
||||
controller.getGui().restoreOldZones(playerView, playerZoneUpdates);
|
||||
}
|
||||
else {
|
||||
// for every other case an all-purpose GuiChoose
|
||||
|
||||
Reference in New Issue
Block a user