예제 #1
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"),
    )
예제 #2
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
예제 #3
0
class FireSpell(Spell):
    damage = 50
    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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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
예제 #9
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.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
예제 #10
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
예제 #11
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
예제 #12
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"),
    )
예제 #13
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"),
    )
예제 #14
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"),
    )
예제 #15
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"),
    )
예제 #16
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
예제 #17
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)
예제 #18
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)
예제 #19
0
class Entity(pygame.sprite.Sprite):
    """
    Класс, отвечающий за предстовление базовой сущности в игре
    """

    # Группа со спрайтами, которые считаются физическими объектами
    # общими для всех сущностей.
    collisions_group: pygame.sprite.Group

    WAITING_TIME = 2000
    UPDATE_TIME = 120
    HEALTH_LINE_WIDTH = 5
    HEALTH_LINE_TIME = 5000

    POISON_DAMAGE = 0.075

    size = (int(TILE_SIZE), ) * 2
    sleeping_frames = cut_sheet(
        load_image('sleep_icon_spritesheet.png', 'assets\\enemies'), 4, 1)
    poison_frames = cut_sheet(
        load_image('poison_static.png', 'assets\\spells'), 5, 1, size)[0]

    def __init__(self, x: float, y: float, *args):
        # Конструктор класса Sprite
        super().__init__(*args)

        # Изображение
        self.cur_frame = 0
        self.image = self.__class__.frames[0][self.cur_frame]
        self.last_update = pygame.time.get_ticks()
        self.width, self.height = self.image.get_size()

        self.last_damage_time = -Entity.HEALTH_LINE_TIME
        self.sleeping_time = None
        self.cur_poison_frame = 0
        self.poison_static_time = 0
        self.ice_buff = 0
        self.poison_buff = 0

        self.start_position = x, y
        self.point = None

        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y
        self.collider = Collider(self.rect.centerx, self.rect.centery)

        # Скорость
        self.dx = self.dy = 0

        # Направление взгляда
        self.look_direction_x = 0
        self.look_direction_y = 1

    def update(self) -> None:
        if self.ice_buff:
            self.ice_buff -= 1
            self.speed = self.__class__.default_speed * 0.3
        else:
            self.speed = self.__class__.default_speed

        if self.poison_buff:
            self.poison_buff -= 1
            self.get_damage(Entity.POISON_DAMAGE)

    def move(self, dx, dy):
        """
        Метод передвижения
        Сдвинется на указанные параметры, если там свободно

        :param dx: Изменение координаты по Х
        :param dy: Изменение координаты по Y
        :return: None
        """
        if not self.alive:
            return
        # Запоминаем координаты
        pos = self.rect.x, self.rect.y

        self.rect.x = round(self.rect.x + dx)
        self.collider.update(self.rect.centerx, self.rect.centery)

        # Если плохо, возвращаем к исходному
        if pygame.sprite.spritecollide(self.collider, Entity.collisions_group,
                                       False):
            self.rect.x = pos[0]
            self.dx = 0
            if self.__class__.__name__.lower() == "player":
                self.dash_force_x = 0
                self.dash_direction_x = self.look_direction_x
                self.dash_force_y = 0
                self.dash_direction_y = self.look_direction_y

        self.rect.y = round(self.rect.y + dy)
        self.collider.update(self.rect.centerx, self.rect.centery)

        # Если плохо, возвращаем к исходному
        if pygame.sprite.spritecollide(self.collider, Entity.collisions_group,
                                       False):
            self.rect.y = pos[1]
            self.dy = 0
            if self.__class__.__name__.lower() == "player":
                self.dash_force_x = 0
                self.dash_direction_x = self.look_direction_x
                self.dash_force_y = 0
                self.dash_direction_y = self.look_direction_y

    def update_frame_state(self, n=0):
        """
        Воспроизводит звук и сменяет кадр анимации

        :param n: если есть, сдвигает номер анимации (стояние вместо движения)
        :return: None
        """
        tick = pygame.time.get_ticks()
        if tick - self.last_update > self.UPDATE_TIME:
            self.last_update = tick

            if not self.alive:
                self.cur_frame = self.cur_frame + 1
                if self.cur_frame >= len(self.__class__.death_frames) - 1:
                    for group in self.groups():
                        group.remove(self)
                try:
                    self.image = self.__class__.death_frames[self.cur_frame]
                except IndexError:
                    pass
                return

            look = self.__class__.look_directions[self.look_direction_x,
                                                  self.look_direction_y]
            look += n
            self.cur_frame = (self.cur_frame + 1) % len(
                self.__class__.frames[look])
            self.image = self.__class__.frames[look][self.cur_frame]
            if (self.__class__.__name__ != 'Player' and
                    DEFAULT_SOUNDS_VOLUME * 200 / self.distance_to_player > 0.1
                    and (look < 2 or 'Slime' in self.__class__.__name__
                         or 'Demon' in self.__class__.__name__)
                    and not self.sounds_channel.get_busy()):
                self.FOOTSTEP_SOUND.set_volume(
                    min(
                        DEFAULT_SOUNDS_VOLUME /
                        (self.distance_to_player / TILE_SIZE) * 3, 1))
                self.sounds_channel.play(self.FOOTSTEP_SOUND)

    def draw_health_bar(self, screen):
        """
        Функция отрисовки полоски здоровья

        :param screen: Экран отрисовки
        :return: None
        """
        if abs(pygame.time.get_ticks() -
               self.last_damage_time) > Entity.HEALTH_LINE_TIME:
            return

        line_width = Entity.HEALTH_LINE_WIDTH
        x, y = self.rect.centerx, self.rect.centery
        width, height = self.rect.size
        x1, y1 = x - width * 0.5, y - height * 0.5
        pygame.draw.rect(screen, 'grey',
                         (x1 - 1, y1 - 10 - 1, width + 2, line_width + 2))
        health_length = width * max(self.health, 0) / self.full_health
        color = '#00b300' if str(
            self.__class__.__name__) == 'Player' else 'red'
        pygame.draw.rect(screen, color,
                         (x1, y1 - 10, health_length, line_width))

    def draw_sign(self, screen):
        """
        Отрисовка знака сна (Z-Z-Z) или восклицательного знака.

        :param screen: Экран отрисовки
        :return: None
        """
        if not self.alive:
            return

        ticks = pygame.time.get_ticks()
        if self.poison_buff:
            if ticks - self.poison_static_time > Entity.UPDATE_TIME:
                self.poison_static_time = ticks
                self.cur_poison_frame = (self.cur_poison_frame + 1) % len(
                    Entity.poison_frames)
            screen.blit(Entity.poison_frames[self.cur_poison_frame],
                        (self.rect.x, self.rect.y))

        if self.player_observed:
            font = pygame.font.Font("assets\\UI\\pixel_font.ttf", 96)
            text = font.render("!", True, (250, 20, 20))
            screen.blit(text, (self.rect.centerx, self.rect.y - 60))

        if not self.player_observed:
            if not self.sleeping_time or ticks - self.sleeping_time >= 250:
                if not self.sleeping_time:
                    self.cur_sleeping_frame = 0
                self.cur_sleeping_frame = (self.cur_sleeping_frame + 1) % len(
                    Entity.sleeping_frames[0])
                self.sleeping_time = ticks
            screen.blit(Entity.sleeping_frames[0][self.cur_sleeping_frame],
                        (self.rect.centerx + 10, self.rect.y - 35))

    def get_damage(self, damage, spell_type='', action_time=0):
        """
        Получение дамага

        :param damage: Столько здоровья надо отнять
        :return: None
        """

        if spell_type == 'ice':
            self.ice_buff += action_time
        if spell_type == 'poison':
            self.poison_buff += action_time

        self.last_damage_time = pygame.time.get_ticks()
        damage *= 10000
        damage += randint(-damage * 0.1, damage * 0.1)
        damage /= 10000
        self.health -= damage
        if self.health <= 0:
            self.death()

    def set_first_frame(self):
        """
        Установка первого спрайта

        :return: None
        """
        look = self.__class__.look_directions[self.look_direction_x,
                                              self.look_direction_y]
        self.cur_frame = 0
        self.image = self.__class__.frames[look][self.cur_frame]

    @staticmethod
    def set_global_collisions_group(group: pygame.sprite.Group):
        """
        Метод устанавливает группу со спрайтами, которые будут считаться
        физическими объектами для всех сущностей на уровне.
        (Кроме индивидуальных спрайтов у конкретных объектов,
        например у врагов будет отдельное взаимодействие с игроком).
        Метод нужен при инициализации
        :param group: Новая группа
        """
        Entity.collisions_group = group
