示例#1
0
    def __init__(self, subject_x: float, subject_y: float, object_x: float,
                 object_y: float, extra_damage: float, object_group,
                 all_spites, *groups):
        super().__init__(all_spites, *groups)
        dx = object_x - subject_x
        dy = object_y - subject_y
        # Увеличиваем расстояние пропорционально
        # Чтобы заклинание летело далеко за пределы экрана, а не просто к курсору
        while (abs(dx) < 5000
               and abs(dy) < 5000) and (self.spell_type != Spell.FLASH
                                        and self.spell_type != Spell.TELEPORT):
            dx *= 2
            dy *= 2
            dy += 1
        self.point = (subject_x + dx, subject_y + dy)

        # Увеличения урона/времени действия в зависимости от уровня существа
        if self.spell_type != Spell.TELEPORT:
            self.damage = self.__class__.damage * extra_damage

        if self.spell_type in (self.ICE, self.POISON):
            self.action_time = self.__class__.action_time * extra_damage

        # Угол, под которым пущено заклинание (для поворота картинки)
        if dx >= 0:
            self.angle = -degrees(atan(dy / max(dx, 0.00001)))
        else:
            self.angle = 180 - degrees(atan(dy / min(dx, -0.00001)))

        self.object_group = object_group
        self.cur_frame = 0
        self.cur_list = 0
        self.last_update_time = pygame.time.get_ticks()

        # Поворачиваем картинку
        self.frames = [[
            pygame.transform.rotate(i, self.angle)
            for i in self.__class__.frames[0]
        ], self.__class__.frames[1:]]

        self.image = self.frames[0][0]
        self.rect = self.image.get_rect()
        self.rect.center = subject_x, subject_y

        self.collider = Collider(*self.rect.center,
                                 (TILE_SIZE * 0.2, TILE_SIZE * 0.2))

        self.update()
示例#2
0
    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
示例#3
0
    def __init__(self, tile_type: str, x: float, y: float, *groups):
        super().__init__(*groups)

        self.type = tile_type  # тип тайла
        self.image = Tile.IMAGES[self.type]  # Изображение по типу
        self.rect = self.image.get_rect().move(x * TILE_SIZE, y * TILE_SIZE)

        # Переменные для удобства
        half = TILE_SIZE // 2
        quarter = TILE_SIZE // 4
        # Далее код, в котором каждому виду стены будет присвоен свой размер коллайдера.
        # Размер и расположение подобраны так, что стены не имеют промежутков между собой, если они смежные
        # (Как это происходит с ящиками и бочками, кстати это так и задумано)
        # Но при этом игрок не слишком жестко взаимодействует с ними при перемещении
        if self.type in ('1', '5'):
            self.collider = Collider(x, y, (half, half * 3))
        elif self.type in ('3', '7'):
            self.collider = Collider(x, y, (half * 3, half))

        elif self.type in ('2', '9'):
            self.collider = Collider(x - quarter, y + quarter,
                                     (half * 2, half * 2))
        elif self.type in ('4', '0'):
            self.collider = Collider(x + quarter, y + quarter,
                                     (half * 2, half * 2))
        elif self.type in ('6', '-'):
            self.collider = Collider(x + quarter, y - quarter,
                                     (half * 2, half * 2))
        elif self.type in ('8', '='):
            self.collider = Collider(x - quarter, y - quarter,
                                     (half * 2, half * 2))

        else:
            self.collider = Collider(x, y)
示例#4
0
 def __init__(self, tile_type: str, x: float, y: float, new_boxes_seed,
              index, *groups):
     super().__init__(*groups)
     # Сохраняем ссылку на сид и индекс, чтоб потом удалить по индексу из сида этот ящик,
     # если он будет разрушен
     self.seed = new_boxes_seed
     self.index = index
     self.image = Furniture.IMAGES[tile_type]
     self.rect = self.image.get_rect().move(x * TILE_SIZE, y * TILE_SIZE)
     self.collider = Collider(x, y)
示例#5
0
    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)
示例#6
0
    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))
