import pygame as pg
from entities.enemy import Enemy
from entities.player import Player
from game.const import *
from entities.shot import BossShot
from os import path
[documentos]
class FinalBoss(Enemy):
"""
classe criada para modelar o comportamento do boss final
"""
def __init__(self, position: tuple, player:Player, shots:pg.sprite.Group, explosion:pg.sprite.Group) -> None:
"""
método de inicialização
Parâmetros
position
type: tuple
description: posição onde o inimigo final deve ser criado
player
type: Player
description: player principal do jogo
shots
type: pg.sprite.Group
description: grupo de tiros
explosion
type: pg.sprite.Group
description: grupo de explosões
"""
super().__init__(position)
current_dir = path.dirname(path.abspath(__file__))
media_dir = path.join(current_dir, "..", "media")
self.image_dir = path.join(media_dir, "final_boss", "boss")
initial_image = path.join(self.image_dir, "final_boss_0.png")
self.image = pg.transform.scale(pg.image.load(initial_image), (64, 128))
self.rect = self.image.get_rect(topleft = position)
self.player = player
self.facing = 1
self.initial_pos = self.rect.left
self.health = 3
self.vunerable = False
self.move_range = INITIAL_RANGE
self.move_counter = 0
self.on_move = True
self.rest_time = 0
self.gun = BossGun(self, player, shots, explosion)
self.shooting = True
self.shot_cooldown = 2*FPS
self.shot_time = 0
self.sel_frame = 0
self.frames = [[pg.image.load(path.join(self.image_dir, "final_boss_2.png")), pg.image.load(path.join(self.image_dir, "final_boss_5.png"))],
[pg.image.load(path.join(self.image_dir, "final_boss_1.png")), pg.image.load(path.join(self.image_dir, "final_boss_4.png"))],
[pg.image.load(path.join(self.image_dir, "final_boss_0.png")), pg.image.load(path.join(self.image_dir, "final_boss_3.png"))]
]
self.animation_counter = 0
self.animation_delay = 10
[documentos]
def animation(self) -> None:
"""
método de gerenciamento das animações do boss final
"""
if self.animation_counter >= self.animation_delay/(self.x_vel + 1) and self.is_alive == True and self.on_move == True:
if self.sel_frame == 0:
self.image = pg.transform.scale(self.frames[self.health - 1][self.sel_frame], (64, 128))
self.sel_frame = 1
elif self.sel_frame == 1:
self.image = pg.transform.scale(self.frames[self.health - 1][self.sel_frame], (64, 128))
self.sel_frame = 0
if self.facing == -1:
self.image = pg.transform.flip(self.image, True, False)
self.animation_counter = 0
self.animation_counter += 1
[documentos]
def move(self) -> None:
"""
método que modela o movimento
"""
if self.on_move:
self.direction.x = self.x_vel*self.facing
self.rect.x += self.direction.x
[documentos]
def delay(self):
"""
método que controla os momentos de vunerabilidade do inimigo
"""
self.rest_time += 1
if self.rest_time == FPS * 5:
self.rest_time = 0
self.move_range = INITIAL_RANGE
self.move_counter = 0
self.on_move = True
self.vunerable = False
[documentos]
def shoot(self):
"""
método que cria os tiros sempre que necessário
"""
self.shot_time += 1
#criação do tiro com base em um cooldown estabelecido que vai variando a medida que ele toma dano (método die())
if self.shot_time == self.shot_cooldown:
self.gun.shot()
self.shot_time = 0
[documentos]
def update(self, square_group: pg.sprite.Group, offset) -> None:
"""
método de atualização
"""
self.initial_pos += offset
self.rect.x += offset
self.apply_gravity()
self.collide_with_square(square_group)
if self.rect.top > SCREEN_HEIGHT:
self.kill()
if self.rect.left == self.initial_pos:
self.shooting = True
if self.rect.left > self.initial_pos + self.move_range and self.on_move and self.shooting:
self.facing = -1
self.move_counter += 1
self.image = pg.transform.flip(self.image, True, False)
self.x_vel = ENTITY_X_VEL
elif self.rect.left < self.initial_pos - self.move_range and self.on_move and self.shooting:
self.facing = 1
self.move_counter += 1
self.image = pg.transform.flip(self.image, True, False)
self.x_vel = ENTITY_X_VEL
elif self.move_counter == 4:
self.move_range = FINAL_RANGE
elif self.move_counter == 5:
self.on_move = False
self.vunerable = True
self.shooting = False
self.delay()
if self.move_counter > 5:
self.move_counter = 0
self.move()
self.animation()
if self.shooting:
self.shoot()
[documentos]
def die(self):
"""
método de gerenciamento da morte do boss final
"""
if not self.vunerable:
return
self.player.direction.y -= 20
#comportamentos necessário a cada dano que ele leva do player principal
self.health -= 1
self.shot_cooldown -= 0.5*FPS
self.move_counter = 0
self.x_vel *= 2
self.rest_time = 0
self.on_move = True
self.move_range = INITIAL_RANGE
self.vunerable = False
#caso o número de vidas dele acabe, ele será removido do grupo
if self.health == 0:
self.player.won = True
super().die()
[documentos]
class BossGun(pg.sprite.Sprite):
"""
classe que modela o comportamento da arma do final boss
"""
def __init__(self, holder: FinalBoss, player: Player, shots:pg.sprite.Group, explosion:pg.sprite.Group) -> None:
"""
método de inicialização
Parâmetros:
holder
type: FinalBoss
descripition: inimigo principal para que ela seja posicionada corretamente sobre ele
player
type: Player
descripition: player para que seja extraida a posição dele e criado um vetor que vai modelar o movimento de cada projétil
shots
type: pg.sprite.Group
descripition: grupo de projéteis
explosion
type: pg.sprite.Group
descripition: grupo de explosões
"""
super().__init__()
self.holder = holder
self.player = player
self.shots = shots
self.explosion = explosion
self.collision = False
#vetor com a direção do player
self.aim_vector = (pg.math.Vector2(self.holder.rect.center) - pg.math.Vector2(self.player.rect.center)).normalize()
self.still_vector = pg.math.Vector2(0, -1)
self.angle_to_player = self.still_vector.angle_to(self.aim_vector)
current_dir = path.dirname(path.abspath(__file__))
media_dir = path.join(current_dir, "..", "media")
self.image_dir = path.join(media_dir, "final_boss", "gun")
initial_image = path.join(self.image_dir, "boss_gun_0.png")
self.frames = [
pg.image.load(path.join(self.image_dir, "boss_gun_2.png")),
pg.image.load(path.join(self.image_dir, "boss_gun_1.png")),
pg.image.load(path.join(self.image_dir, "boss_gun_0.png"))
]
self.initial_image = pg.transform.scale(pg.image.load(initial_image), (32, 120))
self.position_on_holder = (self.holder.rect.left + 4 * 4, self.holder.rect.top + 15 * 4)
self.image = pg.transform.rotate(self.initial_image, -self.angle_to_player)
self.rect = self.initial_image.get_rect(center= self.position_on_holder)
self.image.get_rect()
[documentos]
def shot(self) -> None:
"""
método que cria cada um dos projéteis
"""
#caso o inimigo ainda esteja vivo, ele irá atirar na direção do player
if self.player.alive():
self.shots.add(BossShot((self.rect.center[0], self.rect.center[1]), self.aim_vector, self.explosion, self.holder.health))
[documentos]
def update_image(self) -> None:
"""
método que atualiza a imagem da arma com base no nível de vida do boss
"""
if self.holder.health == 0:
self.initial_image = pg.transform.scale(self.frames[self.holder.health], (32, 120))
else:
self.initial_image = pg.transform.scale(self.frames[self.holder.health - 1], (32, 120))
[documentos]
def update(self, *args) -> None:
"""
método de atualização
"""
self.update_image()
#geração do vetor com a direção do player
self.aim_vector = (pg.math.Vector2(self.holder.rect.center) - pg.math.Vector2(self.player.rect.center)).normalize()
self.angle_to_player = self.still_vector.angle_to(self.aim_vector)
self.image = pg.transform.rotate(self.initial_image, -self.angle_to_player)
self.rect = self.image.get_rect()
#posicionamento da arma sobre o inimigo final
if self.holder.facing == 1:
self.position_on_holder = (self.holder.rect.left + 4 * 4, self.holder.rect.top + 14 * 4 - 1)
else:
self.position_on_holder = (self.holder.rect.right - 4 * 4, self.holder.rect.top + 14 * 4 - 1)
self.rect.center = self.position_on_holder