class Scene(State): def __init__(self, file_path): super(Scene, self).__init__() pygame.mixer.init(44100, -16, 4, 2048) self.position = ['goalkeeper', 'defender', 'midfielder', 'attacker'] self.all_object_list = pygame.sprite.LayeredUpdates() self.selection_circle = [] # Prototype self.left_player = Player() # Prototype self.right_player = Player() # Prototype self.teams = collections.defaultdict(dict) self.field = Object() self.field_border = Object() self.goal = [] self.ball = Ball() self.hud = HUD() self.goal_image = EffectObject() self.formation = {Team_side.LEFT: [1, 4, 3, 3], Team_side.RIGHT: [1, 4, 4, 2]} self.sound_list = dict() # Game config self.game_mode = Game_mode.PvP self.P1_controlled_team = Team_side.LEFT self.P1_controlled_position = 'midfielder' self.P2_controlled_team = Team_side.RIGHT self.P2_controlled_position = 'midfielder' # Load scene's resources self.read_scene(file_path) def init(self): # Set teams info # Left team self.game_mode = self.get_shared_var('mode') self.formation[self.P1_controlled_team] = self.get_shared_var('P1_formation') team1_id = self.get_shared_var('P1_team_ID') self.hud.set_left_team_name(Team_name[team1_id]) self.left_player.setImage(ResourceManager.instance().image_path_list[56 + team1_id]) # Right team self.formation[self.P2_controlled_team] = self.get_shared_var('P2_formation') team2_id = self.get_shared_var('P2_team_ID') self.hud.set_right_team_name(Team_name[team2_id]) self.right_player.setImage(ResourceManager.instance().image_path_list[56 + team2_id]) # Calculate players and ball position self.calculate_position() # Set controlled team self.controlled_team = Team_side.LEFT # Randomize ball direction self.ball.set_angle(random.randrange(0, 360, 20)) # Game control self.game_over = False self.match_state = Match_state.KICKOFF self.match_time = 120000 self.time = self.match_time # Reset score self.hud.reset_score() # Set background music pygame.mixer.stop() self.sound_list['background'].play().set_endevent(pygame.constants.USEREVENT) def process_key_press(self, key): if key[pygame.K_w]: apply_all(self.teams[self.P1_controlled_team][self.P1_controlled_position], 'move_up') if key[pygame.K_s]: apply_all(self.teams[self.P1_controlled_team][self.P1_controlled_position], 'move_down') if key[pygame.K_UP]: apply_all(self.teams[self.P2_controlled_team][self.P2_controlled_position], 'move_up') if key[pygame.K_DOWN]: apply_all(self.teams[self.P2_controlled_team][self.P2_controlled_position], 'move_down') def process_events(self, event): if event.type == pygame.USEREVENT: self.sound_list['background'].play() elif event.type == pygame.MOUSEBUTTONDOWN: return '' elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: if self.is_running: self.pause() else: self.resume() elif event.key == pygame.K_a: apply_all(self.teams[self.P1_controlled_team][self.P1_controlled_position], 'stop') self.P1_controlled_position = self.position[max(0, self.position.index(self.P1_controlled_position) - 1)] elif event.key == pygame.K_d: apply_all(self.teams[self.P1_controlled_team][self.P1_controlled_position], 'stop') self.P1_controlled_position = self.position[min(3, self.position.index(self.P1_controlled_position) + 1)] elif event.key == pygame.K_LEFT: apply_all(self.teams[self.P2_controlled_team][self.P2_controlled_position], 'stop') self.P2_controlled_position = self.position[min(3, self.position.index(self.P2_controlled_position) + 1)] elif event.key == pygame.K_RIGHT: apply_all(self.teams[self.P2_controlled_team][self.P2_controlled_position], 'stop') self.P2_controlled_position = self.position[max(0, self.position.index(self.P2_controlled_position) - 1)] elif event.type == pygame.KEYUP: if event.key == pygame.K_w or event.key == pygame.K_s: apply_all(self.teams[self.P1_controlled_team][self.P1_controlled_position], 'stop') elif event.key == pygame.K_UP or event.key == pygame.K_DOWN: if self.game_mode is Game_mode.PvP: apply_all(self.teams[self.P2_controlled_team][self.P2_controlled_position], 'stop') def update(self): if (not self.is_running): return if self.match_state == Match_state.KICKOFF: self.sound_list['whistle'].play() pygame.time.delay(1500) self.match_state = Match_state.PLAYING elif self.match_state == Match_state.PLAYING: # Logic processing self.time -= 1000 / 60 if self.time <= 0.0: self.match_state = Match_state.KICKOFF self.reset_game() self.hud.set_time(int(self.time)) self.process_ball() elif self.match_state == Match_state.GOAL: goal_image = self.goal_image.clone() goal_image.set_appear_time(500) goal_image.show() self.all_object_list.add(goal_image) self.match_state = Match_state.KICKOFF self.reset_game() # Update all objects self.all_object_list.update() self.hud.update() def draw(self, screen): screen.fill(BLACK) self.all_object_list.draw(screen) apply_all(self.teams[self.P1_controlled_team][self.P1_controlled_position], 'show_selection_circle', screen) if self.game_mode is Game_mode.PvP: apply_all(self.teams[self.P2_controlled_team][self.P2_controlled_position], 'show_selection_circle', screen) self.hud.draw(screen) def reset_game(self): self.calculate_position() self.match_state = Match_state.KICKOFF #--------------------------------------LOGIC PROCESSING SECTION----------------------------------------------- def process_ball(self): # Check if hit goals if (pygame.sprite.collide_mask(self.ball, self.goal[0])) is not None: self.sound_list['goal'].play() self.hud.update_right_score() self.match_state = Match_state.GOAL return elif pygame.sprite.collide_mask(self.ball, self.goal[1]) is not None: self.sound_list['goal'].play() self.hud.update_left_score() self.match_state = Match_state.GOAL return # Check if hit field's border self.process_collide_border() # Check if hit players player_list = [players for sublist in (list(self.teams[Team_side.LEFT].values()) + list(self.teams[Team_side.RIGHT].values())) for players in sublist] hit_player = pygame.sprite.spritecollideany(self.ball, player_list, pygame.sprite.collide_circle) if hit_player is not None: self.sound_list['ball_kick'].play() self.ball.collide_object(hit_player) # Process AI list(chain(*self.teams[Team_side.LEFT].values())) """for position in self.teams[Team_side.RIGHT].keys(): action = 'move_up' if random.randrange(0, 2) is 0 else 'move_down' apply_all(self.teams[Team_side.RIGHT][position], action)""" def process_collide_border(self): """Check if ball touch field's border and calculate rebound direction """ ball_pos = self.ball.get_pos() if ball_pos[0] < 50: self.ball.move_to(51, self.ball.get_pos()[1]) self.ball.toggle_x_vel() elif (ball_pos[0] + self.ball.get_width()) > SCREEN_WIDTH - 50: self.ball.move_to(SCREEN_WIDTH - 51 - self.ball.get_width(), self.ball.get_pos()[1]) self.ball.toggle_x_vel() elif ball_pos[1] < 20: self.ball.move_to(self.ball.get_pos()[0], 21) self.ball.toggle_y_vel() elif (ball_pos[1] + self.ball.get_height()) > SCREEN_HEIGHT - 20: self.ball.move_to(self.ball.get_pos()[0], SCREEN_HEIGHT - 21 - self.ball.get_height()) self.ball.toggle_y_vel() def calculate_position(self): """Calculate and move ball and players to the right position """ # Calculate ball postion x = (SCREEN_WIDTH - self.ball.get_width()) // 2 y = (SCREEN_HEIGHT - 8 - self.ball.get_height()) // 2 self.ball.move_to(x, y) # Generate and calculate red team players for i in range(len(self.formation[Team_side.LEFT])): if len(self.teams[Team_side.LEFT]) == 4: apply_all(self.teams[Team_side.LEFT][self.position[i]], 'kill') self.teams[Team_side.LEFT].update({self.position[i]: []}) # Calculate offset x_offset = 100 y_offset = (self.field.get_height() - 8 - self.left_player.get_height() * self.formation[Team_side.LEFT][i]) // (self.formation[Team_side.LEFT][i] + 1) for j in range(self.formation[Team_side.LEFT][i]): # Calculate position x = self.left_player.get_pos()[0] + (x_offset * (i ** 1.69)) y = y_offset * (j + 1) + self.left_player.get_height() * j + 4 self.teams[Team_side.LEFT][self.position[i]].append(self.left_player.clone()) self.teams[Team_side.LEFT][self.position[i]][j].move_to(x, y) self.teams[Team_side.LEFT][self.position[i]][-1].set_layer(1) self.teams[Team_side.LEFT][self.position[i]][-1].set_radius() self.teams[Team_side.LEFT][self.position[i]][j].set_max_offset(y_offset - 20) self.all_object_list.add(self.teams[Team_side.LEFT][self.position[i]][-1]) # Generate and calculate blue team players for i in range(len(self.formation[Team_side.RIGHT])): if len(self.teams[Team_side.RIGHT]) == 4: apply_all(self.teams[Team_side.RIGHT][self.position[i]], 'kill') self.teams[Team_side.RIGHT].update({self.position[i]: []}) # Calculate offset x_offset = 100 y_offset = (self.field.get_height() - 8 - self.right_player.get_height() * self.formation[Team_side.RIGHT][i]) // (self.formation[Team_side.RIGHT][i] + 1) for j in range(self.formation[Team_side.RIGHT][i]): # Calculate position x = self.right_player.get_pos()[0] - (x_offset * (i ** 1.69)) y = y_offset * (j + 1) + self.right_player.get_height() * j + 4 self.teams[Team_side.RIGHT][self.position[i]].append(self.right_player.clone()) self.teams[Team_side.RIGHT][self.position[i]][j].move_to(x, y) self.teams[Team_side.RIGHT][self.position[i]][-1].set_layer(1) self.teams[Team_side.RIGHT][self.position[i]][-1].set_radius() self.teams[Team_side.RIGHT][self.position[i]][j].set_max_offset(y_offset - 20) self.all_object_list.add(self.teams[Team_side.RIGHT][self.position[i]][-1]) # Assign selection_circle to each player # Left team for player in [players for sublist in list(self.teams[Team_side.LEFT].values()) for players in sublist]: player.selection_circle = self.selection_circle[0].clone() # Right team for player in [players for sublist in list(self.teams[Team_side.RIGHT].values()) for players in sublist]: player.selection_circle = self.selection_circle[1].clone() #----------------------------------------READ FILE SECTION----------------------------------------------------- def read_scene(self, file_path): with open(file_path) as file: self.read_hud(file) self.read_field(file) self.read_red_team(file) self.read_blue_team(file) self.read_selection_circle(file) self.read_ball(file) self.read_effect(file) self.read_sound(file) def read_hud(self, file): file.readline() hud_id = int(file.readline().strip().split(' ')[1]) self.hud.init(ResourceManager.instance().hud_path_list[hud_id]) def read_field(self, file): # Read field file.readline() image_id = int(file.readline().strip().split(' ')[1]) self.field.init('Image', file_name = ResourceManager.instance().image_path_list[image_id]) self.field.scale_to(SCREEN_WIDTH, SCREEN_HEIGHT) self.all_object_list.add(self.field) # Read goal file.readline() image_id_1, image_id_2 = list(map(int, file.readline().strip().split(' ')[1:])) self.goal.append(Object()) self.goal[-1].init('Image', file_name = ResourceManager.instance().image_path_list[image_id_1]) self.goal[-1].scale_to(SCREEN_WIDTH, SCREEN_HEIGHT) self.goal[-1].set_layer(-1) self.goal.append(Object()) self.goal[-1].init('Image', file_name = ResourceManager.instance().image_path_list[image_id_2]) self.goal[-1].scale_to(SCREEN_WIDTH, SCREEN_HEIGHT) self.goal[-1].set_layer(-1) self.all_object_list.add(self.goal) def read_red_team(self, file): self.teams[Team_side.LEFT] = dict() file.readline() image_id = int(file.readline().strip().split(' ')[1]) # Prototype self.left_player.init('Image', file_name = ResourceManager.instance().image_path_list[image_id]) self.left_player.rotate(float(file.readline().strip().split(' ')[1])) self.left_player.scale(*map(float, file.readline().strip().split(' ')[1:])) self.left_player.translate(*map(int, file.readline().strip().split(' ')[1:])) def read_blue_team(self, file): self.teams[Team_side.RIGHT] = dict() file.readline() image_id = int(file.readline().strip().split(' ')[1]) # Prototype self.right_player.init('Image', file_name = ResourceManager.instance().image_path_list[image_id]) self.right_player.rotate(float(file.readline().strip().split(' ')[1])) self.right_player.scale(*map(float, file.readline().strip().split(' ')[1:])) self.right_player.translate(*map(int, file.readline().strip().split(' ')[1:])) def read_selection_circle(self, file): file.readline() image_id = list(map(int, (file.readline().strip().split(' ')[1:]))) image_id_green = image_id[0] image_id_red = image_id[1] # Green circle self.selection_circle.append(Object()) self.selection_circle[-1].init('Image', file_name = ResourceManager.instance().image_path_list[image_id_green], alpha = True) self.selection_circle[-1].rotate(float(file.readline().strip().split(' ')[1])) self.selection_circle[-1].scale(*map(float, file.readline().strip().split(' ')[1:])) self.selection_circle[-1].translate(*map(int, file.readline().strip().split(' ')[1:])) # Red circle self.selection_circle.append(self.selection_circle[-1].clone()) self.selection_circle[-1].setImage(ResourceManager.instance().image_path_list[image_id_red]) def read_ball(self, file): file.readline() image_id = int(file.readline().strip().split(' ')[1]) self.ball.init('Image', file_name = ResourceManager.instance().image_path_list[image_id], alpha = True) self.ball.rotate(float(file.readline().strip().split(' ')[1])) self.ball.scale(*map(float, file.readline().strip().split(' ')[1:])) self.ball.translate(*map(int, file.readline().strip().split(' ')[1:])) self.ball.set_radius() self.all_object_list.add(self.ball) def read_effect(self, file): file.readline() image_id = int(file.readline().strip().split(' ')[1]) self.goal_image.init('Image', file_name = ResourceManager.instance().image_path_list[image_id], alpha = True) self.goal_image.rotate(float(file.readline().strip().split(' ')[1])) self.goal_image.scale(*map(float, file.readline().strip().split(' ')[1:])) self.goal_image.translate(*map(int, file.readline().strip().split(' ')[1:])) self.goal_image.set_layer(5) def read_sound(self, file): sound_num = int(file.readline().strip().split(' ')[1]) for i in range(sound_num): sound_type = file.readline().strip().replace('#', '') sound_id = int(file.readline().strip().split(' ')[1]) sound = pygame.mixer.Sound(ResourceManager.instance().sound_path_list[sound_id]) self.sound_list.update({sound_type: sound})
class Scene(State): def __init__(self): super(Scene, self).__init__() pygame.mixer.init(44100, -16, 4, 2048) self.all_object_list = pygame.sprite.LayeredUpdates() self.zombie_list = pygame.sprite.Group() self.blood_splash = BloodSplash() self.background = Object() self.player = Player() self.zombie_sprite = [] self.grave_list = [] self.hud = HUD() self.sound_list = dict() self.bg_music = pygame.mixer.music self.game_over = False self.time = pygame.time.get_ticks() def init(self): self.read_scene() # Bring cursor to front self.all_object_list.move_to_front(self.player) # Set background music self.sound_list['background'].play().set_endevent(pygame.constants.USEREVENT) # Scale background to screen size self.background.image = pygame.transform.scale(self.background.image, (SCREEN_WIDTH, SCREEN_HEIGHT)) def process_events(self, event): if event.type == pygame.USEREVENT: self.sound_list['background'].play() elif event.type == pygame.MOUSEBUTTONDOWN: self.sound_list['gunshot'].play() self.process_raycast() elif event.type == pygame.KEYDOWN: if (event.key == pygame.K_p): if self.is_running: self.pause() else: self.resume() def update(self): if (not self.is_running): return # Process game self.process_zombie() # Update all objects self.all_object_list.update(self.hud) self.hud.update() def draw(self, screen): screen.fill(WHITE) self.all_object_list.draw(screen) apply_all(self.zombie_list.sprites(), 'show_timer', screen) self.hud.draw(screen) #--------------------------------------LOGIC PROCESSING SECTION----------------------------------------------- def process_zombie(self): # Randomly choose an interval between 0.5 to 1.5 second to spawn another zombie if (pygame.time.get_ticks() - self.time >= random.randrange(1, 3) * 500): # Random location for zombie zombie_pos = random.randrange(0, len(self.grave_list)) # Check if grave's position already had zombie if (not next((True for zombie in self.zombie_list.sprites() if zombie.pos == zombie_pos), False)): # Randomly pick zombie from zombie sprite list zombie = deepcopy(self.zombie_sprite[random.randrange(0, len(self.zombie_sprite))]) zombie.set_pos(zombie_pos) # Set zombie to the right position zombie.move_to(*self.grave_list[zombie_pos].get_pos()) zombie.translate(35, -10) # Set zombie layer zombie.set_layer(self.grave_list[zombie_pos]._layer - 1) # Add to zombie list and object list self.zombie_list.add(zombie) self.all_object_list.add(zombie) # Reset timer self.time = pygame.time.get_ticks() def process_raycast(self): mouse_pos = pygame.mouse.get_pos() hit_zombie = next((zombie for zombie in self.zombie_list.sprites() if zombie.collide_point(list(map(operator.sub, mouse_pos, zombie.get_pos())))), None) if (hit_zombie is not None): # Play zombie's death sound self.sound_list['dying_scream'].play() # Update hit count self.hud.update_hit() # Splash blood blood = deepcopy(self.blood_splash) blood.move_to(*self.player.get_pos()) blood.set_layer(self.all_object_list.get_top_layer() + 1) blood.show() self.all_object_list.add(blood) hit_zombie.kill() #----------------------------------------READ FILE SECTION----------------------------------------------------- def read_scene(self): with open(ResourceManager.scene_path_list[0]) as file: self.read_hud(file) self.read_background(file) self.read_player(file) self.read_grave(file) self.read_zombie(file) self.read_blood_splash(file) self.read_sound(file) def read_hud(self, file): file.readline() hud_id = int(file.readline().strip().split(' ')[1]) self.hud.init(ResourceManager.hud_path_list[hud_id]) def read_background(self, file): file.readline() image_id = int(file.readline().strip().split(' ')[1]) self.background.init('Image', file_name = ResourceManager.image_path_list[image_id]) self.all_object_list.add(self.background) def read_player(self, file): file.readline() image_id = int(file.readline().strip().split(' ')[1]) self.player.init('Image', file_name = ResourceManager.image_path_list[image_id], alpha = True) self.player.translate(*map(int, file.readline().strip().split(' ')[1:])) self.player.rotate(float(file.readline().strip().split(' ')[1])) self.player.scale(*map(float, file.readline().strip().split(' ')[1:])) self.all_object_list.add(self.player) def read_grave(self, file): file.readline() image_id = int(file.readline().strip().split(' ')[1]) grave = Object() grave.init('Image', file_name = ResourceManager.image_path_list[image_id], alpha = True) grave.translate(*map(int, file.readline().strip().split(' ')[1:])) grave.rotate(float(file.readline().strip().split(' ')[1])) grave.scale(*map(float, file.readline().strip().split(' ')[1:])) for i in range(3): for j in range(3): self.grave_list.append(deepcopy(grave)) self.grave_list[-1].translate(200 * j, 100 * i) self.grave_list[-1].set_layer(2 * (i + 1)) self.all_object_list.add(self.grave_list) def read_zombie(self, file): zombie_num = int(file.readline().strip().split(' ')[1]) for i in range(zombie_num): image_id = int(file.readline().strip().split(' ')[1]) self.zombie_sprite.append(Zombie()) self.zombie_sprite[-1].init('Image', file_name = ResourceManager.image_path_list[image_id], alpha = True) self.zombie_sprite[-1].translate(*map(int, file.readline().strip().split(' ')[1:])) self.zombie_sprite[-1].rotate(float(file.readline().strip().split(' ')[1])) self.zombie_sprite[-1].scale(*map(float, file.readline().strip().split(' ')[1:])) def read_blood_splash(self, file): file.readline() blood_id = int(file.readline().strip().split(' ')[1]) self.blood_splash.init('Image', file_name = ResourceManager.image_path_list[blood_id], alpha = True) self.blood_splash.translate(*map(int, file.readline().strip().split(' ')[1:])) self.blood_splash.rotate(float(file.readline().strip().split(' ')[1])) self.blood_splash.scale(*map(float, file.readline().strip().split(' ')[1:])) def read_sound(self, file): sound_num = int(file.readline().strip().split(' ')[1]) for i in range(sound_num): sound_type = file.readline().strip().replace('#', '') sound_id = int(file.readline().strip().split(' ')[1]) sound = pygame.mixer.Sound(ResourceManager.sound_path_list[sound_id]) self.sound_list.update({sound_type: sound})