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)
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