refactor VCardDisplayArea, prevent NPE

This commit is contained in:
Anthony Calosa
2024-11-03 10:07:14 +08:00
parent 68a5cb6c82
commit 0ddf5b25d7
12 changed files with 169 additions and 59 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -3460,4 +3460,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=アトラクションデッキに入れ
lblAttractionRollResult={0} はアトラクションを訪れるために転がりました。結果: {1}。
lblAttractionDeckZone=アトラクションデッキ
lblLights=ライト
lblJunkyardZone=廃品置き場
lblJunkyardZone=廃品置き場
lblActivateAction=アクティブにする | キャスト | 遊ぶ(許可されている場合)
lblActivateBand=カードでバンドをアクティブにする
lblRemoveFromCombat=カードを戦闘から取り除く
lblDeclareAttackersForCard=カードの攻撃者を宣言する
lblAttackWithCard=カードで攻撃する
lblDeclareBlockersForCard=カードのブロッカーを宣言する
lblBlockWithCard=カードでブロックする
lblPayManaWithCard=カードでマナを支払う

View File

@@ -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

View File

@@ -3451,4 +3451,12 @@ lblChooseOrderCardsPutIntoAttractionDeck=选择放入景点牌组的卡片顺序
lblAttractionRollResult={0} 滚动访问他们的景点。结果:{1}。
lblAttractionDeckZone=吸引力甲板
lblLights=
lblJunkyardZone=垃圾场
lblJunkyardZone=垃圾场
lblActivateAction=激活 | 演员 | 播放(如果允许)
lblActivateBand=用卡激活手环
lblRemoveFromCombat=将卡牌从战斗中移除
lblDeclareAttackersForCard=宣布卡的攻击者
lblAttackWithCard=用卡攻击
lblDeclareBlockersForCard=声明卡的拦截器
lblBlockWithCard=用卡挡住
lblPayManaWithCard=用卡支付法力

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;