diff --git a/src/main/java/kst4contest/logic/PriorityCalculator.java b/src/main/java/kst4contest/logic/PriorityCalculator.java index 8fc8436..0a3d341 100644 --- a/src/main/java/kst4contest/logic/PriorityCalculator.java +++ b/src/main/java/kst4contest/logic/PriorityCalculator.java @@ -83,12 +83,13 @@ public class PriorityCalculator { // -------------------------------------------------------------------- double score = 100.0; -// if (!member.isWorked()) { -// score += 200.0; -// } - - //"worked" for scoring is derived ONLY from per-band flags (worked144/432/...) -// EnumSet workedBandsForScoring = getWorkedBands(member); + /* + * A manual Sked-fail mark represents an explicit operator decision. The + * penalty is therefore applied after all positive contributions, including + * an imminent sked. Applying it earlier would allow the later sked boost to + * almost completely cancel the failure mark. + */ + boolean manualSkedFailed = false; if (workedBandsForScoring.isEmpty()) { score += 200.0; // never worked on any supported band -> higher priority @@ -190,9 +191,11 @@ public class PriorityCalculator { } // Manual sked fail (path likely bad) => strong, permanent penalty until reset - if (mx.manualSkedFailed) { - score *= 0.15; - } + /* + * Remember the explicit failure state here. The actual penalty is applied + * after the sked contribution has been calculated. + */ + manualSkedFailed = mx.manualSkedFailed; } } @@ -226,10 +229,25 @@ public class PriorityCalculator { // -------------------------------------------------------------------- // 8) Legacy penalty: failed attempts in ChatMember // -------------------------------------------------------------------- + // -------------------------------------------------------------------- +// 8) Legacy penalty: failed attempts in ChatMember +// -------------------------------------------------------------------- if (member.getFailedQSOAttempts() > 0) { score = score / (member.getFailedQSOAttempts() + 1); } +// -------------------------------------------------------------------- +// 9) Explicit operator override: manual Sked fail +// -------------------------------------------------------------------- + if (manualSkedFailed) { + /* + * Apply the penalty to the complete result. This includes an imminent + * sked and prevents its strong time-dependent boost from cancelling the + * operator's explicit failure assessment. + */ + score *= 0.15; + } + return Math.max(0.0, score); } diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index f2949e4..d619f56 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -4388,10 +4388,10 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL /** * Resolves and selects a priority candidate. * - * If the station is currently visible in the member table, selecting it there is - * preferred because the existing table-selection listener then keeps the rest of - * the UI in sync. If the station is filtered out, we still show FurtherInfo and - * prepare the /cq text so the priority list remains useful with active filters. + *

The common selection helper is used deliberately. It updates Further Info, + * prepares the directed message and selects the corresponding table row when + * the row is currently visible. A candidate hidden by an active table filter + * remains usable without changing or resetting that filter.

*/ private void selectTopCandidate( kst4contest.controller.ScoreService.TopCandidate candidate, @@ -4408,25 +4408,17 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL return; } -// if (tbl_chatMember.getItems().contains(resolved)) { -// tbl_chatMember.getSelectionModel().select(resolved); -// tbl_chatMember.scrollTo(resolved); -// } else { -// selectedCallSignInfoStageChatMember = resolved; -// chatcontroller.getScoreService().setSelectedChatMember(selectedCallSignInfoStageChatMember); -// -// selectedCallSignFurtherInfoPane.getChildren().setAll(generateFurtherInfoAbtSelectedCallsignBP(resolved)); -// txt_chatMessageUserInput.clear(); -// txt_chatMessageUserInput.setText("/cq " + resolved.getCallSign() + " "); -// txt_chatMessageUserInput.requestFocus(); -// txt_chatMessageUserInput.selectEnd(); -// } - - tbl_chatMember.getSelectionModel().select(resolved); //new mechanic for selectionchange events due to textfield - //manipulation... - - // Keep ScoreService selection in sync even if the visible table selection path was used. - chatcontroller.getScoreService().setSelectedChatMember(resolved); + /* + * This is an explicit operator action and therefore follows the same path + * as a timeline click. The exact callsign and category stored in the + * TopCandidate remain authoritative. + * + * focusChatMemberAndPrepareCq() also handles candidates which are currently + * hidden by a FilteredList. In that case the table row cannot be selected, + * but Further Info, ScoreService selection and the prepared /cq message are + * still updated. + */ + focusChatMemberAndPrepareCq(resolved); } /**