Пример #1
0
    def __init__(self):
        """

        Initializes the main game's window and sets its attributes to their
        default values.

        """
        super(RagingSeasWindow, self).__init__()
        self.ui = Ui_RagingSeasWindow()
        self.ui.setupUi(self)
        self.ui.menuFile.menuAction().setStatusTip(self.tr("File"))
        self.ui.menuHelp.menuAction().setStatusTip(self.tr("Help"))
        self.ui.playerOneLabel.hide()
        self.ui.playerTwoLabel.hide()
        self.leftGridMapper = QSignalMapper(self)
        self.rightGridMapper = QSignalMapper(self)
        self.statusLabel = QLabel(self)
        self.statusLabel.setObjectName("StatusLabel")
        self.ui.statusBar.setSizeGripEnabled(False)
        self.ui.statusBar.addPermanentWidget(self.statusLabel)
        self.setStyleSheet(style.INITIAL_MAIN_WINDOW_STYLE)
        self.setFixedSize(self.size())
        self.setWindowFlags(
            self.windowFlags() ^
            (QtCore.Qt.WindowMinimizeButtonHint |
             QtCore.Qt.WindowMaximizeButtonHint)
        )

        self._session = None
        self._grid_size = None
        self._previous_grid_size = None
        self._is_new_game = False
        self._has_game_been_started = False
        self._current_ship_index = 0
        self._current_orientation = ShipOrientation.horizontal
