diff --git a/chess_open_world.py b/chess_open_world.py index 8ad6879..18e58d7 100644 --- a/chess_open_world.py +++ b/chess_open_world.py @@ -21,13 +21,13 @@ COLOR_SELECTED = (255, 0, 0) COLOR_MOVE = (0, 255, 0) COLOR_WHITE_PIECE = (255, 255, 255) # Заливка белых фигур -COLOR_BLACK_PIECE = (0, 0, 0) # Заливка чёрных фигур +COLOR_BLACK_PIECE = (0, 0, 0) # Заливка чёрных фигур # Новые цвета для бонусов COLOR_REGEN = (0, 255, 0) # Зелёный COLOR_HP_UPGRADE = (0, 128, 255) # Синий COLOR_DAMAGE = (255, 0, 0) # Красный -COLOR_KING_HP_UPGRADE = (255, 215, 0) # Золотой +COLOR_KING_HP_UPGRADE = (255, 215, 0) # Золотой COLOR_ADD_PIECE = (255, 255, 255) # Белый (с плюсом) COLOR_ADD_PIECE_PINK = (255, 192, 203) # Розовый @@ -50,7 +50,7 @@ COLOR_COIN = (255, 165, 0) # Оранжевый screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("Шахматы с Открытым Миром 1.1.1") -# Шрифты +# --- Шрифты --- font = pygame.font.SysFont(None, CELL_SIZE // 2) victory_font = pygame.font.SysFont(None, 60) turn_font = pygame.font.SysFont(None, 30) @@ -61,7 +61,7 @@ shop_font = pygame.font.SysFont(None, 24) # --- Глобальное множество открытых клеток (туман войны) --- global_revealed = set() -# --- Словарь символов --- +# --- Словарь символов (фолбэк, если спрайт не найден) --- piece_symbols = { 'rook': 'Л', 'knight': 'К', @@ -78,7 +78,7 @@ base_bonus_probabilities = { 'regen': 0.42, # 42% 'hp_upgrade': 0.32, # 32% 'damage': 0.2, # 20% - 'king_hp_upgrade': 0.05, # 5% + 'king_hp_upgrade': 0.05, # 5% 'add_piece': 0.01 # 1% } @@ -97,17 +97,14 @@ def choose_bonus_type(): def resolve_combat(attacker, defender): a = attacker.hp b = defender.hp - if a < b: defender.hp = b - a attacker.hp = 0 return (False, True) - elif a > b: attacker.hp = a - b defender.hp = 0 return (True, False) - else: attacker.hp = 1 defender.hp = 0 @@ -121,6 +118,7 @@ class Piece: self.x = x self.y = y self.selected = False + self.has_moved = False # Для ограничения повторного хода за один ход # Инициализация HP if self.name == 'king': @@ -136,11 +134,9 @@ class Piece: def get_possible_moves(self, pieces): moves = [] if self.name == 'pawn': - directions = [ - (-1,-1), (0,-1), (1,-1), - (-1, 0), (1, 0), - (-1, 1), (0, 1), (1, 1) - ] + directions = [(-1,-1), (0,-1), (1,-1), + (-1, 0), (1, 0), + (-1, 1), (0, 1), (1, 1)] for dx, dy in directions: nx = self.x + dx ny = self.y + dy @@ -148,19 +144,14 @@ class Piece: blocking = next((p for p in pieces if p.x == nx and p.y == ny), None) if not blocking or blocking.color != self.color: moves.append((nx, ny)) - elif self.name in ['rook', 'bishop', 'queen']: - # Ограничиваем движение до 3 клеток или до 5 для улучшенного хода max_steps = self.move_range if hasattr(self, 'move_range') else 3 if self.name == 'rook': directions = [(-1,0),(1,0),(0,-1),(0,1)] elif self.name == 'bishop': directions = [(-1,-1),(1,-1),(1,1),(-1,1)] elif self.name == 'queen': - directions = [ - (-1,0),(1,0),(0,-1),(0,1), - (-1,-1),(1,-1),(1,1),(-1,1) - ] + directions = [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(1,1),(-1,1)] for dx, dy in directions: for step in range(1, max_steps + 1): nx = self.x + dx * step @@ -174,62 +165,52 @@ class Piece: moves.append((nx, ny)) else: break - elif self.name == 'knight': deltas = [(-2,-1),(-1,-2),(1,-2),(2,-1), (2,1),(1,2),(-1,2),(-2,1)] for dx, dy in deltas: - nx, ny = self.x+dx, self.y+dy + nx, ny = self.x + dx, self.y + dy if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: blocking = next((p for p in pieces if p.x == nx and p.y == ny), None) if not blocking or blocking.color != self.color: moves.append((nx, ny)) - elif self.name == 'king': - directions = [ - (-1,0),(1,0),(0,-1),(0,1), - (-1,-1),(1,-1),(1,1),(-1,1) - ] + directions = [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(1,1),(-1,1)] for dx, dy in directions: - nx, ny = self.x+dx, self.y+dy + nx, ny = self.x + dx, self.y + dy if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: blocking = next((p for p in pieces if p.x == nx and p.y == ny), None) if not blocking or blocking.color != self.color: moves.append((nx, ny)) - return moves # --- Инициализация фигур --- def initialize_pieces(): - """Чёрные: y=2(back), y=3(pawns). Белые: y=27(back), y=26(pawns).""" + """Чёрные: y=2 (back), y=3 (pawns). Белые: y=27 (back), y=26 (pawns).""" pieces = [] - cx = GRID_WIDTH//2 - - # ЧЁРНЫЕ + cx = GRID_WIDTH // 2 + # Чёрные black_back = ['rook','knight','bishop','king','queen','bishop','knight','rook'] - for i,pname in enumerate(black_back): + for i, pname in enumerate(black_back): x = cx - 3 + i y = 2 - pieces.append(Piece(pname,'black',x,y)) + pieces.append(Piece(pname, 'black', x, y)) for i in range(8): - x = cx-3 + i + x = cx - 3 + i y = 3 - pieces.append(Piece('pawn','black',x,y)) - - # БЕЛЫЕ + pieces.append(Piece('pawn', 'black', x, y)) + # Белые white_back = ['rook','knight','bishop','king','queen','bishop','knight','rook'] - for i,pname in enumerate(white_back): + for i, pname in enumerate(white_back): x = cx - 3 + i y = 27 - pieces.append(Piece(pname,'white',x,y)) + pieces.append(Piece(pname, 'white', x, y)) for i in range(8): - x = cx-3 + i + x = cx - 3 + i y = 26 - pieces.append(Piece('pawn','white',x,y)) - + pieces.append(Piece('pawn', 'white', x, y)) return pieces -# Инициализация фигур pieces = initialize_pieces() selected_piece = None @@ -239,12 +220,13 @@ winner = None # --- Туман войны --- cell_counters = {} # {(x,y): turns_since_last_revealed} -BONUS_GENERATION_CHANCE = 0.10 # 10% шанс появления бонуса при повторном открытии -COIN_GENERATION_CHANCE = 0.001 # 0.1% шанс появления монеты +# Каждая новая клетка имеет 10% шанс стать бонусной. +BONUS_GENERATION_CHANCE = 0.10 -# --- Система ходов --- +# --- Система ходов и многоперемещений --- current_turn = 'white' # Начинаем с белых turn_count = 0 +moves_remaining = 3 # За один ход игрок может сделать 3 перемещения # --- Механика повышения вероятности красных полей --- def update_bonus_probabilities(): @@ -292,26 +274,31 @@ def check_timer(): else: switch_turn() +# При переключении хода сбрасываем число оставшихся ходов и флаг has_moved у фигур текущего игрока. def switch_turn(): - global current_turn, turn_count, timer_expired + global current_turn, turn_count, timer_expired, moves_remaining current_turn = 'black' if current_turn == 'white' else 'white' turn_count += 1 reset_timer() timer_expired = False update_bonus_probabilities() + moves_remaining = 3 + for p in pieces: + if p.color == current_turn: + p.has_moved = False +# После успешного перемещения фигуры вызываем эту функцию. +def end_move(moved_piece): + global moves_remaining + moved_piece.has_moved = True + moves_remaining -= 1 + if moves_remaining <= 0: + switch_turn() + +# --- Обновление тумана войны и генерация бонусов/монет --- def update_fog(): - """ - Обновляет туман войны: - - Открытые клетки остаются открытыми. - - Закрытые клетки, которые не находятся рядом с любыми фигурами на расстоянии 2 клеток, - становятся закрытыми через 5 ходов. - - При повторном открытии клетки шанс на появление нового бонуса. - """ global global_revealed, bonus_cells - previous_revealed = global_revealed.copy() - new_revealed = set() for p in pieces: for dx in range(-2, 3): @@ -320,13 +307,9 @@ def update_fog(): ny = p.y + dy if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: new_revealed.add((nx, ny)) - - # Исключаем клетки торговцев из тумана войны for merchant_pos in merchant_positions: new_revealed.add(merchant_pos) - newly_revealed = new_revealed - previous_revealed - for y in range(GRID_HEIGHT): for x in range(GRID_WIDTH): pos = (x, y) @@ -338,34 +321,24 @@ def update_fog(): cell_counters[pos] += 1 else: cell_counters[pos] = 1 - if cell_counters[pos] >= 5: if pos in global_revealed: global_revealed.remove(pos) - # Удаляем бонус, если клетка закрывается и бонус не является 'damage' if pos in bonus_cells and bonus_cells[pos]['type'] != 'damage': del bonus_cells[pos] - - # Генерируем бонусы и монеты на новых открытых клетках for pos in newly_revealed: if pos not in bonus_cells and pos not in coin_cells and random.random() < BONUS_GENERATION_CHANCE: - bonus_type = choose_bonus_type() - if not any(p.x == pos[0] and p.y == pos[1] for p in pieces): - bonus_cells[pos] = {'type': bonus_type} - # Генерация монет - if pos not in coin_cells and random.random() < COIN_GENERATION_CHANCE: - if not any(p.x == pos[0] and p.y == pos[1] for p in pieces): - coin_cells.add(pos) + if random.random() < 0.90: + bonus_type = choose_bonus_type() + if not any(p.x == pos[0] and p.y == pos[1] for p in pieces): + bonus_cells[pos] = {'type': bonus_type} + else: + if not any(p.x == pos[0] and p.y == pos[1] for p in pieces): + coin_cells.add(pos) # --- Добавление торговцев --- -# Определяем позиции торговцев: центральная линия по краям central_line_y = GRID_HEIGHT // 2 -merchant_positions = [ - (0, central_line_y), - (GRID_WIDTH - 1, central_line_y) -] - -# Добавляем торговцы в глобальное множество открытых клеток +merchant_positions = [(0, central_line_y), (GRID_WIDTH - 1, central_line_y)] global_revealed.update(merchant_positions) # --- Монеты на поле --- @@ -375,14 +348,44 @@ coin_cells = set() player_coins = {'white': 0, 'black': 0} # --- Механика магазина --- +# Повышенные цены: 6 монет за покупку shop_open = False shop_items = { - 'extend_move': {'name': 'Расширить ход', 'cost': 3}, - 'increase_hp': {'name': 'Увеличить HP', 'cost': 3}, - 'damage_enemy': {'name': 'Уменьшить HP врага', 'cost': 3}, - 'curse': {'name': 'Проклятие', 'cost': 3} + 'extend_move': {'name': 'Расширить ход', 'cost': 6}, + 'increase_hp': {'name': 'Увеличить HP', 'cost': 6}, + 'damage_enemy': {'name': 'Уменьшить HP врага', 'cost': 6}, + 'curse': {'name': 'Проклятие', 'cost': 6} } +# --- Реализация спрайтов фигур --- +# Ожидается, что в папке "sprites" находятся файлы: wK.png, wQ.png, wR.png, wB.png, wN.png, wP.png для белых, +# и bK.png, bQ.png, bR.png, bB.png, bN.png, bP.png для чёрных. +piece_sprites = {} + +def load_piece_sprites(): + piece_names_map = { + 'king': 'K', + 'queen': 'Q', + 'rook': 'R', + 'bishop': 'B', + 'knight': 'N', + 'pawn': 'P' + } + colors = ['white', 'black'] + for color in colors: + letter = 'w' if color == 'white' else 'b' + for name in piece_names_map: + filename = f"sprites/{letter}{piece_names_map[name]}.png" + try: + image = pygame.image.load(filename).convert_alpha() + image = pygame.transform.scale(image, (CELL_SIZE, CELL_SIZE)) + piece_sprites[(color, name)] = image + except Exception as e: + print(f"Ошибка загрузки спрайта {filename}: {e}") + piece_sprites[(color, name)] = None + +load_piece_sprites() + # --- Функции отрисовки --- def draw_grid(): for x in range(0, CELL_SIZE * GRID_WIDTH, CELL_SIZE): @@ -395,38 +398,37 @@ def draw_fog(): for x in range(GRID_WIDTH): pos = (x, y) if pos not in global_revealed: - rect = pygame.Rect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE) + rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE) pygame.draw.rect(screen, COLOR_FOG, rect) else: if pos in merchant_positions: - continue # Торговцы всегда видимы + continue if cell_counters.get(pos, 0) == 4: - rect = pygame.Rect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE) + rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE) pygame.draw.rect(screen, COLOR_PRE_FOG, rect) def draw_bonus_cells(): for (bx, by), info in bonus_cells.items(): - # Для 'damage' бонусов отображаем всегда, иначе только если клетка видна if info['type'] != 'damage' and (bx, by) not in global_revealed: continue - cx = bx*CELL_SIZE + CELL_SIZE//2 - cy = by*CELL_SIZE + CELL_SIZE//2 + cx = bx * CELL_SIZE + CELL_SIZE // 2 + cy = by * CELL_SIZE + CELL_SIZE // 2 bonus_type = info['type'] if bonus_type == 'regen': color = COLOR_REGEN - pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE//4) + pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE // 4) elif bonus_type == 'hp_upgrade': color = COLOR_HP_UPGRADE - pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE//4) + pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE // 4) elif bonus_type == 'damage': color = COLOR_DAMAGE - pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE//4) + pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE // 4) elif bonus_type == 'king_hp_upgrade': color = COLOR_KING_HP_UPGRADE - pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE//4) + pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE // 4) elif bonus_type == 'add_piece': possible = False - for dx, dy in [(-1,0),(1,0),(0,-1),(0,1), (-1,-1),(1,-1),(1,1),(-1,1)]: + for dx, dy in [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(1,1),(-1,1)]: nx = bx + dx ny = by + dy if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: @@ -434,25 +436,22 @@ def draw_bonus_cells(): possible = True break color = COLOR_ADD_PIECE if possible else COLOR_ADD_PIECE_PINK - pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE//4) - # Рисуем плюс - pygame.draw.line(screen, (0,0,0), (cx - CELL_SIZE//8, cy), (cx + CELL_SIZE//8, cy), 2) - pygame.draw.line(screen, (0,0,0), (cx, cy - CELL_SIZE//8), (cx, cy + CELL_SIZE//8), 2) + pygame.draw.circle(screen, color, (cx, cy), CELL_SIZE // 4) + pygame.draw.line(screen, (0, 0, 0), (cx - CELL_SIZE // 8, cy), (cx + CELL_SIZE // 8, cy), 2) + pygame.draw.line(screen, (0, 0, 0), (cx, cy - CELL_SIZE // 8), (cx, cy + CELL_SIZE // 8), 2) def draw_coin_cells(): for pos in coin_cells: if pos not in global_revealed: - continue # Монеты видны только в открытых клетках + continue x, y = pos cx = x * CELL_SIZE + CELL_SIZE // 2 cy = y * CELL_SIZE + CELL_SIZE // 2 size = CELL_SIZE // 3 - # Рисуем оранжевый треугольник point1 = (cx, cy - size) point2 = (cx - size, cy + size) point3 = (cx + size, cy + size) pygame.draw.polygon(screen, COLOR_COIN, [point1, point2, point3]) - # Рисуем символ доллара dollar_text = font.render('$', True, (0, 0, 0)) text_rect = dollar_text.get_rect(center=(cx, cy)) screen.blit(dollar_text, text_rect) @@ -460,151 +459,123 @@ def draw_coin_cells(): def draw_merchants(): for pos in merchant_positions: x, y = pos - rect = pygame.Rect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE) + rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE) pygame.draw.rect(screen, COLOR_MERCHANT, rect) - # Рисуем стрелку вверх - arrow_color = (255, 255, 255) - arrow_size = CELL_SIZE // 2 points = [ (x * CELL_SIZE + CELL_SIZE // 2, y * CELL_SIZE + CELL_SIZE // 4), (x * CELL_SIZE + CELL_SIZE // 4, y * CELL_SIZE + 3 * CELL_SIZE // 4), (x * CELL_SIZE + 3 * CELL_SIZE // 4, y * CELL_SIZE + 3 * CELL_SIZE // 4) ] - pygame.draw.polygon(screen, arrow_color, points) + pygame.draw.polygon(screen, (255, 255, 255), points) def draw_pieces(pieces): for p in pieces: - rect = pygame.Rect(p.x*CELL_SIZE, p.y*CELL_SIZE, CELL_SIZE, CELL_SIZE) - - if p.color == 'white': - pygame.draw.rect(screen, COLOR_WHITE_PIECE, rect) - symbol = piece_symbols.get(p.name,'?') - text = font.render(symbol, True, (255,0,0)) - text_rect = text.get_rect(center=rect.center) - screen.blit(text, text_rect) - # HP чёрным - hp_str = f"{p.hp}/{p.max_hp}" - hp_text = font.render(hp_str, True, (0,0,0)) - screen.blit(hp_text, (rect.x+2, rect.y+2)) + x = p.x * CELL_SIZE + y = p.y * CELL_SIZE + sprite = piece_sprites.get((p.color, p.name)) + if sprite: + screen.blit(sprite, (x, y)) else: - pygame.draw.rect(screen, COLOR_BLACK_PIECE, rect) - symbol = piece_symbols.get(p.name,'?') - text = font.render(symbol, True, (255,0,0)) + rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) + if p.color == 'white': + pygame.draw.rect(screen, COLOR_WHITE_PIECE, rect) + else: + pygame.draw.rect(screen, COLOR_BLACK_PIECE, rect) + symbol = piece_symbols.get(p.name, '?') + text = font.render(symbol, True, (255, 0, 0)) text_rect = text.get_rect(center=rect.center) screen.blit(text, text_rect) - # HP белым - hp_str = f"{p.hp}/{p.max_hp}" - hp_text = font.render(hp_str, True, (255,255,255)) - screen.blit(hp_text, (rect.x+2, rect.y+2)) - + # Отрисовка HP с полупрозрачным фоном для лучшей читаемости: + hp_str = f"{p.hp}/{p.max_hp}" + # Определяем цвета: для белых фигур текст будет черным, для черных – белым; + # фон выбираем противоположный + if p.color == 'white': + text_color = (0, 0, 0) + bg_color = (255, 255, 255) + else: + text_color = (255, 255, 255) + bg_color = (0, 0, 0) + hp_text = font.render(hp_str, True, text_color) + text_rect = hp_text.get_rect(topleft=(x + 2, y + 2)) + # Создаем временную поверхность для фона с альфа-каналом + bg_surf = pygame.Surface(text_rect.size) + bg_surf.set_alpha(200) # степень прозрачности (0-255) + bg_surf.fill(bg_color) + screen.blit(bg_surf, text_rect.topleft) + screen.blit(hp_text, text_rect.topleft) if p.selected: + rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) pygame.draw.rect(screen, COLOR_SELECTED, rect, 2) - for (mx, my) in possible_moves: - r = pygame.Rect(mx*CELL_SIZE, my*CELL_SIZE, CELL_SIZE, CELL_SIZE) + r = pygame.Rect(mx * CELL_SIZE, my * CELL_SIZE, CELL_SIZE, CELL_SIZE) pygame.draw.rect(screen, COLOR_MOVE, r, 2) def draw_statistics_panel(): panel_rect = pygame.Rect(CELL_SIZE * GRID_WIDTH, 0, PANEL_WIDTH, WINDOW_HEIGHT) pygame.draw.rect(screen, (50, 50, 50), panel_rect) - - # Заголовок title_text = shop_font.render("Статистика", True, (255, 255, 255)) screen.blit(title_text, (CELL_SIZE * GRID_WIDTH + 10, 10)) - - # Текущий ход turn_text = turn_font.render(f"Ход: {current_turn.capitalize()}", True, COLOR_TURN_TEXT) screen.blit(turn_text, (CELL_SIZE * GRID_WIDTH + 10, 50)) - - # Количество ходов counter_text = counter_font.render(f"Ходов: {turn_count}", True, (255, 255, 255)) screen.blit(counter_text, (CELL_SIZE * GRID_WIDTH + 10, 80)) - - # Таймер time_left = get_time_left() minutes = time_left // 60 seconds = time_left % 60 timer_text_str = f"Время: {minutes:02}:{seconds:02}" - if timer_expired and player_timeouts[current_turn] >=1: - color = COLOR_TIMER_WARNING - else: - color = COLOR_TIMER_NORMAL + color = COLOR_TIMER_WARNING if timer_expired and player_timeouts[current_turn] >= 1 else COLOR_TIMER_NORMAL timer_text = timer_font.render(timer_text_str, True, color) screen.blit(timer_text, (CELL_SIZE * GRID_WIDTH + 10, 110)) - - # Количество монет coins_text_white = counter_font.render(f"Белые монеты: {player_coins['white']}", True, (255, 255, 0)) coins_text_black = counter_font.render(f"Чёрные монеты: {player_coins['black']}", True, (255, 255, 0)) screen.blit(coins_text_white, (CELL_SIZE * GRID_WIDTH + 10, 140)) screen.blit(coins_text_black, (CELL_SIZE * GRID_WIDTH + 10, 170)) - - # Если открыт магазин, отображаем его + moves_text = counter_font.render(f"Осталось ходов: {moves_remaining}", True, (255, 255, 255)) + screen.blit(moves_text, (CELL_SIZE * GRID_WIDTH + 10, 200)) if shop_open: draw_shop() def draw_shop(): shop_x = CELL_SIZE * GRID_WIDTH + 10 - shop_y = 200 + shop_y = 240 shop_title = shop_font.render("Магазин", True, (255, 255, 0)) screen.blit(shop_title, (shop_x, shop_y)) - for idx, (key, item) in enumerate(shop_items.items()): item_y = shop_y + 40 + idx * 60 - # Кнопка button_rect = pygame.Rect(shop_x, item_y, PANEL_WIDTH - 20, 50) - # Проверка, хватает ли монет - if player_coins[current_turn] >= item['cost']: - button_color = (0, 255, 0) # Зелёный - else: - button_color = (128, 128, 128) # Серый + button_color = (0, 255, 0) if player_coins[current_turn] >= item['cost'] else (128, 128, 128) pygame.draw.rect(screen, button_color, button_rect) - # Название предмета - item_text = shop_font.render(f"{item['name']} - {item['cost']} мон.", True, (0,0,0)) + item_text = shop_font.render(f"{item['name']} - {item['cost']} мон.", True, (0, 0, 0)) screen.blit(item_text, (shop_x + 5, item_y + 15)) - # Хранение позиции кнопки для обработки кликов shop_items[key]['button_rect'] = button_rect def draw_victory(winner): text = victory_font.render(f"{winner.capitalize()} победил!", True, (255, 255, 255)) - text_rect = text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2)) + text_rect = text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2)) screen.blit(text, text_rect) def apply_bonus(piece, bonus_type, pieces): - """Применяем эффект бонуса к фигуре piece в зависимости от типа.""" if bonus_type == 'regen': - # Регенерация (1..2) для НЕ короля if piece.name != 'king': amt = random.randint(1, 2) piece.hp = min(piece.hp + amt, piece.max_hp) - elif bonus_type == 'hp_upgrade': - # Повышение max_hp только НЕ королю без восстановления HP if piece.name != 'king': piece.max_hp += 1 - # piece.hp остается неизменным - elif bonus_type == 'king_hp_upgrade': - # Только для короля, бонус активируется другим игроком if piece.name != 'king': - # Найти короля той же команды king = next((p for p in pieces if p.name == 'king' and p.color == piece.color), None) if king: if king.hp == king.max_hp: - # Увеличиваем max_hp на 1 king.max_hp += 1 king.hp = king.max_hp else: - # Восстанавливаем HP до max king.hp = min(king.hp + 1, king.max_hp) - elif bonus_type == 'damage': - # Урон при прохождении через клетку 'damage' уже обрабатывается отдельно pass - elif bonus_type == 'add_piece': - # Добавление новой фигуры рядом, если возможно и не король if piece.name != 'king': - directions = [(-1,0),(1,0),(0,-1),(0,1), (-1,-1),(1,-1),(1,1),(-1,1)] + directions = [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(1,1),(-1,1)] random.shuffle(directions) for dx, dy in directions: nx = piece.x + dx @@ -617,15 +588,15 @@ def apply_bonus(piece, bonus_type, pieces): def handle_shop_purchase(key): if player_coins[current_turn] < shop_items[key]['cost']: - return # Недостаточно монет + return player_coins[current_turn] -= shop_items[key]['cost'] if key == 'extend_move': for p in pieces: if p.color == current_turn: if p.name in ['rook', 'bishop', 'queen']: - setattr(p, 'move_range', getattr(p, 'move_range', 3) + 2) # До 5 + setattr(p, 'move_range', getattr(p, 'move_range', 3) + 2) elif p.name == 'pawn': - setattr(p, 'move_range', getattr(p, 'move_range', 1) + 1) # До 2 + setattr(p, 'move_range', getattr(p, 'move_range', 1) + 1) elif key == 'increase_hp': for p in pieces: if p.color == current_turn and p.name != 'king': @@ -640,12 +611,10 @@ def handle_shop_purchase(key): elif p.hp > 1: p.hp = 1 else: - p.hp = 1 # Уже 1, остаётся 1 + p.hp = 1 elif key == 'curse': - # Уменьшаем вероятность появления 'regen' и 'hp_upgrade' на 10% bonus_probabilities['regen'] = max(bonus_probabilities['regen'] - 0.10, 0) bonus_probabilities['hp_upgrade'] = max(bonus_probabilities['hp_upgrade'] - 0.10, 0) - # Нормализуем вероятности total_prob = sum(bonus_probabilities.values()) for bt in bonus_probabilities: bonus_probabilities[bt] /= total_prob if total_prob > 0 else 1 @@ -663,36 +632,25 @@ while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False - elif event.type == pygame.MOUSEBUTTONDOWN and not game_over: mx, my = pygame.mouse.get_pos() if mx < CELL_SIZE * GRID_WIDTH and my < CELL_SIZE * GRID_HEIGHT: gx = mx // CELL_SIZE gy = my // CELL_SIZE - clicked_piece = next((p for p in pieces if p.x == gx and p.y == gy), None) - if selected_piece: if (gx, gy) in possible_moves: - # Проверяем бонус if (gx, gy) in bonus_cells: bonus_type = bonus_cells[(gx, gy)]['type'] apply_bonus(selected_piece, bonus_type, pieces) del bonus_cells[(gx, gy)] - - # Проверяем монету if (gx, gy) in coin_cells: player_coins[current_turn] += 1 coin_cells.remove((gx, gy)) - - # Проверяем, является ли клетка торговцем if (gx, gy) in merchant_positions: shop_open = True - - # Проверяем, есть ли там вражеская фигура defender = next((p for p in pieces if p.x == gx and p.y == gy), None) if defender: - # Проводим бой attacker = selected_piece attacker_alive, defender_alive = resolve_combat(attacker, defender) if not defender_alive: @@ -708,15 +666,14 @@ while running: switch_turn() continue else: - # Перемещаем атакующего path = [] if attacker.name in ['rook', 'bishop', 'queen']: dx = gx - attacker.x dy = gy - attacker.y if dx != 0: - dx = dx // abs(dx) + dx //= abs(dx) if dy != 0: - dy = dy // abs(dy) + dy //= abs(dy) for step in range(1, max(abs(gx - attacker.x), abs(gy - attacker.y))): path_x = attacker.x + dx * step path_y = attacker.y + dy * step @@ -724,7 +681,7 @@ while running: survived = True for pos in path: if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage': - damage = random.randint(1,5) + damage = random.randint(1, 5) attacker.hp = max(attacker.hp - damage, 0) del bonus_cells[pos] if attacker.hp == 0: @@ -738,20 +695,18 @@ while running: attacker.x = gx attacker.y = gy attacker.selected = False + end_move(attacker) selected_piece = None possible_moves.clear() - switch_turn() - else: - # Пустая клетка — просто ходим path = [] if selected_piece.name in ['rook', 'bishop', 'queen']: dx = gx - selected_piece.x dy = gy - selected_piece.y if dx != 0: - dx = dx // abs(dx) + dx //= abs(dx) if dy != 0: - dy = dy // abs(dy) + dy //= abs(dy) for step in range(1, max(abs(gx - selected_piece.x), abs(gy - selected_piece.y))): path_x = selected_piece.x + dx * step path_y = selected_piece.y + dy * step @@ -759,7 +714,7 @@ while running: survived = True for pos in path: if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage': - damage = random.randint(1,5) + damage = random.randint(1, 5) selected_piece.hp = max(selected_piece.hp - damage, 0) del bonus_cells[pos] if selected_piece.hp == 0: @@ -773,36 +728,28 @@ while running: selected_piece.x = gx selected_piece.y = gy selected_piece.selected = False + end_move(selected_piece) selected_piece = None possible_moves.clear() - switch_turn() - update_fog() else: selected_piece.selected = False selected_piece = None possible_moves.clear() - else: - # Выбираем фигуру только если клетка видима и соответствует текущему ходу - if clicked_piece and (gx, gy) in global_revealed and clicked_piece.color == current_turn: + if clicked_piece and (gx, gy) in global_revealed and clicked_piece.color == current_turn and not clicked_piece.has_moved: selected_piece = clicked_piece selected_piece.selected = True possible_moves = selected_piece.get_possible_moves(pieces) - else: - # Клик в правой панели if shop_open: for key, item in shop_items.items(): if 'button_rect' in item and item['button_rect'].collidepoint(mx, my): if player_coins[current_turn] >= item['cost']: handle_shop_purchase(key) - elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: shop_open = False - - # Отрисовка screen.fill(COLOR_BG) draw_grid() draw_fog() diff --git a/sprites/bB.png b/sprites/bB.png new file mode 100644 index 0000000..264f82b Binary files /dev/null and b/sprites/bB.png differ diff --git a/sprites/bK.png b/sprites/bK.png new file mode 100644 index 0000000..c451963 Binary files /dev/null and b/sprites/bK.png differ diff --git a/sprites/bN.png b/sprites/bN.png new file mode 100644 index 0000000..77f00d3 Binary files /dev/null and b/sprites/bN.png differ diff --git a/sprites/bP.png b/sprites/bP.png new file mode 100644 index 0000000..0ef4876 Binary files /dev/null and b/sprites/bP.png differ diff --git a/sprites/bQ.png b/sprites/bQ.png new file mode 100644 index 0000000..199856f Binary files /dev/null and b/sprites/bQ.png differ diff --git a/sprites/bR.png b/sprites/bR.png new file mode 100644 index 0000000..7079afd Binary files /dev/null and b/sprites/bR.png differ diff --git a/sprites/wB.png b/sprites/wB.png new file mode 100644 index 0000000..d457ce5 Binary files /dev/null and b/sprites/wB.png differ diff --git a/sprites/wK.png b/sprites/wK.png new file mode 100644 index 0000000..87f51f5 Binary files /dev/null and b/sprites/wK.png differ diff --git a/sprites/wN.png b/sprites/wN.png new file mode 100644 index 0000000..520c3fb Binary files /dev/null and b/sprites/wN.png differ diff --git a/sprites/wP.png b/sprites/wP.png new file mode 100644 index 0000000..c2f806f Binary files /dev/null and b/sprites/wP.png differ diff --git a/sprites/wQ.png b/sprites/wQ.png new file mode 100644 index 0000000..f87155c Binary files /dev/null and b/sprites/wQ.png differ diff --git a/sprites/wR.png b/sprites/wR.png new file mode 100644 index 0000000..98eadca Binary files /dev/null and b/sprites/wR.png differ