OpenWorldChess/chess_open_world.py
2025-01-31 22:03:51 +03:00

820 lines
34 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
# Инициализация 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']:
# Ограничиваем движение до 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)
]
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}
BONUS_GENERATION_CHANCE = 0.10 # 10% шанс появления бонуса при повторном открытии
COIN_GENERATION_CHANCE = 0.001 # 0.1% шанс появления монеты
# --- Система ходов ---
current_turn = 'white' # Начинаем с белых
turn_count = 0
# --- Механика повышения вероятности красных полей ---
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()
def switch_turn():
global current_turn, turn_count, timer_expired
current_turn = 'black' if current_turn == 'white' else 'white'
turn_count += 1
reset_timer()
timer_expired = False
update_bonus_probabilities()
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):
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)
# Удаляем бонус, если клетка закрывается и бонус не является '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)
# --- Добавление торговцев ---
# Определяем позиции торговцев: центральная линия по краям
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}
# --- Механика магазина ---
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}
}
# --- Функции отрисовки ---
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():
# Для '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
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)
# Рисуем стрелку вверх
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)
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))
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))
if p.selected:
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}"
if timer_expired and player_timeouts[current_turn] >=1:
color = COLOR_TIMER_WARNING
else:
color = 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))
# Если открыт магазин, отображаем его
if shop_open:
draw_shop()
def draw_shop():
shop_x = CELL_SIZE * GRID_WIDTH + 10
shop_y = 200
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) # Серый
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):
"""Применяем эффект бонуса к фигуре 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)]
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) # До 5
elif p.name == 'pawn':
setattr(p, 'move_range', getattr(p, 'move_range', 1) + 1) # До 2
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 # Уже 1, остаётся 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
# --- Инициализируем туман войны в начале игры ---
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 = dx // abs(dx)
if dy != 0:
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
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
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)
if dy != 0:
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
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
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:
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()