class QRecruitBehaviour: game_model: GameModel cp: ControlPoint purchase_groups: dict[UnitType, PurchaseGroup] existing_units_labels = None maximum_units = -1 BUDGET_FORMAT = "Available Budget: <b>${:.2f}M</b>" def __init__(self) -> None: self.existing_units_labels = {} self.purchase_groups = {} self.update_available_budget() @property def pending_deliveries(self) -> PendingUnitDeliveries: return self.cp.pending_unit_deliveries @property def budget(self) -> float: return self.game_model.game.budget @budget.setter def budget(self, value: int) -> None: self.game_model.game.budget = value def add_purchase_row( self, unit_type: UnitType, layout: QGridLayout, row: int, ) -> None: exist = QGroupBox() exist.setProperty("style", "buy-box") exist.setMaximumHeight(36) exist.setMinimumHeight(36) existLayout = QHBoxLayout() exist.setLayout(existLayout) existing_units = self.cp.base.total_units_of_type(unit_type) unitName = QLabel(f"<b>{unit_type.name}</b>") unitName.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) existing_units = QLabel(str(existing_units)) existing_units.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.existing_units_labels[unit_type] = existing_units price = QLabel(f"<b>$ {unit_type.price}</b> M") price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) purchase_group = PurchaseGroup(unit_type, self) self.purchase_groups[unit_type] = purchase_group info = QGroupBox() info.setProperty("style", "buy-box") info.setMaximumHeight(36) info.setMinimumHeight(36) infolayout = QHBoxLayout() info.setLayout(infolayout) unitInfo = QPushButton("i") unitInfo.setProperty("style", "btn-info") unitInfo.setMinimumSize(16, 16) unitInfo.setMaximumSize(16, 16) unitInfo.clicked.connect(lambda: self.info(unit_type)) unitInfo.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) existLayout.addWidget(unitName) existLayout.addItem( QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(existing_units) existLayout.addItem( QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(price) infolayout.addWidget(unitInfo) layout.addWidget(exist, row, 1) layout.addWidget(purchase_group, row, 2) layout.addWidget(info, row, 3) def update_available_budget(self) -> None: GameUpdateSignal.get_instance().updateBudget(self.game_model.game) def recruit_handler(self, recruit_type: RecruitType, unit_type: UnitType) -> None: # Lookup if Keyboard Modifiers were pressed # Shift = 10 times # CTRL = 5 Times modifiers = QApplication.keyboardModifiers() if modifiers == Qt.ShiftModifier: amount = 10 elif modifiers == Qt.ControlModifier: amount = 5 else: amount = 1 for i in range(amount): if recruit_type == RecruitType.SELL: if not self.sell(unit_type): return elif recruit_type == RecruitType.BUY: if not self.buy(unit_type): return def buy(self, unit_type: UnitType) -> bool: if not self.enable_purchase(unit_type): logging.error( f"Purchase of {unit_type} not allowed at {self.cp.name}") return False self.pending_deliveries.order({unit_type: 1}) self.budget -= unit_type.price self.update_purchase_controls() self.update_available_budget() return True def sell(self, unit_type: UnitType) -> bool: if self.pending_deliveries.available_next_turn(unit_type) > 0: self.budget += unit_type.price self.pending_deliveries.sell({unit_type: 1}) self.update_purchase_controls() self.update_available_budget() return True def update_purchase_controls(self) -> None: for group in self.purchase_groups.values(): group.update_state() def enable_purchase(self, unit_type: UnitType) -> bool: return self.budget >= unit_type.price def enable_sale(self, unit_type: UnitType) -> bool: return True def info(self, unit_type: UnitType) -> None: self.info_window = QUnitInfoWindow(self.game_model.game, unit_type) self.info_window.show() def set_maximum_units(self, maximum_units): """ Set the maximum number of units that can be bought """ self.maximum_units = maximum_units
def info(self, unit_type: UnitType) -> None: self.info_window = QUnitInfoWindow(self.game_model.game, unit_type) self.info_window.show()
def info(self, item: TransactionItemType) -> None: self.info_window = QUnitInfoWindow( self.game_model.game, self.purchase_adapter.unit_type_of(item) ) self.info_window.show()
class QRecruitBehaviour: game_model: GameModel cp: ControlPoint existing_units_labels = None bought_amount_labels = None maximum_units = -1 recruitable_types = [] BUDGET_FORMAT = "Available Budget: <b>${}M</b>" def __init__(self) -> None: self.bought_amount_labels = {} self.existing_units_labels = {} self.recruitable_types = [] self.update_available_budget() @property def pending_deliveries(self) -> UnitsDeliveryEvent: return self.cp.pending_unit_deliveries @property def budget(self) -> int: return self.game_model.game.budget @budget.setter def budget(self, value: int) -> None: self.game_model.game.budget = value def add_purchase_row(self, unit_type: Type[UnitType], layout: QLayout, row: int, disabled: bool = False) -> int: exist = QGroupBox() exist.setProperty("style", "buy-box") exist.setMaximumHeight(36) exist.setMinimumHeight(36) existLayout = QHBoxLayout() exist.setLayout(existLayout) existing_units = self.cp.base.total_units_of_type(unit_type) scheduled_units = self.pending_deliveries.units.get(unit_type, 0) unitName = QLabel("<b>" + db.unit_get_expanded_info(self.game_model.game.player_country, unit_type, 'name') + "</b>") unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) existing_units = QLabel(str(existing_units)) existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) amount_bought = QLabel("<b>{}</b>".format(str(scheduled_units))) amount_bought.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.existing_units_labels[unit_type] = existing_units self.bought_amount_labels[unit_type] = amount_bought price = QLabel("<b>$ {:02d}</b> m".format(db.PRICES[unit_type])) price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) buysell = QGroupBox() buysell.setProperty("style", "buy-box") buysell.setMaximumHeight(36) buysell.setMinimumHeight(36) buysellayout = QHBoxLayout() buysell.setLayout(buysellayout) buy = QPushButton("+") buy.setProperty("style", "btn-buy") buy.setDisabled(disabled) buy.setMinimumSize(16, 16) buy.setMaximumSize(16, 16) buy.clicked.connect(lambda: self.buy(unit_type)) buy.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) sell = QPushButton("-") sell.setProperty("style", "btn-sell") sell.setDisabled(disabled) sell.setMinimumSize(16, 16) sell.setMaximumSize(16, 16) sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) sell.clicked.connect(lambda: self.sell(unit_type)) info = QGroupBox() info.setProperty("style", "buy-box") info.setMaximumHeight(36) info.setMinimumHeight(36) infolayout = QHBoxLayout() info.setLayout(infolayout) unitInfo = QPushButton("i") unitInfo.setProperty("style", "btn-info") unitInfo.setDisabled(disabled) unitInfo.setMinimumSize(16, 16) unitInfo.setMaximumSize(16, 16) unitInfo.clicked.connect(lambda: self.info(unit_type)) unitInfo.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) existLayout.addWidget(unitName) existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(existing_units) existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(price) buysellayout.addWidget(sell) buysellayout.addWidget(amount_bought) buysellayout.addWidget(buy) infolayout.addWidget(unitInfo) layout.addWidget(exist, row, 1) layout.addWidget(buysell, row, 2) layout.addWidget(info, row, 3) return row + 1 def _update_count_label(self, unit_type: Type[UnitType]): self.bought_amount_labels[unit_type].setText("<b>{}</b>".format( unit_type in self.pending_deliveries.units and "{}".format(self.pending_deliveries.units[unit_type]) or "0" )) self.existing_units_labels[unit_type].setText("<b>{}</b>".format( self.cp.base.total_units_of_type(unit_type) )) def update_available_budget(self) -> None: GameUpdateSignal.get_instance().updateBudget(self.game_model.game) def buy(self, unit_type: Type[UnitType]): price = db.PRICES[unit_type] if self.budget >= price: self.pending_deliveries.order({unit_type: 1}) self.budget -= price else: # TODO : display modal warning logging.info("Not enough money !") self._update_count_label(unit_type) self.update_available_budget() def sell(self, unit_type): if self.pending_deliveries.available_next_turn(unit_type) > 0: price = db.PRICES[unit_type] self.budget += price self.pending_deliveries.sell({unit_type: 1}) if self.pending_deliveries.units[unit_type] == 0: del self.pending_deliveries.units[unit_type] self._update_count_label(unit_type) self.update_available_budget() def info(self, unit_type): self.info_window = QUnitInfoWindow(self.game_model.game, unit_type) self.info_window.show() def set_maximum_units(self, maximum_units): """ Set the maximum number of units that can be bought """ self.maximum_units = maximum_units def set_recruitable_types(self, recruitables_types): """ Set the maximum number of units that can be bought """ self.recruitables_types = recruitables_types
class UnitTransactionFrame(QFrame, Generic[TransactionItemType]): BUDGET_FORMAT = "Available Budget: <b>${:.2f}M</b>" def __init__( self, game_model: GameModel, purchase_adapter: PurchaseAdapter[TransactionItemType], ) -> None: super().__init__() self.game_model = game_model self.purchase_adapter = purchase_adapter self.existing_units_labels = {} self.purchase_groups: dict[ TransactionItemType, PurchaseGroup[TransactionItemType] ] = {} self.update_available_budget() def current_quantity_of(self, item: TransactionItemType) -> int: return self.purchase_adapter.current_quantity_of(item) def pending_delivery_quantity(self, item: TransactionItemType) -> int: return self.purchase_adapter.pending_delivery_quantity(item) def expected_quantity_next_turn(self, item: TransactionItemType) -> int: return self.purchase_adapter.expected_quantity_next_turn(item) def display_name_of( self, item: TransactionItemType, multiline: bool = False ) -> str: return self.purchase_adapter.name_of(item, multiline) def price_of(self, item: TransactionItemType) -> int: return self.purchase_adapter.price_of(item) @property def budget(self) -> float: return self.game_model.game.blue.budget @budget.setter def budget(self, value: int) -> None: self.game_model.game.blue.budget = value def add_purchase_row( self, item: TransactionItemType, layout: QGridLayout, row: int, ) -> None: exist = QGroupBox() exist.setProperty("style", "buy-box") exist.setMaximumHeight(72) exist.setMinimumHeight(36) existLayout = QHBoxLayout() existLayout.setSizeConstraint(QLayout.SetMinimumSize) exist.setLayout(existLayout) existing_units = self.current_quantity_of(item) unitName = QLabel(f"<b>{self.display_name_of(item, multiline=True)}</b>") unitName.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) ) existing_units = QLabel(str(existing_units)) existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.existing_units_labels[item] = existing_units price = QLabel(f"<b>$ {self.price_of(item)}</b> M") price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) purchase_group = PurchaseGroup(item, self) self.purchase_groups[item] = purchase_group info = QGroupBox() info.setProperty("style", "buy-box") info.setMaximumHeight(72) info.setMinimumHeight(36) infolayout = QHBoxLayout() info.setLayout(infolayout) unitInfo = QPushButton("i") unitInfo.setProperty("style", "btn-info") unitInfo.setMinimumSize(16, 16) unitInfo.setMaximumSize(16, 16) unitInfo.clicked.connect(lambda: self.info(item)) unitInfo.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) existLayout.addWidget(unitName) existLayout.addItem( QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum) ) existLayout.addWidget(existing_units) existLayout.addItem( QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum) ) existLayout.addWidget(price) infolayout.addWidget(unitInfo) layout.addWidget(exist, row, 1) layout.addWidget(purchase_group, row, 2) layout.addWidget(info, row, 3) def update_available_budget(self) -> None: GameUpdateSignal.get_instance().updateBudget(self.game_model.game) def recruit_handler( self, recruit_type: RecruitType, item: TransactionItemType ) -> None: # Lookup if Keyboard Modifiers were pressed # Shift = 10 times # CTRL = 5 Times modifiers = QApplication.keyboardModifiers() if modifiers == Qt.ShiftModifier: amount = 10 elif modifiers == Qt.ControlModifier: amount = 5 else: amount = 1 if recruit_type == RecruitType.SELL: self.sell(item, amount) elif recruit_type == RecruitType.BUY: self.buy(item, amount) def post_transaction_update(self) -> None: self.update_purchase_controls() self.update_available_budget() def buy(self, item: TransactionItemType, quantity: int) -> None: try: self.purchase_adapter.buy(item, quantity) except TransactionError as ex: logging.exception(f"Purchase of {self.display_name_of(item)} failed") QMessageBox.warning(self, "Purchase failed", str(ex), QMessageBox.Ok) finally: self.post_transaction_update() def sell(self, item: TransactionItemType, quantity: int) -> None: try: self.purchase_adapter.sell(item, quantity) except TransactionError as ex: logging.exception(f"Sale of {self.display_name_of(item)} failed") QMessageBox.warning(self, "Sale failed", str(ex), QMessageBox.Ok) finally: self.post_transaction_update() def update_purchase_controls(self) -> None: for group in self.purchase_groups.values(): group.update_state() def enable_purchase(self, item: TransactionItemType) -> bool: return self.purchase_adapter.can_buy(item) def enable_sale(self, item: TransactionItemType) -> bool: return self.purchase_adapter.can_sell_or_cancel(item) @staticmethod def purchase_tooltip(is_enabled: bool) -> str: if is_enabled: return "Buy unit. Use Shift or Ctrl key to buy multiple units at once." else: return "Unit can not be bought." @staticmethod def sell_tooltip(is_enabled: bool) -> str: if is_enabled: return "Sell unit. Use Shift or Ctrl key to buy multiple units at once." else: return "Unit can not be sold." def info(self, item: TransactionItemType) -> None: self.info_window = QUnitInfoWindow( self.game_model.game, self.purchase_adapter.unit_type_of(item) ) self.info_window.show()