Exemple #1
0
    def __init__(self, name, flip_image=False, obey_gravity=False):
        super(Sprite, self).__init__()

        # Signals
        self.moved = Signal()
        self.direction_changed = Signal()
        self.reverse_gravity_changed = Signal()

        # State
        self.rect = pygame.Rect(0, 0, 0, 0)
        self.quad_trees = set()
        self.layer = None
        self.name = name
        self.image = None
        self.visible = 1
        self.dirty = 2
        self.velocity = (0, 0)
        self.obey_gravity = obey_gravity
        self.reverse_gravity = False
        self.lethal = False
        self.falling = False
        self.collidable = True
        self.grabbed = False
        self.grabbable = False
        self.should_check_collisions = False
        self.use_pixel_collisions = False
        self.flip_image = flip_image
        self.collision_rects = []
        self.collision_masks = []
        self._colliding_objects = set()
        self._direction = Direction.RIGHT
        self._flipped_images = {}
Exemple #2
0
class Button(Sprite):
    def __init__(self, name):
        super(Button, self).__init__(name)
        self.pressed = Signal()

    def handle_collision(self, *args, **kwargs):
        self.pressed.emit()
Exemple #3
0
class Effect(object):
    def __init__(self):
        self.timer = None
        self.timer_ms = 150

        # Signals
        self.started = Signal()
        self.stopped = Signal()

    def pre_start(self):
        pass

    def pre_stop(self):
        pass

    def start(self):
        assert not self.timer
        self.pre_start()
        self.timer = Timer(self.timer_ms, self.on_tick)
        self.timer.start()
        self.started.emit()

    def stop(self):
        assert self.timer
        self.pre_stop()
        self.timer.stop()
        self.timer = None
        self.stopped.emit()

    def on_tick(self):
        pass
Exemple #4
0
    def __init__(self):
        self.timer = None
        self.timer_ms = 150

        # Signals
        self.started = Signal()
        self.stopped = Signal()
Exemple #5
0
    def __init__(self):
        super(Player, self).__init__('player', flip_image=True,
                                     obey_gravity=True)
        self.engine = get_engine()
        self.should_check_collisions = True

        # Sprites
        self.propulsion_below = Sprite("propulsion_below", flip_image=True)
        self.propulsion_below.collidable = False
        self.propulsion_below.visible = 0
        self.tractor_beam = TractorBeam(self)

        # State
        self.jumping = False
        self.hovering = False
        self.block_events = False
        self.jump_origin = None
        self.hover_time_ms = 0
        self.health = self.MAX_HEALTH
        self.lives = self.MAX_LIVES
        self.last_safe_spot = None
        self.vehicle = None
        self.vehicle_move_cnx = None

        # Signals
        self.health_changed = Signal()
        self.lives_changed = Signal()

        self.direction_changed.connect(self.on_direction_changed)
        self.reverse_gravity_changed.connect(self.on_reverse_gravity_changed)
Exemple #6
0
class ScreenFlashEffect(ScreenEffect):
    def __init__(self, *args, **kwargs):
        super(ScreenFlashEffect, self).__init__(*args, **kwargs)
        self.fade_out = True
        self.color = (255, 255, 255)

        self.flash_peaked = Signal()

    def pre_start(self):
        self.hit_peak = False
        self.alpha = 100
        self.sprite.show()

        self._fill(0)

    def _fill(self, alpha):
        self.sprite.image.fill((self.color[0], self.color[1], self.color[2],
                                alpha))

    def on_tick(self):
        self._fill(self.alpha)

        if self.hit_peak:
            self.alpha = max(self.alpha - 20, 0)
        else:
            self.alpha = min(self.alpha + 20, 255)

        if self.alpha == 255:
            self.hit_peak = True
            self.flash_peaked.emit()

            if not self.fade_out:
                self.stop()
        elif self.alpha <= 0:
            self.stop()
Exemple #7
0
class Button(Sprite):
    def __init__(self, name):
        super(Button, self).__init__(name)
        self.pressed = Signal()

    def handle_collision(self, *args, **kwargs):
        self.pressed.emit()
Exemple #8
0
    def __init__(self, name, flip_image=False, obey_gravity=False):
        super(Sprite, self).__init__()

        # Signals
        self.moved = Signal()
        self.direction_changed = Signal()
        self.reverse_gravity_changed = Signal()

        # State
        self.rect = pygame.Rect(0, 0, 0, 0)
        self.quad_trees = set()
        self.layer = None
        self.name = name
        self.image = None
        self.visible = 1
        self.dirty = 2
        self.velocity = (0, 0)
        self.obey_gravity = obey_gravity
        self.reverse_gravity = False
        self.lethal = False
        self.falling = False
        self.collidable = True
        self.grabbed = False
        self.grabbable = False
        self.should_check_collisions = False
        self.use_pixel_collisions = False
        self.flip_image = flip_image
        self.collision_rects = []
        self.collision_masks = []
        self._colliding_objects = set()
        self._direction = Direction.RIGHT
        self._flipped_images = {}
Exemple #9
0
    def __init__(self):
        self.pages = []
        self.next_page = 0
        self.current_page = None
        self.allow_escape = True

        # Signals
        self.done = Signal()
Exemple #10
0
    def __init__(self, engine):
        self.engine = engine
        self.time_periods = []
        self.active_area = None
        self.music = None
        self.crossover_timer = None
        self.crossovers = []
        self.pending_crossovers = {}
        self.next_crossover_id = 0

        self.engine.tick.connect(self.on_tick)

        # Signals
        self.area_changed = Signal()
        self.time_period_changed = Signal()
Exemple #11
0
    def __init__(self):
        self.pages = []
        self.next_page = 0
        self.current_page = None
        self.allow_escape = True

        # Signals
        self.done = Signal()
Exemple #12
0
    def __init__(self, *args, **kwargs):
        super(Item, self).__init__(obey_gravity=True, *args, **kwargs)
        self.should_check_collisions = True
        self.grabbable = True
        self.flip_image = True

        # Signals
        self.grab_changed = Signal()
Exemple #13
0
class EventBox(object):
    def __init__(self, area):
        self.area = area
        self.rects = []
        area.register_for_events(self)
        self.entered_objects = set()

        # Signals
        self.event_fired = Signal()
        self.object_moved = Signal()
        self.object_entered = Signal()
        self.object_exited = Signal()

    def watch_object_moves(self, obj):
        obj.moved.connect(lambda dx, dy: self.handle_object_move(obj))

    def handle_event(self, event):
        return self.event_fired.emit(event)

    def handle_object_move(self, obj):
        if obj.layer.area != self.area:
            return

        if obj.rect.collidelist(self.rects) != -1:
            if obj not in self.entered_objects:
                self.entered_objects.add(obj)
                self.object_entered.emit(obj)

            self.object_moved.emit(obj)
        elif obj in self.entered_objects:
            self.entered_objects.remove(obj)
            self.object_exited.emit(obj)
Exemple #14
0
    def __init__(self, area):
        self.area = area
        self.rects = []
        area.register_for_events(self)
        self.entered_objects = set()

        # Signals
        self.event_fired = Signal()
        self.object_moved = Signal()
        self.object_entered = Signal()
        self.object_exited = Signal()
Exemple #15
0
    def __init__(self, engine):
        pygame.font.init()

        self.ready = Signal()

        self.engine = engine
        self.size = engine.screen.get_size()
        self.surface = pygame.Surface(self.size).convert_alpha()
        self.widgets = []
        self.timers = []

        self.default_font = get_font_filename()
        #self.default_font = pygame.font.get_default_font()
        self.font = pygame.font.Font(self.default_font, 20)
        self.small_font = pygame.font.Font(self.default_font, 16)

        self.engine.level_changed.connect(self.on_level_changed)

        self.control_panel = None
        self.paused_textbox = None
        self.confirm_quit_box = None
Exemple #16
0
class Cutscene(object):
    def __init__(self):
        self.pages = []
        self.next_page = 0
        self.current_page = None
        self.allow_escape = True

        # Signals
        self.done = Signal()

    def start(self):
        self.next_page = 0
        self.show_next_page()

    def stop(self):
        self.next_page = -1
        self.current_page.stop()
        self.current_page = None
        self.done.emit()

    def show_next_page(self):
        if self.next_page == len(self.pages):
            self.done.emit()
        elif self.next_page >= 0:
            self.current_page = self.pages[self.next_page]
            self.next_page += 1
            self.current_page.done.connect(self.show_next_page)
            self.current_page.start()

    def draw(self, surface):
        if self.current_page:
            self.current_page.draw(surface)

    def handle_event(self, event):
        if event.type == KEYDOWN:
            if self.allow_escape and event.key == K_ESCAPE:
                self.stop()
            elif event.key in (K_SPACE, K_RETURN, K_RIGHT):
                self.current_page.stop()
Exemple #17
0
class Cutscene(object):
    def __init__(self):
        self.pages = []
        self.next_page = 0
        self.current_page = None
        self.allow_escape = True

        # Signals
        self.done = Signal()

    def start(self):
        self.next_page = 0
        self.show_next_page()

    def stop(self):
        self.next_page = -1
        self.current_page.stop()
        self.current_page = None
        self.done.emit()

    def show_next_page(self):
        if self.next_page == len(self.pages):
            self.done.emit()
        elif self.next_page >= 0:
            self.current_page = self.pages[self.next_page]
            self.next_page += 1
            self.current_page.done.connect(self.show_next_page)
            self.current_page.start()

    def draw(self, surface):
        if self.current_page:
            self.current_page.draw(surface)

    def handle_event(self, event):
        if event.type == KEYDOWN:
            if self.allow_escape and event.key == K_ESCAPE:
                self.stop()
            elif event.key in (K_SPACE, K_RETURN, K_RIGHT):
                self.current_page.stop()
Exemple #18
0
    def __init__(self, screen):
        set_engine(self)

        # Signals
        self.level_changed = Signal()
        self.tick = Signal()

        # State and objects
        self.active_level = None
        self.active_cutscene = None
        self.paused = False
        self.screen = screen
        self.clock = pygame.time.Clock()
        self.player = Player()
        self.ui_manager = UIManager(self)
        self.camera = None

        # Debug flags
        self.debug_rects = False
        self.god_mode = False
        self.show_debug_info = False

        self.area_changed_cnx = None
        self.ui_ready_cnx = None
Exemple #19
0
    def __init__(self, screen):
        set_engine(self)

        # Signals
        self.level_changed = Signal()
        self.tick = Signal()

        # State and objects
        self.active_level = None
        self.active_cutscene = None
        self.paused = False
        self.screen = screen
        self.clock = pygame.time.Clock()
        self.player = Player()
        self.ui_manager = UIManager(self)
        self.camera = None

        # Debug flags
        self.debug_rects = False
        self.god_mode = False
        self.show_debug_info = False

        self.area_changed_cnx = None
        self.ui_ready_cnx = None
Exemple #20
0
    def __init__(self, engine):
        pygame.font.init()

        self.ready = Signal()

        self.engine = engine
        self.size = engine.screen.get_size()
        self.surface = pygame.Surface(self.size).convert_alpha()
        self.widgets = []
        self.timers = []

        self.default_font = get_font_filename()
        # self.default_font = pygame.font.get_default_font()
        self.font = pygame.font.Font(self.default_font, 20)
        self.small_font = pygame.font.Font(self.default_font, 16)

        self.engine.level_changed.connect(self.on_level_changed)

        self.control_panel = None
        self.paused_textbox = None
        self.confirm_quit_box = None
Exemple #21
0
    def __init__(self, *args, **kwargs):
        super(ScreenFlashEffect, self).__init__(*args, **kwargs)
        self.fade_out = True
        self.color = (255, 255, 255)

        self.flash_peaked = Signal()
Exemple #22
0
class Sprite(pygame.sprite.DirtySprite):
    FALL_SPEED = 6

    def __init__(self, name, flip_image=False, obey_gravity=False):
        super(Sprite, self).__init__()

        # Signals
        self.moved = Signal()
        self.direction_changed = Signal()
        self.reverse_gravity_changed = Signal()

        # State
        self.rect = pygame.Rect(0, 0, 0, 0)
        self.quad_trees = set()
        self.layer = None
        self.name = name
        self.image = None
        self.visible = 1
        self.dirty = 2
        self.velocity = (0, 0)
        self.obey_gravity = obey_gravity
        self.reverse_gravity = False
        self.lethal = False
        self.falling = False
        self.collidable = True
        self.grabbed = False
        self.grabbable = False
        self.should_check_collisions = False
        self.use_pixel_collisions = False
        self.flip_image = flip_image
        self.collision_rects = []
        self.collision_masks = []
        self._colliding_objects = set()
        self._direction = Direction.RIGHT
        self._flipped_images = {}

    def __repr__(self):
        return 'Sprite %s (%s, %s, %s, %s)' % \
               (self.name, self.rect.left, self.rect.top,
                self.rect.width, self.rect.height)

    def set_reverse_gravity(self, reverse_gravity):
        if self.reverse_gravity != reverse_gravity:
            self.reverse_gravity = reverse_gravity
            self.velocity = (self.velocity[0], -self.velocity[1])
            self.update_image()
            self.reverse_gravity_changed.emit()

    def _set_direction(self, direction):
        if self.direction != direction:
            self._direction = direction
            self.direction_changed.emit()
            self.update_image()
    direction = property(lambda self: self._direction, _set_direction)

    def show(self):
        if not self.visible:
            self.visible = 1
            self.dirty = 2
            self.layer.update_sprite(self)

    def hide(self):
        if self.visible:
            self.visible = 0
            self.dirty = 1
            self.layer.update_sprite(self)

    def remove(self):
        self.hide()
        self.layer.remove(self)

    def fall(self):
        if self.falling or not self.obey_gravity:
            return

        self.falling = True

        if self.reverse_gravity:
            self.velocity = (self.velocity[0], -self.FALL_SPEED)
        else:
            self.velocity = (self.velocity[0], self.FALL_SPEED)

    def stop_falling(self):
        if self.obey_gravity:
            self.falling = False
            self.velocity = (self.velocity[0], 0)

    def update_image(self):
        self.image = self.generate_image()
        assert self.image
        self.rect.size = self.image.get_rect().size

    def generate_image(self):
        if not self.name:
            # Must be a custom sprite.
            return self.image

        image = load_image(self.name)

        if (self.flip_image and
            (self._direction == Direction.LEFT or self.reverse_gravity)):
            flip_h = (self.direction == Direction.LEFT)
            flip_v = self.reverse_gravity

            key = (self.name, flip_h, flip_v)

            if key not in self._flipped_images:
                self._flipped_images[key] = \
                    pygame.transform.flip(image, flip_h, flip_v)

            image = self._flipped_images[key]

        return image

    def move_to(self, x, y, check_collisions=False):
        self.move_by(x - self.rect.x, y - self.rect.y, check_collisions)

    def move_by(self, dx, dy, check_collisions=True):
        if check_collisions:
            if dx:
                self._move(dx=dx)

            if dy:
                self._move(dy=dy)
        else:
            self.rect.move_ip(dx, dy)

        self.on_moved(dx, dy)

    def _move(self, dx=0, dy=0):
        self.rect.move_ip(dx, dy)
        self.rect.left = max(self.rect.left, 0)
        self.rect.right = min(self.rect.right, self.layer.area.size[0])
        self.check_collisions(dx, dy)

    def check_collisions(self, dx=0, dy=0):
        old_colliding_objects = set(self._colliding_objects)
        self._colliding_objects = set()

        for obj, self_rect, obj_rect in self.get_collisions():
            if (self_rect == self.rect and
                self.should_adjust_position_with(obj, dx, dy)):
                self.position_beside(obj_rect, dx, dy)

            obj.handle_collision(self, obj_rect, dx, dy)
            self.on_collision(dx, dy, obj, self_rect, obj_rect)
            self._colliding_objects.add(obj)

        for obj in old_colliding_objects.difference(self._colliding_objects):
            obj.handle_stop_colliding(self)

    def should_adjust_position_with(self, obj, dx, dy):
        return True

    def position_beside(self, rect, dx, dy):
        if dy < 0:
            self.rect.top = rect.bottom
        elif dy > 0:
            self.rect.bottom = rect.top
        elif dx < 0:
            self.rect.left = rect.right
        elif dx > 0:
            self.rect.right = rect.left

    def get_collisions(self, tree=None, ignore_collidable_flag=False):
        if not self.should_check_collisions and not ignore_collidable_flag:
            raise StopIteration

        if tree is None:
            tree = self.layer.quad_tree

        num_checks = 0

        if self.collision_rects:
            self_rect = self.collision_rects[0].unionall(
                self.collision_rects[1:])
        else:
            self_rect = self.rect

        # We want more detailed collision info, so we use our own logic
        # instead of calling spritecollide.
        for obj in tree.get_sprites(self_rect):
            num_checks += 1
            self_rect, obj_rect = \
                self._check_collision(self, obj, ignore_collidable_flag)

            if self_rect and obj_rect:
                yield obj, self_rect, obj_rect

        #print 'Performing %s checks' % num_checks

    def _check_collision(self, left, right, ignore_collidable_flag):
        if (left == right or
            left.layer.index != right.layer.index or
            (not ignore_collidable_flag and
             ((not left.collidable or not right.collidable) or
              (not left.should_check_collisions and
               not right.should_check_collisions)))):
            return None, None

        left_rects = left.collision_rects or [left.rect]
        right_rects = right.collision_rects or [right.rect]

        for left_index, left_rect in enumerate(left_rects):
            right_index = left_rect.collidelist(right_rects)

            if right_index == -1:
                continue

            right_rect = right_rects[right_index]

            if left.use_pixel_collisions or right.use_pixel_collisions:
                left_mask = left._build_mask(left_rect, left_index)
                right_mask = right._build_mask(right_rect, right_index)

                offset = (left_rect.left - right_rect.left,
                          left_rect.top - right_rect.top)
                pos = right_mask.overlap(left_mask, offset)

                if not pos:
                    continue

                mask = right_mask.overlap_mask(left_mask, offset)
                collision_rect = mask.get_bounding_rects()[0]
                right_rect = pygame.Rect(right_rect.left + collision_rect.left,
                                         right_rect.top + collision_rect.top,
                                         *collision_rect.size)

            return left_rect, right_rect

        return None, None

    def _build_mask(self, rect, collision_index):
        mask = None
        image = None

        if self.use_pixel_collisions:
            if collision_index < len(self.collision_masks):
                mask = self.collision_masks[collision_index]

            if not mask:
                mask = getattr(self, 'mask', None)

            if not mask:
                if rect == self.rect:
                    image = self.image
                else:
                    try:
                        image = self.image.subsurface(rect)
                    except ValueError, e:
                        image = None

                if image:
                    mask = pygame.sprite.from_surface(image)

        if not mask:
            mask = pygame.Mask(rect.size)
            mask.fill()

        if self.collision_rects:
            if not self.collision_masks:
                self.collision_masks = [None] * len(self.collision_rects)

            self.collision_masks[collision_index] = mask
        else:
            self.mask = mask

        return mask
Exemple #23
0
class UIManager(object):
    SCREEN_PADDING = 100
    TEXTBOX_HEIGHT = 100
    CONTROL_PANEL_HEIGHT = 40

    def __init__(self, engine):
        pygame.font.init()

        self.ready = Signal()

        self.engine = engine
        self.size = engine.screen.get_size()
        self.surface = pygame.Surface(self.size).convert_alpha()
        self.widgets = []
        self.timers = []

        self.default_font = get_font_filename()
        # self.default_font = pygame.font.get_default_font()
        self.font = pygame.font.Font(self.default_font, 20)
        self.small_font = pygame.font.Font(self.default_font, 16)

        self.engine.level_changed.connect(self.on_level_changed)

        self.control_panel = None
        self.paused_textbox = None
        self.confirm_quit_box = None

    def add_control_panel(self):
        if not self.control_panel:
            self.control_panel = ControlPanel(self)
            self.control_panel.move_to(0, self.size[1] - self.control_panel.rect.height)

    def show_level(self, level):
        timeline_attrs = {"font": self.small_font, "padding_top": 20}

        widget = self.show_textbox(
            [
                ({"padding_top": 10}, level.name),
                [(timeline_attrs, time_period.name) for time_period in level.time_periods],
            ],
            line_spacing=0,
        )

        timer = Timer(2000, lambda: self.close(widget), one_shot=True)
        timer.start()

        return widget

    def show_textbox(self, text, **kwargs):
        textbox = TextBox(self, text, **kwargs)
        textbox.resize(self.size[0] - 2 * self.SCREEN_PADDING, self.TEXTBOX_HEIGHT)
        textbox.move_to(self.SCREEN_PADDING, self.size[1] - textbox.rect.height - self.SCREEN_PADDING)
        return textbox

    def close(self, widget):
        try:
            self.widgets.remove(widget)
            widget.closed.emit()
        except ValueError:
            # It was already closed
            pass

        if len(self.widgets) == 1:
            assert self.widgets[0] == self.control_panel

            self.ready.emit()

    def handle_event(self, event):
        handled = False

        if event.type == KEYDOWN:
            if self.confirm_quit_box:
                if event.key == K_ESCAPE:
                    self.confirm_quit_box.close()
                    self.confirm_quit_box = None
                    handled = True
                    self.engine.paused = False
                elif event.key == K_q:
                    self.engine.quit()
                    handled = True
                else:
                    return True
            elif event.key in (K_ESCAPE, K_RIGHT, K_SPACE, K_RETURN):
                for widget in self.widgets:
                    if isinstance(widget, TextBox) and widget != self.paused_textbox:
                        widget.close()
                        handled = True

        return handled

    def pause(self):
        assert not self.paused_textbox
        self.paused_textbox = self.show_textbox("Paused")

    def unpause(self):
        if self.paused_textbox:
            self.paused_textbox.close()
            self.paused_textbox = None

    def confirm_quit(self):
        if self.confirm_quit_box:
            self.confirm_quit_box.close()
            return

        self.engine.paused = True
        self.confirm_quit_box = self.show_textbox(
            [
                "Do you want to quit?",
                ({"font": self.small_font}, "Press 'Q' to quit."),
                ({"font": self.small_font}, "Press 'Escape' to cancel."),
            ]
        )

    def draw(self, surface):
        for element in self.widgets:
            element.draw(surface)

    def on_level_changed(self):
        level = self.engine.active_level
        self.show_level(level)
        self.control_panel.level = level
Exemple #24
0
 def __init__(self, name):
     super(Button, self).__init__(name)
     self.pressed = Signal()
