Compare commits

...

5 commits

Author SHA1 Message Date
Maxime Réaux
2901dcd68c show player stats in table 2026-03-28 11:47:46 +01:00
Maxime Réaux
261c64942d clarify war settings 2026-03-26 15:42:40 +01:00
Maxime Réaux
69942a3cff allow closing round with incomplete battles 2026-03-26 11:30:00 +01:00
Maxime Réaux
ae6c033bbe fix useless fallback at end of pairing 2026-03-25 12:17:45 +01:00
Maxime Réaux
a3144dc3c9 display tiebreak place 2026-03-24 16:26:52 +01:00
21 changed files with 644 additions and 160 deletions

View file

@ -26,6 +26,7 @@ class AppController:
self.current_file: Path | None = None self.current_file: Path | None = None
self.view.on_close_callback = self.on_app_close self.view.on_close_callback = self.on_app_close
self.is_dirty: bool = False self.is_dirty: bool = False
self.players_stats_dirty = True
self.__connect() self.__connect()
self.navigation.refresh_players_view() self.navigation.refresh_players_view()
self.navigation.refresh_wars_view() self.navigation.refresh_wars_view()
@ -60,6 +61,12 @@ class AppController:
self.view.on_add_item = self.add_item self.view.on_add_item = self.add_item
self.view.on_edit_item = self.edit_item self.view.on_edit_item = self.edit_item
self.view.on_delete_item = self.delete_item self.view.on_delete_item = self.delete_item
self.view.tabWidget.currentChanged.connect(self.navigation.on_tab_changed)
def mark_model_dirty(self, *, players_stats: bool = False) -> None:
self.is_dirty = True
if players_stats:
self.players_stats_dirty = True
def on_app_close(self) -> bool: def on_app_close(self) -> bool:
if self.is_dirty: if self.is_dirty:
@ -235,10 +242,11 @@ class AppController:
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
e.action() if e.action:
e.action()
else: else:
return return
self.is_dirty = True self.mark_model_dirty(players_stats=True) # participation may affect stats
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def edit_item(self, item_type: str, item_id: str) -> None: def edit_item(self, item_type: str, item_id: str) -> None:
@ -290,7 +298,8 @@ class AppController:
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
try: try:
e.action() if e.action:
e.action()
except DomainError as inner: except DomainError as inner:
QMessageBox.warning( QMessageBox.warning(
self.view, self.view,
@ -300,7 +309,7 @@ class AppController:
return return
else: else:
return return
self.is_dirty = True self.mark_model_dirty(players_stats=(item_type == ItemType.PLAYER))
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def delete_item(self, item_type: str, item_id: str) -> None: def delete_item(self, item_type: str, item_id: str) -> None:
@ -361,7 +370,8 @@ class AppController:
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
try: try:
e.action() if e.action:
e.action()
except DomainError as inner: except DomainError as inner:
QMessageBox.warning( QMessageBox.warning(
self.view, self.view,
@ -371,5 +381,5 @@ class AppController:
return return
else: else:
return return
self.is_dirty = True self.mark_model_dirty(players_stats=True) # participation may affect stats
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)

View file

@ -160,7 +160,7 @@ class CampaignController:
str(e), str(e),
) )
return return
self.app.is_dirty = True self.app.mark_model_dirty(players_stats=True)
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.app.navigation.refresh_and_select( self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=campaign_id RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=campaign_id

View file

@ -8,6 +8,12 @@ from PyQt6.QtGui import QIcon
class ParticipantOption: class ParticipantOption:
id: str id: str
name: str name: str
wars_played: int | None = None
wars_won: int | None = None
campaigns_played: int | None = None
campaigns_won: int | None = None
battles_played: int | None = None
battles_won: int | None = None
@dataclass(frozen=True, slots=True) @dataclass(frozen=True, slots=True)
@ -135,11 +141,6 @@ class WarParticipantScoreDTO:
objective_icons: Dict[str, QIcon] = field(default_factory=dict) objective_icons: Dict[str, QIcon] = field(default_factory=dict)
@dataclass
class TieDialogData:
title: str
@dataclass @dataclass
class WarSettingsDTO: class WarSettingsDTO:
major_value: int major_value: int

View file

@ -6,11 +6,11 @@ if TYPE_CHECKING:
from warchron.controller.app_controller import AppController from warchron.controller.app_controller import AppController
from warchron.controller.dtos import ( from warchron.controller.dtos import (
TreeSelection, TreeSelection,
ParticipantOption,
WarDTO, WarDTO,
CampaignDTO, CampaignDTO,
RoundDTO, RoundDTO,
) )
from warchron.controller.dtos import ParticipantOption
class NavigationController: class NavigationController:
@ -23,13 +23,6 @@ class NavigationController:
# Display methods # Display methods
def refresh_players_view(self) -> None:
players = self.app.model.get_all_players()
players_for_display: List[ParticipantOption] = [
ParticipantOption(id=p.id, name=p.name) for p in players
]
self.app.view.display_players(players_for_display)
def refresh_wars_view(self) -> None: def refresh_wars_view(self) -> None:
wars = self.app.model.get_all_wars() wars = self.app.model.get_all_wars()
wars_dto: List[WarDTO] = [ wars_dto: List[WarDTO] = [
@ -74,6 +67,31 @@ class NavigationController:
self.app.wars._fill_war_details(first_war.id) self.app.wars._fill_war_details(first_war.id)
self.update_actions_state() self.update_actions_state()
def on_tab_changed(self, index: int) -> None:
tab = self.app.view.get_current_tab()
if tab == "players" and self.app.players_stats_dirty:
self.refresh_players_view()
def refresh_players_view(self) -> None:
players = self.app.model.get_all_players()
players_for_display: List[ParticipantOption] = []
for p in players:
stats = self.app.model.get_player_stats(p.id)
players_for_display.append(
ParticipantOption(
id=p.id,
name=p.name,
wars_played=stats.wars_played,
wars_won=stats.wars_won,
campaigns_played=stats.campaigns_played,
campaigns_won=stats.campaigns_won,
battles_played=stats.battles_played,
battles_won=stats.battles_won,
)
)
self.app.players_stats_dirty = False
self.app.view.display_players(players_for_display)
def refresh(self, scope: RefreshScope) -> None: def refresh(self, scope: RefreshScope) -> None:
match scope: match scope:
case RefreshScope.PLAYERS_LIST: case RefreshScope.PLAYERS_LIST:

View file

@ -1,4 +1,5 @@
from typing import Dict from typing import Dict
from dataclasses import dataclass
from PyQt6.QtGui import QIcon from PyQt6.QtGui import QIcon
@ -11,16 +12,20 @@ from warchron.constants import (
ScoreKind, ScoreKind,
) )
from warchron.controller.dtos import TieDialogData
from warchron.model.tiebreaking import TieContext, TieBreaker from warchron.model.tiebreaking import TieContext, TieBreaker
from warchron.model.war import War from warchron.model.war import War
from warchron.model.campaign import Campaign from warchron.model.campaign import Campaign
from warchron.model.round import Round from warchron.model.round import Round
from warchron.model.scoring import ParticipantScore from warchron.model.scoring import ParticipantScore, ScoreComputer
from warchron.model.checking import ResultChecker from warchron.model.checking import ResultChecker
from warchron.model.exception import DomainError from warchron.model.exception import DomainError
@dataclass
class TieDialogData:
title: str
class Presenter: class Presenter:
@staticmethod @staticmethod
@ -30,17 +35,16 @@ class Presenter:
campaign: Campaign | None = None, campaign: Campaign | None = None,
round: Round | None = None, round: Round | None = None,
) -> TieDialogData: ) -> TieDialogData:
# TODO display Nth place if ctx.context_type in (ContextType.WAR, ContextType.CAMPAIGN):
if ctx.context_type == ContextType.WAR: rank = Presenter._get_tie_rank(war, ctx)
if ctx.objective_id: rank_label = Presenter._build_rank_label(rank)
level = str(ctx.context_type).capitalize()
if ctx.objective_id is not None:
obj = war.objectives[ctx.objective_id] obj = war.objectives[ctx.objective_id]
return TieDialogData(f"War objective tie — {obj.name}") return TieDialogData(
return TieDialogData("War tie") f"{level} objective tie for {rank_label}{obj.name}"
if ctx.context_type == ContextType.CAMPAIGN: )
if ctx.objective_id: return TieDialogData(f"{level} tie for {rank_label}")
obj = war.objectives[ctx.objective_id]
return TieDialogData(f"Campaign objective tie — {obj.name}")
return TieDialogData("Campaign tie")
if ctx.context_type == ContextType.BATTLE: if ctx.context_type == ContextType.BATTLE:
if campaign: if campaign:
sector = campaign.sectors[ctx.context_id] sector = campaign.sectors[ctx.context_id]
@ -177,3 +181,57 @@ class Presenter:
compute_icon(battle.player_1_id), compute_icon(battle.player_1_id),
compute_icon(battle.player_2_id), compute_icon(battle.player_2_id),
) )
@staticmethod
def _ordinal(n: int) -> str:
if 10 <= n % 100 <= 20:
suffix = "th"
else:
suffix = {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
return f"{n}{suffix}"
@staticmethod
def _build_rank_label(rank: int | None) -> str:
if rank is None:
return ""
return f"{Presenter._ordinal(rank)} place"
@staticmethod
def _get_tie_rank(
war: War,
ctx: TieContext,
) -> int | None:
from warchron.model.checking import ResultChecker
scores = ScoreComputer.compute_scores(
war,
ctx.context_type,
ctx.context_id,
)
if ctx.objective_id is None:
def value_getter(score: ParticipantScore) -> int:
return score.victory_points
score_kind = ScoreKind.VP
else:
obj_id = ctx.objective_id
def value_getter(score: ParticipantScore) -> int:
return score.narrative_points.get(obj_id, 0)
score_kind = ScoreKind.NP
ranking = ResultChecker.get_effective_ranking(
war,
ctx.context_type,
ctx.context_id,
score_kind,
scores,
value_getter,
ctx.objective_id,
)
tied = set(ctx.participants)
for rank, group, _ in ranking:
if tied.intersection(group):
return rank
return None

View file

@ -186,16 +186,38 @@ class RoundController:
camp = self.app.model.get_campaign_by_round(round_id) camp = self.app.model.get_campaign_by_round(round_id)
war = self.app.model.get_war_by_round(round_id) war = self.app.model.get_war_by_round(round_id)
workflow = RoundClosureWorkflow(self.app) workflow = RoundClosureWorkflow(self.app)
try: confirmed = False
workflow.start(war, camp, rnd) stop = False
except DomainError as e: while True:
QMessageBox.warning( try:
self.app.view, workflow.start(war, camp, rnd, confirmed)
"Closure forbidden", break
str(e), except RequiresConfirmation as e:
) reply = QMessageBox.question(
self.app.view,
"Confirm closing",
str(e),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
)
if reply == QMessageBox.StandardButton.Yes:
if e.action:
e.action()
confirmed = True
continue
else:
stop = True
break
except DomainError as e:
QMessageBox.warning(
self.app.view,
"Closure forbidden",
str(e),
)
stop = True
break
if stop:
return return
self.app.is_dirty = True self.app.mark_model_dirty(players_stats=True)
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.app.navigation.refresh_and_select( self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=round_id RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=round_id
@ -234,10 +256,11 @@ class RoundController:
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
e.action() if e.action:
e.action()
else: else:
return return
self.app.is_dirty = True self.app.mark_model_dirty()
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.app.navigation.refresh_and_select( self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=round_id RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=round_id

View file

@ -147,7 +147,7 @@ class WarController:
str(e), str(e),
) )
return return
self.app.is_dirty = True self.app.mark_model_dirty(players_stats=True)
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.app.navigation.refresh_and_select( self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id
@ -221,10 +221,11 @@ class WarController:
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
e.action() if e.action:
e.action()
else: else:
return return
self.is_dirty = True self.app.mark_model_dirty()
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.app.navigation.refresh_and_select( self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id

View file

@ -18,8 +18,10 @@ class Workflow:
class RoundClosureWorkflow(Workflow): class RoundClosureWorkflow(Workflow):
def start(self, war: War, campaign: Campaign, round: Round) -> None: def start(
Closer.check_round_closable(round) self, war: War, campaign: Campaign, round: Round, confirmed: bool = False
) -> None:
Closer.check_round_closable(round, confirmed)
ties = TieBreaker.find_battle_ties(war, round.id) ties = TieBreaker.find_battle_ties(war, round.id)
while ties: while ties:
for tie in ties: for tie in ties:

View file

@ -73,6 +73,14 @@ class Battle:
return return
raise DomainError("Battle has no available places") raise DomainError("Battle has no available places")
def get_participants_ids(self) -> List[str]:
players: List[str] = []
if self.player_1_id:
players.append(self.player_1_id)
if self.player_2_id:
players.append(self.player_2_id)
return players
def clear_battle_players(self) -> None: def clear_battle_players(self) -> None:
self.player_1_id = None self.player_1_id = None
self.player_2_id = None self.player_2_id = None
@ -80,6 +88,12 @@ class Battle:
def is_finished(self) -> bool: def is_finished(self) -> bool:
return self.winner_id is not None or self.is_draw() return self.winner_id is not None or self.is_draw()
def has_player(self) -> bool:
return self.player_1_id is not None or self.player_2_id is not None
def is_complete(self) -> bool:
return self.player_1_id is not None and self.player_2_id is not None
def toDict(self) -> Dict[str, Any]: def toDict(self) -> Dict[str, Any]:
return { return {
"sector_id": self.sector_id, "sector_id": self.sector_id,

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from warchron.constants import ContextType from warchron.constants import ContextType
from warchron.model.exception import ForbiddenOperation from warchron.model.exception import ForbiddenOperation, RequiresConfirmation
from warchron.model.war_event import InfluenceGained from warchron.model.war_event import InfluenceGained
from warchron.model.war import War from warchron.model.war import War
from warchron.model.campaign import Campaign from warchron.model.campaign import Campaign
@ -14,13 +14,19 @@ class Closer:
# Round methods # Round methods
@staticmethod @staticmethod
def check_round_closable(round: Round) -> None: def check_round_closable(round: Round, confirmed: bool) -> None:
if round.is_over: if round.is_over:
raise ForbiddenOperation("Round already closed") raise ForbiddenOperation("Round already closed")
if not round.all_battles_finished(): if not confirmed:
raise ForbiddenOperation( if any(not bat.is_complete() for bat in round.battles.values()):
"All battles must be finished to close their round" raise RequiresConfirmation(
) "Battle(s) in this round miss player(s).\n"
"Do you want to continue?",
)
if not round.all_battles_finished():
raise ForbiddenOperation(
"All battles must be finished to close their round"
)
@staticmethod @staticmethod
def apply_battle_outcomes(war: War, campaign: Campaign, battle: Battle) -> None: def apply_battle_outcomes(war: War, campaign: Campaign, battle: Battle) -> None:

View file

@ -26,6 +26,6 @@ class DomainDecision(Exception):
class RequiresConfirmation(DomainDecision): class RequiresConfirmation(DomainDecision):
def __init__(self, message: str, action: Callable[[], None]): def __init__(self, message: str, action: Callable[[], None] | None = None):
super().__init__(message) super().__init__(message)
self.action = action self.action = action

View file

@ -15,6 +15,7 @@ from warchron.model.sector import Sector
from warchron.model.round import Round from warchron.model.round import Round
from warchron.model.choice import Choice from warchron.model.choice import Choice
from warchron.model.battle import Battle from warchron.model.battle import Battle
from warchron.model.statistics import PlayerStats, StatisticsComputer
class Model: class Model:
@ -98,6 +99,9 @@ class Model:
) )
del self.players[player_id] del self.players[player_id]
def get_player_stats(self, player_id: str) -> PlayerStats:
return StatisticsComputer.compute_player_stats(self.wars, player_id)
# War methods # War methods
def get_default_war_values(self) -> Dict[str, Any]: def get_default_war_values(self) -> Dict[str, Any]:

View file

@ -63,10 +63,7 @@ class Pairing:
bat.set_winner(None) bat.set_winner(None)
war.revert_choice_ties(round.id) war.revert_choice_ties(round.id)
if any( if any(bat.has_player() for bat in round.battles.values()):
bat.player_1_id is not None or bat.player_2_id is not None
for bat in round.battles.values()
):
raise RequiresConfirmation( raise RequiresConfirmation(
"Battle(s) already have player(s) assigned for this round.\n" "Battle(s) already have player(s) assigned for this round.\n"
"Battle players will be cleared.\n" "Battle players will be cleared.\n"
@ -329,6 +326,8 @@ class Pairing:
round: Round, round: Round,
remaining: List[str], remaining: List[str],
) -> None: ) -> None:
if not remaining:
return
campaign = war.get_campaign_by_round(round.id) campaign = war.get_campaign_by_round(round.id)
if campaign is None: if campaign is None:
raise DomainError("Campaign not found") raise DomainError("Campaign not found")

View file

@ -166,7 +166,6 @@ class Round:
return any(b.is_finished() for b in self.battles.values()) return any(b.is_finished() for b in self.battles.values())
def all_battles_finished(self) -> bool: def all_battles_finished(self) -> bool:
# TODO exception for participant alone
return all( return all(
b.winner_id is not None or b.is_draw() for b in self.battles.values() b.winner_id is not None or b.is_draw() for b in self.battles.values()
) )

View file

@ -0,0 +1,101 @@
from __future__ import annotations
from typing import Dict
from dataclasses import dataclass
from warchron.constants import ContextType, ScoreKind
from warchron.model.war import War
from warchron.model.scoring import ScoreComputer
from warchron.model.checking import ResultChecker
@dataclass
class PlayerStats:
wars_played: int = 0
wars_won: int = 0
campaigns_played: int = 0
campaigns_won: int = 0
battles_played: int = 0
battles_won: int = 0
class StatisticsComputer:
@staticmethod
def compute_player_stats(
wars: Dict[str, War],
player_id: str,
) -> PlayerStats:
stats = PlayerStats()
for war in wars.values():
# --- WAR PARTICIPANT ---
war_part = next(
(wp for wp in war.participants.values() if wp.player_id == player_id),
None,
)
if not war_part:
continue
stats.wars_played += 1
if war.is_over:
scores = ScoreComputer.compute_scores(war, ContextType.WAR, war.id)
ranking = ResultChecker.get_effective_ranking(
war,
ContextType.WAR,
war.id,
ScoreKind.VP,
scores,
lambda s: s.victory_points,
)
if ranking and war_part.id in ranking[0][1]:
stats.wars_won += 1
# --- CAMPAIGNS ---
for campaign in war.campaigns:
campaign_part = next(
(
cp
for cp in campaign.participants.values()
if cp.war_participant_id == war_part.id
),
None,
)
if not campaign_part:
continue
stats.campaigns_played += 1
if campaign.is_over:
scores = ScoreComputer.compute_scores(
war, ContextType.CAMPAIGN, campaign.id
)
ranking = ResultChecker.get_effective_ranking(
war,
ContextType.CAMPAIGN,
campaign.id,
ScoreKind.VP,
scores,
lambda s: s.victory_points,
)
if ranking and war_part.id in ranking[0][1]:
stats.campaigns_won += 1
# --- BATTLES ---
for rnd in campaign.rounds:
for battle in rnd.battles.values():
if not battle.is_finished():
continue
if campaign_part.id not in (
battle.player_1_id,
battle.player_2_id,
):
continue
stats.battles_played += 1
base_winner = None
if battle.winner_id is not None:
base_winner = campaign.campaign_to_war_part_id(
battle.winner_id
)
winner = ResultChecker.get_effective_winner_id(
war,
ContextType.BATTLE,
battle.sector_id,
base_winner,
)
if winner == war_part.id:
stats.battles_won += 1
return stats

View file

@ -63,8 +63,9 @@ class TieBreaker:
for battle in round.battles.values(): for battle in round.battles.values():
if campaign is None: if campaign is None:
raise DomainError("No campaign for this battle tie") raise DomainError("No campaign for this battle tie")
if battle.player_1_id is None or battle.player_2_id is None: if not battle.is_complete():
raise DomainError("Missing player(s) in this battle context.") continue
assert battle.player_1_id is not None and battle.player_2_id is not None
p1_id = campaign.campaign_to_war_part_id(battle.player_1_id) p1_id = campaign.campaign_to_war_part_id(battle.player_1_id)
p2_id = campaign.campaign_to_war_part_id(battle.player_2_id) p2_id = campaign.campaign_to_war_part_id(battle.player_2_id)
if not battle.is_draw(): if not battle.is_draw():

View file

@ -13,7 +13,7 @@ class Ui_settingsDialog(object):
def setupUi(self, settingsDialog): def setupUi(self, settingsDialog):
settingsDialog.setObjectName("settingsDialog") settingsDialog.setObjectName("settingsDialog")
settingsDialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) settingsDialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
settingsDialog.resize(661, 377) settingsDialog.resize(621, 387)
icon = QtGui.QIcon() icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/warchron_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) icon.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/warchron_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
settingsDialog.setWindowIcon(icon) settingsDialog.setWindowIcon(icon)
@ -24,8 +24,8 @@ class Ui_settingsDialog(object):
font.setPointSize(10) font.setPointSize(10)
self.groupBox_2.setFont(font) self.groupBox_2.setFont(font)
self.groupBox_2.setObjectName("groupBox_2") self.groupBox_2.setObjectName("groupBox_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2) self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_2)
self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout_3.setObjectName("verticalLayout_3")
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
self.label_5 = QtWidgets.QLabel(parent=self.groupBox_2) self.label_5 = QtWidgets.QLabel(parent=self.groupBox_2)
@ -56,25 +56,58 @@ class Ui_settingsDialog(object):
self.horizontalLayout.addWidget(self.label_15) self.horizontalLayout.addWidget(self.label_15)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem1) self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout_2.addLayout(self.horizontalLayout) self.verticalLayout_3.addLayout(self.horizontalLayout)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout() self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_12 = QtWidgets.QLabel(parent=self.groupBox_2) self.label_12 = QtWidgets.QLabel(parent=self.groupBox_2)
self.label_12.setObjectName("label_12") self.label_12.setObjectName("label_12")
self.horizontalLayout_5.addWidget(self.label_12) self.horizontalLayout_2.addWidget(self.label_12)
self.rankingComboBox = QtWidgets.QComboBox(parent=self.groupBox_2) self.pointsComboBox = QtWidgets.QComboBox(parent=self.groupBox_2)
self.rankingComboBox.setEnabled(False) self.pointsComboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.rankingComboBox.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.pointsComboBox.sizePolicy().hasHeightForWidth())
self.rankingComboBox.setSizePolicy(sizePolicy) self.pointsComboBox.setSizePolicy(sizePolicy)
self.rankingComboBox.setObjectName("rankingComboBox") self.pointsComboBox.setObjectName("pointsComboBox")
self.rankingComboBox.addItem("") self.pointsComboBox.addItem("")
self.horizontalLayout_5.addWidget(self.rankingComboBox) self.pointsComboBox.addItem("")
self.horizontalLayout_2.addWidget(self.pointsComboBox)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(spacerItem2) self.horizontalLayout_2.addItem(spacerItem2)
self.verticalLayout_2.addLayout(self.horizontalLayout_5) self.internalTiebreak = QtWidgets.QCheckBox(parent=self.groupBox_2)
self.internalTiebreak.setEnabled(False)
self.internalTiebreak.setText("")
self.internalTiebreak.setCheckable(True)
self.internalTiebreak.setChecked(True)
self.internalTiebreak.setObjectName("internalTiebreak")
self.horizontalLayout_2.addWidget(self.internalTiebreak)
self.label_20 = QtWidgets.QLabel(parent=self.groupBox_2)
self.label_20.setObjectName("label_20")
self.horizontalLayout_2.addWidget(self.label_20)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_2.addItem(spacerItem3)
self.verticalLayout_3.addLayout(self.horizontalLayout_2)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.label_19 = QtWidgets.QLabel(parent=self.groupBox_2)
self.label_19.setObjectName("label_19")
self.horizontalLayout_5.addWidget(self.label_19)
self.rankingComboBox_2 = QtWidgets.QComboBox(parent=self.groupBox_2)
self.rankingComboBox_2.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.rankingComboBox_2.sizePolicy().hasHeightForWidth())
self.rankingComboBox_2.setSizePolicy(sizePolicy)
self.rankingComboBox_2.setObjectName("rankingComboBox_2")
self.rankingComboBox_2.addItem("")
self.rankingComboBox_2.addItem("")
self.rankingComboBox_2.addItem("")
self.horizontalLayout_5.addWidget(self.rankingComboBox_2)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(spacerItem4)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.verticalLayout_4.addWidget(self.groupBox_2) self.verticalLayout_4.addWidget(self.groupBox_2)
self.groupBox = QtWidgets.QGroupBox(parent=settingsDialog) self.groupBox = QtWidgets.QGroupBox(parent=settingsDialog)
font = QtGui.QFont() font = QtGui.QFont()
@ -95,8 +128,8 @@ class Ui_settingsDialog(object):
self.label_8 = QtWidgets.QLabel(parent=self.groupBox) self.label_8 = QtWidgets.QLabel(parent=self.groupBox)
self.label_8.setObjectName("label_8") self.label_8.setObjectName("label_8")
self.horizontalLayout_3.addWidget(self.label_8) self.horizontalLayout_3.addWidget(self.label_8)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(spacerItem3) self.horizontalLayout_3.addItem(spacerItem5)
self.label_9 = QtWidgets.QLabel(parent=self.groupBox) self.label_9 = QtWidgets.QLabel(parent=self.groupBox)
self.label_9.setObjectName("label_9") self.label_9.setObjectName("label_9")
self.horizontalLayout_3.addWidget(self.label_9) self.horizontalLayout_3.addWidget(self.label_9)
@ -107,8 +140,8 @@ class Ui_settingsDialog(object):
self.label_10 = QtWidgets.QLabel(parent=self.groupBox) self.label_10 = QtWidgets.QLabel(parent=self.groupBox)
self.label_10.setObjectName("label_10") self.label_10.setObjectName("label_10")
self.horizontalLayout_3.addWidget(self.label_10) self.horizontalLayout_3.addWidget(self.label_10)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(spacerItem4) self.horizontalLayout_3.addItem(spacerItem6)
self.verticalLayout.addLayout(self.horizontalLayout_3) self.verticalLayout.addLayout(self.horizontalLayout_3)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout() self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.horizontalLayout_4.setObjectName("horizontalLayout_4")
@ -121,8 +154,8 @@ class Ui_settingsDialog(object):
self.influenceToken.setChecked(True) self.influenceToken.setChecked(True)
self.influenceToken.setObjectName("influenceToken") self.influenceToken.setObjectName("influenceToken")
self.horizontalLayout_4.addWidget(self.influenceToken) self.horizontalLayout_4.addWidget(self.influenceToken)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_4.addItem(spacerItem5) self.horizontalLayout_4.addItem(spacerItem7)
self.verticalLayout.addLayout(self.horizontalLayout_4) self.verticalLayout.addLayout(self.horizontalLayout_4)
self.verticalLayout_4.addWidget(self.groupBox) self.verticalLayout_4.addWidget(self.groupBox)
self.groupBox_3 = QtWidgets.QGroupBox(parent=settingsDialog) self.groupBox_3 = QtWidgets.QGroupBox(parent=settingsDialog)
@ -130,13 +163,45 @@ class Ui_settingsDialog(object):
font.setPointSize(10) font.setPointSize(10)
self.groupBox_3.setFont(font) self.groupBox_3.setFont(font)
self.groupBox_3.setObjectName("groupBox_3") self.groupBox_3.setObjectName("groupBox_3")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_3) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_3)
self.verticalLayout_3.setObjectName("verticalLayout_3") self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout_7 = QtWidgets.QHBoxLayout() self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.horizontalLayout_7.setObjectName("horizontalLayout_7")
self.label_21 = QtWidgets.QLabel(parent=self.groupBox_3)
self.label_21.setObjectName("label_21")
self.horizontalLayout_7.addWidget(self.label_21)
self.drawComboBox = QtWidgets.QComboBox(parent=self.groupBox_3)
self.drawComboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.drawComboBox.sizePolicy().hasHeightForWidth())
self.drawComboBox.setSizePolicy(sizePolicy)
self.drawComboBox.setObjectName("drawComboBox")
self.drawComboBox.addItem("")
self.drawComboBox.addItem("")
self.drawComboBox.addItem("")
self.horizontalLayout_7.addWidget(self.drawComboBox)
spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_7.addItem(spacerItem8)
self.label_18 = QtWidgets.QLabel(parent=self.groupBox_3)
self.label_18.setObjectName("label_18")
self.horizontalLayout_7.addWidget(self.label_18)
self.shuffle = QtWidgets.QCheckBox(parent=self.groupBox_3)
self.shuffle.setEnabled(False)
self.shuffle.setText("")
self.shuffle.setCheckable(True)
self.shuffle.setChecked(True)
self.shuffle.setObjectName("shuffle")
self.horizontalLayout_7.addWidget(self.shuffle)
spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_7.addItem(spacerItem9)
self.verticalLayout_2.addLayout(self.horizontalLayout_7)
self.horizontalLayout_8 = QtWidgets.QHBoxLayout()
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.label_17 = QtWidgets.QLabel(parent=self.groupBox_3) self.label_17 = QtWidgets.QLabel(parent=self.groupBox_3)
self.label_17.setObjectName("label_17") self.label_17.setObjectName("label_17")
self.horizontalLayout_7.addWidget(self.label_17) self.horizontalLayout_8.addWidget(self.label_17)
self.fallbackComboBox = QtWidgets.QComboBox(parent=self.groupBox_3) self.fallbackComboBox = QtWidgets.QComboBox(parent=self.groupBox_3)
self.fallbackComboBox.setEnabled(False) self.fallbackComboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
@ -146,22 +211,11 @@ class Ui_settingsDialog(object):
self.fallbackComboBox.setSizePolicy(sizePolicy) self.fallbackComboBox.setSizePolicy(sizePolicy)
self.fallbackComboBox.setObjectName("fallbackComboBox") self.fallbackComboBox.setObjectName("fallbackComboBox")
self.fallbackComboBox.addItem("") self.fallbackComboBox.addItem("")
self.horizontalLayout_7.addWidget(self.fallbackComboBox) self.fallbackComboBox.addItem("")
spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_8.addWidget(self.fallbackComboBox)
self.horizontalLayout_7.addItem(spacerItem6) spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.label_18 = QtWidgets.QLabel(parent=self.groupBox_3) self.horizontalLayout_8.addItem(spacerItem10)
self.label_18.setObjectName("label_18") self.verticalLayout_2.addLayout(self.horizontalLayout_8)
self.horizontalLayout_7.addWidget(self.label_18)
self.influenceToken_2 = QtWidgets.QCheckBox(parent=self.groupBox_3)
self.influenceToken_2.setEnabled(False)
self.influenceToken_2.setText("")
self.influenceToken_2.setCheckable(True)
self.influenceToken_2.setChecked(True)
self.influenceToken_2.setObjectName("influenceToken_2")
self.horizontalLayout_7.addWidget(self.influenceToken_2)
spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_7.addItem(spacerItem7)
self.verticalLayout_3.addLayout(self.horizontalLayout_7)
self.horizontalLayout_6 = QtWidgets.QHBoxLayout() self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.label_13 = QtWidgets.QLabel(parent=self.groupBox_3) self.label_13 = QtWidgets.QLabel(parent=self.groupBox_3)
@ -172,8 +226,8 @@ class Ui_settingsDialog(object):
self.rematchValue.setMinimum(1) self.rematchValue.setMinimum(1)
self.rematchValue.setObjectName("rematchValue") self.rematchValue.setObjectName("rematchValue")
self.horizontalLayout_6.addWidget(self.rematchValue) self.horizontalLayout_6.addWidget(self.rematchValue)
spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem11 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_6.addItem(spacerItem8) self.horizontalLayout_6.addItem(spacerItem11)
self.label_16 = QtWidgets.QLabel(parent=self.groupBox_3) self.label_16 = QtWidgets.QLabel(parent=self.groupBox_3)
self.label_16.setObjectName("label_16") self.label_16.setObjectName("label_16")
self.horizontalLayout_6.addWidget(self.label_16) self.horizontalLayout_6.addWidget(self.label_16)
@ -182,9 +236,9 @@ class Ui_settingsDialog(object):
self.occupancyValue.setMinimum(1) self.occupancyValue.setMinimum(1)
self.occupancyValue.setObjectName("occupancyValue") self.occupancyValue.setObjectName("occupancyValue")
self.horizontalLayout_6.addWidget(self.occupancyValue) self.horizontalLayout_6.addWidget(self.occupancyValue)
spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_6.addItem(spacerItem9) self.horizontalLayout_6.addItem(spacerItem12)
self.verticalLayout_3.addLayout(self.horizontalLayout_6) self.verticalLayout_2.addLayout(self.horizontalLayout_6)
self.verticalLayout_4.addWidget(self.groupBox_3) self.verticalLayout_4.addWidget(self.groupBox_3)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=settingsDialog) self.buttonBox = QtWidgets.QDialogButtonBox(parent=settingsDialog)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
@ -205,8 +259,14 @@ class Ui_settingsDialog(object):
self.label_14.setText(_translate("settingsDialog", "victory points")) self.label_14.setText(_translate("settingsDialog", "victory points"))
self.label_6.setText(_translate("settingsDialog", "Draw")) self.label_6.setText(_translate("settingsDialog", "Draw"))
self.label_15.setText(_translate("settingsDialog", "victory points")) self.label_15.setText(_translate("settingsDialog", "victory points"))
self.label_12.setText(_translate("settingsDialog", "Ranking mode")) self.label_12.setText(_translate("settingsDialog", "War points mode"))
self.rankingComboBox.setItemText(0, _translate("settingsDialog", "Sum points & tie-breaks")) self.pointsComboBox.setItemText(0, _translate("settingsDialog", "Sum battle points"))
self.pointsComboBox.setItemText(1, _translate("settingsDialog", "Sum campaign ranking"))
self.label_20.setText(_translate("settingsDialog", "Count internal tie-breaks"))
self.label_19.setText(_translate("settingsDialog", "Ranking mode"))
self.rankingComboBox_2.setItemText(0, _translate("settingsDialog", "Dense (1-2-2-3)"))
self.rankingComboBox_2.setItemText(1, _translate("settingsDialog", "Shift-up (1-2-2-4)"))
self.rankingComboBox_2.setItemText(2, _translate("settingsDialog", "Shift-down (1-3-3-4)"))
self.groupBox.setTitle(_translate("settingsDialog", "Objectives")) self.groupBox.setTitle(_translate("settingsDialog", "Objectives"))
self.label_4.setText(_translate("settingsDialog", "Major objective")) self.label_4.setText(_translate("settingsDialog", "Major objective"))
self.label_8.setText(_translate("settingsDialog", "narrative points")) self.label_8.setText(_translate("settingsDialog", "narrative points"))
@ -215,9 +275,14 @@ class Ui_settingsDialog(object):
self.label_11.setText(_translate("settingsDialog", "Underlying influence")) self.label_11.setText(_translate("settingsDialog", "Underlying influence"))
self.influenceToken.setText(_translate("settingsDialog", "Token")) self.influenceToken.setText(_translate("settingsDialog", "Token"))
self.groupBox_3.setTitle(_translate("settingsDialog", "Pairing")) self.groupBox_3.setTitle(_translate("settingsDialog", "Pairing"))
self.label_21.setText(_translate("settingsDialog", "Draw priority"))
self.drawComboBox.setItemText(0, _translate("settingsDialog", "Best war ranking"))
self.drawComboBox.setItemText(1, _translate("settingsDialog", "Random"))
self.drawComboBox.setItemText(2, _translate("settingsDialog", "Avoid rematch"))
self.label_18.setText(_translate("settingsDialog", "Shuffle groups"))
self.label_17.setText(_translate("settingsDialog", "Fallback mode")) self.label_17.setText(_translate("settingsDialog", "Fallback mode"))
self.fallbackComboBox.setItemText(0, _translate("settingsDialog", "Best ranking first & avoid rematch")) self.fallbackComboBox.setItemText(0, _translate("settingsDialog", "Avoid rematch"))
self.label_18.setText(_translate("settingsDialog", "Shuffle")) self.fallbackComboBox.setItemText(1, _translate("settingsDialog", "Random"))
self.label_13.setText(_translate("settingsDialog", "Rematch weight")) self.label_13.setText(_translate("settingsDialog", "Rematch weight"))
self.label_16.setText(_translate("settingsDialog", "Occupancy weight")) self.label_16.setText(_translate("settingsDialog", "Occupancy weight"))

View file

@ -9,8 +9,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>661</width> <width>621</width>
<height>377</height> <height>387</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -31,7 +31,7 @@
<property name="title"> <property name="title">
<string>Scores</string> <string>Scores</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
@ -117,16 +117,16 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QLabel" name="label_12"> <widget class="QLabel" name="label_12">
<property name="text"> <property name="text">
<string>Ranking mode</string> <string>War points mode</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="rankingComboBox"> <widget class="QComboBox" name="pointsComboBox">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -138,7 +138,12 @@
</property> </property>
<item> <item>
<property name="text"> <property name="text">
<string>Sum points &amp; tie-breaks</string> <string>Sum battle points</string>
</property>
</item>
<item>
<property name="text">
<string>Sum campaign ranking</string>
</property> </property>
</item> </item>
</widget> </widget>
@ -156,6 +161,94 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QCheckBox" name="internalTiebreak">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>Count internal tie-breaks</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_18">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_19">
<property name="text">
<string>Ranking mode</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="rankingComboBox_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Dense (1-2-2-3)</string>
</property>
</item>
<item>
<property name="text">
<string>Shift-up (1-2-2-4)</string>
</property>
</item>
<item>
<property name="text">
<string>Shift-down (1-3-3-4)</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>
@ -297,9 +390,97 @@
<property name="title"> <property name="title">
<string>Pairing</string> <string>Pairing</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_21">
<property name="text">
<string>Draw priority</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="drawComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Best war ranking</string>
</property>
</item>
<item>
<property name="text">
<string>Random</string>
</property>
</item>
<item>
<property name="text">
<string>Avoid rematch</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Shuffle groups</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="shuffle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_17">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item> <item>
<widget class="QLabel" name="label_17"> <widget class="QLabel" name="label_17">
<property name="text"> <property name="text">
@ -320,7 +501,12 @@
</property> </property>
<item> <item>
<property name="text"> <property name="text">
<string>Best ranking first &amp; avoid rematch</string> <string>Avoid rematch</string>
</property>
</item>
<item>
<property name="text">
<string>Random</string>
</property> </property>
</item> </item>
</widget> </widget>
@ -338,42 +524,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Shuffle</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="influenceToken_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_17">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>

