Example #1
0
    def _handle_horizontal_movement(self, dt):
        # horizontal velocity has been calculated, now we just need to apply it
        new_position = make_vector(self._velocity.x * dt, 0.)

        # attempt to move to new position, if we can
        hitbox = self._get_active_hitbox()
        offset = self._get_hitbox_offset()

        hitbox.position = self.position + offset
        collisions = hitbox.approach(self.mario_entity.position +
                                     new_position + offset,
                                     tf_dispatch_events=True)
        self.position = hitbox.position - offset

        # don't allow off left of screen
        if self.position.x < self.mario_entity.level.position.x:
            self._velocity.x = 0
            self.position = make_vector(self.mario_entity.level.position.x,
                                        self.position.y)

        # don't allow off right side of world
        if self.position.x + self.rect.width > self.mario_entity.level.tile_map.width_pixels:
            self._velocity.x = 0
            self.position = make_vector(
                self.mario_entity.level.tile_map.width_pixels -
                self.rect.width, self.position.y)

        if collisions:  # immediately stop horizontal movement on horizontal collision
            self._velocity.x = 0.
Example #2
0
    def _create_container(self, font):
        y_offset = 0

        frame = Container(
            make_vector(10,
                        self.get_title_bar_bottom() + 4),
            (EntityPickerDialog.SIZE[0] - 50,
             EntityPickerDialog.SIZE[0] - self.get_title_bar_bottom() - 20))

        for name, factory in LevelEntity.Factories.items():
            if name == "Mario":  # want spawner for mario instead
                continue

            entity_button = create_button(self.assets.gui_atlas,
                                          make_vector(15, y_offset),
                                          EntityPickerDialog.BUTTON_SIZE,
                                          name,
                                          bind_callback_parameters(
                                              self._make_selection, name),
                                          font,
                                          text_color=pygame.Color('white'))

            frame.add_child(entity_button)
            y_offset += entity_button.height + 3

        return frame, y_offset
Example #3
0
    def __init__(self, assets, level, scoring_labels: Labels, mario_stats):
        super().__init__()

        self.assets = assets
        self.scoring_labels = scoring_labels
        self.mario_stats = mario_stats
        self.level = level

        def make_centered(surface):
            return make_vector(*sr.center) - make_vector(surface.get_width() // 2, surface.get_height() // 2)

        sr = config.screen_rect
        tc = pygame.Color('white')

        self.elapsed = 0
        self.world_title = Labels.font_large.render(level.title, True, tc).convert_alpha()
        self.world_title_pos = make_centered(self.world_title) - make_vector(0, 100)

        self.x = Labels.font_large.render("x", True, tc)
        self.x_pos = make_centered(self.x) + make_vector(0, 40)

        little_mario = assets.character_atlas.load_static("mario_stand_right").image

        # scale it up
        self.mario_icon = pygame.transform.scale2x(little_mario).convert()
        self.mario_pos = copy_vector(self.x_pos) - \
            make_vector(self.mario_icon.get_width() * 2, self.mario_icon.get_height() // 4)

        self.lives = Labels.font_large.render(str(mario_stats.lives), True, tc).convert_alpha()
        self.lives_pos = self.x_pos + make_vector(self.mario_icon.get_width(), 0)
Example #4
0
    def __init__(self, mario_entity, input_state, collider_manager,
                 jump_small_sound, jump_super_sound):
        assert mario_entity is not None
        assert input_state is not None
        assert collider_manager is not None
        assert jump_small_sound is not None
        assert jump_super_sound is not None

        self.mario_entity = mario_entity
        self.collider_manager = collider_manager
        self.input_state = input_state
        self.jump_small_sound = jump_small_sound
        self.jump_super_sound = jump_super_sound

        self.debug_trajectory = JumpTrajectoryVisualizer(
        ) if config.debug_jumps else None

        # state values
        # todo: consider changing from frame counter to delta time, so avoid locking update loop at 1/60 dt
        self._velocity = Vector2()
        self._run_frame_counter = 0  # has nothing to do with animation, see ** in smb_playerphysics.png
        self._use_skid_deceleration = False
        self._skidding = False
        self._jump_stats = level_entry_vertical_physics
        self._falling_gravity = self._jump_stats.gravity
        self.jumped = False
        self._facing_right = True  # this value is "sticky": if no input, then is dir of last input
        self._airborne = False
        self._crouch = False
        self._enabled = False

        # create colliders for mario

        # mario has two different hitboxes:
        #  one is for small mario and crouched super mario
        #  the other is for super mario
        #
        # remember that positions of these colliders != mario's position: they can have offsets and different
        # sizing
        # todo: should active be included in these hitboxes?
        self._small_hitbox = Collider.from_entity(mario_entity,
                                                  self.collider_manager,
                                                  constants.Block)
        self._small_hitbox.rect.width, self._small_hitbox.rect.height = rescale_vector(
            make_vector(10, 14))
        self._small_hitbox_offset = rescale_vector(make_vector(3, 2))
        self._small_hitbox.position = self.mario_entity.position + self._small_hitbox_offset

        self._large_hitbox = Collider.from_entity(mario_entity,
                                                  self.collider_manager,
                                                  constants.Block)
        self._large_hitbox.rect.width, self._large_hitbox.rect.height = rescale_vector(
            make_vector(13, 25))
        self._large_hitbox_offset = rescale_vector(make_vector(2, 7))
        self._large_hitbox.position = self.mario_entity.position + self._large_hitbox_offset

        self.airborne_collider = Collider.from_entity(mario_entity,
                                                      self.collider_manager,
                                                      constants.Block)
Example #5
0
    def __init__(self, assets):
        font = pygame.font.SysFont(None, 24)

        super().__init__(config.screen_rect.center,
                         TilePickerDialog.SIZE,
                         assets.gui_atlas.load_sliced("tb_frame"),
                         tb_bkg=assets.gui_atlas.load_sliced("tb_frame"),
                         additional_height=8,
                         text_start_offset=(12, 5),
                         font=font,
                         title="Tiles")

        self.tileset = assets.tileset

        # contents window
        self.scrollable = ScrollableContents(
            make_vector(6, self.get_title_bar_bottom()),
            (TilePickerDialog.SIZE[0] - 30,
             TilePickerDialog.SIZE[1] - self.get_title_bar_bottom() - 26),
            self._create_tileset_surface())

        self.layout()  # ensure scrollable is positioned

        # create scrollbars
        self.vertical_scroll = create_slider(
            assets.gui_atlas,
            make_vector(self.scrollable.rect.right + 4,
                        self.scrollable.rect.top + 10),
            self.scrollable.rect.height - 15,
            0,
            max(0,
                self.tileset.surface.get_height() - self.scrollable.height),
            self._on_scroll_changed,
            thumb=assets.gui_atlas.load_static("sb_thumb"),
            thumb_mo=assets.gui_atlas.load_static("sb_thumb_light"),
            sb_type=ScrollbarType.VERTICAL)

        self.horizontal_scroll = create_slider(
            assets.gui_atlas,
            make_vector(self.scrollable.rect.left + 5,
                        self.scrollable.rect.bottom + 5),
            self.scrollable.rect.width,
            0,
            max(0,
                self.tileset.surface.get_width() - self.scrollable.width),
            self._on_scroll_changed,
            thumb=assets.gui_atlas.load_static("sb_thumb"),
            thumb_mo=assets.gui_atlas.load_static("sb_thumb_light"),
            sb_type=ScrollbarType.HORIZONTAL)

        self.add_child(self.scrollable)
        self.add_child(self.vertical_scroll)
        self.add_child(self.horizontal_scroll)

        self.layout()

        self.selected_tile_idx = 0
        self.bring_to_front(self.title_bar)
Example #6
0
 def __init__(self, width, height):
     self.width = width
     self.height = height
     self.walls = []
     self.connections = [
         make_vector((30, 0)),
         make_vector((-30, 0)),
         make_vector((0, 30)),
         make_vector((0, -30))
     ]
Example #7
0
    def __init__(self, entity, collider_manager, parameters, patrol_range):
        super().__init__(entity, collider_manager, parameters)

        self.patrol_range = patrol_range

        # set up edge detection collider
        self.edge_detector = Collider.from_entity(entity, collider_manager, constants.Block)
        self._left_offset = make_vector(-self.entity.rect.width, 1)
        self._right_offset = make_vector(self.entity.rect.width, 1)

        # state
        self._patrolled = 0.
Example #8
0
def draw_selection_square(screen, level_map, color, view_rect):
    tile_coords = pixel_coords_to_tile_coords(
        pygame.mouse.get_pos() + make_vector(view_rect.x, view_rect.y),
        level_map.tileset)

    if level_map.is_in_bounds(tile_coords):
        r = pygame.Rect(
            *tile_coords_to_pixel_coords(tile_coords, level_map.tileset),
            level_map.tileset.tile_width, level_map.tileset.tile_height)

        r.topleft -= make_vector(*view_rect.topleft)

        pygame.gfxdraw.rectangle(screen, r, color)
    def launch_fireball(self):
        # initial velocity of fireball depends on direction
        initial_velocity = make_vector(fireball_parameters.max_horizontal_velocity,
                                       fireball_parameters.max_vertical_velocity)\
            if self.level.mario.movement.is_facing_right else \
            make_vector(-fireball_parameters.max_horizontal_velocity, fireball_parameters.max_vertical_velocity)

        fb = Fireball(self.level, fireball_parameters, initial_velocity)
        fb.position = self._get_fireball_position()

        self.level.entity_manager.register(fb)
        self.level.asset_manager.sounds['fireball'].play()

        self.level.mario.animator.throw()
Example #10
0
    def __init__(self, level):
        self.pole = level.asset_manager.interactive_atlas.load_static(
            "flag_pole")
        self.flag = level.asset_manager.interactive_atlas.load_static("flag")
        self.level = level
        self.mario = level.mario
        self.flag_offset = rescale_vector(make_vector(
            -13, 16))  # from top-left of pole
        self.flag_movement = make_vector(0, 0)

        self._pole_height = self.pole.get_rect(
        ).height - self.flag_offset.y - self.flag.height

        super().__init__(self.pole.get_rect())
Example #11
0
    def __init__(self, game_events, stats, scoring_labels):
        super().__init__(game_events)

        self.scoring_labels = scoring_labels

        self._finished = False
        self.game_over = Labels.font.render("TIME", True,
                                            pygame.Color('white'))
        self.game_over_pos = make_vector(
            *config.screen_rect.center) - make_vector(
                self.game_over.get_width() // 2,
                self.game_over.get_height() // 2)
        pygame.mixer_music.stop()

        self._time_left = TimeOut.DURATION
Example #12
0
    def set_foot_y_coord(self, ycoord):
        r = self._get_active_hitbox().rect.copy()

        r.bottom = ycoord

        self.position = make_vector(self.position.x,
                                    r.top - self._get_hitbox_offset()[1])
Example #13
0
    def __init__(self,
                 entity,
                 collider_manager,
                 parameters,
                 movement_mask=constants.Block | constants.Enemy,
                 on_collision_callback=None):
        assert entity is not None
        assert collider_manager is not None
        assert parameters is not None

        super().__init__()

        self.entity = entity
        self.collider_manager = collider_manager
        self.on_collision = on_collision_callback

        self.horizontal_movement_collider = Collider.from_entity(
            entity, collider_manager, movement_mask)

        self.parameters = parameters  # type: CharacterParameters
        self.velocity = make_vector(0., 0.)
        self.vertical_movement = GravityMovement(entity, collider_manager,
                                                 parameters)
        self.vertical_movement.airborne_collider.mask = movement_mask & constants.Block

        # allow other things to collide with our movement collider. Note they
        # must have a mask that hits Layer.Enemy to do this
        collider_manager.register(self.horizontal_movement_collider)
Example #14
0
    def __init__(self, relative_position, sb_type, width_or_height, sb_background,
                 sb_button_background, sb_max_value, sb_min_value=0, sb_button_mouseover=None,
                 on_value_changed_callback=None):
        # todo: to implement anchor, some assumptions in positioning code need fixing. low priority for now

        if sb_type == ScrollbarType.VERTICAL:
            initial_rect = Rect(*relative_position, Scrollbar.VERTICAL_WIDTH, width_or_height)
        else:
            initial_rect = Rect(*relative_position, width_or_height, Scrollbar.HORIZONTAL_HEIGHT)

        super().__init__(relative_position, initial_rect)

        assert sb_min_value <= sb_max_value

        self.sb_type = sb_type
        self.background = sb_background
        self.sb_button = sb_button_background
        self.max_value = sb_max_value
        self.min_value = sb_min_value
        self.on_value_changed = None

        # create slider button
        self.slider = _SliderButton(make_vector(self.width // 2, self.height // 2), None, self, sb_button_background,
                                    sb_button_mouseover)
        self.add_child(self.slider)

        self.layout()

        self.value = sb_min_value
        self.on_value_changed = on_value_changed_callback
Example #15
0
    def __init__(self, assets, entity_manager, stats, title="Not Titled"):
        super().__init__()

        assert entity_manager is not None
        assert assets is not None

        self.entity_manager = entity_manager
        self.tile_map = TileMap((60, 20), assets.tileset)
        self.collider_manager = ColliderManager(self.tile_map)
        self.background_color = (0, 0, 0)
        self.filename = ""
        self.normal_physics = True
        self.stats = stats
        self.title = title
        self.loaded_from = ""

        self.asset_manager = assets
        self.player_input = PlayerInputHandler()
        self.mario = Mario(self.player_input, self)
        self.mario.enabled = False

        self._scroll_position = make_vector(0, 0)
        self._view_rect = Rect(0, 0, config.screen_rect.width,
                               config.screen_rect.height)
        self._cleared = False
        self._timed_out = False
Example #16
0
    def create_tool(self,
                    tools,
                    selected_image_name,
                    unselected_image_name,
                    font,
                    selected_cb,
                    deselected_cb,
                    y_offset=0):
        tool_static = self.gui_atlas.load_static(unselected_image_name)
        tool_hl_static = self.gui_atlas.load_static(selected_image_name)

        offset_x = tools[len(tools) - 1].relative_position.x + tool_static.get_rect().width\
            if len(tools) > 0 else 10

        tool = Option(
            make_vector(offset_x,
                        self.get_title_bar_bottom() + y_offset + 5),
            tool_hl_static.get_rect().size,
            background=self.gui_atlas.load_sliced("control_small_block"),
            font=font,
            selected_image=tool_hl_static,
            unselected_image=tool_static,
            text="",
            on_selected_callback=selected_cb,
            on_deselected_callback=deselected_cb,
            is_selected=False)

        self.add_child(tool)
        tools.append(tool)

        return tool
Example #17
0
    def update(self, dt, view_rect):
        self.collider.position = self.position

        # is mario standing on the platform?
        self.platform_tester.update(dt)

        mario = self.level.mario
        mario_foot_y = mario.movement.get_foot_position().y

        if mario.glued or self._pushed:

            # todo: platform can drag mario into solid blocks, fix

            pos = self.position
            pos.y += Platform.FALL_RATE * dt

            for collision in self.collider.try_move(pos, True):
                if collision.hit_collider and collision.hit_collider.entity:
                    thing_hit = collision.hit_collider.entity

                    if isinstance(thing_hit, Platform):
                        # shove it out of the way
                        thing_hit.position = make_vector(thing_hit.position.x,
                                                         thing_hit.position.y + Platform.FALL_RATE * dt)
                    else:
                        self.collider.position = self.position  # todo: better way to handle this? hit a block

            self.position = self.collider.position

            # want to align mario's feet with our top if he's glued onto the platform
            if mario.glued:
                # mario has been glued onto the platform, we're responsible for moving him (ypos) now
                mario.movement.set_foot_y_coord(self.position.y)
Example #18
0
    def __init__(self,
                 position,
                 size,
                 background,
                 font,
                 anchor=Anchor.TOP_LEFT,
                 text=None,
                 on_click_callback=None,
                 text_color=config.default_text_color,
                 mouseover_image=None):
        if size is None or (size[0] == 0 and size[1] == 0):
            size = background.get_rect().size

        super().__init__(
            position,
            Rect(*position, *size),
            anchor,
        )
        self._background = background
        self._background_mouseover = mouseover_image or background
        self._text = None

        if text is not None:
            self._text = Text(make_vector(size[0] // 2 + 1, size[1] // 2 + 2),
                              text,
                              font,
                              text_color,
                              anchor=Anchor.CENTER)

            self.add_child(self._text)

        self.on_click = on_click_callback
        self._click_down = False
        self._mouseover = False
        self.layout()
Example #19
0
    def respawn(self):
        koopa = KoopaTroopa(self.level)
        koopa.position = get_aligned_foot_position(self.rect, koopa.rect)
        koopa.movement.velocity = make_vector(self.original_velocity.x, 0.)  # original koopa might've been falling
        self.level.entity_manager.register(koopa)

        self.destroy()
Example #20
0
    def set_foot_position(self, foot_position):
        r = self._get_active_hitbox().rect.copy()

        r.midbottom = foot_position

        self.position = make_vector(r.left - self._get_hitbox_offset()[0],
                                    r.top - self._get_hitbox_offset()[1])
Example #21
0
    def on_hit(self, collision):
        # note: don't update velocity inside here: it's quite possible multiple collisions have occurred
        # in the same frame, and if two enemies are very close together, they might end up fighting to
        # change direction over and over
        if collision.hit_block:
            # we hit a block, but it could have been from above. Determine if block is left or right
            is_left = any(self.horizontal_movement_collider.test(
                self.horizontal_movement_collider.position - make_vector(1, 0)))
            is_right = any(self.horizontal_movement_collider.test(
                self.horizontal_movement_collider.position + make_vector(1, 0)))

            # only flip directions if it will improve situation
            if (is_left and self.velocity.x < 0.) or (is_right and self.velocity.x > 0.):
                self._reverse_direction = True
        elif collision.hit_collider is not None and collision.hit_collider.layer == constants.Enemy:
            self._reverse_direction = True
Example #22
0
    def __init__(self, relative_pos, visible_size, content):
        super().__init__(relative_pos, initial_rect=Rect(0, 0, *visible_size))

        self.content_rect = Rect(0, 0, *content.get_rect().size)
        self.scroll_pos = make_vector(0, 0)
        self.content = content
        self.set_scroll(self.scroll_pos)
Example #23
0
    def _handle_vertical_movement(self, dt):
        self.update_airborne()

        current_velocity = copy_vector(self.velocity)

        if not self._airborne:
            current_velocity.y = 0
        else:
            current_velocity.y += (self.parameters.gravity * dt)

            # limit downward velocity
            if current_velocity.y > self.parameters.max_vertical_velocity:
                current_velocity.y = self.parameters.max_vertical_velocity

        vel = make_vector(0, current_velocity.y)
        target_pos = self.entity.position + vel * dt
        self.airborne_collider.position = self.entity.position
        self.velocity = current_velocity

        # very important to be accurate with vertical movement because of the single pixel downward we use
        # for airborne detection.
        if any(self.airborne_collider.try_move(target_pos, True)):
            self.airborne_collider.approach(target_pos)
            self._airborne = False

            if self.on_hit:
                self.on_hit()

        self.entity.position = self.airborne_collider.position
Example #24
0
    def update(self, dt, view_rect):
        super().update(dt, view_rect)

        if self._is_tracking:
            mario = self.level.mario

            delta_y = BowserFireball.TRACK_VELOCITY * dt
            mario_y = mario.movement.get_center_of_mass().y

            dist = math.fabs(self.position.y - mario_y)
            if dist < BowserFireball.LOCK_ON_DISTANCE:
                self._locked_on_time += dt

                if self._locked_on_time >= BowserFireball.TARGET_ACQUIRE_TIME:
                    self._is_tracking = False
            else:
                self._locked_on_time = 0.

            if self._is_tracking:
                # approach mario's coordinate
                if dist < delta_y:  # we're close enough to just match it
                    self.position = make_vector(self.position.x, mario_y)
                else:
                    our_pos = self.position

                    our_pos.y += delta_y * (1. if our_pos.y < mario_y else -1.)

                    # move to this position if we can (if there's a block in the way, stay put and stop tracking)
                    if self.ground_detector.test(our_pos, False):
                        # collided!
                        self._is_tracking = False
                    else:
                        self.position = our_pos
Example #25
0
    def position(self, new_pos):
        max_w, max_h = self.tile_map.width_pixels - self._view_rect.width, \
                       self.tile_map.height_pixels - self._view_rect.height

        self._scroll_position = make_vector(min(max_w, new_pos[0]),
                                            min(max_h, new_pos[1]))
        self._view_rect.topleft = self._scroll_position
Example #26
0
    def update(self, dt, view_rect):
        super().update(dt, view_rect)

        self.animation.update(dt)
        self.harm.update(dt)

        self._timer += dt

        fraction_hidden = 0.

        if self._emerging:
            fraction_hidden = 1 - min(
                self._timer / PiranhaPlant.EXTEND_PARAMETERS[0], 1.)
            if self._timer > sum(PiranhaPlant.EXTEND_PARAMETERS):
                self._timer = 0.
                self._emerging = False
        else:  # must be retracting
            fraction_hidden = min(
                self._timer / PiranhaPlant.RETRACT_PARAMETERS[0], 1.)

            if self._timer > sum(PiranhaPlant.RETRACT_PARAMETERS):
                self._timer = 0
                self._emerging = True

        self.position = make_vector(
            self.visible_rect.left,
            self.visible_rect.top + self.rect.height * fraction_hidden)
        self.position_collider.position = self.position
Example #27
0
def make_brick_uw(level, values):
    brick = BrickUw(level, make_vector(0, 0))

    if values is not None:
        brick.deserialize(values)

    return brick
Example #28
0
    def explode(self):
        self._dead = True

        self.level.entity_manager.unregister(self)

        explode_anim = self.level.asset_manager.interactive_atlas.load_animation(
            "fireball_explode")

        from .corpse import Corpse

        explosion = Corpse(self.level, explode_anim, Corpse.STATIONARY,
                           explode_anim.duration, True)
        explosion.position = self.position + make_vector(self.rect.width // 2, self.rect.height // 2) -\
            make_vector(explosion.rect.width // 2, explosion.rect.height // 2)

        self.level.entity_manager.register(explosion)
Example #29
0
    def update(self, dt):
        self._elapsed += dt

        if self._elapsed < LevelCleared.FLAG_SLIDE_TIME:
            flag_ratio = self._elapsed / LevelCleared.FLAG_SLIDE_TIME
            self.pole.set_flag_position(flag_ratio)
        else:
            if not self.waiting:
                self.waiting = True

                self.pole.set_flag_position(1.0)

                # drop mario and make him move right
                self.mario.position = make_vector(self.pole.position.x, self.doppler.position.y)
                self.mario.reset()
                self.mario.enabled = True
                self.mario.movement.input_state = _FakeInput()

                # spawn point value
                FloatyPoints.display(self.level, self.points, self.pole)

                # add time and flag to score
                self.level.stats.score += self.points
                self.level.stats.score += self.level.stats.remaining_time

                # now wait for fake mario to reach castle
                # a trigger should be set up to hide him or move into a pipe

            self.mario.update(dt, self.level.view_rect)
            self.level.update_triggers_only(dt)
Example #30
0
    def draw(self, screen: pygame.Surface, view_rect):
        super().draw(screen, view_rect)

        # calc position of selected tile, ignoring scroll position for the moment
        coords = tile_index_to_coords(self.selected_tile_idx, self.tileset)

        tile_x = coords[0] * self.tileset.tile_width
        tile_y = coords[1] * self.tileset.tile_height

        # account for scrolling
        top_left = self.scrollable.get_absolute_position() + make_vector(
            tile_x - self.scrollable.get_scroll().x,
            tile_y - self.scrollable.get_scroll().y)
        r = pygame.Rect(*top_left, self.tileset.tile_width,
                        self.tileset.tile_height)
        r.inflate_ip(2, 2)

        existing_clip = screen.get_clip()

        cr = self.rect.copy()
        cr.top += self.get_title_bar_bottom()
        cr.height -= self.get_title_bar_bottom()
        cr.height -= 20  # account for thick frame

        screen.set_clip(cr)

        pygame.draw.rect(screen, (255, 0, 0), r, 3)

        screen.set_clip(existing_clip)