Example #1
0
 def play_menu_sound():
     if Globals.MENU_SOUND is None:
         loader = AssetLoader(sound_path_start='sounds')
         Globals.MENU_SOUND = loader.load_sound('menu_music.ogg')
     if Globals.PLAYING_MENU_SOUND:
         return
     Globals.MENU_SOUND.play(loops=-1)
     Globals.PLAYING_MENU_SOUND = True
Example #2
0
class Level(GameState):
    MAP_BASE = "maps"
    MAX_OFFSET_X = 150
    MAX_OFFSET_Y = 75
    FACTOR = 10
    ALPHA_FACTOR = 550
    MIN_ALPHA = 0
    MAX_ALPHA = 255
    SUBTITLE_BACKGROUND = pygame.color.Color("black")
    SUBTITLE_PADDING = 5
    SUBTITLE_COLOR = pygame.color.Color("white")
    SUBTITLE_FONT = pygame.font.Font(None, 32)
    SUBTITLE_MARGIN = 20
    POTION_CURED_SUBTITLE = 'You have been cured'
    POTION_CURED_SUBTITLE_LOOPS = 5
    ANTIDOTE_HINT = 'Press the action key to use'
    ANTIDOTE_TILE_LOOPS = 1
    ACTION_TILE_HINT = 'Press the action key to use'
    ACTION_TILE_LOOPS = 1
    LOCKED_TILE_HINT = 'This is locked'
    LOCKED_TILE_LOOPS = 1
    GAUNTLET_TILE_HINT = 'Pick up to enable punching'
    GAUNTLET_TILE_LOOPS = 1
    HEALTH_PICKUP = 10
    DAMAGE_TRAP = -1
    POTION_PICKUP_DISORIENTED = 5
    POTION_PICKUP = 10
    PUNCHING_INFLATE = .2
    MUSIC_END_ID_BASE = pygame.USEREVENT
    SOUND_FADE_TIME = 500
    SWITCH_SOUND_PATH = 'switch.ogg'
    IGNORE_TIME = .5

    def __init__(self, definition_path, map_path, init_music_path=None,
                 music_path=None, music_loops=-1, has_timer=True,
                 should_fade_in=True, mid=0):
        self.has_timer = has_timer
        self.keyCode = None
        self.definition_path = definition_path
        self.map_path = map_path
        self.tile_engine = TileEngine(
            join(Level.MAP_BASE, definition_path),
            join(Level.MAP_BASE, map_path)
        )
        self.camera = Camera(self.tile_engine, pygame.Rect(
            0, 0, Globals.WIDTH, Globals.HEIGHT))
        self.tile_rect = self.tile_engine.get_tile_rect()
        self.enemySprites = pygame.sprite.Group()
        self.playerSprites = pygame.sprite.Group()
        self.turrets = list()
        self.lights = list()
        self.init_player()
        self.init_enemies()
        self.timer = None
        if Globals.HEALTH_BAR is None:
            Globals.HEALTH_BAR = HealthBar()
        if Globals.HUD_MANAGER is None:
            Globals.HUD_MANAGER = HUDManager()
        self.black_surf = pygame.Surface(
            (Globals.WIDTH, Globals.HEIGHT)).convert()
        self.black_surf.fill((0, 0, 0))
        self.fade_in = False
        self.fade_out = False
        self.showing_subtitle = False
        self.alpha_factor = 300
        self.should_fade_in = should_fade_in
        self.pausing = False
        self.going_back = False
        self.score_counted = False
        self.respawn_coords = [-1, -1]
        self.timer = None
        self.first_occur = True
        self.find_respawn()
        self.loader = AssetLoader('images', 'sounds')
        self.switch_sound = self.loader.load_sound(Level.SWITCH_SOUND_PATH)
        self.channel = None
        self.music_loops = music_loops
        self.music_handle = None
        if music_path is not None:
            self.background_music_handle = self.loader.load_sound(music_path)
            self.music_handle = self.background_music_handle
        else:
            self.background_music_handle = None
        if init_music_path is not None:
            self.init_music_handle = self.loader.load_sound(init_music_path)
            self.music_handle = self.init_music_handle
        else:
            self.init_music_handle = None
        self.time_init = 0
        self.start_on_stop = True
        self.switched_sound = False
        self.music_end_id = Level.MUSIC_END_ID_BASE + mid
        self.can_open_doors = True

    def start_music(self, end_id=None):
        if end_id is None:
            end_id = self.music_end_id
        if self.channel or self.music_handle is None:
            return
        if not self.switched_sound:
            l = 0
        else:
            l = self.music_loops
        self.channel = self.music_handle.play(
            loops=l, fade_ms=Level.SOUND_FADE_TIME)
        self.channel.set_endevent(end_id)
        self.start_on_stop = True
        # if self.channel:
        #     return
        # self.channel = self.background_music_handle.play(
        #     loops=self.music_loops, fade_ms=Level.SOUND_FADE_TIME)
        # self.channel.set_endevent(Level.MUSIC_END_ID)

    def pause_music(self):
        if self.channel:
            self.channel.pause()
            self.start_on_stop = True

    def resume_music(self):
        if self.channel:
            self.channel.unpause()
            self.start_on_stop = True

    def stop_music(self):
        if self.channel:
            self.channel.fadeout(Level.SOUND_FADE_TIME)
            self.channel = None
            self.start_on_stop = False
            self.music_handle = self.background_music_handle

    def find_respawn(self):
        tile_map = self.tile_engine.tileMap
        for row_num in range(0, len(tile_map)):
            for col_num in range(0, len(tile_map[row_num])):
                if tile_map[row_num][col_num] is None:
                    continue
                if TileType.RESPAWN_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    if self.has_respawn_coords():
                        raise Exception(
                            "There can only be one respawn point in the map"
                        )
                    self.respawn_coords[0] = row_num
                    self.respawn_coords[1] = col_num
        if self.has_respawn_coords():
            tile_map[self.respawn_coords[0]][self.respawn_coords[1]] = \
                self.tile_engine.get_tile_from_attr(TileType.BASE_ATTR)

    def has_respawn_coords(self):
        return self.respawn_coords[0] != -1 and self.respawn_coords[1] != -1

    def got_current_state(self):
        diff = self.camera.initView()
        self.shift_non_player_objects(diff[0], diff[1])
        self.player.rect.center = Globals.SCREEN.get_rect().center
        if self.should_fade_in:
            self.start_fade_in()
        if self.has_timer and self.timer is None:
            self.timer = ScoreTimer()
        elif self.timer is not None:
            self.timer.unpause()
        Globals.stop_menu_sound()
        self.start_music()
        self.first_occur = False

    def got_state_back(self):
        if self.has_respawn_coords():
            diff = self.camera.set_viewpoint_with_coords(
                self.respawn_coords[0], self.respawn_coords[1])
            center = Globals.SCREEN.get_rect().center
            self.player.stop_and_set_direction(Character.INDEX_DOWN)
            self.player.rect.left = center[0] - 16
            self.player.rect.top = center[1]
            self.shift_non_player_objects(diff[0], diff[1])
            self.start_fade_in()
            self.start_music()
        else:
            raise Exception(
                "A respawn point must be defined to return to the level")

    def shift_non_player_objects(self, x_delta, y_delta):
        if x_delta == 0 and y_delta == 0:
            return
        for enemy in self.enemySprites:
            enemy.rect.centerx += x_delta
            enemy.rect.centery += y_delta
        for turret in self.turrets:
            turret.move(x_delta, y_delta)

    def handle_stair_up(self):
        if not self.score_counted:
            self.score_counted = True
            Globals.PLAYER_SCORE += Globals.HEALTH_BAR.health
            if self.has_timer:
                time = self.timer.total_time / 1000
                diff = max(300 - time, 0)
                Globals.PLAYER_SCORE += diff
        self.going_back = False
        self.pausing = False
        self.start_fade_out()

    def handle_stair_down(self):
        self.going_back = True
        self.start_fade_out()

    def handle_enemy_collision(self):
        pass

    def handle_special_collision(self, pair):
        self.replace_special_tile(pair)
        if TileType.TRAP_ATTR in pair.tile.special_attr:
            Globals.HEALTH_BAR.changeHealth(Level.DAMAGE_TRAP)
            self.player.show_damage()
        elif TileType.HEALTH_ATTR in pair.tile.special_attr:
            Globals.HEALTH_BAR.changeHealth(Level.HEALTH_PICKUP)
        elif TileType.KEY_ATTR in pair.tile.special_attr:
            Globals.HUD_MANAGER.add_key()
        elif TileType.POTION_ATTR in pair.tile.special_attr:
            if Globals.DISORIENTED:
                Globals.DISORIENTED = False
                self.show_subtitle(Level.POTION_CURED_SUBTITLE,
                                   Level.POTION_CURED_SUBTITLE_LOOPS)
                self.player.stop_and_set_direction(self.player.direction)
                Globals.HEALTH_BAR.changeHealth(
                    Level.POTION_PICKUP_DISORIENTED)
            else:
                Globals.HEALTH_BAR.changeHealth(Level.POTION_PICKUP)

    def handle_finish_fade_out(self):
        self.stop_music()
        if not Globals.goto_next_level():
            self.handle_last_level()

    def handle_last_level(self):
        manager = HighscoreManager()
        manager.add(Globals.PLAYER_NAME, Globals.PLAYER_SCORE)
        Globals.STATE = WinGame.WinGame()  # for now

    def handle_finish_fade_in(self):
        if self.timer is not None:
            self.timer.unpause()

    def replace_special_tile(self, pair):
        if pair.tile.is_replaceable:
            row, col = self.camera.tileEngine.get_tile_pos(pair.coords[0],
                                                           pair.coords[1])
            base = self.camera.tileEngine.get_tile_from_attr(
                pair.tile.replace_attr)
            if base is None:
                base = self.camera.tileEngine.get_tile_from_attr(
                    TileType.BASE_ATTR)
            self.camera.tileEngine.tileMap[row][col] = base
            self.camera.set_dirty()
            self.lights = [
                l for l in self.lights if not
                l.rect.colliderect(self.player.rect)]

    def init_player(self):
        self.player = Player(
            Globals.WIDTH, Globals.HEIGHT,
            Globals.WIDTH / 2, Globals.HEIGHT / 2
        )
        self.playerSprites.add(self.player)

    def init_enemies(self):
        tile_map = self.tile_engine.tileMap
        base_tile = self.tile_engine.get_tile_from_attr(TileType.BASE_ATTR)
        for row_num in range(0, len(tile_map)):
            for col_num in range(0, len(tile_map[row_num])):
                if tile_map[row_num][col_num] is None:
                    continue
                if TileType.SPAWN_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_enemy(row_num, col_num)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.CHASE_SPAWN_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_enemy(row_num, col_num, chase_enemy=True)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.BOSS_SPAWN in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_enemy(row_num, col_num, boss=True)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.TURRET_LEFT in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_turret(row_num, col_num, True)
                elif TileType.TURRET_RIGHT in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_turret(row_num, col_num, False)
                elif TileType.LIGHT_REPLACE_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_light(row_num, col_num)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.LIGHT_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_light(row_num, col_num)

    def add_light(self, row_num, col_num):
        y = self.tile_rect.height * (row_num - 1) - self.camera.viewpoint.top
        x = self.tile_rect.width * (col_num - 1) - self.camera.viewpoint.left
        self.lights.append(LightSource(x, y, self.tile_rect.width))

    def add_enemy(self, row_num, col_num, chase_enemy=False, boss=False):
        y = self.tile_rect.height * row_num - self.camera.viewpoint.top
        x = self.tile_rect.width * col_num - self.camera.viewpoint.left
        if boss:
            enemy = BossEnemy(camera=self.camera, x=x, y=y)
        elif chase_enemy:
            enemy = ChaseEnemy(camera=self.camera, x=x, y=y)
        else:
            enemy = Enemy(Globals.WIDTH, Globals.HEIGHT, x=x, y=y)
        self.enemySprites.add(enemy)

    def add_turret(self, row_num, col_num, left):
        row_num += 1
        if not left:
            col_num += 1
        y = self.tile_rect.height * row_num - self.camera.viewpoint.top
        x = self.tile_rect.width * col_num - self.camera.viewpoint.left
        self.turrets.append(Turret(x, y, left))

    def check_collisions(self):
        radius = max(self.player.rect.size) * 2
        self.check_turret_collisions()
        self.check_punching_collisions()
        self.enemySprites = pygame.sprite.Group(
            [e for e in self.enemySprites if e.is_alive])
        self.check_enemy_collisions()
        special_tiles = self.camera.get_special_tiles(
            self.player.rect.center, radius)
        self.check_stair_collisions(special_tiles)
        self.check_special_collisions(special_tiles)
        self.check_action_hints()

    def check_action_hints(self):
        if self.showing_subtitle:
            return
        temp_rect = self.player.rect.inflate(
            Player.ACTION_OFFSET, Player.ACTION_OFFSET)
        radius = max(temp_rect.size) * 2
        special_tiles = self.camera.get_special_tiles(
            self.player.rect.center, radius)
        action_tiles = [pair for pair in special_tiles if
                        TileType.ACTION_ATTR in pair.tile.special_attr and
                        temp_rect.colliderect(pair.rect)]
        locked_tiles = [pair for pair in action_tiles if
                        TileType.LOCKED_ATTR in pair.tile.special_attr]
        special_locked = [pair for pair in locked_tiles if
                          TileType.SPECIAL_DOOR_ATTR in pair.tile.special_attr]
        antidote_tiles = [pair for pair in action_tiles if
                          TileType.ANTIDOTE_ATTR in pair.tile.special_attr]
        if len(antidote_tiles) > 0:
            self.show_subtitle(Level.ANTIDOTE_HINT, Level.ANTIDOTE_TILE_LOOPS)
            return
        contains_red = self.camera.contains_attribute(TileType.REDBALL_ATTR)
        if len(action_tiles) > 0 and not self.can_open_doors:
            self.show_subtitle(Level.LOCKED_TILE_HINT, Level.LOCKED_TILE_LOOPS)
        elif len(special_locked) > 0 and contains_red:
            self.show_subtitle(Level.LOCKED_TILE_HINT, Level.LOCKED_TILE_LOOPS)
        elif len(special_locked) == 0 and len(locked_tiles) > 0 and \
                not Globals.HUD_MANAGER.has_key():
            self.show_subtitle(Level.LOCKED_TILE_HINT, Level.LOCKED_TILE_LOOPS)
        elif len(action_tiles) > 0 and (len(special_locked) == 0 or
                                        not contains_red):
            self.show_subtitle(Level.ACTION_TILE_HINT, Level.ACTION_TILE_LOOPS)

    def check_special_collisions(self, special_tiles):
        for pair in special_tiles:
            if self.player.rect.colliderect(pair.rect):
                self.handle_special_collision(pair)

    def check_stair_collisions(self, special_tiles):
        stair_up_rects = [pair.rect for pair in special_tiles
                          if TileType.STAIR_UP_ATTR in pair.tile.special_attr]
        temp_rect = self.player.rect.inflate(
            -Player.STAIR_OFFSET, -Player.STAIR_OFFSET)
        num_up_stairs = len(temp_rect.collidelistall(stair_up_rects))
        if num_up_stairs > 0:
            self.handle_stair_up()
        stair_down_rects = [pair.rect for pair in special_tiles if
                            TileType.STAIR_DOWN_ATTR in pair.tile.special_attr]
        num_down_stairs = len(temp_rect.collidelistall(stair_down_rects))
        if num_down_stairs > 0:
            self.handle_stair_down()

    def check_enemy_collisions(self):
        enemy_rects = [enemy.rect.inflate(-20, -20)
                       for enemy in self.enemySprites]
        player_rect = self.player.rect.copy()
        if self.player.punching:
            # player_rect.inflate_ip(
            #     -player_rect.width * Level.PUNCHING_INFLATE,
            #     -player_rect.height * Level.PUNCHING_INFLATE)
            pass
        collided_indices = player_rect.collidelistall(enemy_rects)
        if len(collided_indices) > 0:
            self.handle_health_change(Enemy.HEALTH_EFFECT)

    def check_punching_collisions(self):
        if not self.player.punching:
            return
        punching_rect = self.player.get_punching_rect()
        for enemy in self.enemySprites:
            if punching_rect.colliderect(enemy.rect):
                enemy.handle_hit(self.camera, self.player)

    def check_turret_collisions(self):
        for turret in self.turrets:
            for syringe in turret.syringeSprites:
                if self.player.rect.colliderect(syringe):
                    self.handle_health_change(syringe.health_effect)
                    syringe.kill()

    def handle_health_change(self, health_effect):
        Globals.HEALTH_BAR.changeHealth(health_effect)
        if health_effect < 0:
            self.player.show_damage()

    def render(self):
        self.render_pre_fade()
        if self.fade_out or self.fade_in:
            Globals.SCREEN.blit(self.black_surf, (0, 0))
        self.render_post_fade()
        # r = self.player.get_punching_rect()
        # if r is not None:
        #     Globals.SCREEN.fill((255, 255, 255), r)

    def render_pre_fade(self):
        self.camera.render(Globals.SCREEN)
        self.render_lights()
        self.enemySprites.draw(Globals.SCREEN)
        for turret in self.turrets:
            turret.render(Globals.SCREEN)
        self.playerSprites.draw(Globals.SCREEN)
        self.render_overlay()
        if self.has_timer and not self.score_counted:
            self.timer.render(Globals.SCREEN)
        if self.showing_subtitle:
            Globals.SCREEN.blit(self.subtitle_surf, self.subtitle_rect)

    def render_lights(self):
        for light in self.lights:
            light.render(Globals.SCREEN)

    def render_overlay(self):
        if Globals.DISORIENTED:
            Globals.SCREEN.blit(Globals.get_disoriented_surf(), (0, 0),
                                special_flags=pygame.BLEND_SUB)

    def render_post_fade(self):
        Globals.HEALTH_BAR.render(Globals.SCREEN)
        Globals.HUD_MANAGER.render(Globals.SCREEN)

    def update(self, time):
        if self.time_init < Level.IGNORE_TIME:
            self.time_init += time
            return
        if Globals.HEALTH_BAR.is_dead():
            self.handle_lose_game()
        if self.fade_out or self.fade_in:
            self.update_alpha(time)
            return
        Globals.HEALTH_BAR.update(time)
        self.player.update(time, self.camera)
        self.enemySprites.update(time, self.camera, self.player)
        for turret in self.turrets:
            turret.update(time, self.camera)
        self.check_camera_position()
        self.check_collisions()
        self.update_subtitle(time)

    def handle_lose_game(self):
        self.stop_music()
        Globals.STATE = LoseGame.LoseGame()

    def update_subtitle(self, time):
        if not self.showing_subtitle:
            return
        old_alpha = self.subtitle_surf.get_alpha()
        if old_alpha == 0 or old_alpha == 255:
            self.alpha_factor *= -1
            if self.subtitle_loops != -1:
                self.subtitle_loops -= 1
                if self.subtitle_loops == 0:
                    self.stop_subtitle()
        new_alpha = int(old_alpha + self.alpha_factor * time)
        if new_alpha < 0:
            new_alpha = 0
        elif new_alpha > 255:
            new_alpha = 255
        self.subtitle_surf.set_alpha(new_alpha)

    def update_alpha(self, time):
        if self.fade_out:
            old_alpha = self.black_surf.get_alpha()
            new_alpha = int(old_alpha + time * Level.ALPHA_FACTOR)
            if new_alpha >= Level.MAX_ALPHA:
                if self.pausing:
                    self.handle_pause()
                elif self.going_back:
                    self.handle_go_back()
                else:
                    self.handle_finish_fade_out()
                self.fade_out = False
            self.black_surf.set_alpha(new_alpha)
        elif self.fade_in:
            old_alpha = self.black_surf.get_alpha()
            new_alpha = int(old_alpha - time * Level.ALPHA_FACTOR)
            if new_alpha <= Level.MIN_ALPHA:
                self.handle_finish_fade_in()
                self.fade_in = False
            self.black_surf.set_alpha(new_alpha)

    def start_fade_out(self):
        self.black_surf.set_alpha(Level.MIN_ALPHA)
        self.fade_out = True

    def start_fade_in(self):
        self.black_surf.set_alpha(Level.MAX_ALPHA)
        self.fade_in = True

    def handle_action_key(self):
        temp_rect = self.player.rect.inflate(
            Player.ACTION_OFFSET, Player.ACTION_OFFSET)
        radius = max(temp_rect.size) * 2
        pairs = self.camera.get_special_tiles(
            self.player.rect.center, radius)
        special_tiles = [pair for pair in pairs if
                         temp_rect.colliderect(pair.rect)]
        self.handle_sliding_doors(special_tiles)
        self.handle_action_switch(special_tiles)
        self.handle_antidote_check(special_tiles)

    def handle_antidote_check(self, special_tiles):
        for pair in special_tiles:
            if TileType.ANTIDOTE_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(
                    pair.coords[0],
                    pair.coords[1]
                )
                base = self.camera.tileEngine.get_tile_from_attr(
                    TileType.ANTIDOTE_REPLACE)
                self.camera.tileEngine.tileMap[row][col] = base
                self.camera.set_dirty()
                self.handle_antidote()

    def handle_antidote(self):
        pass

    def handle_sliding_doors(self, special_tiles):
        if not self.can_open_doors:
            return
        base = self.camera.tileEngine.get_tile_from_attr(
            TileType.BASE_ATTR)
        was_special_door = False
        for pair in special_tiles:
            if TileType.SLIDING_DOOR_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(
                    pair.coords[0],
                    pair.coords[1]
                )
                doors = self.get_sliding_doors(row, col)
                for pos in doors:
                    row, col = pos
                    tile_map = self.camera.tileEngine.tileMap
                    if TileType.SPECIAL_DOOR_ATTR in \
                            tile_map[row][col].special_attr:
                        was_special_door = True
                    tile_map[row][col] = base
                self.camera.set_dirty()
        if was_special_door:
            self.handle_special_door()

    def handle_special_door(self):
        pass

    def get_sliding_doors(self, row, col):
        init_num_keys = Globals.HUD_MANAGER.num_keys
        coords = list()
        use_key = True
        if TileType.LOCKED_ATTR in \
                self.camera.tileEngine.tileMap[row][col].special_attr:
            if TileType.SPECIAL_DOOR_ATTR in \
                    self.camera.tileEngine.tileMap[row][col].special_attr:
                if self.camera.contains_attribute(TileType.REDBALL_ATTR):
                    return list()
            elif not Globals.HUD_MANAGER.has_key():
                return list()
            else:
                init_num_keys = -1
                if use_key:
                    Globals.HUD_MANAGER.use_key()
        coords.append([row, col])
        result = self.get_doors_delta(row, col, init_num_keys, row_delta=-1)
        if result == -1:
            return list()
        coords.extend(result)
        result = self.get_doors_delta(row, col, init_num_keys, row_delta=1)
        if result == -1:
            return list()
        coords.extend(result)
        result = self.get_doors_delta(row, col, init_num_keys, col_delta=-1)
        if result == -1:
            return list()
        coords.extend(result)
        result = self.get_doors_delta(row, col, init_num_keys, col_delta=1)
        if result == -1:
            return list()
        coords.extend(result)
        return coords

    def get_doors_delta(self, row, col,
            init_num_keys, row_delta=0, col_delta=0):
        coords = list()
        tile_map = self.camera.tileEngine.tileMap
        row += row_delta
        col += col_delta
        while self.camera.tileEngine.is_coord_valid(row, col) and \
                tile_map[row][col] is not None and \
                TileType.SLIDING_DOOR_ATTR in tile_map[row][col].special_attr:
            if TileType.LOCKED_ATTR in tile_map[row][col].special_attr:
                if TileType.SPECIAL_DOOR_ATTR in \
                        self.camera.tileEngine.tileMap[row][col].special_attr:
                    if self.camera.contains_attribute(TileType.REDBALL_ATTR):
                        return -1
                elif not Globals.HUD_MANAGER.has_key():
                    return -1
                elif Globals.HUD_MANAGER.num_keys >= init_num_keys:
                    Globals.HUD_MANAGER.use_key()
            coords.append([row, col])
            row += row_delta
            col += col_delta
        return coords

    def handle_action_switch(self, special_tiles):
        for pair in special_tiles:
            if TileType.LEVER_LEFT_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(pair.coords[0],
                                                               pair.coords[1])
                lever_right = self.camera.tileEngine.get_tile_from_attr(
                    TileType.LEVER_RIGHT_ATTR)
                self.camera.tileEngine.tileMap[row][col] = lever_right
                self.camera.set_dirty()
                self.handle_lever_on()
            elif TileType.LEVER_RIGHT_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(pair.coords[0],
                                                               pair.coords[1])
                lever_left = self.camera.tileEngine.get_tile_from_attr(
                    TileType.LEVER_LEFT_ATTR)
                self.camera.tileEngine.tileMap[row][col] = lever_left
                self.camera.set_dirty()
                self.handle_lever_off()

    def handle_lever_on(self):
        self.switch_sound.play()
        for turret in self.turrets:
            turret.turn_off()

    def handle_lever_off(self):
        self.switch_sound.play()
        for turret in self.turrets:
            turret.turn_on()

    def event(self, event):
        if event.type == pygame.KEYDOWN:
            key = event.key if not Globals.DISORIENTED else \
                self.invert_key(event.key)
            self.handle_keydown(key)
        elif event.type == pygame.KEYUP:
            key = event.key if not Globals.DISORIENTED else \
                self.invert_key(event.key)
            self.handle_keyup(key)

    def handle_key_down(self, keydown):
        key = pygame.K_DOWN if not Globals.DISORIENTED else pygame.K_UP
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_key_up(self, keydown):
        key = pygame.K_UP if not Globals.DISORIENTED else pygame.K_DOWN
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_key_left(self, keydown):
        key = pygame.K_LEFT if not Globals.DISORIENTED else pygame.K_RIGHT
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_key_right(self, keydown):
        key = pygame.K_RIGHT if not Globals.DISORIENTED else pygame.K_LEFT
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_attack(self):
        self.player.handle_attack()

    def handle_raw_event(self, event):
        if event.type == pygame.KEYDOWN:
            key = event.key
            if key == pygame.K_1:
                self.handle_stair_up()
            elif key == pygame.K_2:
                delta = 100 - Globals.HEALTH_BAR.health
                Globals.HEALTH_BAR.changeHealth(delta)
            elif key == pygame.K_3:
                Globals.HUD_MANAGER.add_key()
            elif key == pygame.K_4:
                Globals.DISORIENTED = False
            elif key == pygame.K_5:
                self.replace_reds()
        elif event.type == self.music_end_id and not self.switched_sound:
            self.switched_sound = True
            self.stop_subtitle()
            self.music_handle = self.background_music_handle
            if self.start_on_stop:
                self.stop_music()
                self.start_music()

    def replace_reds(self):
        tile_map = self.camera.tileEngine.tileMap
        green_tile = self.camera.tileEngine.get_tile_from_attr(
            TileType.GREENBALL_ATTR)
        for row in range(0, len(tile_map)):
            for col in range(0, len(tile_map[row])):
                tile = tile_map[row][col]
                if tile is not None and \
                        TileType.REDBALL_ATTR in tile.special_attr:
                    tile_map[row][col] = green_tile
        self.camera.set_dirty()

    def handle_keydown(self, key):
        self.keyCode = key
        for p in self.playerSprites:
            p.keyPressed(key)

    def handle_go_back(self):
        if Globals.goto_previous_level():
            self.stop_music()
            self.timer.pause()
            self.player.stop_and_set_direction(Character.INDEX_UP)

    def start_pause_fade(self):
        if self.pausing:
            return
        self.pausing = True
        if self.has_timer:
            self.timer.pause()
        self.start_fade_out()

    def handle_pause(self):
        self.pausing = False
        if self.has_timer:
            self.timer.pause()
        self.pause_music()
        self.goto_pause()

    def goto_pause(self):
        Globals.STATE = PauseScreen(self)

    def handle_unpause(self):
        self.resume_music()
        if self.has_timer:
            self.timer.unpause()

    def handle_escape(self):
        # self.handle_pause()
        self.start_pause_fade()

    def handle_keyup(self, key):
        if key == self.keyCode:
            self.keyCode = None
            for p in self.playerSprites:
                p.keyReleased(key)

    def check_camera_position(self):
        dist_x = self.camera.container.centerx - self.player.rect.centerx
        dist_y = self.camera.container.centery - self.player.rect.centery
        if abs(dist_x) > Level.MAX_OFFSET_X:
            diff = abs(dist_x) - Level.MAX_OFFSET_X
            # player is to the right of center
            if dist_x < 0:
                pass
            # player is to the left of center
            else:
                diff *= -1
            self.camera.move(diff, 0)
            self.player.rect.centerx -= diff
            for enemy in self.enemySprites:
                enemy.rect.centerx -= diff
            for turret in self.turrets:
                turret.move(-diff, 0)
            for light in self.lights:
                light.move(-diff, 0)
        if abs(dist_y) > Level.MAX_OFFSET_Y:
            diff = abs(dist_y) - Level.MAX_OFFSET_Y
            # player is below center
            if dist_y < 0:
                pass
            # player is above center
            else:
                diff *= -1
            self.camera.move(0, diff)
            self.player.rect.centery -= diff
            for enemy in self.enemySprites:
                enemy.rect.centery -= diff
            for turret in self.turrets:
                turret.move(0, -diff)
            for light in self.lights:
                light.move(0, -diff)

    def init_subtitle(self, text):
        text_surf = Level.SUBTITLE_FONT.render(
            text, True, Level.SUBTITLE_COLOR)
        self.subtitle_rect = text_surf.get_rect()
        self.subtitle_rect.centerx = Globals.WIDTH / 2
        self.subtitle_rect.bottom = \
            Globals.HEIGHT - Level.SUBTITLE_MARGIN
        self.subtitle_rect.inflate_ip(
            Level.SUBTITLE_PADDING * 2,
            Level.SUBTITLE_PADDING * 2
        )
        self.subtitle_surf = pygame.Surface(self.subtitle_rect.size).convert()
        self.subtitle_surf.fill(Level.SUBTITLE_BACKGROUND)
        self.subtitle_surf.blit(text_surf, (
            Level.SUBTITLE_PADDING,
            Level.SUBTITLE_PADDING
        ))
        self.subtitle_surf.set_alpha(255)

    def show_subtitle(self, text, loops=-1):
        self.subtitle_loops = loops
        if self.subtitle_loops != -1:
            self.subtitle_loops *= 2
        self.init_subtitle(text)
        self.showing_subtitle = True

    def stop_subtitle(self):
        self.showing_subtitle = False
