class GameManager(object): """The game manager class handles the entire game system.""" global RESOURCE_DIR def __init__(self): """Initialize the render window and start the game running""" # Constants FULLSCREEN = True self.FONT = sf.Font.load_from_file(RESOURCE_DIR + "raleway_thin.ttf") self.num_asteroids = 3 self.view_size = sf.Vector2f(720, 450) # Initialize the window if FULLSCREEN: self.window = sf.RenderWindow(sf.VideoMode(1440, 900), "Multitroids", sf.Style.FULLSCREEN, sf.ContextSettings(antialiasing = 32)) # self.image = sf.Image(1440, 900) else: self.window = sf.RenderWindow(sf.VideoMode(720, 450), "Multitroids", sf.Style.DEFAULT, sf.ContextSettings(antialiasing = 32)) # self.image = sf.Image(720, 450) self.window.framerate_limit = 60 self.window.view = sf.View.from_center_and_size(sf.Vector2f(), self.view_size) self.window.show_mouse_cursor = False self.full_restart() self.state = 'start' # self.RECORDING = Toggle(source = self.keyboard[sf.Key.T], # initVal = False) # self.frameNo = 0 ################################################################################ def start_again(self): """Restart the game, preserving state. Save a new virtual keyboard, and add the most recent set of commands to the army of ghost ships. """ # Save a new virtual keyboard. self.keyboard.history += [(self.keyboard.time, sf.Key.SPACE, 'u')] self.virtual_keyboards += [VirtualKeyboard(self.keyboard)] # Switch over the last bot to running on the virtual keyboard. self.players[-1].reset(self.virtual_keyboards[-1], self.view_size / 2.0) self.players += [Ship(self.keyboard, self.view_size / 2.0)] # Reset all of the keyboards, real and virtual. for vkb in self.virtual_keyboards: vkb.reset() self.keyboard.reset() # Reset player locations for player in self.players: player.reset(None, self.view_size / 2.0) self.players[-1].is_clone = False self.bullets = [] random.setstate(self.rand_state) self.asteroids = [Asteroid(self, clear_zone = self.view_size / 2.0, seed = random.random()) for i in range(self.num_asteroids)] ################################################################################ def full_restart(self): """Fully restart the game, clearing all history, etc.""" # Initialize real and virtual keyboards. self.keyboard = Keyboard() self.virtual_keyboards = [] # Initialize game objects self.players = [Ship(self.keyboard, self.view_size / 2.0)] self.bullets = [] self.rand_state = random.getstate() self.asteroids = [Asteroid(self, clear_zone = self.view_size / 2.0, seed = random.random()) for i in range(self.num_asteroids)] self.DEBUG = DataToggle(source = self.keyboard[sf.Key.NUM0], initVal = False) # self.RECORDING = Toggle(source = self.keyboard[sf.Key.T], # initVal = False) self.won = False # Start the system running self.running = DataToggle( source = DataOr(self.keyboard[sf.Key.ESCAPE], DataAnd(self.keyboard[sf.Key.Q], DataOr(self.keyboard[sf.Key.L_SYSTEM], self.keyboard[sf.Key.R_SYSTEM]))), initVal = True) ################################################################################ def handle_input(self): """Processes the list of events, sending them to their various handlers. """ for event in self.window.iter_events(): # If the window is ever closed, stop running. if event.type == sf.Event.CLOSED: self.running = False return # Send key events to the keyboard, intercepting certain special # cases (that restart the game) elif event.type == sf.Event.KEY_PRESSED: if event.code == sf.Key.R or \ (event.code == sf.Key.SPACE and not self.players[-1].alive): self.start_again() continue self.keyboard.down(event.code) elif event.type == sf.Event.KEY_RELEASED: self.keyboard.up(event.code) self.keyboard.increment() for vkb in self.virtual_keyboards: vkb.increment() ################################################################################ def run(self): """Main game loop. Updates all entities then draws everything, repeating over and over again.""" # Begin loop while self.running: self.handle_input() self.update() if self.running: self.draw() # if self.RECORDING: # self.image.copy_screen(self.window) # self.image.save_to_file("frames/"+str(self.frameNo) + ".png") # self.frameNo += 1 self.shutdown() ################################################################################ def update(self): """Update the game's state.""" # Handle the menu screens if self.state == 'game': if len(self.asteroids) == 0: self.state = 'win' self.keyboard.up(sf.Key.SPACE) return else: if self.state == 'start' and self.keyboard[sf.Key.SPACE]: self.state = 'game' self.full_restart() elif self.state == 'win' and self.keyboard[sf.Key.SPACE]: self.state = 'start' self.keyboard.up(sf.Key.SPACE) return # Update the player, adding any bullets to the master list. for player in self.players: bullet = player.update(self) if bullet: self.bullets += [bullet] # Update bullets and keep only those that are alive. [bullet.update(self) for bullet in self.bullets] self.bullets = filter(lambda x: x.alive, self.bullets) # Figure out if any new asteroids spawn newAsteroids = [] for asteroid in self.asteroids: newAsteroids += asteroid.update(self) self.asteroids = filter(lambda x: x.alive, self.asteroids) self.asteroids += newAsteroids # When the player dies, stop recording keystrokes. if not self.players[-1].alive and self.keyboard.recording: self.keyboard.recording = False ################################################################################ def shutdown(self): """The program is about to end. Clean up and close the window.""" self.window.close() ################################################################################ def draw_FPS(self): """Write the number of frames per second in the upper left corner.""" ft = self.window.frame_time if (ft > 0): text = sf.Text("FPS: %(fps)03d" % {'fps': 1 / ft}) else: text = sf.Text("FPS:inf") text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(0, self.view_size.y - 20) self.window.draw(text) ################################################################################ def draw_HUD(self): """Draw text in the upper left corner showing how many ships have been used.""" text = sf.Text("Ships: %d" % len(self.players), self.FONT, 30) text.style = sf.Text.BOLD text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(15, 15) self.window.draw(text) ################################################################################ def draw(self): """Draw the entire game window.""" self.window.clear() self.window.view = sf.View.from_center_and_size(self.view_size / 2., self.view_size) if self.DEBUG: self.draw_FPS() # Draw the start screen. if self.state == 'start': text = sf.Text("Multitroids", self.FONT, 200) text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(self.view_size.x / 2.0 - text.rect.width / 2.0, self.view_size.y / 2.0 - text.rect.height - 30) self.window.draw(text) text = sf.Text("Arrow keys to fly\nSpacebar to shoot and restart"+ "\n\n Press spacebar to begin", self.FONT, 60) text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(self.view_size.x / 2.0 - text.rect.width / 2.0, self.view_size.y / 2.0 + text.rect.height / 2.0 - 30) self.window.draw(text) text = sf.Text("www.mattkeeter.com", self.FONT, 50) text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(15, self.view_size.y - text.rect.height * 1.5) self.window.draw(text) self.window.display() return # Draw the end screen. elif self.state == 'win': text = sf.Text("YOU WIN", self.FONT, 160) text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(self.view_size.x / 2.0 - text.rect.width / 2.0, self.view_size.y / 2.0 - text.rect.height) self.window.draw(text) text = sf.Text("Ships: %d" % len(self.players), self.FONT, 60) text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(self.view_size.x / 2.0 - text.rect.width / 2.0, self.view_size.y / 2.0 + text.rect.height / 2.0) self.window.draw(text) text = sf.Text("Press spacebar to restart", self.FONT, 40) text.scale = sf.Vector2f(0.5, 0.5) text.position = sf.Vector2f(self.view_size.x / 2.0 - text.rect.width / 2.0, self.view_size.y / 2.0 + text.rect.height * 2.5) self.window.draw(text) self.window.display() return # Draw the rest of the game. for player in self.players: player.draw(self.window) [bullet.draw(self.window) for bullet in self.bullets] # Draw each asteroid at 8 positions, so that they wrap around cleanly. for offset in [sf.Vector2f(0, 0), sf.Vector2f(self.view_size.x, 0), sf.Vector2f(-self.view_size.x, 0), sf.Vector2f(0, self.view_size.y), sf.Vector2f(0, -self.view_size.y)]: [asteroid.draw(self.window, offset) for asteroid in self.asteroids] self.draw_HUD() self.window.display()