예제 #1
0
class DialogComponent:
    """
    A graphical Pygame component that represents a dialog tree

    Display this component to the screen by blitting its publicly accessible surface. Make sure to call redraw() so that
    the graphics are updated. Call update(ms) to have it respond to time passing.
    """
    def __init__(self, surface: Surface, dialog_font: Font, choice_font: Font,
                 images: Dict[str, Surface], animations: Dict[str,
                                                              List[Surface]],
                 sound_player: SoundPlayer, dialog_graph: DialogGraph,
                 picture_size: Vec2, select_blip_sound_id: str):
        self._validate_inputs(dialog_graph, images, sound_player)
        self.surface = surface
        self._sound_player = sound_player
        self._dialog_graph = dialog_graph

        self._current_dialog_node = self._dialog_graph.current_node()

        background_id = self._dialog_graph.background_image_id
        background = images[background_id] if background_id else None
        self._ui = Ui(surface=surface,
                      picture_size=picture_size,
                      dialog_node=self._current_dialog_node,
                      dialog_font=dialog_font,
                      choice_font=choice_font,
                      images=images,
                      animations=animations,
                      sound_player=sound_player,
                      background=background,
                      select_blip_sound_id=select_blip_sound_id)
        self._play_dialog_sound()

    @staticmethod
    def _validate_inputs(dialog_graph: DialogGraph, images: Dict[str, Surface],
                         sound_player: SoundPlayer):
        for node in dialog_graph.nodes():
            if node.graphics.image_ids:
                for image_id in node.graphics.image_ids:
                    if image_id not in images:
                        raise ValueError(
                            f"Invalid config! Graph node '{node.node_id}' refers to missing image: '{image_id}'"
                        )
            if node.sound_id:
                if not sound_player.has_sound(node.sound_id):
                    raise ValueError(
                        f"Invalid config! Graph node '{node.node_id}' refers to missing sound: '{node.sound_id}"
                    )
        background_id = dialog_graph.background_image_id
        if background_id and background_id not in images:
            raise ValueError(
                f"Invalid config! Graph refers to missing background image: '{background_id}'"
            )

    def update(self, elapsed_time: Millis):
        self._ui.update(elapsed_time)
        self._sound_player.update(elapsed_time)

    def skip_text(self):
        self._ui.skip_text()

    def move_choice_selection(self, delta: int):
        self._ui.move_choice_highlight(delta)

    def select_choice_at_position(self, ui_coordinates: Vec2):
        chosen_index = self._ui.choice_button_at_position(ui_coordinates)
        if chosen_index is not None:
            self._ui.set_highlighted_choice(chosen_index)

    def commit_selected_choice(self):
        chosen_index = self._ui.highlighted_choice()
        if chosen_index is not None:
            self._commit_choice(chosen_index)

    def commit_choice_at_position(self, ui_coordinates: Vec2):
        chosen_index = self._ui.choice_button_at_position(ui_coordinates)
        if chosen_index is not None:
            self._commit_choice(chosen_index)

    def _commit_choice(self, chosen_index: int):
        self._dialog_graph.make_choice(chosen_index)
        self._current_dialog_node = self._dialog_graph.current_node()
        self._play_dialog_sound()
        self._ui.set_dialog(self._current_dialog_node)

    def current_node_id(self) -> str:
        return self._current_dialog_node.node_id

    def _play_dialog_sound(self):
        self._sound_player.stop_all_playing_sounds()
        if self._current_dialog_node.sound_id:
            self._sound_player.play(self._current_dialog_node.sound_id)

    def redraw(self):
        self._ui.redraw()