class GameOverMenuViewController:
    MENU_SIZE = 284
    BUTTON_SIZE = (128, 32)

    def __init__(self, resolution, winner_name):
        self.resolution = resolution
        self.origin = ((resolution[0] - self.MENU_SIZE)/2,
                       (resolution[1] - self.MENU_SIZE)/2)
        space_from_border = (self.MENU_SIZE - self.BUTTON_SIZE[0])/2
        button_origin_x = self.origin[0] + space_from_border
        self.new_game_button = PygButton((button_origin_x,
                                          self.origin[1] + 144,
                                          self.BUTTON_SIZE[0],
                                          self.BUTTON_SIZE[1]), 'New Game')
        self.quit_button = PygButton((button_origin_x, self.origin[1] + 192,
                                      self.BUTTON_SIZE[0],
                                      self.BUTTON_SIZE[1]),
                                     'Quit')
        self.all_buttons = [self.new_game_button, self.quit_button]
        self.game_over_menu = GameOverMenu(winner_name)

    def handle_event(self, event):
        if 'click' in self.new_game_button.handleEvent(event):
            self.game_over_menu.state = GameOverMenu.NEW_GAME
        if 'click' in self.quit_button.handleEvent(event):
            self.game_over_menu.state = GameOverMenu.QUIT
Beispiel #2
0
class GameOverMenuViewController:
    MENU_SIZE = 284
    BUTTON_SIZE = (128, 32)

    def __init__(self, resolution, winner_name):
        self.resolution = resolution
        self.origin = ((resolution[0] - self.MENU_SIZE) / 2,
                       (resolution[1] - self.MENU_SIZE) / 2)
        space_from_border = (self.MENU_SIZE - self.BUTTON_SIZE[0]) / 2
        button_origin_x = self.origin[0] + space_from_border
        self.new_game_button = PygButton(
            (button_origin_x, self.origin[1] + 144, self.BUTTON_SIZE[0],
             self.BUTTON_SIZE[1]), 'New Game')
        self.quit_button = PygButton(
            (button_origin_x, self.origin[1] + 192, self.BUTTON_SIZE[0],
             self.BUTTON_SIZE[1]), 'Quit')
        self.all_buttons = [self.new_game_button, self.quit_button]
        self.game_over_menu = GameOverMenu(winner_name)

    def handle_event(self, event):
        if 'click' in self.new_game_button.handleEvent(event):
            self.game_over_menu.state = GameOverMenu.NEW_GAME
        if 'click' in self.quit_button.handleEvent(event):
            self.game_over_menu.state = GameOverMenu.QUIT
Beispiel #3
0
class GameMenuViewController:
    MENU_SIZE = 384
    BUTTON_SIZE = (276, 32)
    MODE_BUTTON_SIZE = (90, 32)

    def __init__(self, screen, resolution):
        self.screen = screen
        self.resolution = resolution
        self.origin = ((resolution[0] - self.MENU_SIZE) / 2,
                       (resolution[1] - self.MENU_SIZE) / 2)
        self.init_buttons(resolution)
        self.game_menu = GameMenu()

    def init_buttons(self, resolution):
        self.all_buttons = []
        #main game option buttons
        space_from_border = (self.MENU_SIZE - self.BUTTON_SIZE[0]) / 2
        button_origin_x = self.origin[0] + space_from_border
        b_width = self.BUTTON_SIZE[0]
        b_height = self.BUTTON_SIZE[1]

        s_p_button_frame = (button_origin_x, self.origin[1] + 60, b_width,
                            b_height)
        self.single_player = PygButton(s_p_button_frame, 'Single Player')
        self.all_buttons.append(self.single_player)

        m_p_button_frame = (button_origin_x, self.origin[1] + 120, b_width,
                            b_height)
        self.multy_player = PygButton(m_p_button_frame, 'Multy Player')
        self.all_buttons.append(self.multy_player)

        p_v_a_i_button_frame = (button_origin_x, self.origin[1] + 180, b_width,
                                b_height)
        self.player_vs_ai = PygButton(p_v_a_i_button_frame, 'Player vs AI')
        self.all_buttons.append(self.player_vs_ai)

        #AI difficulty buttons
        b_width = self.MODE_BUTTON_SIZE[0]
        b_height = self.MODE_BUTTON_SIZE[1]

        normal_button_frame = (button_origin_x, self.origin[1] + 220, b_width,
                               b_height)
        self.normal = PygButton(normal_button_frame, 'Normal')
        self.normal.bgcolor = (128, 128, 128)
        self.chosed_mode_button = self.normal
        self.all_buttons.append(self.normal)

        nightmare_button_frame = (button_origin_x + 92, self.origin[1] + 220,
                                  b_width, b_height)
        self.nightmare = PygButton(nightmare_button_frame, 'Nightmare')
        self.all_buttons.append(self.nightmare)

        inferno_button_frame = (button_origin_x + 184, self.origin[1] + 220,
                                b_width, b_height)
        self.inferno = PygButton(inferno_button_frame, 'Inferno')
        self.all_buttons.append(self.inferno)
        #Sound and Help buttons
        b_width = (self.BUTTON_SIZE[0] - 2) / 2
        b_height = (self.BUTTON_SIZE[1] - 2)
        sound_button_frame = (button_origin_x, self.origin[1] + 280, b_width,
                              b_height)
        self.sound = PygButton(sound_button_frame, 'Sound')
        self.all_buttons.append(self.sound)
        #        help_button_frame = (button_origin + b_width + 2 ,
        #                              self.origin[1] + 280, b_width, b_height)
        #        self.help = PygButton(help_button_frame, 'Help')
        #        self.all_buttons.append(self.help)

        #Quit button
        b_width = self.BUTTON_SIZE[0]
        b_height = self.BUTTON_SIZE[1]
        quit_button_frame = (button_origin_x, self.origin[1] + 340, b_width,
                             b_height)
        self.quit = PygButton(quit_button_frame, 'Quit')
        self.all_buttons.append(self.quit)

    def handle_event(self, event):
        if 'click' in self.single_player.handleEvent(event):
            self.game_menu.state = GameMenu.SINGLE_PLAYER
        if 'click' in self.multy_player.handleEvent(event):
            self.game_menu.state = GameMenu.MULTY_PLAYER
        if 'click' in self.player_vs_ai.handleEvent(event):
            self.game_menu.state = GameMenu.PLAYER_VS_MAC
        if 'click' in self.quit.handleEvent(event):
            self.game_menu.should_quit = True
        #handle game difficulty
        if 'click' in self.normal.handleEvent(event):
            self.game_menu.game_mode = GameMenu.AI_NORMAL
            self.chosed_mode_button.bgcolor = (212, 208, 200)
            self.normal.bgcolor = (128, 128, 128)
            self.chosed_mode_button = self.normal
        if 'click' in self.nightmare.handleEvent(event):
            self.game_menu.game_mode = GameMenu.AI_NIGHTMARE
            self.chosed_mode_button.bgcolor = (212, 208, 200)
            self.nightmare.bgcolor = (128, 128, 128)
            self.chosed_mode_button = self.nightmare
        if 'click' in self.inferno.handleEvent(event):
            self.game_menu.game_mode = GameMenu.AI_INFERNO
            self.chosed_mode_button.bgcolor = (212, 208, 200)
            self.inferno.bgcolor = (128, 128, 128)
            self.chosed_mode_button = self.inferno
        #handle sound
        if 'click' in self.sound.handleEvent(event):
            should_play_sound = self.game_menu.should_play_sound
            self.game_menu.should_play_sound = not should_play_sound
            if self.game_menu.should_play_sound:
                self.sound.bgcolor = (212, 208, 200)
                self.sound.caption = 'Sound'
            else:
                self.sound.bgcolor = (128, 128, 128)
                self.sound.caption = 'No Sound'
Beispiel #4
0
class StartMenu:

    def __init__(self):
        def _setup_display():
            pygame.display.set_mode((1280, 600))
            pygame.display.set_caption('Famished Tournament')
            self.screen = pygame.display.get_surface()
            self.done = False

            self.start_button = PygButton((325,395,140,40),'Start')
            self.help_button = PygButton((485,395,110,40), 'Help')
            self.options_button = PygButton((615,395,175,40), 'Options')
            self.exit_button = PygButton((810,395,105,40), 'Exit')

        def _setup_music():
            turn_on_music()

        pygame.init()
        _setup_display()
        _setup_music()

    def __call__(self):
        while not self.done:
            self.draw_UI()
            self.handle_events()

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if 'click' in self.start_button.handleEvent(event):
                GameLoop(self)()
            if 'click' in self.exit_button.handleEvent(event):
                pygame.quit()
                sys.exit()
            if 'click' in self.help_button.handleEvent(event):
                HelpPage(self)()
            if 'click' in self.options_button.handleEvent(event):
                OptionsPage(self)()
            if event.type == pygame.KEYDOWN:
                if event.key == K_RETURN:
                    GameLoop(self)()

    def draw_UI(self):

        self.image = pygame.image.load('data/temp_start_bkg.png')
        self.screen.blit(self.image, (0,0))

        self.start_button.draw(self.screen)
        self.help_button.draw(self.screen)
        self.options_button.draw(self.screen)
        self.exit_button.draw(self.screen)

        self.title_font = pygame.font.Font('data/Kremlin.ttf', 50)
        self.title1 = self.title_font.render('Famished', True, DKRED)
        self.title2 = self.title_font.render('Tournament', True, DKRED)
        self.screen.blit(self.title1, (495,120))
        self.screen.blit(self.title2, (450, 175))


        #text for transparent buttons
        #self.button_font = pygame.font.Font('data/Kremlin.ttf', 30)
        #self.b_start = self.button_font.render('Start', True, DKRED)
        #self.b_help = self.button_font.render('Help', True, DKRED)
        #self.b_options = self.button_font.render('Options', True, DKRED)
        #self.b_exit = self. button_font.render('Exit', True, DKRED)
        #self.screen.blit(self.b_start, (340, 400))
        #self.screen.blit(self.b_help, (500, 400))
        #self.screen.blit(self.b_options, (630, 400))
        #self.screen.blit(self.b_exit, (845, 400))

        pygame.display.flip()
class GameMenuViewController:
    MENU_SIZE = 384
    BUTTON_SIZE = (276, 32)
    MODE_BUTTON_SIZE = (90, 32)

    def __init__(self, screen, resolution):
        self.screen = screen
        self.resolution = resolution
        self.origin = ((resolution[0] - self.MENU_SIZE) / 2,
                       (resolution[1] - self.MENU_SIZE) / 2)
        self.init_buttons(resolution)
        self.game_menu = GameMenu()

    def init_buttons(self, resolution):
        self.all_buttons = []
        #main game option buttons
        space_from_border = (self.MENU_SIZE - self.BUTTON_SIZE[0])/2
        button_origin_x = self.origin[0] + space_from_border
        b_width = self.BUTTON_SIZE[0]
        b_height = self.BUTTON_SIZE[1]

        s_p_button_frame = (button_origin_x, self.origin[1] + 60, b_width,
                            b_height)
        self.single_player = PygButton(s_p_button_frame, 'Single Player')
        self.all_buttons.append(self.single_player)

        m_p_button_frame = (button_origin_x, self.origin[1] + 120, b_width,
                            b_height)
        self.multy_player = PygButton(m_p_button_frame, 'Multy Player')
        self.all_buttons.append(self.multy_player)

        p_v_a_i_button_frame = (button_origin_x, self.origin[1] + 180, b_width,
                                b_height)
        self.player_vs_ai = PygButton(p_v_a_i_button_frame, 'Player vs AI')
        self.all_buttons.append(self.player_vs_ai)

        #AI difficulty buttons
        b_width = self.MODE_BUTTON_SIZE[0]
        b_height = self.MODE_BUTTON_SIZE[1]

        normal_button_frame = (button_origin_x, self.origin[1] + 220, b_width,
                               b_height)
        self.normal = PygButton(normal_button_frame, 'Normal')
        self.normal.bgcolor = (128, 128, 128)
        self.chosed_mode_button = self.normal
        self.all_buttons.append(self.normal)

        nightmare_button_frame = (button_origin_x + 92, self.origin[1] + 220,
                                  b_width, b_height)
        self.nightmare = PygButton(nightmare_button_frame, 'Nightmare')
        self.all_buttons.append(self.nightmare)

        inferno_button_frame = (button_origin_x + 184, self.origin[1] + 220,
                                b_width, b_height)
        self.inferno = PygButton(inferno_button_frame, 'Inferno')
        self.all_buttons.append(self.inferno)
        #Sound and Help buttons
        b_width = (self.BUTTON_SIZE[0] - 2)/2
        b_height = (self.BUTTON_SIZE[1] - 2)
        sound_button_frame = (button_origin_x, self.origin[1] + 280, b_width,
                              b_height)
        self.sound = PygButton(sound_button_frame, 'Sound')
        self.all_buttons.append(self.sound)
