class DrawableWaypointCircle: def __init__(self, radius, position, ui_manager, order): self.ui_manager = ui_manager self.traversal_order = order self.radius = radius self.colour = pygame.Color("#FF6464") self.rectangle_colour = pygame.Color("#646464") self.color_key = (127, 33, 33) self.world_position = [ int(position[0] - self.radius), int(position[1] - self.radius) ] self.surface = pygame.Surface((self.radius * 2, self.radius * 2)) self.surface.fill(self.color_key) self.surface.set_colorkey(self.color_key) pygame.draw.circle(self.surface, self.colour, (self.radius, self.radius), self.radius) self.surface.set_alpha(100) self.rectangle_surface = pygame.Surface((32, 32)) self.rectangle_surface.fill(self.color_key) self.rectangle_surface.set_colorkey(self.color_key) pygame.draw.rect(self.rectangle_surface, self.rectangle_colour, pygame.Rect(0, 0, 32, 32)) self.rectangle_surface.set_alpha(100) self.position = [self.world_position[0], self.world_position[1]] # self.id_text = fonts[6].render(str(self.traversal_order), True, pygame.Color("#FFFFFF")) self.id_text_label = UILabel(pygame.Rect(self.position, (32, 32)), str(self.traversal_order), manager=self.ui_manager, object_id="#small_screen_text") def kill(self): self.id_text_label.kill() def update_offset_position(self, offset): self.position = [ self.world_position[0] - offset[0], self.world_position[1] - offset[1] ] self.id_text_label.rect.x = self.position[0] + 16 self.id_text_label.rect.y = self.position[1] + 16 def draw(self, screen): screen.blit(self.rectangle_surface, [self.position[0] + 16, self.position[1] + 16]) screen.blit(self.surface, self.position)
class MapEditor: def __init__(self, tiled_level, hud_rect, all_square_sprites, ui_manager): self.editing_layer = 0 self.tiled_level = tiled_level self.hud_rect = hud_rect self.all_square_sprites = all_square_sprites self.ui_manager = ui_manager self.current_levels = [ file for file in os.listdir("data/levels/") if os.path.isfile(os.path.join("data/levels/", file)) ] self.left_mouse_held = False self.right_mouse_held = False self.need_to_refresh_tiles = True self.default_tile = [ pygame.Rect(0, 0, 0, 0), self.tiled_level.tile_map[0][0], "grass_tile", True, None ] self.held_tile_data = self.default_tile self.held_ai_spawn = None self.held_turret_square = None self.rect_of_tile = None self.hovered_rec = None self.rotate_selected_tile_left = False self.rotate_selected_tile_right = False self.all_palette_tile_sprites = pygame.sprite.Group() self.all_ai_spawn_sprites = pygame.sprite.Group() self.palette_page = 0 self.should_increase_palette_page = False self.should_decrease_palette_page = False self.remove_turret_square_icon = None self.place_turret_square_icon = None self.remove_waypoint_icon = None self.place_waypoint_icon = None self.right_click_mode = "place_tile" self.palette_tiles = [] self.palette_ai_spawns = [] self.num_ai_spawns = 3 self.tiles_per_page = 26 all_tiles_and_ai = len( self.tiled_level.all_tile_data.keys()) + self.num_ai_spawns self.max_pages = int(math.ceil(all_tiles_and_ai / self.tiles_per_page)) self.refresh_palette_tiles() self.left_scroll_held = False self.right_scroll_held = False self.up_scroll_held = False self.down_scroll_held = False self.map_scroll_speed = 256.0 self.map_start_pos = self.tiled_level.find_player_start() self.map_position = [self.map_start_pos[0], self.map_start_pos[1]] # self.map_editor_instructions = MapEditorInstructionsWindow([362, 100, 300, 250], fonts) instructions_message = ( "Arrow keys to scroll map <br>" "Left mouse click to select tile from palette<br>" "Right mouse click to place tile<br>" "'>' and '<' to rotate selected tile<br>" "F5 or quit to save map<br>") self.instruction_message_window = UIMessageWindow( pygame.Rect((362, 100), (300, 250)), "Instructions", instructions_message, self.ui_manager) self.level_name_label = UILabel(pygame.Rect((462, 8), (100, 34)), self.tiled_level.level_name, self.ui_manager, object_id="#screen_text") self.make_new_button = UIButton( pygame.Rect(870, self.hud_rect[1] + 24, 100, 20), "Make New", self.ui_manager) self.tile_set_button = UIButton( pygame.Rect(870, self.hud_rect[1] + 49, 100, 20), "Switch Tiles", self.ui_manager) self.playable_area_display = PlayableAreaDisplay() self.visible_way_point_circles = [] self.refresh_visible_waypoint_circles() def end(self): if self.tile_set_button is not None: self.tile_set_button.kill() self.tile_set_button = None if self.make_new_button is not None: self.make_new_button.kill() self.make_new_button = None if self.level_name_label is not None: self.level_name_label.kill() self.level_name_label = None if self.instruction_message_window is not None: self.instruction_message_window.kill() self.instruction_message_window = None for circle in self.visible_way_point_circles: circle.kill() def refresh_visible_waypoint_circles(self): for circle in self.visible_way_point_circles: circle.kill() del circle self.visible_way_point_circles[:] = [] traversal_order = 1 if self.tiled_level.monster_walk_path.start_waypoint is not None: self.visible_way_point_circles.append( DrawableWaypointCircle( self.tiled_level.monster_walk_path.waypoint_radius, self.tiled_level.monster_walk_path.start_waypoint, self.ui_manager, traversal_order)) traversal_order += 1 for waypoint in self.tiled_level.monster_walk_path.waypoints: self.visible_way_point_circles.append( DrawableWaypointCircle( self.tiled_level.monster_walk_path.waypoint_radius, waypoint, self.ui_manager, traversal_order)) traversal_order += 1 def display_turret_placement_squares(self, screen): self.all_square_sprites.draw(screen) def increase_palette_pos(self, x_pos, y_pos): x_pos += self.tiled_level.tile_size[0] + 8 if x_pos > 800: x_pos = 40 y_pos += self.tiled_level.tile_size[1] + 8 return x_pos, y_pos def refresh_palette_tiles(self): self.all_palette_tile_sprites.empty() self.palette_tiles[:] = [] self.palette_ai_spawns[:] = [] x_pos = 40 y_pos = 40 sorted_tile_keys = sorted(self.tiled_level.all_tile_data.keys()) display_tile = self.palette_page * self.tiles_per_page max_tile = (self.palette_page * self.tiles_per_page) + self.tiles_per_page min_tile = len(sorted_tile_keys) + self.num_ai_spawns while display_tile < min_tile and display_tile < max_tile: if display_tile < len(sorted_tile_keys): tile_data = sorted_tile_keys[display_tile] self.palette_tiles.append( Tile([self.hud_rect[0] + x_pos, self.hud_rect[1] + y_pos], 0, self.tiled_level.all_tile_data[tile_data], self.editing_layer)) display_tile += 1 else: self.remove_turret_square_icon = RemoveTurretSquareIcon( [self.hud_rect[0] + x_pos, self.hud_rect[1] + y_pos], self.all_palette_tile_sprites) x_pos, y_pos = self.increase_palette_pos(x_pos, y_pos) display_tile += 1 self.place_turret_square_icon = PlaceTurretSquareIcon( [self.hud_rect[0] + x_pos, self.hud_rect[1] + y_pos], self.all_palette_tile_sprites) x_pos, y_pos = self.increase_palette_pos(x_pos, y_pos) display_tile += 1 self.remove_waypoint_icon = RemoveWaypointIcon( [self.hud_rect[0] + x_pos, self.hud_rect[1] + y_pos], self.all_palette_tile_sprites) x_pos, y_pos = self.increase_palette_pos(x_pos, y_pos) display_tile += 1 self.place_waypoint_icon = PlaceWaypointIcon( [self.hud_rect[0] + x_pos, self.hud_rect[1] + y_pos], self.all_palette_tile_sprites) x_pos, y_pos = self.increase_palette_pos(x_pos, y_pos) display_tile += 1 display_tile += self.num_ai_spawns x_pos, y_pos = self.increase_palette_pos(x_pos, y_pos) for tile in self.palette_tiles: self.all_palette_tile_sprites.add(tile) # for aiSpawn in self.paletteAISpawns: # self.allPaletteTileSprites.add(aiSpawn.sprite) def run(self, screen, background, all_tile_sprites, hud_rect, time_delta): running = True for event in pygame.event.get(): self.ui_manager.process_events(event) if event.type == USEREVENT: if event.user_type == "ui_button_pressed": if event.ui_element == self.make_new_button: new_level_num = len(self.current_levels) + 1 new_level_name = "Level " + str(new_level_num) self.tiled_level.change_level_name_and_save( new_level_name) self.level_name_label.set_text( self.tiled_level.level_name) elif event.ui_element == self.tile_set_button: self.tiled_level.toggle_tile_map() for tile in self.palette_tiles: tile.reload_tile_image_from_data( self.tiled_level.all_tile_data) if event.type == QUIT: self.tiled_level.save_tiles() running = False if event.type == MOUSEBUTTONDOWN: if event.button == 1: self.left_mouse_held = True if event.button == 3: self.right_mouse_held = True if event.type == MOUSEBUTTONUP: if event.button == 1: self.left_mouse_held = False if event.button == 3: self.right_mouse_held = False if event.type == KEYDOWN: if event.key == K_ESCAPE: self.tiled_level.save_tiles() running = False if event.key == K_F5: self.tiled_level.save_tiles() if event.key == K_PERIOD: self.rotate_selected_tile_right = True if event.key == K_COMMA: self.rotate_selected_tile_left = True if event.key == K_UP: self.up_scroll_held = True if event.key == K_DOWN: self.down_scroll_held = True if event.key == K_LEFT: self.left_scroll_held = True if event.key == K_RIGHT: self.right_scroll_held = True if event.key == K_1: self.editing_layer = 1 if event.key == K_0: self.editing_layer = 0 if event.key == K_RIGHTBRACKET: self.should_increase_palette_page = True if event.key == K_LEFTBRACKET: self.should_decrease_palette_page = True if event.type == KEYUP: if event.key == K_UP: self.up_scroll_held = False if event.key == K_DOWN: self.down_scroll_held = False if event.key == K_LEFT: self.left_scroll_held = False if event.key == K_RIGHT: self.right_scroll_held = False self.ui_manager.update(time_delta) if self.should_increase_palette_page: self.should_increase_palette_page = False if self.palette_page < self.max_pages - 1: self.palette_page += 1 else: self.palette_page = 0 # loop back round self.refresh_palette_tiles() if self.should_decrease_palette_page: self.should_decrease_palette_page = False if self.palette_page > 0: self.palette_page -= 1 else: self.palette_page = self.max_pages - 1 # loop back round self.refresh_palette_tiles() if self.up_scroll_held: self.map_position[1] -= self.map_scroll_speed * time_delta if self.map_position[1] < self.tiled_level.initial_screen_offset[1]: self.map_position[1] = self.tiled_level.initial_screen_offset[ 1] if self.down_scroll_held: self.map_position[1] += self.map_scroll_speed * time_delta y_limit = self.tiled_level.level_pixel_size[ 1] - self.tiled_level.initial_screen_offset[1] + self.hud_rect[ 1] if self.map_position[1] > y_limit: self.map_position[1] = y_limit if self.left_scroll_held: self.map_position[0] -= self.map_scroll_speed * time_delta if self.map_position[0] < self.tiled_level.initial_screen_offset[0]: self.map_position[0] = self.tiled_level.initial_screen_offset[ 0] if self.right_scroll_held: self.map_position[0] += self.map_scroll_speed * time_delta max_x = self.tiled_level.level_pixel_size[ 0] - self.tiled_level.initial_screen_offset[0] if self.map_position[0] > max_x: self.map_position[0] = max_x if self.rotate_selected_tile_right and self.held_tile_data[ 4] is not None: self.rotate_selected_tile_right = False self.held_tile_data[4].rotate_tile_right() self.need_to_refresh_tiles = True if self.rotate_selected_tile_left and self.held_tile_data[ 4] is not None: self.rotate_selected_tile_left = False self.held_tile_data[4].rotate_tile_left() self.need_to_refresh_tiles = True if self.left_mouse_held: click_pos = pygame.mouse.get_pos() if self.is_inside_hud(click_pos, hud_rect): self.held_tile_data = self.get_palette_tile_data_at_pos( click_pos) if self.held_tile_data is None: if self.remove_turret_square_icon.is_inside(click_pos): self.right_click_mode = "remove_turret_square" elif self.place_turret_square_icon.is_inside(click_pos): self.right_click_mode = "place_turret_square" elif self.remove_waypoint_icon.is_inside(click_pos): self.right_click_mode = "remove_waypoint" elif self.place_waypoint_icon.is_inside(click_pos): self.right_click_mode = "place_waypoint" else: self.right_click_mode = "place_tile" else: self.held_tile_data = self.tiled_level.get_tile_data_at_pos( click_pos, self.editing_layer) if self.right_mouse_held: click_pos = pygame.mouse.get_pos() if self.is_inside_hud(click_pos, hud_rect): pass else: angle = 0 if self.right_click_mode == "place_tile" and self.held_tile_data is not None: if self.held_tile_data[4] is not None: angle = self.held_tile_data[4].angle self.rect_of_tile = self.tiled_level.set_tile_at_pos( click_pos, self.held_tile_data[2], angle, self.editing_layer) elif self.right_click_mode == "place_ai" and self.held_ai_spawn is not None: self.tiled_level.add_ai_spawn_at_pos( click_pos, self.held_ai_spawn) elif self.right_click_mode == "remove_ai": self.tiled_level.remove_ai_spawn_at_pos(click_pos) elif self.right_click_mode == "remove_turret_square": self.tiled_level.remove_turret_square_at_pos(click_pos) elif self.right_click_mode == "place_turret_square": self.tiled_level.place_turret_square_at_pos(click_pos) elif self.right_click_mode == "remove_waypoint": self.tiled_level.remove_waypoint_at_pos(click_pos) self.refresh_visible_waypoint_circles() elif self.right_click_mode == "place_waypoint": if self.tiled_level.place_waypoint_at_pos(click_pos): self.refresh_visible_waypoint_circles() if self.tiled_level.update_offset_position(self.map_position, all_tile_sprites): self.need_to_refresh_tiles = True self.all_ai_spawn_sprites.empty() for ai_spawn in self.tiled_level.ai_spawns: self.all_ai_spawn_sprites.add(ai_spawn) self.hovered_rec = self.tiled_level.get_tile_data_at_pos( pygame.mouse.get_pos(), self.editing_layer)[0] screen.blit(background, (0, 0)) # draw the background all_tile_sprites.draw(screen) self.all_ai_spawn_sprites.draw(screen) self.playable_area_display.draw(screen, self.tiled_level.position_offset) self.display_turret_placement_squares(screen) for waypoint_circles in self.visible_way_point_circles: waypoint_circles.update_offset_position( self.tiled_level.position_offset) waypoint_circles.draw(screen) if self.held_tile_data is not None: if not self.held_tile_data[3]: pygame.draw.rect(screen, pygame.Color("#FF6464"), self.held_tile_data[0], 1) if self.hovered_rec is not None: pygame.draw.rect(screen, pygame.Color("#FFE164"), self.hovered_rec, 1) # draw the hud pygame.draw.rect(screen, pygame.Color("#3C3C3C"), hud_rect, 0) self.all_palette_tile_sprites.draw(screen) if self.held_tile_data is not None: if self.held_tile_data[3]: pygame.draw.rect(screen, pygame.Color("#FF6464"), self.held_tile_data[0], 1) self.ui_manager.draw_ui(screen) return running @staticmethod def is_inside_hud(pos, hud_rect): if hud_rect[0] <= pos[0] and hud_rect[1] <= pos[1]: if hud_rect[0] + hud_rect[2] > pos[ 0] and hud_rect[1] + hud_rect[3] > pos[1]: return True return False def get_palette_tile_data_at_pos(self, click_pos): for tile in self.palette_tiles: x_min = tile.rect[0] x_max = tile.rect[0] + tile.rect[2] y_min = tile.rect[1] y_max = tile.rect[1] + tile.rect[3] if x_min <= click_pos[0] < x_max: if y_min <= click_pos[1] < y_max: return [tile.rect, tile.image, tile.tile_id, True, None] return None def get_ai_spawn_data_at_pos(self, click_pos): for ai_spawn in self.palette_ai_spawns: x_min = ai_spawn.rect[0] x_max = ai_spawn.rect[0] + ai_spawn.rect[2] y_min = ai_spawn.rect[1] y_max = ai_spawn.rect[1] + ai_spawn.rect[3] if x_min <= click_pos[0] < x_max: if y_min <= click_pos[1] < y_max: return ai_spawn return None
class GameState(BaseAppState): def __init__(self, ui_manager: pygame_gui.UIManager, screen_surface, screen_data, state_manger): super().__init__('game', 'main_menu', state_manger) self.ui_manager = ui_manager self.screen_surface = screen_surface self.screen_data = screen_data self.background = None self.level_to_load_path = None self.level_to_load = None # objects that do stuff self.collision_grid = None self.tiled_level = None self.monster_wave_spawner = None self.splat_loader = None self.player_resources = None self.should_redraw_static_sprites = False # game sub state booleans self.is_play_game = False self.restart_game = False self.is_setup = False self.is_game_over = False self.should_show_count_down_message = False self.upgrade_hud_active = False self.hud_panel = None # labels self.fps_counter_label = None self.wave_display_label = None self.count_down_message_label = None self.health_label = None self.cash_label = None self.win_message_label = None self.play_again_message_label = None self.frame_rates = deque([]) # turrets self.active_upgrade_turret = None self.mouse_active_turret = None # lists of things self.explosions = [] self.new_explosions = [] self.bullets = [] self.turrets = [] self.monsters = [] self.hud_buttons = [] # sprite groups self.static_sprite_surface = None self.all_tile_sprites = None self.all_square_sprites = None self.all_monster_sprites = None self.all_turret_sprites = None self.all_bullet_sprites = None self.all_explosion_sprites = None self.splat_sprites = None # images self.explosions_sprite_sheet = None self.image_atlas = None self.turret_costs = TurretCosts() self.count_down_message = "" self.win_message = "" # timer self.setup_time = 10.0 self.setup_accumulator = 0.0 self.hud_rect = None def start(self): self.hud_rect = pygame.Rect(0, self.screen_data.screen_size[1] - 128, self.screen_data.screen_size[0], 128) self.player_resources = PlayerResources() self.background = pygame.Surface(self.screen_surface.get_size()) self.background = self.background.convert(self.screen_surface) self.background.fill((95, 140, 95)) self.should_redraw_static_sprites = True self.static_sprite_surface = pygame.Surface( self.screen_surface.get_size()) self.all_tile_sprites = pygame.sprite.Group() self.all_square_sprites = pygame.sprite.Group() self.all_monster_sprites = pygame.sprite.Group() self.all_turret_sprites = pygame.sprite.Group() self.all_bullet_sprites = pygame.sprite.Group() self.all_explosion_sprites = pygame.sprite.Group() self.splat_sprites = pygame.sprite.Group() self.explosions_sprite_sheet = pygame.image.load( "images/explosions.png").convert_alpha() self.image_atlas = pygame.image.load( "images/image_atlas.png").convert_alpha() self.splat_loader = SplatLoader() self.level_to_load_path = self.incoming_transition_data[ 'selected_level_path'] self.fps_counter_label = UILabel(pygame.Rect((900, 10), (100, 40)), "0 FPS", manager=self.ui_manager, object_id="#screen_text") self.wave_display_label = None grid_size = 64 screen_filling_number_of_grid_squares = [ int(self.screen_data.screen_size[0] / grid_size), int(self.screen_data.screen_size[1] / grid_size) ] self.collision_grid = CollisionGrid( screen_filling_number_of_grid_squares, grid_size) # clear level self.tiled_level = TiledLevel( self.level_to_load_path, [40, 21], self.all_tile_sprites, self.all_monster_sprites, self.all_square_sprites, self.image_atlas, self.monsters, self.screen_data, self.explosions_sprite_sheet) self.tiled_level.load_tiles() self.tiled_level.update_offset_position( self.tiled_level.find_player_start(), self.all_tile_sprites) self.monster_wave_spawner = MonsterWaveSpawner( self.monsters, self.tiled_level.monster_walk_path, 10, self.all_monster_sprites, self.image_atlas, self.collision_grid, self.splat_loader, self.ui_manager) self.is_play_game = True self.restart_game = True self.should_redraw_static_sprites = True self.hud_panel = HUDPanel(self.hud_rect, self.ui_manager, self.player_resources, self.turret_costs) def end(self): if self.fps_counter_label is not None: self.fps_counter_label.kill() self.fps_counter_label = None if self.count_down_message_label is not None: self.count_down_message_label.kill() self.count_down_message_label = None if self.wave_display_label is not None: self.wave_display_label.kill() self.wave_display_label = None if self.hud_panel is not None: self.hud_panel.kill() self.hud_panel = None if self.win_message_label is not None: self.win_message_label.kill() self.win_message_label = None if self.play_again_message_label is not None: self.play_again_message_label.kill() self.play_again_message_label = None for sprite in self.all_monster_sprites.sprites(): sprite.kill() self.all_monster_sprites.empty() self.all_turret_sprites.empty() self.all_bullet_sprites.empty() self.all_explosion_sprites.empty() def run(self, surface, time_delta): if not self.restart_game and self.is_setup: if self.setup_accumulator >= self.setup_time: self.is_setup = False self.setup_accumulator = 0.0 elif self.setup_accumulator >= (self.setup_time - 6.0): seconds_string = str( int(self.setup_time - self.setup_accumulator)) self.count_down_message = "First wave in " + seconds_string + " seconds" self.should_show_count_down_message = True self.setup_accumulator += time_delta if self.count_down_message_label is None: count_down_message_label_rect = pygame.Rect((400, 10), (250, 40)) count_down_message_label_rect.centerx = self.screen_data.screen_size[ 0] / 2 count_down_message_label_rect.centery = 24 self.count_down_message_label = UILabel( count_down_message_label_rect, self.count_down_message, manager=self.ui_manager, object_id="#screen_text") else: self.setup_accumulator += time_delta remaining_time = str( int(self.setup_time - self.setup_accumulator)) self.count_down_message = "Setup Time remaining: " + remaining_time + " seconds" self.should_show_count_down_message = True if self.count_down_message_label is None: count_down_message_label_rect = pygame.Rect((400, 10), (250, 40)) count_down_message_label_rect.centerx = self.screen_data.screen_size[ 0] / 2 count_down_message_label_rect.centery = 24 self.count_down_message_label = UILabel( count_down_message_label_rect, self.count_down_message, manager=self.ui_manager, object_id="#screen_text") elif self.restart_game: self.restart_game = False # clear all stuff self.explosions[:] = [] self.new_explosions[:] = [] self.bullets[:] = [] self.turrets[:] = [] self.monsters[:] = [] self.hud_buttons[:] = [] self.all_monster_sprites.empty() self.all_turret_sprites.empty() self.all_bullet_sprites.empty() self.all_explosion_sprites.empty() self.tiled_level.reset_squares() # reset player resources self.player_resources = PlayerResources() self.hud_panel.player_resources = self.player_resources self.is_game_over = False self.is_setup = True self.setup_accumulator = 0.0 self.monster_wave_spawner = MonsterWaveSpawner( self.monsters, self.tiled_level.monster_walk_path, 10, self.all_monster_sprites, self.image_atlas, self.collision_grid, self.splat_loader, self.ui_manager) self.mouse_active_turret = None self.active_upgrade_turret = None self.upgrade_hud_active = False self.should_show_count_down_message = False if self.count_down_message_label is not None: self.count_down_message_label.kill() self.count_down_message_label = None if self.wave_display_label is not None: self.wave_display_label.kill() self.wave_display_label = None if self.win_message_label is not None: self.win_message_label.kill() self.win_message_label = None if self.play_again_message_label is not None: self.play_again_message_label.kill() self.play_again_message_label = None self.hud_panel.display_normal_hud() elif self.is_game_over: self.should_show_count_down_message = False if self.count_down_message_label is not None: self.count_down_message_label.kill() self.count_down_message_label = None else: self.monster_wave_spawner.update(time_delta, self.tiled_level.position_offset) if self.wave_display_label is not None: self.wave_display_label.kill() self.wave_display_label = None if self.monster_wave_spawner.should_show_wave_countdown: self.should_show_count_down_message = True self.count_down_message = self.monster_wave_spawner.count_down_message if self.count_down_message_label is None: count_down_message_label_rect = pygame.Rect((400, 10), (250, 40)) count_down_message_label_rect.centerx = self.screen_data.screen_size[ 0] / 2 count_down_message_label_rect.centery = 24 self.count_down_message_label = UILabel( count_down_message_label_rect, self.count_down_message, manager=self.ui_manager, object_id="#screen_text") else: self.should_show_count_down_message = False if self.count_down_message_label is not None: self.count_down_message_label.kill() self.count_down_message_label = None if self.wave_display_label is None: current_wave = str( self.monster_wave_spawner.current_wave_number) max_wave = str( self.monster_wave_spawner.maximum_wave_number) wave_display_label_rect = pygame.Rect((400, 10), (100, 40)) wave_display_label_rect.centerx = self.screen_data.screen_size[ 0] / 2 wave_display_label_rect.centery = 24 self.wave_display_label = UILabel(wave_display_label_rect, "Wave " + current_wave + "/" + max_wave, manager=self.ui_manager, object_id="#screen_text") if not self.is_game_over and self.player_resources.current_base_health <= 0: self.is_game_over = True self.win_message = "You have been defeated!" self.win_message_label = UILabel(pygame.Rect((347, 200), (330, 70)), self.win_message, manager=self.ui_manager, object_id="#win_message_text") self.play_again_message_label = UILabel( pygame.Rect((362, 250), (300, 70)), "Play Again? Press 'Y' to restart", manager=self.ui_manager, object_id="#screen_text") on_final_wave = self.monster_wave_spawner.current_wave_number == self.monster_wave_spawner.maximum_wave_number if not self.is_game_over and on_final_wave and len(self.monsters) == 0: self.is_game_over = True self.win_message = "You are victorious!" self.win_message_label = UILabel(pygame.Rect((347, 200), (330, 70)), self.win_message, manager=self.ui_manager, object_id="#win_message_text") self.play_again_message_label = UILabel( pygame.Rect((362, 250), (300, 70)), "Play Again? Press 'Y' to restart", manager=self.ui_manager, object_id="#screen_text") self.all_turret_sprites.empty() self.all_bullet_sprites.empty() self.all_explosion_sprites.empty() # handle UI and inout events for event in pygame.event.get(): if event.type == pygame.QUIT: self.trigger_transition() if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.trigger_transition() if event.key == pygame.K_g: self.monster_wave_spawner.wave_points = 1000 self.monster_wave_spawner.wave_time_accumulator = 5.0 if event.key == pygame.K_y: if self.is_game_over: self.restart_game = True self.ui_manager.process_events(event) if event.type == pygame.USEREVENT: if event.user_type == "ui_button_pressed": if event.ui_object_id == "#gun_turret_button": if self.turret_costs.gun <= self.player_resources.current_cash: new_turret = GunTurret( pygame.mouse.get_pos(), self.turret_costs.gun, self.explosions_sprite_sheet, self.image_atlas, self.collision_grid) self.mouse_active_turret = new_turret self.turrets.append(new_turret) elif event.ui_object_id == "#flame_turret_button": if self.turret_costs.flamer <= self.player_resources.current_cash: new_turret = FlameTurret( pygame.mouse.get_pos(), self.turret_costs.flamer, self.explosions_sprite_sheet, self.image_atlas, self.collision_grid) self.mouse_active_turret = new_turret self.turrets.append(new_turret) elif event.ui_object_id == "#missile_turret_button": if self.turret_costs.missile <= self.player_resources.current_cash: new_turret = MissileTurret( pygame.mouse.get_pos(), self.turret_costs.missile, self.explosions_sprite_sheet, self.image_atlas, self.collision_grid) self.mouse_active_turret = new_turret self.turrets.append(new_turret) elif event.ui_object_id == "#slow_turret_button": if self.turret_costs.slow <= self.player_resources.current_cash: new_turret = SlowTurret(pygame.mouse.get_pos(), self.turret_costs.slow, self.image_atlas) self.mouse_active_turret = new_turret self.turrets.append(new_turret) elif event.ui_object_id == "#laser_turret_button": if self.turret_costs.laser <= self.player_resources.current_cash: new_turret = LaserTurret(pygame.mouse.get_pos(), self.turret_costs.laser, self.image_atlas) self.mouse_active_turret = new_turret self.turrets.append(new_turret) elif event.ui_object_id == "#upgrade_button": if self.active_upgrade_turret is not None: if self.player_resources.current_cash >= self.active_upgrade_turret.get_upgrade_cost( ): self.player_resources.current_cash -= self.active_upgrade_turret.get_upgrade_cost( ) self.active_upgrade_turret.upgrade() self.upgrade_hud_active = False self.active_upgrade_turret = None self.hud_panel.display_normal_hud() elif event.ui_object_id == "#sell_button": if self.active_upgrade_turret is not None: self.player_resources.current_cash += self.active_upgrade_turret.get_sell_value( ) for square in self.tiled_level.turret_squares: if square.rect.collidepoint( self.active_upgrade_turret.position): square.occupied = False self.turrets.remove(self.active_upgrade_turret) self.upgrade_hud_active = False self.active_upgrade_turret = None self.hud_panel.display_normal_hud() if not self.is_game_over and event.type == pygame.MOUSEBUTTONDOWN: if event.button == 3: if self.mouse_active_turret is not None: self.turrets[:] = [ turret for turret in self.turrets if turret is not self.mouse_active_turret ] self.mouse_active_turret = None if self.upgrade_hud_active: self.upgrade_hud_active = False self.hud_panel.display_normal_hud() if event.button == 1: if self.mouse_active_turret is not None: placed_turret = False for square in self.tiled_level.turret_squares: if self.mouse_active_turret is not None: if square.rect.collidepoint( pygame.mouse.get_pos( )) and not square.occupied: self.mouse_active_turret.set_position( square.position) placed_turret = True square.occupied = True self.player_resources.current_cash -= self.mouse_active_turret.build_cost self.mouse_active_turret.placed = True self.mouse_active_turret = None if not placed_turret: self.turrets[:] = [ turret for turret in self.turrets if turret is not self.mouse_active_turret ] self.mouse_active_turret = None else: for turret in self.turrets: if turret.rect.collidepoint( pygame.mouse.get_pos()): if turret.get_level() < turret.get_max_level(): self.upgrade_hud_active = True self.active_upgrade_turret = turret self.hud_panel.display_upgrade_hud( self.active_upgrade_turret) if not self.is_game_over and self.mouse_active_turret is not None: is_over_square = False for square in self.tiled_level.turret_squares: if square.rect.collidepoint( pygame.mouse.get_pos()) and not square.occupied: self.mouse_active_turret.set_position(square.position) self.mouse_active_turret.show_radius = True is_over_square = True if not is_over_square: self.mouse_active_turret.set_position(pygame.mouse.get_pos()) self.mouse_active_turret.show_radius = False for bullet in self.bullets: self.all_bullet_sprites = bullet.update_sprite( self.all_bullet_sprites) bullet.update_movement_and_collision(self.monsters, time_delta, self.new_explosions, self.explosions) self.bullets[:] = [ bullet for bullet in self.bullets if not bullet.should_die ] for monster in self.monsters: monster.update_movement_and_collision( time_delta, self.player_resources, self.tiled_level.position_offset, self.splat_sprites) monster.update_sprite() self.monsters[:] = [ monster for monster in self.monsters if not monster.should_die ] self.new_explosions[:] = [] for turret in self.turrets: turret.update_movement_and_collision(time_delta, self.monsters, self.bullets, pygame.mouse.get_pos()) self.all_turret_sprites = turret.update_sprite( self.all_turret_sprites) for explosion in self.explosions: self.all_explosion_sprites = explosion.update_sprite( self.all_explosion_sprites, time_delta) self.explosions[:] = [ explosion for explosion in self.explosions if not explosion.should_die ] self.splat_sprites.update(time_delta) self.collision_grid.update_shape_grid_positions() self.collision_grid.check_collisions() self.ui_manager.update(time_delta) for collided_shape in self.collision_grid.shapes_collided_this_frame: if collided_shape.owner is not None: collided_shape.owner.react_to_collision() if self.should_redraw_static_sprites: self.should_redraw_static_sprites = False self.static_sprite_surface.blit(self.background, (0, 0)) # draw the background self.all_tile_sprites.draw(self.static_sprite_surface) surface.blit(self.static_sprite_surface, (0, 0)) self.splat_sprites.draw(surface) if self.mouse_active_turret is not None: self.all_square_sprites.draw(surface) self.all_monster_sprites.draw(surface) self.all_turret_sprites.draw(surface) self.all_bullet_sprites.draw(surface) self.all_explosion_sprites.draw(surface) # collision debug # for monster in monsters: # monster.draw_collision_circle(screen) # for bullet in bullets: # bullet.draw_collision_rect(screen) for turret in self.turrets: if turret.show_radius: turret.draw_radius_circle(surface) if time_delta > 0.0 and self.fps_counter_label is not None: if len(self.frame_rates) < 300: self.frame_rates.append(1.0 / time_delta) else: self.frame_rates.popleft() self.frame_rates.append(1.0 / time_delta) fps = sum(self.frame_rates) / len(self.frame_rates) self.fps_counter_label.set_text("FPS: {:.2f}".format(fps)) if self.should_show_count_down_message and self.count_down_message_label is not None: self.count_down_message_label.set_text(self.count_down_message) if self.wave_display_label is not None and self.monster_wave_spawner.has_changed_wave: current_wave = str(self.monster_wave_spawner.current_wave_number) max_wave = str(self.monster_wave_spawner.maximum_wave_number) self.wave_display_label.set_text("Wave " + current_wave + "/" + max_wave) self.ui_manager.draw_ui(surface)