class Editor: def __init__(self): # init mixer first to prevent audio delay pygame.mixer.pre_init(44100, -16, 2, 2048) pygame.mixer.init() pygame.init() pygame.display.set_caption('NEXTGAME') self.option_handler = optionhandler.OptionHandler() mode = 0 if self.option_handler.fullscreen: mode = pygame.FULLSCREEN self.screen = pygame.display.set_mode(self.option_handler.resolution, mode) self.image_handler = imagehandler.ImageHandler() self.input_handler = inputhandler.InputHandler() self.clock = pygame.time.Clock() self.time_step = 15.0 / self.option_handler.fps self.font = pygame.font.Font(None, 30) self.level = Level() self.grid_color = [150, 150, 150] self.wall_start = None self.grabbed_object = None self.grab_offset = np.zeros(2) self.object_types = ['wall', 'platform'] self.type_index = 0 self.camera = Camera([0, 0], self.option_handler.resolution) def main_loop(self): while not self.input_handler.quit: self.input_handler.update(self.camera) self.input() if self.grabbed_object is not None: w = self.grabbed_object.collider.half_width h = self.grabbed_object.collider.half_height pos = np.floor(self.input_handler.mouse_position + self.grab_offset - np.floor(w) - np.floor(h)) self.grabbed_object.set_position(pos + w + h) self.screen.fill((50, 50, 50)) self.draw_grid(1.0) self.level.draw(self.screen, self.camera, self.image_handler) for p in self.level.player_spawns: p.draw(self.screen, self.camera, self.image_handler) self.draw_selection() type_str = self.font.render(self.object_types[self.type_index], True, self.image_handler.debug_color) self.screen.blit(type_str, (50, 50)) pygame.display.update() self.clock.tick(self.option_handler.fps) def input(self): if self.input_handler.mouse_pressed[0]: for obj in self.level.walls + self.level.player_spawns + list(self.level.objects.values()) + \ [self.level.scoreboard]: if obj.collider.point_inside( self.input_handler.mouse_position): self.grabbed_object = obj self.grab_offset = self.grabbed_object.position - self.input_handler.mouse_position break else: self.wall_start = np.round(self.input_handler.mouse_position) if self.input_handler.mouse_released[0]: if self.grabbed_object is not None: if isinstance(self.grabbed_object, Weapon): for obj in self.level.objects.values(): if type(obj) is Crate and obj.collider.point_inside( self.input_handler.mouse_position): obj.loot_list.append(type(obj)) del self.grabbed_object break self.grabbed_object = None else: end = np.round(self.input_handler.mouse_position) pos = 0.5 * (self.wall_start + end) size = np.abs(end - self.wall_start) if np.all(size): if self.type_index == 0: self.level.add_wall(pos, size[0], size[1]) else: self.level.add_platform(pos, size[0]) self.wall_start = None if self.input_handler.mouse_down[1]: self.camera.position -= self.input_handler.mouse_change if self.input_handler.mouse_pressed[2]: for w in self.level.walls: if w.collider.point_inside(self.input_handler.mouse_position): self.level.walls.remove(w) for w in self.level.player_spawns: if w.collider.point_inside(self.input_handler.mouse_position): self.level.player_spawns.remove(w) for k in list(self.level.objects.keys()): if self.level.objects[k].collider.point_inside( self.input_handler.mouse_position): del self.level.objects[k] if self.input_handler.mouse_pressed[3]: self.camera.zoom *= 1.5 if self.input_handler.mouse_pressed[4]: self.camera.zoom /= 1.5 if self.input_handler.keys_pressed[pygame.K_s]: for i, o in enumerate(self.level.objects.values()): o.id = i with open('data/levels/lvl.pickle', 'wb') as f: pickle.dump(self.level.get_data(), f) if self.input_handler.keys_pressed[pygame.K_l]: with open('data/levels/lvl.pickle', 'rb') as f: data = pickle.load(f) self.level.clear() self.level.apply_data(data) if self.input_handler.keys_pressed[pygame.K_DELETE]: self.level.clear() if self.input_handler.keys_pressed[pygame.K_w]: if self.type_index == len(self.object_types) - 1: self.type_index = 0 else: self.type_index += 1 pos = np.floor(self.input_handler.mouse_position) + np.array( [0.5, 0.501]) if self.input_handler.keys_pressed[pygame.K_p]: self.level.player_spawns.append(PlayerSpawn(pos)) if self.input_handler.keys_pressed[pygame.K_c]: self.level.add_object(Crate(pos)) if self.input_handler.keys_pressed[pygame.K_b]: self.level.add_object(Ball(pos)) if self.input_handler.keys_pressed[pygame.K_g]: self.level.add_object(Shotgun(pos)) if self.input_handler.keys_pressed[pygame.K_t]: self.level.walls.append(Basket(pos)) if self.input_handler.keys_pressed[pygame.K_i]: self.level.scoreboard = Scoreboard(pos) def draw_grid(self, size): x_min = math.floor(self.camera.position[0] - self.camera.half_width[0] / self.camera.zoom) x_max = math.ceil(self.camera.position[0] + self.camera.half_width[0] / self.camera.zoom) y_min = math.floor(self.camera.position[1] - self.camera.half_height[1] / self.camera.zoom) y_max = math.ceil(self.camera.position[1] + self.camera.half_height[1] / self.camera.zoom) for x in np.linspace(x_min, x_max, int(abs(x_max - x_min) / size) + 1): if x % 5 == 0: width = 3 else: width = 1 pygame.draw.line(self.screen, self.grid_color, self.camera.world_to_screen([x, y_min]), self.camera.world_to_screen([x, y_max]), width) for y in np.linspace(y_min, y_max, int(abs(y_max - y_min) / size) + 1): if y % 5 == 0: width = 3 else: width = 1 pygame.draw.line(self.screen, self.grid_color, self.camera.world_to_screen([x_min, y]), self.camera.world_to_screen([x_max, y]), width) def draw_selection(self): if self.wall_start is not None: end = np.round(self.input_handler.mouse_position) size = self.camera.zoom * (end - self.wall_start) size[1] *= -1 pos = self.camera.world_to_screen(self.wall_start) rect = pygame.rect.Rect(pos[0], pos[1], size[0], size[1]) pygame.draw.rect(self.screen, [255, 255, 255], rect, 5)
class GameLoop: def __init__(self, option_handler): self.option_handler = option_handler self.state = State.MENU self.level = Level() self.players = dict() self.colliders = [] self.time_scale = 1.0 self.camera = Camera([0, 0], self.option_handler.resolution) self.respawn_time = 50.0 self.menu = MainMenu() self.player_menus = [ PlayerMenu((6 * i - 9) * basis(0)) for i in range(4) ] self.options_menu = OptionsMenu() self.options_menu.set_values(self.option_handler) self.network = None self.network_id = -1 self.obj_id = -1 self.controller_id = 0 def load_level(self): self.level = Level('lvl') size = [ int(self.camera.zoom * self.level.width), int(self.camera.zoom * self.level.height) ] self.level.background = pygame.Surface(size) self.level.background.fill((150, 150, 150)) self.colliders.clear() self.colliders = [[[] for _ in range(int(self.level.height))] for _ in range(int(self.level.width))] for wall in self.level.walls: wall.collider.update_occupied_squares(self.colliders) for obj in self.level.objects.values(): obj.collider.update_occupied_squares(self.colliders) def reset_game(self): for w in self.level.walls: if type(w) is Basket: self.level.scoreboard.scores[w.team] = w.score for o in self.level.objects.values(): if o.collider: o.collider.clear_occupied_squares(self.colliders) self.level.reset() for o in self.level.objects.values(): o.collider.update_occupied_squares(self.colliders) for p in self.players.values(): p.set_spawn(self.level, self.players) p.reset(self.colliders) def add_player(self, controller_id, network_id=-1): if network_id == -1: network_id = controller_id player = Player([0, 0], controller_id, network_id) self.players[network_id] = player def update(self, time_step): if self.state is State.PLAY: alive = 0 for player in self.players.values(): if player.active: alive += 1 player.update(self.level.gravity, self.time_scale * time_step, self.colliders) if len(self.players) > 1 >= alive: self.reset_game() self.level.update(self.time_scale * time_step, self.colliders) for o in list(self.level.objects.values()): if type(o) is Ball and o.scored and o.speed < 0.1: self.reset_game() self.camera.update(self.time_scale * time_step, self.players, self.level) elif self.state is State.MENU: self.menu.update(time_step) self.state = self.menu.target_state self.menu.target_state = State.MENU if self.state is State.OPTIONS: self.option_handler.load() self.options_menu.set_values(self.option_handler) elif self.state is State.PLAYER_SELECT: if not self.players: return for pm in self.player_menus: if pm.controller_id is not None: # FIXME: hardcoded index self.players[pm.controller_id].body_type = pm.buttons[ 1].get_value() self.players[pm.controller_id].head_type = pm.buttons[ 0].get_value() for pm in self.player_menus: if pm.controller_id is None: if pm.controller_id in self.players: del self.players[pm.controller_id] if pm.target_state is State.MENU: pm.target_state = State.PLAYER_SELECT self.state = State.MENU self.players.clear() return if pm.controller_id is not None and pm.target_state is State.PLAYER_SELECT: return self.state = State.PLAY self.load_level() for p in self.players.values(): p.set_spawn(self.level, self.players) p.reset(self.colliders) elif self.state is State.LAN: if self.network is None: self.network = Network() data = self.network.data if data is None: print('Server can not be reached') self.network = None self.state = State.MENU return self.network_id = data[0][0] self.add_player(self.controller_id, data[0][0]) self.players[data[0][0]].apply_data(data[0]) self.level.clear() self.level.apply_data(data[1]) self.colliders = [[[] for _ in range(int(self.level.height + 1))] for _ in range(int(self.level.width + 1))] for wall in self.level.walls: wall.collider.update_occupied_squares(self.colliders) for obj in self.level.objects.values(): obj.collider.update_occupied_squares(self.colliders) start_new_thread(self.network_thread, ()) player = self.players[self.network_id] player.update(self.level.gravity, self.time_scale * time_step, self.colliders) obj = self.players[self.network_id].object if obj is not None: obj.update(self.level.gravity, self.time_scale * time_step, self.colliders) self.camera.update(time_step, self.players, self.level) for i in list(self.level.objects): obj = self.level.objects[i] if isinstance(obj, Destroyable): obj.update(self.level.gravity, self.time_scale * time_step, self.colliders) if obj.destroyed and not obj.debris: del self.level.objects[i] elif isinstance(obj, Bullet): obj.update(self.level.gravity, self.time_scale * time_step, self.colliders) if obj.destroyed and not obj.particle_clouds: del self.level.objects[i] elif self.state is State.OPTIONS: self.state = self.options_menu.target_state self.options_menu.target_state = State.OPTIONS if self.options_menu.options_changed: if self.options_menu.buttons[0].get_value() == 'windowed': pygame.display.set_mode( self.options_menu.buttons[1].get_value()) self.option_handler.fullscreen = False else: pygame.display.set_mode( self.options_menu.buttons[1].get_value(), pygame.FULLSCREEN) self.option_handler.fullscreen = True self.camera.set_resolution( self.options_menu.buttons[1].get_value()) self.option_handler.resolution = self.options_menu.buttons[ 1].get_value() self.option_handler.shadows = self.options_menu.buttons[ 4].get_value() == 'ON' self.option_handler.save() self.options_menu.options_changed = False def network_thread(self): while True: data = [self.players[self.network_id].get_data()] if self.obj_id != -1: data.append(self.level.objects[self.obj_id].get_data()) self.level.objects[self.obj_id].attacked = False data = self.network.send(data) for p in data[0]: if p[0] == self.network_id: player = self.players[self.network_id] if player.health <= 0 < p[9]: player.set_spawn(self.level, self.players) player.reset(self.colliders) player.health = p[9] else: if p[0] not in self.players: self.add_player(-1, p[0]) self.players[p[0]].apply_data(p) # kinda purkka ids = [p[0] for p in data[0]] for k in list(self.players.keys()): if k != self.network_id and k not in ids: del self.players[k] for d in data[1]: if d[0] == self.obj_id: continue if d[0] in self.level.objects: self.level.objects[d[0]].apply_data(d) else: obj = d[1]([d[2], d[3]]) obj.apply_data(d) self.level.objects[d[0]] = obj self.colliders[obj.collider.group].append(obj.collider) ids = [o[0] for o in data[1]] for i in list(self.level.objects): obj = self.level.objects[i] obj.collider.update_occupied_squares(self.colliders) if i not in ids: if isinstance(obj, Destroyable): obj.destroy(self.colliders) elif isinstance(obj, Bullet): obj.destroy() def input(self, input_handler): input_handler.update(self.camera) if input_handler.quit: self.state = State.QUIT if self.state is State.PLAY: if 0 in self.players: input_handler.relative_mouse[:] = input_handler.mouse_position - self.players[ 0].shoulder for i, player in enumerate(self.players.values()): player.input(input_handler) if input_handler.keys_pressed[pygame.K_r]: self.reset_game() elif self.state is State.MENU: self.menu.input(input_handler) for i in range(len(input_handler.controllers)): if self.menu.selection_moved[i]: self.controller_id = i break elif self.state is State.PLAYER_SELECT: for i, controller in enumerate(input_handler.controllers): for pm in self.player_menus: if pm.controller_id == i: pm.input(input_handler, i) break else: for pm in self.player_menus: if pm.controller_id is None: pm.input(input_handler, i) if pm.controller_id == i: self.add_player(i) break if all(pm.target_state is State.PLAYER_SELECT for pm in self.player_menus): for i, controller in enumerate(input_handler.controllers): if controller.button_down['B']: self.state = State.MENU elif self.state is State.LAN: if self.network is not None: player = self.players[self.network_id] input_handler.relative_mouse[:] = input_handler.mouse_position - player.shoulder self.obj_id = player.object.id if player.object is not None else -1 player.input(input_handler) elif self.state is State.OPTIONS: self.options_menu.input(input_handler) def draw(self, screen, image_handler): if self.state in [State.PLAY, State.LAN]: screen.fill((0, 0, 0)) screen.blit( self.level.background, self.camera.world_to_screen(np.array([0, self.level.height]))) if self.option_handler.shadows: self.level.draw_shadow(screen, self.camera, image_handler) for p in self.players.values(): p.draw_shadow(screen, self.camera, image_handler, self.level.light) self.level.draw(screen, self.camera, image_handler) for player in self.players.values(): player.draw(screen, self.camera, image_handler) if self.option_handler.debug_draw: self.debug_draw(screen, image_handler) elif self.state is State.MENU: screen.fill((50, 50, 50)) self.menu.draw(screen, self.camera, image_handler) elif self.state is State.PLAYER_SELECT: screen.fill((50, 50, 50)) for pm in self.player_menus: pm.draw(screen, self.camera, image_handler) if pm.controller_id is not None: self.players[pm.controller_id].set_position(pm.position + 3 * basis(1)) self.players[pm.controller_id].on_ground = True self.players[pm.controller_id].animate(0.0) self.players[pm.controller_id].draw( screen, self.camera, image_handler) elif self.state is State.OPTIONS: screen.fill((50, 50, 50)) self.options_menu.draw(screen, self.camera, image_handler) def debug_draw(self, screen, image_handler): for player in self.players.values(): player.debug_draw(screen, self.camera, image_handler) self.level.debug_draw(screen, self.camera, image_handler) def play_sounds(self, sound_handler): if self.state in [State.PLAY, State.LAN]: for p in self.players.values(): p.play_sounds(sound_handler) self.level.play_sounds(sound_handler) elif self.state is State.OPTIONS: sound_handler.set_volume(self.options_menu.buttons[2].get_value()) sound_handler.set_music_volume( self.options_menu.buttons[3].get_value()) self.option_handler.sfx_volume = self.options_menu.buttons[ 2].get_value() self.option_handler.music_volume = self.options_menu.buttons[ 3].get_value() self.option_handler.save() self.menu.play_sounds(sound_handler) self.options_menu.play_sounds(sound_handler) else: self.menu.play_sounds(sound_handler) self.options_menu.play_sounds(sound_handler) for pm in self.player_menus: pm.play_sounds(sound_handler)