#        help_button_frame = (button_origin + b_width + 2 ,
#                              self.origin[1] + 280, b_width, b_height)
#        self.help = PygButton(help_button_frame, 'Help')
#        self.all_buttons.append(self.help)

        #Quit button
        b_width = self.BUTTON_SIZE[0]
        b_height = self.BUTTON_SIZE[1]
        quit_button_frame = (button_origin_x, self.origin[1] + 340, b_width,
                             b_height)
        self.quit = PygButton(quit_button_frame, 'Quit')
        self.all_buttons.append(self.quit)

    def handle_event(self, event):
        if 'click' in self.single_player.handleEvent(event):
            self.game_menu.state = GameMenu.SINGLE_PLAYER
        if 'click' in self.multy_player.handleEvent(event):
            self.game_menu.state = GameMenu.MULTY_PLAYER
        if 'click' in self.player_vs_ai.handleEvent(event):
            self.game_menu.state = GameMenu.PLAYER_VS_MAC
        if 'click' in self.quit.handleEvent(event):
            self.game_menu.should_quit = True
        #handle game difficulty
        if 'click' in self.normal.handleEvent(event):
            self.game_menu.game_mode = GameMenu.AI_NORMAL
            self.chosed_mode_button.bgcolor = (212, 208, 200)
            self.normal.bgcolor = (128, 128, 128)
            self.chosed_mode_button = self.normal
        if 'click' in self.nightmare.handleEvent(event):
            self.game_menu.game_mode = GameMenu.AI_NIGHTMARE
            self.chosed_mode_button.bgcolor = (212, 208, 200)
            self.nightmare.bgcolor = (128, 128, 128)
            self.chosed_mode_button = self.nightmare
        if 'click' in self.inferno.handleEvent(event):
            self.game_menu.game_mode = GameMenu.AI_INFERNO
            self.chosed_mode_button.bgcolor = (212, 208, 200)
            self.inferno.bgcolor = (128, 128, 128)
            self.chosed_mode_button = self.inferno
        #handle sound
        if 'click' in self.sound.handleEvent(event):
            should_play_sound = self.game_menu.should_play_sound
            self.game_menu.should_play_sound = not should_play_sound
            if self.game_menu.should_play_sound:
                self.sound.bgcolor = (212, 208, 200)
                self.sound.caption = 'Sound'
            else:
                self.sound.bgcolor = (128, 128, 128)
                self.sound.caption = 'No Sound'
Beispiel #6
0
class StartMenu:
    def __init__(self):
        def _setup_display():
            pygame.display.set_mode((1280, 600))
            pygame.display.set_caption('Famished Tournament')
            self.screen = pygame.display.get_surface()
            self.done = False

            self.start_button = PygButton((325, 395, 140, 40), 'Start')
            self.help_button = PygButton((485, 395, 110, 40), 'Help')
            self.options_button = PygButton((615, 395, 175, 40), 'Options')
            self.exit_button = PygButton((810, 395, 105, 40), 'Exit')

        def _setup_music():
            turn_on_music()

        pygame.init()
        _setup_display()
        _setup_music()

    def __call__(self):
        while not self.done:
            self.draw_UI()
            self.handle_events()

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if 'click' in self.start_button.handleEvent(event):
                GameLoop(self)()
            if 'click' in self.exit_button.handleEvent(event):
                pygame.quit()
                sys.exit()
            if 'click' in self.help_button.handleEvent(event):
                HelpPage(self)()
            if 'click' in self.options_button.handleEvent(event):
                OptionsPage(self)()
            if event.type == pygame.KEYDOWN:
                if event.key == K_RETURN:
                    GameLoop(self)()

    def draw_UI(self):

        self.image = pygame.image.load('data/temp_start_bkg.png')
        self.screen.blit(self.image, (0, 0))

        self.start_button.draw(self.screen)
        self.help_button.draw(self.screen)
        self.options_button.draw(self.screen)
        self.exit_button.draw(self.screen)

        self.title_font = pygame.font.Font('data/Kremlin.ttf', 50)
        self.title1 = self.title_font.render('Famished', True, DKRED)
        self.title2 = self.title_font.render('Tournament', True, DKRED)
        self.screen.blit(self.title1, (495, 120))
        self.screen.blit(self.title2, (450, 175))

        #text for transparent buttons
        #self.button_font = pygame.font.Font('data/Kremlin.ttf', 30)
        #self.b_start = self.button_font.render('Start', True, DKRED)
        #self.b_help = self.button_font.render('Help', True, DKRED)
        #self.b_options = self.button_font.render('Options', True, DKRED)
        #self.b_exit = self. button_font.render('Exit', True, DKRED)
        #self.screen.blit(self.b_start, (340, 400))
        #self.screen.blit(self.b_help, (500, 400))
        #self.screen.blit(self.b_options, (630, 400))
        #self.screen.blit(self.b_exit, (845, 400))

        pygame.display.flip()
Beispiel #7
0
class GameOverPage:
    def __init__(self):
        self.bg_image = image_load('data/backgrounds/bg_menus_dim.png')
        self.menu_box = Rect2(topleft=(320, 120), size=(640, 240), border_color=BLACK, fill_color=DGREY)
        main_font = 'data/fonts/Kremlin.ttf'
        game_over_font = pygame.font.Font(main_font, 95)
        self.game_over_xy = font_position_center(self.menu_box, game_over_font, '-Game Over-')
        self.game_over_rendered = game_over_font.render('-Game Over-', True, RED)
        self.main_menu_button = PygButton((395, 270, 200, 50), 'Main Menu')
        self.exit_button = PygButton((730, 270, 100, 50), 'Exit')
        self.selection_box = Deque2([self.main_menu_button, self.exit_button])

    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.draw()
            self.input()
            self.events()
            GL.CLOCK.tick(GL.FPS)

    def draw(self):
        scaled_bg = image_scale(self.bg_image, self.menu_box.size)
        GL.SCREEN.blit(scaled_bg, self.menu_box.topleft)
        pygame.draw.rect(GL.SCREEN, self.menu_box.border_color, self.menu_box, 4)
        GL.SCREEN.blit(self.game_over_rendered, (self.game_over_xy[0], self.menu_box.top))
        self.main_menu_button.draw(GL.SCREEN)
        self.exit_button.draw(GL.SCREEN)
        pygame.draw.rect(GL.SCREEN, SELECTION_BOX_COLOR, self.selection_box().rect, SELECTION_BOX_WIDTH)
        draw_mouse_debug()
        pygame.display.update()

    def input(self):
        GL.INPUT1.refresh()

        if GL.INPUT1.CANCEL:
            self.return_now = True
            GL.NEXT_PAGE = '_start'

        if GL.INPUT1.CONFIRM:

            if self.selection_box() == self.main_menu_button:
                self.return_now = True
                GL.NEXT_PAGE = '_start'

            elif self.selection_box() == self.exit_button:
                EXIT_GAME()

        if GL.INPUT1.LEFT_EVENT:
            +self.selection_box

        if GL.INPUT1.RIGHT_EVENT:
            -self.selection_box

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                EXIT_GAME()

            if 'click' in self.main_menu_button.handleEvent(event):
                while self.selection_box() != self.main_menu_button:
                    +self.selection_box
                self.return_now = True
                GL.NEXT_PAGE = '_start'

            if 'click' in self.exit_button.handleEvent(event):
                while self.selection_box() != self.exit_button:
                    +self.selection_box
                EXIT_GAME()
Beispiel #8
0
class OptionsPage:
    def __init__(self):
        self.bg_image = image_load('data/backgrounds/bg_start_page.png')
        self.active_colors = BLACK, DKRED
        self.inactive_colors = DKRED, BLACK

        self.main_menu_button = PygButton((0, 550, 300, 50), 'Main Menu')
        self.music_on_button = PygButton((650, 200, 60, 50), 'ON')
        self.sound_on_button = PygButton((650, 260, 60, 50), 'ON')
        self.music_off_button = PygButton((730, 200, 80, 50), 'OFF')
        self.sound_off_button = PygButton((730, 260, 80, 50), 'OFF')

        font = pygame.font.Font('data/fonts/Kremlin.ttf', 40)
        self.bg_font = font.render('Music:', True, DKRED)
        self.se_font = font.render('Sound:', True, DKRED)

        self.selection_box = Deque2([
            Deque2([self.main_menu_button]),
            Deque2([self.sound_on_button, self.sound_off_button]),
            Deque2([self.music_on_button, self.music_off_button]),
        ])

        if not AUDIO.music_on:
            +self.selection_box(2)
        if not AUDIO.sound_on:
            +self.selection_box(1)

    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.draw()
            self.input()
            self.events()
            GL.CLOCK.tick(GL.FPS)

    def draw(self):
        if AUDIO.music_on:
            self.music_on_button.fgcolor, self.music_on_button.bgcolor = self.active_colors
            self.music_off_button.fgcolor, self.music_off_button.bgcolor = self.inactive_colors
        else:
            self.music_on_button.fgcolor, self.music_on_button.bgcolor = self.inactive_colors
            self.music_off_button.fgcolor, self.music_off_button.bgcolor = self.active_colors

        if AUDIO.sound_on:
            self.sound_on_button.fgcolor, self.sound_on_button.bgcolor = self.active_colors
            self.sound_off_button.fgcolor, self.sound_off_button.bgcolor = self.inactive_colors
        else:
            self.sound_on_button.fgcolor, self.sound_on_button.bgcolor = self.inactive_colors
            self.sound_off_button.fgcolor, self.sound_off_button.bgcolor = self.active_colors

        GL.SCREEN.blit(self.bg_image, (0, 0))
        GL.SCREEN.blit(self.bg_font, (450, 200))
        GL.SCREEN.blit(self.se_font, (450, 260))
        self.music_on_button.draw(GL.SCREEN)
        self.music_off_button.draw(GL.SCREEN)
        self.sound_on_button.draw(GL.SCREEN)
        self.sound_off_button.draw(GL.SCREEN)
        self.main_menu_button.draw(GL.SCREEN)

        pygame.draw.rect(GL.SCREEN, SELECTION_BOX_COLOR, self.selection_box()().rect, SELECTION_BOX_WIDTH)
        draw_mouse_debug()
        pygame.display.update()

    def input(self):
        GL.INPUT1.refresh()

        if GL.INPUT1.CONFIRM:
            if self.selection_box()() == self.main_menu_button:
                self.return_now = True
                GL.NEXT_PAGE = '_start'

        if GL.INPUT1.CANCEL:
            if self.selection_box()() == self.main_menu_button:  # if already on main menu button when hit select / b ..
                self.return_now = True  # .. then return
                GL.NEXT_PAGE = '_start'
            else:  # if not on main menu button when hit select / b ..
                while self.selection_box()() != self.main_menu_button:  # .. then go to return button
                    +self.selection_box

        if GL.INPUT1.UP_EVENT:
            -self.selection_box

        if GL.INPUT1.DOWN_EVENT:
            +self.selection_box

        if GL.INPUT1.LEFT_EVENT or GL.INPUT1.RIGHT_EVENT:
            if GL.INPUT1.LEFT_EVENT:
                +self.selection_box()
            if GL.INPUT1.RIGHT_EVENT:
                -self.selection_box()

            if self.selection_box()() == self.sound_on_button:
                AUDIO.turn_on_effects()
            elif self.selection_box()() == self.sound_off_button:
                AUDIO.turn_off_effects()
            elif self.selection_box()() == self.music_on_button:
                AUDIO.turn_on_music()
            elif self.selection_box()() == self.music_off_button:
                AUDIO.turn_off_music()

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                EXIT_GAME()

            if 'click' in self.music_on_button.handleEvent(event):
                while self.selection_box()() not in (self.music_on_button, self.music_off_button):
                    +self.selection_box
                while self.selection_box()() != self.music_on_button:
                    +self.selection_box()
                AUDIO.turn_on_music()

            if 'click' in self.music_off_button.handleEvent(event):
                while self.selection_box()() not in (self.music_on_button, self.music_off_button):
                    +self.selection_box
                while self.selection_box()() != self.music_off_button:
                    +self.selection_box()
                AUDIO.turn_off_music()

            if 'click' in self.sound_on_button.handleEvent(event):
                while self.selection_box()() not in (self.sound_on_button, self.sound_off_button):
                    +self.selection_box
                while self.selection_box()() != self.sound_on_button:
                    +self.selection_box()
                AUDIO.turn_on_effects()

            if 'click' in self.sound_off_button.handleEvent(event):
                while self.selection_box()() not in (self.sound_on_button, self.sound_off_button):
                    +self.selection_box
                while self.selection_box()() != self.sound_off_button:
                    +self.selection_box()
                AUDIO.turn_off_effects()

            if 'click' in self.main_menu_button.handleEvent(event):
                while self.selection_box()() != self.main_menu_button:
                    +self.selection_box
                self.return_now = True
                GL.NEXT_PAGE = '_start'