View file

@ -67,7 +67,7 @@ class Ui_tieDialog(object):
def retranslateUi(self, tieDialog): def retranslateUi(self, tieDialog):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
tieDialog.setWindowTitle(_translate("tieDialog", "Tie")) tieDialog.setWindowTitle(_translate("tieDialog", "Tie-break"))
self.tieContext.setText(_translate("tieDialog", "Battle tie")) self.tieContext.setText(_translate("tieDialog", "Battle tie"))

View file

@ -14,7 +14,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Tie</string> <string>Tie-break</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset> <iconset>

View file

@ -200,15 +200,47 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
elif action == delete_action and self.on_delete_item: elif action == delete_action and self.on_delete_item:
self.on_delete_item(ItemType.PLAYER, player_id) self.on_delete_item(ItemType.PLAYER, player_id)
def _make_ratio_item(
self, won: int | None, played: int | None
) -> QtWidgets.QTableWidgetItem:
if not played:
text = ""
ratio = -1.0
else:
won = won or 0
text = f"{won} / {played}"
ratio = won / played
item = QtWidgets.QTableWidgetItem(text)
item.setData(Qt.ItemDataRole.UserRole, ratio)
return item
def display_players(self, players: List[ParticipantOption]) -> None: def display_players(self, players: List[ParticipantOption]) -> None:
# TODO display stats (war, campaign battles...)
table = self.playersTable table = self.playersTable
table.setSortingEnabled(False) table.setSortingEnabled(False)
table.setColumnCount(4)
table.setHorizontalHeaderLabels(["Name", "Wars", "Campaigns", "Battles"])
table.setRowCount(len(players)) table.setRowCount(len(players))
for row, player in enumerate(players): for row, player in enumerate(players):
play_item = QtWidgets.QTableWidgetItem(player.name) play_item = QtWidgets.QTableWidgetItem(player.name)
wars_item = self._make_ratio_item(
player.wars_won,
player.wars_played,
)
camp_item = self._make_ratio_item(
player.campaigns_won,
player.campaigns_played,
)
bat_item = self._make_ratio_item(
player.battles_won,
player.battles_played,
)
play_item.setData(Qt.ItemDataRole.UserRole, player.id) play_item.setData(Qt.ItemDataRole.UserRole, player.id)
table.setItem(row, 0, play_item) table.setItem(row, 0, play_item)
table.setItem(row, 1, wars_item)
table.setItem(row, 2, camp_item)
table.setItem(row, 3, bat_item)
table.setSortingEnabled(True) table.setSortingEnabled(True)
table.sortItems(0, Qt.SortOrder.AscendingOrder) table.sortItems(0, Qt.SortOrder.AscendingOrder)
table.resizeColumnsToContents() table.resizeColumnsToContents()