Esempio n. 1
0
 def open_file_browser(self, *arg):
     path = None
     if self.li.value:
         f = self.li.value
         if os.path.isdir(f):
             path =f
         else:
             path = os.path.dirname(f)
     d = EnhancedFileDialog(path=path, path_filter=self.path_filter)
     d.connect(gui.CHANGE, self.handle_file_browser_closed, d)
     d.open()
Esempio n. 2
0
class Application(gui.Desktop):

    def __init__(self, **params):
        super(Application, self).__init__(**params)
        self.connect(gui.QUIT, self.quit, None)

        self.widget = CTRLWidget(width=640,height=490)

        self._audio = {}
        self.muted = False

        self._mode = "play"
        self._game_state = "stopped"
        self._finish_event = None
        self.campaign = JikibanCampaign()
        self.campaign_lvl_no = -1
        self.fcounter = 0
        self.score = ScoreTracker()
        self.edit_level = None
        self.level = None
        self.auto_play = None
        self.group = None
        self.skip_till_time_jump = gui.Switch(value=False)
        self.skip_till_time_jump.connect(gui.CHANGE, self.toggle_auto_finish)
        self.game_window = GameWindow(resource_dirs=[ROOT_DIR])
        self.ctrl_widget = self.widget
        self.ctrl_widget.game_window = self.game_window
        self.play_ctrl = PlayKeyController(view=self.game_window)
        self.play_mctrl = MouseController(self.game_window)
        self.edit_ctrl = None
        self.edit_mctrl = EditMouseController(self.game_window)

        level_dir  = os.path.join(ROOT_DIR, "levels")
        self.open_campaign_d = EnhancedFileDialog(title_txt="Start Campaign",
                                                  button_txt="Start Campaign",
                                                  path=level_dir,
                                                  path_filter=LSF_FILTER)
        self.open_lvl_d = EnhancedFileDialog(title_txt="Play Level",
                                             button_txt="Play",
                                             path=level_dir,
                                             path_filter=LVL_FILTER)
        self.new_lvl_d = NewLevelDialog()
        self.open_campaign_d.connect(gui.CHANGE, self.load_campaign_action)
        self.open_lvl_d.connect(gui.CHANGE, self.action_open_lvl)
        self.new_lvl_d.connect(gui.CHANGE, self.new_map)

    def init(self, *args, **kwords):
        super(Application, self).init(*args, **kwords)

        c = self.widget

        game_window = self.game_window
        spacer = 8
        from_top = 0
        from_left = spacer

        pygame.mixer.init()

        pygame.key.set_repeat() # Disable repeats

        self._audio["time-paradox"]  = pygame.mixer.Sound("sound/123921__silencer1337__machinefail.wav")
        self._audio["game-complete"] = pygame.mixer.Sound("sound/90138__pierrecartoons1979__win1.wav")
        self._audio["background"] = pygame.mixer.Sound("sound/POL-sand-and-water-short_repeat.wav")

        self.play_sound("background", loops = -1, volume = 1.0/3)

        menus = gui.Menus([
                ('File/Load campaign', self.open_campaign_d.open, None),
                ('File/Load level', self.open_lvl_d.open, None),
                ('File/Quit', self.quit, None),
                ('Fun/Change theme', self.change_theme, None),
                ('Help/Tutorial', self.open_tutorial, None),
        ])
        c.add(menus, 0, from_top)
        menus.rect.w, menus.rect.h = menus.resize()
        from_top += menus.rect.h + spacer

        c.add(game_window, spacer, from_top)
        game_window.rect.w, game_window.rect.h = game_window.resize()

        from_top += game_window.rect.h + spacer

        c.add(self.score, spacer, from_top)
        self.score.rect.w, self.score.rect.h = self.score.resize()
        from_top += self.score.rect.h + spacer


        play_ctrls = make_game_ctrls(self, width=640, height=490 - from_top)
        edit_ctrls = make_edit_ctrls(self, width=640, height=490 - from_top)

        self.group = gui.Group(name='ctrl-mode', value=self._mode)

        play_mode = gui.Tool(self.group, gui.Label("Play-mode"), "play")
        edit_mode = gui.Tool(self.group, gui.Label("Edit-mode"), "edit")

        c.add(play_mode, spacer, from_top)
        play_mode.rect.w, play_mode.rect.h = play_mode.resize()

        c.add(edit_mode, 2 * spacer + play_mode.rect.w, from_top)
        edit_mode.rect.w, edit_mode.rect.h = edit_mode.resize()

        from_top += play_mode.rect.h + spacer

        # edit-mode has no key ctrl, so always init this to play_ctrl
        self.ctrl_widget.key_ctrl = self.play_ctrl

        if app.mode == "play":
            w = gui.ScrollArea(play_ctrls)
            self.ctrl_widget.mouse_ctrl = self.play_mctrl
            self.ctrl_widget.mouse_ctrl.active = True
        else:
            w = gui.ScrollArea(edit_ctrls)
            self.ctrl_widget.mouse_ctrl = self.edit_mctrl
            self.ctrl_widget.mouse_ctrl.active = True


        def switch_mode():
            self._mode = self.group.value
            self.ctrl_widget.mouse_ctrl.active = False
            if self.mode == "play":
                w.widget = play_ctrls
                self.ctrl_widget.mouse_ctrl = self.play_mctrl
                self.ctrl_widget.key_ctrl = self.play_ctrl
                if self.level:
                    self.game_window.use_level(self.level, grid=False)
            else:
                self.ctrl_widget.mouse_ctrl = self.edit_mctrl
                w.widget = edit_ctrls
                if self.edit_level:
                    self.game_window.use_level(self.edit_level, grid=True)

            self.ctrl_widget.mouse_ctrl.active = True

        self.group.connect(gui.CHANGE, switch_mode)

        c.add(w, 0, from_top)
        w.rect.w, w.rect.h = w.resize()

        self.resize()
        self.repaint()

    @property
    def mode(self):
        return self._mode

    @mode.setter
    def mode(self, val):
        if self.group:
            self.group.value = val
        else:
            self._mode = val

    def reset_clone(self, *args):
        if self.auto_play:
            return
        if self.level:
            self.level.perform_move('reset-time-jump')

    def reset_level(self, *args):
        if self.auto_play:
            return
        if self.level:
            self.level.perform_move('reset-level')

    def action_open_lvl(self):
        self.load_level(self.open_lvl_d.value)

    def next_level(self):
        if self.campaign_lvl_no != -1:
            self.campaign_lvl_no += 1
            if self.campaign_lvl_no < len(self.campaign):
                self.load_level(self.campaign[self.campaign_lvl_no])

    def game_event(self, ge):
        event_text = self.level.get_metadata_raw("tutorial-%s" % ge.event_type)
        if event_text:
            title, msg = event_text.split("\n .\n", 1)
            msg = msg.replace("\n .\n", "\n\n")
            MessageDialog(msg, title).open()
        if (ge.event_type != "time-jump" and ge.event_type != "end-of-turn" and
              ge.event_type != "game-complete" and ge.event_type != "time-paradox"):
            return

        # FIXME: If the player types fast (i.e. "enqueues a lot of
        # moves", we will receive these events _waaaaay_ ahead of the
        # animation.  Generally we are here before the animation triggering
        # this event has even started (sort of okay for time-paradox)
        if ge.event_type == "game-complete":
            self._game_state = "complete"
            self._finish_event = ge
        elif ge.event_type == "time-paradox":
            self._game_state = "time-paradox"
            self._finish_event = ge

        if not self.skip_till_time_jump.value:
            return
        if ge.event_type != "end-of-turn":
            self.auto_play = None
            return
        if self.level.active_player or self.auto_play:
            return
        self.fcounter = 0
        self.auto_play = itertools.repeat("skip-turn")

    def toggle_auto_finish(self, *args):
        nvalue = self.skip_till_time_jump.value
        if not nvalue:
            self.auto_play = None
        if nvalue and self.mode == "play" and not self.auto_play and self.level:
            if self.level.turn[0] > 0 and not self.level.active_player:
                self.fcounter = 0
                self.auto_play = itertools.repeat("skip-turn")

    def load_campaign_action(self):
        return self.load_campaign(self.open_campaign_d.value)

    def load_campaign(self, fname):
        self.auto_play = None
        campaign = JikibanCampaign()
        try:
            campaign.load_campaign(fname)
        except IOError as e:
            self._show_error(str(e), "Cannot load campaign")
            return
        if len(campaign) < 1:
            self._show_error("The campaign does not have any levels in it",
                             "Empty campaign")
            return

        self.campaign = campaign
        self.campaign_lvl_no = 0
        self.load_level(campaign[0])

    def load_level(self, fname):
        self.auto_play = None
        edit_level = EditableLevel()
        level = Level()
        try:
            edit_level.load_level(fname)
        except IOError as e:
            self._show_error(str(e), "Cannot load map")
            return

        try:
            level.init_from_level(edit_level)
        except (IOError, ValueError) as e:
            if self.mode != "edit":
                self._show_error(str(e), "Cannot play map!")
                return
            level = None

        self._game_state = "stopped"
        self.edit_level = edit_level
        self.edit_mctrl.level = edit_level

        self.play_ctrl.edit_level = edit_level

        if level:
            self.level = level
            self.level.add_event_listener(self.game_event)
            self.play_ctrl.level = level
            self.play_mctrl.level = level
            sc = functools.partial(self.score.update_score, self.level)

        lvl = edit_level
        grid = True
        if self.mode == "play":
            lvl = level
            grid = False

        self.game_window.use_level(lvl, grid=grid)
        if level:
            level.add_event_listener(sc)
            # must be done after game_window.use_level
            level.start()
            self._game_state = "running"


    def _show_error(self, body_msg, title_msg):
        MessageDialog(body_msg, title_msg).open()

    def play_solution(self, *args):
        if not self.level:
            return

        self.skip_till_time_jump.value = False

        sol = self.level.get_metadata_raw("solution")
        if not sol:
            msg = "Solution for %s is not available" % self.level.name
            self._show_error(msg, "No solution available!")
            return
        self.reset_level()
        print "Playing solution"
        self.auto_play = solution2actions(sol)
        self.fcounter = 0

    def new_map(self):
        self.new_lvl_d.close()
        res = self.new_lvl_d.value
        edit_level = self.edit_level
        can_rel = True
        trans = None

        if edit_level is None:
            can_rel = False
            edit_level = EditableLevel()

        def _compute_value(name, s):
            res = 0
            if s and (s[0] == "+" or s[0] == "-" or s == "0"):
                # +x or -x or 0 is assumed to be relative (the latter being
                # short for "+0")
                if not can_rel:
                    raise ValueError("Cannot do relative size based on non-existent map: %s=%s" \
                                         % (name, s))
                if name == "width":
                    return edit_level.width + int(s)
                return edit_level.height + int(s)
            else:
                return int(s.lstrip("="))

        try:
            width = _compute_value("width", res["width"].value)
            height = _compute_value("height", res["height"].value)
            clear = res["clear"].value
            if not clear:
                trans = Position(int(res["trans-width"].value),
                                 int(res["trans-height"].value))
        except (TypeError, ValueError) as e:
            self._show_error(str(e), "Cannot create map")
            return

        edit_level.new_map(width, height, translate=trans)

        self.edit_level = edit_level
        self.play_ctrl.edit_level = edit_level
        self.edit_mctrl.level = edit_level
        self.game_window.use_level(edit_level, grid=True)

    def chg_edit_mode(self, mode):
        if mode != "none":
            self.edit_mctrl.brush_mode = "field-brush"
            self.edit_mctrl.field_tool = mode
        else:
            self.edit_mctrl.brush_mode = "none"

    def show_hint(self):
        if self.level:
            h = self.level.get_metadata_raw("description")
            if h is not None:
                d = MessageDialog(h, title="Hint")
                d.open();
            else:
                d = MessageDialog("No hint available", title="Sorry")
                d.open();

    def save_level(self, *args):
        if not self.edit_level:
            return
        d = self.edit_level.name
        # Keep SelectFileDialog here because that dialog makes it
        # easier to reuse the file name (which is a way of making
        # the lack of "save" vs "save as" less painful).
        sld = SelectFileDialog("Save", "Save", "Save level", default_file=d,
                               path_filter=LVL_FILTER)
        sld.connect(gui.CHANGE, self.write_level, sld)
        sld.open()

    def write_level(self, dialog):
        try:
            name = dialog.value['fname'].value
            self.edit_level.print_lvl(name)
            self.edit_level.name = name
            MessageDialog("Saved level to %s" % os.path.basename(name),
                          "Level saved").open()
        except IOError as e:
            self._show_error(str(e), "Cannot save map")

    def play_edit_level(self, *args):
        if not self.edit_level:
            return
        level = Level()
        try:
            level.init_from_level(self.edit_level)
        except ValueError as e:
            self._show_error(str(e), "Cannot play map")
            return
        self.mode = "play"
        self._game_state = "stopped"
        self._finish_event = None
        self.level = level
        self.level.add_event_listener(self.game_event)
        self.play_ctrl.level = self.level
        self.game_window.use_level(self.level, grid=False)
        sc = functools.partial(self.score.update_score, self.level)
        self.level.add_event_listener(sc)
        self._game_state = "running"
        self.level.start()

    def play_sound(self, sound, loops = 0, volume = 1):
        if sound not in self._audio:
            # Emit this even if we are muted (for error finding)
            print "W: Unknown sound %s" % sound
            return
        if self.muted:
            return
        self._audio[sound].set_volume(volume)
        self._audio[sound].play(loops)

    def open_tutorial(self, *args):
        t = Tutorial()
        t.open()

    def loop(self):
        self.game_window.process_game_events()
        if not self.game_window.pending_animation:
            # No pending animation - is the game finished?
            if self._game_state == "complete":
                self._game_state = "stopped" # do this only once!
                self._finish_event = None
                print "Your score is: %d" % self.level.score
                self.play_sound("game-complete")
                self.auto_play = None
                self._finish_event = None
                self.next_level()
            elif self._game_state == "time-paradox":
                ge = self._finish_event
                self._finish_event = None
                self._game_state = "stopped" # do this only once!
                self.play_sound("time-paradox")
                self.auto_play = None
                self._show_error(ge.reason, "Time paradox or non-determinism")
            elif self.mode == "play" and self._game_state == "running":
                self.ctrl_widget.key_ctrl.tick_event()

            # The fcounter is to give a slight delay to auto-playing
            # (else every move happens as fast as possible...)
            self.fcounter = (self.fcounter + 1) % 4
            if not self.fcounter:
                if self.auto_play and self.mode == "play":
                    act = next(self.auto_play, None)
                    if not act:
                        self.auto_play = None
                    else:
                        self.level.perform_move(act)
        else:
            self.fcounter = 0
        super(Application, self).loop()

    def change_theme(self, *args):
        theme = self.game_window.tileset
        if theme == "tileset":
            theme = "snowytileset"
        else:
            theme = "tileset"
        self.game_window.tileset = theme