Beispiel #9
0
class LevelSelectPage:
    def __init__(self):
        self.return_button = PygButton((0, 550, 300, 50), 'Main Menu')
        self.ready = False
        self.bg_image = image_load('data/backgrounds/bg_level_select.png')
        self.bg_image2 = image_load('data/backgrounds/bg_level_select2.png')

        arena_image_size = (369, 153)
        human_arena_image = image_scale(image_load('data/backgrounds/arena_human.png'), arena_image_size)
        elf_arena_image = image_scale(image_load('data/backgrounds/arena_vines.png'), arena_image_size)
        android_arena_image = image_scale(image_load('data/backgrounds/arena_android.png'), arena_image_size)

        human_arena = Rect2(topleft=(29, 194), size=arena_image_size, bg=human_arena_image, arena=GL.arena4)
        elf_arena = Rect2(topleft=(444, 194), size=arena_image_size, bg=elf_arena_image, arena=GL.arena3)
        android_arena = Rect2(topleft=(874, 194), size=arena_image_size, bg=android_arena_image, arena=GL.arena5)
        self.levels = Deque2([human_arena, elf_arena, android_arena])


    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.input()
            self.draw()
            self.events()
            GL.CLOCK.tick(GL.FPS)

    def draw(self):
        GL.SCREEN.blit(self.bg_image, (0, 0))
        GL.SCREEN.blit(self.bg_image2, (0, 0))
        for level in self.levels:
            if self.levels() == level:
                outer = Rect2(left=level.left - 10, top=level.top - 10, width=level.width + 20, height=level.height + 20)
                inner = Rect2(left=level.left - 5, top=level.top - 5, width=level.width + 10, height=level.height + 10)
                pygame.draw.rect(GL.SCREEN, DKCYAN, outer)
                pygame.draw.rect(GL.SCREEN, CYAN, inner)
            GL.SCREEN.blit(level.bg, level.topleft)
        self.return_button.draw(GL.SCREEN)
        draw_mouse_debug()
        pygame.display.update()

    def input(self):
        GL.INPUT1.refresh()  # only player 1 can select level

        if GL.INPUT1.LEFT_EVENT:
            +self.levels

        if GL.INPUT1.RIGHT_EVENT:
            -self.levels

        if GL.INPUT1.CANCEL:
            GL.NEXT_PAGE = 'PlayerSelectPage()'
            self.return_now = True

        if GL.INPUT1.CONFIRM:
            self.ready = True
            print('Ready to load, setting arena ... ', end='')
            GL.SELECTED_ARENA = Arena(self.levels().arena)
            print('set.')
            GL.NEXT_PAGE = 'GameLoop()'
            self.return_now = True

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                EXIT_GAME()
            if 'click' in self.return_button.handleEvent(event):
                self.return_now = True
                GL.NEXT_PAGE = '_start'
Beispiel #10
0
class PlayerSelectPage:
    def __init__(self):
        self.return_button = PygButton((0, 550, 300, 50), 'Main Menu')

        self.bg_image = image_load('data/backgrounds/bg_player_select.png')
        self.humanPortrait = image_load('data/sprites+portraits/human_portrait.png')
        self.elfPortrait = image_load('data/sprites+portraits/elf_portrait.png')

        self.portraits = Deque2([self.humanPortrait, self.elfPortrait])
        self.portraits2 = Deque2([self.humanPortrait, self.elfPortrait])

        self.start_font = pygame.font.Font('data/fonts/Kremlin.ttf', 50)
        self.start_font_xy = font_position_center(GL.SCREEN.get_rect(), self.start_font, '---------------Press Start when ready---------------')
        self.start_font_rendered = self.start_font.render('---------------Press Start when ready---------------', True, YELLOW)

        self.ready1 = False

        # if there is a second gamepad, there is a second player
        # set ready to false if second player exists
        # if no second player, set ready to true
        self.ready2 = False if GL.INPUT2.gamepad_found else True

    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.draw()
            self.input()
            self.events()
            GL.CLOCK.tick(GL.FPS)

    def draw(self):
        GL.SCREEN.blit(self.bg_image, (0, 0))
        self.return_button.draw(GL.SCREEN)

        GL.SCREEN.blit(self.portraits(), (167, 106))
        GL.SCREEN.blit(self.portraits2(), (810, 106))

        if self.ready1 and self.ready2:
            GL.SCREEN.blit(self.start_font_rendered, self.start_font_xy)
        draw_mouse_debug()
        pygame.display.update()

    def input(self):

        def refresh_inputs():
            GL.INPUT1.refresh()
            GL.INPUT2.refresh()

        def player_select_inputs():
            if not self.ready1:
                if GL.INPUT1.LEFT_EVENT:
                    +self.portraits

                elif GL.INPUT1.RIGHT_EVENT:
                    -self.portraits

            if not self.ready2:
                if GL.INPUT2.LEFT_EVENT:
                    +self.portraits2

                elif GL.INPUT2.RIGHT_EVENT:
                    -self.portraits2


        def player_done_selecting():
            # if player presses A
            # they selected sprite
            # set sprite to player
            # if they pressed select
            # they want to select a different sprite or return to start screen
            if GL.INPUT1.CONFIRM:
                if self.portraits() == self.portraits2() and self.ready2:
                    print('Player 2 is using this character. Select a different one.')
                else:
                    print('Player 1 ready')
                    self.ready1 = True

            if GL.INPUT2.CONFIRM:
                if self.portraits() == self.portraits2() and self.ready1:
                    print('Player 1 is using this character. Select a different one.')
                else:
                    print('Player 2 ready')
                    self.ready2 = True

            # if player presses back when previously stated they were ready
            # allow them to reselect player
            if self.ready1 and GL.INPUT1.CANCEL:
                print('Player 1 not ready anymore')
                self.ready1 = False

            elif not self.ready1 and GL.INPUT1.CANCEL:
                print('Player 1 requested to go back to start')
                self.return_now = True
                GL.NEXT_PAGE = '_start'

            if self.ready2 and GL.INPUT2.CANCEL:
                print('Player 2 not ready anymore')
                self.ready2 = False

            elif not self.ready2 and GL.INPUT2.CANCEL:
                print('Player 2 requested to go back to start')
                self.return_now = True
                GL.NEXT_PAGE = '_start'

        def ready_for_start():
            if self.ready1 and self.ready2:
                # if player 1 or player 2 presses start when both players are ready
                # go to level select
                # if using a keyboard - only one player
                # if keyboard user presses 'A' when he is ready
                # go to level select
                if GL.INPUT1.CONFIRM or GL.INPUT2.CONFIRM:
                    print('Setting sprites')
                    set_sprites()
                    print('Set sprites')
                    print('going to level select screen')
                    GL.NEXT_PAGE = 'LevelSelectPage()'
                    self.return_now = True

        def set_sprites():
            # set spritesheet for player1
            if self.portraits() == self.humanPortrait:
                self.player1_spritesheet = 'data/sprites+portraits/human_p1.png'
            elif self.portraits() == self.elfPortrait:
                self.player1_spritesheet = 'data/sprites+portraits/elf_p1.png'

            # set spritesheet for player2
            if self.portraits2() == self.humanPortrait:
                self.player2_spritesheet = 'data/sprites+portraits/human_p2.png'
            elif self.portraits2() == self.elfPortrait:
                self.player2_spritesheet = 'data/sprites+portraits/elf_p2.png'

            GL.P1_SPRITESHEET = self.player1_spritesheet
            GL.P2_SPRITESHEET = self.player2_spritesheet

        refresh_inputs()
        ready_for_start()
        player_select_inputs()
        player_done_selecting()

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                EXIT_GAME()
            if 'click' in self.return_button.handleEvent(event):
                self.return_now = True
                GL.NEXT_PAGE = '_start'
Beispiel #11
0
class HelpPage:
    def __init__(self):
        self.return_button = PygButton((0, 550, 300, 50), 'Main Menu')
        self.section_font = pygame.font.Font('data/fonts/Kremlin.ttf', 40)
        self.font = pygame.font.Font('data/fonts/arial_narrow_7.ttf', 20)
        self.bg_image = image_load('data/backgrounds/bg_help.png')
        self.bg_title = self.section_font.render('Background', True, WHITE)
        self.bg_text = textwrap.wrap('Under the tyranny of the dark overlord, the world ' +
                                     'is in chaos and all the resources are nearly depleted.  ' +
                                     'Entire populations have been subjugated to life in labor ' +
                                     'camps, brutally policed by the overlord\'s military forces.  ' +
                                     'As your people\'s champion, you must fight to the death in the ' +
                                     'battle arena to win much needed resources.', width=50)
        self.goals_title = self.section_font.render('Goals', True, WHITE)
        self.goals_text = textwrap.wrap('Ultimately, you want to slay your opponent.  ' +
                                        'To become a better fighter, kill the monsters, gain ' +
                                        'experience, and pick up skills.  The player to land ' +
                                        'the last hit on the monster will receives the experience ' +
                                        'points.  An ultimate boss will spawn every few ' +
                                        'minutes.  These bosses drop ultimate skills which ' +
                                        'will help you humiliate and destroy your opponent.  ' +
                                        'Muah Hah Hah!!', width=50)
        self.selection_box = Deque2([self.return_button])

    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.draw()
            self.input()
            self.events()
            GL.CLOCK.tick(GL.FPS)

    def draw(self):
        GL.SCREEN.fill(BLACK)
        GL.SCREEN.blit(self.bg_image, (0, 0))

        GL.SCREEN.blit(self.bg_title, (800, 40))
        for num, text in enumerate(self.bg_text):
            line = self.font.render(text, True, DKRED)
            GL.SCREEN.blit(line, (800, 90 + (num * 20)))

        GL.SCREEN.blit(self.goals_title, (800, 250))
        for num, text in enumerate(self.goals_text):
            line = self.font.render(text, True, DKRED)
            GL.SCREEN.blit(line, (800, 300 + (num * 20)))

        self.return_button.draw(GL.SCREEN)
        pygame.draw.rect(GL.SCREEN, SELECTION_BOX_COLOR, self.selection_box().rect, SELECTION_BOX_WIDTH)
        draw_mouse_debug()
        pygame.display.update()

    def input(self):
        GL.INPUT1.refresh()

        if GL.INPUT1.CONFIRM or GL.INPUT1.CANCEL:
            self.return_now = True
            GL.NEXT_PAGE = '_start'

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                EXIT_GAME()
            if 'click' in self.return_button.handleEvent(event):
                self.return_now = True
                GL.NEXT_PAGE = '_start'
Beispiel #12
0
class StartPage:
    def __init__(self):
        self.bg_image = image_load('data/backgrounds/bg_start_page.png')
        self.start_button = PygButton((325, 395, 140, 40), 'Start')
        self.help_button = PygButton((485, 395, 110, 40), 'Help')
        self.options_button = PygButton((615, 395, 175, 40), 'Options')
        self.exit_button = PygButton((810, 395, 105, 40), 'Exit')
        if AUDIO.music_on:
            AUDIO.turn_on_music()
        title_font = pygame.font.Font('data/fonts/Kremlin.ttf', 50)
        self.title_font1 = title_font.render('Famished', True, DKRED)
        self.title_font2 = title_font.render('Tournament', True, DKRED)
        self.selection_box = Deque2([self.start_button, self.help_button, self.options_button, self.exit_button])

    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.draw()
            self.input()
            self.events()
            GL.CLOCK.tick(GL.FPS)

    def draw(self):
        GL.SCREEN.blit(self.bg_image, (0, 0))
        self.start_button.draw(GL.SCREEN)
        self.help_button.draw(GL.SCREEN)
        self.options_button.draw(GL.SCREEN)
        self.exit_button.draw(GL.SCREEN)
        GL.SCREEN.blit(self.title_font1, (495, 120))
        GL.SCREEN.blit(self.title_font2, (450, 175))
        pygame.draw.rect(GL.SCREEN, SELECTION_BOX_COLOR, self.selection_box().rect, SELECTION_BOX_WIDTH)
        draw_mouse_debug()
        pygame.display.update()

    def input(self):
        GL.INPUT1.refresh()
        if GL.INPUT1.QUICK_START:
            all_sprites = ['data/sprites+portraits/human_p1.png',
                           'data/sprites+portraits/elf_p1.png',
                           'data/sprites+portraits/human_p2.png',
                           'data/sprites+portraits/elf_p2.png']
            p1_sprite = random.choice(all_sprites)
            p2_sprite = random.choice([s for s in all_sprites if s != p1_sprite])
            GL.P1_SPRITESHEET = p1_sprite
            GL.P2_SPRITESHEET = p2_sprite

            arena = random.choice([GL.arena3, GL.arena4, GL.arena5])
            GL.SELECTED_ARENA = Arena(arena)

            self.return_now = True
            GL.NEXT_PAGE = 'GameLoop()'

        if GL.INPUT1.CONFIRM:

            if self.selection_box() == self.start_button:
                self.return_now = True
                GL.NEXT_PAGE = 'PlayerSelectPage()'

            elif self.selection_box() == self.help_button:
                self.return_now = True
                GL.NEXT_PAGE = '_help'

            elif self.selection_box() == self.options_button:
                self.return_now = True
                GL.NEXT_PAGE = '_options'

            elif self.selection_box() == self.exit_button:
                EXIT_GAME()

        if GL.INPUT1.LEFT_EVENT:
            +self.selection_box

        if GL.INPUT1.RIGHT_EVENT:
            -self.selection_box

        if GL.INPUT1.CANCEL:
            EXIT_GAME()

    def events(self):
        for event in pygame.event.get():
            if 'click' in self.start_button.handleEvent(event):
                while self.selection_box() != self.start_button:
                    +self.selection_box
                self.return_now = True
                GL.NEXT_PAGE = 'PlayerSelectPage()'

            if 'click' in self.help_button.handleEvent(event):
                while self.selection_box() != self.help_button:
                    +self.selection_box
                self.return_now = True
                GL.NEXT_PAGE = '_help'

            if 'click' in self.options_button.handleEvent(event):
                while self.selection_box() != self.options_button:
                    +self.selection_box
                self.return_now = True
                GL.NEXT_PAGE = '_options'

            if 'click' in self.exit_button.handleEvent(event):
                while self.selection_box() != self.exit_button:
                    +self.selection_box
                EXIT_GAME()

            if event.type == pygame.QUIT:
                EXIT_GAME()
