Пример #1
0
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)
Пример #2
0
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
Пример #3
0
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)