Пример #2
0
class RagingSeasWindow(QMainWindow):

    """A window used to play the game (contains both players' grids)

    """
    def __init__(self):
        """

        Initializes the main game's window and sets its attributes to their
        default values.

        """
        super(RagingSeasWindow, self).__init__()
        self.ui = Ui_RagingSeasWindow()
        self.ui.setupUi(self)
        self.ui.menuFile.menuAction().setStatusTip(self.tr("File"))
        self.ui.menuHelp.menuAction().setStatusTip(self.tr("Help"))
        self.ui.playerOneLabel.hide()
        self.ui.playerTwoLabel.hide()
        self.leftGridMapper = QSignalMapper(self)
        self.rightGridMapper = QSignalMapper(self)
        self.statusLabel = QLabel(self)
        self.statusLabel.setObjectName("StatusLabel")
        self.ui.statusBar.setSizeGripEnabled(False)
        self.ui.statusBar.addPermanentWidget(self.statusLabel)
        self.setStyleSheet(style.INITIAL_MAIN_WINDOW_STYLE)
        self.setFixedSize(self.size())
        self.setWindowFlags(
            self.windowFlags() ^
            (QtCore.Qt.WindowMinimizeButtonHint |
             QtCore.Qt.WindowMaximizeButtonHint)
        )

        self._session = None
        self._grid_size = None
        self._previous_grid_size = None
        self._is_new_game = False
        self._has_game_been_started = False
        self._current_ship_index = 0
        self._current_orientation = ShipOrientation.horizontal

    def get_session(self):
        return self._session

    def set_session(self, value):
        self._session = value

    session = property(get_session, set_session)

    def get_grid_size(self):
        return self._grid_size

    def set_grid_size(self, value):
        self._grid_size = value

    grid_size = property(get_grid_size, set_grid_size)

    def get_is_new_game(self):
        return self._is_new_game

    def set_is_new_game(self, value):
        self._is_new_game = value

    is_new_game = property(get_is_new_game, set_is_new_game)

    def get_has_game_been_started(self):
        return self._has_game_been_started

    def set_has_game_been_started(self, value):
        self._has_game_been_started = value

    has_game_been_started = property(get_has_game_been_started,
                                     set_has_game_been_started)

    def get_current_ship_index(self):
        return self._current_ship_index

    def set_current_ship_index(self, value):
        self._current_ship_index = value

    current_ship_index = property(get_current_ship_index,
                                  set_current_ship_index)

    def get_current_orientation(self):
        return self._current_orientation

    def set_current_orientation(self, value):
        self._current_orientation = value

    current_orientation = property(get_current_orientation,
                                   set_current_orientation)

    def initialize_grids(self):
        """Creates the players' grids using the chose grid size.

        """
        dimensions = self._grid_size.value

        # disconnect the signal mappers and clear the layouts if a game has
        # previously been started
        if self._has_game_been_started:
            self._disconnect_mappers()
            self._clear_layout(self.ui.gridLayoutLeft)
            self._clear_layout(self.ui.gridLayoutRight)
        else:
            self._has_game_been_started = True

        for i in xrange(0, dimensions):
            for j in xrange(0, dimensions):
                # creating QCustomPushButtons
                self._populate_grid_at_position(GridPosition.left,
                                                self.leftGridMapper,
                                                self.ui.gridLayoutLeft,
                                                (i, j))

                self._populate_grid_at_position(GridPosition.right,
                                                self.rightGridMapper,
                                                self.ui.gridLayoutRight,
                                                (i, j))

        # mapping QSignalMappers to the slots that they will be using
        self.leftGridMapper.mapped[str].connect(self.leftGridButtonClick)
        self.rightGridMapper.mapped[str].connect(self.rightGridButtonClick)

        self._adjust_main_window()
        self._update_status_bar()

    def resolve_grid_button(self, grid_layout, coordinates):
        """Fetches a button at the given coordinates.

        :param grid_layout: an instance of QGridLayout,
        :param coordinates: an instance of type Coordinates, location of the
            item
        :return: an instance of type QPushButton, located at the specified
            coordinates

        """
        x, y = coordinates.x, coordinates.y
        layout_item = grid_layout.itemAtPosition(x, y)

        return layout_item.widget()

    def _adjust_main_window(self):
        # resizes the main window to fit the chosen size of the grid

        dimensions = self._grid_size.value
        width = dimensions * (style.FIELD_ICON_SIZE + 10) * 2 + \
            style.SPACER_WIDTH
        height = dimensions * (style.FIELD_ICON_SIZE + 10) + \
            style.PLAYER_LABEL_HEIGHT
        status_bar_geometry = self.ui.statusBar.geometry()

        self.setFixedSize(width, height + (status_bar_geometry.height() * 2))
        self.setStyleSheet(style.NEW_GAME_MAIN_WINDOW_STYLE)
        self.ui.horizontalLayout.setGeometry(QRect(0, 0, width, height))
        self.ui.playerOneLabel.show()
        self.ui.playerTwoLabel.show()

    def _populate_grid_at_position(self,
                                   grid_position,
                                   mapper,
                                   layout,
                                   coordinate_pair):
        # creates a button and positions it on the grid

        x, y = coordinate_pair[0], coordinate_pair[1]
        textual_coordinates = "{0}-{1}".format(x, y)
        button = QCustomPushButton(
            self,
            grid_position,
            Coordinates.parse_coordinates(textual_coordinates,
                                          self._grid_size)
        )

        Helpers.paint_grid_button(button, style.FIELD_BLUE)
        button.setObjectName("GridButton")
        button.setFixedSize(style.FIELD_ICON_SIZE + 10,
                            style.FIELD_ICON_SIZE + 10)
        button.setIconSize(QSize(style.FIELD_ICON_SIZE, style.FIELD_ICON_SIZE))

        # set the QSignalMapper's mapping to work with strings
        mapper.setMapping(button, textual_coordinates)
        # connecting the button's clicked signal to the QSignalMappers
        # mapped slot
        button.clicked.connect(mapper.map)
        # finally, add the button to the QGridLayout
        layout.addWidget(button, x, y)

    def _clear_layout(self, layout):
        # recursively deletes a QLayout's items making it ready to accept new
        # ones

        if layout is not None:
            while layout.count():
                child = layout.takeAt(0)

                if child.widget() is not None:
                    child.widget().deleteLater()
                elif child.layout() is not None:
                    self._clear_layout(child.layout())

    def _disconnect_mappers(self):
        # resets the QSignalMappers so that they can be used again

        dimensions = self._previous_grid_size.value

        for i in xrange(0, dimensions):
            for j in xrange(0, dimensions):
                button_left = self.resolve_grid_button(
                    self.ui.gridLayoutLeft,
                    Coordinates(i, j)
                )
                button_right = self.resolve_grid_button(
                    self.ui.gridLayoutRight,
                    Coordinates(i, j)
                )

                self.leftGridMapper.removeMappings(button_left)
                self.rightGridMapper.removeMappings(button_right)

        self.leftGridMapper.disconnect()
        self.rightGridMapper.disconnect()

    def _update_status_bar(self):
        session_phase = self.session.session_phase
        message = EnumConverters.session_phase_to_string_converter(
            session_phase
        )

        if session_phase == SessionPhase.fleet_layout:
            fleet = self.session.player_one.fleet
            current_ship = fleet.ships[self._current_ship_index]
            message += self.tr(", Current ship: {0} (size - {1})".format(
                EnumConverters.ship_type_to_string_converter(
                    current_ship.ship_type
                ),
                current_ship.ship_type.value)
            )

        self.statusLabel.setText(self.tr("Phase: " + message))

    def _paint_player_grid(self, grid_layout, player, reveal=False):
        # marks the grid's squares according to their state

        grid = player.grid
        dimensions = grid.grid_size.value

        if player.player_type == PlayerType.human:
            reveal = True

        for i in xrange(0, dimensions):
            for j in xrange(0, dimensions):
                current_square = grid.squares.item((i, j))
                owner = current_square.owner
                square_state = current_square.square_state
                button = self.resolve_grid_button(grid_layout,
                                                  Coordinates(i, j))

                if (
                    square_state == SquareState.vacant or
                    square_state == SquareState.unoccupiable
                ):
                    Helpers.paint_grid_button(button, style.FIELD_BLUE)
                elif square_state == SquareState.populated:
                    if reveal:
                        Helpers.paint_grid_button(button, style.FIELD_GRAY)
                    else:
                        Helpers.paint_grid_button(button, style.FIELD_BLUE)
                elif square_state == SquareState.hit:
                    if owner is not None:
                        if owner.ship_state == ShipState.damaged:
                            Helpers.paint_grid_button(button, style.FIELD_RED)
                        elif owner.ship_state == ShipState.sunk:
                            Helpers.paint_grid_button(button,
                                                      style.FIELD_BLACK)
                    else:
                        Helpers.paint_grid_button(button,
                                                  style.FIELD_LIGHT_BLUE)

    def _paint_both_player_grids(self, player_one, player_two, reveal=False):
            self._paint_player_grid(self.ui.gridLayoutLeft,
                                    player_one,
                                    reveal)
            self._paint_player_grid(self.ui.gridLayoutRight,
                                    player_two,
                                    reveal)

    def _declare_winner(self, winner):
        self.session.session_phase = SessionPhase.game_over
        self._is_new_game = False
        self._update_status_bar()

        player_type_textual = EnumConverters.\
            player_type_to_string_converter(winner.player_type)

        Helpers.raise_info(
            self,
            self.tr("Game over - {0} wins!".format(
                    player_type_textual))
        )

    @pyqtSlot(str, name="leftGridButtonClick")
    def leftGridButtonClick(self, textual_coordinates):
        """Slot used when the left grid's QCustomPushButton is clicked.

        :param textual_coordinates: position of the button represented as a
            string

        """
        numerical_coordinates = Coordinates.parse_coordinates(
            textual_coordinates, self._grid_size
        )
        session_phase = self._session.session_phase

        if session_phase == SessionPhase.fleet_layout:
            grid = self._session.player_one.grid
            fleet = self._session.player_one.fleet
            ship = fleet.ships[self._current_ship_index]

            if fleet.position_ship(grid,
                                   ship,
                                   self._current_orientation,
                                   numerical_coordinates):
                self._current_ship_index += 1

                if self._current_ship_index == len(fleet.ships):
                    self._session.session_phase = SessionPhase.battle

                    self._session.player_one.initialize_fleet()
                    self._session.player_two.initialize_fleet()

            self._update_status_bar()
        self._paint_player_grid(self.ui.gridLayoutLeft,
                                self._session.player_one)

    @pyqtSlot(str, name="rightGridButtonClick")
    def rightGridButtonClick(self, textual_coordinates):
        """Slot used when the right grid's QCustomPushButton is clicked.

        :param textual_coordinates: position of the button represented as a
            string

        """
        numerical_coordinates = Coordinates.parse_coordinates(
            textual_coordinates, self._grid_size
        )
        session_phase = self._session.session_phase
        player_one = self.session.player_one
        player_two = self.session.player_two

        if session_phase == SessionPhase.battle:
            if not player_one.shoot(player_two.grid, numerical_coordinates):
                return
            else:
                if player_two.fleet.is_sunk():
                    self._paint_both_player_grids(player_one, player_two, True)
                    self._declare_winner(player_one)
                    return

            if not player_two.shoot(player_one.grid):
                return
            else:
                if player_one.fleet.is_sunk():
                    self._paint_both_player_grids(player_one, player_two, True)
                    self._declare_winner(player_two)
                    return

            self._paint_both_player_grids(player_one, player_two)

    @pyqtSlot(name="menuNewGameClick")
    def menuNewGameClick(self):
        """Slot used when the "New Game" menu item is clicked.

        """
        raise_dialog = True
        new_game_dialog = NewGameDialog(self)
        if self._is_new_game:
            if not Helpers.raise_question(
                self,
                self.tr("Are you sure you want to start a new game?")
            ):
                raise_dialog = False
        if raise_dialog:
            self._previous_grid_size = self._grid_size
            new_game_dialog.exec_()

    @pyqtSlot(name="menuAboutClick")
    def menuAboutClick(self):
        """Slot used when the "About" menu item is clicked.

        """
        about_dialog = AboutDialog(self)
        about_dialog.exec_()

    @pyqtSlot(name="menuExitClick")
    def menuExitClick(self):
        """Slot used when the "Exit" menu item is clicked.

        """
        self.close()

    def closeEvent(self, e):
        """Overridden close event.

        :param e: an instance of type QCloseEvent

        """
        if self._is_new_game:
            if Helpers.raise_question(
                self,
                self.tr("Are you sure you want to quit?")
            ):
                e.accept()
            else:
                e.ignore()