from random import choice from data import * from player import Player from enemy import Enemy class Level: def __init__(self): self.display_surface = pygame.display.get_surface() self.ui = UI() # группы основных спрайтов видимых и нет self.visible_sprites = Camera() self.obstacle_sprites = pygame.sprite.Group() # группа спрайтов способных разрушиться self.current_attack = None self.attack_sprites = pygame.sprite.Group() self.attackable_sprites = pygame.sprite.Group() # генерация карты self.create_map() def create_map(self): layouts = { 'barryer': import_csv_layout('../data/map/барьеры.csv'), 'stone': import_csv_layout('../data/map/камни.csv'), 'entities': import_csv_layout('../data/map/мобы.csv') } graphics = {'stone': import_folder('../data/textures/stone')} for style, layout in layouts.items(): for row_index, row in enumerate(layout): for col_index, col in enumerate(row): if col != '-1': x = col_index * TILESIZE y = row_index * TILESIZE if style == 'barryer': Tile((x, y), [self.obstacle_sprites], 'invisible') if style == 'stone': random_grass_image = choice(graphics['stone']) Tile((x, y), [self.visible_sprites, self.obstacle_sprites, self.attackable_sprites], 'stone', random_grass_image) if style == 'entities': if col == '2': self.player = Player((x, y), [self.visible_sprites], self.obstacle_sprites, self.destroy_attack, self.create_attack) else: Enemy("ninja", (x, y), [self.visible_sprites, self.attackable_sprites], self.obstacle_sprites, self.damage_player, self.add_exp) def create_attack(self): # создание атаки и спрайта оружия self.current_attack = Weapon(self.player, [self.visible_sprites, self.attack_sprites]) def destroy_attack(self): # разрушение разрушаемых объектов if self.current_attack: self.current_attack.kill() self.current_attack = None def player_attack(self): # удары по другим объектам if self.attack_sprites: for attack_sprite in self.attack_sprites: collision_sprites = pygame.sprite.spritecollide(attack_sprite, self.attackable_sprites, False) if collision_sprites: for target_sprite in collision_sprites: if target_sprite.sprite_type == 'stone': # камни разрушаются с одного удара target_sprite.kill() else: # мобы получают урон target_sprite.get_damage(self.player) def damage_player(self, amount): self.player.health -= amount self.player.hurt_time = pygame.time.get_ticks() def add_exp(self, amount): self.player.exp += amount def run(self): self.visible_sprites.custom_draw(self.player) self.ui.display(self.player) self.visible_sprites.update() self.visible_sprites.enemy_update(self.player) self.player_attack() # КАМЕРА СЛЕДЯЩАЯ ЗА ПЕРСОНАЖЕМ class Camera(pygame.sprite.Group): def __init__(self): super().__init__() self.display_surface = pygame.display.get_surface() self.half_width = self.display_surface.get_size()[0] // 2 self.half_height = self.display_surface.get_size()[1] // 2 self.offset = pygame.math.Vector2() # отрисовка основной карты self.floor_surf = pygame.image.load('../data/map/map.png').convert() self.floor_rect = self.floor_surf.get_rect(topleft=(0, 0)) def custom_draw(self, player): self.offset.x = player.rect.centerx - self.half_width # ПОЛУЧЕНИЕ ПОЗИЦИИ ИГРОКА self.offset.y = player.rect.centery - self.half_height floor_offset_pos = self.floor_rect.topleft - self.offset # ОТРИСОВКА ОСНОВНОЙ КАРТЫ ПОД ИГРОКОМ self.display_surface.blit(self.floor_surf, floor_offset_pos) for sprite in sorted(self.sprites(), key=lambda sprite: sprite.rect.centery): offset_pos = sprite.rect.topleft - self.offset self.display_surface.blit(sprite.image, offset_pos) def enemy_update(self, player): # ОТРИСОВКА МОБОВ enemy_sprites = [] for sprite in self.sprites(): if hasattr(sprite, 'sprite_type') and sprite.sprite_type == 'enemy': enemy_sprites.append(sprite) for enemy in enemy_sprites: enemy.enemy_update(player) # КЛАСС ОТРИСОВКИ ПЛИТОК class Tile(pygame.sprite.Sprite): def __init__(self, pos, groups, sprite_type, surface=pygame.Surface((TILESIZE, TILESIZE))): super().__init__(groups) self.sprite_type = sprite_type y_offset = HITBOX[sprite_type] self.image = surface if sprite_type == 'object': self.rect = self.image.get_rect(topleft=(pos[0], pos[1] - TILESIZE)) else: self.rect = self.image.get_rect(topleft=pos) self.hitbox = self.rect.inflate(0, y_offset) # КЛАСС ОРУЖИЯ class Weapon(pygame.sprite.Sprite): def __init__(self, player, groups): super().__init__(groups) self.sprite_type = 'weapon' self.image = pygame.image.load(f'../data/textures/sword/{player.status.split("_")[0]}.png').convert_alpha() # отрисовка оружия по отношению к герою if player.status == 'right': self.rect = self.image.get_rect(midleft=player.rect.midright + pygame.math.Vector2(-3, 16)) elif player.status == 'left': self.rect = self.image.get_rect(midright=player.rect.midleft + pygame.math.Vector2(3, 16)) elif player.status == 'down': self.rect = self.image.get_rect(midtop=player.rect.midbottom + pygame.math.Vector2(-10, 0)) else: self.rect = self.image.get_rect(midbottom=player.rect.midtop + pygame.math.Vector2(-10, 20)) # КЛАСС ИНТРЕФЕЙСА class UI: def __init__(self): self.screen = pygame.display.get_surface() self.font = pygame.font.SysFont("arial", 24) # размер и формат чисел опыта def health_bar(self, player): pygame.draw.rect(self.screen, '#222222', pygame.Rect(10, 10, 120, 30)) # перевод кол-ва здоровья в полоску ratio = player.health / player.stats['health'] current_width = pygame.Rect(10, 10, 120, 30).width * ratio current_rect = pygame.Rect(10, 10, 120, 30).copy() current_rect.width = current_width pygame.draw.rect(self.screen, 'red', current_rect) pygame.draw.rect(self.screen, 'black', pygame.Rect(10, 10, 120, 30), 3) def show_exp(self, exp): text_surf = self.font.render(str(int(exp)), False, 'white') x = self.screen.get_size()[0] - 20 # место ячейки опыта y = self.screen.get_size()[1] - 1040 text_rect = text_surf.get_rect(bottomright=(x, y)) pygame.draw.rect(self.screen, '#222222', text_rect.inflate(20, 20)) # отрисовка опыта и серого фона self.screen.blit(text_surf, text_rect) pygame.draw.rect(self.screen, 'black', text_rect.inflate(20, 20), 3) # отрисовка чёрной рамки def display(self, player): self.health_bar(player) self.show_exp(player.exp)