class Dungeon(Element): """класс подземелья""" def __init__(self): super().__init__() self.unused_keys = [] self.first = True self.rooms = {} self.enemies = [] self.objects = [] self.base = [] self.entities = [] self.buttons = [] self.user_name = '' self.con = sqlite3.connect(DATABASE) self.background, self.top_panel, self.bottom_panel = None, None, None self.player = Player((1, 1), 5, 5, 1, 1, 3, 3, 0, 255) self.current_room = 1 self.turn = 1 self.change_room(1) def new(self): self.unused_keys = [] self.rooms = {} self.enemies = [] self.objects = [] self.base = [] self.entities = [] self.buttons = [] self.user_name = '' self.first = True self.background, self.top_panel, self.bottom_panel = None, None, None self.player = Player((1, 1), 5, 5, 1, 1, 3, 3, 0, 255) self.current_room = 1 self.turn = 1 self.change_room(1) def change_room(self, num): # смена комнаты, в которой находится игрок self.enemies = [] self.objects = [] self.base = [] if num not in self.rooms.keys(): # если следующей комнаты не существует self.generate_level(num) else: self.enemies = self.rooms[num].enemies self.objects = self.rooms[num].objects if self.first: # если запустили первый раз self.player.position = (1, 1) self.first = False elif num > self.current_room: # смотрим, откуда пришел игрок self.player.position = self.rooms[num].enter[1], \ self.rooms[num].enter[0] else: self.player.position = self.rooms[num].exit_[1], \ self.rooms[num].exit_[0] self.current_room = num self.entities = [self.player, *self.enemies] self.load_room(self.current_room) def load_room(self, num_of_room): # загрузка комнаты на экран self.base = [] level = self.rooms[num_of_room].structure() empty = Image.open('Sprites/ground/idle/00.png') wall = Image.open('Sprites/wall/idle/00.png') background = Image.new('RGB', (len(level[0]) * TILE, len(level) * TILE), (255, 255, 255)) # собираем из маленьких изображений пустых клетов и стен # одно большое изображение поля чтобы потом отображать только его for i in range(len(level)): for k in range(len(level[i])): if level[i][k] == 'W': self.base.append(Wall((k, i))) background.paste(wall, (k * TILE, i * TILE)) else: self.base.append(Empty((k, i))) background.paste(empty, (k * TILE, i * TILE)) self.background = pygame.image.fromstring(background.tobytes(), background.size, background.mode) self.top_panel = Panel(self.player, 0) # создаем верхнюю self.bottom_panel = Panel(None, 550) # и нижнюю панели self.buttons = [ # создаем кнопки Button('game/panel/exit', (550, 10), 'menu'), Button('game/panel/inventory', (450, 10), 'inventory'), Button('game/panel/save', (500, 10), 'save'), ] def generate_enemies(self, room): patterns = [ ['blue', 2, 2, 1, 1, 2, 2], ['green', 3, 3, 1, 2, 2, 2], ['red', 3, 3, 2, 2, 2, 2], ['purple', 4, 4, 2, 3, 3, 3], ] enemy_number = [0, 0, 0, 0] if room == 1: a = random.randint(2, 4) enemy_number = [a, 4 - a, 0, 0] elif 2 <= room <= 5: a = random.randint(3, 4) enemy_number = [4 - a, a, 0, 0] elif 6 <= room <= 9: a = random.randint(2, 3) enemy_number = [1, a, 3 - a, 0] elif 10 <= room <= 14: a = random.randint(3, 5) enemy_number = [0, 5 - a, a, 0] elif 15 <= room <= 20: a = random.randint(3, 4) enemy_number = [0, 4 - a, a, 1] elif room > 20: a = random.randint(4, 5) enemy_number = [0, 0, 6 - a, a] return [ patterns[i] for i in range(len(patterns)) for _ in range(enemy_number[i]) ] def generate_level(self, num): # генерация уровня игры (карты) closed_cells = [self.player.position] enter = (0, 0) if num != 1: enter = self.rooms[num - 1].enter_from_exit() enemies_options = self.generate_enemies(num) for i in range(len(enemies_options)): # генерация врагов x, y = random.randint(2, 9), random.randint(2, 8) while (x, y) in closed_cells: x, y = random.randint(2, 9), random.randint(2, 8) self.enemies.append(Enemy((x, y), *enemies_options[i])) closed_cells.append((x, y)) for i in range(random.randint(6, 7)): # генерация коробок x, y = random.randint(2, 8), random.randint(2, 7) while (x, y) in closed_cells: x, y = random.randint(2, 8), random.randint(2, 7) self.objects.append(Box((x, y))) closed_cells.append((x, y)) a, b = (0, 2) for i in range(random.randint(a, b)): # генерация зельев для здоровья x, y = random.randint(1, 9), random.randint(2, 8) while (x, y) in closed_cells: x, y = random.randint(1, 9), random.randint(2, 8) self.objects.append(Chest((x, y), 'potion', 'green')) closed_cells.append((x, y)) exit_ = random.choice([(random.randint(2, 8), 11), (9, random.randint(2, 9))]) if num > 6: # генерация зельев для повышения силы или количества ходов if not random.randint(0, 2): x, y = random.randint(1, 9), random.randint(2, 8) while (x, y) in closed_cells: x, y = random.randint(1, 9), random.randint(2, 8) self.objects.append( Chest((x, y), 'potion', random.choice(['red', 'blue']))) closed_cells.append((x, y)) if not random.randint(0, 2) and len(self.unused_keys) < 6: # генерация двери, если неиспользованных ключей меньше 6 door_color = random.choice(['red', 'blue', 'green']) x, y = random.randint(1, 9), random.randint(1, 8) while (x, y) in closed_cells: x, y = random.randint(2, 9), random.randint(2, 8) self.objects.append(Chest((x, y), 'key', door_color)) self.unused_keys.append(door_color) if not random.randint(0, 2) and self.unused_keys: # добавится ли дверь в текущую комнату self.objects.append( Door((exit_[1], exit_[0]), self.unused_keys.pop( random.randint(0, len(self.unused_keys) - 1)))) self.rooms[num] = Room(exit_, self.enemies, self.objects, num, enter) def load(self, user_name): # загрузка игры с базы cur = self.con.cursor() self.user_name = user_name player = cur.execute(f"""SELECT room_num, hit_points, max_hit_points, damage, max_damage, action_points, max_action_points, posX, posY, experience, max_experience FROM users WHERE user_name = '{user_name}'""").fetchone() # все харастеристикик игрока self.unused_keys = list( map( lambda i: i[0], cur.execute(f"""SELECT type FROM inventory WHERE user = '******' AND used = 'False'"""))) self.player = Player((player[-4], player[-3]), *player[1:-4], *player[-2:]) # игрок self.player.inventory = list( map( lambda i: i[0], cur.execute(f"""SELECT type FROM inventory WHERE user = '******' AND used = 'True'"""))) self.current_room = player[0] rooms = cur.execute(f"""SELECT id, number, enter_posX, enter_posY, exit_posX, exit_posY FROM rooms WHERE user = '******'""").fetchall() self.rooms = {} for room_id, number, *positions in rooms: # все комнаты подземелья enemies = cur.execute(f"""SELECT color, hit_points, max_hit_points, action_points, max_action_points, damage, max_damage, posX, posY FROM entities WHERE room_id = {room_id}""").fetchall() list_of_enemies = [] list_of_objects = [] # все враги на карте for color, hit, m_hit, act, m_act, dam, m_dam, x, y in enemies: list_of_enemies.append( Enemy((x, y), color, hit, m_hit, dam, m_dam, act, m_act)) objects = cur.execute(f"""SELECT type, posX, posY, inside, color, stage FROM objects WHERE room_id = {room_id}""").fetchall() for type_, x, y, inside, color, stage in objects: # все объекты на карте if type_ == 1: # коробки list_of_objects.append(Box((x, y))) elif type_ == 2: # сундуки list_of_objects.append( Chest((x, y), *reversed(inside.split('_')))) if stage: list_of_objects[-1].touch() else: # двери list_of_objects.append(Door((x, y), color)) self.rooms[number] = Room(positions[-2:], list_of_enemies, list_of_objects, number, positions[:2]) self.enemies = self.rooms[self.current_room].enemies self.entities = [self.player, *self.enemies] self.objects = self.rooms[self.current_room].objects self.load_room(player[0]) def save_room(self, n): # сохранение 1 комнаты в базе cur = self.con.cursor() room_id = cur.execute(f"""SELECT id FROM rooms WHERE number = {n} and user = '******'""").fetchone()[0] room = self.rooms[n] for enemy in room.enemies: if enemy.alive: cur.execute(f"""INSERT INTO entities(hit_points, max_hit_points, action_points, max_action_points, damage, max_damage, posX, posY, room_id, color) values({enemy.hit_points[0]}, {enemy.hit_points[1]}, {enemy.action_points[0]}, {enemy.action_points[1]}, {enemy.damage[0]}, {enemy.damage[1]}, {enemy.position[0]}, {enemy.position[1]}, {room_id}, '{enemy.color}')""") for obj in room.objects: # объекты комнаты if obj.name == 'box': cur.execute(f"""INSERT INTO objects(type, posX, posY, room_id) values(1, {obj.position[0]}, {obj.position[1]}, {room_id})""") elif obj.stage == 0: # если объект активен if obj.name == 'chest': cur.execute(f"""INSERT INTO objects(type, posX, posY, room_id, inside) values(2, {obj.position[0]}, {obj.position[1]}, {room_id}, '{obj.inside.name}')""") elif obj.name == 'door': cur.execute(f"""INSERT INTO objects(type, posX, posY, room_id, color) values(3, {obj.position[0]}, {obj.position[1]}, {room_id}, '{obj.color}')""") elif obj.stage == 1 and obj.name == 'chest': if obj.name == 'chest': cur.execute(f"""INSERT INTO objects(type, posX, posY, room_id, inside, stage) values(2, {obj.position[0]}, {obj.position[1]}, {room_id}, '{obj.inside.name}', 1)""") self.con.commit() def update_base(self): # обновление базы (если имя пользователя уже вводилось) cur = self.con.cursor() cur.execute(f"""UPDATE users SET room_num = {self.current_room}, hit_points = {self.player.hit_points[0]}, max_hit_points = {self.player.hit_points[1]}, action_points = {self.player.action_points[0]}, max_action_points = {self.player.action_points[1]}, damage = {self.player.damage[0]}, max_damage = {self.player.damage[1]}, posX = {self.player.position[0]}, posY = {self.player.position[1]}, experience = '{self.player.experience[0]}', max_experience = '{self.player.experience[1]}' WHERE user_name = '{self.user_name}'""") self.con.commit() cur.execute("""DELETE FROM inventory WHERE user = '******'""") # удаление старого инвентаря for obj in self.player.inventory: cur.execute(f"""INSERT INTO inventory(user, type, used) values('{self.user_name}', '{obj}', 'True')""") for obj in self.unused_keys: cur.execute(f"""INSERT INTO inventory(user, type, used) values('{self.user_name}', '{obj}', 'False')""") self.con.commit() for n, room in self.rooms.items(): # если в комнату заходили, то есть могли изменяться # положения врагов, объектов и тд room_id = cur.execute(f"""SELECT id FROM rooms WHERE number = {n} and user = '******'""").fetchone()[0] cur.execute(f"""DELETE FROM entities WHERE room_id = {room_id}""") self.con.commit() cur.execute(f"""DELETE FROM objects WHERE room_id = {room_id}""") self.con.commit() self.save_room(n) def save(self, user_name): # функция сохранения базы cur = self.con.cursor() self.user_name = user_name if not self.user_name else self.user_name cur.execute(f"""INSERT INTO users(user_name, room_num, hit_points, max_hit_points, action_points, max_action_points, damage, max_damage, posX, posY, experience, max_experience) values('{self.user_name}', {self.current_room}, {self.player.hit_points[0]}, {self.player.hit_points[1]}, {self.player.action_points[0]}, {self.player.action_points[1]}, {self.player.damage[0]},{self.player.damage[1]}, {self.player.position[0]}, {self.player.position[1]}, '{self.player.experience[0]}', '{self.player.experience[1]}')""") # добавление нового пользователя со всеми характеристиками self.con.commit() for obj in self.player.inventory: # добавление инвентаря игрока cur.execute(f"""INSERT INTO inventory(user, type, used) values('{self.user_name}', '{obj}', 'True')""") for obj in self.unused_keys: # добавляются неиспользованные ключи cur.execute(f"""INSERT INTO inventory(user, type, used) values('{self.user_name}', '{obj}', 'False')""") for n, room in self.rooms.items(): cur.execute(f"""INSERT INTO rooms(number, enter_posX, enter_posY, exit_posX, exit_posY, user) values({n}, {room.enter[0]}, {room.enter[1]}, {room.exit_[0]}, {room.exit_[1]}, '{self.user_name}')""") self.con.commit() self.save_room(n) # добавление каждой комнаты в базу def get(self, coordinates, diff=(0, 0)): """Возвращает объект по координатам""" for obj in [*self.entities, *self.objects, *self.base]: if obj.position == (coordinates[0] + diff[1], coordinates[1] + diff[0]): if getattr(obj, 'alive', True): return obj def player_move(self, button): """Движение игрока""" # словарь вида {кнопка: (смещение на X и Y)} buttons_keys = { pygame.K_LEFT: (0, -1), pygame.K_RIGHT: (0, 1), pygame.K_UP: (-1, 0), pygame.K_DOWN: (1, 0) } if any([ i.animator.animation not in ['idle', 'die'] for i in self.enemies ]): # если враги еще совершают какие-то действия, то игрок стоит return if self.player.animator.animation != 'idle': # если игрок совершает какое-то действие, то # мы не начинаем новое действие return if button not in buttons_keys.keys(): return # если нажали на неизвестную кнопку # проверяем на нахождение в телепорте self.player.interaction_teleport(self) # взаимодействуем с объектом obj = self.get(self.player.position, buttons_keys[button]) obj_name = obj.name if hasattr(obj, 'name') else None self.player.interaction(self, buttons_keys[button]) if obj_name == 'enemy' and not obj.alive: self.player.experience[0] += 2 if self.player.experience[0] > self.player.experience[1]: config.NEXT_WINDOW = 'win' def enemies_move(self): """Движение врагов""" if self.player.animator.animation != 'idle': return # если игрок что-то делает, то враги не начинают новых действий options = [(-1, 0), (1, 0), (0, -1), (0, 1)] res = [] blocked_cells = [] for enemy in self.enemies: if not enemy.alive: # текущий враг метрв, то переходим к следущему врагу continue if enemy.animator.animation != 'idle': res.append(True) # если враг уже совершает действие, # то переходим к следущему врагу continue checking = [] for i in options: checking.append( self.get(enemy.position, i).name in ('player', 'empty')) if not any(checking): # если врагу некуда идти res.append(False) continue player_pos = self.player.position enemy_pos = enemy.position diff = (0, 0) if random.randint(0, 1): # генерация ходов врага # враг пытается приблизиться к игроку if enemy_pos[0] != player_pos[0]: diff = (0, -1) if enemy_pos[0] > player_pos[0] else (0, 1) elif enemy_pos[1] != player_pos[1]: diff = (-1, 0) if enemy_pos[1] > player_pos[1] else (1, 0) else: if enemy_pos[1] != player_pos[1]: diff = (-1, 0) if enemy_pos[1] > player_pos[1] else (1, 0) elif enemy_pos[0] != player_pos[0]: diff = (0, -1) if enemy_pos[0] > player_pos[0] else (0, 1) while (enemy_pos[0] + diff[0], enemy_pos[1] + diff[1]) in blocked_cells or \ self.get(enemy_pos, diff).name not in ('empty', 'player'): diff = options[random.randint(0, len(options) - 1)] blocked_cells.append( (enemy_pos[0] + diff[0], enemy_pos[1] + diff[1])) # добавляем результат взаимодействия с список res.append(enemy.interaction(self, diff)) if not any(res): # если у всех врагов закончились очки действий self.turn = 1 # передаем ход игроку for enemy in self.enemies: # обновляем очки действий у врагов enemy.action_points[0] = enemy.action_points[1] def show(self, surf): """Отображение на поверхность""" if self.turn == 2: self.enemies_move() surf.blit(self.background, apply((0, 0))) # отображаем поле for entity in self.entities: # отображаем существ entity.show(surf) for obj in self.objects: # отображаем объекты obj.show(surf) self.top_panel.show(surf) # отображаем верхнюю self.bottom_panel.show(surf) # и нижнюю панели for elem in self.buttons: # отображаем кнопки elem.show(surf) def button_down(self, mouse_pos): """Нажатие мыши""" # получаем объект, на который нажали obj = self.get( (mouse_pos[0] // TILE, (mouse_pos[1] - PANEL_HEIGHT) // TILE)) if isinstance(obj, Enemy) and obj.alive: # если нажали на врага self.bottom_panel.change_target(obj) # меняем цель нижней панели else: self.bottom_panel.change_target(None) for elem in self.buttons: # проверяем нажатие на кнопки elem.button_down(mouse_pos) def key_down(self, key): """Нажатие на клавиатуру""" for elem in self.buttons: elem.key_down(key) if self.turn == 1: # если ход игрока self.player_move(key) # то вызываем функцию движения игрока