예제 #20
0
class Entity(pygame.sprite.Sprite):
    """
    Класс, отвечающий за предстовление базовой сущности в игре
    """

    # Группа со спрайтами, которые считаются физическими объектами
    # общими для всех сущностей.
    collisions_group: pygame.sprite.Group
    all_sprites: pygame.sprite.Group
    player = None

    # Группа со всеми сущностями (экземплярами этого класса)
    # Нужна в основном для коллизий между существами
    entities_group = pygame.sprite.Group()
    damages_group = pygame.sprite.Group()
    spells_group = pygame.sprite.Group()

    default_speed = TILE_SIZE * 0.2

    WAITING_TIME = 2000
    UPDATE_TIME = 120
    HEALTH_LINE_WIDTH = 10
    HEALTH_LINE_TIME = 5000

    POISON_DAMAGE = 5
    BETWEEN_POISON_DAMAGE = 1000

    size = (int(TILE_SIZE),) * 2
    sleeping_frames = cut_sheet(load_image('assets/sprites/enemies/sleep_icon_spritesheet.png'), 4, 1, size)
    poison_frames = cut_sheet(load_image('assets/sprites/spells/poison_static.png'), 5, 1, size)[0]

    small_font = load_game_font(15)
    font = load_game_font(24)

    def __init__(self, x: float, y: float, *args):
        # Конструктор класса Sprite
        super().__init__(*((Entity.entities_group,) + args))
        self.alive = True

        # Изображение
        self.cur_frame = 0
        self.image = self.__class__.frames[0][self.cur_frame]
        self.last_update = pygame.time.get_ticks()
        self.width, self.height = self.image.get_size()

        self.last_damage_time = -Entity.HEALTH_LINE_TIME
        self.last_poison_damage = 0
        self.sleeping_time = None
        self.cur_sleeping_frame = 0
        self.cur_poison_frame = 0
        self.poison_static_time = 0
        self.ice_buff = 0
        self.poison_buff = 0

        self.start_position = x, y
        self.point = None
        self.speed = 0.0001

        self.rect = self.image.get_rect()
        self.rect.center = x, y
        self.collider = Collider(*self.rect.center)

        # Скорость
        self.dx = self.dy = 0

        # Направление взгляда
        self.look_direction_x = 0
        self.look_direction_y = 1

    def update(self) -> None:
        if self.ice_buff:
            self.ice_buff -= 1
            self.speed = self.__class__.default_speed * 0.2
        else:
            self.speed = self.__class__.default_speed

        ticks = pygame.time.get_ticks()
        if self.poison_buff and ticks - self.last_poison_damage > Entity.BETWEEN_POISON_DAMAGE:
            self.last_poison_damage = ticks
            self.poison_buff -= 1
            self.get_damage(Entity.POISON_DAMAGE, 'poison')

    def move(self, dx, dy):
        """
        Метод передвижения. Сущность сдвинется на указанные параметры,
        если там свободно.
        :param dx: Изменение координаты по Х
        :param dy: Изменение координаты по Y
        """
        if not self.alive:
            return
        # Координаты до движения
        pos = self.rect.x, self.rect.y
        # Перемещение по x и обновление столкновений
        self.rect.x = round(self.rect.x + dx)
        self.collider.update(*self.rect.center)

        # Если сущность врезалась во что-то, возвращаем к исходному x
        if pygame.sprite.spritecollide(self.collider, Entity.collisions_group, False):
            self.rect.x = pos[0]
            self.dx = 0
            if self.__class__.__name__ == 'Player':
                self.dash_force_x *= 0.8
                self.dash_direction_x = self.look_direction_x
            elif self.__class__.__name__ == 'PlayerAssistant':
                self.point = None
        # Перемещение по y и обновление столкновений
        self.rect.y = round(self.rect.y + dy)
        self.collider.update(*self.rect.center)

        # Если сущность врезалась во что-то, возвращаем к исходному y
        if pygame.sprite.spritecollide(self.collider, Entity.collisions_group, False):
            self.rect.y = pos[1]
            self.dy = 0
            if self.__class__.__name__ == 'Player':
                self.dash_force_y *= 0.8
                self.dash_direction_y = self.look_direction_y
            elif self.__class__.__name__ == 'PlayerAssistant':
                self.point = None

    def update_frame_state(self, n=0):
        """
        Воспроизводит звук и сменяет кадр анимации

        :param n: если есть, сдвигает номер анимации (стояние вместо движения)
        
        """
        tick = pygame.time.get_ticks()
        if tick - self.last_update > self.UPDATE_TIME:
            self.last_update = tick

            if not self.alive:
                self.cur_frame = self.cur_frame + 1
                if self.cur_frame >= len(self.__class__.death_frames) - 1:
                    if self.__class__.__name__ == 'Player':
                        self.destroyed = True
                    else:
                        for group in self.groups():
                            group.remove(self)
                if self.cur_frame < len(self.__class__.death_frames):
                    self.image = self.__class__.death_frames[self.cur_frame]
                return

            look = self.__class__.look_directions[self.look_direction_x, self.look_direction_y]
            look += n
            self.cur_frame = (self.cur_frame + 1) % len(self.__class__.frames[look])
            self.image = self.__class__.frames[look][self.cur_frame]
            if (self.__class__.__name__ != 'Player' and DEFAULT_SOUNDS_VOLUME * 200 / self.distance_to_player > 0.1
                    and (look < 2 or 'Slime' in self.__class__.__name__ or 'Demon' in self.__class__.__name__)
                    and not self.sounds_channel.get_busy()):
                self.FOOTSTEP_SOUND.set_volume(min(DEFAULT_SOUNDS_VOLUME / (self.distance_to_player / TILE_SIZE) * 3, 1))
                self.sounds_channel.play(self.FOOTSTEP_SOUND)

    def draw_health_bar(self, screen):
        """
        Отрисовка полоски здоровья и отрисовка знака сна (Z-Z-Z).
        :param screen: Экран
        """
        line_width = Entity.HEALTH_LINE_WIDTH  # длинна полоски здоровья
        x, y = self.rect.center  # текущая позиция сущности
        width, height = self.rect.size  # текущие размеры сущности
        # Позиция над сущностью
        x1, y1 = x - width * 0.5, y - height * 0.5
        # При получении урона или наведении прицелом выводиться шкала здоровья
        if pygame.time.get_ticks() - self.last_damage_time < Entity.HEALTH_LINE_TIME or \
                pygame.sprite.collide_rect(self, Entity.player.scope):
            # Обводка вокруг шкалы здоровья
            pygame.draw.rect(screen, 'dark grey', (x1 - 1, y1 - 10 - 1, width + 2, line_width + 2))
            # Длинная полоски здоровья
            health_length = width * max(self.health, 0) / self.full_health
            # Для игрока и его помошника зелёный цвет, для остальных красный
            color = '#00b300' if self.__class__.__name__ in ('Player', 'PlayerAssistant') else 'red'
            # Сама полоска здоровья
            pygame.draw.rect(screen, color, (x1, y1 - 10, health_length, line_width))
            # Текст с текущем здоровьем
            health_text = f'{round(self.health + 0.5)}/{self.full_health}'
            health = self.small_font.render(health_text, True, (255, 255, 255))
            # Отцентровка текста по середине
            rect = health.get_rect()
            rect.center = (x1 + width // 2, y1 - 5)
            # Вывод на экран
            screen.blit(health, rect.topleft)
        # Для всех сущностей кроме игрока выводиться их название
        if self.__class__.__name__ not in ('Player',):
            name = self.font.render(self.name, True, (255, 255, 255))
            rect = name.get_rect()
            rect.center = (x1 + width // 2, y1 - 12 - line_width)
            screen.blit(name, rect.topleft)

        if not self.alive:
            return
        # Отрисовка эффетка яда и обновление параметров
        ticks = pygame.time.get_ticks()
        if self.poison_buff:
            if ticks - self.poison_static_time > Entity.UPDATE_TIME:
                self.poison_static_time = ticks
                self.cur_poison_frame = (self.cur_poison_frame + 1) % len(Entity.poison_frames)
            screen.blit(Entity.poison_frames[self.cur_poison_frame], (self.rect.x, self.rect.y))
        # Отрисовка анимации сна и обновление параметров
        if self.__class__.__name__ not in ('Player',) and not self.target_observed:
            if not self.sleeping_time or ticks - self.sleeping_time >= 250:
                self.cur_sleeping_frame = (self.cur_sleeping_frame + 1) % len(self.sleeping_frames[0])
                self.sleeping_time = ticks
            screen.blit(self.sleeping_frames[0][self.cur_sleeping_frame], (self.rect.centerx + 10, self.rect.y - 35))

    def get_damage(self, damage, spell_type='', action_time=0):
        """
        Получение дамага

        :param damage: Столько здоровья надо отнять
        :param spell_type: Тип урона, чтоб узнавать, на кого он действует сильнее
        :param action_time: Время действия (для льда и отравления)
        
        """

        if not self.alive:
            return
        if spell_type == 'ice':
            self.ice_buff += action_time
        if spell_type == 'poison' and damage >= 5:
            self.poison_buff += action_time

        if damage >= 0:
            look = self.__class__.look_directions[self.look_direction_x, self.look_direction_y]
            self.image = self.get_damage_frames[look]
            self.last_update = pygame.time.get_ticks() + 75
            self.last_damage_time = pygame.time.get_ticks()

        if (self.__class__.__name__ == 'Demon' and spell_type == 'ice' or
                self.__class__.__name__ == 'GreenSlime' and spell_type == 'flash' or
                self.__class__.__name__ == 'DirtySlime' and spell_type == 'void' or
                self.__class__.__name__ == 'Zombie' and spell_type == 'fire' or
                self.__class__.__name__ == 'FireWizard' and spell_type == 'poison' or
                self.__class__.__name__ == 'VoidWizard' and spell_type == 'fire'):
            damage *= 2

        if (self.__class__.__name__ == 'Demon' and spell_type == 'fire' or
                self.__class__.__name__ == 'GreenSlime' and spell_type == 'poison' or
                self.__class__.__name__ == 'DirtySlime' and spell_type == 'ice' or
                self.__class__.__name__ == 'Zombie' and spell_type == 'flash' or
                self.__class__.__name__ == 'FireWizard' and spell_type == 'fire' or
                self.__class__.__name__ == 'VoidWizard' and spell_type == 'void'):
            damage *= 0.25

        damage *= 1000
        damage += randint(-abs(round(-damage * 0.2)), abs(round(damage * 0.2)))
        damage /= 1000

        x, y = self.rect.midtop
        colors = {
            'poison': (100, 230, 125),
            'ice':    (66, 170, 255),
            'void':   (148, 0, 211),
            'flash':  (255, 255, 0),
            'fire':   (226, 88, 34),
        }
        ReceivingDamage(x, y, damage, Entity.all_sprites, Entity.damages_group, color=colors[spell_type])

        self.health = min(self.health - damage, self.full_health)
        if self.health <= 0:
            self.health = 0
            self.death()

    def set_first_frame(self):
        """Установка первого спрайта"""
        tick = pygame.time.get_ticks()
        if tick - self.last_update > self.UPDATE_TIME:
            self.last_update = tick

            look = self.__class__.look_directions[self.look_direction_x, self.look_direction_y]
            self.cur_frame = 0
            self.image = self.__class__.frames[look][self.cur_frame]

    @staticmethod
    def set_global_groups(collisions_group: pygame.sprite.Group, all_sprites: pygame.sprite.Group):
        """
        Метод устанавливает группу со спрайтами, которые будут считаться
        физическими объектами для всех сущностей на уровне.
        (Кроме индивидуальных спрайтов у конкретных объектов,
        например у врагов будет отдельное взаимодействие с игроком).
        Метод нужен при инициализации
        :param collisions_group: Новая группа
        :param all_sprites: Группа всех спрайтов
        """
        Entity.collisions_group = collisions_group
        Entity.all_sprites = all_sprites