Exemple #25
0
class UIManager(object):
    SCREEN_PADDING = 100
    TEXTBOX_HEIGHT = 100
    CONTROL_PANEL_HEIGHT = 40

    def __init__(self, engine):
        pygame.font.init()

        self.ready = Signal()

        self.engine = engine
        self.size = engine.screen.get_size()
        self.surface = pygame.Surface(self.size).convert_alpha()
        self.widgets = []
        self.timers = []

        self.default_font = get_font_filename()
        #self.default_font = pygame.font.get_default_font()
        self.font = pygame.font.Font(self.default_font, 20)
        self.small_font = pygame.font.Font(self.default_font, 16)

        self.engine.level_changed.connect(self.on_level_changed)

        self.control_panel = None
        self.paused_textbox = None
        self.confirm_quit_box = None

    def add_control_panel(self):
        if not self.control_panel:
            self.control_panel = ControlPanel(self)
            self.control_panel.move_to(
                0, self.size[1] - self.control_panel.rect.height)

    def show_level(self, level):
        timeline_attrs = {
            'font': self.small_font,
            'padding_top': 20,
        }

        widget = self.show_textbox([({
            'padding_top': 10
        }, level.name),
                                    [(timeline_attrs, time_period.name)
                                     for time_period in level.time_periods]],
                                   line_spacing=0)

        timer = Timer(2000, lambda: self.close(widget), one_shot=True)
        timer.start()

        return widget

    def show_textbox(self, text, **kwargs):
        textbox = TextBox(self, text, **kwargs)
        textbox.resize(self.size[0] - 2 * self.SCREEN_PADDING,
                       self.TEXTBOX_HEIGHT)
        textbox.move_to(
            self.SCREEN_PADDING,
            self.size[1] - textbox.rect.height - self.SCREEN_PADDING)
        return textbox

    def close(self, widget):
        try:
            self.widgets.remove(widget)
            widget.closed.emit()
        except ValueError:
            # It was already closed
            pass

        if len(self.widgets) == 1:
            assert self.widgets[0] == self.control_panel

            self.ready.emit()

    def handle_event(self, event):
        handled = False

        if event.type == KEYDOWN:
            if self.confirm_quit_box:
                if event.key == K_ESCAPE:
                    self.confirm_quit_box.close()
                    self.confirm_quit_box = None
                    handled = True
                    self.engine.paused = False
                elif event.key == K_q:
                    self.engine.quit()
                    handled = True
                else:
                    return True
            elif event.key in (K_ESCAPE, K_RIGHT, K_SPACE, K_RETURN):
                for widget in self.widgets:
                    if (isinstance(widget, TextBox)
                            and widget != self.paused_textbox):
                        widget.close()
                        handled = True

        return handled

    def pause(self):
        assert not self.paused_textbox
        self.paused_textbox = self.show_textbox('Paused')

    def unpause(self):
        if self.paused_textbox:
            self.paused_textbox.close()
            self.paused_textbox = None

    def confirm_quit(self):
        if self.confirm_quit_box:
            self.confirm_quit_box.close()
            return

        self.engine.paused = True
        self.confirm_quit_box = self.show_textbox([
            "Do you want to quit?",
            ({
                'font': self.small_font
            }, "Press 'Q' to quit."),
            ({
                'font': self.small_font
            }, "Press 'Escape' to cancel.")
        ])

    def draw(self, surface):
        for element in self.widgets:
            element.draw(surface)

    def on_level_changed(self):
        level = self.engine.active_level
        self.show_level(level)
        self.control_panel.level = level
Exemple #26
0
class ForeverEndEngine(object):
    FPS = 30

    def __init__(self, screen):
        set_engine(self)

        # Signals
        self.level_changed = Signal()
        self.tick = Signal()

        # State and objects
        self.active_level = None
        self.active_cutscene = None
        self.paused = False
        self.screen = screen
        self.clock = pygame.time.Clock()
        self.player = Player()
        self.ui_manager = UIManager(self)
        self.camera = None

        # Debug flags
        self.debug_rects = False
        self.god_mode = False
        self.show_debug_info = False

        self.area_changed_cnx = None
        self.ui_ready_cnx = None

    def run(self):
        self.active_cutscene = OpeningCutscene()
        self.active_cutscene.done.connect(self._setup_game)
        self.active_cutscene.start()

        self._mainloop()

    def quit(self):
        pygame.quit()
        sys.exit(0)

    def dead(self):
        def on_timeout():
            widget.close()
            self.restart_level()

        widget = self.ui_manager.show_textbox([
            "It's okay! You had another guy!",
            "%s lives left." % self.player.lives
        ])
        self.paused = True

        timer = Timer(2000, on_timeout, one_shot=True)

    def restart_level(self):
        self.switch_level(self.levels.index(self.active_level))

    def game_over(self):
        def on_timeout():
            widget.close()
            self._setup_game()
            #sys.exit(0)

        widget = self.ui_manager.show_textbox('Game Over')
        self.paused = True

        timer = Timer(2000, on_timeout, one_shot=True)

    def show_end_scene(self):
        self.ui_manager.control_panel.close()
        self.active_level = None
        self.active_cutscene = ClosingCutscene()
        self.active_cutscene.start()

    def _setup_game(self):
        self.ui_manager.add_control_panel()
        self.camera = Camera(self)
        self.tick.clear()

        self.active_cutscene = None

        self.player.lives = self.player.MAX_LIVES
        self.player.health = self.player.MAX_HEALTH
        self.player.update_image()
        self.levels = [level(self) for level in get_levels()]
        self.switch_level(0)

        if self.ui_ready_cnx:
            self.ui_ready_cnx.disconnect()

        self.paused = True
        self.ui_ready_cnx = self.ui_manager.ready.connect(self.show_tutorial)

    def show_tutorial(self):
        def on_done():
            self.active_cutscene = False
            self.paused = False

        self.ui_ready_cnx.disconnect()
        self.ui_ready_cnx = None

        self.active_cutscene = TutorialCutscene()
        self.active_cutscene.start()
        self.active_cutscene.done.connect(on_done)

    def switch_level(self, num):
        assert num < len(self.levels)
        self.paused = False
        self.player.stop_tractor_beam()
        self.player.stop_riding()
        self.player.block_events = False
        self.player.reverse_gravity = False
        self.player.jumping = False
        self.player.falling = False
        self.player.fall()
        self.active_level = self.levels[num]
        self.active_level.reset()

        if self.area_changed_cnx:
            self.area_changed_cnx.disconnect()

        self.area_changed_cnx = \
            self.active_level.area_changed.connect(self._on_area_changed)

        self.active_level.switch_time_period(0)
        self.player.move_to(*self.active_level.active_area.start_pos)
        self.camera.update()
        self.player.show()

        if False and has_mixer:
            pygame.mixer.music.stop()

            if self.active_level.music:
                pygame.mixer.music.load(
                    get_music_filename(self.active_level.music))
                pygame.mixer.music.play(-1)

        self.level_changed.emit()

    def next_level(self):
        def on_timeout():
            widget.close()
            unload_images()
            self.switch_level(next_level)

        next_level = self.levels.index(self.active_level) + 1
        widget = self.ui_manager.show_textbox([
            'You got the artifact! %s left to go.' %
            (len(self.levels) - next_level),
        ])

        timer = Timer(2000, on_timeout, one_shot=True)
        timer.start()

    def _on_area_changed(self):
        area = self.active_level.active_area
        self.surface = pygame.Surface(area.size)

        if self.camera:
            self.camera.update()

    def _mainloop(self):
        while 1:
            for event in pygame.event.get():
                if not self._handle_event(event):
                    return

            self.tick.emit()
            self._paint()
            self.clock.tick(self.FPS)

    def _handle_event(self, event):
        if event.type == QUIT:
            self.quit()
            return False

        if (self.ui_manager and not self.active_cutscene
                and self.ui_manager.handle_event(event)):
            return True

        if event.type == KEYDOWN and event.key == K_F2:
            self.show_debug_info = not self.show_debug_info
        elif event.type == KEYDOWN and event.key == K_F3:
            self.debug_rects = not self.debug_rects
        elif event.type == KEYDOWN and event.key == K_F4:
            self.god_mode = not self.god_mode
        elif self.active_cutscene:
            self.active_cutscene.handle_event(event)
        elif self.active_level:
            if event.type == KEYDOWN and event.key == K_TAB:
                # Switch time periods
                self._show_time_periods()
            elif event.type == KEYDOWN and event.key == K_RETURN:
                if self.paused:
                    self._unpause()
                else:
                    self._pause()
            elif not self.paused:
                if event.type == KEYDOWN and event.key in (K_1, K_a):
                    self.active_level.switch_time_period(0)
                elif event.type == KEYDOWN and event.key in (K_2, K_s):
                    self.active_level.switch_time_period(1)
                elif event.type == KEYDOWN and event.key in (K_3, K_d):
                    self.active_level.switch_time_period(2)
                elif event.type == KEYDOWN and event.key == K_F5:
                    # XXX For debugging only.
                    i = self.levels.index(self.active_level)

                    if i + 1 == len(self.levels):
                        self.switch_level(0)
                    else:
                        self.switch_level(i + 1)
                elif event.type == KEYDOWN and event.key == K_ESCAPE:
                    self.ui_manager.confirm_quit()
                elif not self.player.handle_event(event):
                    area = self.active_level.active_area

                    for box in area.event_handlers:
                        if hasattr(box, 'rects'):
                            rects = box.rects
                        else:
                            rects = [box.rect]

                        if (self.player.rect.collidelist(rects) != -1
                                and box.handle_event(event)):
                            break

        return True

    def _pause(self):
        self.paused = True
        self.ui_manager.pause()

    def _unpause(self):
        self.paused = False
        self.ui_manager.unpause()

    def _show_time_periods(self):
        self._pause()

    def _paint(self):
        if self.camera:
            self.camera.update()

        if self.active_cutscene:
            self.screen.set_clip(None)
            self.active_cutscene.draw(self.screen)

        if self.active_level:
            self.surface.set_clip(self.camera.rect)
            self.active_level.draw(self.surface)
            self.screen.blit(self.surface.subsurface(self.camera.rect), (0, 0))

        self.ui_manager.draw(self.screen)

        if self.show_debug_info:
            debug_str = '%0.f FPS     X: %s     Y: %s' % (self.clock.get_fps(
            ), self.player.rect.left, self.player.rect.top)

            self.screen.blit(
                self.ui_manager.small_font.render(debug_str, True,
                                                  (255, 0, 0)), (30, 10))

        pygame.display.flip()
Exemple #27
0
class ForeverEndEngine(object):
    FPS = 30

    def __init__(self, screen):
        set_engine(self)

        # Signals
        self.level_changed = Signal()
        self.tick = Signal()

        # State and objects
        self.active_level = None
        self.active_cutscene = None
        self.paused = False
        self.screen = screen
        self.clock = pygame.time.Clock()
        self.player = Player()
        self.ui_manager = UIManager(self)
        self.camera = None

        # Debug flags
        self.debug_rects = False
        self.god_mode = False
        self.show_debug_info = False

        self.area_changed_cnx = None
        self.ui_ready_cnx = None

    def run(self):
        self.active_cutscene = OpeningCutscene()
        self.active_cutscene.done.connect(self._setup_game)
        self.active_cutscene.start()

        self._mainloop()

    def quit(self):
        pygame.quit()
        sys.exit(0)

    def dead(self):
        def on_timeout():
            widget.close()
            self.restart_level()

        widget = self.ui_manager.show_textbox([
            "It's okay! You had another guy!",
            "%s lives left." % self.player.lives
        ])
        self.paused = True

        timer = Timer(2000, on_timeout, one_shot=True)

    def restart_level(self):
        self.switch_level(self.levels.index(self.active_level))

    def game_over(self):
        def on_timeout():
            widget.close()
            self._setup_game()
            #sys.exit(0)

        widget = self.ui_manager.show_textbox('Game Over')
        self.paused = True

        timer = Timer(2000, on_timeout, one_shot=True)

    def show_end_scene(self):
        self.ui_manager.control_panel.close()
        self.active_level = None
        self.active_cutscene = ClosingCutscene()
        self.active_cutscene.start()

    def _setup_game(self):
        self.ui_manager.add_control_panel()
        self.camera = Camera(self)
        self.tick.clear()

        self.active_cutscene = None

        self.player.lives = self.player.MAX_LIVES
        self.player.health = self.player.MAX_HEALTH
        self.player.update_image()
        self.levels = [level(self) for level in get_levels()]
        self.switch_level(0)

        if self.ui_ready_cnx:
            self.ui_ready_cnx.disconnect()

        self.paused = True
        self.ui_ready_cnx = self.ui_manager.ready.connect(self.show_tutorial)

    def show_tutorial(self):
        def on_done():
            self.active_cutscene = False
            self.paused = False

        self.ui_ready_cnx.disconnect()
        self.ui_ready_cnx = None

        self.active_cutscene = TutorialCutscene()
        self.active_cutscene.start()
        self.active_cutscene.done.connect(on_done)

    def switch_level(self, num):
        assert num < len(self.levels)
        self.paused = False
        self.player.stop_tractor_beam()
        self.player.stop_riding()
        self.player.block_events = False
        self.player.reverse_gravity = False
        self.player.jumping = False
        self.player.falling = False
        self.player.fall()
        self.active_level = self.levels[num]
        self.active_level.reset()

        if self.area_changed_cnx:
            self.area_changed_cnx.disconnect()

        self.area_changed_cnx = \
            self.active_level.area_changed.connect(self._on_area_changed)

        self.active_level.switch_time_period(0)
        self.player.move_to(*self.active_level.active_area.start_pos)
        self.camera.update()
        self.player.show()

        if False and has_mixer:
            pygame.mixer.music.stop()

            if self.active_level.music:
                pygame.mixer.music.load(
                    get_music_filename(self.active_level.music))
                pygame.mixer.music.play(-1)

        self.level_changed.emit()

    def next_level(self):
        def on_timeout():
            widget.close()
            unload_images()
            self.switch_level(next_level)

        next_level = self.levels.index(self.active_level) + 1
        widget = self.ui_manager.show_textbox([
            'You got the artifact! %s left to go.'
            % (len(self.levels) - next_level),
        ])

        timer = Timer(2000, on_timeout, one_shot=True)
        timer.start()

    def _on_area_changed(self):
        area = self.active_level.active_area
        self.surface = pygame.Surface(area.size)

        if self.camera:
            self.camera.update()

    def _mainloop(self):
        while 1:
            for event in pygame.event.get():
                if not self._handle_event(event):
                    return

            self.tick.emit()
            self._paint()
            self.clock.tick(self.FPS)

    def _handle_event(self, event):
        if event.type == QUIT:
            self.quit()
            return False

        if (self.ui_manager and not self.active_cutscene and
            self.ui_manager.handle_event(event)):
            return True

        if event.type == KEYDOWN and event.key == K_F2:
            self.show_debug_info = not self.show_debug_info
        elif event.type == KEYDOWN and event.key == K_F3:
            self.debug_rects = not self.debug_rects
        elif event.type == KEYDOWN and event.key == K_F4:
            self.god_mode = not self.god_mode
        elif self.active_cutscene:
            self.active_cutscene.handle_event(event)
        elif self.active_level:
            if event.type == KEYDOWN and event.key == K_TAB:
                # Switch time periods
                self._show_time_periods()
            elif event.type == KEYDOWN and event.key == K_RETURN:
                if self.paused:
                    self._unpause()
                else:
                    self._pause()
            elif not self.paused:
                if event.type == KEYDOWN and event.key in (K_1, K_a):
                    self.active_level.switch_time_period(0)
                elif event.type == KEYDOWN and event.key in (K_2, K_s):
                    self.active_level.switch_time_period(1)
                elif event.type == KEYDOWN and event.key in (K_3, K_d):
                    self.active_level.switch_time_period(2)
                elif event.type == KEYDOWN and event.key == K_F5:
                    # XXX For debugging only.
                    i = self.levels.index(self.active_level)

                    if i + 1 == len(self.levels):
                        self.switch_level(0)
                    else:
                        self.switch_level(i + 1)
                elif event.type == KEYDOWN and event.key == K_ESCAPE:
                    self.ui_manager.confirm_quit()
                elif not self.player.handle_event(event):
                    area = self.active_level.active_area

                    for box in area.event_handlers:
                        if hasattr(box, 'rects'):
                            rects = box.rects
                        else:
                            rects = [box.rect]

                        if (self.player.rect.collidelist(rects) != -1 and
                            box.handle_event(event)):
                            break

        return True

    def _pause(self):
        self.paused = True
        self.ui_manager.pause()

    def _unpause(self):
        self.paused = False
        self.ui_manager.unpause()

    def _show_time_periods(self):
        self._pause()

    def _paint(self):
        if self.camera:
            self.camera.update()

        if self.active_cutscene:
            self.screen.set_clip(None)
            self.active_cutscene.draw(self.screen)

        if self.active_level:
            self.surface.set_clip(self.camera.rect)
            self.active_level.draw(self.surface)
            self.screen.blit(self.surface.subsurface(self.camera.rect), (0, 0))

        self.ui_manager.draw(self.screen)

        if self.show_debug_info:
            debug_str = '%0.f FPS     X: %s     Y: %s' % (
                self.clock.get_fps(),
                self.player.rect.left, self.player.rect.top)

            self.screen.blit(
                self.ui_manager.small_font.render(debug_str, True, (255, 0, 0)),
                (30, 10))

        pygame.display.flip()
Exemple #28
0
class Level(object):
    CROSSOVER_TIME_INTERVAL = (4000, 10000)
    MAX_CROSSOVERS = 3

    def __init__(self, engine):
        self.engine = engine
        self.time_periods = []
        self.active_area = None
        self.music = None
        self.crossover_timer = None
        self.crossovers = []
        self.pending_crossovers = {}
        self.next_crossover_id = 0

        self.engine.tick.connect(self.on_tick)

        # Signals
        self.area_changed = Signal()
        self.time_period_changed = Signal()

    def add(self, time_period):
        self.time_periods.append(time_period)

    def add_artifact(self, area, x, y):
        # Artifact
        self.artifact = Artifact(area, 1)
        area.main_layer.add(self.artifact)
        self.artifact.move_to(x - self.artifact.rect.width / 2,
                              y - self.artifact.rect.height - 50)
        self.artifact.grab_changed.connect(self.on_artifact_grabbed)

    def reset(self):
        self.active_area = None
        self.active_time_period = None
        self.time_periods = []
        self._setup()

    def setup(self):
        pass

    def _setup(self):
        self.setup()

        for time_period in self.time_periods:
            time_period.setup()

    def switch_area(self, area):
        if area == self.active_area:
            return

        player = self.engine.player

        if self.active_area:
            self.active_area.main_layer.remove(player)

        self.active_area = area
        self.active_area.main_layer.add(player)
        self.area_changed.emit()
        player.reset_gravity()

        for crossover, timer in self.crossovers:
            timer.stop()
            crossover.remove()

        for pending_timer in self.pending_crossovers.itervalues():
            pending_timer.stop()

        self.crossovers = []
        self.pending_crossovers = {}

    def switch_time_period(self, num):
        time_period = self.time_periods[num]

        if self.active_area:
            key = self.active_area.key
            area = time_period.areas.get(key, None)
        else:
            area = time_period.default_area

        player = self.engine.player

        if (area and
            (not self.active_area or
             not list(player.get_collisions(tree=area.main_layer.quad_tree)))):
            self.active_time_period = time_period
            self.time_period_changed.emit()
            self.switch_area(area)

    def draw(self, screen):
        self.active_area.draw(screen)

    def on_artifact_grabbed(self):
        if self.artifact.grabbed:
            player = self.engine.player
            player.block_events = True
            player.velocity = (0, 0)
            player.fall()

            timer = Timer(1000, self.engine.next_level, one_shot=True)
            timer.start()

    def on_tick(self):
        if not self.engine.paused and self.engine.active_level == self:
            self.active_area.tick()

            if (len(self.time_periods) > 1 and
                len(self.crossovers) + len(self.pending_crossovers) <
                self.MAX_CROSSOVERS):
                crossover_id = self.next_crossover_id
                self.next_crossover_id += 1
                timer = Timer(
                    random.randint(*self.CROSSOVER_TIME_INTERVAL),
                    lambda: self.show_crossover(crossover_id),
                    one_shot=True)
                self.pending_crossovers[crossover_id] = timer

    def show_crossover(self, crossover_id):
        def hide_crossover():
            crossover_sprite.remove()
            self.crossovers.remove((crossover_sprite, timer))

        self.pending_crossovers[crossover_id].stop()
        del self.pending_crossovers[crossover_id]

        key = self.active_area.key

        time_periods = [
            time_period.areas[key]
            for time_period in self.time_periods
            if (time_period != self.active_time_period and
                key in time_period.areas)
        ]

        if len(time_periods) - 1 <= 0:
            return

        i = random.randint(0, len(time_periods) - 1)


        crossover_sprite = Crossover(time_periods[i])
        crossover_sprite.rect = self.engine.camera.rect

        if random.randint(0, 5) <= 3:
            layer = self.active_area.bg_layer
        else:
            layer = self.active_area.main_layer

        layer.add(crossover_sprite)

        timer = Timer(500, hide_crossover, one_shot=True)
        timer.start()
        self.crossovers.append((crossover_sprite, timer))
Exemple #29
0
class Player(Sprite):
    MOVE_SPEED = 6
    JUMP_SPEED = 6
    MAX_JUMP_HEIGHT = 100
    HOVER_TIME_MS = 1000

    MAX_HEALTH = 3
    MAX_LIVES = 3

    PROPULSION_BELOW_OFFSET = 8

    def __init__(self):
        super(Player, self).__init__('player', flip_image=True,
                                     obey_gravity=True)
        self.engine = get_engine()
        self.should_check_collisions = True

        # Sprites
        self.propulsion_below = Sprite("propulsion_below", flip_image=True)
        self.propulsion_below.collidable = False
        self.propulsion_below.visible = 0
        self.tractor_beam = TractorBeam(self)

        # State
        self.jumping = False
        self.hovering = False
        self.block_events = False
        self.jump_origin = None
        self.hover_time_ms = 0
        self.health = self.MAX_HEALTH
        self.lives = self.MAX_LIVES
        self.last_safe_spot = None
        self.vehicle = None
        self.vehicle_move_cnx = None

        # Signals
        self.health_changed = Signal()
        self.lives_changed = Signal()

        self.direction_changed.connect(self.on_direction_changed)
        self.reverse_gravity_changed.connect(self.on_reverse_gravity_changed)

    def handle_event(self, event):
        if self.block_events:
            return

        if event.type == KEYDOWN:
            if event.key == K_RIGHT:
                self.move_right()
            elif event.key == K_LEFT:
                self.move_left()
            elif event.key == K_SPACE:
                self.jump()
            elif event.key in (K_LSHIFT, K_RSHIFT):
                self.start_tractor_beam()
        elif event.type == KEYUP:
            if event.key == K_LEFT:
                if self.velocity[0] < 0:
                    self.velocity = (0, self.velocity[1])
            elif event.key == K_RIGHT:
                if self.velocity[0] > 0:
                    self.velocity = (0, self.velocity[1])
            elif event.key == K_SPACE:
                self.fall()
            elif event.key in (K_LSHIFT, K_RSHIFT):
                self.stop_tractor_beam()

    def move_right(self):
        self.velocity = (self.MOVE_SPEED, self.velocity[1])

        if self.direction != Direction.RIGHT:
            self.direction = Direction.RIGHT
            self.tractor_beam.direction = Direction.RIGHT
            self.update_image()

    def move_left(self):
        self.velocity = (-self.MOVE_SPEED, self.velocity[1])

        if self.direction != Direction.LEFT:
            self.direction = Direction.LEFT
            self.tractor_beam.direction = Direction.LEFT
            self.update_image()

    def start_tractor_beam(self):
        self.tractor_beam.show()
        self.tractor_beam.update_position(self)
        self.calculate_collision_rects()

    def stop_tractor_beam(self):
        self.tractor_beam.ungrab()
        self.tractor_beam.hide()
        self.calculate_collision_rects()

    def ride(self, vehicle):
        self.vehicle = vehicle
        self.velocity = (self.velocity[0], 0)
        self.vehicle_moved_cnx = \
            self.vehicle.moved.connect(self.on_vehicle_moved)
        self.calculate_collision_rects()

    def jump(self):
        if (self.falling and not self.engine.god_mode) or self.jumping:
            return

        self.jump_origin = (self.rect.left, self.rect.top)
        self.jumping = True
        self.falling = False

        if self.reverse_gravity:
            self.velocity = (self.velocity[0], self.JUMP_SPEED)
        else:
            self.velocity = (self.velocity[0], -self.JUMP_SPEED)

        self.propulsion_below.show()
        self.stop_riding()

    def reset_gravity(self):
        if self.reverse_gravity:
            self.reverse_gravity = False

            if self.falling:
                # Reset the fall
                self.falling = False
                self.fall()

            self.update_image()

    def stop_riding(self):
        if self.vehicle:
            self.vehicle_moved_cnx.disconnect()
            self.vehicle = None
            self.calculate_collision_rects()

    def fall(self):
        if self.falling:
            return

        self.propulsion_below.hide()
        self.jumping = False
        self.hovering = False
        super(Player, self).fall()

    def hover(self):
        if self.hovering:
            return

        self.propulsion_below.show()
        self.jumping = False
        self.hovering = True
        self.hover_time_ms = 0
        self.velocity = (self.velocity[0], 0)

    def calculate_collision_rects(self):
        self.collision_masks = []
        self.collision_rects = [self.rect]

        if self.tractor_beam.item:
            self.collision_rects.append(self.tractor_beam.item.rect)

        if self.vehicle:
            self.collision_rects.append(self.vehicle.rect)

    def check_collisions(self, *args, **kwargs):
        super(Player, self).check_collisions(*args, **kwargs)

    def should_adjust_position_with(self, obj, dx, dy):
        from foreverend.sprites.common import FloatingSprite

        return (obj != self.vehicle and
                (dx == 0 or not isinstance(obj, FloatingSprite)))

    def tick(self):
        if self.hovering:
            self.hover_time_ms += 1.0 / self.engine.FPS * 1000

            if self.hover_time_ms >= self.HOVER_TIME_MS:
                self.fall()

        super(Player, self).tick()

    def on_added(self, layer):
        for obj in (self.propulsion_below, self.tractor_beam, self.vehicle):
            if obj:
                layer.add(obj)

    def on_removed(self, layer):
        for obj in (self.propulsion_below, self.tractor_beam, self.vehicle):
            if obj:
                layer.remove(obj)

    def on_moved(self, dx, dy):
        if not self.last_safe_spot:
            self.last_safe_spot = self.rect.topleft

        self.rect.top = max(self.rect.top, 0)

        if self.rect.top > self.layer.area.size[1]:
            self.on_dead()
            return

        if (self.jumping and
            not self.engine.god_mode and
            ((not self.reverse_gravity and
              self.jump_origin[1] - self.rect.top >= self.MAX_JUMP_HEIGHT) or
             (self.reverse_gravity and
              self.rect.top - self.jump_origin[1] >= self.MAX_JUMP_HEIGHT))):
            self.hover()

        if self.propulsion_below.visible:
            if self.direction == Direction.RIGHT:
                offset = self.PROPULSION_BELOW_OFFSET
            if self.direction == Direction.LEFT:
                offset = self.rect.width - self.propulsion_below.rect.width - \
                         self.PROPULSION_BELOW_OFFSET

            if self.reverse_gravity:
                y = self.rect.top - self.propulsion_below.rect.height
            else:
                y = self.rect.bottom

            self.propulsion_below.move_to(self.rect.left + offset, y)

        if self.tractor_beam.visible:
            self.tractor_beam.update_position(self)

        if self.vehicle and dx != 0:
            self.vehicle.move_to(
                self.rect.left -
                (self.vehicle.rect.width - self.rect.width) / 2,
                self.vehicle.rect.top)

        self.calculate_collision_rects()

        super(Player, self).on_moved(dx, dy)

    def on_collision(self, dx, dy, obj, self_rect, obj_rect):
        if obj.lethal and not self.engine.god_mode and self_rect == self.rect:
            self.on_hit()
            return

        self.last_safe_spot = self.rect.topleft

        if self.tractor_beam.item and self_rect == self.tractor_beam.item.rect:
            move_player = True

            if self.tractor_beam.adjust_item_position(obj_rect):
                move_player = False

            if move_player:
                if self.direction == Direction.LEFT:
                    self.move_to(obj_rect.right +
                                 (self.rect.left -
                                  self.tractor_beam.item.rect.left),
                                 self.rect.top)
                elif self.direction == Direction.RIGHT:
                    self.move_to(obj_rect.left -
                                 (self.tractor_beam.item.rect.right -
                                  self.rect.left),
                                 self.rect.top)

        if dy > 0 and not self.vehicle and isinstance(obj, Vehicle):
            self.ride(obj)

        if self.jumping and dy < 0:
            self.fall()
        else:
            super(Player, self).on_collision(dx, dy, obj, self_rect, obj_rect)

    def on_direction_changed(self):
        if self.vehicle:
            self.vehicle.direction = self.direction

    def on_reverse_gravity_changed(self):
        self.propulsion_below.set_reverse_gravity(self.reverse_gravity)
        self.tractor_beam.set_reverse_gravity(self.reverse_gravity)

        if self.tractor_beam.item:
            self.tractor_beam.item.set_reverse_gravity(self.reverse_gravity)

        self.fall()

    def on_vehicle_moved(self, dx, dy):
        if dy != 0:
            self.move_by(0, dy)

    def on_hit(self):
        self.health -= 1
        self.health_changed.emit()

        if self.health == 0:
            self.on_dead()
        else:
            self.move_to(*self.last_safe_spot)
            self.velocity = (0, 0)
            self.falling = False
            self.fall()

    def on_dead(self):
        self.lives -= 1
        self.lives_changed.emit()
        self.velocity = (0, 0)

        if self.lives == 0:
            self.engine.game_over()
        else:
            self.health = self.MAX_HEALTH
            self.health_changed.emit()
            self.engine.dead()
Exemple #30
0
 def __init__(self, name):
     super(Button, self).__init__(name)
     self.pressed = Signal()
Exemple #31
0
 def __init__(self):
     self.done = Signal()
