Ver 1.1.1

This commit is contained in:
dima 2025-01-31 22:03:51 +03:00
parent e0ba49f02e
commit c655d5b815
2 changed files with 337 additions and 234 deletions

View File

@ -1,6 +1,6 @@
# Шахматы с Открытым Миром 1.1 # Шахматы с Открытым Миром 1.1.1
Добро пожаловать в **Шахматы с Открытым Миром 1.1** инновационную версию классической игры, обогащённую новыми механиками, бонусами и динамическим туманом войны. Погрузитесь в захватывающий мир стратегии, где каждое решение может изменить ход партии! Добро пожаловать в **Шахматы с Открытым Миром 1.1.1** инновационную версию классической игры, обогащённую новыми механиками, бонусами, магазином и динамическим туманом войны. Погрузитесь в захватывающий мир стратегии, где каждое решение может изменить ход партии!
## Содержание ## Содержание
@ -9,6 +9,7 @@
- [Туман Войны](#туман-войны) - [Туман Войны](#туман-войны)
- [Бонусные Поля](#бонусные-поля) - [Бонусные Поля](#бонусные-поля)
- [Бои и HP Фигур](#бои-и-hp-фигур) - [Бои и HP Фигур](#бои-и-hp-фигур)
- [Торговцы и Магазин](#торговцы-и-магазин)
3. [Механика Времени](#механика-времени) 3. [Механика Времени](#механика-времени)
4. [Процедурная Генерация Бонусов](#процедурная-генерация-бонусов) 4. [Процедурная Генерация Бонусов](#процедурная-генерация-бонусов)
5. [Баланс Фигур](#баланс-фигур) 5. [Баланс Фигур](#баланс-фигур)
@ -44,7 +45,7 @@
- **Типы Бонусов**: - **Типы Бонусов**:
- **Регенерация (regen)**: Восстанавливает 1-2 HP фигуре (кроме короля). - **Регенерация (regen)**: Восстанавливает 1-2 HP фигуре (кроме короля).
- **Повышение HP (hp_upgrade)**: Увеличивает максимальный HP фигуры на 1 (кроме короля). - **Повышение HP (hp_upgrade)**: Увеличивает максимальный HP фигуры на 1 (кроме короля).
- **Урон (damage)**: Наносит 1-5 урона фигуре. - **Урон (damage)**: Вычитает 3 HP из фигуры противника без её уничтожения.
- **Повышение HP Короля (king_hp_upgrade)**: Увеличивает или восстанавливает HP короля. - **Повышение HP Короля (king_hp_upgrade)**: Увеличивает или восстанавливает HP короля.
- **Добавление Фигуры (add_piece)**: Добавляет новую фигуру рядом с текущей, если возможно. - **Добавление Фигуры (add_piece)**: Добавляет новую фигуру рядом с текущей, если возможно.
@ -70,12 +71,24 @@
- **Взаимодействие Фигур**: - **Взаимодействие Фигур**:
- **Атака**: Когда фигура перемещается на клетку с вражеской фигурой, начинается бой. - **Атака**: Когда фигура перемещается на клетку с вражеской фигурой, начинается бой.
- **Исход Боя**: - **Исход Боя**:
- Если HP атакующего меньше HP защитника: Защитник теряет HP атакующего, атакующий погибает. - Если HP атакующего меньше HP защитника: Защитник теряет HP атакующего, атакующий теряет 3 HP (не умирает).
- Если HP атакующего больше HP защитника: Атакующий теряет HP защитника, защитник погибает. - Если HP атакующего больше HP защитника: Атакующий теряет HP защитника, защитник теряет 3 HP (не умирает).
- Если HP равны: Атакующий остаётся с 1 HP, защитник погибает. - Если HP равны: Атакующий остаётся с 1 HP, защитник теряет 3 HP (не умирает).
- **Убийство Короля**: Захват короля противника завершает игру. - **Убийство Короля**: Захват короля противника завершает игру.
### Торговцы и Магазин
На поле расположены **торговцы**, представляющие специальные клетки, с которыми можно взаимодействовать:
- **Позиции Торговцев**: Центральная линия по краям поля.
- **Взаимодействие с Торговцем**: При перемещении на клетку торговца открывается **магазин**.
- **Магазин** предлагает следующие улучшения:
- **Расширить ход**: Увеличивает длину хода для специальных фигур до 5 клеток и для пешек до 2 клеток. Стоимость: 3 монеты.
- **Увеличить HP**: Увеличивает `max_hp` всех фигур на 3, кроме короля. Стоимость: 3 монеты.
- **Уменьшить HP врага**: Вычитает 3 HP у всех фигур противника без их уничтожения. Если HP противника меньше 3, устанавливает HP на 1. Стоимость: 3 монеты.
- **Проклятие**: Уменьшает вероятность появления бонусов у противника `regen` и `hp_upgrade` на 10% каждая. Стоимость: 3 монеты.
--- ---
## Механика Времени ## Механика Времени
@ -86,7 +99,9 @@
- **Отсчёт Времени**: - **Отсчёт Времени**:
- Таймер отображается в правом нижнем углу. - Таймер отображается в правом нижнем углу.
- При истечении времени, таймер становится красным. - При истечении времени, таймер становится красным.
- **Повторный Пропуск**: Если игрок снова пропустит свой ход, он автоматически проигрывает. - **Переполнение Времени**:
- **Первый пропуск**: Таймер становится красным.
- **Второй пропуск**: Игрок автоматически проигрывает.
- **Переключение Хода**: После каждого хода время сбрасывается для следующего игрока. - **Переключение Хода**: После каждого хода время сбрасывается для следующего игрока.
--- ---
@ -97,10 +112,10 @@
- **Когда Генерируются Бонусы**: - **Когда Генерируются Бонусы**:
- При выходе клетки из тумана войны, с вероятностью **10%** может появиться бонусное поле. - При выходе клетки из тумана войны, с вероятностью **10%** может появиться бонусное поле.
- **Тип Бонуса**: - **Тип Бонуса**:
- Тип бонуса выбирается на основе текущих вероятностей. - Тип бонуса выбирается на основе текущих вероятностей.
- После **100 ходов**, вероятность появления бонусов типа "урон" (красные поля) постепенно увеличивается до **50%**, делая игру более опасной. - После **100 ходов**, вероятность появления бонусов типа "урон" (**damage**) постепенно увеличивается до **50%**, делая игру более опасной.
--- ---
@ -118,27 +133,24 @@
## Обновления и Изменения ## Обновления и Изменения
### Версия 1.1 ### Версия 1.1.1
1. **Баланс Красных Полей**: 1. **Добавление Магазина и Монет**:
- **Урон**: Теперь красные поля наносят от **1 до 5 урона**, исключая возможность отсутствия урона. - **Магазин**: Теперь доступен при взаимодействии с торговцами на поле. Магазин предлагает различные улучшения за монеты.
- **Исправление Бага**: При прохождении через красное поле и гибели фигуры, ход корректно переходит к противнику. - **Монеты**: Появляются на поле с шансом **0.1%**, становясь более ценными и редкими.
2. **Исправление Механики Товара "Уменьшить HP врага"**:
- Теперь при покупке этого бонуса у всех фигур противника вычитается **3 HP**, но фигуры не умирают.
- Если HP противника меньше 3, устанавливается **1 HP**.
2. **Процедурная Генерация Бонусов**: 3. **Улучшение Баланса и Механик**:
- Бонусные поля теперь генерируются **процедурно** при выходе клеток из тумана войны. - **Шанс генерации монет** снижен до **0.1%**, уменьшая их количество и повышая ценность.
- **Вероятность Появления**: Каждая новая открытая клетка имеет **10%** шанс стать бонусной. - **Удалено отображение вероятностей** генерации бонусов и монет из интерфейса игры, упрощая визуальное восприятие.
- **Тип Бонуса**: Выбор типа бонуса осуществляется согласно текущим вероятностям.
3. **Баланс Фигур с Свободным Передвижением**: 4. **Прочие Улучшения**:
- **Ферзь и Ладья** теперь могут перемещаться до **3 клеток** вместо 5, обеспечивая более сбалансированную игру. - Оптимизация интерфейса магазина для улучшения взаимодействия с пользователем.
- Устранение багов, связанных с переходом ходов после применения бонусов.
4. **Механика Повышения Вероятности Красных Полей**: Спасибо за внимание и приятной игры в **Шахматы с Открытым Миром 1.1.1**!
- После **100 ходов**, вероятность появления бонусных полей типа "урон" постепенно увеличивается до **50%**.
- **Отображение Ходов**: Внизу экрана отображается счётчик ходов. После 100 ходов счётчик становится красным, сигнализируя о начале увеличения опасности.
5. **Механика Времени на Ход**: ---
- Каждый игрок имеет **2 минуты** на свой ход.
- **Отображение Таймера**: Таймер находится в правом нижнем углу экрана.
- **Переполнение Времени**:
- При первом переполнении таймер становится красным.
- При втором переполнении, игрок автоматически проигрывает.

View File

@ -9,7 +9,8 @@ pygame.init()
CELL_SIZE = 32 CELL_SIZE = 32
GRID_WIDTH = 30 GRID_WIDTH = 30
GRID_HEIGHT = 30 GRID_HEIGHT = 30
WINDOW_WIDTH = CELL_SIZE * GRID_WIDTH PANEL_WIDTH = 200 # Ширина правой панели
WINDOW_WIDTH = CELL_SIZE * GRID_WIDTH + PANEL_WIDTH
WINDOW_HEIGHT = CELL_SIZE * GRID_HEIGHT WINDOW_HEIGHT = CELL_SIZE * GRID_HEIGHT
# --- ЦВЕТА --- # --- ЦВЕТА ---
@ -40,8 +41,14 @@ COLOR_PRE_FOG = (169, 169, 169) # Светло-серый
COLOR_TIMER_NORMAL = (255, 255, 255) # Белый COLOR_TIMER_NORMAL = (255, 255, 255) # Белый
COLOR_TIMER_WARNING = (255, 0, 0) # Красный 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)) screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Шахматы с открытым миром 1.0") pygame.display.set_caption("Шахматы с Открытым Миром 1.1.1")
# Шрифты # Шрифты
font = pygame.font.SysFont(None, CELL_SIZE // 2) font = pygame.font.SysFont(None, CELL_SIZE // 2)
@ -49,6 +56,7 @@ victory_font = pygame.font.SysFont(None, 60)
turn_font = pygame.font.SysFont(None, 30) turn_font = pygame.font.SysFont(None, 30)
counter_font = pygame.font.SysFont(None, 30) counter_font = pygame.font.SysFont(None, 30)
timer_font = pygame.font.SysFont(None, 25) timer_font = pygame.font.SysFont(None, 25)
shop_font = pygame.font.SysFont(None, 24)
# --- Глобальное множество открытых клеток (туман войны) --- # --- Глобальное множество открытых клеток (туман войны) ---
global_revealed = set() global_revealed = set()
@ -59,15 +67,13 @@ piece_symbols = {
'knight': 'К', 'knight': 'К',
'bishop': 'С', 'bishop': 'С',
'queen': 'Ф', 'queen': 'Ф',
'king': 'К', # Король (можно заменить на 'K' или другой символ) 'king': 'К', # Король
'pawn': 'П' 'pawn': 'П'
} }
# === МЕХАНИКА БОНУСОВ === # === МЕХАНИКА БОНУСОВ ===
# Храним в глобальном словаре bonus_cells: {(x,y): {'type': ...}}
bonus_cells = {} bonus_cells = {}
# Вероятности появления различных типов бонусов
base_bonus_probabilities = { base_bonus_probabilities = {
'regen': 0.42, # 42% 'regen': 0.42, # 42%
'hp_upgrade': 0.32, # 32% 'hp_upgrade': 0.32, # 32%
@ -78,7 +84,6 @@ base_bonus_probabilities = {
bonus_probabilities = base_bonus_probabilities.copy() bonus_probabilities = base_bonus_probabilities.copy()
# Функция для выбора бонуса на основе вероятностей
def choose_bonus_type(): def choose_bonus_type():
rand = random.random() rand = random.random()
cumulative = 0 cumulative = 0
@ -90,35 +95,20 @@ def choose_bonus_type():
# === МЕХАНИКА БОЯ === # === МЕХАНИКА БОЯ ===
def resolve_combat(attacker, defender): def resolve_combat(attacker, defender):
"""
Атакующая фигура (A) идёт на клетку защитника (B).
HP(A)=a, HP(B)=b.
- Если a < b: B теряет a (b := b-a), A погибает.
- Если a > b: A теряет b (a := a-b), B погибает.
- Если a == b: A побеждает, убивает B, а у A остаётся 1 HP.
Возвращаем кортеж: (attacker_alive, defender_alive)
при этом изменяем HP в самих объектах.
"""
a = attacker.hp a = attacker.hp
b = defender.hp b = defender.hp
if a < b: if a < b:
# Защитник теряет a HP
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:
# Атакующая теряет b HP
attacker.hp = a - b attacker.hp = a - b
# Защитник погибает
defender.hp = 0 defender.hp = 0
return (True, False) return (True, False)
else: else:
# a == b
# Атакующая побеждает, остаётся с 1 HP
attacker.hp = 1 attacker.hp = 1
defender.hp = 0 defender.hp = 0
return (True, False) return (True, False)
@ -156,12 +146,12 @@ class Piece:
ny = self.y + dy ny = 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 in ['rook', 'bishop', 'queen']: elif self.name in ['rook', 'bishop', 'queen']:
# Ограничиваем движение до 3 клеток # Ограничиваем движение до 3 клеток или до 5 для улучшенного хода
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':
@ -172,7 +162,7 @@ class Piece:
(-1,-1),(1,-1),(1,1),(-1,1) (-1,-1),(1,-1),(1,1),(-1,1)
] ]
for dx, dy in directions: for dx, dy in directions:
for step in range(1, 4): # До 3 клеток for step in range(1, max_steps + 1):
nx = self.x + dx * step nx = self.x + dx * step
ny = self.y + dy * step ny = self.y + dy * step
if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT:
@ -242,9 +232,6 @@ def initialize_pieces():
# Инициализация фигур # Инициализация фигур
pieces = initialize_pieces() pieces = initialize_pieces()
# Remove initial bonus cell generation
# initialize_bonus_cells(pieces, num_each=10)
selected_piece = None selected_piece = None
possible_moves = [] possible_moves = []
game_over = False game_over = False
@ -252,7 +239,8 @@ winner = None
# --- Туман войны --- # --- Туман войны ---
cell_counters = {} # {(x,y): turns_since_last_revealed} cell_counters = {} # {(x,y): turns_since_last_revealed}
BONUS_GENERATION_CHANCE = 0.1 # 10% шанс появления бонуса при повторном открытии BONUS_GENERATION_CHANCE = 0.10 # 10% шанс появления бонуса при повторном открытии
COIN_GENERATION_CHANCE = 0.001 # 0.1% шанс появления монеты
# --- Система ходов --- # --- Система ходов ---
current_turn = 'white' # Начинаем с белых current_turn = 'white' # Начинаем с белых
@ -264,13 +252,10 @@ def update_bonus_probabilities():
if turn_count <= 100: if turn_count <= 100:
bonus_probabilities = base_bonus_probabilities.copy() bonus_probabilities = base_bonus_probabilities.copy()
else: else:
# Calculate how much to increase 'damage' probability
extra_turns = turn_count - 100 extra_turns = turn_count - 100
# Let's say over the next 200 turns, it increases to 50%
max_extra = 200 max_extra = 200
increase_per_turn = (0.50 - base_bonus_probabilities['damage']) / max_extra 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) new_damage_prob = min(base_bonus_probabilities['damage'] + increase_per_turn * extra_turns, 0.50)
# Adjust other probabilities accordingly
remaining_prob = 1 - new_damage_prob remaining_prob = 1 - new_damage_prob
total_other_probs = sum(base_bonus_probabilities[bt] for bt in base_bonus_probabilities if bt != 'damage') total_other_probs = sum(base_bonus_probabilities[bt] for bt in base_bonus_probabilities if bt != 'damage')
bonus_probabilities = {} bonus_probabilities = {}
@ -302,11 +287,9 @@ def check_timer():
timer_expired = True timer_expired = True
player_timeouts[current_turn] += 1 player_timeouts[current_turn] += 1
if player_timeouts[current_turn] >= 2: if player_timeouts[current_turn] >= 2:
# Opponent wins
winner = 'black' if current_turn == 'white' else 'white' winner = 'black' if current_turn == 'white' else 'white'
game_over = True game_over = True
else: else:
# Switch turn
switch_turn() switch_turn()
def switch_turn(): def switch_turn():
@ -327,10 +310,8 @@ def update_fog():
""" """
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):
@ -340,25 +321,24 @@ def update_fog():
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:
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)
if pos in new_revealed: if pos in new_revealed:
# Если клетка видна, сбрасываем счетчик
cell_counters[pos] = 0 cell_counters[pos] = 0
global_revealed.add(pos) global_revealed.add(pos)
else: else:
# Если клетка не видна, увеличиваем счетчик
if pos in cell_counters: if pos in cell_counters:
cell_counters[pos] += 1 cell_counters[pos] += 1
else: else:
cell_counters[pos] = 1 cell_counters[pos] = 1
# Если счетчик достигает 5, закрываем клетку
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)
@ -366,19 +346,49 @@ def update_fog():
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 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() bonus_type = choose_bonus_type()
# Проверяем, чтобы бонус не был под фигурой
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):
bonus_cells[pos] = {'type': bonus_type} 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(): def draw_grid():
for x in range(0, WINDOW_WIDTH, CELL_SIZE): for x in range(0, CELL_SIZE * GRID_WIDTH, CELL_SIZE):
pygame.draw.line(screen, COLOR_GRID, (x, 0), (x, WINDOW_HEIGHT)) pygame.draw.line(screen, COLOR_GRID, (x, 0), (x, CELL_SIZE * GRID_HEIGHT))
for y in range(0, WINDOW_HEIGHT, CELL_SIZE): for y in range(0, CELL_SIZE * GRID_HEIGHT, CELL_SIZE):
pygame.draw.line(screen, COLOR_GRID, (0, y), (WINDOW_WIDTH, y)) pygame.draw.line(screen, COLOR_GRID, (0, y), (CELL_SIZE * GRID_WIDTH, y))
def draw_fog(): def draw_fog():
for y in range(GRID_HEIGHT): for y in range(GRID_HEIGHT):
@ -388,22 +398,13 @@ def draw_fog():
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:
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():
"""
Отрисовка бонусов (если клетка открыта или бонус типа 'damage').
Цвета:
- 'regen': (0,255,0) зелёный
- 'hp_upgrade': (0,128,255) синий
- 'damage': (255,0,0) красный
- 'king_hp_upgrade': (255,215,0) золотой
- 'add_piece': (255,255,255) белый с плюсом или розовый
"""
for (bx, by), info in bonus_cells.items(): for (bx, by), info in bonus_cells.items():
# Для 'damage' бонусов отображаем всегда, иначе только если клетка видна # Для 'damage' бонусов отображаем всегда, иначе только если клетка видна
if info['type'] != 'damage' and (bx, by) not in global_revealed: if info['type'] != 'damage' and (bx, by) not in global_revealed:
@ -424,7 +425,6 @@ def draw_bonus_cells():
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
@ -439,8 +439,40 @@ def draw_bonus_cells():
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():
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): def draw_pieces(pieces):
"""Рисуем фигуры, их HP и подсвечиваем ходы."""
for p in pieces: for p in pieces:
rect = pygame.Rect(p.x*CELL_SIZE, p.y*CELL_SIZE, CELL_SIZE, CELL_SIZE) rect = pygame.Rect(p.x*CELL_SIZE, p.y*CELL_SIZE, CELL_SIZE, CELL_SIZE)
@ -472,36 +504,69 @@ def draw_pieces(pieces):
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 display_victory(winner): def draw_statistics_panel():
text = victory_font.render(f"{winner.capitalize()} победил!", True, (255, 255, 255)) panel_rect = pygame.Rect(CELL_SIZE * GRID_WIDTH, 0, PANEL_WIDTH, WINDOW_HEIGHT)
text_rect = text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2)) pygame.draw.rect(screen, (50, 50, 50), panel_rect)
screen.blit(text, text_rect)
def display_turn(current_turn): # Заголовок
text = turn_font.render(f"Текущий ход: {current_turn.capitalize()}", True, COLOR_TURN_TEXT) title_text = shop_font.render("Статистика", True, (255, 255, 255))
screen.blit(text, (10, WINDOW_HEIGHT - 30)) screen.blit(title_text, (CELL_SIZE * GRID_WIDTH + 10, 10))
def display_turn_counter(): # Текущий ход
global turn_count turn_text = turn_font.render(f"Ход: {current_turn.capitalize()}", True, COLOR_TURN_TEXT)
if turn_count < 100: screen.blit(turn_text, (CELL_SIZE * GRID_WIDTH + 10, 50))
color = (255, 255, 255)
else:
color = (255, 0, 0)
text = counter_font.render(f"Ход: {turn_count}", True, color)
text_rect = text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT - 15))
screen.blit(text, text_rect)
def display_timer(): # Количество ходов
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() time_left = get_time_left()
minutes = time_left // 60 minutes = time_left // 60
seconds = time_left % 60 seconds = time_left % 60
timer_text = f"{minutes:02}:{seconds:02}" timer_text_str = f"Время: {minutes:02}:{seconds:02}"
if timer_expired and player_timeouts[current_turn] >=1: if timer_expired and player_timeouts[current_turn] >=1:
color = COLOR_TIMER_WARNING color = COLOR_TIMER_WARNING
else: else:
color = COLOR_TIMER_NORMAL color = COLOR_TIMER_NORMAL
text = timer_font.render(timer_text, True, color) timer_text = timer_font.render(timer_text_str, True, color)
text_rect = text.get_rect(bottomright=(WINDOW_WIDTH - 10, WINDOW_HEIGHT - 10)) 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) screen.blit(text, text_rect)
def apply_bonus(piece, bonus_type, pieces): def apply_bonus(piece, bonus_type, pieces):
@ -525,48 +590,70 @@ def apply_bonus(piece, bonus_type, pieces):
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
# то увеличиваем 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
# просто восстанавливаем 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':
# Нанесение урона (1..5) # Урон при прохождении через клетку 'damage' уже обрабатывается отдельно
damage = random.randint(1, 5) pass
piece.hp = max(piece.hp - damage, 0)
if piece.hp == 0:
# Удаляем фигуру, если HP равен 0
pieces.remove(piece)
if piece.name == 'king':
global winner, game_over
winner = 'black' if piece.color == 'white' else 'white'
game_over = True
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
ny = piece.y + dy ny = piece.y + dy
if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT: 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:
if not any(p.x == nx and p.y == ny for p in pieces) and (nx, ny) not in bonus_cells:
# Создаём новую фигуру того же типа
new_piece = Piece(piece.name, piece.color, nx, ny) new_piece = Piece(piece.name, piece.color, nx, ny)
pieces.append(new_piece) pieces.append(new_piece)
break 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() update_fog()
# --- Основной цикл игры ---
running = True running = True
clock = pygame.time.Clock() clock = pygame.time.Clock()
@ -579,149 +666,153 @@ while running:
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()
gx = mx // CELL_SIZE if mx < CELL_SIZE * GRID_WIDTH and my < CELL_SIZE * GRID_HEIGHT:
gy = my // CELL_SIZE 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) 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:
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()
# Проверяем, есть ли там вражеская фигура
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: else:
# Атакующая жива, перемещаем её # Пустая клетка — просто ходим
# Проверка прохождения через клетки (для длинных ходов)
path = [] path = []
if attacker.name in ['rook', 'bishop', 'queen']: if selected_piece.name in ['rook', 'bishop', 'queen']:
dx = gx - attacker.x dx = gx - selected_piece.x
dy = gy - attacker.y dy = gy - selected_piece.y
if dx != 0: if dx != 0:
dx = dx // abs(dx) dx = dx // abs(dx)
if dy != 0: if dy != 0:
dy = dy // abs(dy) 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 - selected_piece.x), abs(gy - selected_piece.y))):
path_x = attacker.x + dx * step path_x = selected_piece.x + dx * step
path_y = attacker.y + dy * step path_y = selected_piece.y + dy * step
path.append((path_x, path_y)) path.append((path_x, path_y))
# Применяем урон при прохождении через клетки и удаляем 'damage' клетки
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) # Исправлено на 1..5 damage = random.randint(1,5)
attacker.hp = max(attacker.hp - damage, 0) selected_piece.hp = max(selected_piece.hp - damage, 0)
# Удаляем 'damage' клетку как использованную
del bonus_cells[pos] del bonus_cells[pos]
if attacker.hp == 0: if selected_piece.hp == 0:
pieces.remove(attacker) pieces.remove(selected_piece)
selected_piece = None selected_piece = None
possible_moves.clear() possible_moves.clear()
survived = False survived = False
# Смена хода, так как игрок погиб
switch_turn() switch_turn()
break break
if survived: if survived:
attacker.x = gx selected_piece.x = gx
attacker.y = gy selected_piece.y = gy
attacker.selected = False selected_piece.selected = False
selected_piece = None selected_piece = None
possible_moves.clear() possible_moves.clear()
# Смена хода
switch_turn() switch_turn()
update_fog()
else: else:
# Пустая клетка — просто ходим selected_piece.selected = False
# Проверка прохождения через клетки (для длинных ходов) selected_piece = None
path = [] possible_moves.clear()
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))
# Применяем урон при прохождении через клетки и удаляем 'damage' клетки
survived = True
for pos in path:
if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage':
damage = random.randint(1,5) # Исправлено на 1..5
selected_piece.hp = max(selected_piece.hp - damage, 0)
# Удаляем 'damage' клетку как использованную
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: else:
# Клик вне возможных ходов # Выбираем фигуру только если клетка видима и соответствует текущему ходу
selected_piece.selected = False if clicked_piece and (gx, gy) in global_revealed and clicked_piece.color == current_turn:
selected_piece = None selected_piece = clicked_piece
possible_moves.clear() selected_piece.selected = True
possible_moves = selected_piece.get_possible_moves(pieces)
else: else:
# Выбираем фигуру только если клетка видима и соответствует текущему ходу # Клик в правой панели
if clicked_piece and (gx, gy) in global_revealed and clicked_piece.color == current_turn: if shop_open:
selected_piece = clicked_piece for key, item in shop_items.items():
selected_piece.selected = True if 'button_rect' in item and item['button_rect'].collidepoint(mx, my):
possible_moves = selected_piece.get_possible_moves(pieces) 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) screen.fill(COLOR_BG)
draw_grid() draw_grid()
draw_fog() draw_fog()
draw_merchants()
draw_bonus_cells() draw_bonus_cells()
draw_coin_cells()
draw_pieces(pieces) draw_pieces(pieces)
draw_statistics_panel()
if game_over and winner: if game_over and winner:
display_victory(winner) draw_victory(winner)
else:
display_turn(current_turn)
display_turn_counter()
display_timer()
pygame.display.flip() pygame.display.flip()
pygame.quit() pygame.quit()