Dependency tab (#7013)

This commit is contained in:
tool4ever
2025-02-19 10:31:59 +01:00
committed by GitHub
parent 049eb19be4
commit ec20b59ff3
20 changed files with 258 additions and 17 deletions

View File

@@ -707,7 +707,7 @@ public class GameAction {
// needed for ETB lookahead like Bronzehide Lion // needed for ETB lookahead like Bronzehide Lion
stAb.putParam("AffectedZone", "All"); stAb.putParam("AffectedZone", "All");
SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Battlefield"); SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Battlefield");
game.getAction().moveToCommand(eff, cause); eff.getOwner().getZone(ZoneType.Command).add(eff);
} }
eff.addRemembered(copied); eff.addRemembered(copied);
@@ -1112,6 +1112,10 @@ public class GameAction {
// search for cards with static abilities // search for cards with static abilities
final FCollection<StaticAbility> staticAbilities = new FCollection<>(); final FCollection<StaticAbility> staticAbilities = new FCollection<>();
final CardCollection staticList = new CardCollection(); final CardCollection staticList = new CardCollection();
Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies = null;
if (preList.isEmpty()) {
dependencies = HashBasedTable.create();
}
game.forEachCardInGame(new Visitor<Card>() { game.forEachCardInGame(new Visitor<Card>() {
@Override @Override
@@ -1146,7 +1150,7 @@ public class GameAction {
StaticAbility stAb = staticsForLayer.get(0); StaticAbility stAb = staticsForLayer.get(0);
// dependency with CDA seems unlikely // dependency with CDA seems unlikely
if (!stAb.isCharacteristicDefining()) { if (!stAb.isCharacteristicDefining()) {
stAb = findStaticAbilityToApply(layer, staticsForLayer, preList, affectedPerAbility); stAb = findStaticAbilityToApply(layer, staticsForLayer, preList, affectedPerAbility, dependencies);
} }
staticsForLayer.remove(stAb); staticsForLayer.remove(stAb);
final CardCollectionView previouslyAffected = affectedPerAbility.get(stAb); final CardCollectionView previouslyAffected = affectedPerAbility.get(stAb);
@@ -1233,6 +1237,8 @@ public class GameAction {
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.Immediate, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Immediate, runParams, false);
game.getView().setDependencies(dependencies);
} }
// Update P/T and type in the view only once after all the cards have been processed, to avoid flickering // Update P/T and type in the view only once after all the cards have been processed, to avoid flickering
@@ -1251,7 +1257,8 @@ public class GameAction {
game.getTracker().unfreeze(); game.getTracker().unfreeze();
} }
private StaticAbility findStaticAbilityToApply(StaticAbilityLayer layer, List<StaticAbility> staticsForLayer, CardCollectionView preList, Map<StaticAbility, CardCollectionView> affectedPerAbility) { private StaticAbility findStaticAbilityToApply(StaticAbilityLayer layer, List<StaticAbility> staticsForLayer, CardCollectionView preList, Map<StaticAbility, CardCollectionView> affectedPerAbility,
Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies) {
if (staticsForLayer.size() == 1) { if (staticsForLayer.size() == 1) {
return staticsForLayer.get(0); return staticsForLayer.get(0);
} }
@@ -1309,6 +1316,13 @@ public class GameAction {
if (dependency) { if (dependency) {
dependencyGraph.addVertex(otherStAb); dependencyGraph.addVertex(otherStAb);
dependencyGraph.addEdge(stAb, otherStAb); dependencyGraph.addEdge(stAb, otherStAb);
if (dependencies != null) {
if (dependencies.contains(stAb, otherStAb)) {
dependencies.get(stAb, otherStAb).add(layer);
} else {
dependencies.put(stAb, otherStAb, EnumSet.of(layer));
}
}
} }
// undo changes and check next pair // undo changes and check next pair

View File

@@ -1,8 +1,12 @@
package forge.game; package forge.game;
import java.util.List; import java.util.List;
import java.util.Set;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
import forge.LobbyPlayer; import forge.LobbyPlayer;
import forge.deck.Deck; import forge.deck.Deck;
import forge.game.GameOutcome.AnteResult; import forge.game.GameOutcome.AnteResult;
@@ -16,6 +20,8 @@ import forge.game.phase.PhaseType;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.player.RegisteredPlayer; import forge.game.player.RegisteredPlayer;
import forge.game.spellability.StackItemView; import forge.game.spellability.StackItemView;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityLayer;
import forge.game.zone.MagicStack; import forge.game.zone.MagicStack;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject; import forge.trackable.TrackableObject;
@@ -200,15 +206,36 @@ public class GameView extends TrackableObject {
public TrackableCollection<CardView> getRevealedCollection() { public TrackableCollection<CardView> getRevealedCollection() {
return get(TrackableProperty.RevealedCardsCollection); return get(TrackableProperty.RevealedCardsCollection);
} }
public void updateRevealedCards(TrackableCollection<CardView> collection) { public void updateRevealedCards(TrackableCollection<CardView> collection) {
set(TrackableProperty.RevealedCardsCollection, collection); set(TrackableProperty.RevealedCardsCollection, collection);
} }
public String getDependencies() {
return get(TrackableProperty.Dependencies);
}
public void setDependencies(Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies) {
if (dependencies.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder();
StaticAbilityLayer layer = null;
for (StaticAbilityLayer sal : StaticAbilityLayer.CONTINUOUS_LAYERS_WITH_DEPENDENCY) {
for (Cell<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dep : dependencies.cellSet()) {
if (dep.getValue().contains(sal)) {
if (layer != sal) {
layer = sal;
sb.append("Layer " + layer.num).append(": ");
}
sb.append(dep.getColumnKey().getHostCard().toString()).append(" <- ").append(dep.getRowKey().getHostCard().toString()).append("\n");
}
}
}
set(TrackableProperty.Dependencies, sb.toString());
}
public CombatView getCombat() { public CombatView getCombat() {
return get(TrackableProperty.CombatView); return get(TrackableProperty.CombatView);
} }
public void updateCombatView(CombatView combatView) { public void updateCombatView(CombatView combatView) {
set(TrackableProperty.CombatView, combatView); set(TrackableProperty.CombatView, combatView);
} }

View File

@@ -3,35 +3,44 @@ package forge.game.staticability;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
public enum StaticAbilityLayer { public enum StaticAbilityLayer {
/** Layer 1 for control-changing effects. */ /** Layer 1 for copiable values. */
COPY, COPY("1"),
/** Layer 2 for control-changing effects. */ /** Layer 2 for control-changing effects. */
CONTROL, CONTROL("2"),
/** Layer 3 for text-changing effects. */ /** Layer 3 for text-changing effects. */
TEXT, TEXT("3"),
/** Layer 4 for type-changing effects. */ /** Layer 4 for type-changing effects. */
TYPE, TYPE("4"),
/** Layer 5 for color-changing effects. */ /** Layer 5 for color-changing effects. */
COLOR, COLOR("5"),
/** Layer 6 for ability effects. */ /** Layer 6 for ability effects. */
ABILITIES, ABILITIES("6"),
/** Layer 7a for characteristic-defining power/toughness effects. */ /** Layer 7a for characteristic-defining power/toughness effects. */
CHARACTERISTIC, CHARACTERISTIC("7a"),
/** Layer 7b for power- and/or toughness-setting effects. */ /** Layer 7b for power- and/or toughness-setting effects. */
SETPT, SETPT("7b"),
/** Layer 7c for power- and/or toughness-modifying effects. */ /** Layer 7c for power- and/or toughness-modifying effects. */
MODIFYPT, MODIFYPT("7c"),
/** Layer 7d for power- and/or toughness-switching effects. */
//SWITCHPT("7d"),
/** Layer for game rule-changing effects. */ /** Layer for game rule-changing effects. */
RULES; RULES("8");
public final String num;
StaticAbilityLayer(String n) {
num = n;
}
public final static ImmutableList<StaticAbilityLayer> CONTINUOUS_LAYERS = public final static ImmutableList<StaticAbilityLayer> CONTINUOUS_LAYERS =
ImmutableList.of(COPY, CONTROL, TEXT, TYPE, COLOR, ABILITIES, CHARACTERISTIC, SETPT, MODIFYPT, RULES); ImmutableList.of(COPY, CONTROL, TEXT, TYPE, COLOR, ABILITIES, CHARACTERISTIC, SETPT, MODIFYPT, RULES);

View File

@@ -301,7 +301,8 @@ public enum TrackableProperty {
GameLog(TrackableTypes.StringType), GameLog(TrackableTypes.StringType),
NeedsPhaseRedrawn(TrackableTypes.BooleanType), NeedsPhaseRedrawn(TrackableTypes.BooleanType),
PlayerTurn(TrackableTypes.PlayerViewType, FreezeMode.IgnoresFreeze), PlayerTurn(TrackableTypes.PlayerViewType, FreezeMode.IgnoresFreeze),
Phase(TrackableTypes.EnumType(PhaseType.class), FreezeMode.IgnoresFreeze); Phase(TrackableTypes.EnumType(PhaseType.class), FreezeMode.IgnoresFreeze),
Dependencies(TrackableTypes.StringType);
public enum FreezeMode { public enum FreezeMode {
IgnoresFreeze, IgnoresFreeze,

View File

@@ -87,6 +87,7 @@ public enum EDocID {
REPORT_MESSAGE (), REPORT_MESSAGE (),
REPORT_STACK (), REPORT_STACK (),
REPORT_COMBAT (), REPORT_COMBAT (),
REPORT_DEPENDENCIES (),
REPORT_LOG (), REPORT_LOG (),
DEV_MODE (), DEV_MODE (),

View File

@@ -104,6 +104,7 @@ import forge.player.PlayerZoneUpdate;
import forge.player.PlayerZoneUpdates; import forge.player.PlayerZoneUpdates;
import forge.screens.match.controllers.CAntes; import forge.screens.match.controllers.CAntes;
import forge.screens.match.controllers.CCombat; import forge.screens.match.controllers.CCombat;
import forge.screens.match.controllers.CDependencies;
import forge.screens.match.controllers.CDetailPicture; import forge.screens.match.controllers.CDetailPicture;
import forge.screens.match.controllers.CDev; import forge.screens.match.controllers.CDev;
import forge.screens.match.controllers.CDock; import forge.screens.match.controllers.CDock;
@@ -167,6 +168,7 @@ public final class CMatchUI
private final CAntes cAntes = new CAntes(this); private final CAntes cAntes = new CAntes(this);
private final CCombat cCombat = new CCombat(); private final CCombat cCombat = new CCombat();
private final CDependencies cDependencies = new CDependencies(this);
private final CDetailPicture cDetailPicture = new CDetailPicture(this); private final CDetailPicture cDetailPicture = new CDetailPicture(this);
private final CDev cDev = new CDev(this); private final CDev cDev = new CDev(this);
private final CDock cDock = new CDock(this); private final CDock cDock = new CDock(this);
@@ -190,6 +192,7 @@ public final class CMatchUI
this.myDocs.put(EDocID.REPORT_MESSAGE, getCPrompt().getView()); this.myDocs.put(EDocID.REPORT_MESSAGE, getCPrompt().getView());
this.myDocs.put(EDocID.REPORT_STACK, getCStack().getView()); this.myDocs.put(EDocID.REPORT_STACK, getCStack().getView());
this.myDocs.put(EDocID.REPORT_COMBAT, cCombat.getView()); this.myDocs.put(EDocID.REPORT_COMBAT, cCombat.getView());
this.myDocs.put(EDocID.REPORT_DEPENDENCIES, cDependencies.getView());
this.myDocs.put(EDocID.REPORT_LOG, cLog.getView()); this.myDocs.put(EDocID.REPORT_LOG, cLog.getView());
this.myDocs.put(EDocID.DEV_MODE, getCDev().getView()); this.myDocs.put(EDocID.DEV_MODE, getCDev().getView());
this.myDocs.put(EDocID.BUTTON_DOCK, getCDock().getView()); this.myDocs.put(EDocID.BUTTON_DOCK, getCDock().getView());
@@ -410,6 +413,11 @@ public final class CMatchUI
cCombat.update(); cCombat.update();
} // showCombat(CombatView) } // showCombat(CombatView)
@Override
public void updateDependencies() {
cDependencies.update();
}
@Override @Override
public void updateDayTime(String daytime) { public void updateDayTime(String daytime) {
super.updateDayTime(daytime); super.updateDayTime(daytime);

View File

@@ -0,0 +1,51 @@
package forge.screens.match.controllers;
import forge.game.GameView;
import forge.gui.framework.ICDoc;
import forge.screens.match.CMatchUI;
import forge.screens.match.views.VDependencies;
/**
* Controls the combat panel in the match UI.
*
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
*
*/
public class CDependencies implements ICDoc {
private final CMatchUI matchUI;
private final VDependencies view;
public CDependencies(CMatchUI cMatchUI) {
view = new VDependencies(this);
matchUI = cMatchUI;
}
public VDependencies getView() {
return view;
}
@Override
public void register() {
}
/* (non-Javadoc)
* @see forge.gui.framework.ICDoc#initialize()
*/
@Override
public void initialize() {
}
/* (non-Javadoc)
* @see forge.gui.framework.ICDoc#update()
*/
@Override
public void update() {
GameView game = matchUI.getGameView();
if (game == null || game.getDependencies() == null) {
return;
}
String dependencies = game.getDependencies();
view.updateDependencies(dependencies.lines().count(), dependencies);
}
}

View File

@@ -0,0 +1,115 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Nate
*
* 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.screens.match.views;
import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab;
import forge.gui.framework.EDocID;
import forge.gui.framework.IVDoc;
import forge.screens.match.controllers.CDependencies;
import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinnedTextArea;
import forge.util.Localizer;
import net.miginfocom.swing.MigLayout;
/**
* Assembles Swing components of layer dependencies.
*
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
*/
public class VDependencies implements IVDoc<CDependencies> {
// Fields used with interface IVDoc
private DragCell parentCell;
private final DragTab tab = new DragTab(Localizer.getInstance().getMessage("lblDependenciesTab"));
private final SkinnedTextArea tar = new SkinnedTextArea();
private final CDependencies controller;
public VDependencies(final CDependencies controller) {
this.controller = controller;
tar.setOpaque(false);
tar.setBorder(new FSkin.MatteSkinBorder(0, 0, 0, 0, FSkin.getColor(FSkin.Colors.CLR_BORDERS)));
tar.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
tar.setFocusable(false);
tar.setLineWrap(true);
}
//========== Overridden methods
/* (non-Javadoc)
* @see forge.gui.framework.IVDoc#populate()
*/
@Override
public void populate() {
parentCell.getBody().removeAll();
parentCell.getBody().setLayout(new MigLayout("insets 0, gap 0, wrap"));
parentCell.getBody().add(tar, "w 95%!, gapleft 3%, gaptop 1%, h 95%");
}
/* (non-Javadoc)
* @see forge.gui.framework.IVDoc#setParentCell()
*/
@Override
public void setParentCell(final DragCell cell0) {
this.parentCell = cell0;
}
/* (non-Javadoc)
* @see forge.gui.framework.IVDoc#getParentCell()
*/
@Override
public DragCell getParentCell() {
return this.parentCell;
}
/* (non-Javadoc)
* @see forge.gui.framework.IVDoc#getDocumentID()
*/
@Override
public EDocID getDocumentID() {
return EDocID.REPORT_DEPENDENCIES;
}
/* (non-Javadoc)
* @see forge.gui.framework.IVDoc#getTabLabel()
*/
@Override
public DragTab getTabLabel() {
return tab;
}
/* (non-Javadoc)
* @see forge.gui.framework.IVDoc#getLayoutControl()
*/
@Override
public CDependencies getLayoutControl() {
return controller;
}
//========= Observer update methods
public void updateDependencies(final long cntDependencies, final String desc) {
tab.setText(cntDependencies > 0 ? (Localizer.getInstance().getMessage("lblDependenciesTab") + " : " + cntDependencies) : Localizer.getInstance().getMessage("lblDependenciesTab"));
// No need to update this unless it's showing
if (parentCell == null || !this.equals(parentCell.getSelected())) { return; }
tar.setText(desc);
}
}

View File

@@ -4,6 +4,7 @@
<doc>REPORT_STACK</doc> <doc>REPORT_STACK</doc>
<doc>REPORT_COMBAT</doc> <doc>REPORT_COMBAT</doc>
<doc>REPORT_LOG</doc> <doc>REPORT_LOG</doc>
<doc>REPORT_DEPENDENCIES</doc>
</cell> </cell>
<cell x="0.0" y="0.643" w="0.2" h="0.357"> <cell x="0.0" y="0.643" w="0.2" h="0.357">
<doc>REPORT_MESSAGE</doc> <doc>REPORT_MESSAGE</doc>

View File

@@ -1105,6 +1105,7 @@ lblPlayers=Spieler
lblLog=Bericht lblLog=Bericht
lblDev=Entw. lblDev=Entw.
lblCombatTab=Kampf lblCombatTab=Kampf
lblDependenciesTab=Dependencies
lblStack=Stapel lblStack=Stapel
lblMustWaitPriority=Warte auf Priorität... lblMustWaitPriority=Warte auf Priorität...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1118,6 +1118,7 @@ lblPlayers=Players
lblLog=Log lblLog=Log
lblDev=Dev lblDev=Dev
lblCombatTab=Combat lblCombatTab=Combat
lblDependenciesTab=Dependencies
lblStack=Stack lblStack=Stack
lblMustWaitPriority=Must wait for priority... lblMustWaitPriority=Must wait for priority...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1111,6 +1111,7 @@ lblPlayers=Jugadores
lblLog=Log lblLog=Log
lblDev=Dev lblDev=Dev
lblCombatTab=Combate lblCombatTab=Combate
lblDependenciesTab=Dependencies
lblStack=Pila lblStack=Pila
lblMustWaitPriority=Debes esperar debido a la prioridad... lblMustWaitPriority=Debes esperar debido a la prioridad...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1109,6 +1109,7 @@ lblPlayers=Joueurs
lblLog=Journal lblLog=Journal
lblDev=Dev lblDev=Dev
lblCombatTab=Combat lblCombatTab=Combat
lblDependenciesTab=Dependencies
lblStack=Empiler lblStack=Empiler
lblMustWaitPriority=Doit attendre la priorité... lblMustWaitPriority=Doit attendre la priorité...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1106,6 +1106,7 @@ lblPlayers=Giocatori
lblLog=Registro lblLog=Registro
lblDev=Sviluppo lblDev=Sviluppo
lblCombatTab=Combattimento lblCombatTab=Combattimento
lblDependenciesTab=Dependencies
lblStack=Pila lblStack=Pila
lblMustWaitPriority=Devi aspettare la priorità ... lblMustWaitPriority=Devi aspettare la priorità ...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1108,6 +1108,7 @@ lblPlayers=プレイヤー
lblLog=ログ lblLog=ログ
lblDev=開発者 lblDev=開発者
lblCombatTab=戦闘 lblCombatTab=戦闘
lblDependenciesTab=Dependencies
lblStack=スタック lblStack=スタック
lblMustWaitPriority=優先権を待つ必要があります... lblMustWaitPriority=優先権を待つ必要があります...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1133,6 +1133,7 @@ lblPlayers=Jogadores
lblLog=Registros lblLog=Registros
lblDev=Desenv. lblDev=Desenv.
lblCombatTab=Batalha lblCombatTab=Batalha
lblDependenciesTab=Dependencies
lblStack=Pilha lblStack=Pilha
lblMustWaitPriority=Deve esperar pela prioridade... lblMustWaitPriority=Deve esperar pela prioridade...
#FDeckEditor.java #FDeckEditor.java

View File

@@ -1112,6 +1112,7 @@ lblPlayers=玩家列表
lblLog=日志 lblLog=日志
lblDev=开发者工具 lblDev=开发者工具
lblCombatTab=战斗 lblCombatTab=战斗
lblDependenciesTab=Dependencies
lblStack=堆叠 lblStack=堆叠
lblMustWaitPriority=等待获得优先权 lblMustWaitPriority=等待获得优先权
#FDeckEditor.java #FDeckEditor.java

View File

@@ -846,5 +846,8 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
} }
daytime = null; daytime = null;
} }
public void updateDependencies() {
}
// End of Choice code // End of Choice code
} }

View File

@@ -203,6 +203,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
@Override @Override
public Void visit(final GameEventPlayerPriority event) { public Void visit(final GameEventPlayerPriority event) {
needCombatUpdate = true; needCombatUpdate = true;
matchController.updateDependencies();
return processEvent(); return processEvent();
} }

View File

@@ -105,6 +105,8 @@ public interface IGuiGame {
void updateLives(Iterable<PlayerView> livesUpdate); void updateLives(Iterable<PlayerView> livesUpdate);
void updateShards(Iterable<PlayerView> shardsUpdate); void updateShards(Iterable<PlayerView> shardsUpdate);
void updateDependencies();
void setPanelSelection(CardView hostCard); void setPanelSelection(CardView hostCard);
SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent); SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent);