mirror of
https://github.com/correl/mage.git
synced 2024-12-25 11:11:16 +00:00
Merge branch 'master' of https://github.com/magefree/mage.git
This commit is contained in:
commit
6834dc87ef
14 changed files with 99 additions and 66 deletions
|
@ -35,6 +35,11 @@
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j</artifactId>
|
||||||
<version>1.2.14</version>
|
<version>1.2.14</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.trove4j</groupId>
|
||||||
|
<artifactId>trove4j</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mortennobel</groupId>
|
<groupId>com.mortennobel</groupId>
|
||||||
<artifactId>java-image-scaling</artifactId>
|
<artifactId>java-image-scaling</artifactId>
|
||||||
|
@ -96,12 +101,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.schlichtherle.truezip</groupId>
|
<groupId>de.schlichtherle.truezip</groupId>
|
||||||
<artifactId>truezip-file</artifactId>
|
<artifactId>truezip-file</artifactId>
|
||||||
<version>7.6.3</version>
|
<version>7.7.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.schlichtherle.truezip</groupId>
|
<groupId>de.schlichtherle.truezip</groupId>
|
||||||
<artifactId>truezip-driver-zip</artifactId>
|
<artifactId>truezip-driver-zip</artifactId>
|
||||||
<version>7.6.3</version>
|
<version>7.7.5</version>
|
||||||
<!--<scope>runtime</scope>-->
|
<!--<scope>runtime</scope>-->
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -14,21 +14,21 @@ import java.awt.image.BufferedImage;
|
||||||
*/
|
*/
|
||||||
public class DialogContainer extends JPanel {
|
public class DialogContainer extends JPanel {
|
||||||
|
|
||||||
private static int X_OFFSET = 30;
|
private static final int X_OFFSET = 30;
|
||||||
private static int Y_OFFSET = 30;
|
private static final int Y_OFFSET = 30;
|
||||||
private BufferedImage shadow = null;
|
private final BufferedImage shadow = null;
|
||||||
//private DialogManager.MTGDialogs dialogType;
|
//private DialogManager.MTGDialogs dialogType;
|
||||||
//private DlgParams params;
|
//private DlgParams params;
|
||||||
private Color backgroundColor = new Color(0, 255, 255, 60);
|
private Color backgroundColor = new Color(0, 255, 255, 60);
|
||||||
private int alpha = 50;
|
private int alpha = 50;
|
||||||
|
|
||||||
private boolean isGradient = false;
|
private final boolean isGradient = false;
|
||||||
private TexturePaint tp = null;
|
private final TexturePaint tp = null;
|
||||||
private Image gradient = null;
|
private final Image gradient = null;
|
||||||
private BufferedImage b;
|
private BufferedImage b;
|
||||||
|
|
||||||
private boolean drawContainer = true;
|
private boolean drawContainer = true;
|
||||||
private DialogManager.MTGDialogs dialogType;
|
private final DialogManager.MTGDialogs dialogType;
|
||||||
|
|
||||||
public DialogContainer(DialogManager.MTGDialogs dialogType, DlgParams params) {
|
public DialogContainer(DialogManager.MTGDialogs dialogType, DlgParams params) {
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
|
@ -36,8 +36,8 @@ public class ChoiceDialog extends IDialogPanel {
|
||||||
|
|
||||||
private JButton jButtonSort = null;
|
private JButton jButtonSort = null;
|
||||||
|
|
||||||
private CardsView cards;
|
private final CardsView cards;
|
||||||
private UUID gameId;
|
private final UUID gameId;
|
||||||
|
|
||||||
private int page = 1;
|
private int page = 1;
|
||||||
private int maxPages;
|
private int maxPages;
|
||||||
|
@ -48,12 +48,14 @@ public class ChoiceDialog extends IDialogPanel {
|
||||||
private boolean isChooseAbility = false;
|
private boolean isChooseAbility = false;
|
||||||
private boolean isCancelStopsPlaying = true;
|
private boolean isCancelStopsPlaying = true;
|
||||||
|
|
||||||
private DlgParams params;
|
private final DlgParams params;
|
||||||
|
|
||||||
private String title;
|
private final String title;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the default constructor
|
* This is the default constructor
|
||||||
|
* @param params
|
||||||
|
* @param title
|
||||||
*/
|
*/
|
||||||
public ChoiceDialog(DlgParams params, String title) {
|
public ChoiceDialog(DlgParams params, String title) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
|
@ -125,7 +125,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
|
||||||
/**
|
/**
|
||||||
* Free resources so GC can work
|
* Free resources so GC can work
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void cleanUp() {
|
||||||
if (updateDeckTask != null) {
|
if (updateDeckTask != null) {
|
||||||
updateDeckTask.cancel(true);
|
updateDeckTask.cancel(true);
|
||||||
}
|
}
|
||||||
|
@ -319,7 +319,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
|
||||||
|
|
||||||
public void removeDeckEditor() {
|
public void removeDeckEditor() {
|
||||||
hidePopup();
|
hidePopup();
|
||||||
this.clear();
|
this.cleanUp();
|
||||||
|
|
||||||
Component c = this.getParent();
|
Component c = this.getParent();
|
||||||
while (c != null && !(c instanceof DeckEditorPane)) {
|
while (c != null && !(c instanceof DeckEditorPane)) {
|
||||||
|
|
|
@ -59,10 +59,10 @@ import java.util.Map.Entry;
|
||||||
*/
|
*/
|
||||||
public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
||||||
|
|
||||||
private Map<UUID, MagePermanent> permanents = new LinkedHashMap<UUID, MagePermanent>();
|
private final Map<UUID, MagePermanent> permanents = new LinkedHashMap<UUID, MagePermanent>();
|
||||||
private UUID gameId;
|
private UUID gameId;
|
||||||
private BigCard bigCard;
|
private BigCard bigCard;
|
||||||
private Map<String, JComponent> ui = new HashMap<String, JComponent>();
|
private final Map<String, JComponent> uiComponentsList = new HashMap<String, JComponent>();
|
||||||
|
|
||||||
protected Map<UUID, PermanentView> battlefield;
|
protected Map<UUID, PermanentView> battlefield;
|
||||||
private Dimension cardDimension;
|
private Dimension cardDimension;
|
||||||
|
@ -71,7 +71,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
||||||
private JScrollPane jScrollPane;
|
private JScrollPane jScrollPane;
|
||||||
private int width;
|
private int width;
|
||||||
|
|
||||||
private static int i = 0;
|
//private static int iCounter = 0;
|
||||||
|
|
||||||
private boolean addedPermanent;
|
private boolean addedPermanent;
|
||||||
private boolean addedArtifact;
|
private boolean addedArtifact;
|
||||||
|
@ -81,9 +81,9 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
||||||
|
|
||||||
/** Creates new form BattlefieldPanel */
|
/** Creates new form BattlefieldPanel */
|
||||||
public BattlefieldPanel() {
|
public BattlefieldPanel() {
|
||||||
ui.put("battlefieldPanel", this);
|
uiComponentsList.put("battlefieldPanel", this);
|
||||||
initComponents();
|
initComponents();
|
||||||
ui.put("jPanel", jPanel);
|
uiComponentsList.put("jPanel", jPanel);
|
||||||
|
|
||||||
addComponentListener(new ComponentAdapter() {
|
addComponentListener(new ComponentAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,7 +104,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
||||||
|
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
permanents.clear();
|
permanents.clear();
|
||||||
Plugins.getInstance().sortPermanents(ui, permanents.values());
|
Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(Map<UUID, PermanentView> battlefield) {
|
public void update(Map<UUID, PermanentView> battlefield) {
|
||||||
|
@ -145,11 +145,11 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
||||||
|
|
||||||
removedCreature = false;
|
removedCreature = false;
|
||||||
|
|
||||||
for (Iterator<Entry<UUID, MagePermanent>> i = permanents.entrySet().iterator(); i.hasNext();) {
|
for (Iterator<Entry<UUID, MagePermanent>> iterator = permanents.entrySet().iterator(); iterator.hasNext();) {
|
||||||
Entry<UUID, MagePermanent> entry = i.next();
|
Entry<UUID, MagePermanent> entry = iterator.next();
|
||||||
if (!battlefield.containsKey(entry.getKey())) {
|
if (!battlefield.containsKey(entry.getKey())) {
|
||||||
removePermanent(entry.getKey(), 1);
|
removePermanent(entry.getKey(), 1);
|
||||||
i.remove();
|
iterator.remove();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
|
||||||
|
|
||||||
//TODO: review sorting stuff
|
//TODO: review sorting stuff
|
||||||
public void sortLayout() {
|
public void sortLayout() {
|
||||||
int height = Plugins.getInstance().sortPermanents(ui, permanents.values());
|
int height = Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values());
|
||||||
BattlefieldPanel.this.jPanel.setPreferredSize(new Dimension(width - 30, height));
|
BattlefieldPanel.this.jPanel.setPreferredSize(new Dimension(width - 30, height));
|
||||||
this.jScrollPane.repaint();
|
this.jScrollPane.repaint();
|
||||||
this.jScrollPane.revalidate();
|
this.jScrollPane.revalidate();
|
||||||
|
|
|
@ -50,7 +50,11 @@
|
||||||
<artifactId>trove</artifactId>
|
<artifactId>trove</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.trove4j</groupId>
|
||||||
|
<artifactId>trove4j</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.xerial</groupId>
|
<groupId>org.xerial</groupId>
|
||||||
<artifactId>sqlite-jdbc</artifactId>
|
<artifactId>sqlite-jdbc</artifactId>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package mage.utils;
|
package mage.utils;
|
||||||
|
|
||||||
|
import gnu.trove.set.hash.THashSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -13,7 +14,6 @@ import mage.cards.decks.Deck;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.ColoredManaSymbol;
|
import mage.constants.ColoredManaSymbol;
|
||||||
import mage.interfaces.rate.RateCallback;
|
import mage.interfaces.rate.RateCallback;
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds deck from provided card pool.
|
* Builds deck from provided card pool.
|
||||||
|
@ -263,7 +263,7 @@ public class DeckBuilder {
|
||||||
final Map<String, Integer> singleCount = new HashMap<String, Integer>();
|
final Map<String, Integer> singleCount = new HashMap<String, Integer>();
|
||||||
int maxSingleCount = 0;
|
int maxSingleCount = 0;
|
||||||
int multicolor = 0;
|
int multicolor = 0;
|
||||||
Set<String> colors = new HashSet<String>();
|
Set<String> colors = new THashSet<String>();
|
||||||
for (String symbol : card.getManaCost().getSymbols()) {
|
for (String symbol : card.getManaCost().getSymbols()) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
symbol = symbol.replace("{", "").replace("}", "");
|
symbol = symbol.replace("{", "").replace("}", "");
|
||||||
|
|
|
@ -59,23 +59,23 @@ import mage.players.Player;
|
||||||
public class GameView implements Serializable {
|
public class GameView implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private int priorityTime;
|
private final int priorityTime;
|
||||||
private List<PlayerView> players = new ArrayList<PlayerView>();
|
private final List<PlayerView> players = new ArrayList<PlayerView>();
|
||||||
private SimpleCardsView hand;
|
private SimpleCardsView hand;
|
||||||
private Map<String, SimpleCardsView> opponentHands;
|
private Map<String, SimpleCardsView> opponentHands;
|
||||||
private CardsView stack = new CardsView();
|
private final CardsView stack = new CardsView();
|
||||||
private List<ExileView> exiles = new ArrayList<ExileView>();
|
private final List<ExileView> exiles = new ArrayList<ExileView>();
|
||||||
private List<RevealedView> revealed = new ArrayList<RevealedView>();
|
private final List<RevealedView> revealed = new ArrayList<RevealedView>();
|
||||||
private List<LookedAtView> lookedAt = new ArrayList<LookedAtView>();
|
private List<LookedAtView> lookedAt = new ArrayList<LookedAtView>();
|
||||||
private List<CombatGroupView> combat = new ArrayList<CombatGroupView>();
|
private final List<CombatGroupView> combat = new ArrayList<CombatGroupView>();
|
||||||
private TurnPhase phase;
|
private final TurnPhase phase;
|
||||||
private PhaseStep step;
|
private final PhaseStep step;
|
||||||
private UUID activePlayerId;
|
private final UUID activePlayerId;
|
||||||
private String activePlayerName = "";
|
private String activePlayerName = "";
|
||||||
private String priorityPlayerName = "";
|
private String priorityPlayerName = "";
|
||||||
private int turn;
|
private final int turn;
|
||||||
private boolean special = false;
|
private boolean special = false;
|
||||||
private boolean isPlayer;
|
private final boolean isPlayer;
|
||||||
|
|
||||||
|
|
||||||
public GameView(GameState state, Game game, boolean isPlayer) {
|
public GameView(GameState state, Game game, boolean isPlayer) {
|
||||||
|
|
|
@ -48,24 +48,24 @@ import mage.game.command.Commander;
|
||||||
public class PlayerView implements Serializable {
|
public class PlayerView implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private UUID playerId;
|
private final UUID playerId;
|
||||||
private String name;
|
private final String name;
|
||||||
private int life;
|
private final int life;
|
||||||
private int poison;
|
private final int poison;
|
||||||
private int libraryCount;
|
private final int libraryCount;
|
||||||
private int handCount;
|
private final int handCount;
|
||||||
private boolean isActive;
|
private final boolean isActive;
|
||||||
private boolean hasPriority;
|
private final boolean hasPriority;
|
||||||
private boolean hasLeft;
|
private final boolean hasLeft;
|
||||||
private ManaPoolView manaPool;
|
private final ManaPoolView manaPool;
|
||||||
private SimpleCardsView graveyard = new SimpleCardsView();
|
private final SimpleCardsView graveyard = new SimpleCardsView();
|
||||||
private Map<UUID, PermanentView> battlefield = new LinkedHashMap<UUID, PermanentView>();
|
private final Map<UUID, PermanentView> battlefield = new LinkedHashMap<UUID, PermanentView>();
|
||||||
private CardView topCard;
|
private final CardView topCard;
|
||||||
private UserDataView userDataView;
|
private final UserDataView userDataView;
|
||||||
private List<CommandObjectView> commandList = new ArrayList<CommandObjectView>();
|
private final List<CommandObjectView> commandList = new ArrayList<CommandObjectView>();
|
||||||
private List<UUID> attachments = new ArrayList<UUID>();
|
private final List<UUID> attachments = new ArrayList<UUID>();
|
||||||
private int statesSavedSize;
|
private final int statesSavedSize;
|
||||||
private int priorityTimeLeft;
|
private final int priorityTimeLeft;
|
||||||
|
|
||||||
public PlayerView(Player player, GameState state, Game game) {
|
public PlayerView(Player player, GameState state, Game game) {
|
||||||
this.playerId = player.getId();
|
this.playerId = player.getId();
|
||||||
|
|
|
@ -152,8 +152,9 @@ class KioraPreventionEffect extends PreventionEffectImpl<KioraPreventionEffect>
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
if (super.applies(event, source, game) && event instanceof DamageEvent) {
|
if (super.applies(event, source, game) && event instanceof DamageEvent) {
|
||||||
Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
Permanent targetPermanent = game.getPermanent(this.getTargetPointer().getFirst(game, source));
|
||||||
if (event.getSourceId().equals(targetPermanent.getId()) || event.getTargetId().equals(targetPermanent.getId())) {
|
if (targetPermanent != null
|
||||||
|
&& (event.getSourceId().equals(targetPermanent.getId()) || event.getTargetId().equals(targetPermanent.getId()))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,11 @@
|
||||||
<artifactId>ormlite-jdbc</artifactId>
|
<artifactId>ormlite-jdbc</artifactId>
|
||||||
<version>4.42</version>
|
<version>4.42</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.trove4j</groupId>
|
||||||
|
<artifactId>trove4j</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -44,6 +44,7 @@ import mage.target.Targets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
|
* @param <T>
|
||||||
*/
|
*/
|
||||||
public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements ManaCosts<T> {
|
public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements ManaCosts<T> {
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import mage.players.Player;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action for drawing cards.
|
* Action for drawing cards.
|
||||||
|
@ -22,7 +23,7 @@ public class MageDrawAction extends MageAction {
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final int amount;
|
private final int amount;
|
||||||
private ArrayList<UUID> appliedEffects;
|
private final ArrayList<UUID> appliedEffects;
|
||||||
private List<Card> drawnCards;
|
private List<Card> drawnCards;
|
||||||
|
|
||||||
private static final int NEGATIVE_VALUE = -1000000;
|
private static final int NEGATIVE_VALUE = -1000000;
|
||||||
|
@ -37,6 +38,7 @@ public class MageDrawAction extends MageAction {
|
||||||
* Draw and set action score.
|
* Draw and set action score.
|
||||||
*
|
*
|
||||||
* @param game Game context.
|
* @param game Game context.
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int doAction(Game game) {
|
public int doAction(Game game) {
|
||||||
|
@ -51,7 +53,7 @@ public class MageDrawAction extends MageAction {
|
||||||
score += value;
|
score += value;
|
||||||
}
|
}
|
||||||
if (numDrawn > 0) {
|
if (numDrawn > 0) {
|
||||||
game.fireInformEvent(player.getName() + " draws " + Integer.toString(numDrawn) + " card" + (numDrawn > 1 ? "s" : ""));
|
game.fireInformEvent(player.getName() + " draws " + CardUtil.numberToText(numDrawn, "a") + " card" + (numDrawn > 1 ? "s" : ""));
|
||||||
}
|
}
|
||||||
if (player.isEmptyDraw()) {
|
if (player.isEmptyDraw()) {
|
||||||
game.doAction(new MageLoseGameAction(player, MageLoseGameAction.DRAW_REASON));
|
game.doAction(new MageLoseGameAction(player, MageLoseGameAction.DRAW_REASON));
|
||||||
|
|
|
@ -351,8 +351,25 @@ public class CardUtil {
|
||||||
* Converts an integer number to string
|
* Converts an integer number to string
|
||||||
* Numbers > 20 will be returned as digits
|
* Numbers > 20 will be returned as digits
|
||||||
*
|
*
|
||||||
|
* @param number
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String numberToText(int number) {
|
public static String numberToText(int number) {
|
||||||
|
return numberToText(number, "one");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an integer number to string like "one", "two", "three", ...
|
||||||
|
* Numbers > 20 will be returned as digits
|
||||||
|
*
|
||||||
|
* @param number number to convert to text
|
||||||
|
* @param forOne if the number is 1, this string will be returnedinstead of "one".
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String numberToText(int number, String forOne) {
|
||||||
|
if (number == 1 && forOne != null) {
|
||||||
|
return forOne;
|
||||||
|
}
|
||||||
if (number >= 0 && number < 21) {
|
if (number >= 0 && number < 21) {
|
||||||
return numberStrings[number];
|
return numberStrings[number];
|
||||||
}
|
}
|
||||||
|
@ -368,11 +385,7 @@ public class CardUtil {
|
||||||
|
|
||||||
public static String numberToText(String number, String forOne) {
|
public static String numberToText(String number, String forOne) {
|
||||||
if (checkNumeric(number)) {
|
if (checkNumeric(number)) {
|
||||||
int intNumber = Integer.parseInt(number);
|
return numberToText(Integer.parseInt(number));
|
||||||
if (forOne != null && intNumber == 1) {
|
|
||||||
return forOne;
|
|
||||||
}
|
|
||||||
return numberToText(intNumber);
|
|
||||||
}
|
}
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue