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)