changeset 532:0667189a5973

New load level dialog.
author Simon Cross <hodgestar@gmail.com>
date Fri, 27 Nov 2009 21:28:20 +0000
parents 452cde9af2a2
children 7addf41b6abb
files gamelib/engine.py gamelib/loadlevel.py gamelib/mainmenu.py
diffstat 3 files changed, 137 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/gamelib/engine.py	Fri Nov 27 21:18:18 2009 +0000
+++ b/gamelib/engine.py	Fri Nov 27 21:28:20 2009 +0000
@@ -9,7 +9,6 @@
 import constants
 import mainmenu
 import helpscreen
-import loadlevel
 import level
 from misc import check_exit
 
@@ -20,14 +19,13 @@
         self.level = level.Level(level_name)
         self._open_window = None
         self.gameboard = None
-        self.level_loader = None
 
     def tick(self):
         """Tic toc."""
         pygame.time.wait(10)
 
-    def load_new_level(self):
-        self.level = self.level_loader.cur_level
+    def load_new_level(self, new_level):
+        self.level = new_level
 
     def open_window(self, window):
         """Open a widget as the main window."""
@@ -42,9 +40,9 @@
                 self.level)
         self.open_window(self.gameboard.get_top_widget())
 
-    def switch_gameboard(self, gameboard):
+    def switch_gameboard(self, new_gameboard):
         """Switch over to a new gameboard."""
-        self.gameboard = gameboard
+        self.gameboard = new_gameboard
         self.gameboard.disp = self.main_app
         self.gameboard.create_display()
         self.open_window(self.gameboard.get_top_widget())
@@ -52,7 +50,6 @@
     def set_main_menu(self):
         """Open the main menu"""
         self.scoreboard = gameover.ScoreTable(self.level)
-        self.level_loader = None
         main_menu = mainmenu.make_main_menu(self.level)
         self.open_window(main_menu)
 
@@ -61,11 +58,6 @@
         help_screen = helpscreen.make_help_screen(self.level)
         self.open_window(help_screen)
 
-    def set_level_screen(self):
-        """Open the Load Level screen"""
-        level_screen, self.level_loader = loadlevel.make_load_screen(self.level)
-        self.open_window(level_screen)
-
     def create_game_over(self):
         """Create and open the Game Over window"""
         level = self.gameboard.level
@@ -93,8 +85,6 @@
             return DayState(self.game)
         elif events_equal(e, GO_HELP_SCREEN):
             return HelpScreenState(self.game)
-        elif events_equal(e, GO_LEVEL_SCREEN):
-            return LevelScreenState(self.game)
         elif e.type is KEYDOWN:
             if e.key == K_ESCAPE:
                 return Quit(self.game)
@@ -103,6 +93,9 @@
                 return DayState(self.game)
             elif e.key == K_i:
                 return HelpScreenState(self.game)
+        elif e.type is DO_LOAD_LEVEL:
+            self.game.load_new_level(e.level)
+            return
         elif e.type is DO_LOAD_SAVEGAME:
             self.game.switch_gameboard(e.gameboard)
             e.gameboard.skip_next_start_day()
@@ -142,32 +135,6 @@
         pygame.display.update(update)
 
 
-class LevelScreenState(State):
-    def init(self):
-        sound.stop_background_music()
-        self.game.set_level_screen()
-
-    def event(self, e):
-        if e.type is KEYDOWN and e.key == K_ESCAPE:
-            return MainMenuState(self.game)
-        elif events_equal(e, GO_MAIN_MENU):
-            return MainMenuState(self.game)
-        elif events_equal(e, DO_LOAD_LEVEL):
-            self.game.load_new_level()
-            return MainMenuState(self.game)
-        elif e.type is not QUIT:
-            self.game.main_app.event(e)
-
-    def paint(self, screen):
-        screen.fill((0,0,0))
-        self.game.main_app.paint(screen)
-        pygame.display.flip()
-
-    def update(self, screen):
-        update = self.game.main_app.update(screen)
-        pygame.display.update(update)
-
-
 class DayState(State):
     def init(self):
         """Add some chickens to the farm"""
@@ -329,12 +296,11 @@
 START_NIGHT = pygame.event.Event(USEREVENT, name="START_NIGHT")
 GO_MAIN_MENU = pygame.event.Event(USEREVENT, name="GO_MAIN_MENU")
 GO_HELP_SCREEN = pygame.event.Event(USEREVENT, name="GO_HELP_SCREEN")
-GO_LEVEL_SCREEN = pygame.event.Event(USEREVENT, name="GO_LEVEL_SCREEN")
-DO_LOAD_LEVEL = pygame.event.Event(USEREVENT, name="DO_LEVEL_SCREEN")
 FAST_FORWARD = pygame.event.Event(USEREVENT, name="FAST_FORWARD")
 MOVE_FOX_ID = USEREVENT + 1
 MOVE_FOXES = pygame.event.Event(MOVE_FOX_ID, name="MOVE_FOXES")
 DO_LOAD_SAVEGAME = USEREVENT + 2
