import pygame import sys import random import time pygame.init() # ========= Глобальные настройки по умолчанию ========= CELL_SIZE = 32 PANEL_WIDTH = 200 # Значения по умолчанию для поля и шанса бонусов (меню позволит их изменять) default_GRID_WIDTH = 30 default_GRID_HEIGHT = 30 default_BONUS_GENERATION_CHANCE = 0.10 # 10% # ========= Цвета ========= COLOR_BG = (30, 30, 30) COLOR_GRID = (50, 50, 50) COLOR_FOG = (20, 20, 20) COLOR_SELECTED = (255, 0, 0) COLOR_MOVE = (0, 255, 0) COLOR_WHITE_PIECE = (255, 255, 255) 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_ADD_PIECE = (255, 255, 255) COLOR_ADD_PIECE_PINK = (255, 192, 203) COLOR_TURN_TEXT = (255, 255, 255) COLOR_TIMER_NORMAL = (255, 255, 255) COLOR_TIMER_WARNING = (255, 0, 0) COLOR_MERCHANT = (139, 69, 19) COLOR_COIN = (255, 165, 0) # ========= Шрифты ========= font = pygame.font.SysFont(None, CELL_SIZE // 2) victory_font = pygame.font.SysFont(None, 60) turn_font = pygame.font.SysFont(None, 30) counter_font = pygame.font.SysFont(None, 30) timer_font = pygame.font.SysFont(None, 25) shop_font = pygame.font.SysFont(None, 24) # ========= Меню настроек ========= def main_menu(): menu_running = True menu_width, menu_height = 600, 400 menu_screen = pygame.display.set_mode((menu_width, menu_height)) pygame.display.set_caption("Настройки игры") # Начальные значения из настроек по умолчанию grid_width = default_GRID_WIDTH grid_height = default_GRID_HEIGHT bonus_chance = default_BONUS_GENERATION_CHANCE # 0.10 = 10% # Подготавливаем тексты для кнопок minus_text = shop_font.render("-", True, (0, 0, 0)) plus_text = shop_font.render("+", True, (0, 0, 0)) while menu_running: menu_screen.fill((50, 50, 50)) # Заголовок title_text = shop_font.render("Настройки игры", True, (255,255,255)) title_rect = title_text.get_rect(center=(menu_width//2, 40)) menu_screen.blit(title_text, title_rect) # Размер поля field_text = shop_font.render("Размер поля:", True, (255,255,255)) menu_screen.blit(field_text, (50, 80)) # Ширина поля width_text = shop_font.render(f"Ширина: {grid_width}", True, (255,255,255)) menu_screen.blit(width_text, (100, 130)) width_minus_rect = pygame.Rect(50, 130, 40, 30) width_plus_rect = pygame.Rect(250, 130, 40, 30) pygame.draw.rect(menu_screen, (200,200,200), width_minus_rect) pygame.draw.rect(menu_screen, (200,200,200), width_plus_rect) menu_screen.blit(minus_text, minus_text.get_rect(center=width_minus_rect.center)) menu_screen.blit(plus_text, plus_text.get_rect(center=width_plus_rect.center)) # Высота поля height_text = shop_font.render(f"Высота: {grid_height}", True, (255,255,255)) menu_screen.blit(height_text, (100, 180)) height_minus_rect = pygame.Rect(50, 180, 40, 30) height_plus_rect = pygame.Rect(250, 180, 40, 30) pygame.draw.rect(menu_screen, (200,200,200), height_minus_rect) pygame.draw.rect(menu_screen, (200,200,200), height_plus_rect) menu_screen.blit(minus_text, minus_text.get_rect(center=height_minus_rect.center)) menu_screen.blit(plus_text, plus_text.get_rect(center=height_plus_rect.center)) # Шанс бонусов (выводим значение в процентах) bonus_label = shop_font.render("Шанс бонусов (%):", True, (255,255,255)) menu_screen.blit(bonus_label, (100, 230)) bonus_value = shop_font.render(f"{int(bonus_chance*100)}", True, (255,255,255)) menu_screen.blit(bonus_value, (300, 230)) bonus_minus_rect = pygame.Rect(350, 225, 40, 30) bonus_plus_rect = pygame.Rect(450, 225, 40, 30) pygame.draw.rect(menu_screen, (200,200,200), bonus_minus_rect) pygame.draw.rect(menu_screen, (200,200,200), bonus_plus_rect) menu_screen.blit(minus_text, minus_text.get_rect(center=bonus_minus_rect.center)) menu_screen.blit(plus_text, plus_text.get_rect(center=bonus_plus_rect.center)) # Кнопка "Начать игру" start_rect = pygame.Rect(menu_width//2 - 75, 300, 150, 50) pygame.draw.rect(menu_screen, (0,255,0), start_rect) start_text = shop_font.render("Начать игру", True, (0,0,0)) menu_screen.blit(start_text, start_text.get_rect(center=start_rect.center)) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.MOUSEBUTTONDOWN: mx, my = pygame.mouse.get_pos() if width_minus_rect.collidepoint(mx, my): grid_width = max(8, grid_width - 1) elif width_plus_rect.collidepoint(mx, my): grid_width = min(100, grid_width + 1) elif height_minus_rect.collidepoint(mx, my): grid_height = max(8, grid_height - 1) elif height_plus_rect.collidepoint(mx, my): grid_height = min(100, grid_height + 1) elif bonus_minus_rect.collidepoint(mx, my): bonus_chance = max(0.0, bonus_chance - 0.01) elif bonus_plus_rect.collidepoint(mx, my): bonus_chance = min(1.0, bonus_chance + 0.01) elif start_rect.collidepoint(mx, my): menu_running = False pygame.display.flip() return grid_width, grid_height, bonus_chance # ========= Вызываем меню настроек и обновляем глобальные параметры ========= GRID_WIDTH, GRID_HEIGHT, BONUS_GENERATION_CHANCE = main_menu() WINDOW_WIDTH = CELL_SIZE * GRID_WIDTH + PANEL_WIDTH WINDOW_HEIGHT = CELL_SIZE * GRID_HEIGHT # Теперь устанавливаем главное окно игры screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("Шахматы с Открытым Миром 1.1.1") # ========= Глобальные переменные ========= global_revealed = set() bonus_cells = {} base_bonus_probabilities = { 'regen': 0.42, 'hp_upgrade': 0.32, 'damage': 0.2, 'king_hp_upgrade': 0.05, 'add_piece': 0.01 } bonus_probabilities = base_bonus_probabilities.copy() piece_symbols = { 'rook': 'Л', 'knight': 'К', 'bishop': 'С', 'queen': 'Ф', 'king': 'К', 'pawn': 'П' } # ========= Класс фигур ========= class Piece: def __init__(self, name, color, x, y): self.name = name self.color = color # 'white' или 'black' self.x = x self.y = y self.selected = False self.has_moved = False if self.name == 'king': self.max_hp = 5 self.hp = 5 elif self.name in ['rook', 'knight', 'bishop', 'queen']: self.max_hp = 3 self.hp = 3 elif self.name == 'pawn': self.max_hp = 1 self.hp = 1 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)] for dx, dy in directions: nx = self.x + dx ny = 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 in ['rook', 'bishop', 'queen']: max_steps = getattr(self, 'move_range', 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)] for dx, dy in directions: for step in range(1, max_steps + 1): nx = self.x + dx * step ny = self.y + dy * step 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 blocking: if blocking.color != self.color: moves.append((nx, ny)) break 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 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)] for dx, dy in directions: 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 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 return (True, False) # ========= Функция инициализации фигур ========= def initialize_pieces(): pieces = [] cx = GRID_WIDTH // 2 offset = 3 # для базовой линии – 8 фигур # Вычисляем вертикальные позиции пропорционально высоте поля: black_back_y = max(0, int(GRID_HEIGHT * 0.1)) black_pawn_y = min(GRID_HEIGHT - 1, black_back_y + 1) white_back_y = GRID_HEIGHT - 1 - int(GRID_HEIGHT * 0.1) white_pawn_y = max(0, white_back_y - 1) black_back = ['rook','knight','bishop','king','queen','bishop','knight','rook'] for i, pname in enumerate(black_back): x = cx - offset + i if 0 <= x < GRID_WIDTH: pieces.append(Piece(pname, 'black', x, black_back_y)) for i in range(8): x = cx - offset + i if 0 <= x < GRID_WIDTH: pieces.append(Piece('pawn', 'black', x, black_pawn_y)) white_back = ['rook','knight','bishop','king','queen','bishop','knight','rook'] for i, pname in enumerate(white_back): x = cx - offset + i if 0 <= x < GRID_WIDTH: pieces.append(Piece(pname, 'white', x, white_back_y)) for i in range(8): x = cx - offset + i if 0 <= x < GRID_WIDTH: pieces.append(Piece('pawn', 'white', x, white_pawn_y)) return pieces # ========= Спрайты для фигур ========= 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() # ========= Спрайты для окружающей среды ========= try: open_cell_sprite = pygame.image.load("sprites/open_cell.png").convert_alpha() open_cell_sprite = pygame.transform.scale(open_cell_sprite, (CELL_SIZE, CELL_SIZE)) except Exception as e: print(f"Ошибка загрузки open_cell.png: {e}") open_cell_sprite = None try: fog_sprite = pygame.image.load("sprites/fog.png").convert_alpha() fog_sprite = pygame.transform.scale(fog_sprite, (CELL_SIZE, CELL_SIZE)) except Exception as e: print(f"Ошибка загрузки fog.png: {e}") fog_sprite = None # ========= Механика бонусов ========= def choose_bonus_type(): rand = random.random() cumulative = 0 for bonus_type, prob in bonus_probabilities.items(): cumulative += prob if rand < cumulative: return bonus_type return 'regen' # ========= Торговцы, монеты и магазин ========= merchant_positions = [(0, GRID_HEIGHT // 2), (GRID_WIDTH - 1, GRID_HEIGHT // 2)] global_revealed.update(merchant_positions) coin_cells = set() player_coins = {'white': 0, 'black': 0} shop_open = False shop_items = { 'extend_move': {'name': 'Расширить ход', 'cost': 6}, 'increase_hp': {'name': 'Увеличить HP', 'cost': 6}, 'damage_enemy': {'name': 'Уменьшить HP врага', 'cost': 6}, 'curse': {'name': 'Проклятие', 'cost': 6} } # ========= Система ходов ========= current_turn = 'white' turn_count = 0 moves_remaining = 3 def update_bonus_probabilities(): global bonus_probabilities if turn_count <= 100: bonus_probabilities = base_bonus_probabilities.copy() else: extra_turns = turn_count - 100 max_extra = 200 increase_per_turn = (0.50 - base_bonus_probabilities['damage']) / max_extra new_damage_prob = min(base_bonus_probabilities['damage'] + increase_per_turn * extra_turns, 0.50) remaining_prob = 1 - new_damage_prob total_other_probs = sum(base_bonus_probabilities[bt] for bt in base_bonus_probabilities if bt != 'damage') bonus_probabilities = {} for bt, prob in base_bonus_probabilities.items(): if bt == 'damage': bonus_probabilities[bt] = new_damage_prob else: bonus_probabilities[bt] = prob / total_other_probs * remaining_prob # ========= Время на ход ========= TURN_TIME_LIMIT = 120 # секунд timer_start_time = time.time() timer_expired = False player_timeouts = {'white': 0, 'black': 0} def reset_timer(): global timer_start_time, timer_expired timer_start_time = time.time() timer_expired = False def get_time_left(): elapsed = time.time() - timer_start_time return max(0, TURN_TIME_LIMIT - int(elapsed)) def check_timer(): global timer_expired, game_over, winner time_left = get_time_left() if time_left <= 0 and not timer_expired: timer_expired = True player_timeouts[current_turn] += 1 if player_timeouts[current_turn] >= 2: winner = 'black' if current_turn == 'white' else 'white' game_over = True else: switch_turn() def switch_turn(): 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 # Если фигура погибла, этот ход всё равно расходуется moves_remaining -= 1 if moves_remaining <= 0: switch_turn() # ========= Обновление тумана (с удалением монет при старении) ========= cell_counters = {} # {(x,y): turns_since_last_revealed} def update_fog(): global global_revealed, bonus_cells, coin_cells previous_revealed = global_revealed.copy() new_revealed = set() for p in pieces: for dx in range(-2, 3): for dy in range(-2, 3): nx = p.x + dx 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) if pos in new_revealed: cell_counters[pos] = 0 global_revealed.add(pos) else: if pos in cell_counters: cell_counters[pos] += 1 else: cell_counters[pos] = 1 if cell_counters[pos] >= 5: if pos in global_revealed: global_revealed.remove(pos) if pos in bonus_cells and bonus_cells[pos]['type'] != 'damage': del bonus_cells[pos] if pos in coin_cells: del coin_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: # 95% шанс бонус, 5% монета if random.random() < 0.95: 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) # ========= Функции отрисовки ========= def draw_grid(): for x in range(0, CELL_SIZE * GRID_WIDTH, CELL_SIZE): pygame.draw.line(screen, COLOR_GRID, (x, 0), (x, CELL_SIZE * GRID_HEIGHT)) for y in range(0, CELL_SIZE * GRID_HEIGHT, CELL_SIZE): pygame.draw.line(screen, COLOR_GRID, (0, y), (CELL_SIZE * GRID_WIDTH, y)) def draw_environment(): for y in range(GRID_HEIGHT): for x in range(GRID_WIDTH): pos = (x, y) rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE) if pos in global_revealed: if open_cell_sprite: screen.blit(open_cell_sprite, rect) else: pygame.draw.rect(screen, (30, 30, 30), rect) if cell_counters.get(pos, 0) == 4 and (pos not in bonus_cells) and (pos not in coin_cells): if fog_sprite: overlay = fog_sprite.copy() overlay.set_alpha(128) screen.blit(overlay, rect) else: overlay = pygame.Surface((CELL_SIZE, CELL_SIZE)) overlay.set_alpha(128) overlay.fill((169, 169, 169)) screen.blit(overlay, rect) else: if fog_sprite: screen.blit(fog_sprite, rect) else: pygame.draw.rect(screen, COLOR_FOG, rect) def draw_bonus_cells(): for (bx, by), info in bonus_cells.items(): 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 bonus_type = info['type'] if bonus_type == 'regen': color = COLOR_REGEN 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) elif bonus_type == 'damage': color = COLOR_DAMAGE 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) 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)]: nx = bx + dx ny = by + dy if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: if not any(p.x == nx and p.y == ny for p in pieces): 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) def draw_coin_cells(): for pos in coin_cells: if pos not in global_revealed: 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) def draw_merchants(): for pos in merchant_positions: x, y = pos rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE) pygame.draw.rect(screen, COLOR_MERCHANT, rect) 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, (255, 255, 255), points) def draw_pieces(pieces): for p in pieces: 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: 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_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) 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) 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}" 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 = 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) 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)) 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)) screen.blit(text, text_rect) # ========= Применение бонуса ========= def apply_bonus(piece, bonus_type, pieces): if bonus_type == 'regen': if piece.name != 'king': amt = random.randint(1, 2) piece.hp = min(piece.hp + amt, piece.max_hp) elif bonus_type == 'hp_upgrade': if piece.name != 'king': piece.max_hp += 1 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: king.max_hp += 1 king.hp = king.max_hp else: king.hp = min(king.hp + 1, king.max_hp) elif bonus_type == 'damage': damage = random.randint(1, 5) piece.hp -= damage print(f"Бонус DAMAGE: Фигура получила {damage} урона, осталось {piece.hp} HP") if piece.hp <= 0: try: pieces.remove(piece) except ValueError: pass return 'dead' 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)] random.shuffle(directions) for dx, dy in directions: nx = piece.x + dx ny = piece.y + dy if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: if not any(p.x == nx and p.y == ny for p in pieces) and (nx, ny) not in bonus_cells and (nx, ny) not in merchant_positions: new_piece = Piece(piece.name, piece.color, nx, ny) pieces.append(new_piece) break # ========= Инициализация фигур ========= pieces = initialize_pieces() selected_piece = None possible_moves = [] game_over = False winner = None # ========= Инициализация тумана ========= cell_counters = {} # {(x,y): turns_since_last_revealed} update_fog() # ========= Основной игровой цикл ========= running = True clock = pygame.time.Clock() while running: clock.tick(30) check_timer() 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: # Если на клетке есть бонус damage по пути if selected_piece.name in ['rook', 'bishop', 'queen']: # Если фигура двигается на несколько клеток, проверяем промежуточный путь path = [] dx = gx - selected_piece.x dy = gy - selected_piece.y if dx != 0: dx //= abs(dx) if dy != 0: 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 path.append((path_x, path_y)) survived = True for pos in path: if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage': damage = random.randint(1, 5) selected_piece.hp = max(selected_piece.hp - damage, 0) del bonus_cells[pos] if selected_piece.hp == 0: # — модификация для обработки смерти на пути: pieces.remove(selected_piece) selected_piece = None possible_moves.clear() survived = False # Считаем, что ход потрачен, уменьшаем moves_remaining moves_remaining -= 1 break if not survived: if moves_remaining <= 0: switch_turn() continue # Если бонус (damage или другой) находится непосредственно на целевой клетке if (gx, gy) in bonus_cells: bonus_type = bonus_cells[(gx, gy)]['type'] result = apply_bonus(selected_piece, bonus_type, pieces) del bonus_cells[(gx, gy)] if result == 'dead': # Если фигура погибла, засчитываем ход, уменьшаем счетчик и остаёмся на том же игроке moves_remaining -= 1 if moves_remaining <= 0: switch_turn() selected_piece = None possible_moves.clear() continue 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: if defender.name == 'king': winner = attacker.color game_over = True pieces.remove(defender) if not attacker_alive: pieces.remove(attacker) selected_piece = None possible_moves.clear() moves_remaining -= 1 if moves_remaining <= 0: switch_turn() continue else: path = [] if attacker.name in ['rook', 'bishop', 'queen']: dx = gx - attacker.x dy = gy - attacker.y if dx != 0: dx //= abs(dx) if dy != 0: 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 path.append((path_x, path_y)) survived = True for pos in path: if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage': damage = random.randint(1, 5) attacker.hp = max(attacker.hp - damage, 0) del bonus_cells[pos] if attacker.hp == 0: pieces.remove(attacker) selected_piece = None possible_moves.clear() survived = False moves_remaining -= 1 break if not survived: if moves_remaining <= 0: switch_turn() continue attacker.x = gx attacker.y = gy attacker.selected = False end_move(attacker) selected_piece = None possible_moves.clear() else: path = [] if selected_piece.name in ['rook', 'bishop', 'queen']: dx = gx - selected_piece.x dy = gy - selected_piece.y if dx != 0: dx //= abs(dx) if dy != 0: 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 path.append((path_x, path_y)) survived = True for pos in path: if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage': damage = random.randint(1, 5) selected_piece.hp = max(selected_piece.hp - damage, 0) del bonus_cells[pos] if selected_piece.hp == 0: pieces.remove(selected_piece) selected_piece = None possible_moves.clear() survived = False moves_remaining -= 1 break if not survived: if moves_remaining <= 0: switch_turn() continue selected_piece.x = gx selected_piece.y = gy selected_piece.selected = False end_move(selected_piece) selected_piece = None possible_moves.clear() 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 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']: # Здесь можно добавить логику покупки pass elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: shop_open = False screen.fill(COLOR_BG) draw_environment() draw_grid() draw_merchants() draw_bonus_cells() draw_coin_cells() draw_pieces(pieces) draw_statistics_panel() if game_over and winner: draw_victory(winner) pygame.display.flip() pygame.quit() sys.exit()