ver 1.1.2
@ -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': 'К',
|
||||
@ -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),
|
||||
directions = [(-1,-1), (0,-1), (1,-1),
|
||||
(-1, 0), (1, 0),
|
||||
(-1, 1), (0, 1), (1, 1)
|
||||
]
|
||||
(-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:
|
||||
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}
|
||||
# Генерация монет
|
||||
if pos not in coin_cells and random.random() < COIN_GENERATION_CHANCE:
|
||||
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)
|
||||
|
||||
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)
|
||||
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))
|
||||
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 с полупрозрачным фоном для лучшей читаемости:
|
||||
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.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()
|
||||
|
BIN
sprites/bB.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
sprites/bK.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
sprites/bN.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
sprites/bP.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
sprites/bQ.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
sprites/bR.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
sprites/wB.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
sprites/wK.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
sprites/wN.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
sprites/wP.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
sprites/wQ.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
sprites/wR.png
Normal file
After Width: | Height: | Size: 2.8 KiB |