Commit 7d0dbff8 authored by Michael Kamensky's avatar Michael Kamensky

Merge branch 'master' into 'master'

Select cards from any Zone that can be shown and do arrangeForScry by popping up library and moving cards (in desktop GUI); Pass min/max card selections through to GUI

See merge request core-developers/forge!1260
parents 6974e2de 3609ff9e
......@@ -162,7 +162,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public <T extends GameEntity> List<T> chooseEntitiesForEffect(
FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title,
FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title,
Player targetedPlayer) {
// this isn't used
return null;
......@@ -1090,7 +1090,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public List<Card> chooseCardsForZoneChange(
ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList,
ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max,
DelayedReveal delayedReveal, String selectPrompt, Player decider) {
// this isn't used
return null;
......
......@@ -86,7 +86,7 @@ public class Localizer {
resourceBundle = ResourceBundle.getBundle(languageRegionID, new Locale(splitLocale[0], splitLocale[1]), loader);
} catch (NullPointerException | MissingResourceException e) {
//If the language can't be loaded, default to US English
resourceBundle = ResourceBundle.getBundle("en-GB", new Locale("en", "GB"), loader);
resourceBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
e.printStackTrace();
}
......
......@@ -852,7 +852,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
// ensure that selection is within maximum allowed changeNum
do {
selectedCards = decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, delayedReveal, selectPrompt, decider);
selectedCards = decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, 0, changeNum, delayedReveal, selectPrompt, decider);
} while (selectedCards != null && selectedCards.size() > changeNum);
if (selectedCards != null) {
for (Card card : selectedCards) {
......
......@@ -111,7 +111,7 @@ public abstract class PlayerController {
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
Map<String, Object> params);
public abstract <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer);
public abstract <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer);
public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message);
public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
......@@ -239,7 +239,7 @@ public abstract class PlayerController {
// better to have this odd method than those if playerType comparison in ChangeZone
public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider);
public abstract List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, Player decider);
public abstract List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider);
public abstract void autoPassCancel();
......
......@@ -29,7 +29,7 @@ import forge.item.PaperCard;
import forge.model.FModel;
import forge.screens.match.CMatchUI;
import forge.toolbox.FOptionPane;
import forge.view.arcane.ListCardArea;
public class GuiChoose {
......@@ -285,5 +285,31 @@ public class GuiChoose {
return null;
}
public static List<Card> manipulateCardList(final CMatchUI gui, final String title, final List<Card> cards, final List<Card> manipulable,
final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
final Callable<List<Card>> callable = new Callable<List<Card>>() {
@Override
public List<Card> call() throws Exception {
ListCardArea tempArea = new ListCardArea(gui,title,cards,manipulable,toTop,toBottom,toAnywhere);
// tempArea.pack();
// window? tempArea.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
tempArea.show();
// tempArea.dispose();
//try { Thread.sleep(1000); } catch(InterruptedException ex) { }
final List<Card> cardList = tempArea.getCardList();
return cardList;
}
};
final FutureTask<List<Card>> ft = new FutureTask<List<Card>>(callable);
FThreads.invokeInEdtAndWait(ft);
try {
List<Card> result = ft.get();
return result;
} catch (final Exception e) { // we have waited enough
e.printStackTrace();
}
return null;
}
}
......@@ -51,6 +51,7 @@ import forge.deck.Deck;
import forge.deckchooser.FDeckViewer;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.combat.CombatView;
import forge.game.phase.PhaseType;
......@@ -101,6 +102,7 @@ import forge.util.gui.SOptionPane;
import forge.view.FView;
import forge.view.arcane.CardPanel;
import forge.view.arcane.FloatingCardArea;
import forge.match.input.*;
/**
* Constructs instance of match UI controller, used as a single point of
......@@ -395,7 +397,8 @@ public final class CMatchUI
break;
case Hand:
updateHand = true;
//$FALL-THROUGH$
updateZones = true;
break;
default:
updateZones = true;
FloatingCardArea.refresh(owner, zone);
......@@ -424,6 +427,57 @@ public final class CMatchUI
}
}
@Override
public Iterable<PlayerZoneUpdate> tempShowZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
for (final PlayerZoneUpdate update : zonesToUpdate) {
final PlayerView player = update.getPlayer();
for (final ZoneType zone : update.getZones()) {
switch (zone) {
case Battlefield: // always shown
break;
case Hand: // controller hand always shown
if (controller != player) {
FloatingCardArea.show(this,player,zone);
}
break;
case Library:
case Graveyard:
case Exile:
case Flashback:
case Command:
FloatingCardArea.show(this,player,zone);
break;
default:
break;
}
}
}
return zonesToUpdate; //pfps should return only the newly shown zones
}
@Override
public void hideZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
for (final PlayerZoneUpdate update : zonesToUpdate) {
final PlayerView player = update.getPlayer();
for (final ZoneType zone : update.getZones()) {
switch (zone) {
case Battlefield: // always shown
break;
case Hand: // the controller's hand should never be temporarily shown, but ...
case Library:
case Graveyard:
case Exile:
case Flashback:
case Command:
FloatingCardArea.hide(this,player,zone);
break;
default:
break;
}
}
}
}
// Player's mana pool changes
@Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
......@@ -465,6 +519,7 @@ public final class CMatchUI
}
break;
default:
FloatingCardArea.refresh(c.getController(),zone); // in case the card is visible in the zone
break;
}
}
......@@ -942,11 +997,16 @@ public final class CMatchUI
}
@Override
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final DelayedReveal delayedReveal) {
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final int min, final int max, final DelayedReveal delayedReveal) {
if (delayedReveal != null) {
reveal(delayedReveal.getMessagePrefix(), delayedReveal.getCards()); //TODO: Merge this into search dialog
}
return (List<GameEntityView>) order(title,"Selected", 0, optionList.size(), optionList, null, null, false);
return (List<GameEntityView>) order(title,"Selected", min, max, optionList, null, null, false);
}
@Override
public List<Card> manipulateCardList(final String title, final List<Card> cards, final List<Card> manipulable, final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
return GuiChoose.manipulateCardList(this, title, cards, manipulable, toTop, toBottom, toAnywhere);
}
@Override
......
......@@ -246,8 +246,8 @@ public class CardArea extends CardPanelContainer implements CardPanelMouseListen
dragPanel.setDisplayEnabled(false);
CardPanel.setDragAnimationPanel(new CardPanel(dragPanel.getMatchUI(), dragPanel.getCard()));
final JFrame frame = (JFrame) SwingUtilities.windowForComponent(this);
final JLayeredPane layeredPane = frame.getLayeredPane();
final RootPaneContainer frame = (RootPaneContainer) SwingUtilities.windowForComponent(this);
final JLayeredPane layeredPane = frame.getLayeredPane();
layeredPane.add(CardPanel.getDragAnimationPanel());
layeredPane.moveToFront(CardPanel.getDragAnimationPanel());
final Point p = SwingUtilities.convertPoint(this, this.mouseDragStartX, this.mouseDragStartY, layeredPane);
......
......@@ -182,6 +182,10 @@ public abstract class CardPanelContainer extends SkinnedPanel {
});
}
protected boolean cardPanelDraggable(final CardPanel panel) {
return true;
}
private MouseMotionListener setupMotionMouseListener() {
final MouseMotionListener mml = new MouseMotionListener() {
@Override
......@@ -207,20 +211,23 @@ public abstract class CardPanelContainer extends SkinnedPanel {
if (panel != mouseDownPanel) {
return;
}
if (intialMouseDragX == -1) {
intialMouseDragX = x;
intialMouseDragY = y;
return;
}
if ((Math.abs(x - intialMouseDragX) < CardPanelContainer.DRAG_SMUDGE)
if (cardPanelDraggable(panel)) { // allow for non-draggable cards
if (intialMouseDragX == -1) {
intialMouseDragX = x;
intialMouseDragY = y;
return;
}
if ((Math.abs(x - intialMouseDragX) < CardPanelContainer.DRAG_SMUDGE)
&& (Math.abs(y - intialMouseDragY) < CardPanelContainer.DRAG_SMUDGE)) {
return;
}
mouseDownPanel = null;
setMouseDragPanel(panel);
mouseDragOffsetX = panel.getX() - intialMouseDragX;
mouseDragOffsetY = panel.getY() - intialMouseDragY;
mouseDragStart(getMouseDragPanel(), evt);
return;
}
mouseDownPanel = null;
setMouseDragPanel(panel);
mouseDragOffsetX = panel.getX() - intialMouseDragX;
mouseDragOffsetY = panel.getY() - intialMouseDragY;
mouseDragStart(getMouseDragPanel(), evt);
}
}
@Override
......@@ -453,4 +460,4 @@ public abstract class CardPanelContainer extends SkinnedPanel {
this.layoutListeners.add(listener);
}
}
\ No newline at end of file
}
......@@ -69,6 +69,10 @@ public class FloatingCardArea extends CardArea {
final FloatingCardArea cardArea = _init(matchUI, player, zone);
cardArea.showWindow();
}
public static void hide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final FloatingCardArea cardArea = _init(matchUI, player, zone);
cardArea.hideWindow();
}
private static FloatingCardArea _init(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final int key = getKey(player, zone);
FloatingCardArea cardArea = floatingAreas.get(key);
......@@ -205,6 +209,11 @@ public class FloatingCardArea extends CardArea {
window.setFocusableWindowState(false); // should probably do this earlier
window.setVisible(true);
}
private void hideWindow() {
onShow();
window.setFocusableWindowState(false); // should probably do this earlier
window.setVisible(false);
}
private void showOrHideWindow() {
onShow();
window.setFocusableWindowState(false); // should probably do this earlier
......
......@@ -174,7 +174,7 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer) {
public <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer) {
// this isn't used
return null;
}
......@@ -624,7 +624,7 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, Player decider) {
public List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider) {
// this isn't used
return null;
}
......
......@@ -29,6 +29,7 @@ import forge.deck.CardPool;
import forge.deck.FSideboardDialog;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
......@@ -343,6 +344,16 @@ public class MatchController extends AbstractGuiGame {
view.updateZones(zonesToUpdate);
}
@Override
public Iterable<PlayerZoneUpdate> tempShowZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
return view.tempShowZones(controller, zonesToUpdate);
}
@Override
public void hideZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
view.hideZones(controller, zonesToUpdate);
}
@Override
public void updateCards(final Iterable<CardView> cards) {
for (final CardView card : cards) {
......@@ -506,8 +517,14 @@ public class MatchController extends AbstractGuiGame {
}
@Override
public List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal) {
return SGuiChoose.order(title, "Selected", 0, -1, (List<GameEntityView>) optionList, null);
public List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, int min, int max, DelayedReveal delayedReveal) {
return SGuiChoose.order(title, "Selected", min, max, (List<GameEntityView>) optionList, null);
}
@Override
public List<Card> manipulateCardList(final String title, final List<Card> cards, final List<Card> manipulable, final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
System.err.println("Not implemented yet - should never be called");
return null;
}
@Override
......
......@@ -468,6 +468,15 @@ public class MatchScreen extends FScreen {
}
}
public Iterable<PlayerZoneUpdate> tempShowZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
// pfps needs to actually do something
return zonesToUpdate; // pfps should return only those zones newly shown
}
public void hideZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
// pfps needs to actually do something
}
public void updateSingleCard(final CardView card) {
final CardAreaPanel pnl = CardAreaPanel.get(card);
if (pnl == null) { return; }
......
......@@ -11,6 +11,7 @@ import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
......@@ -47,6 +48,8 @@ public interface IGuiGame {
void showManaPool(PlayerView player);
void hideManaPool(PlayerView player);
void updateStack();
Iterable<PlayerZoneUpdate> tempShowZones(PlayerView controller, Iterable<PlayerZoneUpdate> zonesToUpdate);
void hideZones(PlayerView controller, Iterable<PlayerZoneUpdate> zonesToUpdate);
void updateZones(Iterable<PlayerZoneUpdate> zonesToUpdate);
void updateSingleCard(CardView card);
void updateCards(Iterable<CardView> cards);
......@@ -144,7 +147,11 @@ public interface IGuiGame {
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
GameEntityView chooseSingleEntityForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal, boolean isOptional);
List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal);
List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, int min, int max, DelayedReveal delayedReveal);
// show a list of cards and allow some of them to be moved around and return new list
List<Card> manipulateCardList(String title, final List<Card> cards, final List<Card> manipulable, boolean toTop, boolean toBottom, boolean toAnywhere);
void setCard(CardView card);
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
......
......@@ -11,12 +11,17 @@ 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;
public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSelectManyBase<T> {
private static final long serialVersionUID = -6609493252672573139L;
private final FCollectionView<T> validChoices;
protected final FCollection<T> selected = new FCollection<T>();
protected Iterable<PlayerZoneUpdate> zonesShown; // want to hide these zones when input done
public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView<T> validChoices0) {
this(controller, min, max, validChoices0, null);
......@@ -28,6 +33,17 @@ public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSele
if (min > validChoices.size()) {
System.out.println(String.format("Trying to choose at least %d things from a list with only %d things!", min, validChoices.size()));
}
PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
for (final GameEntity c : validChoices) {
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ;
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(),cz.getZoneType()));
}
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public void run() {
controller.getGui().updateZones(zonesToUpdate);
zonesShown = controller.getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate);
}
});
}
@Override
......@@ -93,4 +109,10 @@ public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSele
? String.format(message, selected.size())
: String.format(message, max - selected.size());
}
@Override
protected void onStop() {
getController().getGui().hideZones(getController().getPlayer().getView(),zonesShown);
super.onStop();
}
}
......@@ -49,6 +49,8 @@ public enum ProtocolMethod {
hideManaPool (Mode.SERVER, Void.TYPE, PlayerView.class),
updateStack (Mode.SERVER),
updateZones (Mode.SERVER, Void.TYPE, Iterable/*PlayerZoneUpdate*/.class),
tempShowZones (Mode.SERVER, Iterable/*PlayerZoneUpdate*/.class, PlayerView.class, Iterable/*PlayerZoneUpdate*/.class),
hideZones (Mode.SERVER, Void.TYPE, PlayerView.class, Iterable/*PlayerZoneUpdate*/.class),
updateCards (Mode.SERVER, Void.TYPE, Iterable/*CardView*/.class),
updateManaPool (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
updateLives (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
......@@ -65,7 +67,8 @@ public enum ProtocolMethod {
order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class),
chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class, Boolean.TYPE),
chooseEntitiesForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class),
chooseEntitiesForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, Integer.TYPE, Integer.TYPE, DelayedReveal.class),
manipulateCardList (Mode.SERVER, List.class, String.class, List.class, List.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE),
setCard (Mode.SERVER, Void.TYPE, CardView.class),
// TODO case "setPlayerAvatar":
openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
......@@ -184,4 +187,4 @@ public enum ProtocolMethod {
throw new IllegalStateException(String.format("Protocol method %s: illegal return object type %s returned by client, expected %s", name(), value.getClass().getName(), getReturnType().getName()));
}
}
}
\ No newline at end of file
}
......@@ -11,6 +11,7 @@ import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
......@@ -147,6 +148,18 @@ public class NetGuiGame extends AbstractGuiGame {
send(ProtocolMethod.updateZones, zonesToUpdate);
}
@Override
public Iterable<PlayerZoneUpdate> tempShowZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
updateGameView();
return sendAndWait(ProtocolMethod.tempShowZones, controller, zonesToUpdate);
}
@Override
public void hideZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
updateGameView();
send(ProtocolMethod.hideZones, controller, zonesToUpdate);
}
@Override
public void updateCards(final Iterable<CardView> cards) {
updateGameView();
......@@ -232,8 +245,13 @@ public class NetGuiGame extends AbstractGuiGame {
}
@Override
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final DelayedReveal delayedReveal) {
return sendAndWait(ProtocolMethod.chooseEntitiesForEffect, title, optionList, delayedReveal);
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final int min, final int max, final DelayedReveal delayedReveal) {
return sendAndWait(ProtocolMethod.chooseEntitiesForEffect, title, optionList, min, max, delayedReveal);
}
@Override
public List<Card> manipulateCardList(final String title, final List<Card> cards, final List<Card> manipulable, final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
return sendAndWait(ProtocolMethod.manipulateCardList, title, cards, manipulable, toTop, toBottom, toAnywhere);
}
@Override
......
......@@ -125,7 +125,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
UI_DISABLE_IMAGES_EFFECT_CARDS("false"),
UI_ALLOW_ORDER_GRAVEYARD_WHEN_NEEDED ("Never"),
UI_DEFAULT_FONT_SIZE("12"),
UI_SELECT_FROM_CARD_DISPLAYS("true"),
UI_FOR_TOUCHSCREN("false"),
UI_VIBRATE_ON_LIFE_LOSS("true"),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment