Beispiel #1
0
class Game():
    def __init__(self):
        pygame.init()
        pygame.font.init()
        self.screen_commit = pygame.display.set_mode(WINDOW_SIZE)
        pygame.display.set_caption("Master Key - LD43")
        self.screen = pygame.Surface(MAX_FRAME_SIZE)

        pygame.mixer.pre_init(44100, -16, 1, 512)
        self.mus = pygame.mixer.Sound(fp("LD43.wav"))
        self.mus.play(-1)

        self.box_slide = pygame.mixer.Sound(fp("box_slide.wav"))
        self.box_slide.set_volume(0.12)
        self.box_fall = pygame.mixer.Sound(fp("box_fall.wav"))
        self.box_fall.set_volume(0.35)
        self.prompt_continue = pygame.mixer.Sound(fp("prompt_continue.wav"))
        self.prompt_continue.set_volume(0.3)
        self.collect_gem = pygame.mixer.Sound(fp("collect_gem.wav"))
        self.collect_gem.set_volume(0.5)
        self.altar_place = pygame.mixer.Sound(fp("altar_place.wav"))
        self.altar_place.set_volume(0.4)
        self.reset_noise = pygame.mixer.Sound(fp("reset.wav"))
        self.reset_noise.set_volume(0.15)
        self.dash_sound = pygame.mixer.Sound(fp("dash.wav"))
        self.dash_sound.set_volume(0.05)
        self.key_select = pygame.mixer.Sound(fp("key_select.wav"))
        self.key_select.set_volume(0.15)

        self.cam = Camera(self.screen_commit)
        self.cam.set_pan_pid(6, 2, -0.2)

        self.notice_frame = pygame.image.load(fp("notice.png"))

        self.levels = [
            Level("level_1.txt"),
            Level("level_2.txt"),
            Level("level_3.txt"),
            Level("level_3.5.txt"),
            Level("level_4.txt"),
            Level("level_5.txt"),
            Level("level_6.txt"),
            Level("level_7.txt")
        ]

        self.main()

    def main(self):

        #        level_to_msg = {0: "Welcome to Master Key!\nUse the arrow keys to move.",
        #            1: "Hold Z while moving to dash.",
        #            2: "Hold X to push objects.",
        #            3: "Hold C to hop up ledges.",
        #            4: "Altars require you to\nsacrifice your controls!\n\nStand in front of the book, and\n drag a key onto it with the mouse.",
        #            5: "You're on your own now.\n Good Luck!"}

        #        level_to_hint = {0: "Move around with the arrow keys.",
        #            1: "Hold Z while moving to dash.\nThis allows you to cross short gaps.",
        #            2: "Hold X while moving to push.\nYou can push cubes into holes.",
        #            3: "Hold C while moving to jump.\nThis lets you get up short platforms.",
        #            4: "Try standing next to the book, and\n drag a key onto it with the mouse.",
        #            5: "You can replace the key on an altar\nby dragging a different key onto it.",
        #            6: "Don't forget you can sacrifice\nyour movement keys, too!",
        #            7: "Don't forget you can sacrifice\nyour movement keys, too!",
        #            8: ""}

        #        for key in level_to_msg:
        #            self.tooltip(level_to_msg[key])
        #        for key in level_to_hint:
        #            self.tooltip(level_to_hint[key])

        self.level_counter = 0
        while self.level_counter < len(self.levels):
            self.cam.zoom = 1.0
            self.cam.set_target_zoom(1.0)
            self.run_level(self.levels[self.level_counter])
            self.level_counter += 1
        self.tooltip("That's all!\nThanks for playing!")

    def set_starting_hud(self):
        if self.level_counter == 0:
            bad = []
            for key in self.hud_key_array.hud_keys:
                if key.key in [JUMP, DASH, PUSH]:
                    bad.append(key)
            for item in bad:
                self.hud_key_array.hud_keys.remove(item)

        elif self.level_counter == 1:
            bad = []
            for key in self.hud_key_array.hud_keys:
                if key.key in [JUMP, PUSH]:
                    bad.append(key)
            for item in bad:
                self.hud_key_array.hud_keys.remove(item)

        elif self.level_counter == 2:
            bad = []
            for key in self.hud_key_array.hud_keys:
                if key.key in [JUMP]:
                    bad.append(key)
            for item in bad:
                self.hud_key_array.hud_keys.remove(item)


#################################################333333

    def tooltip(self, str):
        self.mus.set_volume(0.4)
        self.press_enable = False
        frame = self.notice_frame.copy().convert_alpha()
        cur_state = self.screen_commit.copy()

        myfont = pygame.font.Font(fp("Myriad.otf"), 30)
        tsplit = str.split("\n")
        l = len(tsplit)
        any_button = pygame.font.Font(fp("Myriad.otf"), 20)
        any_button_text = any_button.render("Press any button to continue.", 1,
                                            (0, 0, 0))

        tool_shadow_alpha = 0
        tool_shadow = pygame.Surface(WINDOW_SIZE)
        tool_shadow.fill((0, 0, 0))
        tool_shadow.set_alpha(tool_shadow_alpha)
        tool_shadow_rate = 300
        tool_shadow_max = 150
        tool_shadow_multiplier = 1.0

        for i2, i in enumerate(tsplit):
            a = myfont.render(i, 1, (0, 0, 0))
            frame.blit(a,
                       (frame.get_width() / 2 - a.get_width() / 2,
                        frame.get_height() / 2 - a.get_height() / 2 -
                        a.get_height() * 0.6 * l + a.get_height() * 1.2 * i2))
        frame.blit(
            any_button_text,
            (frame.get_width() / 2 - any_button_text.get_width() / 2, 240))

        then = time.time()
        pause_time = time.time()
        paused = True
        to_break = False

        while not to_break:
            now = time.time()
            dt = now - then
            then = now

            tsm = tool_shadow_multiplier

            events = pygame.event.get()
            for event in events:
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if time.time() - pause_time > 0.5:
                        if paused:
                            self.prompt_continue.play()
                            self.mus.set_volume(1.0)
                        paused = False

            tool_shadow_alpha = min(
                tool_shadow_max,
                int((time.time() - pause_time) * tool_shadow_rate))
            tool_shadow.set_alpha(tool_shadow_alpha * tool_shadow_multiplier)

            self.screen_commit.fill((0, 0, 0))
            self.screen_commit.blit(cur_state, (0, 0))
            self.screen_commit.blit(tool_shadow, (0, 0))
            x = WINDOW_WIDTH / 2 - self.notice_frame.get_width() / 2
            y = WINDOW_HEIGHT / 2 - self.notice_frame.get_height() / 2
            self.screen_commit.blit(
                frame,
                (x, int(y *
                        (3 * tsm - 2) - 200 * math.sin(2 * math.pi * tsm))))
            pygame.display.flip()

            if paused == False:
                tool_shadow_multiplier -= dt * 3
                if tool_shadow_multiplier < 0:
                    to_break = True

        pass

    def run_level(self, level_obj):

        self.press_en = True

        self.mrd = DOWN

        self.hud_key_array = HudKeyArray()

        self.clicked = 0
        self.selected_key = []
        self.hovered_shrine = []
        self.last_hover = []

        self.cur_level = level_obj

        self.then = time.time()

        self.player_pos = level_obj.player_start_pos()
        self.cam.set_center((self.player_pos[0] * TILE_WIDTH,
                             self.player_pos[1] * TILE_WIDTH - TILE_WIDTH))

        self.player = Player(self.player_pos)

        self.goal = Goal(level_obj.goal_pos())
        self.blocks = []
        for item in level_obj.block_pos():
            self.blocks.append(Block(item))

        self.set_starting_hud()

        self.shrines = []
        self.doors = []
        if self.cur_level.shrine_count() > 0:
            self.shrine_0 = Shrine(self.cur_level.shrine_0_pos(), num=0)
            self.door_0 = Door(self.cur_level.door_0_pos(), self.shrine_0)
            self.shrines.append(self.shrine_0)
            self.doors.append(self.door_0)

        shadow = pygame.Surface(WINDOW_SIZE)
        shadow.fill((0, 0, 0))
        shadow_opacity = 255.0
        shadow_fade_rate = 255.0
        self.shadow_dir = 1
        self.level_start = time.time()
        msg_not_said_yet = 1

        while True:

            now = time.time()
            dt = now - self.then
            self.then = now
            dt = self.cam.time_step(dt)

            if msg_not_said_yet and self.level_counter <= 5:
                self.press_en = 0

            if shadow_opacity < 80 and self.shadow_dir == 1:
                self.test_keydowns()
            else:
                pygame.event.get()

            self.screen.fill((0, 0, 0))

            pypos = self.player.pos[1]

            items_to_draw = self.blocks + self.shrines + self.doors + [
                self.player
            ] + [self.goal]
            self.draw_items(items_to_draw)

            self.cam.capture(self.screen)

            self.draw_tools(self.screen_commit)

            self.update_objects(dt)
            self.mouse_triggers()

            if shadow_opacity > 0 or self.shadow_dir == -1:
                shadow_opacity = max(
                    0,
                    shadow_opacity - self.shadow_dir * shadow_fade_rate * dt)
                shadow.set_alpha(shadow_opacity)
                self.screen_commit.blit(shadow, (0, 0))
            pygame.display.flip()

            if self.player_hit_goal(level_obj) and self.shadow_dir == 1:
                self.collect_gem.play()
                self.cam.set_zoom_pid(5, 1, -0.2)
                self.cam.set_target_zoom(1.5)
                self.goal.sprite.start_animation("collect")
                self.shadow_dir = -1

            if shadow_opacity > 255 and self.shadow_dir == -1:
                break

            level_to_msg = {
                0: "Welcome to Master Key!\nUse the arrow keys to move.",
                1: "Hold Z while moving to dash.",
                2: "Hold X to push objects.",
                3: "Hold C to hop up ledges.",
                4:
                "Altars require you to\nsacrifice your controls!\n\nStand in front of the book, and\n drag a key onto it with the mouse.",
                5: "You're on your own now.\n Good Luck!"
            }

            self.level_to_hint = {
                0: "Move around with the arrow keys.",
                1:
                "Hold Z while moving to dash.\nThis allows you to cross short gaps.",
                2:
                "Hold X while moving to push.\nYou can push cubes into holes.",
                3:
                "Hold C while moving to jump.\nThis lets you get up short platforms.",
                4:
                "Try standing next to the book, and\n drag a key onto it with the mouse.",
                5:
                "You can replace the key on an altar\nby dragging a different key onto it.",
                6: "Don't forget you can sacrifice\nyour movement keys, too!",
                7: "Don't forget you can sacrifice\nyour movement keys, too!",
                8: ""
            }

            if time.time(
            ) - self.level_start > 0.75 and msg_not_said_yet and self.level_counter in level_to_msg and self.shadow_dir != -1:
                self.tooltip(level_to_msg[self.level_counter])
                self.then = time.time()
                msg_not_said_yet = 0
                self.press_en = True

    def draw_items(self, items):
        self.player.pos = self.player.pos[0], self.player.pos[1] + 0.001

        bump_these = []
        for thing in items:
            if thing in self.blocks:
                if thing.mrd == UP and not thing.fell:
                    thing.pos = thing.pos[0], thing.pos[1] + 1
                    bump_these.append(thing)

        if self.mrd == UP:
            self.player.pos = self.player.pos[
                0], self.player.pos[1] + 1 + self.player.dash_mode
        items.sort(key=lambda x: x.pos[1])
        self.player.pos = self.player.pos[0], int(self.player.pos[1])
        y = 0
        while y <= self.cur_level.height:
            self.cur_level.draw_level(self.screen, y_range=(y, y))
            if not len(items):
                y += 1
                continue
            while items[0].pos[1] == y:
                items[0].draw(self.screen)
                items = items[1:]
                if items == []:
                    break
            y += 1
        if self.mrd == UP:
            self.player.pos = self.player.pos[
                0], self.player.pos[1] - 1 - self.player.dash_mode

        for block in bump_these:
            block.pos = block.pos[0], block.pos[1] - 1

    def can_move_here(self, pos, block=False, prev_pos=(0, 0), hop=False):

        if self.unpassable_door_here(pos):
            return False

        block_poses = [item.pos for item in self.blocks]

        if self.cur_level.can_move_here(pos,
                                        block=block,
                                        prev_pos=prev_pos,
                                        hop=hop):
            if pos not in block_poses:
                return True

        if self.cur_level.pit_here(pos) and pos in block_poses:
            return True

        return False

    def unpassable_door_here(self, pos):
        for item in self.doors:
            if item.pos == pos:
                if not item.is_passable():
                    return True
        if self.cur_level.unpassable_here(pos):
            return True
        if self.cur_level.shrine_here(pos):
            return True
        return False

    def block_here(self, pos):
        block_poses = [item.pos for item in self.blocks]
        for item in block_poses:
            if item == pos:
                if not self.cur_level.pit_here(pos):
                    return True

        return False

    def test_keydowns(self):

        events = pygame.event.get()

        for event in events:
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        if not self.press_en:
            return

        keydowns = self.player.get_keydowns(events)
        keyups = self.player.get_keyups(events)
        bad_keydowns = self.player.get_bad_keydowns(events)

        for item in keydowns:
            if item in [UP, DOWN, RIGHT, LEFT]:

                if not self.unpassable_door_here(
                        self.player.move_target(item, force_1=True)):
                    if not self.block_here(
                            self.player.move_target(item, force_1=True)):
                        if self.can_move_here(self.player.move_target(item),
                                              prev_pos=self.player.pos,
                                              hop=self.player.jump_mode):
                            if self.player.dash_mode:
                                self.dash_sound.play()
                            self.player.move(item)
                            self.mrd = item
                            if self.player.jump_mode:
                                self.player.hop()

                        elif self.can_move_here(self.player.move_target(
                                item, force_1=True),
                                                prev_pos=self.player.pos,
                                                hop=self.player.jump_mode):
                            if self.player.dash_mode:
                                self.dash_sound.play()
                            self.player.move(item, force_1=True)
                            self.mrd = item
                            if self.player.jump_mode:
                                self.player.hop()

                    elif self.player.push_mode:
                        for block in self.blocks:
                            if block.pos == self.player.move_target(item):
                                target = block.move_target(item)
                                if self.can_move_here((target), block=True):
                                    self.box_slide.play()
                                    block.move(item)
                                    self.player.move(item)
                                    self.mrd = item
                                # elif self.cur_level.

            if item == DASH:
                self.player.dash_mode = 1
            if item == JUMP:
                self.player.jump_mode = 1
            if item == PUSH:
                self.player.push_mode = 1

            if item == RESET:
                self.reset_noise.play()
                self.shadow_dir = -1
                level_index = self.levels.index(self.cur_level)
                self.level_counter -= 1

            if item == HINT:
                self.tooltip(self.level_to_hint[self.level_counter])
                self.then = time.time()

        for item in keyups:
            if item == DASH:
                self.player.dash_mode = 0
            if item == JUMP:
                self.player.jump_mode = 0
            if item == PUSH:
                self.player.push_mode = 0

    def update_objects(self, dt):
        self.hud_key_array.update(dt)
        if len(self.selected_key):
            self.selected_key[0].update(dt)
        for shrine in self.shrines:
            shrine.update(dt)
        for door in self.doors:
            door.update(dt)
        for block in self.blocks:
            block.update(dt)
            if self.cur_level.pit_here(block.pos):
                if block.fell == 0:
                    self.box_fall.play()
                    block.fall()

        self.goal.update(dt)

        self.player.update(dt)

        cam_x = self.player.pos[0] * TILE_WIDTH
        cam_y = self.player.pos[1] * TILE_WIDTH
        self.cam.set_target_center((cam_x, cam_y))

    def player_hit_goal(self, level_obj):
        if self.player.pos == level_obj.goal_pos():
            return True

    def draw_tools(self, surf):
        enabled = self.player.control_enables

        self.hud_key_array.draw(surf)
        if len(self.selected_key):
            self.selected_key[0].draw(surf)

    def check_adjacency_to_shrine(self, shrine):
        dist = abs(shrine.pos[0] -
                   self.player.pos[0]) + abs(shrine.pos[1] -
                                             self.player.pos[1])
        if dist <= 1:
            return True
        else:
            return False

    def mouse_triggers(self):
        clicked = pygame.mouse.get_pressed()[0]

        if not clicked and len(self.selected_key):
            if len(self.hovered_shrine):
                if self.check_adjacency_to_shrine(self.hovered_shrine[0]):
                    cap_key = self.hovered_shrine[0].captured_key
                    if len(cap_key):
                        cap_key[0].x, cap_key[0].y = (
                            cap_key[0].x - self.cam.pos[0] + WINDOW_WIDTH / 2 -
                            HUD_KEY_WIDTH, cap_key[0].y - self.cam.pos[1] +
                            WINDOW_WIDTH / 2 - HUD_KEY_HEIGHT - TILE_WIDTH * 2)
                        self.hud_key_array.hud_keys.append(cap_key[0])
                    self.selected_key[0].scale = 0.8
                    self.altar_place.play()
                    self.hovered_shrine[0].captured_key = [
                        self.selected_key.pop()
                    ]
                else:
                    self.hud_key_array.return_key(self.selected_key[0])
            else:
                self.hud_key_array.return_key(self.selected_key[0])
            self.selected_key = []

        self.hovered_shrine = []

        mpos = pygame.mouse.get_pos()
        mx = mpos[0]
        my = mpos[1]

        for key in self.hud_key_array.hud_keys:
            if key.mouse_over(mpos):
                if key not in self.last_hover:
                    self.last_hover = [key]
                    self.key_select.play()
                if clicked:
                    if len(self.selected_key):
                        self.hud_key_array.return_key(self.selected_key[0])
                    self.hud_key_array.select_key(key.key)
                    self.selected_key = [key]

        if len(self.selected_key):
            key = self.selected_key[0]
            key.target_x = mx - HUD_KEY_WIDTH / 2
            key.target_y = my - HUD_KEY_HEIGHT / 2

            for item in self.shrines:
                x = item.pos[
                    0] * TILE_WIDTH + -self.cam.pos[0] + WINDOW_WIDTH / 2
                y = item.pos[
                    1] * TILE_WIDTH + -self.cam.pos[1] + WINDOW_HEIGHT / 2
                key.target_scale = 1.2
                margin = TILE_WIDTH / 2
                if mx > x - margin and mx < x + item.width + margin:
                    if my > y - margin and my < y + item.height + margin:
                        key.target_scale = 0.5
                        self.hovered_shrine = [item]

        self.set_control_enables()

    def set_control_enables(self):
        self.player.reset_control_enables()

        for item in self.hud_key_array.hud_keys:
            for k in CONTROLS:
                if CONTROLS[k] == item.key:
                    self.player.control_enables[k] = 1
