Compare commits
5 commits
c144845376
...
2901dcd68c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2901dcd68c | ||
|
|
261c64942d | ||
|
|
69942a3cff | ||
|
|
ae6c033bbe | ||
|
|
a3144dc3c9 |
21 changed files with 644 additions and 160 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]:
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
101
src/warchron/model/statistics.py
Normal file
101
src/warchron/model/statistics.py
Normal 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
|
||||||
|
|
@ -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():
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 & 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 & 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>
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue