Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full Control unleashed #6700

Merged
merged 6 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1653,4 +1653,9 @@ public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> v

return choices;
}

@Override
public List<CostPart> orderCosts(List<CostPart> costs) {
return costs;
}
}
2 changes: 0 additions & 2 deletions forge-core/src/main/java/forge/util/Localizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ public String getMessage(boolean forcedEnglish, final String key, final Object..
}

public void setLanguage(final String languageRegionID, final String languagesDirectory) {

String[] splitLocale = languageRegionID.split("-");

Locale oldLocale = locale;
Expand Down Expand Up @@ -174,7 +173,6 @@ public void setLanguage(final String languageRegionID, final String languagesDir
notifyObservers();

}

}

public List<Language> getLanguages() {
Expand Down
3 changes: 0 additions & 3 deletions forge-game/src/main/java/forge/game/card/Card.java
Original file line number Diff line number Diff line change
Expand Up @@ -4828,7 +4828,6 @@ public final boolean tap(boolean attacker, boolean tapAnimation, SpellAbility ca
return false;
}

// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(this);
runParams.put(AbilityKey.Attacker, attacker);
runParams.put(AbilityKey.Cause, cause);
Expand All @@ -4844,12 +4843,10 @@ public final boolean tap(boolean attacker, boolean tapAnimation, SpellAbility ca
public final boolean untap(boolean untapAnimation) {
if (!tapped) { return false; }

// Run Replacement effects
if (getGame().getReplacementHandler().run(ReplacementType.Untap, AbilityKey.mapFromAffected(this)) != ReplacementResult.NotReplaced) {
return false;
}

// Run triggers
getGame().getTriggerHandler().runTrigger(TriggerType.Untaps, AbilityKey.mapFromCard(this), false);

runUntapCommands();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public final boolean executePayment(Player payer, SpellAbility ability, Card tar

// always returns true, made this to inline with return
protected boolean executePayment(Player payer, SpellAbility ability, CardCollectionView targetCards, final boolean effect) {
// need to refresh statics (e.g. sacrificing Omnath, Locus of Mana to Momentous Fall could end up with less toughness)
payer.getGame().getAction().checkStaticAbilities();
table.setLastStateBattlefield(payer.getGame().copyLastStateBattlefield());
table.setLastStateGraveyard(payer.getGame().copyLastStateGraveyard());

Expand Down
15 changes: 7 additions & 8 deletions forge-game/src/main/java/forge/game/cost/CostPayment.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,7 @@ public static boolean canPayAdditionalCosts(Cost cost, final SpellAbility abilit
* @return a boolean.
*/
public final boolean isFullyPaid() {
for (final CostPart part : adjustedCost.getCostParts()) {
if (!this.paidCostParts.contains(part)) {
return false;
}
}

return true;
return paidCostParts.containsAll(adjustedCost.getCostParts());
}

/**
Expand All @@ -136,7 +130,12 @@ public final void refundPayment() {

public boolean payCost(final CostDecisionMakerBase decisionMaker) {
adjustedCost = CostAdjustment.adjust(cost, ability, decisionMaker.isEffect());
final List<CostPart> costParts = adjustedCost.getCostPartsWithZeroMana();
List<CostPart> costParts = adjustedCost.getCostPartsWithZeroMana();

if (adjustedCost.getCostParts().size() > 1) {
// if mana part is shown here it wouldn't include reductions, but that's just a minor inconvenience
costParts = decisionMaker.getPlayer().getController().orderCosts(costParts);
}

final Game game = decisionMaker.getPlayer().getGame();

Expand Down
26 changes: 22 additions & 4 deletions forge-game/src/main/java/forge/game/player/PlayerController.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@

import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

/**
Expand All @@ -58,9 +60,21 @@ public enum BinaryChoiceType {
OddsOrEvens,
UntapOrLeaveTapped,
LeftOrRight,
AddOrRemove,
AddOrRemove
}

public enum FullControlFlag {
ChooseCostOrder,
ChooseCostReductionOrderAndVariableAmount,
//ChooseManaPoolShard, // select shard with special properties
NoPaymentFromManaAbility,
NoFreeCombatCostHandling,
AllowPaymentStartWithMissingResources,
//AdditionalLayerTimestampOrder // tokens etc.
}

private Set<FullControlFlag> fullControls = EnumSet.noneOf(FullControlFlag.class);

protected final GameView gameView;

protected final Player player;
Expand Down Expand Up @@ -287,10 +301,12 @@ public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, Str

public abstract List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider);

public boolean isFullControl() {
return false;
public Set<FullControlFlag> getFullControl() {
return fullControls;
}
public boolean isFullControl(FullControlFlag f) {
return fullControls.contains(f);
}
public void setFullControl(boolean full) {}

public abstract void autoPassCancel();

Expand Down Expand Up @@ -321,4 +337,6 @@ public AnteResult getAnteResult() {
public abstract CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap,
SpellAbility sa, String title, boolean isOptional);

public abstract List<CostPart> orderCosts(List<CostPart> costs);

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import forge.game.cost.Cost;
import forge.game.cost.CostPayment;
import forge.game.player.Player;
import forge.game.player.PlayerController.FullControlFlag;
import forge.game.staticability.StaticAbilityCantBeCast;

/**
Expand Down Expand Up @@ -95,7 +96,8 @@ public boolean canPlay() {
return false;
}

return CostPayment.canPayAdditionalCosts(this.getPayCosts(), this, false);
return player.getController().isFullControl(FullControlFlag.AllowPaymentStartWithMissingResources)
|| CostPayment.canPayAdditionalCosts(this.getPayCosts(), this, false);
}

/** {@inheritDoc} */
Expand Down
4 changes: 3 additions & 1 deletion forge-game/src/main/java/forge/game/spellability/Spell.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import forge.game.cost.Cost;
import forge.game.cost.CostPayment;
import forge.game.player.Player;
import forge.game.player.PlayerController.FullControlFlag;
import forge.game.replacement.ReplacementType;
import forge.game.staticability.StaticAbilityCantBeCast;
import forge.game.zone.ZoneType;
Expand Down Expand Up @@ -101,7 +102,8 @@ public boolean canPlay() {
return false;
}

if (!CostPayment.canPayAdditionalCosts(this.getPayCosts(), this, false)) {
if (!activator.getController().isFullControl(FullControlFlag.AllowPaymentStartWithMissingResources) &&
!CostPayment.canPayAdditionalCosts(this.getPayCosts(), this, false)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ public void initialize() {
lstControls.add(Pair.of(view.getCbCompactMainMenu(), FPref.UI_COMPACT_MAIN_MENU));
lstControls.add(Pair.of(view.getCbUseSentry(), FPref.USE_SENTRY));
lstControls.add(Pair.of(view.getCbCheckSnapshot(), FPref.CHECK_SNAPSHOT_AT_STARTUP));
lstControls.add(Pair.of(view.getCbPromptFreeBlocks(), FPref.MATCHPREF_PROMPT_FREE_BLOCKS));
lstControls.add(Pair.of(view.getCbPauseWhileMinimized(), FPref.UI_PAUSE_WHILE_MINIMIZED));
lstControls.add(Pair.of(view.getCbWorkshopSyntax(), FPref.DEV_WORKSHOP_SYNTAX));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final JCheckBox cbCompactMainMenu = new OptionsCheckBox(localizer.getMessage("cbCompactMainMenu"));
private final JCheckBox cbDetailedPaymentDesc = new OptionsCheckBox(localizer.getMessage("cbDetailedPaymentDesc"));
private final JCheckBox cbGrayText = new OptionsCheckBox(localizer.getMessage("cbGrayText"));
private final JCheckBox cbPromptFreeBlocks = new OptionsCheckBox(localizer.getMessage("cbPromptFreeBlocks"));
private final JCheckBox cbPauseWhileMinimized = new OptionsCheckBox(localizer.getMessage("cbPauseWhileMinimized"));
private final JCheckBox cbCompactPrompt = new OptionsCheckBox(localizer.getMessage("cbCompactPrompt"));
private final JCheckBox cbEscapeEndsTurn = new OptionsCheckBox(localizer.getMessage("cbEscapeEndsTurn"));
Expand Down Expand Up @@ -255,9 +254,6 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbCloneImgSource, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlCloneImgSource")), descriptionConstraints);

pnlPrefs.add(cbPromptFreeBlocks, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlPromptFreeBlocks")), descriptionConstraints);

pnlPrefs.add(cbPauseWhileMinimized, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlPauseWhileMinimized")), descriptionConstraints);

Expand Down Expand Up @@ -884,11 +880,6 @@ public JCheckBox getCbCloneImgSource() {
return cbCloneImgSource;
}

/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbPromptFreeBlocks() {
return cbPromptFreeBlocks;
}

public JCheckBox getCbPauseWhileMinimized() {
return cbPauseWhileMinimized;
}
Expand Down
39 changes: 39 additions & 0 deletions forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -28,9 +29,11 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
Expand Down Expand Up @@ -68,6 +71,7 @@
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.Player;
import forge.game.player.PlayerController.FullControlFlag;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
Expand Down Expand Up @@ -1519,4 +1523,39 @@ private void createLandPopupPanel(Card land) {
FOptionPane.showOptionDialog(null, title, null, mainPanel, options);
}
}

public void showFullControl(PlayerView pv, MouseEvent e) {
if (pv.isAI()) {
return;
}
Set<FullControlFlag> controlFlags = getGameView().getGame().getPlayer(pv).getController().getFullControl();
final String lblFullControl = Localizer.getInstance().getMessage("lblFullControl");
final JPopupMenu menu = new JPopupMenu(lblFullControl);
GuiUtils.addMenuItem(menu, lblFullControl, null, () -> {
FOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblFullControlDetails"), lblFullControl);
});

addFullControlEntry(menu, "lblChooseCostOrder", FullControlFlag.ChooseCostOrder, controlFlags);
addFullControlEntry(menu, "lblChooseCostReductionOrder", FullControlFlag.ChooseCostReductionOrderAndVariableAmount, controlFlags);
addFullControlEntry(menu, "lblNoPaymentFromManaAbility", FullControlFlag.NoPaymentFromManaAbility, controlFlags);
addFullControlEntry(menu, "lblNoFreeCombatCostHandling", FullControlFlag.NoFreeCombatCostHandling, controlFlags);
addFullControlEntry(menu, "lblAllowPaymentStartWithMissingResources", FullControlFlag.AllowPaymentStartWithMissingResources, controlFlags);

menu.show(view.getControl().getFieldViewFor(pv).getAvatarArea(), e.getX(), e.getY());
}

private void addFullControlEntry(JPopupMenu menu, String label, FullControlFlag flag, Set<FullControlFlag> controlFlags) {
JCheckBoxMenuItem item = new JCheckBoxMenuItem(Localizer.getInstance().getMessage(label));
if (controlFlags.contains(flag)) {
item.setSelected(true);
}
item.addActionListener(arg0 -> {
if (controlFlags.contains(flag)) {
controlFlags.remove(flag);
} else {
controlFlags.add(flag);
}
});
menu.add(item);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.awt.event.MouseListener;
import java.util.function.Function;

import javax.swing.SwingUtilities;

import forge.game.player.PlayerView;
import forge.game.zone.ZoneType;
import forge.gamemodes.match.input.Input;
Expand All @@ -46,7 +48,11 @@ public class CField implements ICDoc {
private final MouseListener madAvatar = new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
matchUI.getGameController().selectPlayer(player, new MouseTriggerEvent(e));
if (SwingUtilities.isRightMouseButton(e)) {
matchUI.showFullControl(player, e);
} else {
matchUI.getGameController().selectPlayer(player, new MouseTriggerEvent(e));
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -786,4 +786,9 @@ public List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spell
// TODO Auto-generated method stub
return null;
}

@Override
public List<CostPart> orderCosts(List<CostPart> costs) {
return costs;
}
}
40 changes: 40 additions & 0 deletions forge-gui-mobile/src/forge/screens/match/MatchController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import forge.adventure.scene.DuelScene;
import forge.adventure.util.Config;
import forge.ai.GameState;
import forge.deck.Deck;
import forge.game.player.Player;
import forge.game.player.PlayerController.FullControlFlag;
import forge.item.IPaperCard;
import forge.util.collect.FCollection;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -48,6 +50,9 @@
import forge.localinstance.properties.ForgePreferences;
import forge.localinstance.properties.ForgePreferences.FPref;
import forge.localinstance.skin.FSkinProp;
import forge.menu.FCheckBoxMenuItem;
import forge.menu.FMenuItem;
import forge.menu.FPopupMenu;
import forge.model.FModel;
import forge.player.PlayerZoneUpdate;
import forge.player.PlayerZoneUpdates;
Expand All @@ -63,6 +68,7 @@
import forge.toolbox.FOptionPane;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
import forge.util.Localizer;
import forge.util.MessageUtil;
import forge.util.WaitCallback;
import forge.util.collect.FCollectionView;
Expand Down Expand Up @@ -737,4 +743,38 @@ public static HostedMatch hostMatch() {
public static HostedMatch getHostedMatch() {
return hostedMatch;
}

public void showFullControl(PlayerView selected, float x, float y) {
if (selected.isAI()) {
return;
}
Set<FullControlFlag> controlFlags = getGameView().getGame().getPlayer(selected).getController().getFullControl();
FPopupMenu menu = new FPopupMenu() {
@Override
protected void buildMenu() {
addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblFullControl"),
e -> {
FOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblFullControlDetails"), "Full Control details");
}));
addItem(getFullControlMenuEntry("lblChooseCostOrder", FullControlFlag.ChooseCostOrder, controlFlags));
addItem(getFullControlMenuEntry("lblChooseCostReductionOrder", FullControlFlag.ChooseCostReductionOrderAndVariableAmount, controlFlags));
addItem(getFullControlMenuEntry("lblNoPaymentFromManaAbility", FullControlFlag.NoPaymentFromManaAbility, controlFlags));
addItem(getFullControlMenuEntry("lblNoFreeCombatCostHandling", FullControlFlag.NoFreeCombatCostHandling, controlFlags));
addItem(getFullControlMenuEntry("lblAllowPaymentStartWithMissingResources", FullControlFlag.AllowPaymentStartWithMissingResources, controlFlags));
}
};

menu.show(getView(), getView().getPlayerPanel(selected).localToScreenX(x), getView().getPlayerPanel(selected).localToScreenY(y));
}

private FCheckBoxMenuItem getFullControlMenuEntry(String label, FullControlFlag flag, Set<FullControlFlag> controlFlags) {
return new FCheckBoxMenuItem(Forge.getLocalizer().getMessage(label), controlFlags.contains(flag),
e -> {
if (controlFlags.contains(flag)) {
controlFlags.remove(flag);
} else {
controlFlags.add(flag);
}
});
}
}
Loading
Loading