Ejemplo n.º 1
0
 def onShoot(event, sprite):
     if sprite.facing == "east":
         tsprite = bullet.Bullet(sprite.x, sprite.y, sprite.bullet_speed, 0)          
     else:
         tsprite = bullet.Bullet(sprite.x, sprite.y, -sprite.bullet_speed, 0)
     images = [engine.load_image('bullet.png')]
     ani = em.Animation(images)            
     event = em.SpriteEvent("onWalk", None, ani, 0, 0)
     tsprite.em.add(event)    
     images = [engine.load_image('bullet-explode.png')]
     ani = em.Animation(images)            
     event = em.SpriteEvent("onCollide", None, ani, 2, 0)
     tsprite.em.add(event)
     globals.bullets.append(tsprite)
     sprite.em.notify("onShoot", "end")
Ejemplo n.º 2
0
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.012
    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
Ejemplo n.º 3
0
class GreenSlime(WalkingMonster):
    """
    Зеленый слизень

    Медленный
    Среднее количество жизней
    Не очень большой урон
    Устойчивость к льду и отравлению (я сам отравление)
    Слабость к молниям
    """
    damage = 60
    size = (int(TILE_SIZE // 8 * 7), ) * 2
    frames = cut_sheet(load_image('green_slime_any.png', 'assets\\enemies'), 4,
                       2)
    frames += cut_sheet(load_image('green_slime_any.png', 'assets\\enemies'),
                        4, 2)

    death_frames = cut_sheet(
        load_image('green_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.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 * 5

        self.health = 80
        self.full_health = self.health
Ejemplo n.º 4
0
class FireSpell(Spell):
    damage = 40
    spell_type = Spell.FIRE
    mana_cost = 50
    UPDATE_TIME = 40
    speed = TILE_SIZE * 0.22
    acceleration = 2
    action_time = 0

    size = (TILE_SIZE // 4 * 3, ) * 2
    frames = cut_sheet(load_image('fire_laser.png', 'assets\\spells'), 6, 1,
                       size)
    frames += cut_sheet(load_image('fire_explosion.png', 'assets\\spells'), 7,
                        8, size)

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)

    # Звуки
    CAST_SOUND = pygame.mixer.Sound(
        concat_two_file_paths("assets/spells/audio", "cast_sound_2.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_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_14.ogg")),
        pygame.mixer.Sound(
            concat_two_file_paths("assets/spells/audio",
                                  "spell_sound_24.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)
Ejemplo n.º 5
0
    def __init__(self, position: tuple, *args):
        super().__init__(*args)

        # Изображение
        self.image = load_image("game_logo.png", path_to_folder="assets/UI")
        self.image = pygame.transform.scale2x(self.image)
        self.rect = self.image.get_rect()
        self.rect.centerx, self.rect.centery = position
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
class Chest(pygame.sprite.Sprite):
    size = (TILE_SIZE, TILE_SIZE)
    frames = cut_sheet(load_image("assets/sprites/tiles/CHEST.png"), 8, 1,
                       size)[0]
    back_of_chest = load_tile('back_of_chest.png')
    chest_group: pygame.sprite.Group
    UPDATE_TIME = 40

    def __init__(self, x, y, new_boxes_seed, index, *args):
        super().__init__(Chest.chest_group, *args)
        # Сохраняем индекс и сид, чтоб потом убрать из сида сундук, как открытый
        self.seed = new_boxes_seed
        self.index = index
        self.opened = False
        self.last_update_time = 0
        self.current_frame = 0
        self.update_time = self.UPDATE_TIME

        self.image = self.frames[self.current_frame]
        self.back_image = self.back_of_chest.copy()
        self.rect = self.image.get_rect().move(x * TILE_SIZE, y * TILE_SIZE)
        self.back_image_rect = self.back_image.get_rect()
        self.collider = Collider(x, y)

    def draw_back_image(self, screen):
        screen.blit(self.back_image, self.rect)

    def open(self):
        self.opened = True

    def update(self):
        # Если сундук не открыт, с ним ничего не происходит
        if not self.opened:
            return

        ticks = pygame.time.get_ticks()
        if ticks - self.last_update_time > self.update_time:
            self.last_update_time = ticks

            # Чтоб анимация замедлялась
            self.update_time *= 1.2

            # Если первый фрейм, убираем из сида и спавним предмет
            if self.current_frame == 0:
                spawn_item(*self.rect.center,
                           all_sprites=Entity.all_sprites,
                           k=5)
                self.seed[self.index] = '0'

            # Если изображения кончились, убиваем
            if self.current_frame >= len(self.frames):
                self.kill()
                return

            # Меняем изображение
            self.image = self.frames[self.current_frame]
            self.current_frame += 1
Ejemplo n.º 8
0
def show_instructions():
    """ Shows game instructions when first launched.
    """
    game.draw_background()
    intro = pygame.transform.smoothscale(engine.load_image("introduction.png"), game.screen.get_size())
    game.screen.blit(intro, intro.get_rect(center = game.screen.get_rect().center))
    pygame.display.update()
    game.dimmer.dim()
    engine.click_to_continue()
Ejemplo n.º 9
0
def load_tile(filename: str) -> pygame.surface.Surface:
    """
    Функция нужна для загрузки тайлов и их расширения до TILE_SIZE.
    (нужно, т.к. при использовании load_image код выглядит менее красивым)
    :param filename: Имя файла с тайлом
    :return: Поверхность, растянутого изображение
    """
    image = load_image(f'assets/sprites/tiles/{filename}')
    image = pygame.transform.scale(image, (TILE_SIZE, TILE_SIZE))
    return image
Ejemplo n.º 10
0
class PoisonSpell(Spell):
    """Заклинание отравления

    Отравляет противника,
    постепенно нанося урон
    Эффективно против:
    Огненных магов
    Неэффективно против:
    Зеленых слизеней"""
    spell_type = Spell.POISON
    damage = 10
    action_time = 10
    extra_damage = Entity.POISON_DAMAGE * action_time
    mana_cost = 50
    UPDATE_TIME = 40
    speed = TILE_SIZE * 0.2
    acceleration = 0.5

    size = (TILE_SIZE // 4 * 3, ) * 2
    frames = cut_sheet(load_image("assets/sprites/spells/poison_laser.png"), 7,
                       1, size)
    frames += cut_sheet(
        load_image("assets/sprites/spells/poison_explosion.png"), 11, 1, size)
    frames += cut_sheet(
        load_image("assets/sprites/spells/poison_explosion_1.png"), 25, 1,
        size)

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)

    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/cast_sound_3.ogg")
    SPELL_SOUNDS = (
        load_sound("assets/audio/sfx/spells/spell_sound_1.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_12.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_13.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_17.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_23.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_24.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_26.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_27.ogg"),
    )
Ejemplo n.º 11
0
class FlashSpell(Spell):
    """Заклинание молнии

    Бьёт через стены
    Эффективно против:
    Зеленых слизеней
    Неэффективно против:
    Зомби"""
    damage = 50
    spell_type = Spell.FLASH
    mana_cost = 150
    UPDATE_TIME = 60
    speed = TILE_SIZE * 3
    acceleration = 1
    damage_frame = 2
    action_time = 0

    size = (TILE_SIZE // 2 * 2, TILE_SIZE // 2 * 5)
    frames = cut_sheet(load_image("assets/sprites/tiles/EMPTY.png"), 1, 1,
                       size)
    frames += cut_sheet(load_image("assets/sprites/spells/light.png"), 15, 1,
                        size)

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)

    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/cast_sound_4.ogg")
    SPELL_SOUNDS = (
        load_sound("assets/audio/sfx/spells/spell_sound_3.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_4.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_5.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_6.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_7.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_8.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_9.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_10.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_11.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_14.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_26.ogg"),
    )
Ejemplo n.º 12
0
class SpellContainer:
    """Класс представляет UI элемент с отображением данных о заклинании."""

    # В этом случае фонт всегда будет общий у всех, поэтому это атрибут класса
    font = pygame.font.Font("assets\\UI\\pixel_font.ttf", 64)
    # Иконки кнопок джойстика, чтобы отображать кнопки для переключения заклиананий
    JOYSTICK_ICONS = {
        "o": load_image("joystick_o.png", path_to_folder="assets/UI/icons"),
        "x": load_image("joystick_x.png", path_to_folder="assets/UI/icons"),
        "triangle": load_image("joystick_triangle.png", path_to_folder="assets/UI/icons"),
        "square": load_image("joystick_square.png", path_to_folder="assets/UI/icons"),
        "L1": load_image("joystick_L1.png", path_to_folder="assets/UI/icons"),
    }

    def __init__(self, icon_filename: str, position: tuple):
        self.spell_icon = load_image(icon_filename, "assets/UI/icons")
        self.position = position

    def draw(self, screen: pygame.surface.Surface, is_joystick: bool, spell_key: str):
        """
        Рисует UI элемент на экране screen
        :param screen: Экран для отрисовки
        :param is_joystick: Подключен ли джойстик
        :param spell_key: Строка, представляющая либо ключ для вывода иконки
        для джойстика, либо текст для вывода возле иконки заклинания
        """
        # Иконка заклинания
        screen.blit(self.spell_icon, self.position)
        # Смещение между иконкой заклинания и кнопкой для переключения
        MARGIN = 25
        pos = (self.position[0] + MARGIN,
               self.position[1] - self.spell_icon.get_height())

        # Если подключён джойстик, то рисуется специальная иконка
        if is_joystick:
            screen.blit(SpellContainer.JOYSTICK_ICONS[spell_key], pos)
        # Иначе просто текст
        else:
            text_surface = SpellContainer.font.render(spell_key, True,
                                                       pygame.Color("white"))
            screen.blit(text_surface, pos)
Ejemplo n.º 13
0
    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.text = text
        self.font = load_game_font(text_size)
        # Базовое изображение
        self.text_surface = self.font.render(text, True, pygame.Color("white"))
        self.base_image = load_image(
            f"assets/sprites/UI/components/{base_button_filename}")
        self.base_image.blit(
            self.text_surface,
            self.text_surface.get_rect(
                center=self.base_image.get_rect().center))
        # Изображение при наведении
        self.hover_image = load_image(
            f"assets/sprites/UI/components/{hover_button_filename}")
        self.hover_image.blit(
            self.text_surface,
            self.text_surface.get_rect(
                center=self.hover_image.get_rect().center))
        # Текущее изображение
        self.image = self.base_image
        self.rect = self.image.get_rect()
        # Двигаем кнопку, но с учётом размера
        self.rect = self.rect.move(position[0] - self.rect.width / 2,
                                   position[1] - self.rect.height / 2)
Ejemplo n.º 14
0
    def update(self):
        # Проверка для смены кадра
        if pygame.time.get_ticks() - self.last_update_time > self.delay:
            self.current_frame_number += 1
            if self.current_frame_number > self.frames_end:
                # Переводим счётчик на начало
                self.current_frame_number = self.frames_start

            self.image = load_image(self.base_filename.format(self.current_frame_number),
                                    path_to_folder=self.path_to_folder)
            self.image = pygame.transform.scale(self.image, self.screen_size)
            self.last_update_time = pygame.time.get_ticks()
Ejemplo n.º 15
0
class VoidSpell(Spell):
    """Заклинание пустоты

    Урон по площади
    Эффективно против:
    Грязных слизеней
    Неэффективно против:
    Древних магов"""
    damage = 60
    spell_type = Spell.VOID
    mana_cost = 100
    speed = TILE_SIZE * 0.24
    acceleration = 3
    UPDATE_TIME = 40
    damage_frame = 2
    action_time = 0

    size = (TILE_SIZE * 3, ) * 2
    frames = cut_sheet(load_image("assets/sprites/spells/void_laser.png"), 10,
                       1)
    frames += cut_sheet(load_image("assets/sprites/spells/void_explosion.png"),
                        12, 2, size)
    frames += cut_sheet(
        load_image("assets/sprites/spells/void_explosions.png"), 10, 5, size)

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)

    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/cast_sound_5.ogg")
    SPELL_SOUNDS = (
        load_sound("assets/audio/sfx/spells/spell_sound_2.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_5.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_14.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_15.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_16.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_17.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_18.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_25.ogg"),
    )
Ejemplo n.º 16
0
class MessageBox:
    """
    Класс, представляющий диалог с сообщением, который закрывается
    при нажатии в любую область экрана
    """
    # Загружаем фоновое изображение
    background_image = load_image(
        "assets/sprites/UI/components/dialog_box.png")

    def __init__(self, text: str, text_size: int, position: tuple):
        self.font = load_game_font(text_size)  # шрифт
        self.image = self.background_image  # фон
        indent = 50  # Отступ
        text = text.strip()
        # Высчитывание размера для фона
        size = (max(int(text_size * 0.38 * max(map(len, text.split('\n')))),
                    300),
                round(indent +
                      len(text.split('\n')) * self.font.get_height() * 0.9))
        # Отмасштабированый фон с текстурой для красоты
        self.image = scale_frame(self.image, size, indent)
        self.rect = self.image.get_rect()
        self.rect.center = position  # местоположение
        # Текст для диалога
        self.texts = text.split('\n')
        # Так нужно для вывода сразу нескольких строк
        self.text_surfaces = [
            self.font.render(part.strip(), True, (255, 255, 255))
            for part in self.texts
        ]
        # Флаг для отрисовки (если True, то диалог рисуется)
        self.need_to_draw = True

    def update(self, was_click):
        if was_click:
            self.need_to_draw = False

    def draw(self, screen: pygame.surface.Surface):
        # Фоновое изображение
        screen.blit(self.image, self.rect.topleft)
        # Вывод текста
        margin = self.font.get_height() * 0.9
        # Следующая позиции y (по началу, просто отступ от самого верха)
        next_y = 20
        # Отрисовка поверхностей с текстом
        # (каждая поверхность - отдельная строка)
        for text_surface in self.text_surfaces:
            y_pos = self.rect.top + next_y
            screen.blit(
                text_surface,
                text_surface.get_rect(midtop=(self.rect.centerx, y_pos)))
            next_y += margin
Ejemplo n.º 17
0
class FlashSpell(Spell):
    damage = 50
    spell_type = Spell.FLASH
    mana_cost = 100
    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)
Ejemplo n.º 18
0
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
Ejemplo n.º 19
0
class IceSpell(Spell):
    """Ледяное заклинание

    На время замедляет противника
    (включая перезарядку заклинаний)
    Эффективно против:
    Демонов
    Неэффективно против:
    Грязных слизней"""
    damage = 25
    spell_type = Spell.ICE
    mana_cost = 40
    UPDATE_TIME = 40
    speed = TILE_SIZE * 0.3
    acceleration = 4
    action_time = 500

    size = (TILE_SIZE // 4 * 3, ) * 2
    frames = cut_sheet(load_image("assets/sprites/spells/ice_laser.png"), 30,
                       1, size)
    frames += cut_sheet(load_image("assets/sprites/spells/ice_explosion.png"),
                        28, 1, size)

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)
    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/cast_sound_1.ogg")
    SPELL_SOUNDS = (
        load_sound("assets/audio/sfx/spells/spell_sound_2.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_12.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_18.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_19.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_20.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_21.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_22.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_26.ogg"),
    )
Ejemplo n.º 20
0
class TeleportSpell(Spell):
    """Заклинание телепортации

    Переносит игрока в позицию прицела"""
    spell_type = Spell.TELEPORT
    damage = "---"
    mana_cost = 250
    UPDATE_TIME = 40
    speed = TILE_SIZE * 3
    acceleration = 0
    damage_frame = 2
    action_time = 0

    size = (TILE_SIZE // 4 * 7, ) * 2
    frames = cut_sheet(load_image("assets/sprites/tiles/EMPTY.png"), 1, 1,
                       size)
    frames += cut_sheet(load_image("assets/sprites/spells/teleport_puf.png"),
                        8, 1, size)

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)

    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/teleport_sound.ogg")
    SPELL_SOUNDS = (load_sound("assets/audio/sfx/spells/teleport_sound.ogg"), )

    def __init__(self, subject_x: float, subject_y: float, object_x: float,
                 object_y: float, extra_damage: float, object_group, *groups):
        super().__init__(subject_x, subject_y, object_x, object_y,
                         extra_damage, 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
Ejemplo n.º 21
0
class Torch(pygame.sprite.Sprite):
    frames = cut_sheet(load_image("assets/sprites/tiles/TORCH.png"), 8, 1,
                       (round(TILE_SIZE / 4 * 3), ) * 2)[0]

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(0)
    min_distance_to_player = 100
    update_sounds_channel = 0

    # Звуки
    BURNING_SOUND = load_sound("assets/audio/sfx/world/torch_sound.mp3")
    BURNING_SOUND.set_volume(DEFAULT_SOUNDS_VOLUME)

    def __init__(self, x: float, y: float, *groups):
        super().__init__(*groups)
        self.image = Torch.frames[randint(0, len(Torch.frames) - 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:
        ticks = pygame.time.get_ticks()
        # Через каждые назначенные промежутки времени обновляем минимальное расстояние до игрока
        if ticks - Torch.update_sounds_channel > 100:
            Torch.update_sounds_channel = ticks + randint(-20, 20)
            Torch.min_distance_to_player = 100
            return
        if ticks - self.update_time > 100:
            self.update_time = pygame.time.get_ticks()
            while 1:
                n = randint(0, len(self.frames) - 1)
                if n != self.cur_frame:
                    self.cur_frame = n
                    break
            self.image = Torch.frames[self.cur_frame]
        # Вычисляем расстояние до игрока и ставим с этим коэффиццентом громкость звука
        # Так он будет меняться, когда мы подходим к факелам и отходим
        distance_to_player = dist(player.rect.center, self.rect.center)
        Torch.min_distance_to_player = min(max(distance_to_player, 0.000001),
                                           Torch.min_distance_to_player)
        self.BURNING_SOUND.set_volume(
            min(
                DEFAULT_SOUNDS_VOLUME /
                (Torch.min_distance_to_player / TILE_SIZE) * 3, 1.2))
        if not self.sounds_channel.get_busy():
            self.sounds_channel.play(self.BURNING_SOUND)
Ejemplo n.º 22
0
    def __init__(self, text: str, text_size: int, position: tuple):
        # Фон
        self.image = load_image("dialog_box.png", path_to_folder="assets/UI")
        size = (round(self.image.get_width() * 1.2),
                round(self.image.get_height() * 1.2))
        self.image = pygame.transform.smoothscale(self.image, size)
        self.rect = self.image.get_rect()
        # Корректирование позиции в соответствии с размерами фона
        self.rect = self.rect.move(position[0] - self.image.get_width() * 0.5,
                                   position[1] - self.image.get_height() * 0.5)
        # Текст для диалога
        self.texts = text.split('\n')
        self.font = pygame.font.Font("assets\\UI\\pixel_font.ttf", text_size)
        # Так нужно для вывода сразу нескольких строк
        self.text_surfaces = [self.font.render(part, True, pygame.Color("white")) for part in self.texts]

        # Флаг для отрисовки (если True, то диалог рисуется)
        self.need_to_draw = True
Ejemplo n.º 23
0
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)
Ejemplo n.º 24
0
 def load_frame(self, frame_number: int) -> pygame.surface.Surface:
     """
     Метод загружает кадр анимации по переданному номеру
     :param frame_number: Номер кадра
     :return: Поверхность загруженного кадра
     """
     frame = load_image(
         f"{self.path_to_folder}/{self.filename.format(frame_number)}")
     # Отдельное масштабирование кадра, если необходимо
     if self.scale_2n:
         width, height = frame.get_size()
         # Вычисление коэффицента для масштабирования и само масштабирование
         coefficient = max(round(self.screen_size[0] / width + 0.5),
                           round(self.screen_size[1] / height + 0.5))
         frame = pygame.transform.scale(
             frame, (width * coefficient, height * coefficient))
     # Иначе просто приведение размера к размеру экрана
     else:
         frame = pygame.transform.scale(frame, self.screen_size)
     return frame
Ejemplo n.º 25
0
    def __init__(self, position: tuple, text: str, text_size: int):
        super().__init__()

        # События, которые будут вызываться 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.image = load_image("button.png", path_to_folder="assets/UI")
        # Текст
        self.text = text
        self.font = pygame.font.SysFont(pygame.font.get_default_font(), 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)
Ejemplo n.º 26
0
    def __init__(self, icon_filename: str, spell_class, player):
        # Иконка заклинания
        self.spell_icon = load_image(
            f"assets/sprites/UI/icons/{icon_filename}")
        self.rect = self.spell_icon.get_rect()
        self.w, self.h = self.spell_icon.get_size()  # размер иконки
        # Картинка затемнения
        self.locked = pygame.transform.scale(self.LOCKED, (self.w, self.h))
        self.mana_cost = spell_class.mana_cost  # Стоимость заклинания для игрока
        # ссылка на игрока для получение параметров, связанных с заклинаниями
        self.player = player
        # Информация, которая будет показана в рамке при наведении
        self.information = f'''{spell_class.__doc__}

        Урон: {spell_class.damage}{f' + {spell_class.extra_damage}' if spell_class.__name__ == 'PoisonSpell' else ''}
        {'Время действия: ' + str(spell_class.action_time) + ' c' 
        if spell_class.__name__ in ('IceSpell', 'PoisonSpell') else 'Мгновенное действие'}
        Затраты маны: {spell_class.mana_cost}'''.strip()
        # Диалоговое окно для вывода информации при наведении
        self.massage_box = MessageBox(self.information, 30, (0, 0))
        # время наведения, нужное для определение того, когда надо
        # отрисовать окно с информацией
        self.hover_time = 0
Ejemplo n.º 27
0
 def init(self):	    
     #  This should all be done by the manifest parser
     globals.marek = marek.marek(120, 100, 2)    # initialize the marek object        
     increment = 580/int(globals.spider_number)
     for i in range(20, 600, increment):            
         sprite = spider.spider(i, 420)          
         images = [engine.load_image('spider.png')]
         ani = em.Animation(images)            
         event = em.SpriteEvent("onIdle", None, ani, 0, 0)
         sprite.em.add(event)
         event = em.SpriteEvent("onWalkLeft", None, ani, 0, 0)
         sprite.em.add(event)
         event = em.SpriteEvent("onWalkRight", None, ani, 0, 0)
         sprite.em.add(event)
         event = em.SpriteEvent("onJump", None, ani, 0, 0)
         sprite.em.add(event)
         event = em.SpriteEvent("onFall", None, ani, 0, 0)
         sprite.em.add(event)            
         event = em.SpriteEvent("onShoot", None, ani, 0, 0)
         sprite.em.add(event)            
         event = em.Event("onCollide", None)
         sprite.em.add(event)            
         globals.spiders.append(sprite)  
     globals.camera = engine.Camera(globals.map, globals.marek, globals.window.width, globals.window.height)
Ejemplo n.º 28
0
 def __init__(self, position: tuple, *args):
     super().__init__(*args)
     # Изображение
     self.image = load_image("assets/sprites/UI/components/game_logo.png")
     self.rect = self.image.get_rect()
     self.rect.center = position
Ejemplo n.º 29
0
class PlayerIcon:
    """
    Класс, представляющий UI элемент с отображением данных об игроке
    или компаньёне
    """
    # В этом случае фонт всегда будет общий у всех, поэтому это атрибут класса
    font = load_game_font(32)
    # Изображение с иконкой игрока
    PLAYER_FACE = pygame.transform.scale2x(
        load_image('assets/sprites/UI/icons/player_face.png'))
    # Изображение с иконкой помошника
    ASSISTANT_FACE = pygame.transform.scale2x(
        load_image('assets/sprites/UI/icons/assistant_face.png'))
    # Рамка вокруг иконки
    FRAME = load_image('assets/sprites/UI/icons/player_icon_frame.png')
    # Иконка яда
    size = (40, 40)
    POISON_ICON = load_image('assets/sprites/UI/icons/poison_icon.png', size)

    def __init__(self, player_or_assistant):
        # Ссылка на игрока (или асистента) для получение необходимоых
        # параметров, таких как: здоровье, мана и т.п.
        self.player_or_assistant = player_or_assistant

    def draw(self,
             screen: pygame.surface.Surface,
             position=(0, 0),
             size_coefficient=1):
        """
        Рисует UI элемент на экране screen
        :param screen: Экран для отрисовки
        :param position: позиция отрисовки от левого верхнего угла экрана
        :param size_coefficient: Коэффицент размера иконки
        """
        # Позиция
        x1, y1 = (0, 0)
        # Пустое изображение всей иконки, куда будут отрисовываться части ниже
        image = pygame.surface.Surface(self.FRAME.get_size(), pygame.SRCALPHA)
        # Высчитывание длинны полосы здоровья
        health_length = round(264 * (self.player_or_assistant.health /
                                     self.player_or_assistant.full_health) +
                              0.5)
        # Поверхность со здоровьем
        health_line = pygame.surface.Surface((health_length, 24))
        health_line.fill((255, 30, 30))
        # Отрисовка полоски со здоровьем и количества здоровья
        image.blit(health_line, (x1 + 132, y1 + 12))
        image.blit(
            self.font.render(
                f'{round(self.player_or_assistant.health + 0.5)}/' +
                f'{self.player_or_assistant.full_health}', True,
                (255, 255, 255)), (x1 + 220, y1 + 10))
        # Высчитывание длинны полосы маны
        mana_length = round(264 * (self.player_or_assistant.mana /
                                   self.player_or_assistant.full_mana) + 0.5)
        # Поверхность с маной
        mana_line = pygame.surface.Surface((mana_length, 24))
        mana_line.fill((30, 30, 255))
        # Отрисовка полоски с маной и количества маны
        image.blit(mana_line, (x1 + 132, y1 + 52))
        image.blit(
            self.font.render(
                f'{round(self.player_or_assistant.mana + 0.5)}/' +
                f'{self.player_or_assistant.full_mana}', True,
                (255, 255, 255)), (x1 + 220, y1 + 50))
        # Если текущая иконка относится к игроку
        if self.player_or_assistant.__class__.__name__ == 'Player':
            screen.blit(
                self.font.render(f'{round(self.player_or_assistant.money)}',
                                 True, (255, 255, 30)),
                (self.FRAME.get_width() + 20, 20))
            image.blit(self.PLAYER_FACE, (x1 + 25, y1 + 20))
        else:
            image.blit(self.ASSISTANT_FACE, (x1 + 25, y1 + 20))
        # Отрисовка фона (рамки) на иконку
        image.blit(self.FRAME, (x1, y1))
        # Отрисовка пустого текста на иконке
        # (для смещения, т.е. по сути это декоративный эффект)
        text_surface = self.font.render('', True, (255, 255, 255))
        image.blit(text_surface, (x1 + 8, y1 + 14))
        # Вывод всей иклггки на экран с учётом коэффицента размера
        screen.blit(
            pygame.transform.scale(
                image, (int(self.FRAME.get_width() * size_coefficient),
                        int(self.FRAME.get_height() * size_coefficient))),
            position)
Ejemplo n.º 30
0
class SpellContainer:
    """Класс представляет UI элемент с отображением данных о заклинании"""

    # В этом случае шрифт всегда будет общий у всех, поэтому это атрибут класса
    font = load_game_font(32)
    mini_font = load_game_font(16)
    # Задержка курсора на иконке перед показом рамки
    delay_time = 35
    size = (39, 39)  # размер для иконок ниже
    # Иконки кнопок джойстика, чтобы отображать кнопки для вызова заклинаний
    JOYSTICK_ICONS = {
        "o":
        load_image("assets/sprites/UI/icons/joystick_o.png", size),
        "x":
        load_image("assets/sprites/UI/icons/joystick_x.png", size),
        "triangle":
        load_image("assets/sprites/UI/icons/joystick_triangle.png", size),
        "square":
        load_image("assets/sprites/UI/icons/joystick_square.png", size),
        "L1":
        load_image("assets/sprites/UI/icons/joystick_L1.png", size),
        "L2":
        load_image("assets/sprites/UI/icons/joystick_L2.png", size),
    }
    # Поверхность, которая отображается, если заклинание недоступно
    # (т.е. эффект замедления)
    LOCKED = pygame.surface.Surface((20, 20)).convert_alpha()
    LOCKED.fill((0, 0, 0, 180))
    # Рамка (фон) вокруг иконки с заклинанием
    FRAME = load_image('assets/sprites/UI/icons/spell_icon_frame.png')

    def __init__(self, icon_filename: str, spell_class, player):
        # Иконка заклинания
        self.spell_icon = load_image(
            f"assets/sprites/UI/icons/{icon_filename}")
        self.rect = self.spell_icon.get_rect()
        self.w, self.h = self.spell_icon.get_size()  # размер иконки
        # Картинка затемнения
        self.locked = pygame.transform.scale(self.LOCKED, (self.w, self.h))
        self.mana_cost = spell_class.mana_cost  # Стоимость заклинания для игрока
        # ссылка на игрока для получение параметров, связанных с заклинаниями
        self.player = player
        # Информация, которая будет показана в рамке при наведении
        self.information = f'''{spell_class.__doc__}

        Урон: {spell_class.damage}{f' + {spell_class.extra_damage}' if spell_class.__name__ == 'PoisonSpell' else ''}
        {'Время действия: ' + str(spell_class.action_time) + ' c' 
        if spell_class.__name__ in ('IceSpell', 'PoisonSpell') else 'Мгновенное действие'}
        Затраты маны: {spell_class.mana_cost}'''.strip()
        # Диалоговое окно для вывода информации при наведении
        self.massage_box = MessageBox(self.information, 30, (0, 0))
        # время наведения, нужное для определение того, когда надо
        # отрисовать окно с информацией
        self.hover_time = 0

    def draw(self, screen: pygame.surface.Surface, position: tuple,
             is_joystick: bool, spell_key: str):
        """
        Рисует UI элемент на экране screen
        :param screen: Экран для отрисовки
        :param position: Позиция отрисовки
        :param is_joystick: Подключен ли джойстик
        :param spell_key: Строка, представляющая либо ключ для вывода иконки
        для джойстика, либо текст для вывода возле иконки заклинания
        """
        x1, y1 = position  # координаты для отрисовки
        pos = (x1 + 2, y1 + 18)  # учёт отступа по размерам края рамки
        self.rect.topleft = pos
        # Иконка заклинания
        screen.blit(self.spell_icon, pos)
        # Отрисовка затемнения, елси заклинание сейчас недоступно
        if self.player.mana < self.mana_cost or \
                pygame.time.get_ticks() - self.player.shoot_last_time < self.player.between_shoots_range:
            screen.blit(self.locked, pos)
        # Отрисовка рамки вокруг иконки заклинания
        screen.blit(self.FRAME, position)
        # Смещение между иконкой заклинания и кнопкой для переключения
        pos = (x1 + 5, y1 + 14)
        # Если подключён джойстик, то рисуется специальная иконка элемента,
        # которая активирует заклинание
        if is_joystick:
            screen.blit(SpellContainer.JOYSTICK_ICONS[spell_key], pos)
        # Иначе просто текст кнопки с клавиатуры
        else:
            button_text = SpellContainer.font.render(spell_key, True,
                                                     (255, 255, 255))
            screen.blit(button_text, pos)
        # При наведении курсора на заклинание, рисуется табличка с информацией
        if self.rect.collidepoint(*self.player.scope.rect.center):
            # Если время наведения на иконку с заклинанием привысело порог, то
            # информация выводится
            if self.hover_time == self.delay_time:
                # Смещение окошка в сторону прицела и отрисовка
                self.massage_box.rect.bottomleft = self.player.scope.rect.center
                self.massage_box.draw(screen)
            else:
                self.hover_time += 1
        elif self.hover_time:
            self.hover_time = 0
        # Отрисовка цены маны за заклинание в правом нижнем углу
        pos = (x1 + self.h - 6, y1 + self.w - 2)  # позиция
        cost_text = SpellContainer.mini_font.render(str(self.mana_cost), True,
                                                    (255, 255, 255))  # цена
        screen.blit(cost_text, pos)
Ejemplo n.º 31
0
def execute(screen: pygame.surface.Surface):
    """
    Функция запускает меню игры на паузе на переданном экране. В
    зависимости от действий закрывает всю игру, либо продолжает дальше
    :param screen: Экран на котором надо отрисовывать менюв
    :return: Возвращает -1, если игру надо закрыть, None если нет
    """
    is_open = True
    clock = pygame.time.Clock()
    joystick = get_joystick() if check_any_joystick() else None

    # Смещение между UI элементами
    UI_MARGIN = 20
    # Фоновое изображение для всего экрана
    background_image = load_image("pause_menu_BG.png", "assets/UI")
    background_image = pygame.transform.scale(background_image,
                                              screen.get_size())
    # Фоновое игображение
    ui_background_image = load_image("pause_menu_UI_BG.png", "assets/UI")
    # Центральная координата всего меню на экране
    menu_top_left = (screen.get_width() * 0.5 -
                     ui_background_image.get_width() * 0.5,
                     screen.get_height() * 0.5 -
                     ui_background_image.get_height() * 0.5)

    # Создание UI элементов
    next_y = menu_top_left[1] + ui_background_image.get_width(
    ) * 0.5 - UI_MARGIN * 2.5
    button_continue = Button((screen.get_width() // 2, next_y),
                             "Продолжить",
                             32,
                             base_button_filename="button_1.png",
                             hover_button_filename="button_1_hover.png")

    next_y += button_continue.rect.width * 0.5 + UI_MARGIN
    button_exit = Button((screen.get_width() // 2, next_y),
                         "Выйти в меню",
                         32,
                         base_button_filename="button_1.png",
                         hover_button_filename="button_1_hover.png")

    # Добавление в группу
    UI_sprites = pygame.sprite.Group()
    UI_sprites.add(button_continue)
    UI_sprites.add(button_exit)

    # Изображение для курсора
    cursor_image = load_image("cursor.png", "assets/UI/icons")
    # координаты курсора
    cursor_x, cursor_y = screen.get_rect().center
    cursor_speed = 25  # скорость курсора (нужно если используется джойстик)

    # Цикл меню
    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_continue.text:
                    is_open = False
                    UI_sprites.empty()  # удаление всех спрайтов в группе
                    break

                # Выход
                if sender_text == button_exit.text:
                    return -1

        # Определение местоположения для курсора
        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
        screen.blit(ui_background_image, menu_top_left)
        # Рисуем весь 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)
Ejemplo n.º 32
0
def execute(screen: pygame.surface.Surface) -> int:
    """
    Функция запускает меню игры на паузе на переданном экране. В
    зависимости от действий закрывает всю игру, либо продолжает дальше
    :param screen: Экран на котором надо отрисовывать меню
    :return: Возвращает код. (1 - начать заного, -1 - закрыть игру, None - ничего)
    """
    is_open = True
    clock = pygame.time.Clock()
    joystick = get_joystick() if check_any_joystick() else None
    # Смещение между UI элементами
    margin = 20
    # Фоновое изображение для всего экрана
    background_image = AnimatedBackground(
        "pause_menu_BG_{0}.png", "assets/sprites/UI/backgrounds/pause_BG", 1,
        45, 25, screen.get_size())
    menu_width, menu_height = 280, 360
    # Фоновое игображение
    background_menu_image = scale_frame(
        load_image("assets/sprites/UI/backgrounds/pause_menu_UI_BG.png"),
        (menu_width, menu_height))
    # Центральная координата всего меню на экране
    menu_top_left = (screen.get_width() * 0.5 - menu_width * 0.5,
                     screen.get_height() * 0.25)
    # Группа со спрайтами интерфейса
    UI_sprites = pygame.sprite.Group()
    # Создание кнопок
    next_y = menu_top_left[1] + margin * 3.5  # позиция y следущего элемента
    titles = ("Продолжить", "Начать заново", "Выйти в меню"
              )  # заголовки кнопок
    for number in range(len(titles)):
        # Текущая кнопка
        button = Button((screen.get_width() // 2, next_y),
                        titles[number],
                        32,
                        base_button_filename="button_1.png",
                        hover_button_filename="button_1_hover.png")
        # Высчитывание следущей позиции по y со смещением
        next_y += button.rect.height + margin
        # Добавление в группу
        UI_sprites.add(button)
    # Изображение для курсора
    cursor_image = load_image("assets/sprites/UI/icons/cursor.png")
    # координаты курсора
    cursor_x, cursor_y = screen.get_rect().center
    cursor_speed = 40  # скорость курсора (нужно если используется джойстик)
    # Цикл меню
    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 == pygame.KEYDOWN:
                if event.key == CONTROLS["KEYBOARD_PAUSE"]:
                    is_open = False
                    UI_sprites.empty()  # удаление всех спрайтов в группе
                    break

            if event.type == Button.PRESS_TYPE:
                # Текст нажатой кнопки
                # (гарантированно есть, т.к. устанавливается при инициализации)
                sender_text = event.dict["sender_text"]
                # Продолжить
                if sender_text == titles[0]:
                    is_open = False
                    UI_sprites.empty()  # удаление всех спрайтов в группе
                    break
                # Начать заного
                if sender_text == titles[1]:
                    return 1
                # Выход
                if sender_text == titles[-1]:
                    return -1
        # Определение местоположения для курсора
        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))
        # Фоновое изображение окна
        background_image.update()
        screen.blit(background_image.image, (0, 0))
        # Фоновое изобраджение UI
        screen.blit(background_menu_image, menu_top_left)
        # Рисуем весь 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)
Ejemplo n.º 33
0
def execute(screen: pygame.surface.Surface) -> int:
    """
    Функция запускает главное меню игры на переданном экране. В
    зависимости от действий возвращает свой код, описанный в main.py
    :param screen: Экран, на котором надо отрисовывать меню
    :return: Код
    """
    is_open = True
    clock = pygame.time.Clock()
    joystick = get_joystick() if check_any_joystick() else None

    # Смещение между ui элементами
    button_margin = 55

    screen_center = screen.get_width() // 2
    # Создание ui элементов
    game_logo = LogoImage(
        (screen_center, screen.get_height() // 6 - button_margin))
    next_y = game_logo.rect.y + game_logo.rect.height + button_margin * 2

    button_play = Button((screen_center, next_y), "Играть", 32)
    next_y = button_play.rect.y + button_play.rect.height + button_margin
    # Если файла сохранения нет (т.е. игрок играет в первый раз), то эта кнопка
    # выделяется caps lock'ом (выделяются кнопки "управление" и "об игре")
    button_controls = Button(
        (screen_center, next_y),
        "Управление" if exists('data/save.txt') else "УПРАВЛЕНИЕ", 32)
    next_y = button_controls.rect.y + button_controls.rect.height + button_margin

    button_about = Button((screen_center, next_y),
                          "Об игре" if exists('data/save.txt') else "ОБ ИГРЕ",
                          32)
    next_y = button_about.rect.y + button_about.rect.height + button_margin

    button_exit = Button((screen_center, 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_exit)
    # Текущие диалог (может появлятся при нажатии кнопок)
    current_message_box = None
    # Текст появляющийся в сообщении при нажатии на кнопку "управление"
    control_text = """На клавиатуре: 
    WASD/Стрелки направлений - Двигаться
    Shift - Рывок
    1-5 - Заклинания атаки
    Space - Заклинание телепорта

    На джойстике PS4 (проводном): 
    PADS - Двигаться
    R1 - Рывок
    Заклинания см. на иконках
    """
    control_message_box = MessageBox(
        control_text, 32, (screen_center, screen.get_height() * 0.5))
    # Текст появляющийся в сообщении при нажатии на кнопку "об игре"
    about_text = """
    Pixelarious
Игра была создана как проект на тему PyGame для Яндекс Лицея.
Игра жанра Rogulite, поэтому смерть в игре перманентна.
Чтобы победить, надо пройти 10 уровней подземелья.
Чтобы убивать врагов, нужно использовать заклинания (см. управление).
Играть можно как на клавиутуре, так и на проводном джойстике от PS4.
Управление показывается внутри игры на главном окне при нажатии на кнопку "Управление". 
Его РЕКОМЕНДУЕТСЯ прочитать перед началом игры.
Ещё ОБЯЗАТЕЛЬНО посмотрите ОСОБЕННОСТИ заклинаний, НАВЕДЯ НА ИКОНКУ заклинания внизу.

Удачи в прохождении!
"""
    about_message_box = MessageBox(about_text, 32,
                                   (screen_center, screen.get_height() * 0.5))
    # Фоновое изоюражение
    background_image = load_image(
        "assets/sprites/UI/backgrounds/main_menu_BG.png")
    # Меняем размер картинки в зависимости от размера экрана
    background_image = pygame.transform.scale(background_image,
                                              screen.get_size())
    # Делаем курсор мыши невидимым и загружаем вместо него своё изображение
    pygame.mouse.set_visible(False)
    cursor_image = load_image("assets/sprites/UI/icons/cursor.png")
    # координаты курсора
    cursor_x, cursor_y = screen_center, screen.get_height() * 0.1
    # Фоновая музыка
    pygame.mixer.music.load("assets/audio/music/main_menu.ogg")
    # Воспроизведение музыки вечно
    pygame.mixer.music.play(-1)
    # Установка громкости
    pygame.mixer.music.set_volume(DEFAULT_MUSIC_VOLUME)
    # Цикл окна
    while is_open:
        # Переменная, становящайся True если было нажатие курсора
        # (предусмотрен как джойстик, так и обычная мышка)
        was_click = False
        # Обработка событий
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                is_open = False
            # Мышь
            if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
                was_click = True
            # Клавиши
            if event.type == pygame.KEYDOWN:
                if event.key in (pygame.K_KP_ENTER, pygame.K_SPACE,
                                 pygame.K_RETURN):
                    # Музыка затухает (1 секунду), т.к. главный экран закроется
                    pygame.mixer.music.fadeout(1000)
                    return 1
                if event.key == CONTROLS["KEYBOARD_PAUSE"]:
                    # Музыка затухает (1 секунду), т.к. главный экран закроется
                    pygame.mixer.music.fadeout(1000)
                    return 0
            # Кастомное событие нажатия на кнопку
            if event.type == Button.PRESS_TYPE:
                # Текст нажатой кнопки (нужно для определения какая кнопка нажата
                # (гарантированно есть, т.к. устанавливается при инициализации)
                sender_text = event.dict["sender_text"]
                # Управление
                if sender_text == button_controls.text:
                    current_message_box = control_message_box
                    current_message_box.need_to_draw = True
                # Об игре
                elif sender_text == button_about.text:
                    current_message_box = about_message_box
                    current_message_box.need_to_draw = True
                # Играть
                elif sender_text == button_play.text:
                    # Музыка затухает (1 секунду), т.к. главный экран закроется
                    pygame.mixer.music.fadeout(1000)
                    return 1
                # Выход
                elif sender_text == button_exit.text:
                    # Музыка затухает (1 секунду), т.к. главный экран закроется
                    pygame.mixer.music.fadeout(1000)
                    return 0

        # Определение местоположения для курсора
        if joystick:
            axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1)
            if abs(axis_x) >= JOYSTICK_SENSITIVITY:
                cursor_x += JOYSTICK_CURSOR_SPEED * axis_x
            if abs(axis_y) >= JOYSTICK_SENSITIVITY:
                cursor_y += JOYSTICK_CURSOR_SPEED * axis_y
            # Проверка на нажатие
            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.blit(background_image, (0, 0))
        # Рисуем весь ui
        ui_sprites.draw(screen)
        # Если есть диалог, то его тоже обновляем и рисуем
        if current_message_box:
            if current_message_box.need_to_draw:
                current_message_box.draw(screen)
            current_message_box.update(was_click)
        # Рисуем курсор поверх всего
        screen.blit(cursor_image, cursor_position)
        pygame.display.flip()
        # Обновляем состояние джойстика
        joystick = get_joystick() if check_any_joystick() else None
        clock.tick(FPS)
    return 0
Ejemplo n.º 34
0
 def load_images(self):
     images = {}
     images['stand_left'] = engine.load_image('marek-left.png')
     images['stand_right'] = engine.load_image('marek-right.png')
     return images
Ejemplo n.º 35
0
 def __init__(self, screen):
     self.screen = screen
     self.clock = pygame.time.Clock()
     self.fps = 60
     self.font = pygame.font.SysFont("arial", 12)
     self.worldMap = [
         [8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 6, 4, 4, 6, 4, 6, 4, 4, 4, 6, 4],
         [8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4],
         [8, 0, 3, 3, 0, 0, 0, 0, 0, 8, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6],
         [8, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6],
         [8, 0, 3, 3, 0, 0, 0, 0, 0, 8, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4],
         [8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 4, 0, 0, 0, 0, 0, 6, 6, 6, 0, 6, 4, 6],
         [8, 8, 8, 8, 0, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, 4, 6, 0, 0, 0, 0, 0, 6],
         [7, 7, 7, 7, 0, 7, 7, 7, 7, 0, 8, 0, 8, 0, 8, 0, 8, 4, 0, 4, 0, 6, 0, 6],
         [7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 0, 8, 0, 8, 0, 8, 8, 6, 0, 0, 0, 0, 0, 6],
         [7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 6, 0, 0, 0, 0, 0, 4],
         [7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 6, 0, 6, 0, 6, 0, 6],
         [7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 0, 8, 0, 8, 0, 8, 8, 6, 4, 6, 0, 6, 6, 6],
         [7, 7, 7, 7, 0, 7, 7, 7, 7, 8, 8, 4, 0, 6, 8, 4, 8, 3, 3, 3, 0, 3, 3, 3],
         [2, 2, 2, 2, 0, 2, 2, 2, 2, 4, 6, 4, 0, 0, 6, 0, 6, 3, 0, 0, 0, 0, 0, 3],
         [2, 2, 0, 0, 0, 0, 0, 2, 2, 4, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 3],
         [2, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 3],
         [1, 0, 0, 0, 0, 0, 0, 0, 1, 4, 4, 4, 4, 4, 6, 0, 6, 3, 3, 0, 0, 0, 3, 3],
         [2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 2, 2, 2, 6, 6, 0, 0, 5, 0, 5, 0, 5],
         [2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 0, 5, 0, 5, 0, 0, 0, 5, 5],
         [2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 5, 0, 5, 0, 5, 0, 5, 0, 5],
         [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
         [2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 5, 0, 5, 0, 5, 0, 5, 0, 5],
         [2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 0, 5, 0, 5, 0, 0, 0, 5, 5],
         [2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5],
     ]
     self.sprite_positions = [
         (20.5, 11.5, 2, 0, 0.0, 0.0, 0),  # green light in front of playerstart
         # green lights in every room
         (18.5, 4.5, 2, 0, 0.0, 0.0, 0),
         (10.0, 4.5, 2, 0, 0.0, 0.0, 0),
         (10.0, 12.5, 2, 0, 0.0, 0.0, 0),
         (3.5, 6.5, 2, 0, 0.0, 0.0, 0),
         (3.5, 20.5, 2, 0, 0.0, 0.0, 0),
         (3.5, 14.5, 2, 0, 0.0, 0.0, 0),
         (14.5, 20.5, 2, 0, 0.0, 0.0, 0),
         # row of pillars in front of wall: fisheye test
         (18.5, 10.5, 1, 0, 0.0, 0.0, 0),
         (18.5, 11.5, 1, 0, 0.0, 0.0, 0),
         (18.5, 12.5, 1, 0, 0.0, 0.0, 0),
     ]
     sprites = [
         load_image("pics/items/barrel.png", False, colorKey=(0, 0, 0)),
         load_image("pics/items/pillar.png", False, colorKey=(0, 0, 0)),
         load_image("pics/items/greenlight.png", False, colorKey=(0, 0, 0)),
         load_image("pics/items/pinky_l.png", False, colorKey=(0, 0, 0)),
         load_image("pics/items/pinky_r.png", False, colorKey=(0, 0, 0)),
     ]
     images = [
         load_image("pics/walls/eagle.png", False),
         load_image("pics/walls/redbrick.png", False),
         load_image("pics/walls/purplestone.png", False),
         load_image("pics/walls/greystone.png", False),
         load_image("pics/walls/bluestone.png", False),
         load_image("pics/walls/mossy.png", False),
         load_image("pics/walls/wood.png", False),
         load_image("pics/walls/colorstone.png", False),
         load_image("pics/walls/eagle.png", True),
         load_image("pics/walls/redbrick.png", True),
         load_image("pics/walls/purplestone.png", True),
         load_image("pics/walls/greystone.png", True),
         load_image("pics/walls/bluestone.png", True),
         load_image("pics/walls/mossy.png", True),
         load_image("pics/walls/wood.png", True),
         load_image("pics/walls/colorstone.png", True),
     ]
     self.wm = WorldManager(
         self.worldMap, x=22, y=11.5, dirx=-1, diry=0, planex=0, planey=0.66, sprites=sprites, images=images
     )
     self.view = View(self.wm)