class PirateInvasion: """Class to manage game assets and behavior""" def __init__(self): """Initialize the game and create game resources""" pygame.init() self.settings = Settings() self.screen = pygame.display.set_mode( (self.settings.screen_width, self.settings.screen_height)) self.background = self.settings.background_image pygame.display.set_caption("Pirate Invasion") #Create an instance to store game statistics #and create a score # board self.cannon = Cannon(self) self.stats = GameStats(self) self.sb = Scoreboard(self) self.cannonballs = pygame.sprite.Group() self.pirates = pygame.sprite.Group() self._create_fleet() #Make the Play button self.play_button = Button(self, "Start Fight!") #Set the background color self.bg_color = (62, 164, 236) #Set background song mixer.music.load( 'C:/Users/Khyr/Desktop/CIT228/Sideways_Shooter/music/music.wav') mixer.music.play(-1) mixer.music.set_volume(0.2) def run_game(self): """Start the main loop for game""" while True: self._check_events() if self.stats.game_active: self.cannon.update() self._update_cannonballs() self._update_pirates() self._update_screen() def _check_events(self): """Respond to keypresses & mouse events""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN or event.type == ord('s'): self._check_keydown_events(event) elif event.type == pygame.KEYUP or event.type == ord('w'): self._check_keyup_events(event) elif event.type == pygame.MOUSEBUTTONDOWN: mouse_pos = pygame.mouse.get_pos() self._check_play_button(mouse_pos) def _check_keydown_events(self, event): """Respond to keypresses""" #Set the cannonball sound cannonballSound = pygame.mixer.Sound( 'C:/Users/Khyr/Desktop/CIT228/Sideways_Shooter/music/sound.wav') mixer.music.set_volume(0.2) if event.key == pygame.K_UP or event.key == ord('w'): self.cannon.moving_up = True elif event.key == pygame.K_DOWN or event.key == ord('s'): self.cannon.moving_down = True elif event.key == pygame.K_q: sys.exit() elif event.key == pygame.K_SPACE: self._fire_cannonball() cannonballSound.play() def _check_keyup_events(self, event): if event.key == pygame.K_UP or event.key == ord('w'): self.cannon.moving_up = False elif event.key == pygame.K_DOWN or event.key == ord('s'): self.cannon.moving_down = False def _check_play_button(self, mouse_pos): """Start a new game when the player clicks Play""" button_clicked = self.play_button.rect.collidepoint(mouse_pos) if button_clicked and not self.stats.game_active: #Reset the game settings self.settings.intitalize_dynamic_settings() #Reset the game statistics self.stats.reset_stats() self.stats.game_active = True self.sb.prep_score() self.sb.prep_level() self.sb.prep_cannons() #Get rid of any remaining pirates and cannonballs self.pirates.empty() self.cannonballs.empty() #Create a new fleet and center the cannon self._create_fleet() self.cannon.center_cannon() #Hide the mouse cursor pygame.mouse.set_visible(False) def _fire_cannonball(self): """Create a new cannonball and add to the cannonball group""" if len(self.cannonballs) < self.settings.cannonballs_allowed: new_cannonball = Cannonball(self) self.cannonballs.add(new_cannonball) def _update_cannonballs(self): """Update the position of cannonballs and get rid of old cannonballs""" #Update cannonball positions self.cannonballs.update() #Get rid of cannonballs off screen for cannonball in self.cannonballs.copy(): if cannonball.rect.bottom <= 0: self.cannonballs.remove(cannonball) self._check_cannonball_pirate_collisions() def _check_cannonball_pirate_collisions(self): """Respond to cannonball-pirate collisions""" #Remove any cannonballs and pirates that have collided collisions = pygame.sprite.groupcollide(self.cannonballs, self.pirates, True, True) if collisions: for pirates in collisions.values(): self.stats.score += self.settings.pirate_points * len(pirates) self.sb.prep_score() self.sb.check_high_score() if not self.pirates: #Destroy existing cannonballs and create new fleet self.cannonballs.empty() self._create_fleet() self.settings.increase_speed() #Increase level self.stats.level += 1 self.sb.prep_level() def _update_pirates(self): """Check if the fleet is at an edge, then update the positions of all the pirates in fleet""" self._check_fleet_edges() self.pirates.update() #Look for pirate-cannon collisions if pygame.sprite.spritecollideany(self.cannon, self.pirates): self._cannon_hit() #Look for pirates hitting the bottom of the screen self._check_pirates_bottom() def _cannon_hit(self): """Respond to cannon being hit by a pirate""" if self.stats.cannons_left > 0: #Decrement cannons_left, and update scoreboard self.stats.cannons_left -= 1 self.sb.prep_cannons() #Get rid of any remaining pirates and cannonballs self.pirates.empty() self.cannonballs.empty() #Create a new fleet and center the cannon self._create_fleet() self.cannon.center_cannon() #Pause sleep(0.5) else: self.stats.game_active = False pygame.mouse.set_visible(True) def _check_pirates_bottom(self): """Check if any pirates have reached the bottom of the screen""" screen_rect = self.screen.get_rect() for pirate in self.pirates.sprites(): if pirate.rect.bottom >= screen_rect.bottom: #Treat this the same as if teh cannon got hit self._cannon_hit() break def _update_screen(self): """Updates images on the screen, flip to the new screen""" self.screen.blit(self.background, [0, 0]) self.cannon.blitme() for cannonball in self.cannonballs.sprites(): cannonball.draw_cannonball() self.pirates.draw(self.screen) #Draw the score information self.sb.show_score() #Draw the play button if the game is inactive if not self.stats.game_active: self.play_button.draw_button() #Make the most recent drawn screen visible pygame.display.flip() def _create_fleet(self): """Make a fleet of pirates""" #Create a pirate ship and find the number of pirates in a row #Spacing between each pirate is equal to one pirate width pirate = Pirate(self) pirate_width, pirate_height = pirate.rect.size available_space_x = self.settings.screen_width - (2 * pirate_width) number_pirates_x = available_space_x // (2 * pirate_width) #Determine the number of rows of pirates that fit on screen cannon_height = self.cannon.rect.height available_space_y = (self.settings.screen_height - (3 * pirate_height) - cannon_height) number_rows = available_space_y // (2 * pirate_height) #Create a full fleet of pirates for row_number in range(number_rows): for pirate_number in range(number_pirates_x): self._create_alien(pirate_number, row_number) def _create_alien(self, pirate_number, row_number): #Create pirate and place in row pirate = Pirate(self) pirate_width, pirate_height = pirate.rect.size pirate.x = pirate_width + 2 * pirate_width * pirate_number pirate.rect.x = pirate.x pirate.rect.y = pirate.rect.height + 2 * pirate.rect.height * row_number self.pirates.add(pirate) def _check_fleet_edges(self): """Respond if any pirates have reached an edge""" for pirate in self.pirates.sprites(): if pirate.check_edges(): self._change_fleet_direction() break def _change_fleet_direction(self): """Drop the entire fleet and change the fleet direction""" for pirate in self.pirates.sprites(): pirate.rect.y += self.settings.fleet_drop_speed self.settings.fleet_direction *= -1
class Level(GameState): """Level is a GameState with behavior for one full game level. Contains Sprites player, enemy, shield, hbullet, cannon, and Group player_bullets """ def __init__(self, manager): """manager is required to be a child of GameManager with these functions: -kill_player() -next_level() -add_score(amount) -give_energy(amount) -spend_energy(amount) -give_life() """ GameState.__init__(self, manager) self.player = Ship(*opt.player_args) self.enemy = EnemyBase(opt.mover_args, opt.spinner_args, opt.shooter_args, self.player) self.shield = EnemyShield(self.enemy, opt.shield_filename, formation, formation_center) self.hbullet = HomingBullet(opt.homer_filename, self.player, opt.homer_speed) self.cannon = Cannon(opt.deactivated_cannon_args, opt.standby_cannon_args, opt.firing_cannon_args, self.player) self.ion_field = IonField(*opt.ion_field_args) self.player_bullets = Group() self.reset_positions() def update(self): self.player.update() self.enemy.update() self.shield.update() self.hbullet.update() self.cannon.update() self.player_bullets.update() self.ion_field.update() self.collisions() def handle_events(self, events, keys): return (event_handlers.check_quit(events, keys) and event_handlers.check_shoot_button(events, keys, self.shoot) and event_handlers.move_player(events, keys, self.player)) def draw(self, screen): self.ion_field.draw(screen) self.enemy.draw(screen) self.shield.draw(screen) self.player.draw(screen) self.hbullet.draw(screen) self.cannon.draw(screen) self.player_bullets.draw(screen) def collisions(self): """Handles collisions """ player = self.player enemy = self.enemy shield = self.shield hbullet = self.hbullet cannon = self.cannon ion_field = self.ion_field player_bullets = self.player_bullets #player with homing bullet if collide_mask(player, hbullet) and not collide_mask(player, ion_field): self.kill_player() #player with enemy base if collide_mask(player, enemy): #if base in moving phase, give player energy if enemy.get_state_number() == EnemyBase.MOVING: self.manager.give_energy(opt.energy_from_enemy) #if base in spinning or shooting phase, kill player elif enemy.get_state_number() == EnemyBase.SPINNING: self.kill_player() elif enemy.get_state_number() == EnemyBase.SHOOTING: self.kill_player() #player with cell #-hitting a cell will bounce the player a bit to the left #-if the player hit the cell twice in a short enough span, # the cell is eaten and the player gets energy #-in case of multiple collisions, deal with cell closest to player's center # #TODO: This still isn't quite right. #Should be able to eat top/bottom rows with diagonal movement. #(vertical movement should still move player all the way left) pc_collides = spritecollide(player, shield, False, collide_mask) center_cell = self.find_centermost_cell(pc_collides) if center_cell is not None: player.rect.right = center_cell.rect.left - opt.cell_bounceback if not center_cell.marked: center_cell.mark() elif shield.can_eat(): center_cell.kill() self.manager.give_energy(opt.energy_from_cell) self.manager.add_score(opt.score_cell_eat) shield.start_delay(opt.frames_to_eat_cell) #player with cannon if collide_mask(player, cannon): #if in deactivated phase, try spending required energy to activate if (cannon.get_state_number() == Cannon.DEACTIVATED and self.manager.spend_energy(opt.cannon_energy_cost)): cannon.start_standby() #if in firing phase, kill player if cannon.get_state_number() == Cannon.FIRING: self.kill_player() #if in returning phase, give energy and deactivate cannon if cannon.get_state_number() == Cannon.RETURNING: cannon.start_transition(Cannon.DEACTIVATED) self.manager.give_energy(opt.energy_from_cannon) #cannon with cell #kill one cell and reverse cannon direction #assuming this is only possible if cannon in firing state if cannon.get_state_number() == Cannon.FIRING: cannon_collides = spritecollide(cannon, shield, False, collide_mask) if len(cannon_collides) > 0: cannon_collides[0].kill() self.manager.add_score(opt.score_cell_shoot) cannon.start_transition(Cannon.RETURNING) #cannon with enemy base -- only if cannon in firing state #give points corresponding to enemy state and end level #if enemy base is in shooting state, player also gets a life if cannon.get_state_number() == Cannon.FIRING and collide_mask( cannon, enemy): if enemy.get_state_number() == EnemyBase.MOVING: self.manager.add_score(opt.score_mover_destroy) elif enemy.get_state_number() == EnemyBase.SPINNING: self.manager.add_score(opt.score_spinner_destroy) elif enemy.get_state_number() == EnemyBase.SHOOTING: self.manager.add_score(opt.score_shooter_destroy) self.manager.give_life() self.end_level() #player's bullet with cell #kill player bullet but remove cells in a cross pattern #if somehow one bullet hits multiple cells one is arbitrarily selected bc_collides = groupcollide(player_bullets, shield, True, False, collide_mask) for current_bullet in bc_collides.keys(): self.manager.add_score(opt.score_cell_shoot) shield.remove_cross(bc_collides[current_bullet][0]) def find_centermost_cell(self, cells): """Given a list of Cell sprites, returns the one whose rect.centery is closest to the player's rect.centery Returns None if list is empty """ closest_cell = None for current_cell in cells: current_dist = abs(current_cell.rect.centery - self.player.rect.centery) if closest_cell is None or current_dist < closest_dist: closest_cell = current_cell closest_dist = current_dist return closest_cell def shoot(self): """If cannon can be fired, fires cannon. Otherwise creates bullet moving from player's center along player's direction as long as options.max_player_bullets won't be exeeded """ if collide_mask(self.player, self.ion_field): return if self.cannon.start_transition(Cannon.FIRING): return if len(self.player_bullets) < opt.max_player_bullets: new_bullet = Bullet(opt.bullet_filename, opt.bullet_speed, self.player.get_rect().center, self.player.get_direction()) self.player_bullets.add(new_bullet) def reset_positions(self): """Moves sprites to their initial locations: player starts in left center facing south and with 0 energy player bullets are removed enemy bullet starts on enemy base enemy base is in moving state cannon is in deactivated state Note: shield configuration is not reset """ self.player.rect.midleft = (20, int(opt.height / 2)) self.player.set_direction(vector.SOUTH) self.player_bullets.empty() self.enemy.resume_mover_state() self.cannon.start_deactivated() self.hbullet.rect.center = self.enemy.rect.center def kill_player(self): death_animation = DeathAnimation(self.manager, self.player, (self.enemy, self.shield), self, opt.death_animation_delay, opt.death_animation_total_runtime) self.manager.change_state(death_animation) def end_level(self): win_animation = WinAnimation(self.manager, self.player, opt.win_animation_total_runtime, opt.exp_field_args) self.manager.change_state(win_animation)
def main(): fps = 30 clock = pygame.time.Clock() screen = pygame.display.set_mode((config.WIDTH, config.HEIGHT)) pygame.font.init() font = pygame.font.Font("DejaVuSans.ttf", 24) if TYPE == 'HOST': s, game_map, conn = hostOptions(screen, font) elif TYPE == 'JOIN': s, game_map = joinOptions(screen, font) else: textOnMiddle(screen, 'Generating map...', font) pygame.display.flip() game_map = GameMap().generate(config.WIDTH, config.HEIGHT, 2500) #icon = pygame.image.load("pygame-icon.png") #icon = icon.convert_alpha() #icon_w, icon_h = icon.get_size() shooting_force = -1 bullets = [] cannon1 = Cannon(screen, game_map, 100, (0, 255, 0), 0) cannon2 = Cannon(screen, game_map, config.WIDTH - 100, (255, 0, 0), 180) gameEnded = False while True: msg = {"cannon": {}} recv_msg = {} shooting_force, quitGame = getInputEvents(screen, cannon1, cannon2, bullets, game_map, shooting_force, msg) if quitGame: if TYPE == 'HOST': conn.close() if TYPE == 'JOIN' or TYPE == 'HOST': print('Closing Server...') return s return ping = time.time() if TYPE == 'HOST': try: conn.send(encodeDict(msg)) recv_msg = conn.recv(1024) recv_msg = decodeDict(recv_msg) except ConnectionResetError: print('Connection closed by client') conn.close() return s except json.decoder.JSONDecodeError: print('Connection closed by client') conn.close() return s elif TYPE == 'JOIN': try: recv_msg = s.recv(1024) s.send(encodeDict(msg)) recv_msg = decodeDict(recv_msg) except json.decoder.JSONDecodeError: print('Connection closed by host') return s except ConnectionResetError: print('Connection closed by host') return s if 'shoot' in recv_msg: if TYPE == 'JOIN': rot = cannon1.rot center = cannon1.base.rect.center else: rot = cannon2.rot center = cannon2.base.rect.center bullets.append( Bullet(screen, game_map, center[0], center[1], recv_msg['shoot']['shooting_force'], rot)) if 'cannon' in recv_msg: if 'rotation' in recv_msg['cannon']: rotation = recv_msg['cannon']['rotation'] if TYPE == 'JOIN': cannon1.rotating = rotation if TYPE == 'HOST': cannon2.rotating = rotation ping = time.time() - ping #reset drawing screen.fill((0, 0, 0)) #draw background game_map.printBackground(screen) #draw top info text = font.render('10uv >>', True, (255, 255, 255)) fps_text = font.render( str(round(clock.get_fps(), 2)) + ' fps', True, (255, 255, 255)) ping_text = font.render('ping ' + str(int(ping * 1000)) + 'ms', True, (255, 255, 255)) text_w, text_h = fps_text.get_size() ping_text_w, _ = ping_text.get_size() screen.blit(text, (0, 0)) screen.blit(fps_text, (config.WIDTH - text_w, 0)) screen.blit(ping_text, (config.WIDTH - ping_text_w, text_h)) #draw map ground game_map.printGround(screen) #draw shoot bar if shooting_force != -1: if shooting_force < 50: shooting_force += 1 hud.drawShootForce(screen, shooting_force) #draw shoot bar ruler hud.drawShootForceRuler(screen) #update and draw bullets for bullet in bullets[:]: if not bullet.update(): if bullet.collide: x = int(bullet.x) collision_range = range(x - 15, x + 15) for i in collision_range: #TODO: send this to game map if len(game_map.map_curve) > i >= 0: game_map.map_curve[i] += 10 if not cannon1.isAlive(collision_range): winnerText = font.render('Cannon 2 won.', True, (255, 255, 255)) gameEnded = True break if not cannon2.isAlive(collision_range): winnerText = font.render('Cannon 1 won.', True, (255, 255, 255)) gameEnded = True break bullets.remove(bullet) del bullet else: bullet.draw() #update and draw cannons cannon1.update() cannon1.draw(screen) cannon2.update() cannon2.draw(screen) if gameEnded: while len(bullets) > 0: bullets.pop() winnerText_w, winnerText_h = winnerText.get_size() screen.blit(winnerText, (config.WIDTH / 2 - winnerText_w / 2, config.HEIGHT / 2 - winnerText_h / 2)) #end drawing pygame.display.flip() clock.tick(fps)
class Level(GameState): """Level is a GameState with behavior for one full game level. Contains Sprites player, enemy, shield, hbullet, cannon, and Group player_bullets """ def __init__(self, manager): """manager is required to be a child of GameManager with these functions: -kill_player() -next_level() -add_score(amount) -give_energy(amount) -spend_energy(amount) -give_life() """ GameState.__init__(self, manager) self.player = Ship(*opt.player_args) self.enemy = EnemyBase(opt.mover_args, opt.spinner_args, opt.shooter_args, self.player) self.shield = EnemyShield(self.enemy, opt.shield_filename, formation, formation_center) self.hbullet = HomingBullet(opt.homer_filename, self.player, opt.homer_speed) self.cannon = Cannon(opt.deactivated_cannon_args, opt.standby_cannon_args, opt.firing_cannon_args, self.player) self.ion_field = IonField(*opt.ion_field_args) self.player_bullets = Group() self.reset_positions() def update(self): self.player.update() self.enemy.update() self.shield.update() self.hbullet.update() self.cannon.update() self.player_bullets.update() self.ion_field.update() self.collisions() def handle_events(self, events, keys): return (event_handlers.check_quit(events, keys) and event_handlers.check_shoot_button(events, keys, self.shoot) and event_handlers.move_player(events, keys, self.player)) def draw(self, screen): self.ion_field.draw(screen) self.enemy.draw(screen) self.shield.draw(screen) self.player.draw(screen) self.hbullet.draw(screen) self.cannon.draw(screen) self.player_bullets.draw(screen) def collisions(self): """Handles collisions """ player = self.player enemy = self.enemy shield = self.shield hbullet = self.hbullet cannon = self.cannon ion_field = self.ion_field player_bullets = self.player_bullets #player with homing bullet if collide_mask(player, hbullet) and not collide_mask(player, ion_field): self.kill_player() #player with enemy base if collide_mask(player, enemy): #if base in moving phase, give player energy if enemy.get_state_number() == EnemyBase.MOVING: self.manager.give_energy(opt.energy_from_enemy) #if base in spinning or shooting phase, kill player elif enemy.get_state_number() == EnemyBase.SPINNING: self.kill_player() elif enemy.get_state_number() == EnemyBase.SHOOTING: self.kill_player() #player with cell #-hitting a cell will bounce the player a bit to the left #-if the player hit the cell twice in a short enough span, # the cell is eaten and the player gets energy #-in case of multiple collisions, deal with cell closest to player's center # #TODO: This still isn't quite right. #Should be able to eat top/bottom rows with diagonal movement. #(vertical movement should still move player all the way left) pc_collides = spritecollide(player, shield, False, collide_mask) center_cell = self.find_centermost_cell(pc_collides) if center_cell is not None: player.rect.right = center_cell.rect.left - opt.cell_bounceback if not center_cell.marked: center_cell.mark() elif shield.can_eat(): center_cell.kill() self.manager.give_energy(opt.energy_from_cell) self.manager.add_score(opt.score_cell_eat) shield.start_delay(opt.frames_to_eat_cell) #player with cannon if collide_mask(player, cannon): #if in deactivated phase, try spending required energy to activate if (cannon.get_state_number() == Cannon.DEACTIVATED and self.manager.spend_energy(opt.cannon_energy_cost)): cannon.start_standby() #if in firing phase, kill player if cannon.get_state_number() == Cannon.FIRING: self.kill_player() #if in returning phase, give energy and deactivate cannon if cannon.get_state_number() == Cannon.RETURNING: cannon.start_transition(Cannon.DEACTIVATED) self.manager.give_energy(opt.energy_from_cannon) #cannon with cell #kill one cell and reverse cannon direction #assuming this is only possible if cannon in firing state if cannon.get_state_number() == Cannon.FIRING: cannon_collides = spritecollide(cannon, shield, False, collide_mask) if len(cannon_collides) > 0: cannon_collides[0].kill() self.manager.add_score(opt.score_cell_shoot) cannon.start_transition(Cannon.RETURNING) #cannon with enemy base -- only if cannon in firing state #give points corresponding to enemy state and end level #if enemy base is in shooting state, player also gets a life if cannon.get_state_number() == Cannon.FIRING and collide_mask(cannon, enemy): if enemy.get_state_number() == EnemyBase.MOVING: self.manager.add_score(opt.score_mover_destroy) elif enemy.get_state_number() == EnemyBase.SPINNING: self.manager.add_score(opt.score_spinner_destroy) elif enemy.get_state_number() == EnemyBase.SHOOTING: self.manager.add_score(opt.score_shooter_destroy) self.manager.give_life() self.end_level() #player's bullet with cell #kill player bullet but remove cells in a cross pattern #if somehow one bullet hits multiple cells one is arbitrarily selected bc_collides = groupcollide(player_bullets, shield, True, False, collide_mask) for current_bullet in bc_collides.keys(): self.manager.add_score(opt.score_cell_shoot) shield.remove_cross(bc_collides[current_bullet][0]) def find_centermost_cell(self, cells): """Given a list of Cell sprites, returns the one whose rect.centery is closest to the player's rect.centery Returns None if list is empty """ closest_cell = None for current_cell in cells: current_dist = abs(current_cell.rect.centery - self.player.rect.centery) if closest_cell is None or current_dist < closest_dist: closest_cell = current_cell closest_dist = current_dist return closest_cell def shoot(self): """If cannon can be fired, fires cannon. Otherwise creates bullet moving from player's center along player's direction as long as options.max_player_bullets won't be exeeded """ if collide_mask(self.player, self.ion_field): return if self.cannon.start_transition(Cannon.FIRING): return if len(self.player_bullets) < opt.max_player_bullets: new_bullet = Bullet(opt.bullet_filename, opt.bullet_speed, self.player.get_rect().center, self.player.get_direction()) self.player_bullets.add(new_bullet) def reset_positions(self): """Moves sprites to their initial locations: player starts in left center facing south and with 0 energy player bullets are removed enemy bullet starts on enemy base enemy base is in moving state cannon is in deactivated state Note: shield configuration is not reset """ self.player.rect.midleft = (20, int(opt.height/2)) self.player.set_direction(vector.SOUTH) self.player_bullets.empty() self.enemy.resume_mover_state() self.cannon.start_deactivated() self.hbullet.rect.center = self.enemy.rect.center def kill_player(self): death_animation = DeathAnimation(self.manager, self.player, (self.enemy, self.shield), self, opt.death_animation_delay, opt.death_animation_total_runtime) self.manager.change_state(death_animation) def end_level(self): win_animation = WinAnimation(self.manager, self.player, opt.win_animation_total_runtime, opt.exp_field_args) self.manager.change_state(win_animation)