view mamba/habitats/levelmenu.py @ 539:c798ba211b6e

Display something when you haven't created any user levels
author Stefano Rivera <stefano@rivera.za.net>
date Sun, 18 Sep 2011 01:36:44 +0200
parents dc5fdd7b3ce5
children 2d3dee657638
line wrap: on
line source

"""Level menu."""

import pygame
from pygame.locals import KEYDOWN, K_PAGEUP, K_PAGEDOWN

from mamba.constants import ESCAPE_KEYS
from mamba.engine import Habitat, NewHabitatEvent, HabitatSetupEvent
from mamba.gamestate import done_levels
from mamba.data import get_official_levels, load_image
from mamba.level import Level
from mamba.widgets.base import GridContainer
from mamba.widgets.levelbutton import LevelButton
from mamba.widgets.overlay import OverlayButton
from mamba.widgets.text import TextWidget


OFFICIAL_LEVELS = []
LEVEL_CACHE = {}


class LevelMenu(Habitat):

    WIDTH = 5
    HEIGHT = 3

    def __init__(self):
        super(LevelMenu, self).__init__()
        self.level_buttons = {}
        self.page = 0

        self.bg_image = load_image('menus/level-menu.png')
        self.grid_container = GridContainer(width=self.WIDTH)
        self.container.add(self.grid_container)

        self.prev_button = self.add_box((9, 497), (52, 45), 'left',
                                        self.change_page, -1)
        self.next_button = self.add_box((738, 497), (54, 44), 'right',
                                        self.change_page, 1)
        self.add_box((12, 11), (142, 41), 'back', self.return_to_mainmenu)

        self.container.add_callback(KEYDOWN, self.keydown_event)

    def add_box(self, pos, size, overlay_name, clicked, *args):
        image = load_image('menus/submenu-%s.png' % overlay_name)
        box = OverlayButton(pygame.Rect(pos, size), image)
        box.add_callback('clicked', clicked, *args)
        self.container.add(box)
        return box

    def draw_background(self):
        self.surface.blit(self.bg_image, self.surface.get_rect())

    def on_enter(self):
        super(LevelMenu, self).on_enter()
        self._setup_levels()

    def dispatch(self, ev):
        if HabitatSetupEvent.matches(ev):
            self._setup_levels(ev)
            return
        super(LevelMenu, self).dispatch(ev)

    def _setup_levels(self, ev=None):
        if ev is not None:
            self.setup_levels()
        else:
            HabitatSetupEvent.post()

    def setup_level(self, i, name):
        level = self.get_level(name)
        button = LevelButton((94 + (100 + 27) * (i % self.WIDTH),
                              92 + (120 + 27) * (i // self.WIDTH)),
                             level)
        button.add_callback('clicked', self.level_selected, name)
        button.done = level.unique_name() in done_levels
        self.grid_container.add(button)
        self.level_buttons[name] = button

    def setup_levels(self):
        if not getattr(self, '_setup_gen', None):
            self._setup_gen = self._setup_levels_gen()
        try:
            self._setup_gen.next()
            HabitatSetupEvent.post()
        except StopIteration:
            self._setup_gen = None
            # We're done here.
            return

    def _setup_levels_gen(self):
        del self.grid_container.children[:]
        levels = list(self.list_levels())
        if self.page * self.WIDTH * self.HEIGHT > len(levels):
            self.page = len(levels) // self.WIDTH // self.HEIGHT
        elif self.page < 0:
            self.page = 0
        first = self.page * self.WIDTH * self.HEIGHT
        last = first + (self.WIDTH * self.HEIGHT)
        for i, name in enumerate(levels[first:last]):
            yield self.setup_level(i, name)

        if self.page == 0:
            self.prev_button.disable()
        else:
            self.prev_button.enable()
        if len(levels) <= last:
            self.next_button.disable()
        else:
            self.next_button.enable()
        self.container.defocus()

        if levels:
            # We now have widgets, let's give them focus
            self.container.adjust_focus(1)
        else:
            if self.level_namespace == 'user':
                self.container.add(TextWidget((80, 280),
                                   "You haven't created any levels yet.",
                                   fontsize=24, color='white'))
                self.container.add(TextWidget((80, 320),
                                   "We suggest you spend some time in the "
                                   "Level Editor.",
                                   fontsize=24, color='white'))

    def change_page(self, ev, widget, direction):
        self.container.defocus()
        self.page += direction
        self.setup_levels()
        return True

    def list_levels(self):
        raise NotImplementedError()

    def get_level(self, name):
        raise NotImplementedError()

    def level_selected(self, ev, widget, name):
        from mamba.habitats.level import LevelHabitat
        next_level = self.mk_next_level(name)
        NewHabitatEvent.post(
            LevelHabitat(widget.level, self.go_menu, next_level))
        return True

    def mk_next_level(self, name):
        return self.go_menu

    @classmethod
    def go_menu(cls):
        NewHabitatEvent.post(cls())

    def keydown_event(self, ev, widget):
        if ev.key in ESCAPE_KEYS:
            return self.return_to_mainmenu(ev, widget)
        if ev.key == K_PAGEUP:
            return self.change_page(ev, widget, -1)
        if ev.key == K_PAGEDOWN:
            return self.change_page(ev, widget, 1)

    def return_to_mainmenu(self, ev, widget):
        from mamba.habitats.mainmenu import MainMenu
        NewHabitatEvent.post(MainMenu())
        return True


class OfficialLevelMenu(LevelMenu):

    level_namespace = 'official'

    def mk_next_level(self, name):
        levels = self.list_levels()
        if name not in levels:
            return self.go_menu
        idx = levels.index(name) + 1
        if idx >= len(levels):
            return self.go_menu
        next_name = levels[idx]

        def next_level():
            from mamba.habitats.level import LevelHabitat
            next_level = self.mk_next_level(next_name)
            NewHabitatEvent.post(LevelHabitat(self.get_level(next_name),
                                              self.go_menu, next_level))

        return next_level

    def list_levels(self):
        if not OFFICIAL_LEVELS:
            OFFICIAL_LEVELS.extend(get_official_levels())
        return OFFICIAL_LEVELS[:]

    def get_level(self, name):
        if name not in LEVEL_CACHE:
            LEVEL_CACHE[name] = Level(name, self.level_namespace)
        return LEVEL_CACHE[name]