class TeleportSpell(Spell): spell_type = Spell.TELEPORT mana_cost = 300 UPDATE_TIME = 40 speed = TILE_SIZE * 1 acceleration = 0 damage_frame = 2 action_time = 0 size = (TILE_SIZE // 4 * 7,) * 2 frames = cut_sheet(load_image('EMPTY.png', 'assets\\tiles'), 1, 1, size) frames += cut_sheet(load_image('teleport_puf.png', 'assets\\spells'), 8, 1, size) # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки CAST_SOUND = pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "teleport_sound.ogg")) CAST_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) SPELL_SOUNDS = ( pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "teleport_sound.ogg")), ) for sound in SPELL_SOUNDS: sound.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, subject_x: float, subject_y: float, object_x: float, object_y: float, object_group, *groups): super().__init__(subject_x, subject_y, object_x, object_y, object_group, *groups) self.start_sprite = pygame.sprite.Sprite() self.start_sprite.start_position = None self.start_sprite.point = None self.start_sprite.image = TeleportSpell.frames[0][0] self.start_sprite.rect = self.start_sprite.image.get_rect() self.start_sprite.rect.center = object_group[0].rect.center
class IceSpell(Spell): damage = 20 spell_type = Spell.ICE mana_cost = 30 UPDATE_TIME = 40 speed = TILE_SIZE * 0.3 acceleration = 4 action_time = 500 size = (TILE_SIZE // 4 * 3,) * 2 frames = cut_sheet(load_image('ice_laser.png', 'assets\\spells'), 30, 1, size) frames += cut_sheet(load_image('ice_explosion.png', 'assets\\spells'), 28, 1, size) # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки CAST_SOUND = pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "cast_sound_1.ogg")) CAST_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) SPELL_SOUNDS = ( pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_2.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_12.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_18.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_19.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_20.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_21.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_22.ogg")), pygame.mixer.Sound(concat_two_file_paths("assets/spells/audio", "spell_sound_26.ogg")), ) for sound in SPELL_SOUNDS: sound.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, subject_x: float, subject_y: float, object_x: float, object_y: float, object_group, *groups): super().__init__(subject_x, subject_y, object_x, object_y, object_group, *groups)
class PoisonSpell(Spell): damage = 10 spell_type = Spell.POISON mana_cost = 40 UPDATE_TIME = 40 speed = TILE_SIZE * 0.2 acceleration = 0.5 action_time = 500 size = (TILE_SIZE // 4 * 3, ) * 2 frames = cut_sheet(load_image('poison_laser.png', 'assets\\spells'), 7, 1, size) frames += cut_sheet(load_image('poison_explosion.png', 'assets\\spells'), 11, 1, size) frames += cut_sheet(load_image('poison_explosion_1.png', 'assets\\spells'), 25, 1, size) # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки CAST_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "cast_sound_3.ogg")) CAST_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) SPELL_SOUNDS = ( pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_1.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_12.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_13.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_17.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_23.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_24.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_26.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_27.ogg")), ) for sound in SPELL_SOUNDS: sound.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, subject_x: float, subject_y: float, object_x: float, object_y: float, object_group, *groups): super().__init__(subject_x, subject_y, object_x, object_y, object_group, *groups)
class VoidSpell(Spell): damage = 70 spell_type = Spell.VOID mana_cost = 120 speed = TILE_SIZE * 0.24 acceleration = 3 UPDATE_TIME = 40 damage_frame = 5 action_time = 0 size = (TILE_SIZE * 3, ) * 2 frames = cut_sheet(load_image('void_laser.png', 'assets\\spells'), 10, 1) frames += cut_sheet(load_image('void_explosion.png', 'assets\\spells'), 12, 2, size) frames += cut_sheet(load_image('void_explosions.png', 'assets\\spells'), 10, 5, size) # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки CAST_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "cast_sound_5.ogg")) CAST_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) SPELL_SOUNDS = ( pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_2.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_5.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_14.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_15.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_16.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_17.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_18.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_25.ogg")), ) for sound in SPELL_SOUNDS: sound.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, subject_x: float, subject_y: float, object_x: float, object_y: float, object_group, *groups): super().__init__(subject_x, subject_y, object_x, object_y, object_group, *groups)
class LongWizard(ShootingMonster): """ Большой маг Подвижный Среднее количество жизней Большой урон Устойчивость к молниям Слабость к огню """ damage = 20 size = (int(TILE_SIZE // 8 * 7), ) * 2 frames = cut_sheet(load_image('long_wizard_run.png', 'assets\\enemies'), 4, 2) frames += cut_sheet(load_image('long_wizard_idle.png', 'assets\\enemies'), 4, 2) death_frames = cut_sheet( load_image('long_wizard_dying.png', 'assets\\enemies'), 16, 1)[0] default_speed = TILE_SIZE * 0.02 look_directions = { (-1, -1): 1, (-1, 0): 1, (-1, 1): 1, (0, -1): 1, (0, 0): 0, (0, 1): 0, (1, -1): 0, (1, 0): 0, (1, 1): 0 } # Канал для звуков sounds_channel = pygame.mixer.Channel(2) # Звуки FOOTSTEP_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/enemies/audio", "wizard_rustle.mp3")) FOOTSTEP_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, x, y, *args): super().__init__(x, y, *args) self.visibility_range = TILE_SIZE * 13 self.health = 80 self.full_health = self.health self.reload_time = self.reload_time * 4 / 3
class Demon(WalkingMonster): """ Демон Мало жизней Быстрый Больно бьёт Устойчивойть к огню Слабость к льду """ damage = 50 size = (int(TILE_SIZE // 8 * 5), ) * 2 frames = cut_sheet(load_image('demon_run.png', 'assets\\enemies'), 4, 2, size) frames += cut_sheet(load_image('demon_idle.png', 'assets\\enemies'), 4, 2, size) death_frames = cut_sheet(load_image('demon_dying.png', 'assets\\enemies'), 16, 1)[0] UPDATE_TIME = 60 default_speed = TILE_SIZE * 0.023 look_directions = { (-1, -1): 1, (-1, 0): 1, (-1, 1): 1, (0, -1): 1, (0, 0): 0, (0, 1): 0, (1, -1): 0, (1, 0): 0, (1, 1): 0 } # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки FOOTSTEP_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/enemies/audio", "little_steps.mp3")) FOOTSTEP_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, x, y, *args): super().__init__(x, y, *args) self.alive = True self.visibility_range = TILE_SIZE * 7 self.health = 40 self.full_health = self.health
class DirtySlime(WalkingMonster): """ Грязный слизень Я как Зеленый, но чуть крепче Медленный Много жизней Не очень большой урон Устойчивость к льду и отравлению Слабость к молниям """ damage = 70 size = (int(TILE_SIZE // 8 * 7), ) * 2 frames = cut_sheet(load_image('dirty_slime_any.png', 'assets\\enemies'), 4, 2) frames += cut_sheet(load_image('dirty_slime_any.png', 'assets\\enemies'), 4, 2) death_frames = cut_sheet( load_image('dirty_slime_dying.png', 'assets\\enemies'), 16, 1)[0] default_speed = TILE_SIZE * 0.02 look_directions = { (-1, -1): 1, (-1, 0): 1, (-1, 1): 1, (0, -1): 1, (0, 0): 0, (0, 1): 0, (1, -1): 0, (1, 0): 0, (1, 1): 0 } # Канал для звуков sounds_channel = pygame.mixer.Channel(4) # Звуки FOOTSTEP_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/enemies/audio", "slime_sound_1.ogg")) FOOTSTEP_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, x, y, *args): super().__init__(x, y, *args) self.alive = True self.visibility_range = TILE_SIZE * 5 self.health = 100 self.full_health = self.health
class Wizard(ShootingMonster): """ Маг Подвижный Маловато жизней Средний урон Устойчивость к молниям Слабость к огню (МОЙ ПЛАЩ ГОРИТ) """ damage = 10 size = (TILE_SIZE // 8 * 7, ) * 2 frames = cut_sheet(load_image('wizard_run.png', 'assets\\enemies'), 4, 2, size) frames += cut_sheet(load_image('wizard_idle.png', 'assets\\enemies'), 4, 2, size) death_frames = cut_sheet(load_image('wizard_dying.png', 'assets\\enemies'), 16, 1)[0] default_speed = TILE_SIZE * 0.022 look_directions = { (-1, -1): 1, (-1, 0): 1, (-1, 1): 1, (0, -1): 1, (0, 0): 0, (0, 1): 0, (1, -1): 0, (1, 0): 0, (1, 1): 0 } # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки FOOTSTEP_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/enemies/audio", "wizard_rustle.mp3")) FOOTSTEP_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, x, y, *args): super().__init__(x, y, *args) self.alive = True self.visibility_range = TILE_SIZE * 9 self.health = 60 self.full_health = self.health
class Zombie(WalkingMonster): """ Зомби Не медленный, но и не быстрый Среднее количество жизней Средний урон Устойчивость к молниям (они двигают мои нейроны) Слабостей не обнаружено (земля пухом ученым) """ damage = 30 size = (int(TILE_SIZE // 4 * 3), ) * 2 frames = cut_sheet(load_image('zombie_run.png', 'assets\\enemies'), 4, 2) frames += cut_sheet(load_image('zombie_idle.png', 'assets\\enemies'), 4, 2) death_frames = cut_sheet(load_image('zombie_dying.png', 'assets\\enemies'), 16, 1)[0] default_speed = TILE_SIZE * 0.02 look_directions = { (-1, -1): 1, (-1, 0): 1, (-1, 1): 1, (0, -1): 1, (0, 0): 0, (0, 1): 0, (1, -1): 0, (1, 0): 0, (1, 1): 0 } # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки FOOTSTEP_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/enemies/audio", "stone_steps_1.mp3")) FOOTSTEP_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, x, y, *args): super().__init__(x, y, *args) self.alive = True self.visibility_range = TILE_SIZE * 6 self.health = 80 self.full_health = self.health
class Torch(pygame.sprite.Sprite): frames = cut_sheet(load_image('TORCH.png', 'assets\\tiles'), 8, 1, (round(TILE_SIZE / 4 * 3), ) * 2) # Канал для звуков sounds_channel = pygame.mixer.Channel(0) min_distance_to_player = 100 # Звуки BURNING_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/audio", "torch_sound.mp3")) BURNING_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME - SOUNDS_VOLUME_REDUCER) def __init__(self, x: float, y: float, *groups): super().__init__(*groups) self.image = Torch.frames[0][randint(0, len(Torch.frames[0]) - 1)] self.rect = self.image.get_rect().move(x * TILE_SIZE, y * TILE_SIZE) self.cur_frame = 0 self.update_time = pygame.time.get_ticks() def update(self, player=None) -> None: if not player: Torch.min_distance_to_player = 100000 return if pygame.time.get_ticks() - self.update_time > 100 + randint(-20, 20): self.update_time = pygame.time.get_ticks() while 1: n = randint(0, 7) if n != self.cur_frame: self.cur_frame = n break self.image = Torch.frames[0][self.cur_frame] dx, dy = player.rect.centerx - self.rect.centerx, player.rect.centery - self.rect.centery Torch.min_distance_to_player = min(max((dx**2 + dy**2)**0.5, 0.000001), Torch.min_distance_to_player) self.BURNING_SOUND.set_volume( min( DEFAULT_SOUNDS_VOLUME / (Torch.min_distance_to_player / TILE_SIZE) * 1, 1.2)) if not self.sounds_channel.get_busy(): self.sounds_channel.play(self.BURNING_SOUND)
class Door(pygame.sprite.Sprite): frames = [load_tile('DOOR.png'), load_tile('EMPTY.png')] # Канал для звуков sounds_channel = pygame.mixer.Channel(0) min_distance_to_player = 100 # Звуки OPEN_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/audio", "door_open.mp3")) OPEN_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) CLOSE_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/audio", "door_close.mp3")) CLOSE_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, x: float, y: float, *groups): super().__init__(*groups) self.image = Door.frames[0] self.rect = self.image.get_rect().move(x * TILE_SIZE, y * TILE_SIZE) self.collider = Collider(x, y) self.opened = False def update(self, player=None, enemies_group=None, player_group=None) -> None: if not player: Door.min_distance_to_player = 100000 return collide = pygame.sprite.spritecollide if not self.opened and (collide(self, enemies_group, False) or collide(self, player_group, False)): dx, dy = player.rect.centerx - self.rect.centerx, player.rect.centery - self.rect.centery Door.min_distance_to_player = min( max((dx**2 + dy**2)**0.5, 0.000001), Door.min_distance_to_player) volume = min( DEFAULT_SOUNDS_VOLUME / (Door.min_distance_to_player / TILE_SIZE) * 10, 1.2) self.OPEN_SOUND.set_volume(volume) if self.sounds_channel.get_busy(): self.sounds_channel.stop() self.sounds_channel.play(self.OPEN_SOUND) self.opened = True self.image = Door.frames[1] elif self.opened and not (collide(self, enemies_group, False) or collide(self, player_group, False)): dx, dy = player.rect.centerx - self.rect.centerx, player.rect.centery - self.rect.centery Door.min_distance_to_player = min( max((dx**2 + dy**2)**0.5, 0.000001), Door.min_distance_to_player) volume = min( DEFAULT_SOUNDS_VOLUME / (Door.min_distance_to_player / TILE_SIZE) * 10, 1.2) self.CLOSE_SOUND.set_volume(volume) if self.sounds_channel.get_busy(): self.sounds_channel.stop() self.sounds_channel.play(self.CLOSE_SOUND) self.opened = False self.image = Door.frames[0]
def execute(screen: pygame.surface.Surface, is_win=False): """ Функция запускает конечной экран (либо смерти, либо победы) :param screen: Экран на котором надо отрисовывать менюв :param is_win: Флаг, выиграл ли игрок """ is_open = True clock = pygame.time.Clock() joystick = get_joystick() if check_any_joystick() else None # Фоновое изображение для всего экрана if not is_win: animated_background = AnimatedBackground("death_{0}.png", 1, 23, 60, screen.get_size()) else: # Фоновая музыка для победителя pygame.mixer.music.load( concat_two_file_paths("assets/audio", "win_screen_BG.ogg")) pygame.mixer.music.play(-1) animated_background = AnimatedBackground("win_{0}.png", 1, 8, 60, screen.get_size()) # Лого игры logo = LogoImage((screen.get_width() * 0.5, screen.get_height() * 0.1)) # Кнопка возвращения в меню button_exit = Button((screen.get_width() // 2, screen.get_height() * 0.9), "Вернутся в меню", 32, base_button_filename="button_1.png", hover_button_filename="button_1_hover.png") # Добавление в группу UI_sprites = pygame.sprite.Group() UI_sprites.add(logo) UI_sprites.add(button_exit) # Изображение для курсора cursor_image = load_image("cursor.png", "assets/UI/icons") # координаты курсора cursor_x, cursor_y = screen.get_rect().center cursor_speed = 15 # скорость курсора (нужно если используется джойстик) # Т.к. игрок завершил игру, то файл с сохранением будет перезаписан if os.path.isfile("data/save.txt"): with open('data/save.txt', 'r+', encoding="utf-8") as file: file.truncate(0) # Цикл меню while is_open: # Переменная, становящайся True если было нажатие курсора # (предусмотрен как джойстик, так и обычная мышка) was_click = False # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: is_open = False break if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: was_click = True if event.type == Button.PRESS_TYPE: # Текст нажатой кнопки # (гарантированно есть, т.к. устанавливается при инициализации) sender_text = event.dict["sender_text"] # Выход if sender_text == button_exit.text: return # Определение местоположения для курсора if joystick: axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) cursor_x += cursor_speed * axis_x if abs( axis_x) >= JOYSTICK_SENSITIVITY else 0 cursor_y += cursor_speed * axis_y if abs( axis_y) >= JOYSTICK_SENSITIVITY else 0 # Проверка на нажатие was_click = joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]) else: cursor_x, cursor_y = pygame.mouse.get_pos() cursor_position = (cursor_x, cursor_y) # Обновляем все UI элементы UI_sprites.update(cursor_position, was_click) # Очистка экрана screen.fill((0, 0, 0)) animated_background.update() # Вывод текущего кадра фонового изображения screen.blit(animated_background.image, (0, 0)) # Рисуем весь UI UI_sprites.draw(screen) # Рисуем курсор поверх всего screen.blit(cursor_image, cursor_position) pygame.display.flip() # Обновляем состояние джойстика joystick = get_joystick() if check_any_joystick() else None clock.tick(FPS)
class Button(pygame.sprite.Sprite): """ Класс отвечающий за кнопку в UI элементах """ # Типы событий PRESS_TYPE = pygame.USEREVENT + 1 HOVER_TYPE = pygame.USEREVENT + 2 # Звук при наведении HOVER_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/audio", "button_hover.wav")) HOVER_SOUND.set_volume(DEFAULT_HOVER_SOUND_VOLUME) def __init__(self, position: tuple, text: str, text_size: int, base_button_filename="button.png", hover_button_filename="button_hover.png", *args): super().__init__(*args) # События, которые будут вызываться pygame внутри update # (с помощью sender_text будет определено какая кнопка нажата) self.PRESS_EVENT = pygame.event.Event(Button.PRESS_TYPE, {"sender_text": text}) self.HOVER_EVENT = pygame.event.Event(Button.HOVER_TYPE, {"sender_text": text}) # Свойство, чтобы при наведении звук воспроизводился только один раз self.was_sound_played = False # Базовое изображение self.base_image = load_image(base_button_filename, path_to_folder="assets/UI") # Изображение при наведении self.hover_image = load_image(hover_button_filename, path_to_folder="assets/UI") # Текущее изображение self.image = self.base_image # Текст self.text = text self.font = pygame.font.Font("assets\\UI\\pixel_font.ttf", text_size) self.text_surface = self.font.render(text, True, pygame.Color("white")) # Выводим текст поверх кнопки self.image.blit( self.text_surface, self.text_surface.get_rect(center=self.image.get_rect().center)) self.rect = self.image.get_rect() # Двигаем кнопку, но с учётом размера self.rect = self.rect.move(position[0] - self.rect.width / 2, position[1] - self.rect.height / 2) def update(self, scope_position: tuple, was_click: bool, *args) -> None: """ Метод обновляет состояние кнопки. Если что-то произошло, то вызывается соответствующий метод, и меняется sprite (если нужно) :param scope_position: Кортеж координат курсора или прицела, если подключён джойстик :param was_click: Было ли произведено нажатие """ # Проверяем наличие колизии курсора с кнопкой if self.rect.collidepoint(*scope_position): # Добавляем нужное событие в конец списка событий if was_click: pygame.event.post(self.PRESS_EVENT) else: # Если звук наведения не был воспроизведён, то он воспроизводится if not self.was_sound_played: Button.HOVER_SOUND.play() self.was_sound_played = True pygame.event.post(self.HOVER_EVENT) # Меняем изображение self.image = self.hover_image else: self.image = self.base_image # Т.к. на кнопку наведения нет, то сбрасываем свойство self.was_sound_played = False # Заного выводим текст поверх кнопки self.image.blit( self.text_surface, self.text_surface.get_rect(center=self.image.get_rect().center))
class FlashSpell(Spell): damage = 50 spell_type = Spell.FLASH mana_cost = 200 UPDATE_TIME = 60 speed = TILE_SIZE * 0.5 acceleration = 1 damage_frame = 5 action_time = 0 size = (TILE_SIZE // 2 * 3, ) * 2 frames = cut_sheet(load_image('EMPTY.png', 'assets\\tiles'), 1, 1, size) frames += cut_sheet(load_image('light.png', 'assets\\spells'), 15, 1, size) # Канал для звуков sounds_channel = pygame.mixer.Channel(3) # Звуки CAST_SOUND = pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "cast_sound_4.ogg")) CAST_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME) SPELL_SOUNDS = ( pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_3.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_4.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_5.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_6.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_7.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_8.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_9.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_10.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_11.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_14.ogg")), pygame.mixer.Sound( concat_two_file_paths("assets/spells/audio", "spell_sound_26.ogg")), ) for sound in SPELL_SOUNDS: sound.set_volume(DEFAULT_SOUNDS_VOLUME) def __init__(self, subject_x: float, subject_y: float, object_x: float, object_y: float, object_group, *groups): super().__init__(subject_x, subject_y, object_x, object_y, object_group, *groups)
def execute(screen: pygame.surface.Surface) -> int: """ Функция запускает главное меню игры на переданном экране. В зависимости от действий возвращает свой код 0 - была нажата кнопка выйти 1 - была нажата кнопка играть :param screen: Экран на котором надо отрисовывать менюв :return: Код """ is_open = True clock = pygame.time.Clock() joystick = get_joystick() if check_any_joystick() else None # Смещение между UI элементами UI_MARGIN = 55 # Создание UI элементов game_logo = LogoImage( (screen.get_width() // 2, screen.get_height() // 6 - UI_MARGIN)) next_y = game_logo.rect.y + game_logo.rect.height + UI_MARGIN * 2 button_play = Button((screen.get_width() // 2, next_y), "Играть", 32) next_y = button_play.rect.y + button_play.rect.height + UI_MARGIN button_controls = Button((screen.get_width() // 2, next_y), "Управление", 32) next_y = button_controls.rect.y + button_controls.rect.height + UI_MARGIN button_about = Button((screen.get_width() // 2, next_y), "Об игре", 32) next_y = button_about.rect.y + button_about.rect.height + UI_MARGIN button_authors = Button((screen.get_width() // 2, next_y), "Авторы", 32) next_y = button_authors.rect.y + button_authors.rect.height + UI_MARGIN button_exit = Button((screen.get_width() // 2, next_y), "Выйти", 32) # Добавление в группу UI_sprites = pygame.sprite.Group() UI_sprites.add(game_logo) UI_sprites.add(button_play) UI_sprites.add(button_controls) UI_sprites.add(button_about) UI_sprites.add(button_authors) UI_sprites.add(button_exit) # Текущие диалог (может появлятся при нажатии кнопок) current_message_box = None # Фоновое изоюражение background_image = load_image("main_menu_BG.png", "assets/UI") # Меняем размер картинки в зависимости от размера экрана background_image = pygame.transform.scale( background_image, (screen.get_width(), screen.get_height())) # Делаем курсор мыши невидимым и загружаем вместо него своё изображение pygame.mouse.set_visible(False) cursor_image = load_image("cursor.png", "assets/UI/icons") # координаты курсора cursor_x, cursor_y = screen.get_width() * 0.5, screen.get_height() * 0.1 cursor_speed = 30 # скорость курсора (нужно если используется джойстик) # Фоновая музыка pygame.mixer.music.load( concat_two_file_paths("assets/audio", "main_menu.ogg")) # Воспроизведение музыки вечно pygame.mixer.music.play(-1) # Установка громкости pygame.mixer.music.set_volume(DEFAULT_MUSIC_VOLUME) # Переменная, становящайся True если было нажатие курсора # (предусмотрен как джойстик, так и обычная мышка) was_click = False # Цикл окна while is_open: # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: is_open = False break if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: was_click = True if event.type == Button.PRESS_TYPE: # Текст нажатой кнопки # (гарантированно есть, т.к. устанавливается при инициализации) sender_text = event.dict["sender_text"] # Управление if sender_text == button_controls.text: text = str("Базовое управление:\n" + "На клавиатуре: WASD - двигаться; Q - рывок\n" + "На джойстике: PADS - двигаться; R1 - рывок\n") current_message_box = MessageBox( text, 30, (screen.get_width() * 0.5, screen.get_height() * 0.5)) continue # Об игре if sender_text == button_about.text: text = str("Игра жанра Rogulite, в \n" "которой надо пройти \n" + "сквозь подземелье, заполненное врагами.\n" "Желаем удачи\n") current_message_box = MessageBox( text, 30, (screen.get_width() * 0.5, screen.get_height() * 0.5)) continue # Авторы if sender_text == button_authors.text: text = str("Никита Сошнев (Nik4ant)\n" "Максим Рудаков (Massering)") current_message_box = MessageBox( text, 30, (screen.get_width() * 0.5, screen.get_height() * 0.5)) continue # Музыка затухает (1 секунду), т.к. главный экран закроется pygame.mixer.music.fadeout(1000) # Проверяем какая кнопка была нажата if sender_text == button_play.text: return 1 elif sender_text == button_exit.text: return 0 # Определение местоположения для курсора if joystick: axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) cursor_x += cursor_speed * axis_x if abs( axis_x) >= JOYSTICK_SENSITIVITY else 0 cursor_y += cursor_speed * axis_y if abs( axis_y) >= JOYSTICK_SENSITIVITY else 0 # Проверка на нажатие was_click = joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]) else: cursor_x, cursor_y = pygame.mouse.get_pos() cursor_position = (cursor_x, cursor_y) # Обновляем все UI элементы UI_sprites.update(cursor_position, was_click) # Очистка экрана screen.fill((0, 0, 0)) # Фоновое изобраджение screen.blit(background_image, (0, 0)) # Рисуем весь UI UI_sprites.draw(screen) # Если есть диалог, то его тоже обновляем и рисуем if current_message_box: current_message_box.update(was_click) if current_message_box.need_to_draw: current_message_box.draw(screen) # Рисуем курсор поверх всего screen.blit(cursor_image, cursor_position) pygame.display.flip() # Обновляем состояние джойстика joystick = get_joystick() if check_any_joystick() else None was_click = False clock.tick(FPS)