diff --git a/src/warchron/controller/app_controller.py b/src/warchron/controller/app_controller.py index 394d04b..63909de 100644 --- a/src/warchron/controller/app_controller.py +++ b/src/warchron/controller/app_controller.py @@ -215,13 +215,13 @@ class AppController: self.navigation.refresh_and_select( RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=rnd.id ) - self.is_dirty = True except DomainError as e: QMessageBox.warning( self.view, - "Deletion forbidden", + "Add forbidden", str(e), ) + return except RequiresConfirmation as e: reply = QMessageBox.question( self.view, @@ -231,7 +231,10 @@ class AppController: ) if reply == QMessageBox.StandardButton.Yes: e.action() - self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) + else: + return + self.is_dirty = True + self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) def edit_item(self, item_type: str, item_id: str) -> None: try: @@ -266,13 +269,13 @@ class AppController: elif item_type == ItemType.BATTLE: self.rounds.edit_round_battle(item_id) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) - self.is_dirty = True except DomainError as e: QMessageBox.warning( self.view, - "Deletion forbidden", + "Update forbidden", str(e), ) + return except RequiresConfirmation as e: reply = QMessageBox.question( self.view, @@ -282,7 +285,10 @@ class AppController: ) if reply == QMessageBox.StandardButton.Yes: e.action() - self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) + else: + return + self.is_dirty = True + self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) def delete_item(self, item_type: str, item_id: str) -> None: reply = QMessageBox.question( @@ -326,13 +332,13 @@ class AppController: self.navigation.refresh_and_select( RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp_id ) - self.is_dirty = True except DomainError as e: QMessageBox.warning( self.view, "Deletion forbidden", str(e), ) + return except RequiresConfirmation as e: reply = QMessageBox.question( self.view, @@ -342,4 +348,7 @@ class AppController: ) if reply == QMessageBox.StandardButton.Yes: e.action() - self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) + else: + return + self.is_dirty = True + self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) diff --git a/src/warchron/controller/campaign_controller.py b/src/warchron/controller/campaign_controller.py index d66154e..147bd93 100644 --- a/src/warchron/controller/campaign_controller.py +++ b/src/warchron/controller/campaign_controller.py @@ -177,9 +177,10 @@ class CampaignController: except DomainError as e: QMessageBox.warning( self.app.view, - "Deletion forbidden", + "Closure forbidden", str(e), ) + return self.app.is_dirty = True self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh_and_select( diff --git a/src/warchron/controller/round_controller.py b/src/warchron/controller/round_controller.py index 8fdc4c9..dcfcea3 100644 --- a/src/warchron/controller/round_controller.py +++ b/src/warchron/controller/round_controller.py @@ -167,9 +167,10 @@ class RoundController: except DomainError as e: QMessageBox.warning( self.app.view, - "Deletion forbidden", + "Closure forbidden", str(e), ) + return self.app.is_dirty = True self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh_and_select( diff --git a/src/warchron/controller/war_controller.py b/src/warchron/controller/war_controller.py index 54f28f6..5ebf567 100644 --- a/src/warchron/controller/war_controller.py +++ b/src/warchron/controller/war_controller.py @@ -11,7 +11,11 @@ from warchron.constants import ( IconName, RANK_TO_ICON, ) -from warchron.model.exception import DomainError, ForbiddenOperation +from warchron.model.exception import ( + DomainError, + ForbiddenOperation, + RequiresConfirmation, +) if TYPE_CHECKING: from warchron.controller.app_controller import AppController @@ -152,9 +156,10 @@ class WarController: except DomainError as e: QMessageBox.warning( self.app.view, - "Deletion forbidden", + "Closure forbidden", str(e), ) + return self.app.is_dirty = True self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh_and_select( @@ -200,6 +205,7 @@ class WarController: "Setting forbidden", str(e), ) + return self.app.is_dirty = True self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) @@ -215,6 +221,7 @@ class WarController: "Setting forbidden", str(e), ) + return self.app.is_dirty = True self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) @@ -230,8 +237,23 @@ class WarController: "Setting forbidden", str(e), ) + return + except RequiresConfirmation as e: + reply = QMessageBox.question( + self.app.view, + "Confirm update", + str(e), + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + ) + if reply == QMessageBox.StandardButton.Yes: + e.action() + else: + return self.app.is_dirty = True self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) + self.app.navigation.refresh_and_select( + RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id + ) # Objective methods diff --git a/src/warchron/model/war.py b/src/warchron/model/war.py index 8681d12..b6bf57c 100644 --- a/src/warchron/model/war.py +++ b/src/warchron/model/war.py @@ -3,8 +3,14 @@ from uuid import uuid4 from datetime import datetime from typing import Any, Dict, List -from warchron.model.war_event import WarEvent, InfluenceGained, InfluenceSpent -from warchron.model.exception import ForbiddenOperation +from warchron.constants import ContextType +from warchron.model.war_event import ( + WarEvent, + InfluenceGained, + InfluenceSpent, + TieResolved, +) +from warchron.model.exception import ForbiddenOperation, RequiresConfirmation from warchron.model.war_participant import WarParticipant from warchron.model.objective import Objective from warchron.model.campaign_participant import CampaignParticipant @@ -55,10 +61,67 @@ class War: def set_influence_token(self, new_state: bool) -> None: if self.is_over: raise ForbiddenOperation("Can't set influence token of a closed war.") - # TODO raise RequiresConfirmation - # * disable: cleanup if any token has already been gained/spent - # * enable: retrigger battle_outcomes and resolve tie again if any draw - self.influence_token = new_state + + def cleanup_token_and_tie() -> None: + new_events: List[WarEvent] = [] + for ev in self.events: + if isinstance(ev, (InfluenceSpent, InfluenceGained)): + continue + if isinstance(ev, TieResolved): + ev.set_participant(None) + new_events.append(ev) + self.events = new_events + self.influence_token = new_state + + def reset_tie_break() -> None: + new_events: List[WarEvent] = [] + for ev in self.events: + if isinstance(ev, (TieResolved)): + if ev.context_type == ContextType.BATTLE: + battle = self.get_battle(ev.context_id) + campaign = self.get_campaign_by_sector(battle.sector_id) + round = campaign.get_round_by_battle(ev.context_id) + round.is_over = False + elif ev.context_type == ContextType.CAMPAIGN: + campaign = self.get_campaign(ev.context_id) + campaign.is_over = False + else: + new_events.append(ev) + self.events = new_events + # TODO Reopen rounds with battle on sector with influence objective + for camp in self.campaigns: + for sect in camp.get_all_sectors(): + if sect.influence_objective_id is not None: + round = camp.get_round_by_battle(sect.id) + round.is_over = False + + if new_state is False: + if not self.events: + self.influence_token = new_state + return + raise RequiresConfirmation( + "Some influence tokens already exist in this war.\n" + "All tokens will be deleted and tie-breaks will become draw.\n" + "Do you want to continue?", + action=cleanup_token_and_tie, + ) + if new_state is True: + has_tie_resolved = any(isinstance(ev, TieResolved) for ev in self.events) + + grant_influence = any( + (sect.influence_objective_id is not None) + for camp in self.campaigns + for sect in camp.get_all_sectors() + ) + if not has_tie_resolved and not grant_influence: + self.influence_token = new_state + return + raise RequiresConfirmation( + "Some influence tokens and draws exist in this war.\n" + "All influence outcomes and tie-breaks will be reset.\n" + "Do you want to continue?", + action=reset_tie_break, + ) def set_state(self, new_state: bool) -> None: self.is_over = new_state