Пример #1
0
class Corpse(Projectile):
    STATIONARY = CharacterParameters(0., 0., 0., 0., 0.)
    STANDARD = CharacterParameters(0., mstpvv('04200'), mstpva('00280'), mstpvv('04000'), mstpvv('04000'))

    """Stays on-screen for a while, then disappears."""

    def __init__(self, level, animation, parameters, duration, ignore_ground=False):
        super().__init__(animation, level, parameters, (0, 0), animation.rect.size, 0, 0)

        self.level = level
        self.duration = duration
        self.animation = animation

        # kind of kludgy, but we need to edit GravityMovement's colliders if the corpse
        # is to ignore the ground
        if ignore_ground:
            self.movement.vertical_movement.airborne_collider.mask = 0  # will always be "airborne"

    def update(self, dt, view_rect):
        super().update(dt, view_rect)

        self.duration -= dt

        if self.duration < 0.:
            self.destroy()

    @property
    def layer(self):
        return constants.Active

    def destroy(self):
        self.level.entity_manager.unregister(self)

    def on_movement_collision(self, collision):
        pass

    def on_hit(self, collision):
        pass

    @staticmethod
    def create_corpse_animation(animation):
        # creates a simple corpse by flipping current animation frame
        current_frame = animation.image

        corpse = pygame.transform.flip(current_frame, False, True)

        return StaticAnimation(corpse)

    @staticmethod
    def create_ghost_corpse_from_entity(entity, entity_animation, level, duration, parameters=STATIONARY, initial_y=0.):
        # creates a special type of corpse which is inverted and falls off the screen, ignoring blocks
        corpse_animation = Corpse.create_corpse_animation(entity_animation)

        corpse = Corpse(level, corpse_animation, parameters, duration, ignore_ground=True)
        corpse.position = get_aligned_foot_position(entity.rect, corpse.rect)

        corpse.movement.velocity = make_vector(0., initial_y or -parameters.jump_velocity)
        return corpse
Пример #2
0
    def __init__(self, level):
        super().__init__(level)

        ca = level.asset_manager.character_atlas

        self.left_animation = ca.load_animation("koopa_red_winged_left")
        self.right_animation = ca.load_animation("koopa_red_winged_right")

        from .behaviors.koopa_floating import KoopaFloating
        self.movement.destroy()
        self.movement = KoopaFloating(level, self,
                                      WingedKoopaTroopaRed.FLY_HEIGHT,
                                      WingedKoopaTroopaRed.FLY_FREQUENCY,
                                      make_vector(0, 0), mstpvv('08000'),
                                      self.on_squashed,
                                      self._on_mario_invincible)

        self.squashable.on_squashed = self.on_squashed
Пример #3
0
class BowserFireball(Projectile):
    """Bowser's tracking fireball"""
    TRACK_VELOCITY = mstpvv(
        '01200')  # how quickly the fireball can alter position to track mario
    TARGET_ACQUIRE_TIME = 0.1  # have to be within lock-on distance for this amount of time before stop tracking
    LOCK_ON_DISTANCE = config.base_tile_dimensions[
        1] * config.rescale_factor * 0.75

    def __init__(self, level):
        self.animation = level.asset_manager.interactive_atlas.load_animation(
            "bowser_fireball")

        super().__init__(self.animation, level, bowser_fb_parameters, (6, 2),
                         (6, 6), constants.Block, constants.Mario)

        self.level = level
        self.ground_detector = Collider.from_entity(self,
                                                    level.collider_manager,
                                                    constants.Block)

        self.movement.velocity = make_vector(
            -bowser_fb_parameters.max_horizontal_velocity, 0.)

        # state
        self._is_tracking = True
        self._locked_on_time = 0.
        self._dead = False

    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

    def die(self):
        self.level.entity_manager.unregister(self)
        self.movement.destroy()
        self.level.asset_manager.sounds['stomp'].play()
        self._dead = True

    def on_movement_collision(self, collision):
        if not self._dead:
            self.die()

    def on_hit(self, collision):
        from .behaviors import DamageMario

        mario = self.level.mario

        if mario.is_starman:
            self.die()
        elif not mario.is_invincible:
            DamageMario.hurt_mario(self.level, self.level.mario)
Пример #4
0
import math
from util import make_vector
import constants
from .projectile import Projectile
from .parameters import CharacterParameters
from util import mario_str_to_pixel_value_velocity as mstpvv
from ..collider import Collider
import config

bowser_fb_parameters = CharacterParameters(mstpvv('00800'), mstpvv('01600'),
                                           0., 0., 0.)


class BowserFireball(Projectile):
    """Bowser's tracking fireball"""
    TRACK_VELOCITY = mstpvv(
        '01200')  # how quickly the fireball can alter position to track mario
    TARGET_ACQUIRE_TIME = 0.1  # have to be within lock-on distance for this amount of time before stop tracking
    LOCK_ON_DISTANCE = config.base_tile_dimensions[
        1] * config.rescale_factor * 0.75

    def __init__(self, level):
        self.animation = level.asset_manager.interactive_atlas.load_animation(
            "bowser_fireball")

        super().__init__(self.animation, level, bowser_fb_parameters, (6, 2),
                         (6, 6), constants.Block, constants.Mario)

        self.level = level
        self.ground_detector = Collider.from_entity(self,
                                                    level.collider_manager,
Пример #5
0
from .enemy import Enemy
from .parameters import CharacterParameters
from util import world_to_screen, mario_str_to_pixel_value_velocity as mstpvv
from util import mario_str_to_pixel_value_acceleration as mstpva
from .level_entity import LevelEntity
import constants

bowser_parameters = CharacterParameters(mstpvv('00500'), mstpvv('04000'),
                                        mstpva('00200'), mstpvv('03000'),
                                        mstpvv('02000'))


class FakeBowser(Enemy):
    BOWSER_HITPOINTS = 5  # number of fireballs needed to kill
    DELAY_LEVEL_END = 3.  # how long to wait after bowser dies to end the level

    def __init__(self, level):
        ca = level.asset_manager.character_atlas

        self.animation_open = ca.load_animation("bowser_left_mouth_open")
        self.animation_close = ca.load_animation("bowser_left_mouth_closed")

        super().__init__(level, self.animation_close.get_rect())

        from .behaviors.bowser_logic import BowserLogic
        from .behaviors.simple_movement import SimpleMovement

        self.level = level
        self.movement = SimpleMovement(self, level.collider_manager,
                                       bowser_parameters)
        self.logic = BowserLogic(self, level, self.movement)
Пример #6
0
class MarioDeath(Entity, EventHandler):
    FREEZE_LENGTH = 0.25  # in seconds
    VELOCITY = mstpvv('04000')
    GRAVITY = mstpva('00200')

    def __init__(self, level, position):
        animation_name = "mario_fire_dead" if level.mario.effects & entities.characters.mario.MarioEffectFire \
            else "mario_dead"
        self.animation = level.asset_manager.character_atlas.load_static(
            animation_name)

        super().__init__(self.animation.get_rect())

        self.level = level
        self.position = position

        self._elapsed = 0.
        self._applied_jump = False
        self._velocity = make_vector(0, 0)
        self._finished = False

        # play death music
        pygame.mixer_music.load('sounds/music/smb_mariodie.wav')
        pygame.mixer_music.set_endevent(pygame.USEREVENT)

        pygame.mixer_music.play()

        state_stack.top.game_events.register(self)

    def update(self, dt, view_rect):
        self._elapsed += dt

        if not self._applied_jump and self._elapsed > MarioDeath.FREEZE_LENGTH:
            # apply jump
            self._velocity.y = -MarioDeath.VELOCITY
            self._applied_jump = True

        if self._applied_jump:
            self._velocity.y += MarioDeath.GRAVITY * dt

        self.position = self.position + self._velocity * dt

        if self._finished:
            self.level.entity_manager.unregister(self)

            # Decrement live counter
            self.level.stats.lives -= 1

            # if have more lives, display world start again
            if self.level.stats.lives > 0:
                # reset any state mario might have had
                self.level.mario.effects = entities.characters.mario.MarioEffectSmall

                # kludgy :( no time to do it the nice way though
                run_session = state_stack.top

                while run_session is not None and not isinstance(
                        run_session, state.run_session.RunSession):
                    run_session = state_stack.get_next(run_session)

                state_stack.push(
                    state.level_begin.LevelBegin(self.level.asset_manager,
                                                 self.level,
                                                 run_session.scoring_labels,
                                                 self.level.stats))

                # also resetore level state...
                self.level.reset()

    def draw(self, screen, view_rect):
        screen.blit(self.animation.image,
                    world_to_screen(self.position, view_rect))

    def handle_event(self, evt, game_events):
        # only event we care about is the end of sound one
        if evt.type == pygame.USEREVENT:
            self._finished = True
            pygame.mixer_music.set_endevent()
            state_stack.top.game_events.unregister(self)

    @property
    def layer(self):
        return constants.Active
Пример #7
0
from event import PlayerInputHandler
from ..parameters import CharacterParameters
from util import mario_str_to_pixel_value_velocity as mstpvv
from util import mario_str_to_pixel_value_acceleration as mstpva
from util import make_vector
from ..fireball import Fireball

fireball_parameters = CharacterParameters(mstpvv('03900'), mstpvv('02400'),
                                          mstpva('00300'), mstpvv('02400'), 0.)


class FireballThrow:
    DELAY = 0.25

    def __init__(self, level, input_state):
        super().__init__()

        self.input_state = input_state  # type: PlayerInputHandler
        self.level = level

        # state
        self._fired = False
        self._cooldown = 0.

    def update(self, dt):
        from .mario import MarioEffectFire, MarioEffectStar

        mario = self.level.mario

        # only let mario throw fire if:
        #  button pressed
Пример #8
0
from entities.entity import Entity
import entities.characters.behaviors
from .parameters import CharacterParameters
from util import world_to_screen, mario_str_to_pixel_value_acceleration as mstpva, \
    mario_str_to_pixel_value_velocity as mstpvv
import entities.effects
import constants
from util import make_vector
from .floaty_points import FloatyPoints

# todo: scale this by rescale factor?
mushroom_movement = CharacterParameters(50, mstpvv('03800'), mstpva('00300'),
                                        0., mstpva('00300'))


class Mushroom(Entity):
    POINT_VALUE = 1000

    def __init__(self, level, position):
        self.animation = level.asset_manager.pickup_atlas.load_static(
            "mushroom_red")

        super().__init__(self.animation.image.get_rect())

        self.level = level
        self.pickup = entities.characters.behaviors.interactive.Interactive(
            level, self, (0, 0), (16, 16), self.on_collected)
        self.movement = entities.characters.behaviors.simple_movement.SimpleMovement(
            self, level.collider_manager, mushroom_movement)
        self.movement.horizontal_movement_collider.mask = constants.Block  # exclude enemies
Пример #9
0
import pygame
from .corpse import Corpse
from util import make_vector
from util import mario_str_to_pixel_value_velocity as mstpvv
from ..entity import Entity
from .parameters import CharacterParameters

floaty_font = None
floaty_parameters = CharacterParameters(0., mstpvv('01500'), 0.,
                                        mstpvv('00950'), 0.)


class FloatyPoints(Corpse):
    DURATION = 0.33

    def __init__(self, level, points):
        if isinstance(points, int):
            points = str(points)

        global floaty_font
        from animation import StaticAnimation

        # lazy load font
        floaty_font = floaty_font or pygame.font.Font(
            "scoring/super_mario_font.ttf", 12)

        frame = floaty_font.render(points, True, pygame.Color('white'))

        animation = StaticAnimation(frame)

        super().__init__(level, animation, floaty_parameters,
Пример #10
0
from ..entity import Entity
import constants
from util import world_to_screen
from .parameters import CharacterParameters
from util import mario_str_to_pixel_value_velocity as mstpvv
from util import mario_str_to_pixel_value_acceleration as mstpva
from util import make_vector
from .floaty_points import FloatyPoints

starman_parameters = CharacterParameters(mstpvv('00750'), mstpvv('04000'),
                                         mstpva('00200'), mstpvv('02500'), 0.)


class Starman(Entity):
    DURATION = 15.
    POINT_VALUE = 2000

    def __init__(self, level, position):
        self.animation = level.asset_manager.pickup_atlas.load_animation(
            "star")

        super().__init__(self.animation.get_rect())

        from .behaviors import Interactive
        from .behaviors import JumpingMovement

        self.collect = Interactive(level, self, (4, 4), (9, 9),
                                   self.on_collected)
        self.level = level
        self.position = position
        self.movement = JumpingMovement(self, level.collider_manager,
Пример #11
0
import math
from .projectile import Projectile
from util import mario_str_to_pixel_value_velocity as mstpvv
from util import mario_str_to_pixel_value_acceleration as mstpva
from .parameters import CharacterParameters
from util import make_vector
from .behaviors import DamageMario
from . import Enemy
import constants

deadly_shell_parameters = CharacterParameters(mstpvv('03500'), mstpvv('04000'),
                                              mstpva('00300'), 0.,
                                              mstpvv('00700'))


class Shell(Projectile):
    # todo: mario stomping shell in motion
    # todo: richochet off pipes
    # todo: don't kill offscreen enemies
    """Deadly version of the shell"""
    def __init__(self, level, direction, shell_animation):
        self.level = level
        self.sounds = level.asset_manager.sounds

        from animation import StaticAnimation

        self.shell = StaticAnimation(shell_animation.image)

        super().__init__(self.shell, level, deadly_shell_parameters, (1, 1),
                         (14, 14), constants.Block,
                         constants.Enemy | constants.Mario)
Пример #12
0
class Platform(LevelEntity):
    FALL_RATE = mstpvv('01500')

    """A horizontal platform that moves downward as mario stands on it"""
    def __init__(self, level):
        self.animation = level.asset_manager.interactive_atlas.load_static("horizontal_platform")

        super().__init__(self.animation.get_rect())

        self.level = level

        from entities.collider import Collider
        from entities.characters.behaviors import Interactive

        # need to register a collider in the world for mario to stand on
        self.collider = Collider.from_entity(self, level.collider_manager, constants.Block)
        level.collider_manager.register(self.collider)

        # now another collider to let us know when to drop the platform
        # remember: it assumes unscaled values, while animation has been scaled
        self.platform_tester = Interactive(level, self,
                                           (0, -3),
                                           (self.rect.width / config.rescale_factor,
                                            self.rect.height / config.rescale_factor),
                                           self.attach_mario,
                                           self.detach_mario)

        # state
        self._attached = False
        self._pushed = False

    def attach_mario(self, collision):
        mario = self.level.mario

        mario.glued = mario.vertical_speed >= 0 and air_max_vertical_velocity >= Platform.FALL_RATE

    def detach_mario(self):
        self.level.mario.glued = False

    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)

    def draw(self, screen, view_rect):
        screen.blit(self.animation.image, world_to_screen(self.position, view_rect))
        self.platform_tester.draw(screen, view_rect)

    @property
    def layer(self):
        return constants.Block

    def destroy(self):
        self.level.collider_manager.unregister(self.collider)
        self.level.entity_manager.unregister(self)

    def create_preview(self):
        return self.animation.image.copy()

    @property
    def position(self):
        return super().position

    @position.setter
    def position(self, val):
        super(Platform, self.__class__).position.fset(self, val)
        self.collider.position = val
Пример #13
0
from entities.characters.level_entity import LevelEntity
from entities.characters import Corpse
from entities.entity import Entity
from entities.characters.enemy import Enemy
from entities.characters.behaviors import EnemyGroundMovement, Squashable
from . import CharacterParameters
import constants
from util import mario_str_to_pixel_value_velocity as mstpvv
from util import mario_str_to_pixel_value_acceleration as mstpva
from entities.collider import Collider
from .behaviors import SimpleMovement
from util import get_aligned_foot_position, world_to_screen, make_vector
from .shell import Shell
from .floaty_points import FloatyPoints

koopa_parameters = CharacterParameters(35, mstpvv('04800'), mstpva('00300'), 100, mstpvv('04200'))

# todo: red koopa, patrols a set area and doesn't suicide off ledges


class KoopaTroopa(Enemy):
    POINT_VALUE = 100  # when stomped into shell

    """Actively walking koopa, green, walks left until defeated or falls"""
    def __init__(self, level):
        self.level = level

        ca = level.asset_manager.character_atlas

        self.left_animation = ca.load_animation("koopa_green_left")
        self.right_animation = ca.load_animation("koopa_green_right")
Пример #14
0
from .koopa_troopa import KoopaTroopa, StunnedKoopaTroopa
from entities.characters.level_entity import LevelEntity
from entities.characters import Corpse
from . import CharacterParameters
from util import mario_str_to_pixel_value_velocity as mstpvv
from util import mario_str_to_pixel_value_acceleration as mstpva
from util import get_aligned_foot_position
from .behaviors.smart_enemy_ground_movement import SmartEnemyGroundMovement
import config
from util import make_vector, copy_vector
from .floaty_points import FloatyPoints

# todo: tweak movement characteristics
koopa_red_parameters = CharacterParameters(40, mstpvv('04800'),
                                           mstpva('00300'), 100,
                                           mstpvv('04200'))
winged_koopa_red_parameters = CharacterParameters(40, mstpvv('04800'),
                                                  mstpva('00300'), 100,
                                                  mstpvv('08000'))


class KoopaTroopaRed(KoopaTroopa):
    PATROL_RANGE = 300. * config.rescale_factor

    def __init__(self, level):
        super().__init__(level)

        ca = level.asset_manager.character_atlas

        self.left_animation = ca.load_animation("koopa_red_left")
        self.right_animation = ca.load_animation("koopa_red_right")