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
stAb.putParam("AffectedZone", "All");
SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Battlefield");
game.getAction().moveToCommand(eff, cause);
eff.getOwner().getZone(ZoneType.Command).add(eff);
}
eff.addRemembered(copied);
@@ -1112,6 +1112,10 @@ public class GameAction {
// search for cards with static abilities
final FCollection<StaticAbility> staticAbilities = new FCollection<>();
final CardCollection staticList = new CardCollection();
Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies = null;
if (preList.isEmpty()) {
dependencies = HashBasedTable.create();
}
game.forEachCardInGame(new Visitor<Card>() {
@Override
@@ -1146,7 +1150,7 @@ public class GameAction {
StaticAbility stAb = staticsForLayer.get(0);
// dependency with CDA seems unlikely
if (!stAb.isCharacteristicDefining()) {
stAb = findStaticAbilityToApply(layer, staticsForLayer, preList, affectedPerAbility);
stAb = findStaticAbilityToApply(layer, staticsForLayer, preList, affectedPerAbility, dependencies);
}
staticsForLayer.remove(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.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
@@ -1251,7 +1257,8 @@ public class GameAction {
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) {
return staticsForLayer.get(0);
}
@@ -1309,6 +1316,13 @@ public class GameAction {
if (dependency) {
dependencyGraph.addVertex(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

View File

@@ -1,8 +1,12 @@
package forge.game;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
import forge.LobbyPlayer;
import forge.deck.Deck;
import forge.game.GameOutcome.AnteResult;
@@ -16,6 +20,8 @@ import forge.game.phase.PhaseType;
import forge.game.player.PlayerView;
import forge.game.player.RegisteredPlayer;
import forge.game.spellability.StackItemView;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityLayer;
import forge.game.zone.MagicStack;
import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject;
@@ -200,15 +206,36 @@ public class GameView extends TrackableObject {
public TrackableCollection<CardView> getRevealedCollection() {
return get(TrackableProperty.RevealedCardsCollection);
}
public void updateRevealedCards(TrackableCollection<CardView> 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() {
return get(TrackableProperty.CombatView);
}
public void updateCombatView(CombatView combatView) {
set(TrackableProperty.CombatView, combatView);
}

View File

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

View File

@@ -301,7 +301,8 @@ public enum TrackableProperty {
GameLog(TrackableTypes.StringType),
NeedsPhaseRedrawn(TrackableTypes.BooleanType),
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 {
IgnoresFreeze,

View File

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

View File

@@ -104,6 +104,7 @@ import forge.player.PlayerZoneUpdate;
import forge.player.PlayerZoneUpdates;
import forge.screens.match.controllers.CAntes;
import forge.screens.match.controllers.CCombat;
import forge.screens.match.controllers.CDependencies;
import forge.screens.match.controllers.CDetailPicture;
import forge.screens.match.controllers.CDev;
import forge.screens.match.controllers.CDock;
@@ -167,6 +168,7 @@ public final class CMatchUI
private final CAntes cAntes = new CAntes(this);
private final CCombat cCombat = new CCombat();
private final CDependencies cDependencies = new CDependencies(this);
private final CDetailPicture cDetailPicture = new CDetailPicture(this);
private final CDev cDev = new CDev(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_STACK, getCStack().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.DEV_MODE, getCDev().getView());
this.myDocs.put(EDocID.BUTTON_DOCK, getCDock().getView());
@@ -410,6 +413,11 @@ public final class CMatchUI
cCombat.update();
} // showCombat(CombatView)
@Override
public void updateDependencies() {
cDependencies.update();
}
@Override
public void updateDayTime(String 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_COMBAT</doc>
<doc>REPORT_LOG</doc>
<doc>REPORT_DEPENDENCIES</doc>
</cell>
<cell x="0.0" y="0.643" w="0.2" h="0.357">
<doc>REPORT_MESSAGE</doc>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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