ver 1.1.2

This commit is contained in:
dima 2025-02-01 13:45:33 +03:00
parent c655d5b815
commit 9be9cfc942
13 changed files with 155 additions and 208 deletions

View File

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

BIN
sprites/bB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
sprites/bK.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
sprites/bN.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
sprites/bP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
sprites/bQ.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
sprites/bR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
sprites/wB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
sprites/wK.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
sprites/wN.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
sprites/wP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
sprites/wQ.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
sprites/wR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB