Beispiel #1
0
    def __init__(self, screen):
        super(PlatformingScene, self).__init__(screen)

        self.background_drawn = False
        self.headersize = 30

        self.camera = pygame.rect.Rect(0, 0, game_constants.w,
                                       game_constants.h)
        self.special_chars = SpecialChars()
        self.movelist = []
        self.time_elapsed = 0

        self.health = Health(5)
        self.score = Score(screen)
        self.player = Player(
            (screen.get_width() / 2, screen.get_height() / 2 - 30))
        self.playergroup = RenderUpdatesDraw(self.player)

        self.platforms = {}
        self.platforms[game_constants.h - 80] = [
            general.Box(-10000, game_constants.h - 80, 20000, 16)
        ]

        self.statics = RenderUpdatesDraw()
        self.statics.add(self.platforms.values()[0])
        self.screenstatics = RenderUpdatesDraw()
        self.actives = RenderUpdatesDraw()
        self.powerups = RenderUpdatesDraw()

        self.place_platforms()
        self.player.colliders = self.screenstatics

        self.header = pygame.Surface((screen.get_width(), self.headersize))
        self.header.fill(Color.BLACK)

        self.background = pygame.Surface(screen.get_size()).convert()

        gradiation = 128.0
        for i in xrange(int(gradiation)):
            color = (150 - (i * 150 / gradiation),
                     150 - (i * 150 / gradiation), 200)
            rect = (0, i * game_constants.h / gradiation, game_constants.w,
                    game_constants.h / gradiation + 1)
            self.background.fill(color, rect)

        # Move sprites into or out of 'screenstatics' group based on whether they're in camera
        self.screencheck()

        # fixme: rearchitect this
        self.challenging = False
Beispiel #2
0
    def __init__(self, screen):
        super(PlatformingScene, self).__init__(screen)

        self.background_drawn = False
        self.headersize = 30

        self.camera = pygame.rect.Rect(0, 0, game_constants.w, game_constants.h)
        self.special_chars = SpecialChars()
        self.movelist = []
        self.time_elapsed = 0

        self.health = Health(5)
        self.score = Score(screen)
        self.player = Player((screen.get_width() / 2, screen.get_height() / 2 - 30))
        self.playergroup = RenderUpdatesDraw(self.player)

        self.platforms = {}
        self.platforms[game_constants.h - 80] = [general.Box(-10000, game_constants.h - 80, 20000, 16)]

        self.statics = RenderUpdatesDraw()
        self.statics.add(self.platforms.values()[0])
        self.screenstatics = RenderUpdatesDraw()
        self.actives = RenderUpdatesDraw()
        self.powerups = RenderUpdatesDraw()

        self.place_platforms()
        self.player.colliders = self.screenstatics

        self.header = pygame.Surface((screen.get_width(), self.headersize))
        self.header.fill(Color.BLACK)

        self.background = pygame.Surface(screen.get_size()).convert()

        gradiation = 128.0
        for i in xrange(int(gradiation)):
            color = (150 - (i * 150 / gradiation), 150 - (i * 150 / gradiation), 200)
            rect = (0, i * game_constants.h / gradiation, game_constants.w, game_constants.h / gradiation + 1)
            self.background.fill(color, rect)

        # Move sprites into or out of 'screenstatics' group based on whether they're in camera
        self.screencheck()

        # fixme: rearchitect this
        self.challenging = False
Beispiel #3
0
    def __init__(self, position):
        super(Player, self).__init__()

        self.state = States.falling
        self.colliders = None

        self.loadanims()
        self.direction = 'r'
        self.jumpdir = 'u'
        self.moving = False
        self.delayframes = 0
        self.weapon = Weapons.normal
        self.shots_left = 0
        self.bullets = RenderUpdatesDraw()
        self.velocity = 0

        self.selected_opponent = None
        self.position = position
        self.setanim()  # Also sets self.rect for collisions
Beispiel #4
0
    def __init__(self, position):
        super(Player, self).__init__()

        self.state = States.falling
        self.colliders = None

        self.loadanims()
        self.direction = 'r'
        self.jumpdir = 'u'
        self.moving = False
        self.delayframes = 0
        self.weapon = Weapons.normal
        self.shots_left = 0
        self.bullets = RenderUpdatesDraw()
        self.velocity = 0

        self.selected_opponent = None
        self.position = position
        self.setanim() # Also sets self.rect for collisions
Beispiel #5
0
class PlatformingScene(BaseScene):
    """ Class for the main playable game environment. """
    def __init__(self, screen):
        super(PlatformingScene, self).__init__(screen)

        self.background_drawn = False
        self.headersize = 30

        self.camera = pygame.rect.Rect(0, 0, game_constants.w,
                                       game_constants.h)
        self.special_chars = SpecialChars()
        self.movelist = []
        self.time_elapsed = 0

        self.health = Health(5)
        self.score = Score(screen)
        self.player = Player(
            (screen.get_width() / 2, screen.get_height() / 2 - 30))
        self.playergroup = RenderUpdatesDraw(self.player)

        self.platforms = {}
        self.platforms[game_constants.h - 80] = [
            general.Box(-10000, game_constants.h - 80, 20000, 16)
        ]

        self.statics = RenderUpdatesDraw()
        self.statics.add(self.platforms.values()[0])
        self.screenstatics = RenderUpdatesDraw()
        self.actives = RenderUpdatesDraw()
        self.powerups = RenderUpdatesDraw()

        self.place_platforms()
        self.player.colliders = self.screenstatics

        self.header = pygame.Surface((screen.get_width(), self.headersize))
        self.header.fill(Color.BLACK)

        self.background = pygame.Surface(screen.get_size()).convert()

        gradiation = 128.0
        for i in xrange(int(gradiation)):
            color = (150 - (i * 150 / gradiation),
                     150 - (i * 150 / gradiation), 200)
            rect = (0, i * game_constants.h / gradiation, game_constants.w,
                    game_constants.h / gradiation + 1)
            self.background.fill(color, rect)

        # Move sprites into or out of 'screenstatics' group based on whether they're in camera
        self.screencheck()

        # fixme: rearchitect this
        self.challenging = False

    def lazy_redraw(self):
        return False

    def fill_level(self, height):
        platform_level = self.platforms.setdefault(height, [])

        max_right = self.camera.right + game_constants.w
        min_left = self.camera.left - game_constants.w

        # cull platforms that have gone too far -- TODO this doesn't work yet
        bad_platforms = [
            p for p in platform_level
            if p.rect.left > max_right or p.rect.right < min_left
        ]
        [platform_level.remove(p) for p in bad_platforms]
        [self.statics.remove(p) for p in bad_platforms]

        # add on platforms until the limit is reached
        if not len(platform_level):
            leftmost = random.randint(-game_constants.w, game_constants.w)
            rightmost = leftmost = random.randint(-game_constants.w,
                                                  game_constants.w)
        else:
            leftmost = min([p.rect.left for p in platform_level])
            rightmost = max([p.rect.right for p in platform_level])

        while rightmost < max_right:
            new_start = rightmost + random.randint(150, 300)
            new_width = random.randint(200, 1000)
            new_width = new_width - new_width % 16  # Cut off to the nearest multiple of 16
            new_p = Platform(new_start, height, new_width, 16)
            platform_level.append(new_p)
            self.statics.add(new_p)
            rightmost = new_start + new_width
        while leftmost > min_left:
            new_start = leftmost - random.randint(150, 300)
            new_width = random.randint(200, 1000)
            new_width = new_width - new_width % 16  # Cut off to the nearest multiple of 16
            new_p = Platform(new_start - new_width, height, new_width, 16)
            platform_level.append(new_p)
            self.statics.add(new_p)
            leftmost = new_start - new_width

    def place_platforms(self):
        # Generate platforms based on camera position: Make sure there are always platforms extending at least as far as +-4*game_constants.w, +-4*game_constants.h from the player
        max_height = self.camera.top - 2 * game_constants.h
        lowest = max(self.platforms.keys())
        highest = min(self.platforms.keys())

        for h in range(highest, lowest, 80):
            self.fill_level(h)

        while highest > max_height:
            highest -= 80
            self.fill_level(highest)

    def draw_background(self):
        self.screen.blit(self.background, (0, 0))
        self.screen.blit(self.header, (0, 0))
        pygame.display.update()
        self.background_drawn = True

    def draw_header(self):
        dirty = []
        self.screen.set_clip(0, 0, game_constants.w,
                             self.headersize)  # Only draw in header area
        self.score.clear(self.screen, self.header)
        self.health.clear(self.screen, self.header)
        dirty += self.score.draw(self.screen, self.time_elapsed)
        dirty += self.health.draw(self.screen)
        return dirty

    def draw(self):
        if not self.background_drawn:
            self.draw_background()

        self.camshift()

        header_dirty = self.draw_header()

        dirty = []

        # Don't draw over header
        self.screen.set_clip(0, self.headersize, game_constants.w,
                             game_constants.h)

        rect_sources = [
            self.screenstatics,
            self.powerups,
            self.actives,
            self.playergroup,
            self.player.bullets,
        ]

        for rect_source in rect_sources:
            rect_source.clear(self.screen, self.background)

        for rect_source in rect_sources:
            dirty += rect_source.draw(self.screen, self.camera)

        # hack: repaint selected_opponent to make sure it's visible
        if self.player.selected_opponent:
            dirty += [
                self.player.selected_opponent.draw(self.screen, self.camera)
            ]

        # Constrain all dirty rectangles in main game area to main game area.
        dirty = [
            dirty_rect.clip(self.screen.get_clip()) for dirty_rect in dirty
        ]

        return header_dirty + dirty

    def camshift(self):
        newpos = self.player.rect
        bounds = self.camera.inflate(-game_constants.w + 50,
                                     -game_constants.h + 100)
        # Move the screen if we're near the edge
        if newpos.right > bounds.right:
            self.camera = self.camera.move(newpos.right - bounds.right, 0)
        elif newpos.left < bounds.left:
            self.camera = self.camera.move(newpos.left - bounds.left, 0)
        if newpos.bottom > bounds.bottom:
            self.camera = self.camera.move(0, newpos.bottom - bounds.bottom)
        elif newpos.top < bounds.top:
            self.camera = self.camera.move(0, newpos.top - bounds.top)
        self.screencheck()

    def screencheck(self):
        """Classify objects by whether they are on the screen"""

        for platform_level in self.platforms.values():
            for platform in platform_level:
                if platform not in self.screenstatics:
                    # This platform wasn't on the screen: is it now?
                    if platform.rect.colliderect(self.camera):
                        self.screenstatics.add(platform)
                        # If it's a platform (has the attribute'word') give it an
                        #   identifying word while it's onscreen
                        if hasattr(platform, 'word'):
                            platform.word = Word(self.special_chars.new(),
                                                 platform.font)
                else:
                    # This platform is on the screen: is it gone now?
                    if not platform.rect.colliderect(self.camera):
                        self.screenstatics.remove(platform)
                        # If it's a platform (has the attribute'word')
                        #  and it's out of camera, free up its symbol for later use
                        if hasattr(platform, 'word'):
                            self.special_chars.release(platform.word.string)
                            platform.word = None
        for sprite in self.powerups.sprites():
            if not sprite.rect.colliderect(
                    self.camera.inflate(game_constants.w, 0)):
                sprite.kill()
        for sprite in self.actives.sprites(
        ):  # need a list because we're going to delete from it
            # Opponents are allowed to be a full screen width outside of camera, but no further
            if not sprite.rect.colliderect(
                    self.camera.inflate(game_constants.w, 0)):
                if sprite == self.player.selected_opponent:
                    self.player.selected_opponent = None
                sprite.kill()

    def is_reachable(self, platform):
        player_rect = self.player.rect
        """ Is it possible for the player to jump to this platform in the concievable future? """
        # Test 1: the platform must be within jumping height
        if player_rect.bottom - game_constants.maxjump_height < platform.rect.top < player_rect.bottom:
            # Test 2: the player cannot be under the platform
            if (player_rect.right < platform.rect.left):
                # Test 3: the path from the player position to the optimal jump position must be contiguous
                jumpdist = platform.rect.left - player_rect.right
                if jumpdist < game_constants.maxjump_width:  # Maximum distance that can be jumped
                    return True
                else:
                    start = (player_rect.right, player_rect.bottom)
                    size = (jumpdist - game_constants.maxjump_width, 1)
                    testrect = pygame.rect.Rect(start, size)
                    for collider in self.screenstatics:
                        if collider.rect.contains(testrect):
                            return True
            elif (player_rect.left > platform.rect.right):
                # Test 3: the path from the player position to the optimal jump position must be contiguous
                jumpdist = player_rect.left - platform.rect.right
                if jumpdist < game_constants.maxjump_width:  # Maximum distance that can be jumped
                    return True
                else:
                    start = (platform.rect.right +
                             game_constants.maxjump_width, player_rect.bottom)
                    size = (jumpdist - game_constants.maxjump_width, 1)
                    testrect = pygame.rect.Rect(start, size)
                    for collider in self.screenstatics:
                        if collider.rect.contains(testrect):
                            return True

    def tick(self, elapsed):
        self.place_platforms()

        self.time_elapsed += elapsed

        for direction in self.movelist:
            self.player.direct(direction)

        for active_obj in self.actives:
            active_obj.tick(self.player.rect.center)

        for powerup in self.powerups:
            powerup.tick()

        for screenstatic in self.screenstatics:  # Objects on the screen
            # Only platforms have a 'word' attribute
            if hasattr(screenstatic, 'word'):
                # Will it be possible to get to this object without having to walk on air
                screenstatic.reachable = self.is_reachable(screenstatic)
        self.player.tick()

        if (self.health.value() <= 0):
            self.switch_to = GameOverScene(self.screen)

    def type_special(self, key):  # Typing a special character
        for platform_level in self.platforms.values():
            for platform in platform_level:
                if hasattr(platform, 'word'):  # HACK to ignore bottom platform
                    if platform.reachable and self.camera.colliderect(
                            platform.rect):
                        if key == platform.contents():
                            return platform
Beispiel #6
0
class Player(WrappedSprite):
    """ Logic, graphics and methods for a game protagonist """
    def __init__(self, position):
        super(Player, self).__init__()

        self.state = States.falling
        self.colliders = None

        self.loadanims()
        self.direction = 'r'
        self.jumpdir = 'u'
        self.moving = False
        self.delayframes = 0
        self.weapon = Weapons.normal
        self.shots_left = 0
        self.bullets = RenderUpdatesDraw()
        self.velocity = 0

        self.selected_opponent = None
        self.position = position
        self.setanim()  # Also sets self.rect for collisions

    @classmethod
    def loadImages(cls):
        super(Player, cls).loadImages()

        cls.images['upright'] = loadframes("gunstar",
                                           ["gunup1.png", "gunup2.png"])
        cls.images['upleft'] = flippedframes(cls.images['upright'])
        cls.images['upsideright'] = loadframes(
            "gunstar", ["gunupside1.png", "gunupside2.png"])
        cls.images['upsideleft'] = flippedframes(cls.images['upsideright'])
        cls.images['sideright'] = loadframes("gunstar",
                                             ["gunside1.png", "gunside2.png"])
        cls.images['sideleft'] = flippedframes(cls.images['sideright'])
        cls.images['downsideright'] = loadframes(
            "gunstar", ["gundownside1.png", "gundownside2.png"])
        cls.images['downsideleft'] = flippedframes(cls.images['downsideright'])
        cls.images['downright'] = loadframes("gunstar",
                                             ["gundown1.png", "gundown2.png"])
        cls.images['downleft'] = flippedframes(cls.images['downright'])
        cls.images['idleright'] = loadframes("gunstar",
                                             ["rest1.png", "rest2.png"])
        cls.images['idleleft'] = flippedframes(cls.images['idleright'])
        cls.images['runright'] = loadframes("gunstar", n_of("run%i.png", 6))
        cls.images['runleft'] = flippedframes(cls.images['runright'])
        cls.images['jumpright'] = loadframes("gunstar",
                                             ["jump1.png", "jump2.png"])
        cls.images['jumpleft'] = flippedframes(cls.images['jumpright'])
        cls.images['fallright'] = loadframes("gunstar", ["jump3.png"])
        cls.images['fallleft'] = flippedframes(cls.images['fallright'])
        cls.images['hitright'] = loadframes("gunstar",
                                            ["hit1.png", "hit2.png"])
        cls.images['hitleft'] = flippedframes(cls.images['hitright'])

    def loadanims(self):
        self.anims = {
            States.shooting: {
                'upright': Anim(self.images['upright'], (5, 5)),
                'upleft': Anim(self.images['upleft'], (5, 5)),
                'upsideright': Anim(self.images['upsideright'], (5, 5)),
                'upsideleft': Anim(self.images['upsideleft'], (5, 5)),
                'sideright': Anim(self.images['sideright'], (5, 5)),
                'sideleft': Anim(self.images['sideleft'], (5, 5)),
                'downsideright': Anim(self.images['downsideright'], (5, 5)),
                'downsideleft': Anim(self.images['downsideleft'], (5, 5)),
                'downright': Anim(self.images['downright'], (5, 5)),
                'downleft': Anim(self.images['downleft'], (5, 5)),
            },
            States.idle: {
                'r': Anim(self.images['idleright'], (30, 60)),
                'l': Anim(self.images['idleleft'], (30, 60)),
            },
            States.running: {
                'r': Anim(self.images['runright'], (14, ) * 6),
                'l': Anim(self.images['runleft'], (14, ) * 6),
            },
            States.jumping: {
                'r': Anim(self.images['jumpright'], (20, 40)),
                'l': Anim(self.images['jumpleft'], (20, 40)),
            },
            States.falling: {
                'r': Anim(self.images['fallright'], (20, )),
                'l': Anim(self.images['fallleft'], (20, )),
            },
            States.hit: {
                'r': Anim(self.images['hitright'], (5, 5)),
                'l': Anim(self.images['hitleft'], (5, 5)),
            },
        }

    def draw(self, surface, campos):
        return self.current_anim.draw(surface,
                                      self.rect.move((-campos[0], -campos[1])))

    def setanim(self):  # Set animation from state and direction
        if self.state != States.shooting:
            self.current_anim = self.anims[self.state][self.direction]

        # Want to ensure that new bottom = old bottom
        self.rect = self.current_anim.get_rect()
        self.rect.bottom = int(self.position[1])
        self.rect.centerx = int(self.position[0])

    def collide(self):
        return pygame.sprite.spritecollide(self, self.colliders, False)

    def direct(self, direction):
        self.moving = True
        if direction == K_LEFT:
            self.direction = 'l'
            if self.state not in (States.jumping, States.falling, States.hit):
                self.state = States.running
        elif direction == K_RIGHT:
            self.direction = 'r'
            if self.state not in (States.jumping, States.falling, States.hit):
                self.state = States.running

    def tick(self):
        self.delayframes += 1

        if self.state in (States.falling, States.jumping, States.hit):
            if self.velocity > game_constants.terminal_velocity:
                self.velocity -= .25

        # If we're falling, check how far we are from the nearest ground. If we're further than one tick's distance,
        # move jumpspeed units. If we're closer than one tick's distance, move directly to the ground.
        if self.state in (States.falling, States.hit):
            dist = distfromground(self.rect, self.colliders)
            if dist <= abs(self.velocity):
                self.move_vertical(dist)
                if self.state == States.hit:
                    if self.delayframes > game_constants.hitframes:
                        self.moving = True
                        self.state = States.running
                elif self.moving:
                    self.state = States.running
                else:
                    self.state = States.idle
            else:
                self.move_vertical(-self.velocity)
        elif self.state == States.jumping:
            dist = distfromceiling(self.rect, self.colliders)
            if dist <= self.velocity:
                self.move_vertical(-dist)
                self.state = States.falling
                self.velocity = 0
            else:
                self.move_vertical(-self.velocity)
            if self.velocity <= 0 or self.collide():
                self.state = States.falling
        elif self.state == States.running:
            newdist = distfromground(self.rect, self.colliders)
            if newdist > 0:
                self.state = States.falling
            if not self.moving:
                self.state = States.idle  # Not moving but run animation is being displayed

        if self.moving:
            jump_slow = False
            if self.state == States.jumping and self.jump_target <> None:
                # Determine if moving laterally too fast will botch the jump
                # to wit: will the top of the player rect be higher than the bottom of the platform
                # before the edge of the player closest to the platform is within the platform
                # Ugly fudge factor of five pixel units included.
                if self.direction == 'l':
                    if (self.rect.left - game_constants.speed - 5) < self.jump_target.rect.right and \
                       (self.rect.top > self.jump_target.rect.bottom):
                        jump_slow = True
                elif self.direction == 'r':
                    if (self.rect.right + game_constants.speed + 5) > self.jump_target.rect.left and \
                       (self.rect.top > self.jump_target.rect.bottom):
                        jump_slow = True

            if self.state in (
                    States.running, States.jumping, States.falling
            ) and not jump_slow:  # Left-Right movement is allowed
                oldpos, oldrect = self.position, self.rect
                if self.direction == 'l':
                    self.move_horizontal(-game_constants.speed)
                elif self.direction == 'r':
                    self.move_horizontal(game_constants.speed)
                if self.collide():
                    self.position, self.rect = oldpos, oldrect

        self.setanim()
        self.current_anim.tick()

        if self.selected_opponent:
            self.point(self.selected_opponent
                       )  # Point weapon in direction of selected thing

        for bullet in self.bullets:
            bullet.move()  # Move all the laser-things that might be on screen

        self.moving = False  # Require the move() function to refresh this every tick

    # fixme: these are dumb
    def move_vertical(self, vdist):
        self.position = self.position[0], self.position[1] + vdist
        self.rect.move(0, vdist)

    def move_horizontal(self, hdist):
        self.position = self.position[0] + hdist, self.position[1]
        self.rect.move(hdist, 0)

    def idle(self):
        self.state = States.idle

    def jump(self, jump_target=None):
        if self.state in (States.jumping, States.falling): return

        self.jump_target = jump_target
        self.delayframes = 0
        self.anims[States.jumping]['r'].reset()
        self.anims[States.jumping]['l'].reset()
        self.state = States.jumping
        self.velocity = 7

    def hit(self):  # Player was injured
        self.moving = False
        self.state = States.hit
        self.velocity = 0
        self.delayframes = 0

    def point(self, selected):  # Point gun at some quadrant
        # 1 _|_ 2
        # 4  |  3
        if (self.state not in (States.falling, States.jumping)):
            self.state = States.shooting

        x = self.rect.center[0] - selected.x
        y = self.rect.center[1] - selected.y
        h = math.sqrt(x**2 + y**2)
        angle = math.degrees(math.asin(y / h))

        anim_name = None
        if angle > 0:  # Quadrant 1 or 2
            if x < 0:  # Quadrant 2: up, diag or side
                if abs(angle) < 30:
                    anim_name = 'sideright'
                elif 30 < abs(angle) < 70:
                    anim_name = 'upsideright'
                else:
                    anim_name = 'upright'
            else:  # Quadrant 1: up, diag or side
                if abs(angle) < 30:
                    anim_name = 'sideleft'
                elif 30 < abs(angle) < 70:
                    anim_name = 'upsideleft'
                else:
                    anim_name = 'upleft'
        else:  # Quadrant 3 or 4
            if x < 0:  # Quadrant 3: side, diag or down
                if abs(angle) < 30:
                    anim_name = 'sideright'
                elif 30 < abs(angle) < 70:
                    anim_name = 'downsideright'
                else:
                    anim_name = 'downright'
            else:  # Quadrant 4: side, diag or down
                if abs(angle) < 30:
                    anim_name = 'sideleft'
                elif 30 < abs(angle) < 70:
                    anim_name = 'downsideleft'
                else:
                    anim_name = 'downleft'
        self.current_anim = self.anims[States.shooting][anim_name]

    def shoot(self, selected):
        # Gunshots emerge from center of player...
        #   could later make this be exact gun position by dicting anims
        player = self.rect.center

        # Reset powerup weapons after 10 shots.
        if self.weapon != Weapons.normal:
            self.shots_left -= 1
            if self.shots_left < 0:
                self.weapon = Weapons.normal

        if self.weapon == Weapons.normal:
            shotcount = 1
        elif self.weapon == Weapons.shotgun:
            shotcount = 3

        for _ in xrange(shotcount):
            # Fudge the destination point a bit to make it look like shots are being
            #   sent out haphazardly instead of to the same point every time
            fudgex = selected.x + random.randint(-selected.rect.width / 2,
                                                 selected.rect.width / 2)
            fudgey = selected.y + random.randint(-selected.rect.height / 2,
                                                 selected.rect.height / 2)
            distx, disty = (player[0] - fudgex, player[1] - fudgey)
            hypotenuse = math.sqrt(distx**2 + disty**2)
            nx, ny = distx / hypotenuse, disty / hypotenuse
            ttl = int(hypotenuse / game_constants.bulletspeed)

            self.bullets.add(
                Bullet(self, self.weapon,
                       (player[0] - nx * 4, player[1] - ny * 4), [nx, ny],
                       selected, ttl))
Beispiel #7
0
class PlatformingScene(BaseScene):
    """ Class for the main playable game environment. """
    def __init__(self, screen):
        super(PlatformingScene, self).__init__(screen)

        self.background_drawn = False
        self.headersize = 30

        self.camera = pygame.rect.Rect(0, 0, game_constants.w, game_constants.h)
        self.special_chars = SpecialChars()
        self.movelist = []
        self.time_elapsed = 0

        self.health = Health(5)
        self.score = Score(screen)
        self.player = Player((screen.get_width() / 2, screen.get_height() / 2 - 30))
        self.playergroup = RenderUpdatesDraw(self.player)

        self.platforms = {}
        self.platforms[game_constants.h - 80] = [general.Box(-10000, game_constants.h - 80, 20000, 16)]

        self.statics = RenderUpdatesDraw()
        self.statics.add(self.platforms.values()[0])
        self.screenstatics = RenderUpdatesDraw()
        self.actives = RenderUpdatesDraw()
        self.powerups = RenderUpdatesDraw()

        self.place_platforms()
        self.player.colliders = self.screenstatics

        self.header = pygame.Surface((screen.get_width(), self.headersize))
        self.header.fill(Color.BLACK)

        self.background = pygame.Surface(screen.get_size()).convert()

        gradiation = 128.0
        for i in xrange(int(gradiation)):
            color = (150 - (i * 150 / gradiation), 150 - (i * 150 / gradiation), 200)
            rect = (0, i * game_constants.h / gradiation, game_constants.w, game_constants.h / gradiation + 1)
            self.background.fill(color, rect)

        # Move sprites into or out of 'screenstatics' group based on whether they're in camera
        self.screencheck()

        # fixme: rearchitect this
        self.challenging = False

    def lazy_redraw(self):
        return False

    def fill_level(self, height):
        platform_level = self.platforms.setdefault(height, [])

        max_right = self.camera.right + game_constants.w
        min_left = self.camera.left - game_constants.w

        # cull platforms that have gone too far -- TODO this doesn't work yet
        bad_platforms = [p for p in platform_level if p.rect.left > max_right or p.rect.right < min_left]
        [platform_level.remove(p) for p in bad_platforms]
        [self.statics.remove(p) for p in bad_platforms]

        # add on platforms until the limit is reached
        if not len(platform_level):
            leftmost = random.randint(-game_constants.w, game_constants.w)
            rightmost = leftmost = random.randint(-game_constants.w, game_constants.w)
        else:
            leftmost = min([p.rect.left for p in platform_level])
            rightmost = max([p.rect.right for p in platform_level])

        while rightmost < max_right:
            new_start = rightmost + random.randint(150, 300)
            new_width = random.randint(200, 1000)
            new_width = new_width - new_width % 16 # Cut off to the nearest multiple of 16
            new_p = Platform(new_start, height, new_width, 16)
            platform_level.append(new_p)
            self.statics.add(new_p)
            rightmost = new_start + new_width
        while leftmost > min_left:
            new_start = leftmost - random.randint(150, 300)
            new_width = random.randint(200, 1000)
            new_width = new_width - new_width % 16 # Cut off to the nearest multiple of 16
            new_p = Platform(new_start - new_width, height, new_width, 16)
            platform_level.append(new_p)
            self.statics.add(new_p)
            leftmost = new_start - new_width

    def place_platforms(self):
        # Generate platforms based on camera position: Make sure there are always platforms extending at least as far as +-4*game_constants.w, +-4*game_constants.h from the player
        max_height = self.camera.top - 2 * game_constants.h
        lowest = max(self.platforms.keys())
        highest = min(self.platforms.keys())

        for h in range(highest, lowest, 80):
            self.fill_level(h)

        while highest > max_height:
            highest -= 80
            self.fill_level(highest)

    def draw_background(self):
        self.screen.blit(self.background, (0, 0))
        self.screen.blit(self.header, (0, 0))
        pygame.display.update()
        self.background_drawn = True

    def draw_header(self):
        dirty = []
        self.screen.set_clip(0, 0, game_constants.w, self.headersize) # Only draw in header area
        self.score.clear(self.screen, self.header)
        self.health.clear(self.screen, self.header)
        dirty += self.score.draw(self.screen, self.time_elapsed)
        dirty += self.health.draw(self.screen)
        return dirty

    def draw(self):
        if not self.background_drawn:
            self.draw_background()

        self.camshift()

        header_dirty = self.draw_header()

        dirty = []

        # Don't draw over header
        self.screen.set_clip(0, self.headersize, game_constants.w, game_constants.h)

        rect_sources = [
            self.screenstatics,
            self.powerups,
            self.actives,
            self.playergroup,
            self.player.bullets,
        ]

        for rect_source in rect_sources:
            rect_source.clear(self.screen, self.background)

        for rect_source in rect_sources:
            dirty += rect_source.draw(self.screen, self.camera)

        # hack: repaint selected_opponent to make sure it's visible
        if self.player.selected_opponent:
            dirty += [self.player.selected_opponent.draw(self.screen, self.camera)]

        # Constrain all dirty rectangles in main game area to main game area.
        dirty = [dirty_rect.clip(self.screen.get_clip()) for dirty_rect in dirty]

        return header_dirty + dirty

    def camshift(self):
        newpos = self.player.rect
        bounds = self.camera.inflate(-game_constants.w + 50, -game_constants.h + 100)
        # Move the screen if we're near the edge
        if newpos.right > bounds.right:
            self.camera = self.camera.move(newpos.right - bounds.right, 0)
        elif newpos.left < bounds.left:
            self.camera = self.camera.move(newpos.left - bounds.left, 0)
        if newpos.bottom > bounds.bottom:
            self.camera = self.camera.move(0, newpos.bottom - bounds.bottom)
        elif newpos.top < bounds.top:
            self.camera = self.camera.move(0, newpos.top - bounds.top)
        self.screencheck()

    def screencheck(self):
        """Classify objects by whether they are on the screen"""

        for platform_level in self.platforms.values():
            for platform in platform_level:
                if platform not in self.screenstatics:
                    # This platform wasn't on the screen: is it now?
                    if platform.rect.colliderect(self.camera):
                        self.screenstatics.add(platform)
                        # If it's a platform (has the attribute'word') give it an
                        #   identifying word while it's onscreen
                        if hasattr(platform, 'word'):
                            platform.word = Word(self.special_chars.new(), platform.font)
                else:
                    # This platform is on the screen: is it gone now?
                    if not platform.rect.colliderect(self.camera):
                        self.screenstatics.remove(platform)
                        # If it's a platform (has the attribute'word')
                        #  and it's out of camera, free up its symbol for later use
                        if hasattr(platform, 'word'):
                            self.special_chars.release(platform.word.string)
                            platform.word = None
        for sprite in self.powerups.sprites():
            if not sprite.rect.colliderect(self.camera.inflate(game_constants.w, 0)):
                sprite.kill()
        for sprite in self.actives.sprites(): # need a list because we're going to delete from it
            # Opponents are allowed to be a full screen width outside of camera, but no further
            if not sprite.rect.colliderect(self.camera.inflate(game_constants.w, 0)):
                if sprite == self.player.selected_opponent:
                    self.player.selected_opponent = None
                sprite.kill()

    def is_reachable(self, platform):
        player_rect = self.player.rect
        """ Is it possible for the player to jump to this platform in the concievable future? """
        # Test 1: the platform must be within jumping height
        if player_rect.bottom - game_constants.maxjump_height < platform.rect.top < player_rect.bottom:
            # Test 2: the player cannot be under the platform
            if (player_rect.right < platform.rect.left):
                # Test 3: the path from the player position to the optimal jump position must be contiguous
                jumpdist = platform.rect.left - player_rect.right
                if jumpdist < game_constants.maxjump_width: # Maximum distance that can be jumped
                    return True
                else:
                    start = (player_rect.right, player_rect.bottom)
                    size = (jumpdist - game_constants.maxjump_width, 1)
                    testrect = pygame.rect.Rect(start, size)
                    for collider in self.screenstatics:
                        if collider.rect.contains(testrect):
                            return True
            elif (player_rect.left > platform.rect.right):
                # Test 3: the path from the player position to the optimal jump position must be contiguous
                jumpdist = player_rect.left - platform.rect.right
                if jumpdist < game_constants.maxjump_width: # Maximum distance that can be jumped
                    return True
                else:
                    start = (platform.rect.right + game_constants.maxjump_width, player_rect.bottom)
                    size = (jumpdist - game_constants.maxjump_width, 1)
                    testrect = pygame.rect.Rect(start, size)
                    for collider in self.screenstatics:
                        if collider.rect.contains(testrect):
                            return True

    def tick(self, elapsed):
        self.place_platforms()

        self.time_elapsed += elapsed

        for direction in self.movelist:
            self.player.direct(direction)

        for active_obj in self.actives:
            active_obj.tick(self.player.rect.center)

        for powerup in self.powerups:
            powerup.tick()

        for screenstatic in self.screenstatics: # Objects on the screen
            # Only platforms have a 'word' attribute
            if hasattr(screenstatic, 'word'):
                # Will it be possible to get to this object without having to walk on air
                screenstatic.reachable = self.is_reachable(screenstatic)
        self.player.tick()

        if (self.health.value() <= 0):
            self.switch_to = GameOverScene(self.screen)

    def type_special(self, key): # Typing a special character
        for platform_level in self.platforms.values():
            for platform in platform_level:
                if hasattr(platform, 'word'): # HACK to ignore bottom platform
                    if platform.reachable and self.camera.colliderect(platform.rect):
                        if key == platform.contents():
                            return platform
