def damage_in_area(self, area: PhysicalRect, damage, type_, impulse=None, skip=None, **kwargs): """ Наносит урон всем сущностям в заданых границах :param area: границы :param damage: урон :param type_: тип урона :param impulse: импуль при нанесении урона (кулаком) :param skip: список id сущностей, которые по какой-либо причине не получат урон :return: True, если был нанесён урон, False иначе """ if skip is None: skip = [] # флаг, отвечающий за то, что был нанесён урон damaged = False # Пинаем объекты if impulse is not None: for object_ in self.objects: if area.check_intersection(object_.body_rect): object_.body.apply_impulse_at_local_point(impulse) # Наносим урон сущностям и игроку for entity in self.entities_and_player: # Пропускаем нужных сущностей if id(entity) in skip: continue # Если сущность в области нанесения урона if area.check_intersection(entity.body_rect): # подробнее читать в документации на этот метод if 'object_' in kwargs: kwargs['object_'].damaged() # нанесение урона entity.get_damage(damage, type_) # флаг, был нанесён урон damaged = True # Передаём импульс, если он есть if impulse is not None: entity.body.apply_impulse_at_local_point(impulse) return damaged
def __init__(self, x, y, radius=0.3, sprite=None, scene=None, angle=0, mass=1, moment=None, elasticity=0, friction=0.6): if moment is None: moment = pymunk.moment_for_box(mass, (radius, radius)) body = pymunk.Body(mass, moment) shape = pymunk.Circle(body, radius=radius) super(DynamicCircularObject, self).__init__(x=x, y=y, width=radius, height=radius, scene=scene, sprite=sprite, body=body, shape=shape, angle=angle, mass=mass, moment=moment, elasticity=elasticity, friction=friction, type_=pymunk.Body.DYNAMIC) self.body_rect = PhysicalRect(x - radius / 2, y - radius / 2, 2 * radius, 2 * radius)
def __init__(self, screen: pygame.Surface, x=0, y=0, h_fov=pi / 2, distance=15): # Экран, на который будет выводиться изображение self.screen = screen # Временная поверхность для рисования, потом блитится на экран self.temp_surface = pygame.Surface(screen.get_rect().size) # Далее физические характеристики камеры # Физические координаты центра камеры self.__position = Vec2d(x, y) # Горизонтальный угол обзора камеры self.h_fov = h_fov # Расстояние от камеры до поверхности экрана self.__distance = distance # Физическая ширина области вилимости камеры self.window_width = 2 * self.__distance * tan(self.h_fov / 2) # Физическая высота области вилимости камеры self.window_height = self.window_width * SCREEN_HEIGHT / SCREEN_WIDTH # Вертикальный угол обзора камеры self.v_fov = atan(self.window_height / self.__distance / 2) # Физическая область видимости камеры self.camera_rect = PhysicalRect( self.__position[0] - self.window_width / 2, self.__position[1] - self.window_height / 2, self.window_width, self.window_height) # Коэффициент, на который умножаются координаты, чтобы отрисовать объекты на экране на экране self.scale_factor = SCREEN_WIDTH / self.window_width self.dev_font = pygame.font.SysFont(pygame.font.get_fonts()[0], 50)
def __init__(self, x, y, width=0.3, height=0.3, sprite=None): """ :param x: x координата левого нижнего угла объекта :param y: y координата левого нижнего угла объекта :param width: ширина описанного прямоугольника объекта :param height: высота описанного прямоугольника объекта :param sprite: спрайт объекта """ self._position = Vec2d(x, y) self.width = width self.height = height self.sprite = sprite if self.sprite is not None: # Переворачиваем спрайт self.sprite = pygame.transform.flip(self.sprite, False, True).convert_alpha() self.body_rect = PhysicalRect(x, y, width, height) # Преобразованное изображения спрайта # Имеет размер, как проекция объекта на поверхность камеры # Нужно для оптимизации self.scaled_image = self.sprite self.last_camera_distance = -1 # Дистанция от камеры да сцены
def load_level(self, username): """ Функция загрузки уровня из файла На вход принимает название сейва Если названия нет, подгружает резервный сейв под именем DefaultName_save P.S. такого резервного сейва еще нет """ lvl_path = os.path.join('src', 'Levels', 'Saved_Levels', username + '_save') if not os.path.exists(lvl_path): lvl_path = os.path.join('src', 'Levels', 'Saved_Levels', 'default_level_save') with open(lvl_path) as readfile: data = yaml.load(readfile, Loader=yaml.Loader) # Если нет данных выходим if data == {}: return # Загрузка границ self.borders = PhysicalRect( **data['borders'] ) if data['borders'] is not None else PhysicalRect(-10, -5, 20, 10) self.invisible_segments = data['invisible_segments'] self.add_borders() background = data['background'] if background == 'dorm': self.bg = Dorm(self) if background == 'corr': self.bg = Corridor(self) if background == 'base': self.bg = Basment(self) # Загрузка объектов for object_ in data['objects'].values(): self.load_object(object_) # Инициализация игрока self.init_player(*data['MainCharacter']['vector'], **data['MainCharacter']['brain']['init']) # Загрузка сущностей for entity_config in data['entities'].values(): self.load_entity(entity_config)
def position(self, new_position): """ Устанавливает новые физические координаты центра обзора :param new_position: новые физические координаты центра камеры :return: физические координаты центра камеры """ # Изменение позиции self.__position = new_position # Пересчёт физической области видимости self.camera_rect = PhysicalRect( self.__position[0] - self.window_width / 2, self.__position[1] - self.window_height / 2, self.window_width, self.window_height)
def __init__(self, scene, x=0, y=0, brain=ManualController, **kwargs): super(BaseCharacter, self).__init__(scene, x, y, brain=brain, **self.configs['init'], **kwargs) # Имя персонажа self.name = self.__class__.__name__ # Описание персонажа self.description = None # Боёвка hits: dict = default_person['hits'] # Удары рукой self.arming = hits['arming'] # Бросания self.throwing = hits['throwing'] # Список названий ударов рукой self.arming_types = list(self.arming) # Список названия бросков self.throwing_types = list(self.throwing) # Обновляем свойства, согласно конфигу if 'properties' in self.configs: self.__dict__ |= self.configs['properties'] # Обновляем удары, согласно конфигу if 'hits' in self.configs: self.__dict__ |= self.configs['hit'] # Обновляем описанные прямоугольники для разных состояний, согласно конфигу if 'rects' in self.configs: for name_, (width_, height_) in self.configs['rects'].items(): self.__dict__[name_] = PhysicalRect(0, 0, width_, height_) # Атрибуты, которые в данный момент self.__arming_reload = 0 self.__throwing_reload = 0
def __init__(self, scene, x, y, angle=0, type_=Body.STATIC, lifetime=5, damage=None, owner=None, if_damaged='none', if_damaged_many='disappear'): """ Можно ещё передать параметр damage, если нужно, чтобы объект наносил урон :param scene: игровая сценв :param x: координата x объекта :param y: координата y объекта :param angle: начальный угол поворота объекта :param type_: тип объекта (статический, кинематический или динамический) :param lifetime: время жизни, через lifetime сек объект изчезнет. Чтобы он не изчес можно установить float('inf') :param damage: урон при попадании в сущность, не являющейся владельцем, мб None :param owner: владелеец (точнее id), мб None :param if_damaged: действие после нанесения урона 1 раз :param if_damaged_many: действие после нанесения урона на 1 итерации цикла проверки урона """ sprite = None if self.configs['sprite'] is not None: sprite = pil_to_pygame(load_image(self.configs['sprite'])) init_config: dict = self.configs['init'] radius = init_config['radius'] if init_config['moment'] is None: init_config['moment'] = pymunk.moment_for_circle(init_config['mass'], 0, radius) body = pymunk.Body(init_config['mass'], init_config['moment']) shape = pymunk.Circle(body, radius) super(CircularObject, self).__init__(x=x, y=y, width=radius, height=radius, scene=scene, sprite=sprite, lifetime=lifetime, body=body, shape=shape, angle=angle, mass=init_config['mass'], moment=init_config['moment'], elasticity=init_config['elasticity'], friction=init_config['friction'], type_=type_, damage=damage, owner=owner, if_damaged=if_damaged, if_damaged_many=if_damaged_many) self.body_rect = PhysicalRect(x - radius / 2, y - radius / 2, 2 * radius, 2 * radius)
def distance(self, new_distance): """ Возращает физическое расстояние от камеры до сцены :return: """ if new_distance <= 0: # raise CameraError("Distance became negative") return # Расстояние от камеры до поверхности экрана self.__distance = new_distance # Физическая ширина области вилимости камеры self.window_width = 2 * self.__distance * tan(self.h_fov / 2) # Физическая высота области вилимости камеры self.window_height = self.window_width * SCREEN_HEIGHT / SCREEN_WIDTH # Вертикальный угол обзора камеры self.v_fov = atan(self.window_height / self.__distance / 2) # Физическая область видимости камеры self.camera_rect = PhysicalRect( self.__position[0] - self.window_width / 2, self.__position[1] - self.window_height / 2, self.window_width, self.window_height) # Коэффициент, на который умножаются координаты, чтобы отрисовать объекты на экране на экране self.scale_factor = SCREEN_WIDTH / self.window_width
def _hit_box_to_physical_rect(self, box): physical_box = PhysicalRect(*box.values()) physical_box.bottomleft += self.body_rect.bottomleft return physical_box
def __init__(self, scene, x=0, y=0, width=0.7, height=1.8, mass=75, brain=Idle, animations=None, sounds=None, **kwargs): """ :param scene: игровая сцена :param x: x координата левого нижнего края сущности :param y: y координата левого нижнего края сущности :param height: высота сущности :param width: ширина сущности :param brain: мозги сущности, подробнее смотри в Engine/EntityControllers.py """ super(Entity, self).__init__(x=x, y=y, width=width, height=height, sprite=None, scene=scene, mass=mass, moment=float('inf'), elasticity=0, friction=0.6, type_=pymunk.Body.DYNAMIC) # float('inf'), чтобы исключить вращение # Сцена сущности self.scene = scene # Мозг сущности brain_init = {} if 'brain_init' in kwargs: brain_init = kwargs['brain_init'] self.brain = brain(self, **brain_init) # Описанные прямоугольники для разных состояний игрока # Нужны для пересчёта геометрии при смене состояния игрока # Названия говорят сами за себя # Прописывание всего этого рукими можно удалить, но оно нужно для наглядности rects: dict = default_person['rects'] self.idle_rect = PhysicalRect(0, 0, rects['idle_rect'][0], rects['idle_rect'][1]) self.walking_rect = PhysicalRect(0, 0, rects['walking_rect'][0], rects['walking_rect'][1]) self.running_rect = PhysicalRect(0, 0, rects['running_rect'][0], rects['running_rect'][1]) self.sitting_rect = PhysicalRect(0, 0, rects['sitting_rect'][0], rects['sitting_rect'][1]) self.squatting_rect = PhysicalRect(0, 0, rects['squatting_rect'][0], rects['squatting_rect'][1]) self.lying_rect = PhysicalRect(0, 0, rects['lying_rect'][0], rects['lying_rect'][1]) self.crawling_rect = PhysicalRect(0, 0, rects['crawling_rect'][0], rects['crawling_rect'][1]) self.soaring_rect = PhysicalRect(0, 0, rects['soaring_rect'][0], rects['soaring_rect'][1]) self.jumping_rect = PhysicalRect(0, 0, rects['jumping_rect'][0], rects['jumping_rect'][1]) self.flying_rect = PhysicalRect(0, 0, rects['flying_rect'][0], rects['flying_rect'][1]) self.landing_rect = PhysicalRect(0, 0, rects['landing_rect'][0], rects['landing_rect'][1]) self.dying_rect = PhysicalRect(0, 0, rects['dying_rect'][0], rects['dying_rect'][1]) self.win_rect = PhysicalRect(0, 0, rects['win_rect'][0], rects['win_rect'][1]) # Не меняющиеся атрибуты # Свойства сущности properties: dict = default_person['properties'] self.walk_speed = properties['walk_speed'] # Скорость ходьбы сущности self.run_speed = properties['run_speed'] # Скорость бега сущности self.jump_speed = properties['jump_speed'] # Скорость прыжка # Максимальное здоровье self.max_health = properties['max_health'] # Вероятность уклонения self.dodge = properties['dodge'] # Сопротивление урону self.resistance = properties['resistance'] # Атрибуты, которые в данный момент self.__health = self.max_health # Далее флаги, нужные для удобной обработки # Состояние сущности self.__state = State.IDLE # Горизонтальное направление взгляда (влево, вправо) self.horizontal_view_direction = 'right' # Вертикальное направление взгляда (вверх, вниз) self.vertical_view_direction = 'up' # Сами анимации self.animations = EntityAnimations(self) if animations is not None: self.load_animations(animations) # Звуки self.sounds = EntitySounds(self) if sounds is not None: self.load_sounds(sounds)