+DO_LOAD_LEVEL = USEREVENT + 3
 QUIT = pygame.event.Event(QUIT)
 
 # Due to the way pgu's loop timing works, these will only get proceesed
--- a/gamelib/loadlevel.py	Fri Nov 27 21:18:18 2009 +0000
+++ b/gamelib/loadlevel.py	Fri Nov 27 21:28:20 2009 +0000
@@ -4,98 +4,88 @@
 import os
 import pygame
 import level
-import engine
 import data
-import imagecache
 import gameboard
 import constants
 
-def make_load_screen(level):
-    """Create a screen for selecting the levels"""
-    load_screen = LoadScreen(level, width=600)
 
-    c = LoadContainer(align=0, valign=0)
-    c.add(load_screen, 0, 0)
-
-    return c, load_screen
+class LoadLevelDialog(gui.Dialog):
+    """Load level dialog."""
 
-class LoadContainer(gui.Container):
-    def paint(self, s):
-        pygame.display.set_caption('Load Level')
-        splash = imagecache.load_image("images/splash.png", ["lighten_most"])
-        pygame.display.get_surface().blit(splash, (0, 0))
-        gui.Container.paint(self, s)
+    def __init__(self, curr_level, load_func, cls="dialog"):
+        self.value = None
+        self.levels = self._populate_levels()
 
-class LoadScreen(gui.Document):
-    def __init__(self, start_level, **params):
-        gui.Document.__init__(self, **params)
+        self.main_style = {
+            'width': 300, 'height': 350
+        }
 
-        self.levels = []
-        self.cur_level = None
-        for name in os.listdir(data.filepath('levels/')):
-            if name.endswith('.conf'):
-                try:
-                    this_level = level.Level(name)
-                except RuntimeError:
-                    continue # Skip levels that fail to load
-                if os.path.exists(this_level.map):
-                    # Skip level if we can't see the map
-                    self.levels.append(this_level)
-                    if this_level.level_name == start_level.level_name:
-                        self.cur_level = this_level
+        td_style = {
+            'padding_left': 4,
+            'padding_right': 4,
+            'padding_top': 2,
+            'padding_bottom': 2,
+        }
 
-        if not self.cur_level:
-            self.cur_level = self.levels[0]
+        self.level_list = gui.List(**self.main_style)
+        level_names = self.levels.keys()
+        level_names.sort()
+        for name in level_names:
+            self.level_list.add(name, value=name)
+        self.level_list.set_vertical_scroll(0)
+        self.level_list.connect(gui.CHANGE, self._level_list_change)
 
+        self.image_container = gui.Container()
 
-        def done_pressed():
-            pygame.event.post(engine.DO_LOAD_LEVEL)
+        button_ok = gui.Button("Load Level")
+        button_ok.connect(gui.CLICK, self._click_ok)
+
+        button_cancel = gui.Button("Cancel")
+        button_cancel.connect(gui.CLICK, self._click_cancel)
 
-        def cancel_pressed():
-            pygame.event.post(engine.GO_MAIN_MENU)
-
-        def next_pressed():
-            self.next_level()
-
-        def prev_pressed():
-            self.prev_level()
+        body = gui.Table()
+        body.tr()
+        list_style = dict(self.main_style)
+        list_style.update(td_style)
+        body.td(self.level_list, style=list_style, valign=-1, rowspan=2)
+        body.td(self.image_container, style=td_style, colspan=2)
+        body.tr()
+        # putting in the extra spacer squashes the ok and cancel button
+        # up nicely
+        body.td(gui.Spacer(0, 0), style=td_style)
+        body.td(button_ok, style=td_style, align=1)
+        body.td(button_cancel, style=td_style, align=1)
 
-        self.next_button = gui.Button("Next Level >>")
-        self.next_button.connect(gui.CLICK, next_pressed)
-
-        self.prev_button = gui.Button("<< Prev Level")
-        self.prev_button.connect(gui.CLICK, prev_pressed)
+        title = gui.Label("Load Level ...", cls=cls + ".title.label")
+        gui.Dialog.__init__(self, title, body)
 
-        self.cancel_button = gui.Button("Cancel")
-        self.cancel_button.connect(gui.CLICK, cancel_pressed)
-
-        self.done_button = gui.Button("Load This Level")
-        self.done_button.connect(gui.CLICK, done_pressed)
-
-        self.render_level()
+        if curr_level.level_name in self.levels:
+            self.level_list.group.value = curr_level.level_name
+        elif level_names:
+            self.level_list.group.value = level_names[0]
 
-
-    def next_level(self):
-        pos = self.levels.index(self.cur_level) + 1
-        if pos == len(self.levels):
-            pos = 0
-        self.cur_level = self.levels[pos]
-        self.render_level()
+        self.connect(gui.CHANGE, self._load_level, load_func)
 
-    def prev_level(self):
-        pos = self.levels.index(self.cur_level) - 1
-        if pos == -1:
-            pos = len(self.levels) - 1
-        self.cur_level = self.levels[pos]
-        self.render_level()
+    def _populate_levels(self):
+        """Read list of levels from disk."""
+        levels = {}
+        for name in os.listdir(data.filepath('levels/')):
+            if not name.endswith('.conf'):
+                continue
+            try:
+                this_level = level.Level(name)
+            except RuntimeError:
+                # Skip levels that fail to load
+                continue
+            if not os.path.exists(this_level.map):
+                # Skip level if we can't see the map
+                continue
+            levels[this_level.level_name] = (this_level, None)
+        return levels
 