Exemple #32
0
class Sprite(pygame.sprite.DirtySprite):
    FALL_SPEED = 6

    def __init__(self, name, flip_image=False, obey_gravity=False):
        super(Sprite, self).__init__()

        # Signals
        self.moved = Signal()
        self.direction_changed = Signal()
        self.reverse_gravity_changed = Signal()

        # State
        self.rect = pygame.Rect(0, 0, 0, 0)
        self.quad_trees = set()
        self.layer = None
        self.name = name
        self.image = None
        self.visible = 1
        self.dirty = 2
        self.velocity = (0, 0)
        self.obey_gravity = obey_gravity
        self.reverse_gravity = False
        self.lethal = False
        self.falling = False
        self.collidable = True
        self.grabbed = False
        self.grabbable = False
        self.should_check_collisions = False
        self.use_pixel_collisions = False
        self.flip_image = flip_image
        self.collision_rects = []
        self.collision_masks = []
        self._colliding_objects = set()
        self._direction = Direction.RIGHT
        self._flipped_images = {}

    def __repr__(self):
        return 'Sprite %s (%s, %s, %s, %s)' % \
               (self.name, self.rect.left, self.rect.top,
                self.rect.width, self.rect.height)

    def set_reverse_gravity(self, reverse_gravity):
        if self.reverse_gravity != reverse_gravity:
            self.reverse_gravity = reverse_gravity
            self.velocity = (self.velocity[0], -self.velocity[1])
            self.update_image()
            self.reverse_gravity_changed.emit()

    def _set_direction(self, direction):
        if self.direction != direction:
            self._direction = direction
            self.direction_changed.emit()
            self.update_image()

    direction = property(lambda self: self._direction, _set_direction)

    def show(self):
        if not self.visible:
            self.visible = 1
            self.dirty = 2
            self.layer.update_sprite(self)

    def hide(self):
        if self.visible:
            self.visible = 0
            self.dirty = 1
            self.layer.update_sprite(self)

    def remove(self):
        self.hide()
        self.layer.remove(self)

    def fall(self):
        if self.falling or not self.obey_gravity:
            return

        self.falling = True

        if self.reverse_gravity:
            self.velocity = (self.velocity[0], -self.FALL_SPEED)
        else:
            self.velocity = (self.velocity[0], self.FALL_SPEED)

    def stop_falling(self):
        if self.obey_gravity:
            self.falling = False
            self.velocity = (self.velocity[0], 0)

    def update_image(self):
        self.image = self.generate_image()
        assert self.image
        self.rect.size = self.image.get_rect().size

    def generate_image(self):
        if not self.name:
            # Must be a custom sprite.
            return self.image

        image = load_image(self.name)

        if (self.flip_image and
            (self._direction == Direction.LEFT or self.reverse_gravity)):
            flip_h = (self.direction == Direction.LEFT)
            flip_v = self.reverse_gravity

            key = (self.name, flip_h, flip_v)

            if key not in self._flipped_images:
                self._flipped_images[key] = \
                    pygame.transform.flip(image, flip_h, flip_v)

            image = self._flipped_images[key]

        return image

    def move_to(self, x, y, check_collisions=False):
        self.move_by(x - self.rect.x, y - self.rect.y, check_collisions)

    def move_by(self, dx, dy, check_collisions=True):
        if check_collisions:
            if dx:
                self._move(dx=dx)

            if dy:
                self._move(dy=dy)
        else:
            self.rect.move_ip(dx, dy)

        self.on_moved(dx, dy)

    def _move(self, dx=0, dy=0):
        self.rect.move_ip(dx, dy)
        self.rect.left = max(self.rect.left, 0)
        self.rect.right = min(self.rect.right, self.layer.area.size[0])
        self.check_collisions(dx, dy)

    def check_collisions(self, dx=0, dy=0):
        old_colliding_objects = set(self._colliding_objects)
        self._colliding_objects = set()

        for obj, self_rect, obj_rect in self.get_collisions():
            if (self_rect == self.rect
                    and self.should_adjust_position_with(obj, dx, dy)):
                self.position_beside(obj_rect, dx, dy)

            obj.handle_collision(self, obj_rect, dx, dy)
            self.on_collision(dx, dy, obj, self_rect, obj_rect)
            self._colliding_objects.add(obj)

        for obj in old_colliding_objects.difference(self._colliding_objects):
            obj.handle_stop_colliding(self)

    def should_adjust_position_with(self, obj, dx, dy):
        return True

    def position_beside(self, rect, dx, dy):
        if dy < 0:
            self.rect.top = rect.bottom
        elif dy > 0:
            self.rect.bottom = rect.top
        elif dx < 0:
            self.rect.left = rect.right
        elif dx > 0:
            self.rect.right = rect.left

    def get_collisions(self, tree=None, ignore_collidable_flag=False):
        if not self.should_check_collisions and not ignore_collidable_flag:
            raise StopIteration

        if tree is None:
            tree = self.layer.quad_tree

        num_checks = 0

        if self.collision_rects:
            self_rect = self.collision_rects[0].unionall(
                self.collision_rects[1:])
        else:
            self_rect = self.rect

        # We want more detailed collision info, so we use our own logic
        # instead of calling spritecollide.
        for obj in tree.get_sprites(self_rect):
            num_checks += 1
            self_rect, obj_rect = \
                self._check_collision(self, obj, ignore_collidable_flag)

            if self_rect and obj_rect:
                yield obj, self_rect, obj_rect

        #print 'Performing %s checks' % num_checks

    def _check_collision(self, left, right, ignore_collidable_flag):
        if (left == right or left.layer.index != right.layer.index
                or (not ignore_collidable_flag and
                    ((not left.collidable or not right.collidable) or
                     (not left.should_check_collisions
                      and not right.should_check_collisions)))):
            return None, None

        left_rects = left.collision_rects or [left.rect]
        right_rects = right.collision_rects or [right.rect]

        for left_index, left_rect in enumerate(left_rects):
            right_index = left_rect.collidelist(right_rects)

            if right_index == -1:
                continue

            right_rect = right_rects[right_index]

            if left.use_pixel_collisions or right.use_pixel_collisions:
                left_mask = left._build_mask(left_rect, left_index)
                right_mask = right._build_mask(right_rect, right_index)

                offset = (left_rect.left - right_rect.left,
                          left_rect.top - right_rect.top)
                pos = right_mask.overlap(left_mask, offset)

                if not pos:
                    continue

                mask = right_mask.overlap_mask(left_mask, offset)
                collision_rect = mask.get_bounding_rects()[0]
                right_rect = pygame.Rect(right_rect.left + collision_rect.left,
                                         right_rect.top + collision_rect.top,
                                         *collision_rect.size)

            return left_rect, right_rect

        return None, None

    def _build_mask(self, rect, collision_index):
        mask = None
        image = None

        if self.use_pixel_collisions:
            if collision_index < len(self.collision_masks):
                mask = self.collision_masks[collision_index]

            if not mask:
                mask = getattr(self, 'mask', None)

            if not mask:
                if rect == self.rect:
                    image = self.image
                else:
                    try:
                        image = self.image.subsurface(rect)
                    except ValueError, e:
                        image = None

                if image:
                    mask = pygame.sprite.from_surface(image)

        if not mask:
            mask = pygame.Mask(rect.size)
            mask.fill()

        if self.collision_rects:
            if not self.collision_masks:
                self.collision_masks = [None] * len(self.collision_rects)

            self.collision_masks[collision_index] = mask
        else:
            self.mask = mask

        return mask
Exemple #33
0
    def __init__(self, ui_manager):
        self.ui_manager = ui_manager
        self.ui_manager.widgets.append(self)
        self.rect = pygame.Rect(0, 0, 0, 0)

        self.closed = Signal()