Beispiel #8
0
class Player(WrappedSprite):
    """ Logic, graphics and methods for a game protagonist """

    def __init__(self, position):
        super(Player, self).__init__()

        self.state = States.falling
        self.colliders = None

        self.loadanims()
        self.direction = 'r'
        self.jumpdir = 'u'
        self.moving = False
        self.delayframes = 0
        self.weapon = Weapons.normal
        self.shots_left = 0
        self.bullets = RenderUpdatesDraw()
        self.velocity = 0

        self.selected_opponent = None
        self.position = position
        self.setanim() # Also sets self.rect for collisions

    @classmethod
    def loadImages(cls):
        super(Player, cls).loadImages()

        cls.images['upright']       = loadframes("gunstar", ["gunup1.png", "gunup2.png"])
        cls.images['upleft']        = flippedframes(cls.images['upright'])
        cls.images['upsideright']   = loadframes("gunstar", ["gunupside1.png", "gunupside2.png"])
        cls.images['upsideleft']    = flippedframes(cls.images['upsideright'])
        cls.images['sideright']     = loadframes("gunstar", ["gunside1.png", "gunside2.png"])
        cls.images['sideleft']      = flippedframes(cls.images['sideright'])
        cls.images['downsideright'] = loadframes("gunstar", ["gundownside1.png", "gundownside2.png"])
        cls.images['downsideleft']  = flippedframes(cls.images['downsideright'])
        cls.images['downright']     = loadframes("gunstar", ["gundown1.png", "gundown2.png"])
        cls.images['downleft']      = flippedframes(cls.images['downright'])
        cls.images['idleright']     = loadframes("gunstar", ["rest1.png", "rest2.png"])
        cls.images['idleleft']      = flippedframes(cls.images['idleright'])
        cls.images['runright']      = loadframes("gunstar", n_of("run%i.png", 6))
        cls.images['runleft']       = flippedframes(cls.images['runright'])
        cls.images['jumpright']     = loadframes("gunstar", ["jump1.png", "jump2.png"])
        cls.images['jumpleft']      = flippedframes(cls.images['jumpright'])
        cls.images['fallright']     = loadframes("gunstar", ["jump3.png"])
        cls.images['fallleft']      = flippedframes(cls.images['fallright'])
        cls.images['hitright']      = loadframes("gunstar", ["hit1.png", "hit2.png"])
        cls.images['hitleft']       = flippedframes(cls.images['hitright'])

    def loadanims(self):
        self.anims = {
            States.shooting : {
                'upright'       : Anim(self.images['upright'],       (5, 5)),
                'upleft'        : Anim(self.images['upleft'],        (5, 5)),

                'upsideright'   : Anim(self.images['upsideright'],   (5, 5)),
                'upsideleft'    : Anim(self.images['upsideleft'],    (5, 5)),

                'sideright'     : Anim(self.images['sideright'],     (5, 5)),
                'sideleft'      : Anim(self.images['sideleft'],      (5, 5)),

                'downsideright' : Anim(self.images['downsideright'], (5, 5)),
                'downsideleft'  : Anim(self.images['downsideleft'],  (5, 5)),

                'downright'     : Anim(self.images['downright'],     (5, 5)),
                'downleft'      : Anim(self.images['downleft'],      (5, 5)),
                },
            States.idle : {
                'r'        : Anim(self.images['idleright'],     (30, 60)),
                'l'        : Anim(self.images['idleleft'],      (30, 60)),
                },
            States.running : {
                'r'        : Anim(self.images['runright'],      (14,) * 6),
                'l'        : Anim(self.images['runleft'],       (14,) * 6),
                },
            States.jumping : {
                'r'        : Anim(self.images['jumpright'],     (20, 40)),
                'l'        : Anim(self.images['jumpleft'],      (20, 40)),
                },
            States.falling : {
                'r'        : Anim(self.images['fallright'],     (20,)),
                'l'        : Anim(self.images['fallleft'],      (20,)),
                },
            States.hit     : {
                'r'        : Anim(self.images['hitright'],      (5, 5)),
                'l'        : Anim(self.images['hitleft'],       (5, 5)),
                },
            }

    def draw(self, surface, campos):
        return self.current_anim.draw(surface, self.rect.move((-campos[0], -campos[1])))

    def setanim(self): # Set animation from state and direction
        if self.state != States.shooting:
            self.current_anim = self.anims[self.state][self.direction]

        # Want to ensure that new bottom = old bottom
        self.rect = self.current_anim.get_rect()
        self.rect.bottom = int(self.position[1])
        self.rect.centerx = int(self.position[0])

    def collide(self):
        return pygame.sprite.spritecollide(self, self.colliders, False)

    def direct(self, direction):
        self.moving = True
        if direction == K_LEFT:
            self.direction = 'l'
            if self.state not in (States.jumping, States.falling, States.hit):
                self.state = States.running
        elif direction == K_RIGHT:
            self.direction = 'r'
            if self.state not in (States.jumping, States.falling, States.hit):
                self.state = States.running

    def tick(self):
        self.delayframes += 1

        if self.state in (States.falling, States.jumping, States.hit):
            if self.velocity > game_constants.terminal_velocity:
                self.velocity -= .25

        # If we're falling, check how far we are from the nearest ground. If we're further than one tick's distance,
        # move jumpspeed units. If we're closer than one tick's distance, move directly to the ground.
        if self.state in (States.falling, States.hit):
            dist = distfromground(self.rect, self.colliders)
            if dist <= abs(self.velocity):
                self.move_vertical(dist)
                if self.state == States.hit:
                    if self.delayframes > game_constants.hitframes:
                        self.moving = True
                        self.state = States.running
                elif self.moving: self.state = States.running
                else: self.state = States.idle
            else:
                self.move_vertical(-self.velocity)
        elif self.state == States.jumping:
            dist = distfromceiling(self.rect, self.colliders)
            if dist <= self.velocity:
                self.move_vertical(-dist)
                self.state = States.falling
                self.velocity = 0
            else:
                self.move_vertical(-self.velocity)
            if self.velocity <= 0 or self.collide():
                self.state = States.falling
        elif self.state == States.running:
            newdist = distfromground(self.rect, self.colliders)
            if newdist > 0:
                self.state = States.falling
            if not self.moving: self.state = States.idle # Not moving but run animation is being displayed

        if self.moving:
            jump_slow = False
            if self.state == States.jumping and self.jump_target <> None:
                # Determine if moving laterally too fast will botch the jump
                # to wit: will the top of the player rect be higher than the bottom of the platform
                # before the edge of the player closest to the platform is within the platform
                # Ugly fudge factor of five pixel units included.
                if self.direction == 'l':
                    if (self.rect.left - game_constants.speed - 5) < self.jump_target.rect.right and \
                       (self.rect.top > self.jump_target.rect.bottom):
                        jump_slow = True
                elif self.direction == 'r':
                    if (self.rect.right + game_constants.speed + 5) > self.jump_target.rect.left and \
                       (self.rect.top > self.jump_target.rect.bottom):
                        jump_slow = True

            if self.state in (States.running, States.jumping, States.falling) and not jump_slow: # Left-Right movement is allowed
                oldpos, oldrect = self.position, self.rect
                if self.direction == 'l':
                    self.move_horizontal(-game_constants.speed)
                elif self.direction == 'r':
                    self.move_horizontal(game_constants.speed)
                if self.collide():
                    self.position, self.rect = oldpos, oldrect

        self.setanim()
        self.current_anim.tick()

        if self.selected_opponent:
            self.point(self.selected_opponent) # Point weapon in direction of selected thing

        for bullet in self.bullets:
            bullet.move() # Move all the laser-things that might be on screen

        self.moving = False # Require the move() function to refresh this every tick

    # fixme: these are dumb
    def move_vertical(self, vdist):
        self.position = self.position[0], self.position[1] + vdist
        self.rect.move(0, vdist)

    def move_horizontal(self, hdist):
        self.position = self.position[0] + hdist, self.position[1]
        self.rect.move(hdist, 0)

    def idle(self):
        self.state = States.idle

    def jump(self, jump_target = None):
        if self.state in (States.jumping, States.falling): return

        self.jump_target = jump_target
        self.delayframes = 0
        self.anims[States.jumping]['r'].reset()
        self.anims[States.jumping]['l'].reset()
        self.state = States.jumping
        self.velocity = 7

    def hit(self): # Player was injured
        self.moving = False
        self.state = States.hit
        self.velocity = 0
        self.delayframes = 0

    def point(self, selected): # Point gun at some quadrant
        # 1 _|_ 2
        # 4  |  3
        if (self.state not in (States.falling, States.jumping)):
            self.state = States.shooting

        x = self.rect.center[0] - selected.x
        y = self.rect.center[1] - selected.y
        h = math.sqrt(x**2 + y**2)
        angle = math.degrees(math.asin(y / h))

        anim_name = None
        if angle > 0: # Quadrant 1 or 2
            if x < 0: # Quadrant 2: up, diag or side
                if abs(angle) < 30:
                    anim_name = 'sideright'
                elif 30 < abs(angle) < 70:
                    anim_name = 'upsideright'
                else:
                    anim_name = 'upright'
            else: # Quadrant 1: up, diag or side
                if abs(angle) < 30:
                    anim_name = 'sideleft'
                elif 30 < abs(angle) < 70:
                    anim_name = 'upsideleft'
                else:
                    anim_name = 'upleft'
        else: # Quadrant 3 or 4
            if x < 0: # Quadrant 3: side, diag or down
                if abs(angle) < 30:
                    anim_name = 'sideright'
                elif 30 < abs(angle) < 70:
                    anim_name = 'downsideright'
                else:
                    anim_name = 'downright'
            else: # Quadrant 4: side, diag or down
                if abs(angle) < 30:
                    anim_name = 'sideleft'
                elif 30 < abs(angle) < 70:
                    anim_name = 'downsideleft'
                else:
                    anim_name = 'downleft'
        self.current_anim = self.anims[States.shooting][anim_name]

    def shoot(self, selected):
        # Gunshots emerge from center of player...
        #   could later make this be exact gun position by dicting anims
        player = self.rect.center

        # Reset powerup weapons after 10 shots.
        if self.weapon != Weapons.normal:
            self.shots_left -= 1
            if self.shots_left < 0:
                self.weapon = Weapons.normal

        if self.weapon == Weapons.normal:
            shotcount = 1
        elif self.weapon == Weapons.shotgun:
            shotcount = 3

        for _ in xrange(shotcount):
            # Fudge the destination point a bit to make it look like shots are being
            #   sent out haphazardly instead of to the same point every time
            fudgex = selected.x + random.randint(-selected.rect.width / 2, selected.rect.width / 2)
            fudgey = selected.y + random.randint(-selected.rect.height / 2, selected.rect.height / 2)
            distx, disty = (player[0] - fudgex, player[1] - fudgey)
            hypotenuse = math.sqrt(distx**2 + disty**2)
            nx, ny = distx / hypotenuse, disty / hypotenuse
            ttl = int(hypotenuse / game_constants.bulletspeed)

            self.bullets.add(Bullet(
                self,
                self.weapon,
                (player[0] - nx * 4, player[1] - ny * 4),
                [nx, ny],
                selected,
                ttl))