示例#7
0
class Spell(pygame.sprite.Sprite):
    """
    Класс, отвечающий за предстовление базового заклинания в игре
    """
    FIRE = "fire"
    FLASH = "flash"
    ICE = "ice"
    POISON = "poison"
    VOID = "void"
    TELEPORT = "teleport"

    # Номер кадра анимации, на котором происходит действие (телепортация, урон, хил)
    damage_frame = 0

    start_position = None

    def __init__(self, subject_x: float, subject_y: float, object_x: float,
                 object_y: float, extra_damage: float, object_group,
                 all_spites, *groups):
        super().__init__(all_spites, *groups)
        dx = object_x - subject_x
        dy = object_y - subject_y
        # Увеличиваем расстояние пропорционально
        # Чтобы заклинание летело далеко за пределы экрана, а не просто к курсору
        while (abs(dx) < 5000
               and abs(dy) < 5000) and (self.spell_type != Spell.FLASH
                                        and self.spell_type != Spell.TELEPORT):
            dx *= 2
            dy *= 2
            dy += 1
        self.point = (subject_x + dx, subject_y + dy)

        # Увеличения урона/времени действия в зависимости от уровня существа
        if self.spell_type != Spell.TELEPORT:
            self.damage = self.__class__.damage * extra_damage

        if self.spell_type in (self.ICE, self.POISON):
            self.action_time = self.__class__.action_time * extra_damage

        # Угол, под которым пущено заклинание (для поворота картинки)
        if dx >= 0:
            self.angle = -degrees(atan(dy / max(dx, 0.00001)))
        else:
            self.angle = 180 - degrees(atan(dy / min(dx, -0.00001)))

        self.object_group = object_group
        self.cur_frame = 0
        self.cur_list = 0
        self.last_update_time = pygame.time.get_ticks()

        # Поворачиваем картинку
        self.frames = [[
            pygame.transform.rotate(i, self.angle)
            for i in self.__class__.frames[0]
        ], self.__class__.frames[1:]]

        self.image = self.frames[0][0]
        self.rect = self.image.get_rect()
        self.rect.center = subject_x, subject_y

        self.collider = Collider(*self.rect.center,
                                 (TILE_SIZE * 0.2, TILE_SIZE * 0.2))

        self.update()

    def update(self) -> None:
        if self.rect.center == self.point:
            # Значит заклинание достигло цели (либо врезалось)
            # Теперь оно должно нанести урон и уничтожиться после показа анимации
            ticks = pygame.time.get_ticks()
            if ticks - self.last_update_time < self.UPDATE_TIME:
                return
            self.last_update_time = ticks

            if self.cur_frame == self.damage_frame:
                # Производим действие
                if isinstance(self, TeleportSpell):
                    self.object_group[0].rect.center = self.rect.center
                else:
                    if isinstance(self, VoidSpell):
                        # Сделаем, чтоб коллайдер был ровно по границы анимации
                        # Чтоб не было расхождения и не вводить в ступор игрока
                        size = (round((self.rect.w / 3)), ) * 2
                        self.collider.update(*self.rect.center, size)
                    else:
                        size = (round((self.rect.w / 2)), ) * 2
                        self.collider.update(*self.rect.center, size)

                    # Убиваем все заклинание-ломаемые вещи, которые задеваем
                    pygame.sprite.spritecollide(self.collider,
                                                Spell.furniture_group, True)
                    pygame.sprite.spritecollide(self.collider,
                                                Spell.doors_group, True)

                    # Наносим урон группе объектов, на которых направлено заклинание
                    for obj in self.object_group:
                        if pygame.sprite.collide_circle(self.collider, obj):
                            obj.get_damage(self.damage, self.spell_type,
                                           self.action_time)

            # Меняем номер кадра анимации
            self.cur_frame += 1
            if self.cur_frame == len(self.__class__.frames[self.cur_list]):
                # Если кадр последний, убиваем заклинание
                self.kill()
                if isinstance(self, TeleportSpell):
                    # Подчищаем
                    self.start_sprite.kill()
                return

            # Меняем кадр анимации
            self.image = self.__class__.frames[self.cur_list][self.cur_frame]
            # Выравниваем центр изображения
            pos = self.rect.center
            self.rect = self.image.get_rect()
            self.rect.center = pos
            if isinstance(self, TeleportSpell):
                self.start_sprite.image = self.image

        else:
            # Иначе ещё летим
            self.cur_frame = (self.cur_frame + 1) % len(
                self.__class__.frames[0])

            # Вычисляем перемещение
            self_x, self_y = self.rect.center
            point_x, point_y = self.point
            distance_to_object = max(
                ((point_x - self_x)**2 + (point_y - self_y)**2)**0.5,
                self.speed)
            part_move = max(distance_to_object / self.speed, 0.5)
            dx = round((point_x - self_x) / part_move)
            dy = round((point_y - self_y) / part_move)
            self.rect.x = self.rect.x + dx
            self.rect.y = self.rect.y + dy

            # Обновляем позицию коллайдера
            self.collider.update(*self.rect.center)
            do_kill = False
            # Проверяем на всевозможные соприкосновения, от которых заклинание может умереть
            for obj in pygame.sprite.spritecollide(self.collider,
                                                   self.object_group, False):
                if obj.alive:
                    do_kill = True
            for obj in pygame.sprite.spritecollide(self.collider,
                                                   Spell.barrier_group, False):
                obj.collider.update(*obj.rect.center)
                if pygame.sprite.collide_rect(self.collider, obj.collider):
                    do_kill = True

            for obj in pygame.sprite.spritecollide(self.collider,
                                                   Spell.doors_group, False):
                obj.collider.update(*obj.rect.center)
                if not obj.opened:
                    do_kill = True
            for obj in pygame.sprite.spritecollide(self.collider,
                                                   Spell.furniture_group,
                                                   False):
                obj.collider.update(*obj.rect.center)
                if pygame.sprite.collide_rect(self.collider, obj.collider):
                    do_kill = True

            if do_kill and self.spell_type != Spell.FLASH and self.spell_type != Spell.TELEPORT:
                self.point = self.rect.center

            # Проверяем, достигли ли объекта
            if self.rect.center == self.point:
                self.cur_frame = 0
                self.cur_list = randint(1, len(self.__class__.frames) - 1)
                # Нужно, чтоб не задваивался звук
                # Т.к. воспроизводится и в месте каста, и в месте попадания
                if not isinstance(self, TeleportSpell):
                    pass
                self.sounds_channel.play(choice(self.SPELL_SOUNDS))

            ticks = pygame.time.get_ticks()
            if ticks - self.last_update_time < self.UPDATE_TIME:
                return
            # Обновляем кадр анимации
            self.last_update_time = ticks
            self.image = self.frames[0][self.cur_frame]
            if isinstance(self, TeleportSpell):
                self.start_sprite.image = self.image

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

    @staticmethod
    def set_global_breaking_group(doors_group, furniture_group):
        """
        Метод устанавливает группы со спрайтами, которые ломаются от соприкосновений с заклинанием
        :param doors_group: Группа дверей
        :param furniture_group: Группа ящиков и бочек
        :return: None
        """
        Spell.doors_group = doors_group
        Spell.furniture_group = furniture_group