Beispiel #13
0
class GameLoop:
    def __init__(self):
        def _setup_time():
            pygame.time.set_timer(TIME_TICK_EVENT, 250)
            pygame.time.set_timer(REGENERATION_EVENT, 1000)
            self.game_time = GameTime()

        def _setup_ui():
            self.bg_image = image_load('data/backgrounds/bg_menus.png')
            self.return_button = PygButton((490, 550, 300, 50), 'Main Menu')
            self.window_border = Rect2(left=0, top=0, width=1280, height=600)
            self.play_area_border = Rect2(left=60, top=0, width=1160, height=485)
            self.left_grey_fill = Rect2(left=0, top=0, width=65, height=600)
            self.right_grey_fill = Rect2(left=1215, top=0, width=60, height=600)
            self.bottom_grey_fill = Rect2(left=0, top=475, width=1280, height=125)
            self.skill_boxes = [
                # player 1 skill boxes
                Rect2(topleft=(90, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(140, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(190, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(240, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(290, 500), size=(40, 40), color=BLACK),
                # player 2 skill boxes
                Rect2(topleft=(950, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(1000, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(1050, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(1100, 500), size=(40, 40), color=BLACK),
                Rect2(topleft=(1150, 500), size=(40, 40), color=BLACK), ]
            self.health_bar_outline = image_load('data/backgrounds/health_bar_outline.png')
            self.health_bar_outline2 = image_load('data/backgrounds/health_bar_outline2.png')
            self.energy_bar_outline = image_load('data/backgrounds/energy_bar_outline.png')
            self.energy_bar_outline2 = image_load('data/backgrounds/energy_bar_outline2.png')

        def _setup_arena():
            self.arena = GL.SELECTED_ARENA
            self.arena_image = image_load(self.arena.background)

        def _setup_skills():
            initialize_skill_table()

            self.p1_red_skills_deque = Deque2([1] + auto_attack_skills())
            self.p1_blue_skills1_deque = Deque2(regular_skills())
            self.p1_blue_skills2_deque = Deque2(regular_skills())
            self.p1_blue_skills3_deque = Deque2(regular_skills())
            self.p1_yellow_skills_deque = Deque2(ultimate_skills())

            self.p2_red_skills_deque = Deque2([1] + auto_attack_skills())
            self.p2_blue_skills1_deque = Deque2(regular_skills())
            self.p2_blue_skills2_deque = Deque2(regular_skills())
            self.p2_blue_skills3_deque = Deque2(regular_skills())
            self.p2_yellow_skills_deque = Deque2(ultimate_skills())

            self.red_mask = pygame.Surface((40, 40))
            self.red_mask.fill(RED)
            self.red_mask.set_alpha(100)
            self.white_mask = pygame.Surface((40, 40))
            self.white_mask.fill(WHITE)
            self.white_mask.set_alpha(100)
            self.grey_bg = pygame.Surface((40, 40))
            self.grey_bg.fill(GREY)

        def _setup_fonts():
            # main_font = 'data/viner-hand-itc.ttf'
            main_font = 'data/fonts/Kremlin.ttf'
            self.timer_font = pygame.font.Font(main_font, 36)
            self.timer_font_xy = 605, 500
            self.debug_font_small = pygame.font.SysFont('consolas', 12)  # monospace
            self.debug_font_small_2 = pygame.font.SysFont('lucidasans', 12)  # monospace
            self.debug_font = pygame.font.SysFont('consolas', 20)  # monospace
            self.debug_font_xy1 = 1075, 505
            self.debug_font_xy2 = 1075, 520
            self.debug_font_xy3 = 1075, 540
            self.debug_font_xy4 = 1075, 560
            self.debug_font_xy5 = 800, 505
            self.debug_font_xy6 = 800, 520
            self.debug_font_xy7 = 800, 540
            self.cpu_avg = 0.0
            self.cpu_deque = Deque2((0,), maxlen=5)

            # Scrolling text stuff
            self.st_dmg_font = pygame.font.Font(main_font, 12)
            self.st_energy_font = pygame.font.Font(main_font, 12)
            self.st_condition_font = pygame.font.Font(main_font, 15)
            self.st_level_up_font = pygame.font.Font(main_font, 30)

            # Icon text
            self.icon_energy_font = pygame

            # Other text
            self.oor_font = pygame.font.Font(main_font, 20)

        def _setup_particles():
            self.active_particles = []

        def _setup_monsters():
            self.active_monsters = []
            self.ultimate_monster_active = False
            self.dropped_skills = []
            self.spawn_monsters = False
            pygame.event.post(pygame.event.Event(MONSTER_SPAWN_EVENT))

            self.weak_monster_image = image_load('data/sprites+portraits/monster_weak.png')
            self.medium_monster_image = image_load('data/sprites+portraits/monster_medium.png')
            self.ultimate_monster_image = image_load('data/sprites+portraits/monster_ultimate.png')

        def _setup_music():
            if AUDIO.music_on:
                AUDIO.play_next_random_song()

        def _setup_rain():
            self.rain_particles = []
            self.rain = Rect2(left=0, top=0, width=1, height=3)
            self.make_rain = False
            pygame.event.post(pygame.event.Event(MORE_RAIN_EVENT))

        def _setup_players():

            def _setup_player_sprites(spritesheet):
                try:
                    spritesheet1 = image_load(spritesheet)
                    spritesheet1.convert()
                except pygame.error:
                    return None

                m1 = []
                # Put spritesheet into list, each sprite is 64x64 pixels large, except for death
                for num in range(1, 8):  # Standing
                    m1.append(spritesheet1.subsurface((64 * (num - 1), 0, 64, 64)))
                for num in range(8, 16):  # Walk Transition
                    m1.append(spritesheet1.subsurface((64 * (num - 8), 64, 64, 64)))
                for num in range(16, 24):  # Walk Part 1
                    m1.append(spritesheet1.subsurface((64 * (num - 16), 128, 64, 64)))
                for num in range(24, 32):  # Walk Part 2
                    m1.append(spritesheet1.subsurface((64 * (num - 24), 192, 64, 64)))
                for num in range(32, 40):  # Jump and Fall
                    m1.append(spritesheet1.subsurface((64 * (num - 32), 256, 64, 64)))
                for num in range(40, 48):
                    m1.append(spritesheet1.subsurface((64 * (num - 40), 320, 64, 64)))
                for num in range(48, 56):
                    m1.append(spritesheet1.subsurface((64 * (num - 48), 384, 64, 64)))
                for num in range(56, 64):
                    m1.append(spritesheet1.subsurface((64 * (num - 56), 448, 64, 64)))
                for num in range(64, 71):
                    m1.append(spritesheet1.subsurface((64 * (num - 64), 512, 64, 64)))
                for num in range(71, 75):
                    m1.append(spritesheet1.subsurface((128 * (num - 71), 576, 128, 64)))

                for num in range(len(m1)):
                    m1[num].set_colorkey((0, 0, 0))  # sprite bg rgb is (0,0,0)
                    m1[num] = m1[num].convert_alpha()

                return m1

            self.player1 = Player(id=1, topleft=self.arena.p1_spawn, sprite=_setup_player_sprites(GL.P1_SPRITESHEET))
            self.player2 = Player(id=2, topleft=self.arena.p2_spawn, sprite=_setup_player_sprites(GL.P2_SPRITESHEET))

            self.player1.opposite = self.player2  # Makes things a lot easier
            self.player2.opposite = self.player1  # Makes things a lot easier

        _setup_time()
        _setup_ui()
        _setup_arena()
        _setup_skills()
        _setup_monsters()
        _setup_fonts()
        _setup_particles()
        _setup_music()
        _setup_rain()
        _setup_players()

    # ------------------------------------------------------------------------
    def __call__(self):
        self.return_now = False
        while not self.return_now:
            self.handle_players_inputs()
            self.handle_monsters(self.game_time.msec)
            self.handle_particles()
            self.draw_screen()
            self.draw_debug()
            self.handle_event_queue()
            self.check_if_game_over()
            pygame.display.update()
            GL.CLOCK.tick(GL.FPS)

    # -------------------------------------------------------------------------
    def handle_players_inputs(self):

        def _refresh_inputs():
            GL.INPUT1.refresh()
            GL.INPUT2.refresh()

        def _handle_players_inputs():
            self.player1(self.arena)
            self.player2(self.arena)

        def _handle_special_input():
            if GL.INPUT1.PAUSE_MODE_TOGGLED:
                self.return_now = True
                GL.CURR_GAME = self
                GL.NEXT_PAGE = '_pause'

            if GL.INPUT1.RESPAWN_CHEAT:
                self.player1.topleft = self.player1.topleft_initial
                self.player1.dx = self.player1.dx_initial
                self.player1.facing_direction = self.player1.facing_direction_initial
                self.player2.topleft = self.player2.topleft_initial
                self.player2.dx = self.player2.dx_initial
                self.player2.facing_direction = self.player2.facing_direction_initial

            if GL.INPUT1.KILLALL_CHEAT:
                for m in self.active_monsters:
                    m.hit_points = 0

            if GL.INPUT1.NEW_P1_RED_SKILL_CHEAT:
                self.player1.attack_id = +self.p1_red_skills_deque
            if GL.INPUT1.NEW_P1_BLUE1_SKILL_CHEAT:
                self.player1.skill1_id = +self.p1_blue_skills1_deque
            if GL.INPUT1.NEW_P1_BLUE2_SKILL_CHEAT:
                self.player1.skill2_id = +self.p1_blue_skills2_deque
            if GL.INPUT1.NEW_P1_BLUE3_SKILL_CHEAT:
                self.player1.skill3_id = +self.p1_blue_skills3_deque
            if GL.INPUT1.NEW_P1_YELLOW_SKILL_CHEAT:
                self.player1.ult_id = +self.p1_yellow_skills_deque

            if GL.INPUT1.NEW_P2_RED_SKILL_CHEAT:
                self.player2.attack_id = +self.p2_red_skills_deque
            if GL.INPUT1.NEW_P2_BLUE1_SKILL_CHEAT:
                self.player2.skill1_id = +self.p2_blue_skills1_deque
            if GL.INPUT1.NEW_P2_BLUE2_SKILL_CHEAT:
                self.player2.skill2_id = +self.p2_blue_skills2_deque
            if GL.INPUT1.NEW_P2_BLUE3_SKILL_CHEAT:
                self.player2.skill3_id = +self.p2_blue_skills3_deque
            if GL.INPUT1.NEW_P2_YELLOW_SKILL_CHEAT:
                self.player2.ult_id = +self.p2_yellow_skills_deque

            if GL.INPUT1.P1_ININITE_HEALTH_ENERGY_ON:
                self.player1.hit_points = self.player1.hit_points_max
                self.player1.energy = self.player1.energy_max

            if GL.INPUT1.P2_ININITE_HEALTH_ENERGY_ON:
                self.player2.hit_points = self.player2.hit_points_max
                self.player2.energy = self.player2.energy_max

        _refresh_inputs()
        _handle_players_inputs()
        _handle_special_input()

    # -------------------------------------------------------------------------
    def handle_particles(self):

        def _update_active_particles():
            if self.player1.new_particle:
                if isinstance(self.player1.new_particle, list):
                    for p in self.player1.new_particle:
                        if isinstance(p, Particle):
                            self.active_particles.append(p)
                        else:
                            self.arena.rects.append(Rect2(tuple(p)[0:4], color=p.color, hits_to_destroy=p.hits_to_destroy, spawn_point=p.spawn_point))
                else:
                    if isinstance(self.player1.new_particle, Particle):
                        self.active_particles.append(self.player1.new_particle)
                    else:
                        p = self.player1.new_particle
                        self.arena.rects.append(Rect2(tuple(p)[0:4], color=p.color, hits_to_destroy=p.hits_to_destroy, spawn_point=p.spawn_point))
                self.player1.new_particle = None

            if self.player2.new_particle:
                if isinstance(self.player2.new_particle, list):
                    for p in self.player2.new_particle:
                        if isinstance(p, Particle):
                            self.active_particles.append(p)
                        else:
                            self.arena.rects.append(Rect2(tuple(p)[0:4], color=p.color, hits_to_destroy=p.hits_to_destroy, spawn_point=p.spawn_point))
                else:
                    if isinstance(self.player2.new_particle, Particle):
                        self.active_particles.append(self.player2.new_particle)
                    else:
                        p = self.player2.new_particle
                        self.arena.rects.append(Rect2(tuple(p)[0:4], color=p.color, hits_to_destroy=p.hits_to_destroy, spawn_point=p.spawn_point))
                self.player2.new_particle = None

        def _update_particles():
            for p in self.active_particles:
                if p.expired:
                    if p.on_expire_f:
                        p.on_expire_f(p)
                    self.active_particles.remove(p)
                else:
                    p.update(self.game_time.msec)

        def _check_particle_collisions():
            for p in self.active_particles:
                opposite = self.player2 if p.belongs_to == self.player1 else self.player1

                # Ranged Particle
                if isinstance(p, RangeParticle):
                    # Check Terrains
                    all_terrain_hit_i = p.p_collidelistall(self.arena.rects)
                    if all_terrain_hit_i:  # False if empty list
                        if p.on_terrain_f:
                            p.on_terrain_f(p)
                        self.active_particles.remove(p)
                        for i in all_terrain_hit_i:
                            if i in range(0, len(self.arena.rects)):
                                self.arena.rects[i].hits_to_destroy -= 1
                                if self.arena.rects[i].hits_to_destroy == 0:
                                    self.arena.rects.pop(i)
                    # Check Monsters
                    else:
                        first_hit = p.collidelist(self.active_monsters)
                        if first_hit != -1:  # If hit a monsters
                            p.on_hit(self.active_monsters[first_hit], self.game_time.msec)
                            self.active_particles.remove(p)
                    # If didn't hit a monster, check player
                        else:
                            if p.colliderect(opposite):
                                p.on_hit(opposite, self.game_time.msec)
                                self.active_particles.remove(p)
                # Melee Particle
                elif isinstance(p, MeleeParticle):
                    # Check Monsters
                    all_monsters_hit_i = p.collidelistall(self.active_monsters)
                    for i in all_monsters_hit_i:
                        p.on_hit(self.active_monsters[i], self.game_time.msec)

                    first_terrain_hit_i = p.collidelist(self.arena.rects)
                    if first_terrain_hit_i != -1:
                        self.arena.rects[first_terrain_hit_i].hits_to_destroy -= 1
                        if self.arena.rects[first_terrain_hit_i].hits_to_destroy == 0:
                            self.arena.rects.pop(first_terrain_hit_i)
                    # Check Player
                    if p.colliderect(opposite):
                        p.on_hit(opposite, self.game_time.msec)
                # Field Particle
                else:
                    # Check Monsters and players
                    for t in self.active_monsters + [self.player1, self.player2]:
                        if p.is_in_field(t):
                            p.on_hit(t, self.game_time.msec)

        _update_active_particles()
        _update_particles()
        _check_particle_collisions()

    # -------------------------------------------------------------------------
    def handle_monsters(self, time):

        def _handle_monster_spawning():
            if self.spawn_monsters and len(self.active_monsters) < self.arena.max_monsters:
                spawn_point = self.arena.random_spawn_point  # pick a random spawn point
                color = random.choice((LLBLUE, DKYELLOW, DKPURPLE, DKORANGE))  # pick a random color
                monster_info = MONSTER_TABLE[random.choice(self.arena.possible_monsters)]  # pick a random monster
                self.active_monsters.append(Monster(monster_info, spawn_point.topleft, self.player1, self.player2, color))
            if not self.ultimate_monster_active:
                if self.game_time.msec != 0 and (self.game_time.msec % ULTIMATE_SPAWN_RATE) == 0:
                    spawn_point = self.arena.random_spawn_point  # pick a random spawn point
                    color = random.choice((LLBLUE, DKYELLOW, DKPURPLE, DKORANGE))  # pick a random color
                    self.ultimate_monster_active = True
                    self.active_monsters.append(Monster(MONSTER_TABLE[ULTIMATE], spawn_point.topleft, self.player1, self.player2, color))
            self.spawn_monsters = False

        def _handle_dead_monsters():
            for m in self.active_monsters:
                if m.is_dead():
                    dropped_skill_id = get_dropped_skill(m)
                    col = RED
                    if 100 <= dropped_skill_id < 1000:
                        col = BLUE
                    elif dropped_skill_id >= 1000:
                        col = YELLOW

                    dropped_skill_rect = Rect2(topleft=m.topleft, size=(25, 25), id=dropped_skill_id, color=col)
                    self.arena.dropped_skills.append(dropped_skill_rect)
                    if m.kind == ULTIMATE:
                        self.ultimate_monster_active = False
                    if m.last_hit_by is not None:
                        m.last_hit_by.handle_exp(m.exp_value, self.game_time.msec)

                    # Debugging: kill button used
                    else:
                        self.player1.handle_exp(m.exp_value, self.game_time.msec)
                    self.active_monsters.remove(m)

        def _update_monsters(time):
            for m in self.active_monsters:
                m(self.game_time.msec, self.arena)
                if m.colliderect(self.player1):
                    m.on_hit(self.player1, time)
                if m.colliderect(self.player2):
                    m.on_hit(self.player2, time)

        _handle_monster_spawning()
        _handle_dead_monsters()
        _update_monsters(time)

    # -------------------------------------------------------------------------
    def draw_screen(self):

        def _draw_ui1():
            GL.SCREEN.blit(self.bg_image, (0, 0))
            if self.arena.background is not None:
                GL.SCREEN.blit(self.arena_image, (self.arena.play_area_rect.left, 0))

        def _draw_ui2():
            # health bars
            GL.SCREEN.blit(self.health_bar_outline, (5, 20))
            GL.SCREEN.blit(self.health_bar_outline2, (1239, 20))

            # dynamic health bars
            self.damage_taken1 = self.player1.hit_points_max - self.player1.hit_points
            self.damage_taken2 = self.player2.hit_points_max - self.player2.hit_points
            self.health_bar1 = Rect((20, (21 + (2 * self.damage_taken1))), (20, (200 - (2 * self.damage_taken1))))
            self.health_bar2 = Rect((1241, (21 + (2 * self.damage_taken2))), (20, (200 - (2 * self.damage_taken2))))
            pygame.draw.rect(GL.SCREEN, YELLOW, self.health_bar1)
            pygame.draw.rect(GL.SCREEN, YELLOW, self.health_bar2)

            # energy bars
            GL.SCREEN.blit(self.energy_bar_outline, (5, 280))
            GL.SCREEN.blit(self.energy_bar_outline2, (1239, 280))

            # dynamic energy bars
            self.energy_used1 = 10 - self.player1.energy
            self.energy_used2 = 10 - self.player2.energy
            self.energy_bar1 = Rect((20, 281 + (20 * self.energy_used1)), (20, 200 - (20 * self.energy_used1)))
            self.energy_bar2 = Rect((1241, 281 + (20 * self.energy_used2)), (20, 200 - (20 * self.energy_used2)))
            pygame.draw.rect(GL.SCREEN, GREEN, self.energy_bar1)
            pygame.draw.rect(GL.SCREEN, GREEN, self.energy_bar2)

            self.return_button.draw(GL.SCREEN)

        def _draw_ui_skill_boxes():
            skill_ids = self.player1.skills + self.player2.skills
            for i, skill_box in enumerate(self.skill_boxes):
                # skill box color border (depending on skill type)
                if i in (0, 5):
                    pygame.draw.rect(GL.SCREEN, DKRED, skill_box, 5)
                elif i in (1, 2, 3, 6, 7, 8):
                    pygame.draw.rect(GL.SCREEN, BLUE, skill_box, 5)
                elif i in (4, 9):
                    pygame.draw.rect(GL.SCREEN, DKYELLOW, skill_box, 5)

                # skill box (empty/black)
                pygame.draw.rect(GL.SCREEN, skill_box.color, skill_box)

                if skill_ids[i] in ICONS_TABLE.keys():  # if icon picture exists
                    GL.SCREEN.blit(self.grey_bg, (skill_box.left, skill_box.top))
                    GL.SCREEN.blit(ICONS_TABLE[skill_ids[i]], (skill_box.left, skill_box.top))

                else:  # if no icon picture exists
                    skill_text = str(SKILLS_TABLE[skill_ids[i]]['name'])
                    skill_font = self.debug_font_small_2.render(skill_text, True, WHITE)
                    skill_text_xy = font_position_center(skill_box, self.debug_font_small_2, skill_text)
                    GL.SCREEN.blit(skill_font, skill_text_xy)

                if i < 5:
                    if self.player1.energy < SKILLS_TABLE[skill_ids[i]]['energy']:
                        GL.SCREEN.blit(self.red_mask, (skill_box.left, skill_box.top))
                else:
                    if self.player2.energy < SKILLS_TABLE[skill_ids[i]]['energy']:
                        GL.SCREEN.blit(self.red_mask, (skill_box.left, skill_box.top))

        def _draw_ui_controls():
            li = [('X', RED), ('B', BLUE), ('Y', BLUE), ('R1', BLUE), ('R2', DKYELLOW) ]
            li = li + li
            for i, skill_box in enumerate(self.skill_boxes):
                font = self.debug_font.render(li[i][0], True, li[i][1])
                xy_centered = font_position_center(self.skill_boxes[i], self.debug_font, li[i][0])
                xy_new = xy_centered[0], xy_centered[1] - 30
                GL.SCREEN.blit(font, xy_new)

        def _draw_ui_controls2():
            if GL.INPUT1.ATTACK:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[0].topleft)
            if GL.INPUT1.SKILL1:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[1].topleft)
            if GL.INPUT1.SKILL2:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[2].topleft)
            if GL.INPUT1.SKILL3:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[3].topleft)
            if GL.INPUT1.ULT:       GL.SCREEN.blit(self.white_mask, self.skill_boxes[4].topleft)

            if GL.INPUT2.ATTACK:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[5].topleft)
            if GL.INPUT2.SKILL1:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[6].topleft)
            if GL.INPUT2.SKILL2:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[7].topleft)
            if GL.INPUT2.SKILL3:    GL.SCREEN.blit(self.white_mask, self.skill_boxes[8].topleft)
            if GL.INPUT2.ULT:       GL.SCREEN.blit(self.white_mask, self.skill_boxes[9].topleft)

        def _draw_timer():
            time_display = self.timer_font.render(str(self.game_time), True, BLUE)
            GL.SCREEN.blit(time_display, self.timer_font_xy)

        def _draw_arena():
            for rect in self.arena:
                if rect.color is not None:
                    pygame.draw.rect(GL.SCREEN, rect.color, rect)

        def _draw_players():
            def _draw_player(p):
                # Draw player using wait_frames and animation_key
                # wait_frames = frames waited before key is incremented
                # animation_key = index for the sprite list

                # Draw player 1
                if p.state != p.previous_state:
                    p.wait_frames = 0
                    p.animation_key = -1
                    # -1 because it will always get incremented at the start of each check
                flip = False  # value for flipping sprite

                if p.state == DEATH:
                    if p.facing_direction == LEFT:
                        flip = True
                    if p.wait_frames <= 0:
                        p.wait_frames = 3
                        if p.animation_key < 3:
                            p.animation_key += 1
                    if flip:
                        GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 70], flip, False), (p.left - 17 - 64, p.top - 22))
                    else:
                        GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 70], flip, False), (p.left - 17, p.top - 22))

                # elif p.state = WIN:

                elif p.state == ATTACK or p.state == RESET:
                    # Starting Indexes and how much sprites for each attack state are as follows
                    # ONEHAND = 28, 4
                    # TWOHAND = 32, 4
                    # CAST1 = 36, 3
                    # CAST2 = 39, 4
                    # CAST3 = 43, 1
                    # THROW = 44, 4
                    # MACHGUN = 52, 2
                    # BREATH = 54, 3
                    # POKE = 57, 4
                    # BULLET = 61, 4
                    # DASH = 7, 1
                    # RUN = 8, 16

                    if p.facing_direction == LEFT:
                        flip = True

                    if p.attack_state == 'RUN':
                        if p.wait_frames <= 0:
                            p.wait_frames = 2
                            p.animation_key = (p.animation_key + 1) % 16
                        GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 8], flip, False), (p.left - 17, p.top - 22))
                    elif p.attack_state == 'none':
                        p.wait_frames = 1
                    else:
                        if p.wait_frames <= 0:
                            p.wait_frames = p.attack_frame
                            if p.animation_key < \
                                    PL_ATTACK_TABLE[p.attack_state][1]:
                                p.animation_key += 1
                        GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + PL_ATTACK_TABLE[p.attack_state][0]], flip, False), (p.left - 17, p.top - 22))

                # JUMP
                elif p.state == JUMP:
                    if p.facing_direction == LEFT:
                        flip = True
                    if p.wait_frames <= 0:
                        p.wait_frames = 5
                        if p.animation_key <= 0:
                            p.animation_key += 1
                    GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 24], flip, False), (p.left - 17, p.top - 22))

                # FALL
                elif p.state == FALL:
                    if p.facing_direction == LEFT:
                        flip = True
                    if p.wait_frames <= 0:
                        p.wait_frames = 5
                        if p.animation_key <= 0:
                            p.animation_key += 1
                    GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 26], flip, False), (p.left - 17, p.top - 22))

                # WALK
                elif p.state == RWALK or p.state == LWALK:
                    if p.facing_direction == LEFT:
                        flip = True
                    if p.state == RWALK and p.previous_state != RWALK:
                        p.animation_key = -8  # Transition sprites loaded before walk
                    elif p.state == LWALK and p.previous_state != LWALK:
                        p.animation_key = -8
                    if p.wait_frames <= 0:
                        p.wait_frames = 2
                        p.animation_key += 1
                        if p.animation_key > 0:
                            p.animation_key %= 16  # Loops the key
                    GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 8], flip, False), (p.left - 17, p.top - 22))

                # STAND (default animation)
                else:
                    if p.facing_direction == LEFT:
                        flip = True
                    # Currently only have 1 standing sprite
                    GL.SCREEN.blit(pygame.transform.flip(p.sprite[p.animation_key + 1], flip, False), (p.left - 17, p.top - 22))
                p.wait_frames += -1

            if self.player1.sprite is not None:
                _draw_player(self.player1)
            if self.player2.sprite is not None:
                _draw_player(self.player2)

            # If player is above screen
            for p in [self.player1, self.player2]:
                if p.bottom < 0:
                    text = 'P1: ' if p.id == 1 else 'P2: '
                    text2 = str(p.bottom)
                    color = BLUE if p.id == 1 else GREEN
                    v1 = (p.centerx, 5)
                    v2 = (p.centerx - 10, 30)
                    v3 = (p.centerx + 10, 30)
                    vlist = [v1, v2, v3]

                    GL.SCREEN.blit(self.oor_font.render(text, True, color), (p.centerx - 20, 40))
                    GL.SCREEN.blit(self.st_dmg_font.render(text2, True, color), (p.centerx + 10, 40))
                    pygame.draw.polygon(GL.SCREEN, color, vlist)

        def _draw_monsters():
            for m in self.active_monsters:
                if m.kind == WEAK:
                    GL.SCREEN.blit(self.weak_monster_image, m.topleft)
                elif m.kind == MEDIUM:
                    GL.SCREEN.blit(self.medium_monster_image, m.topleft)
                elif m.kind == ULTIMATE:
                    GL.SCREEN.blit(self.ultimate_monster_image, m.topleft)
                health_bar = Rect2(left=m.left, top=m.top - 8, width=m.width, height=6)
                health_bar_width = round(m.width * (m.hit_points / m.hit_points_max))
                health_bar_life = Rect2(left=m.left, top=m.top - 8, width=health_bar_width, height=6)

                pygame.draw.rect(GL.SCREEN, WHITE, health_bar)
                pygame.draw.rect(GL.SCREEN, RED, health_bar_life)
                pygame.draw.rect(GL.SCREEN, BLACK, health_bar, 1)

        def _draw_dropped_skills():
            for skill in self.arena.dropped_skills:
                pygame.draw.rect(GL.SCREEN, skill.color, skill)
                if skill.id not in ICONS_TABLE.keys():
                    skill_text = str(skill.id)
                    skill_font = self.debug_font_small_2.render(skill_text, True, WHITE)
                    skill_text_xy = font_position_center(skill, self.debug_font_small_2, skill_text)
                    GL.SCREEN.blit(skill_font, skill_text_xy)
                else:
                    icon = ICONS_TABLE[skill.id]
                    icon = image_scale(icon, (20, 20))
                    GL.SCREEN.blit(icon, (skill[0] + 2.5, skill[1] + 2.5))

        def _draw_particles():
            for p in self.active_particles:
                if isinstance(p, FieldParticle):
                    pygame.draw.circle(GL.SCREEN, p.color, (p.centerx, p.centery), p.radius, 1)
                else:
                    # If icon art exists
                    if p.sid in PARTICLES_TABLE.keys():
                        pimg = PARTICLES_TABLE[p.sid]
                        if p.direction == LEFT:
                            pimg = pygame.transform.flip(pimg, True, False)

                        # melee rotate
                        if isinstance(p, MeleeParticle):
                            if p.direction == LEFT:
                                pimg = pygame.transform.rotate(pimg, math.degrees(-1 * p.progress))
                            else:
                                pimg = pygame.transform.rotate(pimg, math.degrees(p.progress))

                        # Special rotate cases
                        elif p.sid in [5, 112, 123, 125, 'beehive']:
                            if 'rotator' not in p.__dict__.keys():
                                p.rotator = 0
                            else:
                                p.rotator += 10
                            pimg = pygame.transform.rotate(pimg, p.rotator)
                        GL.SCREEN.blit(pimg, (p.left, p.top))

                    # no particle
                    else:
                        pygame.draw.rect(GL.SCREEN, p.color, p)

        def _draw_scrolling_text():
            for unit in self.active_monsters + [self.player1, self.player2]:
                for i, t in enumerate(unit.st_buffer):
                    if t[2] < 0:
                        unit.st_buffer.append((t[0], t[1], t[2] + self.game_time.msec + 2000))
                        unit.st_buffer.remove(t)
                        continue
                    # Damage scrolling text
                    if t[0] == ST_DMG:
                        text = '-' + str(int(t[1]))
                        color = RED

                        GL.SCREEN.blit(self.st_dmg_font.render(text, True, color),
                                       (unit.centerx + 30, unit.top - (3000 - t[2] + self.game_time.msec) / 50))
                    # Health Gain text
                    elif t[0] == ST_HP:
                        text = '+' + str(int(t[1]))
                        color = GREEN
                        GL.SCREEN.blit(self.st_dmg_font.render(text, True, color),
                                       (unit.centerx + 30, unit.top - (3000 - t[2] + self.game_time.msec) / 50))
                    # Level up scrolling text
                    elif t[0] == ST_LEVEL_UP:
                        text = t[1]
                        color = YELLOW
                        GL.SCREEN.blit(self.st_level_up_font.render(text, True, color),
                                       (unit.centerx - 50, unit.top - (4000 - t[2] + self.game_time.msec) / 50))
                    # Energy gain text
                    elif t[0] == ST_ENERGY:
                        text = str(t[1])
                        color = PURPLE
                        GL.SCREEN.blit(self.st_energy_font.render(text, True, color),
                                       (unit.centerx, unit.top - (3000 - t[2] + self.game_time.msec) / 50))
                    if t[2] <= self.game_time.msec:
                        unit.st_buffer.remove(t)

                # Condition scrolling text
                # Process list
                print_list = []
                for k, vl in unit.conditions.items():
                    # ignore dot
                    if k != DOT:
                        if unit.conditions[k] and k == SHIELD:
                            sum_mag = 0
                            max_duration = 0
                            for sh in unit.conditions[k]:
                                sum_mag += sh.magnitude
                                max_duration = max(max_duration, int((sh.duration - self.game_time.msec + sh.start) / 1000))
                            print_list.append((GREEN, SHIELD + '(' + str(sum_mag) + '):' + str(max_duration)))
                        elif unit.conditions[k]:
                            max_duration = 0
                            for c in unit.conditions[k]:
                                max_duration = max(max_duration, int((c.duration - self.game_time.msec + c.start) / 1000))
                            color = GREEN if k in BUFFS else RED
                            print_list.append((color, k + ':' + str(max_duration)))
                # Print list
                for i, v in enumerate(print_list):
                    GL.SCREEN.blit(self.st_condition_font.render(v[1], True, v[0]), (unit.centerx - 70, unit.top - 20 - (15 * i)))

        def _draw_rain():
            if self.make_rain:
                for i in range(5, self.arena.play_area_rect.width, 10):
                    rain_copy = self.rain.copy()
                    rain_copy.left = i + self.arena.play_area_rect.left
                    self.rain_particles.append(rain_copy)

            for r in self.rain_particles:
                r.move_ip((0, 5))
                pygame.draw.rect(GL.SCREEN, BLUE, r)

            for r in self.rain_particles[:]:
                if r.top > self.arena.play_area_rect.height:
                    self.rain_particles.remove(r)
            self.make_rain = False

        _draw_ui1()
        _draw_ui2()
        _draw_ui_skill_boxes()
        _draw_ui_controls()
        _draw_ui_controls2()
        _draw_timer()
        _draw_arena()
        _draw_dropped_skills()
        _draw_monsters()
        if not GL.INPUT1.DEBUG_MODE_ON:
            _draw_players()
        _draw_particles()
        _draw_scrolling_text()
        # _draw_rain()

    # -------------------------------------------------------------------------
    def draw_debug(self):

        def _draw_spawn_point_rects():
            for rect in self.arena.spawn_points:
                pygame.draw.rect(GL.SCREEN, RED, rect)

        def _draw_play_area_debug_border():
            old_play_area = Rect2(65, 0, 1150, 475)
            pygame.draw.rect(GL.SCREEN, YELLOW, old_play_area, 1)
            pygame.draw.rect(GL.SCREEN, GREEN, self.arena.play_area_rect, 1)
            pygame.draw.rect(GL.SCREEN, RED, self.arena.left_wall, 1)
            pygame.draw.rect(GL.SCREEN, RED, self.arena.floor, 1)
            pygame.draw.rect(GL.SCREEN, RED, self.arena.right_wall, 1)

        def _draw_debug_text():
            x = '| x:{:>8.2f}|'.format(self.player1.x)
            y = '| y:{:>8.2f}|'.format(self.player1.y)
            dx = '|dx:{:>8.2f}|'.format(self.player1.dx)
            dy = '|dy:{:>8.2f}|'.format(self.player1.dy)
            debug_font_1 = self.debug_font.render(x, True, GREEN)
            debug_font_2 = self.debug_font.render(y, True, GREEN)
            debug_font_3 = self.debug_font.render(dx, True, GREEN)
            debug_font_4 = self.debug_font.render(dy, True, GREEN)
            GL.SCREEN.blit(debug_font_1, self.debug_font_xy1)
            GL.SCREEN.blit(debug_font_2, self.debug_font_xy2)
            GL.SCREEN.blit(debug_font_3, self.debug_font_xy3)
            GL.SCREEN.blit(debug_font_4, self.debug_font_xy4)

            num_monsters = '|curr num monsters:{:>4}|'.format(len(self.active_monsters))
            max_monsters = '| max num monsters:{:>4}|'.format(self.arena.max_monsters)
            debug_font_m1 = self.debug_font.render(num_monsters, True, GREEN)
            debug_font_m2 = self.debug_font.render(max_monsters, True, GREEN)
            GL.SCREEN.blit(debug_font_m1, self.debug_font_xy5)
            GL.SCREEN.blit(debug_font_m2, self.debug_font_xy6)

        def _draw_cpu_usage():
            cpu_text = '|CPU Utilization:{:>5.1f}%|'.format(self.cpu_avg) if psutil_found else '|CPU Utilization:  ????|'
            cpu_font = self.debug_font.render(cpu_text, True, RED)
            GL.SCREEN.blit(cpu_font, self.debug_font_xy7)

        def _draw_destructible_terrain_debug_text():
            for rect in self.arena.destructible_terrain:
                rendered_debug_font = self.debug_font_small_2.render(str(rect.hits_to_destroy), True, BLACK)
                pos = font_position_center(rect, self.debug_font_small_2, str(rect.hits_to_destroy))
                GL.SCREEN.blit(rendered_debug_font, pos)

        def _draw_mouse_text():
            mouse_pos = pygame.mouse.get_pos()
            play_area_mouse_pos = mouse_pos[0] - self.arena.play_area_rect.left, mouse_pos[1]
            pygame.draw.circle(GL.SCREEN, WHITE, mouse_pos, 2, 0)
            pygame.draw.circle(GL.SCREEN, BLACK, mouse_pos, 2, 1)
            if 0 <= play_area_mouse_pos[0] <= self.arena.play_area_rect.width and 0 <= play_area_mouse_pos[1] <= self.arena.play_area_rect.height:
                offset_pos_mouse_font = self.debug_font_small.render(str(play_area_mouse_pos), True, DGREY)
                GL.SCREEN.blit(offset_pos_mouse_font, mouse_pos)
            real_pos_mouse_font = self.debug_font_small.render(str(mouse_pos), True, DKYELLOW)
            GL.SCREEN.blit(real_pos_mouse_font, (mouse_pos[0] + 3, mouse_pos[1] + 10))

        def _draw_players_debug(draw_p1=True, draw_p2=True):

            def _draw_player_debug(p, c1, c2):
                pygame.draw.rect(GL.SCREEN, c1, p)
                eye = Rect2(topleft=p.topleft, size=(5, 5))
                if p.facing_direction == LEFT:
                    eye.topleft = p.topleft
                    eye.move_ip((+3, 3))
                else:
                    eye.topright = p.topright
                    eye.move_ip((-3, 3))
                pygame.draw.rect(GL.SCREEN, c2, eye)

            if draw_p1:
                _draw_player_debug(self.player1, DKRED, LBLUE)
            if draw_p2:
                _draw_player_debug(self.player2, LBLUE, DKRED)

        def _draw_player_collision_points_for_debugging():
            coll_data = get_collision_data(self.player1, self.arena)
            locs = []
            for terr, pt, side in coll_data:
                if pt.L: locs.append(self.player1.midleft)
                if pt.R: locs.append(self.player1.midright)
                if pt.T: locs.append(self.player1.midtop)
                if pt.B: locs.append(self.player1.midbottom)
                if pt.TL: locs.append(self.player1.topleft)
                if pt.TR: locs.append(self.player1.topright)
                if pt.BR: locs.append(self.player1.bottomright)
                if pt.BL: locs.append(self.player1.bottomleft)
                if locs:  # True if not empty list
                    pygame.draw.circle(GL.SCREEN, ORANGE, self.player1.center, 5, 0)
                for l in locs:
                    pygame.draw.circle(GL.SCREEN, ORANGE, l, 3, 0)

        if GL.INPUT1.DEBUG_MODE_ON:
            _draw_spawn_point_rects()
            _draw_play_area_debug_border()
            _draw_debug_text()
            _draw_cpu_usage()
            _draw_destructible_terrain_debug_text()
            _draw_players_debug()
            _draw_player_collision_points_for_debugging()
            _draw_mouse_text()
        elif not GL.INPUT1.DEBUG_MODE_ON:
            if self.player1.sprite is None:
                _draw_players_debug(draw_p1=True, draw_p2=False)
            if self.player2.sprite is None:
                _draw_players_debug(draw_p1=False, draw_p2=True)

    # -------------------------------------------------------------------------
    def handle_event_queue(self):

        def _handle_song_end_event():
            for event in pygame.event.get(SONG_END_EVENT):
                if event.type == SONG_END_EVENT:
                    print('the song ended!')
                    AUDIO.play_next_random_song()

        def _handle_return_to_main_menu_from_click():
            for event in pygame.event.get():
                if 'click' in self.return_button.handleEvent(event):
                    self.return_now = True
                    GL.NEXT_PAGE = '_start'

        def _handle_time_tick_event():
            for event in pygame.event.get(TIME_TICK_EVENT):
                if event.type == TIME_TICK_EVENT:
                    self.game_time.inc()

                    # for CPU usage debug text
                    if psutil_found and GL.INPUT1.DEBUG_MODE_ON:
                        new_cpu = psutil.cpu_percent(interval=None)
                        self.cpu_deque.append(new_cpu)
                        self.cpu_avg = sum(self.cpu_deque) / len(self.cpu_deque)

                    # Player 1 conditions
                    for k, v in self.player1.conditions.items():
                        for e in v:
                            if e.is_expired(self.game_time.msec):
                                self.player1.conditions[k].remove(e)

                    # Player 2 conditions
                    for k, v in self.player2.conditions.items():
                        for e in v:
                            if e.is_expired(self.game_time.msec):
                                self.player2.conditions[k].remove(e)

                    # Monster conditions
                    for m in self.active_monsters:
                        for k, v in m.conditions.items():
                            for e in v:
                                if e.is_expired(self.game_time.msec):
                                    m.conditions[k].remove(e)

        def _handle_regeneration_event():
            for event in pygame.event.get(REGENERATION_EVENT):
                if event.type == REGENERATION_EVENT:
                    # Player 1
                    if self.player1.hit_points > 0:
                        if self.player1.conditions[WOUNDED] and not self.player1.conditions[INVIGORATED]:
                            self.player1.hit_points += self.player1.level / 20
                        elif not self.player1.conditions[WOUNDED] and self.player1.conditions[INVIGORATED]:
                            self.player1.hit_points += self.player1.level / 5
                        else:
                            self.player1.hit_points += self.player1.level / 10
                        if self.player1.hit_points > 100:
                            self.player1.hit_points = 100

                        if self.player1.conditions[WEAKENED] and not self.player1.conditions[EMPOWERED]:
                            self.player1.energy += self.player1.level / 10
                        elif not self.player1.conditions[WEAKENED] and self.player1.conditions[EMPOWERED]:
                            self.player1.energy += self.player1.level / 2.5
                        else:
                            self.player1.energy += self.player1.level / 5
                        if self.player1.energy > 10:
                            self.player1.energy = 10
                    # Player 2
                    if self.player2.hit_points > 0:
                        if self.player2.conditions[WOUNDED] and not self.player2.conditions[INVIGORATED]:
                            self.player2.hit_points += self.player2.level / 20
                        elif not self.player2.conditions[WOUNDED] and self.player2.conditions[INVIGORATED]:
                            self.player2.hit_points += self.player2.level / 5
                        else:
                            self.player2.hit_points += self.player2.level / 10
                        if self.player2.hit_points > 100:
                            self.player2.hit_points = 100

                        if self.player2.conditions[WEAKENED] and not self.player2.conditions[EMPOWERED]:
                            self.player2.energy += self.player2.level / 10
                        elif not self.player2.conditions[WEAKENED] and self.player2.conditions[EMPOWERED]:
                            self.player2.energy += self.player2.level / 2.5
                        else:
                            self.player2.energy += self.player2.level / 5
                        if self.player2.energy > 10:
                            self.player2.energy = 10

        def _handle_player_lock_events():
            # player 1 skill lock timer
            for event in pygame.event.get(PLAYER1_LOCK_EVENT):
                if event.type == PLAYER1_LOCK_EVENT:
                    self.player1.attack_cooldown_expired = True
                    pygame.time.set_timer(PLAYER1_LOCK_EVENT, 0)
            # player 2 skill lock timer
            for event in pygame.event.get(PLAYER2_LOCK_EVENT):
                if event.type == PLAYER2_LOCK_EVENT:
                    self.player2.attack_cooldown_expired = True
                    pygame.time.set_timer(PLAYER2_LOCK_EVENT, 0)

        def _handle_player_pickup_skill_events():
            for event in pygame.event.get(PLAYER1_PICKUP_EVENT):
                if event.type == PLAYER1_PICKUP_EVENT:
                    self.player1.pickup_time += 1
                    pygame.time.set_timer(PLAYER1_PICKUP_EVENT, 0)

            for event in pygame.event.get(PLAYER2_PICKUP_EVENT):
                if event.type == PLAYER2_PICKUP_EVENT:
                    self.player2.pickup_time += 1
                    pygame.time.set_timer(PLAYER2_PICKUP_EVENT, 0)

        def _handle_rain_event():
            for event in pygame.event.get(MORE_RAIN_EVENT):
                if event.type == MORE_RAIN_EVENT:
                    self.make_rain = True
                    pygame.time.set_timer(MORE_RAIN_EVENT, 150)

        def _handle_monster_spawn_event():
            for event in pygame.event.get(MONSTER_SPAWN_EVENT):
                if event.type == MONSTER_SPAWN_EVENT:
                    self.spawn_monsters = True
                    pygame.time.set_timer(MONSTER_SPAWN_EVENT, 10000)

        def _handle_quit_event():
            for event in pygame.event.get(QUIT):
                if event.type == QUIT:
                    EXIT_GAME()

        _handle_song_end_event()
        _handle_time_tick_event()
        _handle_regeneration_event()
        _handle_player_lock_events()
        _handle_player_pickup_skill_events()
        _handle_rain_event()
        _handle_monster_spawn_event()
        _handle_quit_event()
        _handle_return_to_main_menu_from_click()

    # ----------------------------------------------------------------------------
    def check_if_game_over(self):
        if self.player1.is_dead() or self.player2.is_dead():
            self.return_now = True
            GL.NEXT_PAGE = '_game_over'
