Exemple #1
0
class GroundItem(pygame.sprite.Sprite):
    sprites_group: pygame.sprite.Group

    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(1)
    # Звуки
    SOUNDS = {
        "meat": load_sound("assets/audio/sfx/items/meat_sound_1.mp3"),
        "money": load_sound("assets/audio/sfx/items/money_sound.mp3"),
    }
    # Изображения
    size = (int(TILE_SIZE * 0.6), ) * 2
    IMAGES = {
        "meat": load_image("assets/sprites/items/meat.png", size),
        "money": load_image("assets/sprites/items/money.png", size),
    }

    def __init__(self, item_type: str, count: int, x: float, y: float,
                 all_sprites, *groups):
        super().__init__(all_sprites, GroundItem.sprites_group, *groups)

        self.type = item_type  # тип предмета
        self.count = abs(int(count))

        if not self.count:
            self.kill()

        self.image, self.sound = GroundItem.IMAGES[
            self.type], GroundItem.SOUNDS[self.type]
        self.sound.set_volume(DEFAULT_SOUNDS_VOLUME * 3)
        self.image = self.image.copy()
        self.rect = self.image.get_rect()
        self.rect.center = x, y
        self.collider = Collider(x, y)

        for other in pygame.sprite.spritecollide(self,
                                                 GroundItem.sprites_group,
                                                 False):
            other: GroundItem
            if self.type == other.type and other != self:
                self.count = self.count + other.count
                other.kill()

        font = load_game_font(32)

        if self.count > 1:
            count_text = font.render(str(self.count), True, (255, 255, 255))
            rect = count_text.get_rect()
            rect.center = self.rect.right - rect.w // 2, self.rect.bottom - rect.h // 2
            self.image.blit(
                count_text,
                count_text.get_rect(
                    bottomright=self.image.get_rect().bottomright))
Exemple #2
0
class FireSpell(Spell):
    """Огненное заклинание

    Обычное
    Эффективно против:
    Зомби, Древних магов
    Неэффективно против:
    Демонов, Огненных магов"""
    damage = 50
    spell_type = Spell.FIRE
    mana_cost = 60
    UPDATE_TIME = 40
    speed = TILE_SIZE * 0.22
    acceleration = 2
    action_time = 0

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

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

    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/cast_sound_2.ogg")
    SPELL_SOUNDS = (
        load_sound("assets/audio/sfx/spells/spell_sound_1.ogg"),
        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_14.ogg"),
        load_sound("assets/audio/sfx/spells/spell_sound_24.ogg"),
    )
Exemple #3
0
class CallZombiesSpell(Spell):
    """
    Заклинание спавна монстров (2-4). Самой реализации спавна тут нет,
    это просто спецэффект (в данном случае звуки)
    """
    # Канал для звуков
    sounds_channel = pygame.mixer.Channel(3)
    # Звуки
    CAST_SOUND = load_sound("assets/audio/sfx/spells/call_zombies.ogg",
                            volume=DEFAULT_SOUNDS_VOLUME * 1.5)
Exemple #4
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
Exemple #5
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"),
    )
Exemple #6
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"),
    )
Exemple #7
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)
Exemple #8
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"),
    )
class Button(pygame.sprite.Sprite):
    """Класс, представляющий кнопку в UI элементах"""

    # Типы событий
    PRESS_TYPE = pygame.USEREVENT + 1
    HOVER_TYPE = pygame.USEREVENT + 2
    # Звук при наведении
    HOVER_SOUND = load_sound("assets/audio/sfx/UI/button_hover.wav")

    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)

    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
Exemple #10
0
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
    update_sounds_channel = 0

    # Звуки
    OPEN_SOUND = load_sound("assets/audio/sfx/world/door_open.mp3")
    CLOSE_SOUND = load_sound("assets/audio/sfx/world/door_close.mp3")

    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:
        ticks = pygame.time.get_ticks()
        if ticks - Door.update_sounds_channel > 100:
            # каждый назначенный промежуток времени сбрасываем минимальную дистанцию до игрока
            Door.update_sounds_channel = ticks
            Door.min_distance_to_player = 100
            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]
Exemple #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"),
    )