Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static void declinePDS(ButtonInteractionEvent event, Game game, Player pl
}
MessageHelper.sendMessageToChannel(
game.isFowMode() ? player.getCorrectChannel() : event.getMessageChannel(), msg);
ButtonHelper.decrementPendingPdsDecisions(game);
Comment on lines 69 to +71
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

declinePDS now decrements the global pending-PDS counter, but this handler doesn’t prevent (a) non-eligible players from clicking Decline in the shared combat thread, or (b) the same player clicking Decline multiple times. Either case can incorrectly reduce the counter to 0 and remove the warning while other PDS-capable players still haven’t responded. Consider validating the clicker is in the eligible PDS-cover list for the relevant system and recording per-faction “responded” state so the decrement is only applied once per eligible player.

Copilot uses AI. Check for mistakes.
}

@ButtonHandler("applytempcombatmod__" + Constants.AC + "__")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ public static void spaceLandedUnits(ButtonInteractionEvent event, Game game, Pla

@ButtonHandler("landUnits_")
public static void landingUnits(String buttonID, ButtonInteractionEvent event, Game game, Player player) {
if (ButtonHelper.warnIfPendingPdsDecisions(player, game, event)) {
return;
}
String regex = "landUnits_" + RegexHelper.posRegex(game) + "_" + RegexHelper.intRegex("num")
+ RegexHelper.unitTypeRegex() + "(?<dmg>(damaged)?)_" + RegexHelper.unitHolderRegex(game, "uh") + "_"
+ RegexHelper.colorRegex(game);
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/ti4/helpers/ButtonHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -5967,6 +5967,13 @@ private static void resolveCombatRoll(
case "spacecannondefence" -> rollType = CombatRollType.SpaceCannonDefence;
default -> {}
}
if ((rollType == CombatRollType.combatround || rollType == CombatRollType.AFB)
&& warnIfPendingPdsDecisions(player, game, event)) {
return;
}
if (rollType == CombatRollType.SpaceCannonOffence) {
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

decrementPendingPdsDecisions(game) is triggered for any SpaceCannonOffence roll, which can happen outside this “pending PDS decisions” window (e.g. EBS / other effects) and can also be invoked multiple times per player via repeated button presses or internal recursion. That can prematurely drive the counter to 0 and suppress the intended warning even though some PDS players never decided. Consider tying the pending state to the specific tactical-action/system (e.g. include active system + eligible factions) and making decrement idempotent per faction/decision rather than a raw global counter.

Suggested change
if (rollType == CombatRollType.SpaceCannonOffence) {
if (first && rollType == CombatRollType.SpaceCannonOffence) {

Copilot uses AI. Check for mistakes.
decrementPendingPdsDecisions(game);
}
if (buttonID.contains("deleteTheseButtons") && event instanceof ButtonInteractionEvent bevent) {
deleteAllButtons(bevent);
} else {
Expand Down Expand Up @@ -6026,6 +6033,44 @@ public static String putInfWithMechsForStarforge(
return successMessage;
}

public static boolean warnIfPendingPdsDecisions(Player player, Game game, GenericInteractionCreateEvent event) {
int pendingPdsDecisions = getPendingPdsDecisions(game);
if (pendingPdsDecisions < 1) {
return false;
}
MessageHelper.sendMessageToChannel(
event.getMessageChannel(),
player.getRepresentation() + " some players have not finished deciding to fire PDS."
+ " If you still want to continue, press the button again.");
game.removeStoredValue("warnAboutPdsBeforeContinuing");
return true;
}

public static void setPendingPdsDecisions(Game game, int count) {
if (count > 0) {
game.setStoredValue("warnAboutPdsBeforeContinuing", Integer.toString(count));
} else {
game.removeStoredValue("warnAboutPdsBeforeContinuing");
}
}

public static void decrementPendingPdsDecisions(Game game) {
setPendingPdsDecisions(game, getPendingPdsDecisions(game) - 1);
}

private static int getPendingPdsDecisions(Game game) {
String value = game.getStoredValue("warnAboutPdsBeforeContinuing");
if (value.isBlank()) {
return 0;
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
game.removeStoredValue("warnAboutPdsBeforeContinuing");
return 0;
}
}

public static void resolveTransitDiodesStep1(Game game, Player player) {
List<Button> buttons = new ArrayList<>();
for (String planet : player.getPlanetsAllianceMode()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ public void finishMovement(ButtonInteractionEvent event, Game game, Player playe
CommanderUnlockCheckService.checkPlayer(player, "obsidian");
}
if (!game.isL1Hero() && !ctx.playersWithPds2.isEmpty()) {
ButtonHelper.setPendingPdsDecisions(game, ctx.playersWithPds2.size());
ButtonHelperTacticalAction.tacticalActionSpaceCannonOffenceStep(
game, player, ctx.playersWithPds2, ctx.tile);
Comment on lines 175 to 178
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

setPendingPdsDecisions is only called when playersWithPds2 is non-empty, so the stored pending-PDS state can leak from a previous activation/tactical action (e.g., next activation has no PDS coverage, or the tactical action ends without any PDS player clicking fire/decline) and incorrectly warn/block later land/combat rolls. Consider always clearing/resetting this state (set to 0) when there are no PDS players, and also clearing it as part of tactical-action cleanup (e.g., resetStoredValuesForTacticalAction / end-of-tactical-action).

Suggested change
if (!game.isL1Hero() && !ctx.playersWithPds2.isEmpty()) {
ButtonHelper.setPendingPdsDecisions(game, ctx.playersWithPds2.size());
ButtonHelperTacticalAction.tacticalActionSpaceCannonOffenceStep(
game, player, ctx.playersWithPds2, ctx.tile);
if (!game.isL1Hero()) {
int pendingPdsCount = ctx.playersWithPds2.size();
ButtonHelper.setPendingPdsDecisions(game, pendingPdsCount);
if (pendingPdsCount > 0) {
ButtonHelperTacticalAction.tacticalActionSpaceCannonOffenceStep(
game, player, ctx.playersWithPds2, ctx.tile);
}

Copilot uses AI. Check for mistakes.
}
Expand Down
Loading