Beispiel #2
0
class Game():
    def __init__(self):
        pygame.init()
        pygame.mixer.init()

        #pygame.mixer.music.load('mus.wav')
        #pygame.mixer.music.play(-1)

        self.screen = pygame.Surface([WINDOW_WIDTH, WINDOW_HEIGHT])
        self.final_screen = pygame.display.set_mode(
            [WINDOW_WIDTH, WINDOW_HEIGHT])
        self.cam = Camera(self.final_screen)
        pygame.display.set_caption("Command Prompt")
        self.screen.fill((0, 0, 0))
        self.framerate = 50

        #   This just got a little too meta.

        self.beat_length = 500
        self.current_color = 0
        self.multiplier = 1.0
        self.health = 100.0
        self.healthbar_health = 100.0
        self.healthbar_color = [255, 255, 255]

        self.time_offset = 0

        self.goal_pos = (60, WINDOW_HEIGHT - 55)

        self.code_font = pygame.font.SysFont("monospace", 20)
        self.multiplier_font = pygame.font.SysFont("monospace", 50)
        self.win_font = pygame.font.SysFont("monospace", 35)
        self.clock = pygame.time.Clock()

        self.key_list = [
            pygame.K_q, pygame.K_w, pygame.K_e, pygame.K_r, pygame.K_t,
            pygame.K_y, pygame.K_u, pygame.K_i, pygame.K_o, pygame.K_p,
            pygame.K_a, pygame.K_s, pygame.K_d, pygame.K_f, pygame.K_g,
            pygame.K_h, pygame.K_j, pygame.K_k, pygame.K_l, pygame.K_z,
            pygame.K_x, pygame.K_c, pygame.K_v, pygame.K_b, pygame.K_n,
            pygame.K_m, pygame.K_RETURN
        ]
        self.last_presses = self.determine_keypresses()

        self.code = CODE
        self.lines = self.code.split("\n")
        for idx, item in enumerate(self.lines):
            if len(item) > 50:
                self.lines[idx] = item[0:50]
        i = 0
        while i < len(self.lines):
            if not len(self.lines[i]):
                self.lines.pop(i)
            else:
                i += 1
        self.line_idx = 0

        self.bang_list = []
        self.flash_list = []

        self.lines_to_render = []
        self.max_lines_rendered = 12
        self.yoffset = 25
        self.additional_offset = self.yoffset
        self.shock_offset = 0

        self.score = 0

        #self.success_sound = pygame.mixer.Sound('success.wav')
        #self.success_sound.set_volume(0.4)
        #self.failure_sound = pygame.mixer.Sound('failure.wav')
        #self.failure_sound.set_volume(0.7)

        self.beat_list = [
            8,
            16,
            24,
            28,
            #   First chorus
            32,
            36,
            40,
            44,
            48,
            52,
            56,
            60,
            64,
            68,
            72,
            76,
            80,
            82,
            84,
            88,
            90,
            92,
            #   Second chorus
            96,
            98,
            100,
            104,
            106,
            108,
            112,
            114,
            116,
            120,
            123,
            125,
            128,
            130,
            132,
            136,
            139,
            142,
            144,
            148,
            150,
            152,
            156,
            158,
            160,
            162,
            164,
            168,
            170,
            172,
            176,
            178,
            180,
            182,
            184,
            186,
            188,
            #   Third chorus
            192,
            194,
            195,
            200,
            204,
            207,
            208,
            212,
            216,
            217,
            218,
            224,
            226,
            228,
            232,
            233,
            234,
            235,
            236,
            239,
            241,
            243,
            244,
            246,
            248,
            249,
            250,
            251,
            252,
            256,
            258,
            260,
            262,
            264,
            265,
            266,
            267,
            268,
            270,
            271,
            272,
            276
        ]

    def render_score(self, pos):
        a = 10 - len(str(self.score))
        if pos == 0:
            string = game.code_font.render(
                "Score: %s%s  Juice:" % ("0" * a, self.score), 1,
                (255, 255, 255))
            self.screen.blit(string,
                             (120 + self.shock_offset, 50 + self.shock_offset))
        elif pos == 1:
            string = game.code_font.render("Score: %s" % (self.score), 1,
                                           (255, 255, 255))
            self.screen.blit(
                string, (300 + self.shock_offset, 260 + self.shock_offset))

    def render_goal(self, size):
        self.goal_size = (size, size)
        self.goal_surface = pygame.Surface(self.goal_size)
        self.goal_surface.set_colorkey((0, 0, 0))
        self.goal_color = (200, 200, 200)
        pygame.draw.circle(self.goal_surface, self.goal_color,
                           (self.goal_size[0] / 2, self.goal_size[0] / 2),
                           self.goal_size[0] / 2, 2)
        half = self.goal_size[0] / 2
        self.screen.blit(self.goal_surface,
                         (self.goal_pos[0] - half, self.goal_pos[1] - half))

    def render_bangs(self, size):
        size = (size + 30) / 2
        for bang in self.bang_list:
            bang_size = (size, size)
            bang_surface = pygame.Surface(bang_size)
            ypos = self.goal_pos[1] - bang.beats_away(self.time_ms) * 100.0
            lightness = max(200 - abs(self.goal_pos[1] - ypos), 120)
            bang_surface.set_colorkey((0, 0, 0))
            bang_color = (lightness, lightness, lightness)
            half = bang_size[0] / 2
            pygame.draw.circle(bang_surface, bang_color, (half, half), half, 0)
            half = bang_size[0] / 2
            self.screen.blit(bang_surface,
                             (self.goal_pos[0] - half, ypos - half))

    def render_flashes(self):
        for prop in self.flash_list:
            if prop > 1:
                prop = 1
            size = 30 + 120 * prop
            surf = pygame.Surface((size, size))
            surf.set_colorkey((0, 0, 0))
            shade = 255 - 255 * prop**0.5
            xpos, ypos = self.goal_pos[0], self.goal_pos[1]
            half = int(size / 2)
            color = [shade / 1.2, shade / 1.2, shade / 1.2]
            color[self.current_color] *= 1.2
            color = [int(i) for i in color]
            pygame.draw.circle(surf, (color[2], color[1], color[0]),
                               (half, half), half, 0)
            self.screen.blit(surf, (xpos - half, ypos - half))

    def make_flash(self):
        self.flash_list.append(0.0)

    def get_next_line(self):
        if self.line_idx >= len(self.lines):
            self.line_idx = 0
        res = self.line_idx
        self.line_idx += 1
        return self.lines[res]

    def render_lines(self):
        yoffset, xoffset, yspacing = 90, 120, self.yoffset
        start_position = (xoffset, WINDOW_HEIGHT - yoffset)
        current_y_position = start_position[1]

        for line in self.lines_to_render:
            t = OLD_FONT_TRANSPARENCY
            if line[0] == "!":
                hack = game.code_font.render(line, 1,
                                             (t / 2 + 128, t / 2, t / 2))
            elif line[:7] == "Current":
                hack = game.code_font.render(line, 1,
                                             (t / 2, t / 2 + 128, t / 2))
            elif line[0] == "#":
                hack = game.code_font.render(
                    line, 1, (t / 2 + 32, t / 2 + 96, t / 2 + 128))
            else:
                hack = game.code_font.render(line, 1, (t, t, t))
            self.screen.blit(
                hack,
                (start_position[0] + self.shock_offset, current_y_position +
                 self.additional_offset + self.shock_offset))
            current_y_position -= yspacing

    def update_lines(self, line):
        self.additional_offset = self.yoffset
        if len(self.lines_to_render) < self.max_lines_rendered:
            self.lines_to_render = [line] + self.lines_to_render
        else:
            self.lines_to_render = [line] + self.lines_to_render[:-1]

    def get_warning_message(self):
        message_list = [
            "!ERROR! : Out of mayonnaise.",
            "!WARNING! : Hacking too much time.",
            "!ERROR! : Speed has not reached 88mph.",
            "!CAUTION! : Ground approaching rapidly.",
            "!DANGER! : Troll in the dungeon.",
            "!WARNING! : Maximum recursion depth of 1 exceeded.",
            "!ERROR! : What the hell is even going on.",
            "!ERROR! : Improbability drive produced rats."
        ]
        a = rd.choice(message_list)
        if self.health <= 20:
            a = "!DANGER! : Juice levels critical!"
        return a

    def loop(self):
        self.clock.tick()
        self.time_ms = self.time_offset
        time_since_semicolon = 0
        speed = 1000
        progress = 0
        press_tolerance = 0.2
        multiplier_mentioned = 0
        flashy = 0
        self.last_error = self.time_ms
        beat = 0
        won = 0
        then = time.time()
        while True:
            pygame.event.pump()
            now = time.time()
            dt = now - then
            then = now
            dt = self.cam.time_step(dt)
            time_diff = dt * 1000
            pressed = self.presses_since_last()
            self.clock.tick(self.framerate)
            prev_time = self.time_ms
            self.time_ms += time_diff
            if prev_time % self.beat_length > self.time_ms % self.beat_length:
                beat += 1
                self.current_color += 1
                if self.current_color >= 3:
                    self.current_color = 0
                if beat + 4 in self.beat_list:
                    self.bang_list.append(
                        Bang(self.time_ms + self.beat_length * 4,
                             self.beat_length))
            time_since_semicolon += time_diff
            for key in pressed[0:-1]:
                if key == 1:
                    progress = min(progress + 0.15, 1.0)
            if pressed[
                    -1] == 1 and time_since_semicolon > 0.05 and progress == 1:
                for item in self.bang_list:
                    if abs(item.beats_away(self.time_ms)) <= press_tolerance:
                        self.multiplier += 1
                        flashy = 1
                        if self.multiplier % 5 == 0:
                            self.update_lines("Current multiplier: %s" %
                                              self.multiplier)
                        #self.success_sound.play()
                        self.update_lines(self.get_next_line() + "")
                        time_since_semicolon = 0
                        progress = 0
                        self.destroy_bang(item)
                        self.make_flash()
                        self.shock_offset = 5

            for item in self.bang_list:
                if item.beats_away(self.time_ms) < -press_tolerance:
                    #self.failure_sound.play()
                    self.cam.set_target_center(
                        (int(rd.random() * WINDOW_WIDTH),
                         int(rd.random() * WINDOW_HEIGHT)))
                    self.cam.set_target_zoom(rd.random() * 2 + 0.5)
                    #self.health = max(0, self.health - 30)
                    self.destroy_bang(item)
                    #self.shock_offset = 50
                    self.update_lines(self.get_warning_message())
                    self.update_lines("Multiplier reset.")
                    self.last_error = self.time_ms
                    self.multiplier = 1.0

            dif = (self.time_ms % self.beat_length) * 1.0 / self.beat_length
            self.render_background(dif)
            if flashy:
                flashy = 0
                self.screen.fill((150, 150, 150))
            num_over_one = 0
            for idx, item in enumerate(self.flash_list):
                self.flash_list[idx] = item + dif / 8
                if item > 1:
                    num_over_one += 1
            self.flash_list = self.flash_list[num_over_one:]

            self.render_flashes()
            min_rad = 0.3
            goal_size = max(dif, 1 - dif)**4
            goal_size = int((min_rad + goal_size * (1 - min_rad)) * 50)
            self.render_health()
            self.render_bangs(goal_size)
            self.render_goal(goal_size)
            self.render_lines()
            self.render_current_line(progress)
            self.additional_offset -= self.additional_offset * time_diff / 100.0
            self.shock_offset *= -0.6
            self.score += int(self.multiplier * time_diff)
            if self.health != 0:
                self.health = min(self.health + time_diff / 200.0, 100)
            self.render_score(0)
            if abs(self.shock_offset) <= 1:
                self.shock_offset = 0

            if beat > 280 or self.health == 0:
                won = self.health > 0
                break

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.display.quit()
                    pygame.quit()
                    sys.exit()

            self.cam.capture(self.screen)
            pygame.display.flip()

        while 1:
            time_diff = self.clock.tick(self.framerate)
            prev_time = self.time_ms
            self.time_ms += time_diff

            if prev_time % self.beat_length > self.time_ms % self.beat_length:
                self.current_color = (self.current_color + 1) % 3
                self.shock_offset += 12

            dif = (self.time_ms % self.beat_length) * 1.0 / self.beat_length
            self.render_background(dif)
            self.render_you_win(won)
            self.render_score(1)

            self.shock_offset *= -0.6
            if abs(self.shock_offset) <= 1:
                self.shock_offset = 0
            pygame.display.flip()

    def render_health(self):
        surf = pygame.Surface((300, 16))
        surf.set_colorkey((0, 0, 0))
        if self.health == 100:
            aim_color = (200, 200, 200)
        elif self.health >= 50:
            aim_color = (80, 110, 150)
        elif self.health >= 20:
            aim_color = (150, 140, 60)
            self.shock_offset += rd.choice([-1, 0, 1])
        else:
            aim_color = (130, 50, 40)
            self.shock_offset += rd.choice([-2, 0, 2])
        self.healthbar_color = [self.healthbar_color[i] + 5*(aim_color[i] - \
            self.healthbar_color[i])/self.framerate for i in range(0, 3)]
        color = (self.healthbar_color[0], self.healthbar_color[1],
                 self.healthbar_color[2])
        self.healthbar_health += 3 * (self.health -
                                      self.healthbar_health) / self.framerate
        pygame.draw.rect(surf, color, (0, 0, 3 * self.healthbar_health, 16))
        self.screen.blit(surf,
                         (432 + self.shock_offset, 52 + self.shock_offset))

    def render_you_win(self, won):
        if won:
            text = "Task completed."
        else:
            text = "Task failed."
        text_obj = self.win_font.render(text, 1, (255, 255, 255))
        self.screen.blit(text_obj,
                         (255 + self.shock_offset, 200 + self.shock_offset))

    def render_background(self, dif):
        self.screen.fill((10, 10, 10))
        if dif < 0.5:
            if self.current_color == 0:
                self.screen.fill(
                    (35 - dif * 50, 35 - dif * 50, 70 - dif * 120))
            elif self.current_color == 1:
                self.screen.fill((35 - dif * 50, 55 - dif * 90, 35 - dif * 50))
            elif self.current_color == 2:
                self.screen.fill(
                    (80 - dif * 140, 35 - dif * 50, 35 - dif * 50))

    def render_current_line(self, progress):
        line = self.lines[self.line_idx]
        num_letters = int(progress * len(line))
        new_line = line[0:num_letters]

        color = (155 + 100 * progress, 155 + 100 * progress,
                 155 + 100 * progress)
        # if progress == 1.0:
        #     color = (150, 255, 180)
        hack = self.code_font.render(new_line, 1, color)

        self.screen.blit(hack, (120 + self.shock_offset, WINDOW_HEIGHT - 90 +
                                self.yoffset + self.shock_offset))

    def determine_keypresses(self):
        pressed = pygame.key.get_pressed()
        self.last_keypresses = [pressed[key] for key in self.key_list]
        return self.last_keypresses

    def presses_since_last(self):
        #   -1 means key release
        a = self.last_keypresses
        b = self.determine_keypresses()
        c = [b[i] - a[i] for i in range(len(a))]
        return c

    def make_bang(self, beats_from_now):
        new_bang = Bang(self.time_ms + beats_from_now * self.beat_length)
        self.bang_list.append(new_bang)

    def destroy_bang(self, bang):
        self.bang_list.remove(bang)