main
1import pygame, sys, os, button 2from tower import Tower 3from enemy import Enemy 4from waves import Wave 5 6FPS = 60 7fpsClock = pygame.time.Clock() 8window_width = 800 9window_height = 600 10 11pygame.display.set_caption('Tower Defense Game') 12pygame.init() 13window = pygame.display.set_mode((window_width, window_height)) 14 15 16class StartScreen: 17 """ 18 A class to represent the start screen of the Tower Defense Game. 19 Attributes: 20 21 Attributes: 22 window: The window surface where the start screen will be rendered. 23 background: The background image of the start screen. 24 font: The font used for rendering text on the start screen. 25 title_text: The rendered text surface for the game title. 26 start_text: The rendered text surface for the start button. 27 start_button_rect: The rectangle area of the start button. 28 29 Methods: 30 render(): Renders the start screen with the background, title, and start button. 31 check_for_click(): Checks for mouse click events and returns True if the start button is clicked. 32 """ 33 def __init__(self, window): 34 """ 35 Initializes the main game window and loads the start screen assets. 36 Args: 37 window: The window surface where the start screen will be rendered. 38 """ 39 self.window = window 40 self.background = pygame.image.load(os.path.join('game_assests', 'start_screen_background.jpg')) 41 self.background = pygame.transform.scale(self.background, (window_width, window_height)) 42 self.font = pygame.font.SysFont(None, 55) 43 self.title_text = self.font.render('Tower Defense Game', True, (255, 255, 255)) 44 self.start_text = self.font.render('Click to Start', True, (255, 255, 255)) 45 46 def render(self): 47 """ 48 Renders the start screen with the background, title, and start button. 49 """ 50 self.window.blit(self.background, (0, 0)) 51 self.window.blit(self.title_text, (window_width // 2 - self.title_text.get_width() // 2, 100)) 52 start_text_rect = self.start_text.get_rect(center=(window_width // 2, 400)) 53 rect_x = start_text_rect.x - 10 54 rect_y = start_text_rect.y - 10 55 rect_width = start_text_rect.width + 20 56 rect_height = start_text_rect.height + 20 57 58 pygame.draw.rect(self.window, (39, 145, 39), (rect_x, rect_y, rect_width, rect_height)) 59 self.window.blit(self.start_text, start_text_rect.topleft) 60 pygame.display.update() 61 self.start_button_rect = pygame.Rect(rect_x, rect_y, rect_width, rect_height) 62 63 def check_for_click(self): 64 """ 65 Checks for mouse click events and returns True if the start button is clicked. 66 67 Returns: 68 bool: True if the start button is clicked, False otherwise. 69 """ 70 for event in pygame.event.get(): 71 if event.type == pygame.QUIT: 72 pygame.quit() 73 sys.exit() 74 if event.type == pygame.MOUSEBUTTONDOWN: 75 mouse_pos = pygame.mouse.get_pos() 76 if self.start_button_rect.collidepoint(mouse_pos): 77 return True 78 return False 79 80 81class MainGameScreen: 82 """The game screen which shows the map and handles the generation of enemies, towers, and player stats.""" 83 def __init__(self, window): 84 self.window = window 85 """The window for the game.""" 86 self.background = pygame.image.load(os.path.join('game_assests', 'map_one.png')) 87 """Loads the image.""" 88 self.background = pygame.transform.scale(self.background, (window_width - 100, window_height - 100)) 89 """Scales the image in self.background appropriately and then saves it back to self.background.""" 90 self.font = pygame.font.SysFont(None, 22) 91 """Sets the font style to be used.""" 92 self.health = 0 93 """Holds the amount of health the player has remaining.""" 94 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 95 """Renders the health text so the player knows how much health they have remaining.""" 96 self.money = 0 97 """Holds the amount of money the player has""" 98 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 99 """Renders the money text so the player knows how much money they have remaining.""" 100 101 # Pause button 102 pause_img = pygame.image.load(os.path.join('game_assests', 'Play-Pause.png')).convert_alpha() 103 """The image of the pause button.""" 104 self.pause_button = button.Button(710, 510, pause_img, 0.15) 105 """Makes the pause button a button to be clicked, using the image stored in pause_img.""" 106 self.pause = True 107 """Sets pause to True when initially ran.""" 108 109 # Map Variables 110 self.map_path = ((0, 274), (116, 274), (116, 124), (258, 124), (258, 322), (444, 322), 111 (444, 226), (700, 226), (800, 226)) 112 """Sets the path for enemies to traverse.""" 113 self.collision_rects = [ 114 pygame.Rect(min(0, 140), min(248, 300), max(0, 140) - min(0, 140), max(248, 300) - min(248, 300)), 115 pygame.Rect(min(140, 96), min(300, 100), max(140, 96) - min(140, 96), max(300, 100) - min(300, 100)), 116 pygame.Rect(min(96, 278), min(100, 146), max(96, 278) - min(96, 278), max(146, 100) - min(100, 146)), 117 pygame.Rect(min(278, 230), min(146, 348), max(278, 230) - min(278, 230), max(348, 146) - min(146, 348)), 118 pygame.Rect(min(230, 470), min(348, 299), max(470, 230) - min(230, 470), max(348, 299) - min(299, 348)), 119 pygame.Rect(min(470, 418), min(299, 200), max(470, 418) - min(470, 418), max(299, 200) - min(200, 299)), 120 pygame.Rect(min(418, 700), min(200, 250), max(700, 418) - min(418, 700), max(250, 200) - min(200, 250)), 121 ] 122 """Sets up some collision spots, where the towers cannot be placed. (ie. the enemy path)""" 123 124 # Tower Variables 125 self.grid_active = False 126 """Used to show the grid""" 127 self.grid_size = 14 # Will remove later, some things still depend on this. 128 """Determines the grid size.""" 129 self.tower_size = 3 130 """Determines the tower size""" 131 self.selected_tower = None 132 """Determines which tower the player has selected.""" 133 self.placed_towers = [] 134 """A list to hold all the towers that have been placed.""" 135 136 # Wave and Enemy 137 self.wave = 1 138 """Stores which wave number is currently displayed to the player""" 139 self._waves = [] # list of waves 140 self._time_since_previous_spawn = 0 141 self._enemy_list = [] 142 self._current_wave = 0 143 self.wave_text = self.font.render(f'Wave: {self.wave}', True, (255, 255, 255)) 144 """Renders the current wave number.""" 145 146 for wave_number in range(1, 60): 147 """Loop for generating waves, changes are planned to further improve.""" 148 enemy_count = 3 * wave_number 149 """Determines amount of enemies to be spawned, dependent on wave number.""" 150 enemy_hp = 10 + (5 * wave_number) 151 """Increases health of the enemies, dependent on wave number.""" 152 wave_data = [ 153 Enemy('circle', enemy_hp, 1, 1, self.map_path) for i in range(enemy_count) 154 ] 155 """Generate wave data, to be added to self._waves""" 156 self._waves.append(Wave(wave_data, 60)) 157 158 # Debugging Variables 159 self.debug = False 160 """Used to help debug.""" 161 self.cursor_text = self.font.render('', True, (255, 255, 255)) 162 """Displays the text for the cursor.""" 163 164 def render(self): 165 """Used to render the game and enemies.""" 166 self.window.blit(self.background, (0, 0)) 167 """Sets window""" 168 169 class Rectangle(): 170 """Creates rectangles for the UI.""" 171 def __init__(self, x, y, width, height, color): 172 self.x = x 173 """The x position of the rectangle.""" 174 self.y = y 175 """The y position of the rectangle""" 176 self.width = width 177 """The width of the rectangle.""" 178 self.height = height 179 """The height of the rectangle.""" 180 self.color = color 181 """The color of the rectangle.""" 182 183 def draw(self): 184 """Draws the rectangle.""" 185 pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height)) 186 187 # create menus, tower slots, and tower image 188 bottom_bar = Rectangle(0, (window_height - 100), window_width, 100, (150, 150, 150)) 189 """Menu on the bottom, currently hold nothing.""" 190 side_bar = Rectangle((window_width - 100), 0, 100, window_height, (150, 150, 150)) 191 """Menu on the right side, shows player stats and towers that can be used.""" 192 tower_boxes = [ 193 Rectangle(705, 100, 90, 90, (100, 100, 100)), 194 Rectangle(705, 200, 90, 90, (100, 100, 100)), 195 Rectangle(705, 300, 90, 90, (100, 100, 100)), 196 Rectangle(705, 400, 90, 90, (100, 100, 100)) 197 ] 198 """Makes the rectangles for the tower boxes.""" 199 tower_image = pygame.image.load(os.path.join("game_assests", "tower.png")) 200 """Loads tower image.""" 201 tower_image = pygame.transform.scale(tower_image, (90, 90)) 202 """Scales tower image.""" 203 204 if self.grid_active: 205 """Checks if grid is currently active, then renders a preview of the tower selected.""" 206 self.render_tower_preview() 207 208 if self.selected_tower: 209 """Renders the attack radius of the selected tower.""" 210 self.draw_radius(self.selected_tower._position, self.selected_tower.get_range(), (128, 128, 128, 100)) 211 # Logic for upgrades and tower selection info should go here 212 213 self.tower1_price = self.font.render(f'$200', True, (255, 255, 255)) 214 """Renders the price of tower1.""" 215 216 for tower in self.placed_towers: 217 """Renders each placed tower.""" 218 tower.render(self.window) 219 220 for enemy in self._enemy_list: 221 enemy.render(self.window) 222 223 if self.debug: 224 """When debug is true, updates the game and renders various aspects.""" 225 self.update_cursor_position() 226 self.draw_enemy_path() 227 self.render_tower_preview() 228 self.render_collision_rects() 229 ''' 230 for tower in self.placed_towers: 231 print(tower._position) 232 ''' 233 if not self.pause: 234 235 self.update_waves() 236 for enemy in self._enemy_list: 237 """For each active enemy, the enemy will move along the set path towards the player base.""" 238 if enemy.is_alive(): 239 enemy._move() 240 if enemy._path_index >= len(enemy._path) - 1: 241 enemy.damage_base(self) 242 self.remove_health(enemy._strength) 243 self.remove_money(enemy._resource_worth) 244 self.update_attacks() 245 246 # Display menu and UI 247 bottom_bar.draw() 248 side_bar.draw() 249 for box in tower_boxes: 250 """Draws each box in the tower_boxes list.""" 251 box.draw() 252 self.window.blit(tower_image, (705, 95)) 253 self.window.blit(self.health_text, (705, 10)) 254 self.window.blit(self.money_text, (705, 40)) 255 self.window.blit(self.wave_text, (705, 70)) 256 self.window.blit(self.tower1_price, (731, 167)) 257 self.pause_button.draw(window) 258 pygame.display.update() 259 260 def check_for_click(self): 261 for event in pygame.event.get(): 262 if event.type == pygame.QUIT: 263 pygame.quit() 264 sys.exit() 265 elif event.type == pygame.MOUSEBUTTONDOWN: 266 mouse_pos = pygame.mouse.get_pos() 267 tower_boxes = [ 268 pygame.Rect(705, 100, 90, 90), 269 pygame.Rect(705, 200, 90, 90), 270 pygame.Rect(705, 300, 90, 90), 271 pygame.Rect(705, 400, 90, 90) 272 ] 273 for box in tower_boxes: 274 if box.collidepoint(mouse_pos): 275 self.grid_active = not self.grid_active 276 return 277 if self.grid_active: 278 self.place_tower(mouse_pos) 279 return 280 281 # selecting towers. 282 tower_clicked = False 283 for tower in self.placed_towers: 284 tower_size = int(self.grid_size * self.tower_size * 0.8) 285 tower_rect = pygame.rect.Rect( 286 tower._position[0] - tower_size // 2, 287 tower._position[1] - tower_size // 2, 288 tower_size, tower_size 289 ) 290 if tower_rect.collidepoint(mouse_pos): 291 self.selected_tower = tower 292 tower_clicked = True 293 break 294 if not tower_clicked: 295 self.selected_tower = None 296 297 # Pause button functionality 298 pause_button = pygame.Rect(710, 510, 75, 75) 299 if pause_button.collidepoint(mouse_pos): 300 if self.pause == True: 301 self.pause = False 302 elif self.pause == False: 303 self.pause = True 304 305 # Debug toggle button. 306 elif event.type == pygame.KEYDOWN: 307 if event.key == pygame.K_p: 308 if self.debug == True: 309 self.debug = False 310 else: 311 self.debug = True 312 return 313 314 def draw_radius(self, center, radius, color): 315 range_surface = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA) 316 range_surface.fill((0, 0, 0, 0)) 317 pygame.draw.circle( 318 range_surface, color, (radius, radius), radius 319 ) 320 top_left = (center[0] - radius, center[1] - radius) 321 self.window.blit(range_surface, top_left) 322 323 def render_tower_preview(self): 324 mouse_x, mouse_y = pygame.mouse.get_pos() 325 temp_tower = Tower("Archer Tower", 50, 3, 100, 80, 1) 326 if self.check_collision(mouse_x, mouse_y): 327 color = (255, 0, 0, 100) 328 else: 329 color = (128, 128, 128, 100) 330 self.draw_radius((mouse_x, mouse_y), temp_tower.get_range(), color) 331 tower_surface = pygame.transform.scale( 332 temp_tower._image, (temp_tower.size * 3, temp_tower.size * 3) 333 ) 334 self.window.blit( 335 tower_surface, 336 (mouse_x - tower_surface.get_width() // 2, mouse_y - tower_surface.get_height() // 2) 337 ) 338 339 def place_tower(self, mouse_pos): 340 if not self.check_collision(mouse_pos[0], mouse_pos[1]): 341 if self.money < 200: 342 return 343 new_tower = Tower("Archer Tower", 20, 2, 1, 80, 1) 344 # need to expand on this to allow for different towers 345 new_tower.place(mouse_pos) 346 self.placed_towers.append(new_tower) 347 self.grid_active = False 348 self.selected_tower = False 349 self.remove_money(200) 350 else: 351 print("Cannot place the tower here. Collision detected.") 352 353 def check_collision(self, x, y): 354 preview_size = self.grid_size * self.tower_size 355 356 preview_rect = pygame.Rect( 357 x - preview_size // 2, y - preview_size // 2, preview_size, preview_size 358 ) 359 360 for tower in self.placed_towers: # checking to see if colliding with any placed towers. 361 tower_size = int(self.grid_size * self.tower_size * 0.8) 362 tower_rect = pygame.Rect( 363 tower._position[0] - tower_size // 2, 364 tower._position[1] - tower_size // 2, 365 tower_size, tower_size 366 ) 367 if preview_rect.colliderect(tower_rect): 368 return True 369 370 for rect in self.collision_rects: # checking to see if collide with map path. 371 if preview_rect.colliderect(rect): 372 return True 373 bottom_bar = pygame.Rect(0, window_height - 100, window_width, 100) 374 side_bar = pygame.Rect(window_width - 100, 0, 100, window_height) 375 376 if preview_rect.colliderect(bottom_bar) or preview_rect.colliderect(side_bar): 377 return True 378 return False 379 380 def update_attacks(self): 381 for tower in self.placed_towers: 382 tower.attack(self._enemy_list) 383 for enemy in self._enemy_list: 384 if not enemy.is_alive(): 385 self.add_money(enemy._resource_worth) 386 self._enemy_list.remove(enemy) 387 388 def update_waves(self): 389 """Checks if the current wave is complete, moves on to the next wave prepared once it is.""" 390 if self._current_wave < len(self._waves): 391 current_wave = self._waves[self._current_wave] 392 if current_wave._is_wave_complete(): 393 self._current_wave += 1 394 if self._current_wave < len(self._waves): 395 self.wave += 1 396 self.wave_text = self.font.render(f"Wave: {self.wave}", True, (255, 255, 255)) 397 return 398 399 self._time_since_previous_spawn += 1 400 if self._time_since_previous_spawn >= current_wave._spawn_timer: 401 if current_wave.spawn_enemy(): 402 new_enemy = current_wave._enemy_list[-1] 403 """Records the next enemy to be spawned.""" 404 self._enemy_list.append(new_enemy) 405 self._time_since_previous_spawn = 0 406 407 def remove_health(self, health): 408 self.health -= health 409 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 410 if self.health <= 0: 411 self.game_over() 412 413 def add_money(self, money): 414 self.money += money 415 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 416 417 def remove_money(self, money): 418 self.money -= money 419 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 420 421 def set_health(self, health): 422 self.health = health 423 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 424 425 def set_money(self, money): 426 self.money = money 427 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 428 429 # Debugging Functions 430 def update_cursor_position(self): 431 """Update and render the cursor position.""" 432 mouse_pos = pygame.mouse.get_pos() 433 self.cursor_text = self.font.render(f'Cursor: {mouse_pos}', True, (255, 255, 255)) 434 self.window.blit(self.cursor_text, (10, 10)) 435 436 def draw_enemy_path(self): 437 path_color = (255, 0, 0) 438 path_width = 3 439 440 for i in range(len(self.map_path) - 1): 441 start_pos = self.map_path[i] 442 end_pos = self.map_path[i + 1] 443 pygame.draw.line(self.window, path_color, start_pos, end_pos, path_width) 444 445 def render_collision_rects(self): 446 """Render the collision rectangles for debugging.""" 447 for rect in self.collision_rects: 448 pygame.draw.rect(self.window, (255, 0, 0), rect, 2) 449 450 451def main(): 452 start_screen = StartScreen(window) 453 main_game_screen = MainGameScreen(window) 454 game_state = 'start_screen' 455 main_game_screen.set_health(100) 456 main_game_screen.set_money(500) 457 while True: 458 if game_state == 'start_screen': 459 start_screen.render() 460 if start_screen.check_for_click(): 461 game_state = 'main_game' 462 463 elif game_state == 'main_game': 464 main_game_screen.render() 465 main_game_screen.check_for_click() 466 for event in pygame.event.get(): 467 if event.type == pygame.QUIT: 468 pygame.quit() 469 sys.exit() 470 471 fpsClock.tick(FPS) 472 473 474if __name__ == '__main__': 475 main()
17class StartScreen: 18 """ 19 A class to represent the start screen of the Tower Defense Game. 20 Attributes: 21 22 Attributes: 23 window: The window surface where the start screen will be rendered. 24 background: The background image of the start screen. 25 font: The font used for rendering text on the start screen. 26 title_text: The rendered text surface for the game title. 27 start_text: The rendered text surface for the start button. 28 start_button_rect: The rectangle area of the start button. 29 30 Methods: 31 render(): Renders the start screen with the background, title, and start button. 32 check_for_click(): Checks for mouse click events and returns True if the start button is clicked. 33 """ 34 def __init__(self, window): 35 """ 36 Initializes the main game window and loads the start screen assets. 37 Args: 38 window: The window surface where the start screen will be rendered. 39 """ 40 self.window = window 41 self.background = pygame.image.load(os.path.join('game_assests', 'start_screen_background.jpg')) 42 self.background = pygame.transform.scale(self.background, (window_width, window_height)) 43 self.font = pygame.font.SysFont(None, 55) 44 self.title_text = self.font.render('Tower Defense Game', True, (255, 255, 255)) 45 self.start_text = self.font.render('Click to Start', True, (255, 255, 255)) 46 47 def render(self): 48 """ 49 Renders the start screen with the background, title, and start button. 50 """ 51 self.window.blit(self.background, (0, 0)) 52 self.window.blit(self.title_text, (window_width // 2 - self.title_text.get_width() // 2, 100)) 53 start_text_rect = self.start_text.get_rect(center=(window_width // 2, 400)) 54 rect_x = start_text_rect.x - 10 55 rect_y = start_text_rect.y - 10 56 rect_width = start_text_rect.width + 20 57 rect_height = start_text_rect.height + 20 58 59 pygame.draw.rect(self.window, (39, 145, 39), (rect_x, rect_y, rect_width, rect_height)) 60 self.window.blit(self.start_text, start_text_rect.topleft) 61 pygame.display.update() 62 self.start_button_rect = pygame.Rect(rect_x, rect_y, rect_width, rect_height) 63 64 def check_for_click(self): 65 """ 66 Checks for mouse click events and returns True if the start button is clicked. 67 68 Returns: 69 bool: True if the start button is clicked, False otherwise. 70 """ 71 for event in pygame.event.get(): 72 if event.type == pygame.QUIT: 73 pygame.quit() 74 sys.exit() 75 if event.type == pygame.MOUSEBUTTONDOWN: 76 mouse_pos = pygame.mouse.get_pos() 77 if self.start_button_rect.collidepoint(mouse_pos): 78 return True 79 return False
A class to represent the start screen of the Tower Defense Game. Attributes:
Attributes: window: The window surface where the start screen will be rendered. background: The background image of the start screen. font: The font used for rendering text on the start screen. title_text: The rendered text surface for the game title. start_text: The rendered text surface for the start button. start_button_rect: The rectangle area of the start button.
Methods: render(): Renders the start screen with the background, title, and start button. check_for_click(): Checks for mouse click events and returns True if the start button is clicked.
34 def __init__(self, window): 35 """ 36 Initializes the main game window and loads the start screen assets. 37 Args: 38 window: The window surface where the start screen will be rendered. 39 """ 40 self.window = window 41 self.background = pygame.image.load(os.path.join('game_assests', 'start_screen_background.jpg')) 42 self.background = pygame.transform.scale(self.background, (window_width, window_height)) 43 self.font = pygame.font.SysFont(None, 55) 44 self.title_text = self.font.render('Tower Defense Game', True, (255, 255, 255)) 45 self.start_text = self.font.render('Click to Start', True, (255, 255, 255))
Initializes the main game window and loads the start screen assets. Args: window: The window surface where the start screen will be rendered.
47 def render(self): 48 """ 49 Renders the start screen with the background, title, and start button. 50 """ 51 self.window.blit(self.background, (0, 0)) 52 self.window.blit(self.title_text, (window_width // 2 - self.title_text.get_width() // 2, 100)) 53 start_text_rect = self.start_text.get_rect(center=(window_width // 2, 400)) 54 rect_x = start_text_rect.x - 10 55 rect_y = start_text_rect.y - 10 56 rect_width = start_text_rect.width + 20 57 rect_height = start_text_rect.height + 20 58 59 pygame.draw.rect(self.window, (39, 145, 39), (rect_x, rect_y, rect_width, rect_height)) 60 self.window.blit(self.start_text, start_text_rect.topleft) 61 pygame.display.update() 62 self.start_button_rect = pygame.Rect(rect_x, rect_y, rect_width, rect_height)
Renders the start screen with the background, title, and start button.
64 def check_for_click(self): 65 """ 66 Checks for mouse click events and returns True if the start button is clicked. 67 68 Returns: 69 bool: True if the start button is clicked, False otherwise. 70 """ 71 for event in pygame.event.get(): 72 if event.type == pygame.QUIT: 73 pygame.quit() 74 sys.exit() 75 if event.type == pygame.MOUSEBUTTONDOWN: 76 mouse_pos = pygame.mouse.get_pos() 77 if self.start_button_rect.collidepoint(mouse_pos): 78 return True 79 return False
Checks for mouse click events and returns True if the start button is clicked.
Returns: bool: True if the start button is clicked, False otherwise.
82class MainGameScreen: 83 """The game screen which shows the map and handles the generation of enemies, towers, and player stats.""" 84 def __init__(self, window): 85 self.window = window 86 """The window for the game.""" 87 self.background = pygame.image.load(os.path.join('game_assests', 'map_one.png')) 88 """Loads the image.""" 89 self.background = pygame.transform.scale(self.background, (window_width - 100, window_height - 100)) 90 """Scales the image in self.background appropriately and then saves it back to self.background.""" 91 self.font = pygame.font.SysFont(None, 22) 92 """Sets the font style to be used.""" 93 self.health = 0 94 """Holds the amount of health the player has remaining.""" 95 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 96 """Renders the health text so the player knows how much health they have remaining.""" 97 self.money = 0 98 """Holds the amount of money the player has""" 99 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 100 """Renders the money text so the player knows how much money they have remaining.""" 101 102 # Pause button 103 pause_img = pygame.image.load(os.path.join('game_assests', 'Play-Pause.png')).convert_alpha() 104 """The image of the pause button.""" 105 self.pause_button = button.Button(710, 510, pause_img, 0.15) 106 """Makes the pause button a button to be clicked, using the image stored in pause_img.""" 107 self.pause = True 108 """Sets pause to True when initially ran.""" 109 110 # Map Variables 111 self.map_path = ((0, 274), (116, 274), (116, 124), (258, 124), (258, 322), (444, 322), 112 (444, 226), (700, 226), (800, 226)) 113 """Sets the path for enemies to traverse.""" 114 self.collision_rects = [ 115 pygame.Rect(min(0, 140), min(248, 300), max(0, 140) - min(0, 140), max(248, 300) - min(248, 300)), 116 pygame.Rect(min(140, 96), min(300, 100), max(140, 96) - min(140, 96), max(300, 100) - min(300, 100)), 117 pygame.Rect(min(96, 278), min(100, 146), max(96, 278) - min(96, 278), max(146, 100) - min(100, 146)), 118 pygame.Rect(min(278, 230), min(146, 348), max(278, 230) - min(278, 230), max(348, 146) - min(146, 348)), 119 pygame.Rect(min(230, 470), min(348, 299), max(470, 230) - min(230, 470), max(348, 299) - min(299, 348)), 120 pygame.Rect(min(470, 418), min(299, 200), max(470, 418) - min(470, 418), max(299, 200) - min(200, 299)), 121 pygame.Rect(min(418, 700), min(200, 250), max(700, 418) - min(418, 700), max(250, 200) - min(200, 250)), 122 ] 123 """Sets up some collision spots, where the towers cannot be placed. (ie. the enemy path)""" 124 125 # Tower Variables 126 self.grid_active = False 127 """Used to show the grid""" 128 self.grid_size = 14 # Will remove later, some things still depend on this. 129 """Determines the grid size.""" 130 self.tower_size = 3 131 """Determines the tower size""" 132 self.selected_tower = None 133 """Determines which tower the player has selected.""" 134 self.placed_towers = [] 135 """A list to hold all the towers that have been placed.""" 136 137 # Wave and Enemy 138 self.wave = 1 139 """Stores which wave number is currently displayed to the player""" 140 self._waves = [] # list of waves 141 self._time_since_previous_spawn = 0 142 self._enemy_list = [] 143 self._current_wave = 0 144 self.wave_text = self.font.render(f'Wave: {self.wave}', True, (255, 255, 255)) 145 """Renders the current wave number.""" 146 147 for wave_number in range(1, 60): 148 """Loop for generating waves, changes are planned to further improve.""" 149 enemy_count = 3 * wave_number 150 """Determines amount of enemies to be spawned, dependent on wave number.""" 151 enemy_hp = 10 + (5 * wave_number) 152 """Increases health of the enemies, dependent on wave number.""" 153 wave_data = [ 154 Enemy('circle', enemy_hp, 1, 1, self.map_path) for i in range(enemy_count) 155 ] 156 """Generate wave data, to be added to self._waves""" 157 self._waves.append(Wave(wave_data, 60)) 158 159 # Debugging Variables 160 self.debug = False 161 """Used to help debug.""" 162 self.cursor_text = self.font.render('', True, (255, 255, 255)) 163 """Displays the text for the cursor.""" 164 165 def render(self): 166 """Used to render the game and enemies.""" 167 self.window.blit(self.background, (0, 0)) 168 """Sets window""" 169 170 class Rectangle(): 171 """Creates rectangles for the UI.""" 172 def __init__(self, x, y, width, height, color): 173 self.x = x 174 """The x position of the rectangle.""" 175 self.y = y 176 """The y position of the rectangle""" 177 self.width = width 178 """The width of the rectangle.""" 179 self.height = height 180 """The height of the rectangle.""" 181 self.color = color 182 """The color of the rectangle.""" 183 184 def draw(self): 185 """Draws the rectangle.""" 186 pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height)) 187 188 # create menus, tower slots, and tower image 189 bottom_bar = Rectangle(0, (window_height - 100), window_width, 100, (150, 150, 150)) 190 """Menu on the bottom, currently hold nothing.""" 191 side_bar = Rectangle((window_width - 100), 0, 100, window_height, (150, 150, 150)) 192 """Menu on the right side, shows player stats and towers that can be used.""" 193 tower_boxes = [ 194 Rectangle(705, 100, 90, 90, (100, 100, 100)), 195 Rectangle(705, 200, 90, 90, (100, 100, 100)), 196 Rectangle(705, 300, 90, 90, (100, 100, 100)), 197 Rectangle(705, 400, 90, 90, (100, 100, 100)) 198 ] 199 """Makes the rectangles for the tower boxes.""" 200 tower_image = pygame.image.load(os.path.join("game_assests", "tower.png")) 201 """Loads tower image.""" 202 tower_image = pygame.transform.scale(tower_image, (90, 90)) 203 """Scales tower image.""" 204 205 if self.grid_active: 206 """Checks if grid is currently active, then renders a preview of the tower selected.""" 207 self.render_tower_preview() 208 209 if self.selected_tower: 210 """Renders the attack radius of the selected tower.""" 211 self.draw_radius(self.selected_tower._position, self.selected_tower.get_range(), (128, 128, 128, 100)) 212 # Logic for upgrades and tower selection info should go here 213 214 self.tower1_price = self.font.render(f'$200', True, (255, 255, 255)) 215 """Renders the price of tower1.""" 216 217 for tower in self.placed_towers: 218 """Renders each placed tower.""" 219 tower.render(self.window) 220 221 for enemy in self._enemy_list: 222 enemy.render(self.window) 223 224 if self.debug: 225 """When debug is true, updates the game and renders various aspects.""" 226 self.update_cursor_position() 227 self.draw_enemy_path() 228 self.render_tower_preview() 229 self.render_collision_rects() 230 ''' 231 for tower in self.placed_towers: 232 print(tower._position) 233 ''' 234 if not self.pause: 235 236 self.update_waves() 237 for enemy in self._enemy_list: 238 """For each active enemy, the enemy will move along the set path towards the player base.""" 239 if enemy.is_alive(): 240 enemy._move() 241 if enemy._path_index >= len(enemy._path) - 1: 242 enemy.damage_base(self) 243 self.remove_health(enemy._strength) 244 self.remove_money(enemy._resource_worth) 245 self.update_attacks() 246 247 # Display menu and UI 248 bottom_bar.draw() 249 side_bar.draw() 250 for box in tower_boxes: 251 """Draws each box in the tower_boxes list.""" 252 box.draw() 253 self.window.blit(tower_image, (705, 95)) 254 self.window.blit(self.health_text, (705, 10)) 255 self.window.blit(self.money_text, (705, 40)) 256 self.window.blit(self.wave_text, (705, 70)) 257 self.window.blit(self.tower1_price, (731, 167)) 258 self.pause_button.draw(window) 259 pygame.display.update() 260 261 def check_for_click(self): 262 for event in pygame.event.get(): 263 if event.type == pygame.QUIT: 264 pygame.quit() 265 sys.exit() 266 elif event.type == pygame.MOUSEBUTTONDOWN: 267 mouse_pos = pygame.mouse.get_pos() 268 tower_boxes = [ 269 pygame.Rect(705, 100, 90, 90), 270 pygame.Rect(705, 200, 90, 90), 271 pygame.Rect(705, 300, 90, 90), 272 pygame.Rect(705, 400, 90, 90) 273 ] 274 for box in tower_boxes: 275 if box.collidepoint(mouse_pos): 276 self.grid_active = not self.grid_active 277 return 278 if self.grid_active: 279 self.place_tower(mouse_pos) 280 return 281 282 # selecting towers. 283 tower_clicked = False 284 for tower in self.placed_towers: 285 tower_size = int(self.grid_size * self.tower_size * 0.8) 286 tower_rect = pygame.rect.Rect( 287 tower._position[0] - tower_size // 2, 288 tower._position[1] - tower_size // 2, 289 tower_size, tower_size 290 ) 291 if tower_rect.collidepoint(mouse_pos): 292 self.selected_tower = tower 293 tower_clicked = True 294 break 295 if not tower_clicked: 296 self.selected_tower = None 297 298 # Pause button functionality 299 pause_button = pygame.Rect(710, 510, 75, 75) 300 if pause_button.collidepoint(mouse_pos): 301 if self.pause == True: 302 self.pause = False 303 elif self.pause == False: 304 self.pause = True 305 306 # Debug toggle button. 307 elif event.type == pygame.KEYDOWN: 308 if event.key == pygame.K_p: 309 if self.debug == True: 310 self.debug = False 311 else: 312 self.debug = True 313 return 314 315 def draw_radius(self, center, radius, color): 316 range_surface = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA) 317 range_surface.fill((0, 0, 0, 0)) 318 pygame.draw.circle( 319 range_surface, color, (radius, radius), radius 320 ) 321 top_left = (center[0] - radius, center[1] - radius) 322 self.window.blit(range_surface, top_left) 323 324 def render_tower_preview(self): 325 mouse_x, mouse_y = pygame.mouse.get_pos() 326 temp_tower = Tower("Archer Tower", 50, 3, 100, 80, 1) 327 if self.check_collision(mouse_x, mouse_y): 328 color = (255, 0, 0, 100) 329 else: 330 color = (128, 128, 128, 100) 331 self.draw_radius((mouse_x, mouse_y), temp_tower.get_range(), color) 332 tower_surface = pygame.transform.scale( 333 temp_tower._image, (temp_tower.size * 3, temp_tower.size * 3) 334 ) 335 self.window.blit( 336 tower_surface, 337 (mouse_x - tower_surface.get_width() // 2, mouse_y - tower_surface.get_height() // 2) 338 ) 339 340 def place_tower(self, mouse_pos): 341 if not self.check_collision(mouse_pos[0], mouse_pos[1]): 342 if self.money < 200: 343 return 344 new_tower = Tower("Archer Tower", 20, 2, 1, 80, 1) 345 # need to expand on this to allow for different towers 346 new_tower.place(mouse_pos) 347 self.placed_towers.append(new_tower) 348 self.grid_active = False 349 self.selected_tower = False 350 self.remove_money(200) 351 else: 352 print("Cannot place the tower here. Collision detected.") 353 354 def check_collision(self, x, y): 355 preview_size = self.grid_size * self.tower_size 356 357 preview_rect = pygame.Rect( 358 x - preview_size // 2, y - preview_size // 2, preview_size, preview_size 359 ) 360 361 for tower in self.placed_towers: # checking to see if colliding with any placed towers. 362 tower_size = int(self.grid_size * self.tower_size * 0.8) 363 tower_rect = pygame.Rect( 364 tower._position[0] - tower_size // 2, 365 tower._position[1] - tower_size // 2, 366 tower_size, tower_size 367 ) 368 if preview_rect.colliderect(tower_rect): 369 return True 370 371 for rect in self.collision_rects: # checking to see if collide with map path. 372 if preview_rect.colliderect(rect): 373 return True 374 bottom_bar = pygame.Rect(0, window_height - 100, window_width, 100) 375 side_bar = pygame.Rect(window_width - 100, 0, 100, window_height) 376 377 if preview_rect.colliderect(bottom_bar) or preview_rect.colliderect(side_bar): 378 return True 379 return False 380 381 def update_attacks(self): 382 for tower in self.placed_towers: 383 tower.attack(self._enemy_list) 384 for enemy in self._enemy_list: 385 if not enemy.is_alive(): 386 self.add_money(enemy._resource_worth) 387 self._enemy_list.remove(enemy) 388 389 def update_waves(self): 390 """Checks if the current wave is complete, moves on to the next wave prepared once it is.""" 391 if self._current_wave < len(self._waves): 392 current_wave = self._waves[self._current_wave] 393 if current_wave._is_wave_complete(): 394 self._current_wave += 1 395 if self._current_wave < len(self._waves): 396 self.wave += 1 397 self.wave_text = self.font.render(f"Wave: {self.wave}", True, (255, 255, 255)) 398 return 399 400 self._time_since_previous_spawn += 1 401 if self._time_since_previous_spawn >= current_wave._spawn_timer: 402 if current_wave.spawn_enemy(): 403 new_enemy = current_wave._enemy_list[-1] 404 """Records the next enemy to be spawned.""" 405 self._enemy_list.append(new_enemy) 406 self._time_since_previous_spawn = 0 407 408 def remove_health(self, health): 409 self.health -= health 410 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 411 if self.health <= 0: 412 self.game_over() 413 414 def add_money(self, money): 415 self.money += money 416 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 417 418 def remove_money(self, money): 419 self.money -= money 420 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 421 422 def set_health(self, health): 423 self.health = health 424 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 425 426 def set_money(self, money): 427 self.money = money 428 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 429 430 # Debugging Functions 431 def update_cursor_position(self): 432 """Update and render the cursor position.""" 433 mouse_pos = pygame.mouse.get_pos() 434 self.cursor_text = self.font.render(f'Cursor: {mouse_pos}', True, (255, 255, 255)) 435 self.window.blit(self.cursor_text, (10, 10)) 436 437 def draw_enemy_path(self): 438 path_color = (255, 0, 0) 439 path_width = 3 440 441 for i in range(len(self.map_path) - 1): 442 start_pos = self.map_path[i] 443 end_pos = self.map_path[i + 1] 444 pygame.draw.line(self.window, path_color, start_pos, end_pos, path_width) 445 446 def render_collision_rects(self): 447 """Render the collision rectangles for debugging.""" 448 for rect in self.collision_rects: 449 pygame.draw.rect(self.window, (255, 0, 0), rect, 2)
The game screen which shows the map and handles the generation of enemies, towers, and player stats.
84 def __init__(self, window): 85 self.window = window 86 """The window for the game.""" 87 self.background = pygame.image.load(os.path.join('game_assests', 'map_one.png')) 88 """Loads the image.""" 89 self.background = pygame.transform.scale(self.background, (window_width - 100, window_height - 100)) 90 """Scales the image in self.background appropriately and then saves it back to self.background.""" 91 self.font = pygame.font.SysFont(None, 22) 92 """Sets the font style to be used.""" 93 self.health = 0 94 """Holds the amount of health the player has remaining.""" 95 self.health_text = self.font.render(f'Health: {self.health}', True, (255, 255, 255)) 96 """Renders the health text so the player knows how much health they have remaining.""" 97 self.money = 0 98 """Holds the amount of money the player has""" 99 self.money_text = self.font.render(f'Money: {self.money}', True, (255, 255, 255)) 100 """Renders the money text so the player knows how much money they have remaining.""" 101 102 # Pause button 103 pause_img = pygame.image.load(os.path.join('game_assests', 'Play-Pause.png')).convert_alpha() 104 """The image of the pause button.""" 105 self.pause_button = button.Button(710, 510, pause_img, 0.15) 106 """Makes the pause button a button to be clicked, using the image stored in pause_img.""" 107 self.pause = True 108 """Sets pause to True when initially ran.""" 109 110 # Map Variables 111 self.map_path = ((0, 274), (116, 274), (116, 124), (258, 124), (258, 322), (444, 322), 112 (444, 226), (700, 226), (800, 226)) 113 """Sets the path for enemies to traverse.""" 114 self.collision_rects = [ 115 pygame.Rect(min(0, 140), min(248, 300), max(0, 140) - min(0, 140), max(248, 300) - min(248, 300)), 116 pygame.Rect(min(140, 96), min(300, 100), max(140, 96) - min(140, 96), max(300, 100) - min(300, 100)), 117 pygame.Rect(min(96, 278), min(100, 146), max(96, 278) - min(96, 278), max(146, 100) - min(100, 146)), 118 pygame.Rect(min(278, 230), min(146, 348), max(278, 230) - min(278, 230), max(348, 146) - min(146, 348)), 119 pygame.Rect(min(230, 470), min(348, 299), max(470, 230) - min(230, 470), max(348, 299) - min(299, 348)), 120 pygame.Rect(min(470, 418), min(299, 200), max(470, 418) - min(470, 418), max(299, 200) - min(200, 299)), 121 pygame.Rect(min(418, 700), min(200, 250), max(700, 418) - min(418, 700), max(250, 200) - min(200, 250)), 122 ] 123 """Sets up some collision spots, where the towers cannot be placed. (ie. the enemy path)""" 124 125 # Tower Variables 126 self.grid_active = False 127 """Used to show the grid""" 128 self.grid_size = 14 # Will remove later, some things still depend on this. 129 """Determines the grid size.""" 130 self.tower_size = 3 131 """Determines the tower size""" 132 self.selected_tower = None 133 """Determines which tower the player has selected.""" 134 self.placed_towers = [] 135 """A list to hold all the towers that have been placed.""" 136 137 # Wave and Enemy 138 self.wave = 1 139 """Stores which wave number is currently displayed to the player""" 140 self._waves = [] # list of waves 141 self._time_since_previous_spawn = 0 142 self._enemy_list = [] 143 self._current_wave = 0 144 self.wave_text = self.font.render(f'Wave: {self.wave}', True, (255, 255, 255)) 145 """Renders the current wave number.""" 146 147 for wave_number in range(1, 60): 148 """Loop for generating waves, changes are planned to further improve.""" 149 enemy_count = 3 * wave_number 150 """Determines amount of enemies to be spawned, dependent on wave number.""" 151 enemy_hp = 10 + (5 * wave_number) 152 """Increases health of the enemies, dependent on wave number.""" 153 wave_data = [ 154 Enemy('circle', enemy_hp, 1, 1, self.map_path) for i in range(enemy_count) 155 ] 156 """Generate wave data, to be added to self._waves""" 157 self._waves.append(Wave(wave_data, 60)) 158 159 # Debugging Variables 160 self.debug = False 161 """Used to help debug.""" 162 self.cursor_text = self.font.render('', True, (255, 255, 255)) 163 """Displays the text for the cursor."""
Scales the image in self.background appropriately and then saves it back to self.background.
Sets up some collision spots, where the towers cannot be placed. (ie. the enemy path)
165 def render(self): 166 """Used to render the game and enemies.""" 167 self.window.blit(self.background, (0, 0)) 168 """Sets window""" 169 170 class Rectangle(): 171 """Creates rectangles for the UI.""" 172 def __init__(self, x, y, width, height, color): 173 self.x = x 174 """The x position of the rectangle.""" 175 self.y = y 176 """The y position of the rectangle""" 177 self.width = width 178 """The width of the rectangle.""" 179 self.height = height 180 """The height of the rectangle.""" 181 self.color = color 182 """The color of the rectangle.""" 183 184 def draw(self): 185 """Draws the rectangle.""" 186 pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height)) 187 188 # create menus, tower slots, and tower image 189 bottom_bar = Rectangle(0, (window_height - 100), window_width, 100, (150, 150, 150)) 190 """Menu on the bottom, currently hold nothing.""" 191 side_bar = Rectangle((window_width - 100), 0, 100, window_height, (150, 150, 150)) 192 """Menu on the right side, shows player stats and towers that can be used.""" 193 tower_boxes = [ 194 Rectangle(705, 100, 90, 90, (100, 100, 100)), 195 Rectangle(705, 200, 90, 90, (100, 100, 100)), 196 Rectangle(705, 300, 90, 90, (100, 100, 100)), 197 Rectangle(705, 400, 90, 90, (100, 100, 100)) 198 ] 199 """Makes the rectangles for the tower boxes.""" 200 tower_image = pygame.image.load(os.path.join("game_assests", "tower.png")) 201 """Loads tower image.""" 202 tower_image = pygame.transform.scale(tower_image, (90, 90)) 203 """Scales tower image.""" 204 205 if self.grid_active: 206 """Checks if grid is currently active, then renders a preview of the tower selected.""" 207 self.render_tower_preview() 208 209 if self.selected_tower: 210 """Renders the attack radius of the selected tower.""" 211 self.draw_radius(self.selected_tower._position, self.selected_tower.get_range(), (128, 128, 128, 100)) 212 # Logic for upgrades and tower selection info should go here 213 214 self.tower1_price = self.font.render(f'$200', True, (255, 255, 255)) 215 """Renders the price of tower1.""" 216 217 for tower in self.placed_towers: 218 """Renders each placed tower.""" 219 tower.render(self.window) 220 221 for enemy in self._enemy_list: 222 enemy.render(self.window) 223 224 if self.debug: 225 """When debug is true, updates the game and renders various aspects.""" 226 self.update_cursor_position() 227 self.draw_enemy_path() 228 self.render_tower_preview() 229 self.render_collision_rects() 230 ''' 231 for tower in self.placed_towers: 232 print(tower._position) 233 ''' 234 if not self.pause: 235 236 self.update_waves() 237 for enemy in self._enemy_list: 238 """For each active enemy, the enemy will move along the set path towards the player base.""" 239 if enemy.is_alive(): 240 enemy._move() 241 if enemy._path_index >= len(enemy._path) - 1: 242 enemy.damage_base(self) 243 self.remove_health(enemy._strength) 244 self.remove_money(enemy._resource_worth) 245 self.update_attacks() 246 247 # Display menu and UI 248 bottom_bar.draw() 249 side_bar.draw() 250 for box in tower_boxes: 251 """Draws each box in the tower_boxes list.""" 252 box.draw() 253 self.window.blit(tower_image, (705, 95)) 254 self.window.blit(self.health_text, (705, 10)) 255 self.window.blit(self.money_text, (705, 40)) 256 self.window.blit(self.wave_text, (705, 70)) 257 self.window.blit(self.tower1_price, (731, 167)) 258 self.pause_button.draw(window) 259 pygame.display.update()
Used to render the game and enemies.
261 def check_for_click(self): 262 for event in pygame.event.get(): 263 if event.type == pygame.QUIT: 264 pygame.quit() 265 sys.exit() 266 elif event.type == pygame.MOUSEBUTTONDOWN: 267 mouse_pos = pygame.mouse.get_pos() 268 tower_boxes = [ 269 pygame.Rect(705, 100, 90, 90), 270 pygame.Rect(705, 200, 90, 90), 271 pygame.Rect(705, 300, 90, 90), 272 pygame.Rect(705, 400, 90, 90) 273 ] 274 for box in tower_boxes: 275 if box.collidepoint(mouse_pos): 276 self.grid_active = not self.grid_active 277 return 278 if self.grid_active: 279 self.place_tower(mouse_pos) 280 return 281 282 # selecting towers. 283 tower_clicked = False 284 for tower in self.placed_towers: 285 tower_size = int(self.grid_size * self.tower_size * 0.8) 286 tower_rect = pygame.rect.Rect( 287 tower._position[0] - tower_size // 2, 288 tower._position[1] - tower_size // 2, 289 tower_size, tower_size 290 ) 291 if tower_rect.collidepoint(mouse_pos): 292 self.selected_tower = tower 293 tower_clicked = True 294 break 295 if not tower_clicked: 296 self.selected_tower = None 297 298 # Pause button functionality 299 pause_button = pygame.Rect(710, 510, 75, 75) 300 if pause_button.collidepoint(mouse_pos): 301 if self.pause == True: 302 self.pause = False 303 elif self.pause == False: 304 self.pause = True 305 306 # Debug toggle button. 307 elif event.type == pygame.KEYDOWN: 308 if event.key == pygame.K_p: 309 if self.debug == True: 310 self.debug = False 311 else: 312 self.debug = True 313 return
315 def draw_radius(self, center, radius, color): 316 range_surface = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA) 317 range_surface.fill((0, 0, 0, 0)) 318 pygame.draw.circle( 319 range_surface, color, (radius, radius), radius 320 ) 321 top_left = (center[0] - radius, center[1] - radius) 322 self.window.blit(range_surface, top_left)
324 def render_tower_preview(self): 325 mouse_x, mouse_y = pygame.mouse.get_pos() 326 temp_tower = Tower("Archer Tower", 50, 3, 100, 80, 1) 327 if self.check_collision(mouse_x, mouse_y): 328 color = (255, 0, 0, 100) 329 else: 330 color = (128, 128, 128, 100) 331 self.draw_radius((mouse_x, mouse_y), temp_tower.get_range(), color) 332 tower_surface = pygame.transform.scale( 333 temp_tower._image, (temp_tower.size * 3, temp_tower.size * 3) 334 ) 335 self.window.blit( 336 tower_surface, 337 (mouse_x - tower_surface.get_width() // 2, mouse_y - tower_surface.get_height() // 2) 338 )
340 def place_tower(self, mouse_pos): 341 if not self.check_collision(mouse_pos[0], mouse_pos[1]): 342 if self.money < 200: 343 return 344 new_tower = Tower("Archer Tower", 20, 2, 1, 80, 1) 345 # need to expand on this to allow for different towers 346 new_tower.place(mouse_pos) 347 self.placed_towers.append(new_tower) 348 self.grid_active = False 349 self.selected_tower = False 350 self.remove_money(200) 351 else: 352 print("Cannot place the tower here. Collision detected.")
354 def check_collision(self, x, y): 355 preview_size = self.grid_size * self.tower_size 356 357 preview_rect = pygame.Rect( 358 x - preview_size // 2, y - preview_size // 2, preview_size, preview_size 359 ) 360 361 for tower in self.placed_towers: # checking to see if colliding with any placed towers. 362 tower_size = int(self.grid_size * self.tower_size * 0.8) 363 tower_rect = pygame.Rect( 364 tower._position[0] - tower_size // 2, 365 tower._position[1] - tower_size // 2, 366 tower_size, tower_size 367 ) 368 if preview_rect.colliderect(tower_rect): 369 return True 370 371 for rect in self.collision_rects: # checking to see if collide with map path. 372 if preview_rect.colliderect(rect): 373 return True 374 bottom_bar = pygame.Rect(0, window_height - 100, window_width, 100) 375 side_bar = pygame.Rect(window_width - 100, 0, 100, window_height) 376 377 if preview_rect.colliderect(bottom_bar) or preview_rect.colliderect(side_bar): 378 return True 379 return False
389 def update_waves(self): 390 """Checks if the current wave is complete, moves on to the next wave prepared once it is.""" 391 if self._current_wave < len(self._waves): 392 current_wave = self._waves[self._current_wave] 393 if current_wave._is_wave_complete(): 394 self._current_wave += 1 395 if self._current_wave < len(self._waves): 396 self.wave += 1 397 self.wave_text = self.font.render(f"Wave: {self.wave}", True, (255, 255, 255)) 398 return 399 400 self._time_since_previous_spawn += 1 401 if self._time_since_previous_spawn >= current_wave._spawn_timer: 402 if current_wave.spawn_enemy(): 403 new_enemy = current_wave._enemy_list[-1] 404 """Records the next enemy to be spawned.""" 405 self._enemy_list.append(new_enemy) 406 self._time_since_previous_spawn = 0
Checks if the current wave is complete, moves on to the next wave prepared once it is.
431 def update_cursor_position(self): 432 """Update and render the cursor position.""" 433 mouse_pos = pygame.mouse.get_pos() 434 self.cursor_text = self.font.render(f'Cursor: {mouse_pos}', True, (255, 255, 255)) 435 self.window.blit(self.cursor_text, (10, 10))
Update and render the cursor position.
452def main(): 453 start_screen = StartScreen(window) 454 main_game_screen = MainGameScreen(window) 455 game_state = 'start_screen' 456 main_game_screen.set_health(100) 457 main_game_screen.set_money(500) 458 while True: 459 if game_state == 'start_screen': 460 start_screen.render() 461 if start_screen.check_for_click(): 462 game_state = 'main_game' 463 464 elif game_state == 'main_game': 465 main_game_screen.render() 466 main_game_screen.check_for_click() 467 for event in pygame.event.get(): 468 if event.type == pygame.QUIT: 469 pygame.quit() 470 sys.exit() 471 472 fpsClock.tick(FPS)