allow closing round with incomplete battles
This commit is contained in:
parent
ae6c033bbe
commit
69942a3cff
10 changed files with 67 additions and 29 deletions
|
|
@ -235,7 +235,8 @@ class AppController:
|
|||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
)
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
e.action()
|
||||
if e.action:
|
||||
e.action()
|
||||
else:
|
||||
return
|
||||
self.is_dirty = True
|
||||
|
|
@ -290,7 +291,8 @@ class AppController:
|
|||
)
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
e.action()
|
||||
if e.action:
|
||||
e.action()
|
||||
except DomainError as inner:
|
||||
QMessageBox.warning(
|
||||
self.view,
|
||||
|
|
@ -361,7 +363,8 @@ class AppController:
|
|||
)
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
e.action()
|
||||
if e.action:
|
||||
e.action()
|
||||
except DomainError as inner:
|
||||
QMessageBox.warning(
|
||||
self.view,
|
||||
|
|
|
|||
|
|
@ -186,14 +186,36 @@ class RoundController:
|
|||
camp = self.app.model.get_campaign_by_round(round_id)
|
||||
war = self.app.model.get_war_by_round(round_id)
|
||||
workflow = RoundClosureWorkflow(self.app)
|
||||
try:
|
||||
workflow.start(war, camp, rnd)
|
||||
except DomainError as e:
|
||||
QMessageBox.warning(
|
||||
self.app.view,
|
||||
"Closure forbidden",
|
||||
str(e),
|
||||
)
|
||||
confirmed = False
|
||||
stop = False
|
||||
while True:
|
||||
try:
|
||||
workflow.start(war, camp, rnd, confirmed)
|
||||
break
|
||||
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
|
||||
self.app.is_dirty = True
|
||||
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
|
||||
|
|
@ -234,7 +256,8 @@ class RoundController:
|
|||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
)
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
e.action()
|
||||
if e.action:
|
||||
e.action()
|
||||
else:
|
||||
return
|
||||
self.app.is_dirty = True
|
||||
|
|
|
|||
|
|
@ -221,7 +221,8 @@ class WarController:
|
|||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
)
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
e.action()
|
||||
if e.action:
|
||||
e.action()
|
||||
else:
|
||||
return
|
||||
self.is_dirty = True
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ class Workflow:
|
|||
|
||||
class RoundClosureWorkflow(Workflow):
|
||||
|
||||
def start(self, war: War, campaign: Campaign, round: Round) -> None:
|
||||
Closer.check_round_closable(round)
|
||||
def start(
|
||||
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)
|
||||
while ties:
|
||||
for tie in ties:
|
||||
|
|
|
|||
|
|
@ -80,6 +80,12 @@ class Battle:
|
|||
def is_finished(self) -> bool:
|
||||
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]:
|
||||
return {
|
||||
"sector_id": self.sector_id,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
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 import War
|
||||
from warchron.model.campaign import Campaign
|
||||
|
|
@ -14,13 +14,19 @@ class Closer:
|
|||
# Round methods
|
||||
|
||||
@staticmethod
|
||||
def check_round_closable(round: Round) -> None:
|
||||
def check_round_closable(round: Round, confirmed: bool) -> None:
|
||||
if round.is_over:
|
||||
raise ForbiddenOperation("Round already closed")
|
||||
if not round.all_battles_finished():
|
||||
raise ForbiddenOperation(
|
||||
"All battles must be finished to close their round"
|
||||
)
|
||||
if not confirmed:
|
||||
if any(not bat.is_complete() for bat in round.battles.values()):
|
||||
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
|
||||
def apply_battle_outcomes(war: War, campaign: Campaign, battle: Battle) -> None:
|
||||
|
|
|
|||
|
|
@ -26,6 +26,6 @@ class DomainDecision(Exception):
|
|||
|
||||
|
||||
class RequiresConfirmation(DomainDecision):
|
||||
def __init__(self, message: str, action: Callable[[], None]):
|
||||
def __init__(self, message: str, action: Callable[[], None] | None = None):
|
||||
super().__init__(message)
|
||||
self.action = action
|
||||
|
|
|
|||
|
|
@ -63,10 +63,7 @@ class Pairing:
|
|||
bat.set_winner(None)
|
||||
war.revert_choice_ties(round.id)
|
||||
|
||||
if any(
|
||||
bat.player_1_id is not None or bat.player_2_id is not None
|
||||
for bat in round.battles.values()
|
||||
):
|
||||
if any(bat.has_player() for bat in round.battles.values()):
|
||||
raise RequiresConfirmation(
|
||||
"Battle(s) already have player(s) assigned for this round.\n"
|
||||
"Battle players will be cleared.\n"
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ class Round:
|
|||
return any(b.is_finished() for b in self.battles.values())
|
||||
|
||||
def all_battles_finished(self) -> bool:
|
||||
# TODO exception for participant alone
|
||||
return all(
|
||||
b.winner_id is not None or b.is_draw() for b in self.battles.values()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -63,8 +63,9 @@ class TieBreaker:
|
|||
for battle in round.battles.values():
|
||||
if campaign is None:
|
||||
raise DomainError("No campaign for this battle tie")
|
||||
if battle.player_1_id is None or battle.player_2_id is None:
|
||||
raise DomainError("Missing player(s) in this battle context.")
|
||||
if not battle.is_complete():
|
||||
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)
|
||||
p2_id = campaign.campaign_to_war_part_id(battle.player_2_id)
|
||||
if not battle.is_draw():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue