Skip to content
Open
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
4 changes: 3 additions & 1 deletion forge-ai/src/main/java/forge/ai/ComputerUtilCost.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,9 @@ public static boolean checkSacrificeCost(final Player ai, final Cost cost, final
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostSacrifice sac) {
if (sac.payCostFromSource()) {
if (!important) {
// Allow self-sacrifice for tokens (they're designed to be sacrificed for effect)
// even if not in immediate danger
if (!important && !source.isToken()) {
return false;
}
if (!CardLists.filterControlledBy(source.getEnchantedBy(), source.getController()).isEmpty()) {
Expand Down
2 changes: 1 addition & 1 deletion forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ protected AiAbilityDecision checkApiLogic(Player ai, final SpellAbility sa) {
// don't put the counter on the dead creature
if (sacSelf && c.equals(source)) {
return false;
} else if (hasSacCost && !ComputerUtil.shouldSacrificeThreatenedCard(ai, c, sa)) {
} else if (hasSacCost && !sacSelf && !ComputerUtil.shouldSacrificeThreatenedCard(ai, c, sa)) {
return false;
}
if ("NoCounterOfType".equals(sa.getParam("AILogic"))) {
Expand Down
15 changes: 10 additions & 5 deletions forge-gui-desktop/src/main/java/forge/GuiDesktop.java
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,16 @@ public void preventSystemSleep(boolean preventSleep) {
}

private static float initializeScreenScale() {
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
AffineTransform at = gc.getDefaultTransform();
double scaleX = at.getScaleX();
double scaleY = at.getScaleY();
return (float) Math.min(scaleX, scaleY);
try {
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
AffineTransform at = gc.getDefaultTransform();
double scaleX = at.getScaleX();
double scaleY = at.getScaleY();
return (float) Math.min(scaleX, scaleY);
} catch (java.awt.HeadlessException e) {
// Running in headless mode (e.g., in tests or CI). Use default scale.
return 1.0f;
}
}
static float screenScale = initializeScreenScale();

Expand Down
14 changes: 11 additions & 3 deletions forge-gui-desktop/src/test/java/forge/ai/AITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import com.google.common.collect.Lists;

import forge.GuiDesktop;
import forge.StaticData;
import forge.deck.Deck;
import forge.game.Game;
Expand All @@ -26,6 +25,7 @@
import forge.item.PaperToken;
import forge.localinstance.properties.ForgePreferences.FPref;
import forge.model.FModel;
import forge.net.HeadlessGuiDesktop;

import org.testng.annotations.BeforeMethod;

Expand Down Expand Up @@ -65,8 +65,16 @@ protected Game initAndCreateGame() {
return game;
}

protected Game initAndCreateThreePlayerGame() {
initAndCreateGame();
protected Game initAndCreateGame() {
if (!initialized) {
GuiBase.setInterface(new HeadlessGuiDesktop());
FModel.initialize(null, preferences -> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's going on here...?

preferences.setPref(FPref.LOAD_CARD_SCRIPTS_LAZILY, false);
preferences.setPref(FPref.UI_LANGUAGE, "en-US");
return null;
});
initialized = true;
}

List<RegisteredPlayer> players = Lists.newArrayList();
Deck d1 = new Deck();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package forge.ai.ability;

import forge.ai.AITest;
import forge.ai.AiPlayDecision;
import forge.ai.SpellAbilityAi;
import forge.ai.SpellApiToAi;
import forge.game.Game;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import org.testng.annotations.Test;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;

public class CountersPutAiTest extends AITest {

@Test
public void testSelfSacrificePutCounterAbilityIsPlayable() {
Game game = initAndCreateGame();
Player ai = game.getPlayers().get(1);

// Mutagen token activation needs one generic mana and a valid creature target.
Card mutagenToken = addToken("c_a_mutagen_sac", ai);
addCard("Plains", ai);
addCard("Runeclaw Bear", ai);

game.getPhaseHandler().devModeSet(PhaseType.MAIN1, ai);
game.getAction().checkStateEffects(true);

// Find the PutCounter ability (with the activation cost)
SpellAbility putCounterSa = null;
for (SpellAbility sa : mutagenToken.getSpellAbilities()) {
if (sa.getDescription().contains("Sacrifice this token: Put a +1/+1")) {
putCounterSa = sa;
break;
}
}
assertNotNull("Could not find PutCounter ability on mutagen token", putCounterSa);

SpellAbilityAi aiLogic = SpellApiToAi.Converter.get(ApiType.PutCounter);
AiPlayDecision decision = aiLogic.canPlayWithSubs(ai, putCounterSa).decision();

assertEquals("AI should activate self-sacrifice mutagen PutCounter ability",
AiPlayDecision.WillPlay, decision);
}
}
20 changes: 18 additions & 2 deletions forge-gui/src/main/java/forge/error/ExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,15 @@ public static void unregisterErrorHandling() throws IOException {
/** {@inheritDoc} */
@Override
public final void uncaughtException(final Thread t, final Throwable ex) {
BugReporter.reportException(ex);
try {
BugReporter.reportException(ex);
} catch (Throwable handlerFailure) {
System.err.println("Fatal error while reporting uncaught exception.");
System.err.println("Original uncaught exception:");
ex.printStackTrace();
System.err.println("Exception while handling uncaught exception:");
handlerFailure.printStackTrace();
}
}

/**
Expand All @@ -229,6 +237,14 @@ public final void uncaughtException(final Thread t, final Throwable ex) {
* a {@link java.lang.Throwable} object.
*/
public final void handle(final Throwable ex) {
BugReporter.reportException(ex);
try {
BugReporter.reportException(ex);
} catch (Throwable handlerFailure) {
System.err.println("Fatal error while reporting AWT exception.");
System.err.println("Original AWT exception:");
ex.printStackTrace();
System.err.println("Exception while handling AWT exception:");
handlerFailure.printStackTrace();
}
}
}