OpenWorldChess/chess_open_world.py
2025-02-01 13:45:33 +03:00

767 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pygame
import sys
import random
import time
pygame.init()
# --- ПАРАМЕТРЫ ПОЛЯ ---
CELL_SIZE = 32
GRID_WIDTH = 30
GRID_HEIGHT = 30
PANEL_WIDTH = 200 # Ширина правой панели
WINDOW_WIDTH = CELL_SIZE * GRID_WIDTH + PANEL_WIDTH
WINDOW_HEIGHT = CELL_SIZE * GRID_HEIGHT
# --- ЦВЕТА ---
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_PRE_FOG = (169, 169, 169) # Светло-серый
# Цвет таймера
COLOR_TIMER_NORMAL = (255, 255, 255) # Белый
COLOR_TIMER_WARNING = (255, 0, 0) # Красный
# Цвет торговца
COLOR_MERCHANT = (139, 69, 19) # Коричневый
# Цвет монеты
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)
counter_font = pygame.font.SysFont(None, 30)
timer_font = pygame.font.SysFont(None, 25)
shop_font = pygame.font.SysFont(None, 24)
# --- Глобальное множество открытых клеток (туман войны) ---
global_revealed = set()
# --- Словарь символов (фолбэк, если спрайт не найден) ---
piece_symbols = {
'rook': 'Л',
'knight': 'К',
'bishop': 'С',
'queen': 'Ф',
'king': 'К', # Король
'pawn': 'П'
}
# === МЕХАНИКА БОНУСОВ ===
bonus_cells = {}
base_bonus_probabilities = {
'regen': 0.42, # 42%
'hp_upgrade': 0.32, # 32%
'damage': 0.2, # 20%
'king_hp_upgrade': 0.05, # 5%
'add_piece': 0.01 # 1%
}
bonus_probabilities = base_bonus_probabilities.copy()
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' # По умолчанию
# === МЕХАНИКА БОЯ ===
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)
# === КЛАСС ФИГУРЫ ===
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 # Для ограничения повторного хода за один ход
# Инициализация HP
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 = 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)]
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 initialize_pieces():
"""Чёрные: y=2 (back), y=3 (pawns). Белые: y=27 (back), y=26 (pawns)."""
pieces = []
cx = GRID_WIDTH // 2
# Чёрные
black_back = ['rook','knight','bishop','king','queen','bishop','knight','rook']
for i, pname in enumerate(black_back):
x = cx - 3 + i
y = 2
pieces.append(Piece(pname, 'black', x, y))
for i in range(8):
x = cx - 3 + i
y = 3
pieces.append(Piece('pawn', 'black', x, y))
# Белые
white_back = ['rook','knight','bishop','king','queen','bishop','knight','rook']
for i, pname in enumerate(white_back):
x = cx - 3 + i
y = 27
pieces.append(Piece(pname, 'white', x, y))
for i in range(8):
x = cx - 3 + i
y = 26
pieces.append(Piece('pawn', 'white', x, y))
return pieces
pieces = initialize_pieces()
selected_piece = None
possible_moves = []
game_over = False
winner = None
# --- Туман войны ---
cell_counters = {} # {(x,y): turns_since_last_revealed}
# Каждая новая клетка имеет 10% шанс стать бонусной.
BONUS_GENERATION_CHANCE = 0.10
# --- Система ходов и многоперемещений ---
current_turn = 'white' # Начинаем с белых
turn_count = 0
moves_remaining = 3 # За один ход игрок может сделать 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 # seconds
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()
# При переключении хода сбрасываем число оставшихся ходов и флаг has_moved у фигур текущего игрока.
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
moved_piece.has_moved = True
moves_remaining -= 1
if moves_remaining <= 0:
switch_turn()
# --- Обновление тумана войны и генерация бонусов/монет ---
def update_fog():
global global_revealed, bonus_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]
for pos in newly_revealed:
if pos not in bonus_cells and pos not in coin_cells and random.random() < BONUS_GENERATION_CHANCE:
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)]
global_revealed.update(merchant_positions)
# --- Монеты на поле ---
coin_cells = set()
# --- Счет монет для игроков ---
player_coins = {'white': 0, 'black': 0}
# --- Механика магазина ---
# Повышенные цены: 6 монет за покупку
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}
}
# --- Реализация спрайтов фигур ---
# Ожидается, что в папке "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):
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_fog():
for y in range(GRID_HEIGHT):
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)
pygame.draw.rect(screen, COLOR_FOG, rect)
else:
if pos in merchant_positions:
continue
if cell_counters.get(pos, 0) == 4:
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():
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 с полупрозрачным фоном для лучшей читаемости:
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)
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':
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)]
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
def handle_shop_purchase(key):
if player_coins[current_turn] < shop_items[key]['cost']:
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)
elif p.name == 'pawn':
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':
p.max_hp += 3
p.hp = min(p.hp + 3, p.max_hp)
elif key == 'damage_enemy':
enemy_color = 'black' if current_turn == 'white' else 'white'
for p in pieces:
if p.color == enemy_color:
if p.hp > 3:
p.hp -= 3
elif p.hp > 1:
p.hp = 1
else:
p.hp = 1
elif key == 'curse':
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
# --- Инициализируем туман войны в начале игры ---
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:
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:
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()
update_fog()
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
switch_turn()
break
if survived:
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
switch_turn()
break
if survived:
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']:
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()
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()