mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
refactor VCardDisplayArea, prevent NPE
This commit is contained in:
@@ -23,25 +23,52 @@ import forge.screens.match.MatchController;
|
||||
import forge.toolbox.FCardPanel;
|
||||
import forge.toolbox.FDisplayObject;
|
||||
import forge.util.ThreadUtil;
|
||||
import io.sentry.Sentry;
|
||||
|
||||
public abstract class VCardDisplayArea extends VDisplayArea implements ActivateHandler {
|
||||
private static final float CARD_STACK_OFFSET = 0.2f;
|
||||
|
||||
protected final List<CardView> orderedCards = new ArrayList<>();
|
||||
protected final List<CardAreaPanel> cardPanels = new ArrayList<>();
|
||||
protected List<CardView> _orderedCards;
|
||||
protected List<CardView> orderedCards() {
|
||||
List<CardView> result = _orderedCards;
|
||||
if (result == null) {
|
||||
synchronized (this) {
|
||||
result = _orderedCards;
|
||||
if (result == null) {
|
||||
result = new ArrayList<>();
|
||||
_orderedCards = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _orderedCards;
|
||||
}
|
||||
protected List<CardAreaPanel> _cardPanels;
|
||||
protected List<CardAreaPanel> cardPanels() {
|
||||
List<CardAreaPanel> result = _cardPanels;
|
||||
if (result == null) {
|
||||
synchronized (this) {
|
||||
result = _cardPanels;
|
||||
if (result == null) {
|
||||
result = new ArrayList<>();
|
||||
_cardPanels = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _cardPanels;
|
||||
}
|
||||
private boolean rotateCards180;
|
||||
|
||||
public Iterable<CardView> getOrderedCards() {
|
||||
return orderedCards;
|
||||
return orderedCards();
|
||||
}
|
||||
|
||||
public Iterable<CardAreaPanel> getCardPanels() {
|
||||
return cardPanels;
|
||||
return cardPanels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return cardPanels.size();
|
||||
return cardPanels().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,8 +85,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
for (CardView card : model) {
|
||||
CardAreaPanel cardPanel = CardAreaPanel.get(card);
|
||||
addCardPanelToDisplayArea(cardPanel);
|
||||
cardPanels.add(cardPanel);
|
||||
if (newCardPanel == null && !orderedCards.contains(card)) {
|
||||
cardPanels().add(cardPanel);
|
||||
if (newCardPanel == null && !orderedCards().contains(card)) {
|
||||
newCardPanel = cardPanel;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +105,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
if (isVisible() == b0) { return; }
|
||||
super.setVisible(b0);
|
||||
if (b0) { //when zone becomes visible, ensure display area of panels is updated and panels layed out
|
||||
for (CardAreaPanel pnl : cardPanels) {
|
||||
for (CardAreaPanel pnl : cardPanels()) {
|
||||
pnl.displayArea = this;
|
||||
}
|
||||
revalidate();
|
||||
@@ -113,7 +140,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
CardPanelContainer.this.remove(CardPanel.getDragAnimationPanel());
|
||||
CardPanelContainer.this.setMouseDragPanel(null);
|
||||
}*/
|
||||
cardPanels.remove(fromPanel);
|
||||
cardPanels().remove(fromPanel);
|
||||
remove(fromPanel);
|
||||
}
|
||||
|
||||
@@ -124,14 +151,14 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
if (!cardPanels.isEmpty()) {
|
||||
for (CardAreaPanel panel : cardPanels) {
|
||||
if (!cardPanels().isEmpty()) {
|
||||
for (CardAreaPanel panel : cardPanels()) {
|
||||
if (panel.displayArea == null || panel.displayArea == this ||
|
||||
!panel.displayArea.cardPanels.contains(panel)) { //don't reset if panel's displayed in another area already
|
||||
!panel.displayArea.cardPanels().contains(panel)) { //don't reset if panel's displayed in another area already
|
||||
panel.reset();
|
||||
}
|
||||
}
|
||||
cardPanels.clear();
|
||||
cardPanels().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +167,16 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
List<CardAreaPanel> attachedPanels = cardPanel.getAttachedPanels();
|
||||
if (!attachedPanels.isEmpty()) {
|
||||
for (int i = attachedPanels.size() - 1; i >= 0; i--) {
|
||||
int count = addCards(attachedPanels.get(i), x, y, cardWidth, cardHeight);
|
||||
x += count * cardWidth * CARD_STACK_OFFSET;
|
||||
totalCount += count;
|
||||
CardAreaPanel attachedPanel = attachedPanels.get(i);
|
||||
if (attachedPanel != null) {
|
||||
int count = addCards(attachedPanel, x, y, cardWidth, cardHeight);
|
||||
x += count * cardWidth * CARD_STACK_OFFSET;
|
||||
totalCount += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
orderedCards.add(cardPanel.getCard());
|
||||
orderedCards().add(cardPanel.getCard());
|
||||
cardPanel.setBounds(x, y, cardWidth, cardHeight);
|
||||
|
||||
if (cardPanel.getNextPanelInStack() != null) { //add next panel in stack if needed
|
||||
@@ -165,16 +195,18 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
|
||||
@Override
|
||||
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
|
||||
orderedCards.clear();
|
||||
orderedCards().clear();
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float cardHeight = visibleHeight;
|
||||
float cardWidth = getCardWidth(cardHeight);
|
||||
|
||||
for (CardAreaPanel cardPanel : cardPanels) {
|
||||
int count = addCards(cardPanel, x, y, cardWidth, cardHeight);
|
||||
x += cardWidth + (count - 1) * cardWidth * CARD_STACK_OFFSET;
|
||||
for (CardAreaPanel cardPanel : new ArrayList<>(cardPanels())) {
|
||||
if (cardPanel != null) {
|
||||
int count = addCards(cardPanel, x, y, cardWidth, cardHeight);
|
||||
x += cardWidth + (count - 1) * cardWidth * CARD_STACK_OFFSET;
|
||||
}
|
||||
}
|
||||
|
||||
return new ScrollBounds(x, visibleHeight);
|
||||
@@ -189,30 +221,33 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
|
||||
@Override
|
||||
public String getActivateAction(int index) {
|
||||
if(!GuiBase.isNetworkplay()) //causes lag on netplay client side
|
||||
return MatchController.instance.getGameController().getActivateDescription(orderedCards.get(index));
|
||||
if(!GuiBase.isNetworkplay()) {
|
||||
//causes lag on netplay client side, also index shouldn't be out of bounds
|
||||
if (index >= 0 && index < orderedCards().size())
|
||||
return MatchController.instance.getGameController().getActivateDescription(orderedCards().get(index));
|
||||
}
|
||||
|
||||
return "Activate | Cast | Play (if allowed)"; //simple text on card zoom swipe up
|
||||
return Forge.getLocalizer().getMessage("lblActivateAction"); //simple text on card zoom swipe up
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedIndex(int index) {
|
||||
//just scroll card into view
|
||||
if (index < orderedCards.size()) {
|
||||
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards.get(index));
|
||||
if (index < orderedCards().size()) {
|
||||
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards().get(index));
|
||||
scrollIntoView(cardPanel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate(int index) {
|
||||
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards.get(index));
|
||||
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards().get(index));
|
||||
//must invoke in game thread in case a dialog needs to be shown
|
||||
ThreadUtil.invokeInGameThread(() -> cardPanel.selectCard(false));
|
||||
}
|
||||
|
||||
public static class CardAreaPanel extends FCardPanel {
|
||||
private static final Map<Integer, CardAreaPanel> allCardPanels = new HashMap<>();
|
||||
private static Map<Integer, CardAreaPanel> allCardPanels = new HashMap<>();
|
||||
|
||||
public static CardAreaPanel get(CardView card0) {
|
||||
CardAreaPanel cardPanel = allCardPanels.get(card0.getId());
|
||||
@@ -224,19 +259,23 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
}
|
||||
|
||||
public static void resetForNewGame() {
|
||||
for (CardAreaPanel cardPanel : allCardPanels.values()) {
|
||||
cardPanel.displayArea = null;
|
||||
cardPanel.attachedToPanel = null;
|
||||
cardPanel.attachedPanels.clear();
|
||||
cardPanel.prevPanelInStack = null;
|
||||
cardPanel.nextPanelInStack = null;
|
||||
if (allCardPanels != null) {
|
||||
for (CardAreaPanel cardPanel : allCardPanels.values()) {
|
||||
cardPanel.displayArea = null;
|
||||
cardPanel.attachedToPanel = null;
|
||||
cardPanel.attachedPanels.clear();
|
||||
cardPanel.prevPanelInStack = null;
|
||||
cardPanel.nextPanelInStack = null;
|
||||
}
|
||||
allCardPanels.clear();
|
||||
} else {
|
||||
allCardPanels = new HashMap<>();
|
||||
}
|
||||
allCardPanels.clear();
|
||||
}
|
||||
|
||||
private VCardDisplayArea displayArea;
|
||||
private CardAreaPanel attachedToPanel;
|
||||
private final List<CardAreaPanel> attachedPanels = new ArrayList<>();
|
||||
private List<CardAreaPanel> attachedPanels = new ArrayList<>();
|
||||
private CardAreaPanel nextPanelInStack, prevPanelInStack;
|
||||
|
||||
//use static get(card) function instead
|
||||
@@ -255,6 +294,12 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
attachedToPanel = attachedToPanel0;
|
||||
}
|
||||
public List<CardAreaPanel> getAttachedPanels() {
|
||||
if (attachedPanels == null) {
|
||||
attachedPanels = new ArrayList<>();
|
||||
String error = getCard() + " - Attached panel is null.";
|
||||
Sentry.captureMessage(error);
|
||||
System.err.println(error);
|
||||
}
|
||||
return attachedPanels;
|
||||
}
|
||||
public CardAreaPanel getNextPanelInStack() {
|
||||
@@ -295,12 +340,12 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (card.getAttachedTo() != null) {
|
||||
if (card != card.getAttachedTo().getAttachedTo())
|
||||
setAttachedToPanel(CardAreaPanel.get(card.getAttachedTo()));
|
||||
CardView getAttachedto = card.getAttachedTo();
|
||||
if (getAttachedto != null) {
|
||||
if (card != getAttachedto.getAttachedTo())
|
||||
setAttachedToPanel(CardAreaPanel.get(getAttachedto));
|
||||
else {
|
||||
attachedPanels.remove(CardAreaPanel.get(card.getAttachedTo()));
|
||||
attachedPanels.remove(CardAreaPanel.get(getAttachedto));
|
||||
setAttachedToPanel(null);
|
||||
}
|
||||
}
|
||||
@@ -334,7 +379,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
FThreads.invokeInEdtLater(CardAreaPanel.this::showZoom);
|
||||
} else if (!selectCard(false)) {
|
||||
//if no cards in stack can be selected, just show zoom/details for card
|
||||
FThreads.invokeInEdtLater(CardAreaPanel.this::showZoom);
|
||||
if (!MatchController.instance.isSelecting())
|
||||
FThreads.invokeInEdtLater(CardAreaPanel.this::showZoom);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@@ -403,7 +449,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
||||
public void showZoom() {
|
||||
if (displayArea == null) { return; }
|
||||
|
||||
final List<CardView> cards = displayArea.orderedCards;
|
||||
final List<CardView> cards = displayArea.orderedCards();
|
||||
CardZoom.show(cards, cards.indexOf(getCard()), displayArea);
|
||||
}
|
||||
|
||||
|
||||
@@ -3458,4 +3458,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=Wählen Sie die Reihenfolge der Karten
|
||||
lblAttractionRollResult={0} gewürfelt, um ihre Attraktionen zu besuchen. Ergebnis: {1}.
|
||||
lblAttractionDeckZone=Attraktionsdeck
|
||||
lblLights=Lichter
|
||||
lblJunkyardZone=schrottplatz
|
||||
lblJunkyardZone=schrottplatz
|
||||
lblActivateAction=Aktivieren | Besetzung | Spielen (falls erlaubt)
|
||||
lblActivateBand=Band mit Karte aktivieren
|
||||
lblRemoveFromCombat=Karte aus dem Kampf entfernen
|
||||
lblDeclareAttackersForCard=Angreifer für Karte deklarieren
|
||||
lblAttackWithCard=Angriff mit Karte
|
||||
lblDeclareBlockersForCard=Deklarieren Sie Blocker für die Karte
|
||||
lblBlockWithCard=Mit Karte sperren
|
||||
lblPayManaWithCard=Bezahle Mana mit Karte
|
||||
@@ -3177,4 +3177,12 @@ lblRelease=Release
|
||||
lblSnapshot=Snapshot
|
||||
lblNewSnapshotVersion=NEW FORGE-{0}!
|
||||
cbSnapshotUpdate=Check snapshot updates on startup.
|
||||
nlSnapshotUpdate=When enabled, automatically check snapshot updates on startup and displays the notification on the title bar.
|
||||
nlSnapshotUpdate=When enabled, automatically check snapshot updates on startup and displays the notification on the title bar.
|
||||
lblActivateAction=Activate | Cast | Play (if allowed)
|
||||
lblActivateBand=Activate band with card
|
||||
lblRemoveFromCombat=Remove card from combat
|
||||
lblDeclareAttackersForCard=Declare attackers for card
|
||||
lblAttackWithCard=Attack with card
|
||||
lblDeclareBlockersForCard=Declare blockers for card
|
||||
lblBlockWithCard=Block with card
|
||||
lblPayManaWithCard=Pay mana with card
|
||||
@@ -3465,4 +3465,12 @@ lbltoattractiondeck=a la plataforma de atracción
|
||||
lblFrom=de
|
||||
lblChooseOrderCardsPutIntoAttractionDeck=Elige el orden de las cartas para colocarlas en el mazo de atracciones.
|
||||
lblAttractionRollResult={0} rodó para visitar sus atracciones. Resultado: {1}.
|
||||
lblLights=Luces
|
||||
lblLights=Luces
|
||||
lblActivateAction=Activar | Elenco | Jugar (si está permitido)
|
||||
lblActivateBand=Activar banda con tarjeta
|
||||
lblRemoveFromCombat=Quitar carta del combate
|
||||
lblDeclareAttackersForCard=Declarar atacantes para tarjeta.
|
||||
lblAttackWithCard=Ataque con tarjeta
|
||||
lblDeclareBlockersForCard=Declarar bloqueadores para tarjeta
|
||||
lblBlockWithCard=Bloquear con tarjeta
|
||||
lblPayManaWithCard=Pagar mana con tarjeta
|
||||
@@ -3466,4 +3466,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=Choisissez l'ordre des cartes à mettre
|
||||
lblAttractionRollResult={0} ont lancé un jet pour visiter leurs attractions. Résultat: {1}.
|
||||
lblAttractionDeckZone=pont d'attraction
|
||||
lblLights=Lumières
|
||||
lblJunkyardZone=dépotoir
|
||||
lblJunkyardZone=dépotoir
|
||||
lblActivateAction=Activer | Casting | Jouer (si autorisé)
|
||||
lblActivateBand=Activer le groupe avec la carte
|
||||
lblRemoveFromCombat=Retirer une carte du combat
|
||||
lblDeclareAttackersForCard=Déclarer les attaquants pour la carte
|
||||
lblAttackWithCard=Attaque avec une carte
|
||||
lblDeclareBlockersForCard=Déclarer les bloqueurs de carte
|
||||
lblBlockWithCard=Bloquer avec une carte
|
||||
lblPayManaWithCard=Payer du mana avec une carte
|
||||
@@ -3464,4 +3464,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=Scegli l'ordine delle carte da inserire
|
||||
lblAttractionRollResult={0} ha rotolato per visitare le sue attrazioni. Risultato: {1}.
|
||||
lblAttractionDeckZone=attrazione
|
||||
lblLights=Luci
|
||||
lblJunkyardZone=discarica
|
||||
lblJunkyardZone=discarica
|
||||
lblActivateAction=Attiva | Cast | Gioca (se consentito)
|
||||
lblActivateBand=Attiva banda con card
|
||||
lblRemoveFromCombat=Rimuovi la carta dal combattimento
|
||||
lblDeclareAttackersForCard=Dichiara gli attaccanti per la carta
|
||||
lblAttackWithCard=Attacca con la carta
|
||||
lblDeclareBlockersForCard=Dichiarare i bloccanti per la carta
|
||||
lblBlockWithCard=Blocca con la carta
|
||||
lblPayManaWithCard=Paga mana con la carta
|
||||
@@ -3460,4 +3460,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=アトラクションデッキに入れ
|
||||
lblAttractionRollResult={0} はアトラクションを訪れるために転がりました。結果: {1}。
|
||||
lblAttractionDeckZone=アトラクションデッキ
|
||||
lblLights=ライト
|
||||
lblJunkyardZone=廃品置き場
|
||||
lblJunkyardZone=廃品置き場
|
||||
lblActivateAction=アクティブにする | キャスト | 遊ぶ(許可されている場合)
|
||||
lblActivateBand=カードでバンドをアクティブにする
|
||||
lblRemoveFromCombat=カードを戦闘から取り除く
|
||||
lblDeclareAttackersForCard=カードの攻撃者を宣言する
|
||||
lblAttackWithCard=カードで攻撃する
|
||||
lblDeclareBlockersForCard=カードのブロッカーを宣言する
|
||||
lblBlockWithCard=カードでブロックする
|
||||
lblPayManaWithCard=カードでマナを支払う
|
||||
@@ -3550,4 +3550,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=Escolha a ordem das cartas para colocar
|
||||
lblAttractionRollResult={0} rolou para visitar suas atrações. Resultado: {1}.
|
||||
lblAttractionDeckZone=deck de atração
|
||||
lblLights=Luzes
|
||||
lblJunkyardZone=ferro-velho
|
||||
lblJunkyardZone=ferro-velho
|
||||
lblActivateAction=Ativar | Elenco | Jogue (se permitido)
|
||||
lblActivateBand=Ativar banda com cartão
|
||||
lblRemoveFromCombat=Remover carta do combate
|
||||
lblDeclareAttackersForCard=Declarar atacantes para cartão
|
||||
lblAttackWithCard=Ataque com cartão
|
||||
lblDeclareBlockersForCard=Declarar bloqueadores para cartão
|
||||
lblBlockWithCard=Bloquear com cartão
|
||||
lblPayManaWithCard=Pague mana com cartão
|
||||
@@ -3451,4 +3451,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=选择放入景点牌组的卡片顺序
|
||||
lblAttractionRollResult={0} 滚动访问他们的景点。结果:{1}。
|
||||
lblAttractionDeckZone=吸引力甲板
|
||||
lblLights=灯
|
||||
lblJunkyardZone=垃圾场
|
||||
lblJunkyardZone=垃圾场
|
||||
lblActivateAction=激活 | 演员 | 播放(如果允许)
|
||||
lblActivateBand=用卡激活手环
|
||||
lblRemoveFromCombat=将卡牌从战斗中移除
|
||||
lblDeclareAttackersForCard=宣布卡的攻击者
|
||||
lblAttackWithCard=用卡攻击
|
||||
lblDeclareBlockersForCard=声明卡的拦截器
|
||||
lblBlockWithCard=用卡挡住
|
||||
lblPayManaWithCard=用卡支付法力
|
||||
@@ -247,18 +247,18 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
public String getActivateAction(Card card) {
|
||||
if (combat.isAttacking(card, currentDefender)) {
|
||||
if (potentialBanding) {
|
||||
return "activate band with card";
|
||||
return Localizer.getInstance().getMessage("lblActivateBand");
|
||||
}
|
||||
return "remove card from combat";
|
||||
return Localizer.getInstance().getMessage("lblRemoveFromCombat");
|
||||
}
|
||||
if (card.getController().isOpponentOf(playerAttacks)) {
|
||||
if (defenders.contains(card)) {
|
||||
return "declare attackers for card";
|
||||
return Localizer.getInstance().getMessage("lblDeclareAttackersForCard");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (playerAttacks.getZone(ZoneType.Battlefield).contains(card) && CombatUtil.canAttack(card, currentDefender)) {
|
||||
return "attack with card";
|
||||
return Localizer.getInstance().getMessage("lblAttackWithCard");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -148,14 +148,14 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
@Override
|
||||
public String getActivateAction(Card card) {
|
||||
if (combat.isAttacking(card)) {
|
||||
return "declare blockers for card";
|
||||
return Localizer.getInstance().getMessage("lblDeclareBlockersForCard");
|
||||
}
|
||||
if (currentAttacker != null && card.isCreature() && defender.getZone(ZoneType.Battlefield).contains(card)) {
|
||||
if (combat.isBlocking(card, currentAttacker)) {
|
||||
return "remove card from combat";
|
||||
return Localizer.getInstance().getMessage("lblRemoveFromCombat");
|
||||
}
|
||||
if (CombatUtil.canBlock(currentAttacker, card, combat)) {
|
||||
return "block with card";
|
||||
return Localizer.getInstance().getMessage("lblBlockWithCard");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -136,7 +136,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
public String getActivateAction(Card card) {
|
||||
for (SpellAbility sa : getAllManaAbilities(card)) {
|
||||
if (sa.canPlay()) {
|
||||
return "pay mana with card";
|
||||
return Localizer.getInstance().getMessage("lblPayManaWithCard");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user