def simulate_flooding(manifest_filename, display_class = None,
                         init_state = None, height = None):
    '''
    Runs a simulation, and displays it in a GUI window OR saves all frames
    as PNG images.

    Normal operation is to read all options from a manifest file, given by
    manifest_filename. If you want to use a custom surface geometry (anything
    other than a square or hex grid), you'll need to supply your own initial
    state (whatever your display object will use, but must be an iterable and
    contain surface_crns.base.Node objects) and a class that can display your
    state (should subclass surface_crns.views.grid_display.SurfaceDisplay),
    which you should pass as "init_state" and "DisplayClass", respectively.
    '''

    ################################
    # READ MANIFEST AND INITIALIZE #
    ################################
    # Parse the manifest
    print("Reading information from manifest file " + manifest_filename + "...")
    manifest_options = \
                readers.manifest_readers.read_manifest(manifest_filename)

    opts = SynchronousCellularAutomataOptionParser(manifest_options)

    print(" Done.")

    if opts.capture_directory != None:
        from signal import signal, SIGPIPE, SIG_DFL
        import subprocess as sp
        base_dir = opts.capture_directory
        MOVIE_DIRECTORY = base_dir
        DEBUG_DIRECTORY = os.path.join(base_dir, DEBUG_SUBDIRECTORY)
        FRAME_DIRECTORY = os.path.join(base_dir, FRAME_SUBDIRECTORY)
        for d in [base_dir, MOVIE_DIRECTORY, DEBUG_DIRECTORY, FRAME_DIRECTORY]:
            if not os.path.isdir(d):
                os.mkdir(d)
        os.environ["SDL_VIDEODRIVER"] = "dummy"
        print("SDL_VIDEODRIVER set to 'dummy'")
    else:
        FRAME_DIRECTORY = ""

    # Initialize simulation
    if init_state:
        grid = init_state
    else:
        if opts.grid is None:
            raise Exception("Initial grid state required.")
        grid = opts.grid



    if height:
        #print("there is already a height")
        height_grid = height
    else:
        #print("there is NOT already a height, need to go to options")
        if opts.height is None:
            raise Exception("Initial grid state required.")
        #print("went to options")
        height_grid = opts.height
        #print(height_grid)
        #print("done print AFTER going to options")


    simulation = SynchronousSimulator(surface= grid,
                            heightgrid = height_grid,
                            seed= opts.rng_seed,
                            simulation_duration= opts.max_duration)

    simulation.init_wall_time = process_time()
    seed = simulation.seed
    time = simulation.time
    event_history = EventHistory()

    if not opts.capture_directory is None:
        simulation.pixels_saved = 0
        simulation.frame_number = 0
        simulation.capture_time = 0

    ################
    # PYGAME SETUP #
    ################
    print("Beginning Pygame setup...")
    if opts.grid_type == 'parallel_emulated':
        from views.grid_display \
                import ParallelEmulatedSquareGridDisplay
        grid_display = ParallelEmulatedSquareGridDisplay(grid = grid,
                            colormap = opts.COLORMAP,
                            emulation_colormap = opts.emulation_colormap,
                            horizontal_buffer = opts.horizontal_buffer,
                            vertical_buffer = opts.vertical_buffer,
                            cell_height = opts.cell_height,
                            cell_width = opts.cell_width,
                            representative_cell_x = opts.representative_cell_x,
                            representative_cell_y = opts.representative_cell_y,
                            min_x = MIN_GRID_WIDTH,
                            min_y = 0,
                            pixels_per_node = opts.pixels_per_node,
                            display_text = opts.display_text)
    elif opts.grid_type == 'standard':
        if display_class:
            DisplayClass = display_class
        elif opts.grid_type == "standard" and opts.surface_geometry == "square":
            DisplayClass = SquareGridDisplay
        elif opts.grid_type == "standard" and opts.surface_geometry == "hex":
            DisplayClass = HexGridDisplay
        grid_display = DisplayClass(grid = grid,
                                    colormap = opts.COLORMAP,
                                    min_x = MIN_GRID_WIDTH,
                                    min_y = 0,
                                    pixels_per_node = opts.pixels_per_node,
                                    display_text = opts.display_text)
    else:
        raise Exception("Unrecognized grid type '" + opts.grid_type + "'")

    legend_display = LegendDisplay(colormap = opts.COLORMAP)

    # Width only requires legend and grid sizes to calculate
    display_width  = grid_display.display_width + legend_display.display_width

    # Width used to calculate time label and button placements
    time_display  = TimeDisplay(display_width)

    button_y      = time_display.display_height + grid_display.display_height+1
            # max(legend_display.display_height, grid_display.display_height) + 1
    #(int(display_width/2) - (button_width + button_buffer), button_y,
    play_back_button  = PygButton(rect =
         (legend_display.display_width + button_buffer, button_y,
         button_width, button_height),
                             caption = '<<')
    step_back_button  = PygButton(rect =
         (play_back_button.rect.right + button_buffer, button_y,
         button_width, button_height),
                             caption = '< (1)')
    pause_button  = PygButton(rect =
         (step_back_button.rect.right + button_buffer, button_y,
         button_width, button_height),
                              caption = 'Pause')
    step_button  = PygButton(rect =
        (pause_button.rect.right + button_buffer, button_y,
         button_width, button_height),
                             caption = '(1) >')
    play_button  = PygButton(rect =
        (step_button.rect.right + button_buffer, button_y,
         button_width, button_height),
                             caption = '>>')
    clip_button = PygButton(rect =
        (play_button.rect.right + 4*button_buffer, button_y,
         button_width * 1.1, button_height),
                            caption = 'Uncache')

    display_height = max(legend_display.display_height + \
                2*legend_display.VERTICAL_BUFFER + time_display.display_height,
                         button_y + button_height + 2*button_buffer)

    if opts.debug:
        print("Initializing display of size " + str(display_width) + ", " +
                str(display_height) + ".")
    display_surface = pygame.display.set_mode((display_width,
                                                   display_height), 0, 32)
    simulation.display_surface = display_surface
    # except:
    #     display_surface = pygame.display.set_mode((display_width,
    #                                                display_height))
    if opts.debug:
        print("Display initialized. Setting caption.")
    pygame.display.set_caption(f'Surface CRN Simulator (Seed: {seed})')
    if opts.debug:
        print("Caption set, initializing clock.")
    fpsClock = pygame.time.Clock()

    if opts.debug:
        print("Clock initialized. Filling display with white.")
    # Initial render
    display_surface.fill(WHITE)
    print("Pygame setup done, first render attempted.")

    # Make the options menu.
    #opts_menu = MainOptionMenu()
    #opts_menu.update()

    time_display.render(display_surface, x_pos = 0,
                                         y_pos = 0)
    legend_display.render(display_surface, x_pos = 0,
                                          y_pos = time_display.y_pos +
                                                  time_display.display_height)
    grid_display.render(display_surface, x_pos = legend_display.display_width,
                                         y_pos = time_display.y_pos +
                                                 time_display.display_height)

    if opts.saving_movie:
        simulation.display_surface_size = display_height * display_width
    else:
        play_back_button.draw(display_surface)
        step_back_button.draw(display_surface)
        pause_button.draw(display_surface)
        step_button.draw(display_surface)
        play_button.draw(display_surface)
        clip_button.draw(display_surface)

    pygame.display.flip()
    update_display(opts, simulation, FRAME_DIRECTORY)

    if "SDL_VIDEODRIVER" in os.environ:
        real_time = os.environ["SDL_VIDEODRIVER"] != "dummy"
    else:
        real_time = True

    # State variables for simulation
    next_reaction_time = 0
    prev_reaction_time = 0
    next_reaction = None
    prev_reaction = None
    running = True #TEMPORARY FIX ME!!!
    first_frame = True
    last_frame  = False
    running_backward = False

    # Resource limit flags
    terminate = False
    termination_string = ""

    print("Beginning simulation....")
    # Iterate through events
    while True:
        # Check for interface events
        for event in pygame.event.get():
            if 'click' in play_back_button.handleEvent(event):
                running = True
                running_backward = True
                last_frame = False
            if 'click' in step_back_button.handleEvent(event):
                running = False
                last_frame = False
                if event_history.at_beginning():
                    time = 0
                    time_display.time = 0
                    time_display.render(display_surface, x_pos = 0, y_pos = 0)
                    pygame.display.update()
                else:
                    prev_reaction = event_history.previous_event()
                    event_history.increment_event(-1)
                    if not event_history:
                        continue
                    prev_reaction_rule = prev_reaction.rule
                    for i in range(len(prev_reaction.participants)):
                        cell = prev_reaction.participants[i]
                        state = prev_reaction_rule.inputs[i]
                        cell.state = state
                    display_next_event(prev_reaction, grid_display)
                    if event_history.at_beginning():
                        time = 0
                    else:
                        # Note that this is NOT the same as the time from the
                        # "previous event" that we're currently processing --
                        # it's actually the time of the event *before* the one
                        # we just undid.
                        time = event_history.previous_event().time
                    time_display.time = time
                    time_display.render(display_surface, x_pos = 0, y_pos = 0)
                    pygame.display.update()
            if 'click' in pause_button.handleEvent(event):
                running = False
            if 'click' in step_button.handleEvent(event):
                running = False
                first_frame = False
                # Process a single reaction
                reading_history = False
                if not event_history.at_end():
                    reading_history = True
                    next_reaction = event_history.next_event()
                    event_history.increment_event(1)
                    next_reaction_rule = next_reaction.rule
                    for i in range(len(next_reaction.participants)):
                        cell = next_reaction.participants[i]
                        state = next_reaction_rule.outputs[i]
                        cell.state = state
                if not next_reaction:
                    next_reaction = simulation.process_next_reaction()
                    if not reading_history:
                        event_history.add_event(next_reaction)
                        event_history.increment_event(1)
                if next_reaction:
                    next_reaction_time = next_reaction.time
                    display_next_event(next_reaction, grid_display)
                    time = next_reaction_time
                    time_display.time = time
                    time_display.render(display_surface, x_pos = 0, y_pos = 0)
                    pygame.display.update()
                    next_reaction = None
                    if opts.debug:
                        print("State after update: " + str(grid))
            if 'click' in play_button.handleEvent(event):
                running = True
                running_backward = False
                first_frame = False
            if 'click' in clip_button.handleEvent(event):
                event_history.clip()
                simulation.time = time
                simulation.reset()
                for rxn in list(simulation.event_queue.queue):
                    print(rxn)
            if event.type == pygl.QUIT:
                if opts.saving_movie:
                    movie_file.close()
                current_state = FINISHED_CLEAN
                cleanup_and_exit(simulation, current_state)
        # Don't do anything if paused.
        if not running:
            pygame.display.update()
            continue

        # Update time
        if opts.debug:
            print(f"Updating time: time = {time}, running_backward = "
                  f"{running_backward}, first_frame = {first_frame}, "
                  f"last_frame = {last_frame}")
        if running_backward and not first_frame:
            #prev_reaction_time = time
            time -= opts.speedup_factor * 1./opts.fps
            last_frame = False
        elif not running_backward and not last_frame:
            #next_reaction_time = time
            time += opts.speedup_factor * 1./opts.fps
            first_frame = False
        if opts.debug:
            print(f"Updating time to {time}")
        time_display.time = time
        time_display.render(display_surface, x_pos = 0,
                            y_pos = 0)

        # Process any simulation events that have happened since the last tick.
        if opts.debug:
            print("Checking for new events...")
        if running_backward and not first_frame:
            if opts.debug and not event_history.at_beginning():
                print(f"While running backwards, checking if there are any "
                      f"events: time = {time}, previous event time = "
                      f"{event_history.previous_event().time}")
            while not event_history.at_beginning() and \
             event_history.previous_event().time > time:
                prev_reaction = event_history.previous_event()
                if event_history.at_beginning():
                    first_frame = True
                event_history.increment_event(-1)
                # if opts.debug:
                #     print("While running backwards, undoing reaction: "
                #           f"{prev_reaction}")
                for i in range(len(prev_reaction.participants)):
                    cell  = prev_reaction.participants[i]
                    state = prev_reaction.rule.inputs[i]
                    cell.state = state
                next_reaction_time = prev_reaction_time
                prev_reaction_time = prev_reaction.time if prev_reaction \
                                                        else 0
                if opts.debug:
                    print("Displaying a new event")
                display_next_event(prev_reaction, grid_display)
                if opts.debug and not event_history.at_beginning():
                    print(f"While running backwards, checking if there are any "
                      f"events: time = {time}, previous event time = "
                      f"{event_history.previous_event().time}")
        elif not running_backward and not last_frame:
            while (not event_history.at_end() or not simulation.done()) \
               and next_reaction_time < time:
                if event_history.at_end():
                    next_reaction = simulation.process_next_reaction()
                    if next_reaction:
                        event_history.add_event(next_reaction)
                        event_history.increment_event(1)
                else:
                    next_reaction = event_history.next_event()
                    event_history.increment_event(1)
                    for i in range(len(next_reaction.participants)):
                        cell = next_reaction.participants[i]
                        state = next_reaction.rule.outputs[i]
                        cell.state = state

                prev_reaction_time = next_reaction_time
                next_reaction_time = next_reaction.time if next_reaction \
                                                    else opts.max_duration + 1
                if opts.debug:
                    print("Displaying a new event")
                display_next_event(next_reaction, grid_display)

        # Render updates and make the next clock tick.
        if opts.debug:
            print("Updating display.")
        update_display(opts, simulation, FRAME_DIRECTORY)
        if real_time:
            fpsClock.tick(opts.fps)

        # Movie-capturing termination conditions
        if not opts.capture_directory is None:
            if simulation.pixels_saved > CUTOFF_SIZE:
                termination_string = "Simulation terminated after " + \
                                     str(simulation.pixels_saved) + \
                                     " pixels saved (~" + \
                                     str(CUTOFF_SIZE/10000000) +" Mb)."
                current_state = MAX_PIXELS
                terminate = True

            # Check the timer. If it's been more than an hour, terminate.
            if process_time() - simulation.init_wall_time > CUTOFF_TIME:
                termination_string = "Simulation cut off at max " \
                                     "processing time"
                current_state = MAX_TIME
                terminate = True

            if simulation.done() or time > opts.max_duration:
                termination_string = "Simulation finished."
                current_state = FINISHED_CLEAN
                terminate = True

            if terminate:
                display_surface = simulation.display_surface
                width = display_surface.get_size()[0]
                text_display = TextDisplay(width)
                text_display.text = termination_string
                text_display.render(display_surface, x_pos = 0, y_pos = 0)
                frame_filename = os.path.join(FRAME_DIRECTORY,
                            f"{opts.movie_title}_{simulation.frame_number}.png")
                if opts.debug:
                    print("Saving final frame at: " + frame_filename)
                pygame.image.save(display_surface, frame_filename)

                # Use ffmpeg to convert images to movie.
                if os.name == 'nt':
                    ffmpeg_name = 'ffmpeg.exe'
                elif os.name == 'posix':
                    # ffmpeg_name = 'ffmpeg'
                    name_found = False
                    for possible_name in ['/usr/local/bin/ffmpeg',
                                          '/usr/bin/ffmpeg']:
                        if os.path.isfile(possible_name):
                            ffmpeg_name = possible_name  # EW: not finding it?
                            name_found = True
                            break
                    if not name_found:
                        raise Exception("Could not find executable ffmpeg in"
                                        " any of the expected locations!")
                else:
                    raise Exception("Unexpected OS name '" + os.name + "'")

                signal(SIGPIPE, SIG_DFL)
                # width = display_surface.get_width()
                # height = display_surface.get_height()
                movie_filename = os.path.join(".", MOVIE_DIRECTORY,
                                              opts.movie_title + ".mp4")
                if opts.debug:
                    print("Writing movie to  file " + movie_filename +
                                 "\n")
                command = [ffmpeg_name,
                           '-y', # Overwrite output file
                           #'-framerate', str(opts.fps),
                           '-start_number', '1', # EW: might it default to
                                                 # starting with 0?
                           '-i', os.path.join(FRAME_DIRECTORY,
                                              opts.movie_title + "_%d.png"),
                           # Try to use better-than-default decoder
                           '-vcodec', 'h264',
                           # Need this for Quicktime to be able to read it
                           '-pix_fmt', 'yuv420p',
                           # Set a higher-than-default bitrate
                           '-crf', '18',
                           '-an', #no audio
                           # Width and height need to be divisible by 2.
                           # Round up if necessary.
                           '-vf', 'pad=ceil(iw/2)*2:ceil(ih/2)*2',
                           movie_filename
                           ]
                print("Calling ffmpeg with: " + str(command))
                print("And right now the current dir is " + os.getcwd())
                print("opts.capture_directory = " + opts.capture_directory)

                if opts.debug:
                    print("Writing movie with command:\n")
                    print("\t" + str(command) + "\n")
                debug_output_stream = open(os.path.join(opts.capture_directory,
                                                        "debug",
                                                        "ffmpeg_debug.dbg"),'w')
                proc = sp.Popen(command,
                                stdout = debug_output_stream,
                                stderr = sp.STDOUT)
                proc.communicate()
                if opts.debug:
                    print("Finished ffmpeg call.")

                cleanup_and_exit(simulation, current_state)

        # Live termination conditions
        if opts.debug:
            print("Checking for simulation completion...")
        if (event_history.at_end() and running and not running_backward and \
           (simulation.done() or time > opts.max_duration)) \
           or terminate:
            if opts.debug:
                print("Done! Cleaning up now.")
            last_frame = True
            running = False
            # Set the time to final time when done.
            time = opts.max_duration
            time_display.time = time
            time_display.render(display_surface, x_pos = 0,
                                y_pos = 0)#opts_menu.display_height)
            if next_reaction:
                display_next_event(next_reaction, grid_display)
            update_display(opts, simulation, FRAME_DIRECTORY)
            if opts.debug:
                print("Simulation state at final time " + \
                      str(opts.max_duration) + ":")
                print(str(grid))

        if event_history.at_beginning() or time == 0:
            first_frame = True