def phase1(self): self.move_speed = 2 # After y is lined up, turn toward player and starting moving on the y axis if self.moving_x and \ abs(self.x - ml.player.rect.centerx) <= \ self.move_speed + float(ml.get_upgrade_values('Movement Speed')): self.moving_x = False self.moving_y = True _ = self.shoot(-999, self.circle_bd, self.circle_fd) # After x is lined up, turn toward player and starting moving on the x axis if self.moving_y and \ abs(self.y - ml.player.rect.centery) <= \ self.move_speed + float(ml.get_upgrade_values('Movement Speed')): self.moving_x = True self.moving_y = False _ = self.shoot(-999, self.circle_bd, self.circle_fd) # Move until self.x lines up with player's x if self.rect.centerx != ml.player.rect.centerx and self.moving_x: self.x, self.y, self.current_angle = ml.move_point( self, (ml.player.rect.centerx, self.rect.centery), self.move_speed, 0, 360) else: self.moving_x = False self.moving_y = True # Move until self.y lines up with player's y if self.rect.centery != ml.player.rect.centery and self.moving_y: self.x, self.y, self.current_angle = ml.move_point( self, (self.rect.centerx, ml.player.rect.centery), self.move_speed, 0, 360) else: self.moving_x = True self.moving_y = False
def update_powerups(self): # Update shield if self.shield and not self.shield.alive(): self.shield_active = False self.shield = None elif self.shield: self.shield_active = True # Update homing shots if self.homing_shots_active: if ml.time() - self.homing_shots_activation_time >= \ ml.get_upgrade_values('Homing Duration'): self.homing_shots_active = False self.image = self.image_original if not self.invincibility_active: self.image = self.homing_image # Flicker sprite color when Homing duration is low if ml.time() - self.homing_shots_activation_time > \ ml.get_upgrade_values('Homing Duration') - 1: if ml.time() - self.toggle_time > 0.15: if self.image == self.image_original: self.toggle_time = ml.time() self.image = self.homing_image else: self.toggle_time = ml.time() self.image = self.image_original
def reset_duration(self): if not ml.time() - self.spawn_time > self.duration: self.duration = ml.get_upgrade_values('Shield Duration') self.spawn_tick = ml.ticks self.bar_stage_duration = (ml.get_upgrade_values('Shield Duration') * ml.framerate) / len(self.bar_stage_images) self.ticks_elapsed = 0 self.spawn_time = ml.time()
def shoot(self): if ml.time() - self.firing_timer >= ( 1 / ml.get_upgrade_values('Attack Speed')): shot_angles = ml.multi_shot_angles( ml.get_upgrade_values('Multi Shot'), 90, 10) for i in range(ml.get_upgrade_values('Multi Shot')): PlayerBullet(self.rect.centerx, self.rect.y, ml.get_upgrade_values('Bullet Speed'), ml.get_upgrade_values('Damage'), shot_angles[i], self.homing_shots_active) self.firing_timer = ml.time()
def damage(self, damage: int): if damage > 0 and not self.shield_active and \ ml.time() - self.last_hit_time > self.invincibility_duration: self.health -= damage self.invincibility_active = True self.last_hit_time = ml.time() self.hit_toggle_time = ml.time() # Heal elif damage < 0: self.health -= damage if self.health > ml.get_upgrade_values('Max Health'): self.health = ml.get_upgrade_values('Max Health') ml.update_player_health()
def __init__(self) -> pygame.sprite.Sprite: pygame.sprite.Sprite.__init__(self) self.spawn_time = ml.time() self.spawn_tick = ml.ticks self.duration = ml.get_upgrade_values('Shield Duration') self.image_path = os.path.join('graphics', 'shield.png') self.image = pygame.image.load(self.image_path) self.image_original = pygame.image.load(self.image_path) self.shield_mask = pygame.mask.from_surface(self.image) self.rect = self.image.get_rect() self.add(ml.powerup_effect_group) # Animation self.animation_duration = 0.2 self.duration_ticks = ml.get_upgrade_values('Shield Duration') * ml.framerate # 1 animation stage per frame self.animation_stages = int(ml.framerate * self.animation_duration) self.stage_duration = self.animation_duration / self.animation_stages self.stage_width = self.image.get_width() / self.animation_stages self.stage_height = self.image.get_height() / self.animation_stages self.stage_images = [] for count in range(self.animation_stages): self.stage_images.append(pygame.transform.smoothscale(self.image_original, (int(self.stage_width * count), int(self.stage_height * count)))) self.stage = 0 self.ticks_elapsed = 0 # Set up bar self.bar_image_path = os.path.join('graphics', 'shield_bar.png') self.bar_image = pygame.image.load(self.bar_image_path) assert self.bar_image.get_width() % 2, 'bar_image width must be odd.' self.bar_image_original = self.bar_image self.bar_stage_images = [self.bar_image] # Create list of bar surfaces with decreasing widths for i in range(self.bar_image.get_width() // 2): self.bar_stage_images.append( pygame.transform.smoothscale(self.bar_image_original, (self.bar_stage_images[i].get_width() - 2, self.bar_image.get_height()) )) self.bar_stage_images.append( pygame.transform.smoothscale(self.bar_image_original, (0, self.bar_image.get_height()))) self.bar_stage_duration = (self.duration * ml.framerate) / \ len(self.bar_stage_images) self.next_bar_image = None self.bar = ShieldBar()
def spawn_random(start_point): if ml.time() - PowerUp.spawn_timer > ml.get_upgrade_values('Powerup Interval'): PowerUp.spawn_timer = ml.time() powerup = random.randint(0, 4) if powerup == 0: HomingShots(start_point, start_point) elif powerup == 1: Shield(start_point, start_point) elif powerup == 2: Heal(start_point, start_point) elif powerup == 3: Bomb(start_point, start_point)
def phase3(self): self.turning_rate = ml.normalize_target_fps(10) # Match the player's speed self.move_speed = ml.normalize_target_fps( float(ml.get_upgrade_values('Movement Speed')) - 1) # Keep the boss positioned above the player above_player = (ml.player.rect.centerx, ml.player.rect.centery - 350) self.x, self.y, self.movement_angle = ml.move_point( self, above_player, self.move_speed, self.movement_angle, self.turning_rate) # Keep boss on screen if self.rect.centery < 0: self.y = 0 # Face the player self.current_angle = ml.angle_to_point(self.rect.center, ml.player.rect.center) # Shoot self.phase2_bd = BulletData(self, coords=self.rect.center, speed=7) self.phase2_timer = self.shoot(self.phase2_timer, self.phase2_bd, self.phase2_fd) self.phase2spread_timer = self.shoot(self.phase2spread_timer, self.phase2spread_bd, self.phase2spread_fd) # Left arc self.phase2arc_bd.coords = self.rect.midleft self.phase2arc_fd.angle = ml.burst_angles(20, 180, 10)[self.phase2arc_counter] self.phase2arc_timer, self.phase2arc_counter = \ self.shoot(self.phase2arc_timer, self.phase2arc_bd, self.phase2arc_fd, self.phase2arc_counter) # Right arc self.phase2arc_bd.coords = self.rect.midright self.phase2arc_fd.angle = ml.burst_angles(20, 0, -10)[self.phase2arc2_counter] self.phase2arc2_timer, self.phase2arc2_counter = \ self.shoot(self.phase2arc2_timer, self.phase2arc_bd, self.phase2arc_fd, self.phase2arc2_counter)
def update_move_speed(self): self.move_speed = ml.normalize_target_fps( float(ml.get_upgrade_values('Movement Speed'))) self.diagonal_move_speed = self.move_speed * math.sin(math.pi / 4)
def check_collision(self): """Check and deal with collision between Player and anything else.""" # Check collision with enemies # Check rect collision first if not self.shield_active: # Disable collision when shield is on enemies_hit = pygame.sprite.spritecollide(self, ml.enemy_group, False) if enemies_hit: # Change mask to hitbox mask before checking collision self.mask = self.hitbox_mask for enemy in enemies_hit: if not enemy.invincible: # Check mask collision enemy.update_mask() # proper mask for rotating enemies if pygame.sprite.collide_mask(self, enemy): self.damage(enemy.get_damage()) # Change mask back to normal self.mask = self.mask_original # Check collision with enemy bullets enemy_bullets_hit = pygame.sprite.spritecollide( self, ml.enemy_bullet_group, False) if enemy_bullets_hit: for enemy_bullet in enemy_bullets_hit: # Hitbox collision if self.hitbox.colliderect(enemy_bullet.get_mask_rect()): self.damage(enemy_bullet.get_damage()) enemy_bullet.kill() # Check collision with powerups powerups_hit = pygame.sprite.spritecollide(self, ml.powerup_group, False) if powerups_hit: for powerup in powerups_hit: # Mask collision if pygame.sprite.collide_mask(self, powerup): if isinstance(powerup, powerups.HomingShots): self.homing_shots_active = True self.homing_shots_activation_time = ml.time() self.image = self.homing_image self.toggle_time = ml.time() powerup.kill() elif isinstance(powerup, powerups.Shield): if not self.shield_active: self.shield = powerups.ShieldEffect() else: self.shield.reset_duration() powerup.kill() elif isinstance(powerup, powerups.Bomb): for enemy in ml.enemy_group.sprites(): enemy.damage(ml.get_upgrade_values('Bomb Damage')) powerup.kill() elif isinstance(powerup, powerups.Heal): if self.health < ml.get_upgrade_values('Max Health'): self.damage(-self.heal_amount) if self.health > ml.get_upgrade_values('Max Health'): self.health = ml.get_upgrade_values('Max Health') powerup.kill() elif isinstance(powerup, powerups.Bonus): ml.change_score(500) powerup.kill()
def __init__(self) -> pygame.sprite.Sprite: pygame.sprite.Sprite.__init__(self) self.health = ml.get_upgrade_values('Max Health') self.hitbox_width = 5 self.move_speed = float(ml.get_upgrade_values('Movement Speed')) self.shift_speed = ml.normalize_target_fps(2) self.diagonal_move_speed = 0 self.invincibility_duration = 1 # Seconds of invulnerability after taking damage self.last_hit_time = -999 self.hit_toggle_time = ml.time() self.invincibility_active = False # Shooting self.firing_timer = ml.time() # Set up image, rect, and mask self.image_path = os.path.join('graphics', 'player.png') self.homing_image_path = os.path.join('graphics', 'player_homing.png') self.invisible_image_path = os.path.join('graphics', 'player_invisible.png') self.image = pygame.image.load(self.image_path).convert_alpha() self.homing_image = pygame.image.load( self.homing_image_path).convert_alpha() self.invisible_image = pygame.image.load( self.invisible_image_path).convert_alpha() self.image_original = self.image # Even width prevents proper centering of hitbox and mouse movement assert self.image.get_width() % 2, "Player image width must be odd." self.x = ml.window_width / 2 self.y = ml.window_height - 100 self.rect = pygame.Rect(0, 0, self.image.get_width(), self.image.get_height()) self.mask = pygame.mask.from_surface(self.image, 250) self.mask_original = self.mask self.hitbox = pygame.Rect(self.rect.centerx, self.rect.centerx, self.hitbox_width, self.hitbox_width) # Create mask for hitbox to deal with enemy collision self.hitbox_mask = pygame.mask.Mask(self.rect.size) self.rect.topleft = 0, 0 top_left = self.rect.centerx - self.hitbox_width // 2, \ self.rect.centery - self.hitbox_width // 2 for x in range(self.hitbox_width): current_x = top_left[0] + x for y in range(self.hitbox_width): current_y = top_left[1] + y self.hitbox_mask.set_at((current_x, current_y), 1) self.rect.center = self.x, self.y # Powerups self.shield_active = False self.shield = None self.heal_amount = 1 self.homing_shots_active = False self.homing_shots_activation_time = None self.toggle_time = None # Add sprite to group self.add(ml.player_group) ml.update_player_data(self) ml.update_player_health()
def update_duration(self): self.duration = ml.get_upgrade_values('Shield Duration') self.bar_stage_duration = (self.duration * ml.framerate) / \ len(self.bar_stage_images)
def upgrade_screen(): """Shows the upgrade screen.""" from collections import OrderedDict upgrade_list = list(ml.upgrades.keys()) button_gap = 50 button_x_list = [] x_margin = 150 y_start = 125 u_button_width = 240 u_button_height = 75 name_font_size = 22 score_margin = 10 score_font = pygame.font.Font(ml.font, 36) value_font = pygame.font.Font(ml.font, 28) upgrades_title_font = pygame.font.Font(ml.font, 48) upgrades_title = upgrades_title_font.render('Upgrades', True, WHITE) continue_button = ml.get_button_surface(button_width, button_height, 'Continue', button_font_size, WHITE) continue_button_rect = pygame.Rect( window.get_width() - button_width - x_margin, window.get_height() - 75 - continue_button.get_height(), button_width, button_height) # Creates list with x coordinates alternating between left and right side of window for i in range(len(upgrade_list)): if i % 2 == 0: button_x_list.append(x_margin) else: button_x_list.append(window.get_width() - x_margin - u_button_width) # Example output: [200, 200, 270, 270, 340, 340, 410, 410, 480, 480] button_y_list = [y_start] for i in range(len(upgrade_list) - 1): if i % 2 == 0: button_y_list.append(button_y_list[i]) else: button_y_list.append(button_y_list[i] + button_gap + u_button_height) # Build list of Rects from those x and y coordinates button_rect_list = [] for i, j in enumerate(upgrade_list): button_rect_list.append( pygame.Rect((button_x_list[i], button_y_list[i]), (u_button_width, u_button_height))) # Loop while True: # Clear the window window.fill(BLACK) # Show the current score in bottom-left corner score_surface = score_font.render('Score: %d' % (ml.score), True, WHITE) window.blit(score_surface, (score_margin, window.get_height() - score_margin - score_surface.get_height())) # Build a list of button surfaces with upgrade names and values button_surfaces = OrderedDict() offset = int(-1 * (u_button_height / 4)) for upgrade in upgrade_list: # Create button with upgrade name on it button_surfaces[upgrade] = ml.get_button_surface( u_button_width, u_button_height, upgrade, name_font_size, WHITE, offset) value = str(ml.get_upgrade_values(upgrade)) value_surface = value_font.render(value, True, WHITE) # Add the text for the current value of each upgrade button_surfaces[upgrade].blit( value_surface, ((button_surfaces[upgrade].get_width() / 2) - (value_surface.get_width() / 2), button_surfaces[upgrade].get_height() - value_surface.get_height() - (button_surfaces[upgrade].get_height() / 20))) # Handle mouse-over pos = pygame.mouse.get_pos() for i, rect in enumerate(button_rect_list): if rect.collidepoint(pos): # Draw upgrade amount next to upgrade value upgrade_amount_surface = value_font.render( ('+' if ml.get_upgrade_amount(upgrade_list[i]) >= 0 else '') + str(ml.get_upgrade_amount(upgrade_list[i])), True, GREEN) button_surfaces[upgrade_list[i]].blit( upgrade_amount_surface, (button_surfaces[upgrade_list[i]].get_width() - upgrade_amount_surface.get_width() - 10, button_surfaces[upgrade_list[i]].get_height() - upgrade_amount_surface.get_height() - (button_surfaces[upgrade_list[i]].get_height() / 20))) # Draw upgrade cost next to score cost_surface = score_font.render( '-%d' % ml.get_upgrade_cost(upgrade_list[i]), True, RED) window.blit(cost_surface, (score_surface.get_width() + score_margin + 10, window.get_height() - score_margin - cost_surface.get_height())) # Event handling for event in pygame.event.get(): if event.type == QUIT: terminate() # Escape or space closes the upgrades screen elif event.type == KEYDOWN: if event.key in (K_ESCAPE, K_SPACE): return # Check for click on each button elif event.type == MOUSEBUTTONUP: for i, rect in enumerate(button_rect_list): if rect.collidepoint(event.pos): ml.purchase_upgrade(upgrade_list[i]) break # noinspection PyArgumentList if continue_button_rect.collidepoint(event.pos): return # Draw title window.blit(upgrades_title, ((window.get_width() / 2) - (upgrades_title.get_width() / 2), 30)) # Draw all of the buttons for count, button in enumerate(button_surfaces): window.blit(button_surfaces[button], button_rect_list[count]) window.blit(continue_button, continue_button_rect) draw_cursor() pygame.display.update() clock.tick(FPS)