-    def render_level(self):
-        self.clear()
-        self.repaint()
-
-        board = gameboard.GameBoard(None, self.cur_level)
-
-        space = self.style.font.size(" ")
+    def _create_image_widget(self, curr_level):
+        """Create an image showing the contents of level file."""
+        board = gameboard.GameBoard(None, curr_level)
         w, h = board.tv.size
 
         map_image = pygame.Surface((constants.TILE_DIMENSIONS[0] * w,
@@ -104,35 +94,62 @@
         board.tv.paint(map_image)
 
         style = {
-                'width' : min(300, 7*w),
-                'height' : min(300, 7*h),
-                }
+            'width' : min(300, 7*w),
+            'height' : min(300, 7*h),
+        }
+
+        doc = gui.Document(style=self.main_style)
+        space = doc.style.font.size(" ")
+
+        doc.block(align=0)
+        doc.add(gui.Image(map_image, style=style))
 
-        image = gui.Image(map_image, style=style)
+        doc.block(align=0)
+        doc.add(gui.Label(curr_level.level_name, style={
+            'border_bottom': 1,
+            'margin_bottom': 5,
+            'margin_top': 5,
+        }))
 
-        self.block(align=0)
-        self.add(image)
+        doc.block(align=0)
+        for word in curr_level.goal.split():
+            doc.add(gui.Label(word))
+            doc.space(space)
+
+        return doc
+
+    def _level_list_change(self):
+        for w in self.image_container.widgets:
+            self.image_container.remove(w)
 
-        self.block(align=-1)
-        self.add(gui.Label(self.cur_level.level_name))
-        self.block(align=-1)
-        for word in self.cur_level.goal.split():
-            self.add(gui.Label(word))
-            self.space(space)
+        name = self.level_list.value
+        curr_level, widget = self.levels[name]
+        if widget is None:
+            widget = self._create_image_widget(curr_level)
+            self.levels[name] = (curr_level, widget)
+
+        self.image_container.add(widget, 0, 0)
+
+    def _click_ok(self):
+        self.value = self.level_list.value
+        if self.value:
+            self.send(gui.CHANGE)
+            self.close()
 
-        self.block(align=0)
-        # NB: pgu 's layout engine is sensitive to ordering here
-        self.add(self.prev_button, align=-1)
-        self.add(self.next_button, align=1)
-        self.add(self.done_button)
-        self.add(self.cancel_button)
+    def _click_cancel(self):
+        self.value = None
+        self.send(gui.CHANGE)
+        self.close()
 
-    def clear(self):
-        """Clear the document"""
-        for widget in self.widgets[:]:
-            self.remove(widget)
-        self.layout._widgets = []
-        self.layout.init()
+    def _load_level(self, load_func):
+        level = self.get_level()
+        if level is not None:
+            load_func(level)
+
+    def get_level(self):
+        if self.value is None:
+            return None
+        return self.levels[self.value][0]
 
 
 
--- a/gamelib/mainmenu.py	Fri Nov 27 21:18:18 2009 +0000
+++ b/gamelib/mainmenu.py	Fri Nov 27 21:28:20 2009 +0000
@@ -7,6 +7,7 @@
 import imagecache
 import gameboard
 import savegame
+import loadlevel
 
 def make_main_menu(level):
     """Create a main menu"""
@@ -31,6 +32,7 @@
     def __init__(self, level, **params):
         gui.Table.__init__(self, **params)
         self.mode = None
+        self.level_name = level.level_name
 
         def fullscreen_toggled():
             pygame.display.toggle_fullscreen()
@@ -42,7 +44,11 @@
             pygame.event.post(engine.START_DAY)
 
         def choose_level():
-            pygame.event.post(engine.GO_LEVEL_SCREEN)
+            def load_func(new_level):
+                pygame.event.post(pygame.event.Event(engine.DO_LOAD_LEVEL, level=new_level))
+                self.level_name = new_level.level_name
+                self.redraw()
+            loadlevel.LoadLevelDialog(level, load_func).open()
 
         def load_game():
             savegame.RestoreDialog(gameboard.GameBoard.restore_game).open()
@@ -63,10 +69,10 @@
         self.tr()
         self.td(change_button, **td_kwargs)
 
-        start_button = gui.Button(level.level_name)
-        start_button.connect(gui.CLICK, start_game)
+        self.start_button = gui.Button(level.level_name)
+        self.start_button.connect(gui.CLICK, start_game)
         self.tr()
-        self.td(start_button, **td_kwargs)
+        self.td(self.start_button, **td_kwargs)
 
         loadgame_button = gui.Button('Restore Game')
         loadgame_button.connect(gui.CLICK, load_game)
@@ -90,3 +96,6 @@
 
         self.tr()
         self.td(quit_button, **td_kwargs)
+
+    def redraw(self):
+        self.start_button.value = self.level_name