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 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()
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 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'
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'
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'
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'
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()
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