Example #3
0
class Level(GameState):
    MAP_BASE = "maps"
    MAX_OFFSET_X = 150
    MAX_OFFSET_Y = 75
    FACTOR = 10
    ALPHA_FACTOR = 550
    MIN_ALPHA = 0
    MAX_ALPHA = 255
    SUBTITLE_BACKGROUND = pygame.color.Color("black")
    SUBTITLE_PADDING = 5
    SUBTITLE_COLOR = pygame.color.Color("white")
    SUBTITLE_FONT = pygame.font.Font(None, 32)
    SUBTITLE_MARGIN = 20
    POTION_CURED_SUBTITLE = 'You have been cured'
    POTION_CURED_SUBTITLE_LOOPS = 5
    ANTIDOTE_HINT = 'Press the action key to use'
    ANTIDOTE_TILE_LOOPS = 1
    ACTION_TILE_HINT = 'Press the action key to use'
    ACTION_TILE_LOOPS = 1
    LOCKED_TILE_HINT = 'This is locked'
    LOCKED_TILE_LOOPS = 1
    GAUNTLET_TILE_HINT = 'Pick up to enable punching'
    GAUNTLET_TILE_LOOPS = 1
    HEALTH_PICKUP = 10
    DAMAGE_TRAP = -1
    POTION_PICKUP_DISORIENTED = 5
    POTION_PICKUP = 10
    PUNCHING_INFLATE = .2
    MUSIC_END_ID_BASE = pygame.USEREVENT
    SOUND_FADE_TIME = 500
    SWITCH_SOUND_PATH = 'switch.ogg'
    IGNORE_TIME = .5

    def __init__(self,
                 definition_path,
                 map_path,
                 init_music_path=None,
                 music_path=None,
                 music_loops=-1,
                 has_timer=True,
                 should_fade_in=True,
                 mid=0):
        self.has_timer = has_timer
        self.keyCode = None
        self.definition_path = definition_path
        self.map_path = map_path
        self.tile_engine = TileEngine(join(Level.MAP_BASE, definition_path),
                                      join(Level.MAP_BASE, map_path))
        self.camera = Camera(self.tile_engine,
                             pygame.Rect(0, 0, Globals.WIDTH, Globals.HEIGHT))
        self.tile_rect = self.tile_engine.get_tile_rect()
        self.enemySprites = pygame.sprite.Group()
        self.playerSprites = pygame.sprite.Group()
        self.turrets = list()
        self.lights = list()
        self.init_player()
        self.init_enemies()
        self.timer = None
        if Globals.HEALTH_BAR is None:
            Globals.HEALTH_BAR = HealthBar()
        if Globals.HUD_MANAGER is None:
            Globals.HUD_MANAGER = HUDManager()
        self.black_surf = pygame.Surface(
            (Globals.WIDTH, Globals.HEIGHT)).convert()
        self.black_surf.fill((0, 0, 0))
        self.fade_in = False
        self.fade_out = False
        self.showing_subtitle = False
        self.alpha_factor = 300
        self.should_fade_in = should_fade_in
        self.pausing = False
        self.going_back = False
        self.score_counted = False
        self.respawn_coords = [-1, -1]
        self.timer = None
        self.first_occur = True
        self.find_respawn()
        self.loader = AssetLoader('images', 'sounds')
        self.switch_sound = self.loader.load_sound(Level.SWITCH_SOUND_PATH)
        self.channel = None
        self.music_loops = music_loops
        self.music_handle = None
        if music_path is not None:
            self.background_music_handle = self.loader.load_sound(music_path)
            self.music_handle = self.background_music_handle
        else:
            self.background_music_handle = None
        if init_music_path is not None:
            self.init_music_handle = self.loader.load_sound(init_music_path)
            self.music_handle = self.init_music_handle
        else:
            self.init_music_handle = None
        self.time_init = 0
        self.start_on_stop = True
        self.switched_sound = False
        self.music_end_id = Level.MUSIC_END_ID_BASE + mid
        self.can_open_doors = True

    def start_music(self, end_id=None):
        if end_id is None:
            end_id = self.music_end_id
        if self.channel or self.music_handle is None:
            return
        if not self.switched_sound:
            l = 0
        else:
            l = self.music_loops
        self.channel = self.music_handle.play(loops=l,
                                              fade_ms=Level.SOUND_FADE_TIME)
        self.channel.set_endevent(end_id)
        self.start_on_stop = True
        # if self.channel:
        #     return
        # self.channel = self.background_music_handle.play(
        #     loops=self.music_loops, fade_ms=Level.SOUND_FADE_TIME)
        # self.channel.set_endevent(Level.MUSIC_END_ID)

    def pause_music(self):
        if self.channel:
            self.channel.pause()
            self.start_on_stop = True

    def resume_music(self):
        if self.channel:
            self.channel.unpause()
            self.start_on_stop = True

    def stop_music(self):
        if self.channel:
            self.channel.fadeout(Level.SOUND_FADE_TIME)
            self.channel = None
            self.start_on_stop = False
            self.music_handle = self.background_music_handle

    def find_respawn(self):
        tile_map = self.tile_engine.tileMap
        for row_num in range(0, len(tile_map)):
            for col_num in range(0, len(tile_map[row_num])):
                if tile_map[row_num][col_num] is None:
                    continue
                if TileType.RESPAWN_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    if self.has_respawn_coords():
                        raise Exception(
                            "There can only be one respawn point in the map")
                    self.respawn_coords[0] = row_num
                    self.respawn_coords[1] = col_num
        if self.has_respawn_coords():
            tile_map[self.respawn_coords[0]][self.respawn_coords[1]] = \
                self.tile_engine.get_tile_from_attr(TileType.BASE_ATTR)

    def has_respawn_coords(self):
        return self.respawn_coords[0] != -1 and self.respawn_coords[1] != -1

    def got_current_state(self):
        diff = self.camera.initView()
        self.shift_non_player_objects(diff[0], diff[1])
        self.player.rect.center = Globals.SCREEN.get_rect().center
        if self.should_fade_in:
            self.start_fade_in()
        if self.has_timer and self.timer is None:
            self.timer = ScoreTimer()
        elif self.timer is not None:
            self.timer.unpause()
        Globals.stop_menu_sound()
        self.start_music()
        self.first_occur = False

    def got_state_back(self):
        if self.has_respawn_coords():
            diff = self.camera.set_viewpoint_with_coords(
                self.respawn_coords[0], self.respawn_coords[1])
            center = Globals.SCREEN.get_rect().center
            self.player.stop_and_set_direction(Character.INDEX_DOWN)
            self.player.rect.left = center[0] - 16
            self.player.rect.top = center[1]
            self.shift_non_player_objects(diff[0], diff[1])
            self.start_fade_in()
            self.start_music()
        else:
            raise Exception(
                "A respawn point must be defined to return to the level")

    def shift_non_player_objects(self, x_delta, y_delta):
        if x_delta == 0 and y_delta == 0:
            return
        for enemy in self.enemySprites:
            enemy.rect.centerx += x_delta
            enemy.rect.centery += y_delta
        for turret in self.turrets:
            turret.move(x_delta, y_delta)

    def handle_stair_up(self):
        if not self.score_counted:
            self.score_counted = True
            Globals.PLAYER_SCORE += Globals.HEALTH_BAR.health
            if self.has_timer:
                time = self.timer.total_time / 1000
                diff = max(300 - time, 0)
                Globals.PLAYER_SCORE += diff
        self.going_back = False
        self.pausing = False
        self.start_fade_out()

    def handle_stair_down(self):
        self.going_back = True
        self.start_fade_out()

    def handle_enemy_collision(self):
        pass

    def handle_special_collision(self, pair):
        self.replace_special_tile(pair)
        if TileType.TRAP_ATTR in pair.tile.special_attr:
            Globals.HEALTH_BAR.changeHealth(Level.DAMAGE_TRAP)
            self.player.show_damage()
        elif TileType.HEALTH_ATTR in pair.tile.special_attr:
            Globals.HEALTH_BAR.changeHealth(Level.HEALTH_PICKUP)
        elif TileType.KEY_ATTR in pair.tile.special_attr:
            Globals.HUD_MANAGER.add_key()
        elif TileType.POTION_ATTR in pair.tile.special_attr:
            if Globals.DISORIENTED:
                Globals.DISORIENTED = False
                self.show_subtitle(Level.POTION_CURED_SUBTITLE,
                                   Level.POTION_CURED_SUBTITLE_LOOPS)
                self.player.stop_and_set_direction(self.player.direction)
                Globals.HEALTH_BAR.changeHealth(
                    Level.POTION_PICKUP_DISORIENTED)
            else:
                Globals.HEALTH_BAR.changeHealth(Level.POTION_PICKUP)

    def handle_finish_fade_out(self):
        self.stop_music()
        if not Globals.goto_next_level():
            self.handle_last_level()

    def handle_last_level(self):
        manager = HighscoreManager()
        manager.add(Globals.PLAYER_NAME, Globals.PLAYER_SCORE)
        Globals.STATE = WinGame.WinGame()  # for now

    def handle_finish_fade_in(self):
        if self.timer is not None:
            self.timer.unpause()

    def replace_special_tile(self, pair):
        if pair.tile.is_replaceable:
            row, col = self.camera.tileEngine.get_tile_pos(
                pair.coords[0], pair.coords[1])
            base = self.camera.tileEngine.get_tile_from_attr(
                pair.tile.replace_attr)
            if base is None:
                base = self.camera.tileEngine.get_tile_from_attr(
                    TileType.BASE_ATTR)
            self.camera.tileEngine.tileMap[row][col] = base
            self.camera.set_dirty()
            self.lights = [
                l for l in self.lights
                if not l.rect.colliderect(self.player.rect)
            ]

    def init_player(self):
        self.player = Player(Globals.WIDTH, Globals.HEIGHT, Globals.WIDTH / 2,
                             Globals.HEIGHT / 2)
        self.playerSprites.add(self.player)

    def init_enemies(self):
        tile_map = self.tile_engine.tileMap
        base_tile = self.tile_engine.get_tile_from_attr(TileType.BASE_ATTR)
        for row_num in range(0, len(tile_map)):
            for col_num in range(0, len(tile_map[row_num])):
                if tile_map[row_num][col_num] is None:
                    continue
                if TileType.SPAWN_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_enemy(row_num, col_num)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.CHASE_SPAWN_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_enemy(row_num, col_num, chase_enemy=True)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.BOSS_SPAWN in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_enemy(row_num, col_num, boss=True)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.TURRET_LEFT in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_turret(row_num, col_num, True)
                elif TileType.TURRET_RIGHT in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_turret(row_num, col_num, False)
                elif TileType.LIGHT_REPLACE_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_light(row_num, col_num)
                    tile_map[row_num][col_num] = base_tile
                    self.camera.set_dirty()
                elif TileType.LIGHT_ATTR in \
                        tile_map[row_num][col_num].special_attr:
                    self.add_light(row_num, col_num)

    def add_light(self, row_num, col_num):
        y = self.tile_rect.height * (row_num - 1) - self.camera.viewpoint.top
        x = self.tile_rect.width * (col_num - 1) - self.camera.viewpoint.left
        self.lights.append(LightSource(x, y, self.tile_rect.width))

    def add_enemy(self, row_num, col_num, chase_enemy=False, boss=False):
        y = self.tile_rect.height * row_num - self.camera.viewpoint.top
        x = self.tile_rect.width * col_num - self.camera.viewpoint.left
        if boss:
            enemy = BossEnemy(camera=self.camera, x=x, y=y)
        elif chase_enemy:
            enemy = ChaseEnemy(camera=self.camera, x=x, y=y)
        else:
            enemy = Enemy(Globals.WIDTH, Globals.HEIGHT, x=x, y=y)
        self.enemySprites.add(enemy)

    def add_turret(self, row_num, col_num, left):
        row_num += 1
        if not left:
            col_num += 1
        y = self.tile_rect.height * row_num - self.camera.viewpoint.top
        x = self.tile_rect.width * col_num - self.camera.viewpoint.left
        self.turrets.append(Turret(x, y, left))

    def check_collisions(self):
        radius = max(self.player.rect.size) * 2
        self.check_turret_collisions()
        self.check_punching_collisions()
        self.enemySprites = pygame.sprite.Group(
            [e for e in self.enemySprites if e.is_alive])
        self.check_enemy_collisions()
        special_tiles = self.camera.get_special_tiles(self.player.rect.center,
                                                      radius)
        self.check_stair_collisions(special_tiles)
        self.check_special_collisions(special_tiles)
        self.check_action_hints()

    def check_action_hints(self):
        if self.showing_subtitle:
            return
        temp_rect = self.player.rect.inflate(Player.ACTION_OFFSET,
                                             Player.ACTION_OFFSET)
        radius = max(temp_rect.size) * 2
        special_tiles = self.camera.get_special_tiles(self.player.rect.center,
                                                      radius)
        action_tiles = [
            pair for pair in special_tiles
            if TileType.ACTION_ATTR in pair.tile.special_attr
            and temp_rect.colliderect(pair.rect)
        ]
        locked_tiles = [
            pair for pair in action_tiles
            if TileType.LOCKED_ATTR in pair.tile.special_attr
        ]
        special_locked = [
            pair for pair in locked_tiles
            if TileType.SPECIAL_DOOR_ATTR in pair.tile.special_attr
        ]
        antidote_tiles = [
            pair for pair in action_tiles
            if TileType.ANTIDOTE_ATTR in pair.tile.special_attr
        ]
        if len(antidote_tiles) > 0:
            self.show_subtitle(Level.ANTIDOTE_HINT, Level.ANTIDOTE_TILE_LOOPS)
            return
        contains_red = self.camera.contains_attribute(TileType.REDBALL_ATTR)
        if len(action_tiles) > 0 and not self.can_open_doors:
            self.show_subtitle(Level.LOCKED_TILE_HINT, Level.LOCKED_TILE_LOOPS)
        elif len(special_locked) > 0 and contains_red:
            self.show_subtitle(Level.LOCKED_TILE_HINT, Level.LOCKED_TILE_LOOPS)
        elif len(special_locked) == 0 and len(locked_tiles) > 0 and \
                not Globals.HUD_MANAGER.has_key():
            self.show_subtitle(Level.LOCKED_TILE_HINT, Level.LOCKED_TILE_LOOPS)
        elif len(action_tiles) > 0 and (len(special_locked) == 0
                                        or not contains_red):
            self.show_subtitle(Level.ACTION_TILE_HINT, Level.ACTION_TILE_LOOPS)

    def check_special_collisions(self, special_tiles):
        for pair in special_tiles:
            if self.player.rect.colliderect(pair.rect):
                self.handle_special_collision(pair)

    def check_stair_collisions(self, special_tiles):
        stair_up_rects = [
            pair.rect for pair in special_tiles
            if TileType.STAIR_UP_ATTR in pair.tile.special_attr
        ]
        temp_rect = self.player.rect.inflate(-Player.STAIR_OFFSET,
                                             -Player.STAIR_OFFSET)
        num_up_stairs = len(temp_rect.collidelistall(stair_up_rects))
        if num_up_stairs > 0:
            self.handle_stair_up()
        stair_down_rects = [
            pair.rect for pair in special_tiles
            if TileType.STAIR_DOWN_ATTR in pair.tile.special_attr
        ]
        num_down_stairs = len(temp_rect.collidelistall(stair_down_rects))
        if num_down_stairs > 0:
            self.handle_stair_down()

    def check_enemy_collisions(self):
        enemy_rects = [
            enemy.rect.inflate(-20, -20) for enemy in self.enemySprites
        ]
        player_rect = self.player.rect.copy()
        if self.player.punching:
            # player_rect.inflate_ip(
            #     -player_rect.width * Level.PUNCHING_INFLATE,
            #     -player_rect.height * Level.PUNCHING_INFLATE)
            pass
        collided_indices = player_rect.collidelistall(enemy_rects)
        if len(collided_indices) > 0:
            self.handle_health_change(Enemy.HEALTH_EFFECT)

    def check_punching_collisions(self):
        if not self.player.punching:
            return
        punching_rect = self.player.get_punching_rect()
        for enemy in self.enemySprites:
            if punching_rect.colliderect(enemy.rect):
                enemy.handle_hit(self.camera, self.player)

    def check_turret_collisions(self):
        for turret in self.turrets:
            for syringe in turret.syringeSprites:
                if self.player.rect.colliderect(syringe):
                    self.handle_health_change(syringe.health_effect)
                    syringe.kill()

    def handle_health_change(self, health_effect):
        Globals.HEALTH_BAR.changeHealth(health_effect)
        if health_effect < 0:
            self.player.show_damage()

    def render(self):
        self.render_pre_fade()
        if self.fade_out or self.fade_in:
            Globals.SCREEN.blit(self.black_surf, (0, 0))
        self.render_post_fade()
        # r = self.player.get_punching_rect()
        # if r is not None:
        #     Globals.SCREEN.fill((255, 255, 255), r)

    def render_pre_fade(self):
        self.camera.render(Globals.SCREEN)
        self.render_lights()
        self.enemySprites.draw(Globals.SCREEN)
        for turret in self.turrets:
            turret.render(Globals.SCREEN)
        self.playerSprites.draw(Globals.SCREEN)
        self.render_overlay()
        if self.has_timer and not self.score_counted:
            self.timer.render(Globals.SCREEN)
        if self.showing_subtitle:
            Globals.SCREEN.blit(self.subtitle_surf, self.subtitle_rect)

    def render_lights(self):
        for light in self.lights:
            light.render(Globals.SCREEN)

    def render_overlay(self):
        if Globals.DISORIENTED:
            Globals.SCREEN.blit(Globals.get_disoriented_surf(), (0, 0),
                                special_flags=pygame.BLEND_SUB)

    def render_post_fade(self):
        Globals.HEALTH_BAR.render(Globals.SCREEN)
        Globals.HUD_MANAGER.render(Globals.SCREEN)

    def update(self, time):
        if self.time_init < Level.IGNORE_TIME:
            self.time_init += time
            return
        if Globals.HEALTH_BAR.is_dead():
            self.handle_lose_game()
        if self.fade_out or self.fade_in:
            self.update_alpha(time)
            return
        Globals.HEALTH_BAR.update(time)
        self.player.update(time, self.camera)
        self.enemySprites.update(time, self.camera, self.player)
        for turret in self.turrets:
            turret.update(time, self.camera)
        self.check_camera_position()
        self.check_collisions()
        self.update_subtitle(time)

    def handle_lose_game(self):
        self.stop_music()
        Globals.STATE = LoseGame.LoseGame()

    def update_subtitle(self, time):
        if not self.showing_subtitle:
            return
        old_alpha = self.subtitle_surf.get_alpha()
        if old_alpha == 0 or old_alpha == 255:
            self.alpha_factor *= -1
            if self.subtitle_loops != -1:
                self.subtitle_loops -= 1
                if self.subtitle_loops == 0:
                    self.stop_subtitle()
        new_alpha = int(old_alpha + self.alpha_factor * time)
        if new_alpha < 0:
            new_alpha = 0
        elif new_alpha > 255:
            new_alpha = 255
        self.subtitle_surf.set_alpha(new_alpha)

    def update_alpha(self, time):
        if self.fade_out:
            old_alpha = self.black_surf.get_alpha()
            new_alpha = int(old_alpha + time * Level.ALPHA_FACTOR)
            if new_alpha >= Level.MAX_ALPHA:
                if self.pausing:
                    self.handle_pause()
                elif self.going_back:
                    self.handle_go_back()
                else:
                    self.handle_finish_fade_out()
                self.fade_out = False
            self.black_surf.set_alpha(new_alpha)
        elif self.fade_in:
            old_alpha = self.black_surf.get_alpha()
            new_alpha = int(old_alpha - time * Level.ALPHA_FACTOR)
            if new_alpha <= Level.MIN_ALPHA:
                self.handle_finish_fade_in()
                self.fade_in = False
            self.black_surf.set_alpha(new_alpha)

    def start_fade_out(self):
        self.black_surf.set_alpha(Level.MIN_ALPHA)
        self.fade_out = True

    def start_fade_in(self):
        self.black_surf.set_alpha(Level.MAX_ALPHA)
        self.fade_in = True

    def handle_action_key(self):
        temp_rect = self.player.rect.inflate(Player.ACTION_OFFSET,
                                             Player.ACTION_OFFSET)
        radius = max(temp_rect.size) * 2
        pairs = self.camera.get_special_tiles(self.player.rect.center, radius)
        special_tiles = [
            pair for pair in pairs if temp_rect.colliderect(pair.rect)
        ]
        self.handle_sliding_doors(special_tiles)
        self.handle_action_switch(special_tiles)
        self.handle_antidote_check(special_tiles)

    def handle_antidote_check(self, special_tiles):
        for pair in special_tiles:
            if TileType.ANTIDOTE_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(
                    pair.coords[0], pair.coords[1])
                base = self.camera.tileEngine.get_tile_from_attr(
                    TileType.ANTIDOTE_REPLACE)
                self.camera.tileEngine.tileMap[row][col] = base
                self.camera.set_dirty()
                self.handle_antidote()

    def handle_antidote(self):
        pass

    def handle_sliding_doors(self, special_tiles):
        if not self.can_open_doors:
            return
        base = self.camera.tileEngine.get_tile_from_attr(TileType.BASE_ATTR)
        was_special_door = False
        for pair in special_tiles:
            if TileType.SLIDING_DOOR_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(
                    pair.coords[0], pair.coords[1])
                doors = self.get_sliding_doors(row, col)
                for pos in doors:
                    row, col = pos
                    tile_map = self.camera.tileEngine.tileMap
                    if TileType.SPECIAL_DOOR_ATTR in \
                            tile_map[row][col].special_attr:
                        was_special_door = True
                    tile_map[row][col] = base
                self.camera.set_dirty()
        if was_special_door:
            self.handle_special_door()

    def handle_special_door(self):
        pass

    def get_sliding_doors(self, row, col):
        init_num_keys = Globals.HUD_MANAGER.num_keys
        coords = list()
        use_key = True
        if TileType.LOCKED_ATTR in \
                self.camera.tileEngine.tileMap[row][col].special_attr:
            if TileType.SPECIAL_DOOR_ATTR in \
                    self.camera.tileEngine.tileMap[row][col].special_attr:
                if self.camera.contains_attribute(TileType.REDBALL_ATTR):
                    return list()
            elif not Globals.HUD_MANAGER.has_key():
                return list()
            else:
                init_num_keys = -1
                if use_key:
                    Globals.HUD_MANAGER.use_key()
        coords.append([row, col])
        result = self.get_doors_delta(row, col, init_num_keys, row_delta=-1)
        if result == -1:
            return list()
        coords.extend(result)
        result = self.get_doors_delta(row, col, init_num_keys, row_delta=1)
        if result == -1:
            return list()
        coords.extend(result)
        result = self.get_doors_delta(row, col, init_num_keys, col_delta=-1)
        if result == -1:
            return list()
        coords.extend(result)
        result = self.get_doors_delta(row, col, init_num_keys, col_delta=1)
        if result == -1:
            return list()
        coords.extend(result)
        return coords

    def get_doors_delta(self,
                        row,
                        col,
                        init_num_keys,
                        row_delta=0,
                        col_delta=0):
        coords = list()
        tile_map = self.camera.tileEngine.tileMap
        row += row_delta
        col += col_delta
        while self.camera.tileEngine.is_coord_valid(row, col) and \
                tile_map[row][col] is not None and \
                TileType.SLIDING_DOOR_ATTR in tile_map[row][col].special_attr:
            if TileType.LOCKED_ATTR in tile_map[row][col].special_attr:
                if TileType.SPECIAL_DOOR_ATTR in \
                        self.camera.tileEngine.tileMap[row][col].special_attr:
                    if self.camera.contains_attribute(TileType.REDBALL_ATTR):
                        return -1
                elif not Globals.HUD_MANAGER.has_key():
                    return -1
                elif Globals.HUD_MANAGER.num_keys >= init_num_keys:
                    Globals.HUD_MANAGER.use_key()
            coords.append([row, col])
            row += row_delta
            col += col_delta
        return coords

    def handle_action_switch(self, special_tiles):
        for pair in special_tiles:
            if TileType.LEVER_LEFT_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(
                    pair.coords[0], pair.coords[1])
                lever_right = self.camera.tileEngine.get_tile_from_attr(
                    TileType.LEVER_RIGHT_ATTR)
                self.camera.tileEngine.tileMap[row][col] = lever_right
                self.camera.set_dirty()
                self.handle_lever_on()
            elif TileType.LEVER_RIGHT_ATTR in pair.tile.special_attr:
                row, col = self.camera.tileEngine.get_tile_pos(
                    pair.coords[0], pair.coords[1])
                lever_left = self.camera.tileEngine.get_tile_from_attr(
                    TileType.LEVER_LEFT_ATTR)
                self.camera.tileEngine.tileMap[row][col] = lever_left
                self.camera.set_dirty()
                self.handle_lever_off()

    def handle_lever_on(self):
        self.switch_sound.play()
        for turret in self.turrets:
            turret.turn_off()

    def handle_lever_off(self):
        self.switch_sound.play()
        for turret in self.turrets:
            turret.turn_on()

    def event(self, event):
        if event.type == pygame.KEYDOWN:
            key = event.key if not Globals.DISORIENTED else \
                self.invert_key(event.key)
            self.handle_keydown(key)
        elif event.type == pygame.KEYUP:
            key = event.key if not Globals.DISORIENTED else \
                self.invert_key(event.key)
            self.handle_keyup(key)

    def handle_key_down(self, keydown):
        key = pygame.K_DOWN if not Globals.DISORIENTED else pygame.K_UP
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_key_up(self, keydown):
        key = pygame.K_UP if not Globals.DISORIENTED else pygame.K_DOWN
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_key_left(self, keydown):
        key = pygame.K_LEFT if not Globals.DISORIENTED else pygame.K_RIGHT
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_key_right(self, keydown):
        key = pygame.K_RIGHT if not Globals.DISORIENTED else pygame.K_LEFT
        if keydown:
            self.handle_keydown(key)
        else:
            self.handle_keyup(key)

    def handle_attack(self):
        self.player.handle_attack()

    def handle_raw_event(self, event):
        if event.type == pygame.KEYDOWN:
            key = event.key
            if key == pygame.K_1:
                self.handle_stair_up()
            elif key == pygame.K_2:
                delta = 100 - Globals.HEALTH_BAR.health
                Globals.HEALTH_BAR.changeHealth(delta)
            elif key == pygame.K_3:
                Globals.HUD_MANAGER.add_key()
            elif key == pygame.K_4:
                Globals.DISORIENTED = False
            elif key == pygame.K_5:
                self.replace_reds()
        elif event.type == self.music_end_id and not self.switched_sound:
            self.switched_sound = True
            self.stop_subtitle()
            self.music_handle = self.background_music_handle
            if self.start_on_stop:
                self.stop_music()
                self.start_music()

    def replace_reds(self):
        tile_map = self.camera.tileEngine.tileMap
        green_tile = self.camera.tileEngine.get_tile_from_attr(
            TileType.GREENBALL_ATTR)
        for row in range(0, len(tile_map)):
            for col in range(0, len(tile_map[row])):
                tile = tile_map[row][col]
                if tile is not None and \
                        TileType.REDBALL_ATTR in tile.special_attr:
                    tile_map[row][col] = green_tile
        self.camera.set_dirty()

    def handle_keydown(self, key):
        self.keyCode = key
        for p in self.playerSprites:
            p.keyPressed(key)

    def handle_go_back(self):
        if Globals.goto_previous_level():
            self.stop_music()
            self.timer.pause()
            self.player.stop_and_set_direction(Character.INDEX_UP)

    def start_pause_fade(self):
        if self.pausing:
            return
        self.pausing = True
        if self.has_timer:
            self.timer.pause()
        self.start_fade_out()

    def handle_pause(self):
        self.pausing = False
        if self.has_timer:
            self.timer.pause()
        self.pause_music()
        self.goto_pause()

    def goto_pause(self):
        Globals.STATE = PauseScreen(self)

    def handle_unpause(self):
        self.resume_music()
        if self.has_timer:
            self.timer.unpause()

    def handle_escape(self):
        # self.handle_pause()
        self.start_pause_fade()

    def handle_keyup(self, key):
        if key == self.keyCode:
            self.keyCode = None
            for p in self.playerSprites:
                p.keyReleased(key)

    def check_camera_position(self):
        dist_x = self.camera.container.centerx - self.player.rect.centerx
        dist_y = self.camera.container.centery - self.player.rect.centery
        if abs(dist_x) > Level.MAX_OFFSET_X:
            diff = abs(dist_x) - Level.MAX_OFFSET_X
            # player is to the right of center
            if dist_x < 0:
                pass
            # player is to the left of center
            else:
                diff *= -1
            self.camera.move(diff, 0)
            self.player.rect.centerx -= diff
            for enemy in self.enemySprites:
                enemy.rect.centerx -= diff
            for turret in self.turrets:
                turret.move(-diff, 0)
            for light in self.lights:
                light.move(-diff, 0)
        if abs(dist_y) > Level.MAX_OFFSET_Y:
            diff = abs(dist_y) - Level.MAX_OFFSET_Y
            # player is below center
            if dist_y < 0:
                pass
            # player is above center
            else:
                diff *= -1
            self.camera.move(0, diff)
            self.player.rect.centery -= diff
            for enemy in self.enemySprites:
                enemy.rect.centery -= diff
            for turret in self.turrets:
                turret.move(0, -diff)
            for light in self.lights:
                light.move(0, -diff)

    def init_subtitle(self, text):
        text_surf = Level.SUBTITLE_FONT.render(text, True,
                                               Level.SUBTITLE_COLOR)
        self.subtitle_rect = text_surf.get_rect()
        self.subtitle_rect.centerx = Globals.WIDTH / 2
        self.subtitle_rect.bottom = \
            Globals.HEIGHT - Level.SUBTITLE_MARGIN
        self.subtitle_rect.inflate_ip(Level.SUBTITLE_PADDING * 2,
                                      Level.SUBTITLE_PADDING * 2)
        self.subtitle_surf = pygame.Surface(self.subtitle_rect.size).convert()
        self.subtitle_surf.fill(Level.SUBTITLE_BACKGROUND)
        self.subtitle_surf.blit(
            text_surf, (Level.SUBTITLE_PADDING, Level.SUBTITLE_PADDING))
        self.subtitle_surf.set_alpha(255)

    def show_subtitle(self, text, loops=-1):
        self.subtitle_loops = loops
        if self.subtitle_loops != -1:
            self.subtitle_loops *= 2
        self.init_subtitle(text)
        self.showing_subtitle = True

    def stop_subtitle(self):
        self.showing_subtitle = False