ver 1.2
This commit is contained in:
parent
9be9cfc942
commit
9ee372b092
244
README.md
244
README.md
@ -1,156 +1,138 @@
|
|||||||
# Шахматы с Открытым Миром 1.1.1
|
# Шахматы с открытым миром 1.2
|
||||||
|
|
||||||
Добро пожаловать в **Шахматы с Открытым Миром 1.1.1** – инновационную версию классической игры, обогащённую новыми механиками, бонусами, магазином и динамическим туманом войны. Погрузитесь в захватывающий мир стратегии, где каждое решение может изменить ход партии!
|
**Шахматы с открытым миром 1.2** – это инновационная версия классических шахмат, где традиционные фигуры перемещаются по динамичному игровому полю, окутанному туманом войны, а на клетках появляются разнообразные бонусы и монеты. Игра объединяет глубокую стратегию классических шахмат с элементами исследования, случайности и управления ресурсами.
|
||||||
|
|
||||||
## Содержание
|
## Оглавление
|
||||||
|
|
||||||
1. [Основные Правила](#основные-правила)
|
- [Обзор игры](#обзор-игры)
|
||||||
2. [Механики Игры](#механики-игры)
|
- [Концепция игры](#концепция-игры)
|
||||||
- [Туман Войны](#туман-войны)
|
- [Основные механики](#основные-механики)
|
||||||
- [Бонусные Поля](#бонусные-поля)
|
- [Структура хода и перемещения](#структура-хода-и-перемещения)
|
||||||
- [Бои и HP Фигур](#бои-и-hp-фигур)
|
- [Туман войны и механизм раскрытия клеток](#туман-войны-и-механизм-раскрытия-клеток)
|
||||||
- [Торговцы и Магазин](#торговцы-и-магазин)
|
- [Генерация бонусов и монет](#генерация-бонусов-и-монет)
|
||||||
3. [Механика Времени](#механика-времени)
|
- [Механика боя](#механика-боя)
|
||||||
4. [Процедурная Генерация Бонусов](#процедурная-генерация-бонусов)
|
- [Магазин](#магазин)
|
||||||
5. [Баланс Фигур](#баланс-фигур)
|
- [Стратегические особенности](#стратегические-особенности)
|
||||||
6. [Обновления и Изменения](#обновления-и-изменения)
|
- [Дополнительные замечания](#дополнительные-замечания)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Основные Правила
|
## Обзор игры
|
||||||
|
|
||||||
**Шахматы с Открытым Миром** сохраняют основные принципы классической шахматной игры:
|
**Шахматы с открытым миром 1.2** представляет собой гибрид классических шахмат и открытого мира, где игровой процесс динамичен и наполнен неожиданностями. Ваша задача – не только выиграть шахматную партию, но и эффективно использовать бонусы, собирать монеты и умело управлять ресурсами, чтобы одержать победу.
|
||||||
|
|
||||||
- **Фигуры и Расстановка**: Игра включает короля, ферзя, ладьи, слонов, коней и пешек для каждой стороны.
|
## Концепция игры
|
||||||
- **Цель Игры**: Захватить короля противника.
|
|
||||||
|
|
||||||
Однако, в этой версии добавлены уникальные механики, которые делают игру более динамичной и стратегически глубокой.
|
В этой игре шахматное поле превращается в открытый мир, где:
|
||||||
|
- **Туман войны** скрывает большую часть поля, и его нужно «рассекречивать» движением фигур.
|
||||||
|
- **Клетки** постоянно меняются: при их раскрытии появляются бонусы и монеты, а спустя время (если клетка не посещается) всё вновь покрывается туманом, и объекты исчезают.
|
||||||
|
- **Несколько ходов за раз:** игроку предоставляется возможность совершить до 3 перемещений за ход, что добавляет динамичности и требует продуманной тактики.
|
||||||
|
|
||||||
|
## Основные механики
|
||||||
|
|
||||||
|
### Структура хода и перемещения
|
||||||
|
|
||||||
|
- **Несколько перемещений за ход:**
|
||||||
|
Каждый игрок в свой ход может совершить до 3 перемещений. Выбирайте фигуру, перемещайтесь по полю и используйте бонусы, чтобы усилить свою позицию.
|
||||||
|
|
||||||
|
- **Виды перемещений:**
|
||||||
|
- Фигуры с коротким радиусом (например, пешка и король) перемещаются на одну клетку.
|
||||||
|
- Фигуры дальнего боя (ладья, слон, ферзь) могут двигаться на несколько клеток за один ход. При их перемещении проверяется каждое промежуточное звено пути.
|
||||||
|
|
||||||
|
- **Потраченное перемещение:**
|
||||||
|
Даже если ваша фигура погибает во время перемещения (например, попадая на клетку с бонусом «damage»), это перемещение засчитывается. Если у вас осталось еще перемещений, вы сможете совершить следующие ходы.
|
||||||
|
|
||||||
|
### Туман войны и механизм раскрытия клеток
|
||||||
|
|
||||||
|
- **Начальное состояние:**
|
||||||
|
Изначально почти все поле скрыто туманом.
|
||||||
|
|
||||||
|
- **Раскрытие клеток:**
|
||||||
|
Фигуры при перемещении «освещают» окружающие клетки (обычно 5×5 клеток вокруг них), делая их видимыми. На этих клетках становятся доступны бонусы и монеты.
|
||||||
|
|
||||||
|
- **Старение клетки:**
|
||||||
|
Если клетка остаётся неизменной (то есть долго не посещается ни одной фигурой), её «счётчик старения» растёт. При достижении определённого порога (например, 5 ходов) клетка вновь покрывается туманом, и все бонусы или монеты, находящиеся на ней, исчезают. Это позволяет в дальнейшем генерировать новые бонусы на этих клетках.
|
||||||
|
|
||||||
|
### Генерация бонусов и монет
|
||||||
|
|
||||||
|
- **Шанс генерации:**
|
||||||
|
Каждая новая (раскрытая) клетка имеет заданный шанс (по умолчанию 10%) сгенерировать объект.
|
||||||
|
|
||||||
|
- **Соотношение бонус/монета:**
|
||||||
|
Если для клетки срабатывает шанс генерации, то:
|
||||||
|
- С вероятностью 95% в клетке появляется бонус.
|
||||||
|
- С вероятностью 5% появляется монета.
|
||||||
|
|
||||||
|
- **Типы бонусов:**
|
||||||
|
Возможные бонусы включают:
|
||||||
|
- **Regen (восстановление):** Восстанавливает часть HP фигуры (кроме короля).
|
||||||
|
- **HP Upgrade (повышение здоровья):** Увеличивает максимальное значение HP фигуры (кроме короля).
|
||||||
|
- **Damage (урон):** Наносит случайный урон (от 1 до 5 единиц) фигуре, попавшей на клетку.
|
||||||
|
При этом вероятность появления бонуса «damage» динамически увеличивается после 100 ходов (до 50%).
|
||||||
|
- **King HP Upgrade:** Увеличивает максимальное здоровье короля или восстанавливает его.
|
||||||
|
- **Add Piece:** Если рядом есть свободное место, создает дополнительную фигуру такого же типа.
|
||||||
|
|
||||||
|
- **Монеты:**
|
||||||
|
Монеты генерируются аналогичным образом, но теперь, как бонусы, монеты исчезают, если клетка «стареет» (т.е. покрывается туманом). Собранные монеты можно потратить в магазине.
|
||||||
|
|
||||||
|
### Механика боя
|
||||||
|
|
||||||
|
- **Бой между фигурами:**
|
||||||
|
Когда ваша фигура перемещается на клетку, занятую вражеской фигурой, происходит бой. Результат боя определяется сравнением HP фигур:
|
||||||
|
- Если HP атакующей фигуры меньше HP защитника, атакующая фигура погибает, а защитник остаётся с уменьшенным количеством HP.
|
||||||
|
- Если HP атакующей фигуры больше, защитник погибает, а атакующая фигура остаётся с уменьшенным количеством HP.
|
||||||
|
- При равных значениях HP защитник погибает, а атакующая фигура остаётся с 1 единицей HP.
|
||||||
|
|
||||||
|
- **Опасности на пути:**
|
||||||
|
Для фигур дальнего боя (ладья, слон, ферзь) каждый промежуточный шаг пути проверяется на наличие бонуса «damage». Если фигура проходит через клетку с таким бонусом, она получает случайный урон.
|
||||||
|
Если фигура погибает в процессе перемещения, этот ход засчитывается (то есть расходуется одно перемещение), но оставшиеся перемещения остаются в распоряжении игрока.
|
||||||
|
|
||||||
|
### Магазин
|
||||||
|
|
||||||
|
- **Взаимодействие:**
|
||||||
|
При попадании на клетку с торговцем открывается магазин.
|
||||||
|
|
||||||
|
- **Что можно купить:**
|
||||||
|
В магазине можно приобрести улучшения за собранные монеты, например:
|
||||||
|
- **Расширение хода:** Увеличивает дальность перемещения некоторых фигур.
|
||||||
|
- **Увеличение HP:** Повышает здоровье фигур.
|
||||||
|
- **Уменьшение HP врага:** Наносит урон вражеским фигурам.
|
||||||
|
- **Проклятие:** Применяет негативный эффект на противника.
|
||||||
|
|
||||||
|
- **Стоимость:**
|
||||||
|
Все предметы в магазине имеют фиксированную стоимость (по умолчанию 6 монет).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Механики Игры
|
## Стратегические особенности
|
||||||
|
|
||||||
### Туман Войны
|
- **Контроль над туманом:**
|
||||||
|
Активное перемещение фигур позволяет раскрывать больше клеток, что помогает обнаружить бонусы, монеты и вражеские фигуры. Однако не забывайте, что клетки, оставшиеся неизменными, снова покрываются туманом, и объекты на них исчезают.
|
||||||
|
|
||||||
Туман войны добавляет элемент неопределённости на поле боя:
|
- **Риск и награда:**
|
||||||
|
Попадание на клетку с бонусом может дать вам значительное преимущество (например, восстановить здоровье или улучшить характеристики фигуры), но некоторые бонусы (damage) несут риск нанесения урона. Особенно будьте осторожны при длинных перемещениях: если ваша фигура (например, ладья или ферзь) проходит по нескольким клеткам, где могут находиться опасные бонусы, существует риск накопительного урона.
|
||||||
|
|
||||||
- **Открытые и Закрытые Клетки**: Изначально большая часть поля скрыта. Клетки открываются, когда фигуры приближаются.
|
- **Управление ресурсами:**
|
||||||
- **Обновление Тумана**: Каждая клетка, не находящаяся в радиусе 2 клеток от любой фигуры, через 5 ходов становится закрытой.
|
Монеты, собранные на поле, являются важным ресурсом для покупок в магазине. Старайтесь балансировать между агрессивной атакой, сбором монет и сохранением здоровья фигур.
|
||||||
- **Предварительное Закрытие**: Клетки, которые скоро закроются, отмечаются светло-серым цветом.
|
|
||||||
|
|
||||||
### Бонусные Поля
|
- **Тактическое планирование:**
|
||||||
|
Игра предоставляет несколько перемещений за ход, что позволяет комбинировать атаки, сбор бонусов и тактическое перемещение. Обдумывайте каждый ход, учитывая не только шахматные правила, но и динамичные элементы игрового поля.
|
||||||
Поле содержит специальные клетки, предоставляющие бонусы:
|
|
||||||
|
|
||||||
- **Типы Бонусов**:
|
|
||||||
- **Регенерация (regen)**: Восстанавливает 1-2 HP фигуре (кроме короля).
|
|
||||||
- **Повышение HP (hp_upgrade)**: Увеличивает максимальный HP фигуры на 1 (кроме короля).
|
|
||||||
- **Урон (damage)**: Вычитает 3 HP из фигуры противника без её уничтожения.
|
|
||||||
- **Повышение HP Короля (king_hp_upgrade)**: Увеличивает или восстанавливает HP короля.
|
|
||||||
- **Добавление Фигуры (add_piece)**: Добавляет новую фигуру рядом с текущей, если возможно.
|
|
||||||
|
|
||||||
- **Генерация Бонусов**:
|
|
||||||
- Бонусные поля генерируются процедурно при выходе клеток из тумана войны.
|
|
||||||
- Шанс появления бонуса составляет **10%** для каждой новой открытой клетки.
|
|
||||||
- Тип бонуса определяется следующими вероятностями:
|
|
||||||
- Регенерация: 42%
|
|
||||||
- Повышение HP: 32%
|
|
||||||
- Урон: 20%
|
|
||||||
- Повышение HP Короля: 5%
|
|
||||||
- Добавление Фигуры: 1%
|
|
||||||
|
|
||||||
### Бои и HP Фигур
|
|
||||||
|
|
||||||
Каждая фигура имеет **HP (здоровье)**, определяющее её выживаемость:
|
|
||||||
|
|
||||||
- **Инициализация HP**:
|
|
||||||
- **Король**: 5 HP
|
|
||||||
- **Ферзь, Ладья, Слон, Конь**: 3 HP
|
|
||||||
- **Пешка**: 1 HP
|
|
||||||
|
|
||||||
- **Взаимодействие Фигур**:
|
|
||||||
- **Атака**: Когда фигура перемещается на клетку с вражеской фигурой, начинается бой.
|
|
||||||
- **Исход Боя**:
|
|
||||||
- Если HP атакующего меньше HP защитника: Защитник теряет HP атакующего, атакующий теряет 3 HP (не умирает).
|
|
||||||
- Если HP атакующего больше HP защитника: Атакующий теряет HP защитника, защитник теряет 3 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 монеты.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Механика Времени
|
## Дополнительные замечания
|
||||||
|
|
||||||
**Время на ход** добавляет дополнительное давление и стратегию в игру:
|
- **Динамика вероятностей:**
|
||||||
|
Исходное распределение бонусов задается в файле (например, regeneration – 42%, HP upgrade – 32%, damage – 20%, и т.д.). По мере увеличения количества ходов вероятность появления бонуса «damage» постепенно растет (до 50% после определённого количества ходов), что делает игру более рискованной с течением времени.
|
||||||
|
|
||||||
- **Лимит Времени**: Каждый игрок имеет **2 минуты** на свой ход.
|
- **Обновление клеток:**
|
||||||
- **Отсчёт Времени**:
|
Если клетка долго не посещалась, она «стареет» и вновь покрывается туманом, удаляя все бонусы и монеты, которые на ней находились. Это позволяет системе генерировать новые объекты, когда клетка снова становится видимой.
|
||||||
- Таймер отображается в правом нижнем углу.
|
|
||||||
- При истечении времени, таймер становится красным.
|
- **Механика потери хода:**
|
||||||
- **Переполнение Времени**:
|
Даже если ваша фигура погибает в процессе перемещения (например, из-за попадания бонуса damage на пути), это перемещение засчитывается. Остальные перемещения остаются, если таковые имеются, и ход продолжается у вас до истечения лимита.
|
||||||
- **Первый пропуск**: Таймер становится красным.
|
|
||||||
- **Второй пропуск**: Игрок автоматически проигрывает.
|
|
||||||
- **Переключение Хода**: После каждого хода время сбрасывается для следующего игрока.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Процедурная Генерация Бонусов
|
Наслаждайтесь игрой, экспериментируйте с тактикой и стратегией, и помните – каждая партия уникальна благодаря элементам случайности и динамическому игровому миру!
|
||||||
|
|
||||||
Бонусные поля генерируются динамически, делая каждую игру уникальной:
|
Удачи и приятной игры!
|
||||||
|
|
||||||
- **Когда Генерируются Бонусы**:
|
|
||||||
- При выходе клетки из тумана войны, с вероятностью **10%** может появиться бонусное поле.
|
|
||||||
|
|
||||||
- **Тип Бонуса**:
|
|
||||||
- Тип бонуса выбирается на основе текущих вероятностей.
|
|
||||||
- После **100 ходов**, вероятность появления бонусов типа "урон" (**damage**) постепенно увеличивается до **50%**, делая игру более опасной.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Баланс Фигур
|
|
||||||
|
|
||||||
Для поддержания баланса и динамики игры, движение некоторых фигур было изменено:
|
|
||||||
|
|
||||||
- **Ферзь и Ладья**:
|
|
||||||
- **Ранее**: Могли перемещаться до **5 клеток** за ход.
|
|
||||||
- **Сейчас**: Могут перемещаться до **3 клеток** за ход.
|
|
||||||
|
|
||||||
Это изменение делает игру более стратегичной, ограничивая возможности мощных фигур и стимулируя разнообразие тактик.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Обновления и Изменения
|
|
||||||
|
|
||||||
### Версия 1.1.1
|
|
||||||
|
|
||||||
1. **Добавление Магазина и Монет**:
|
|
||||||
- **Магазин**: Теперь доступен при взаимодействии с торговцами на поле. Магазин предлагает различные улучшения за монеты.
|
|
||||||
- **Монеты**: Появляются на поле с шансом **0.1%**, становясь более ценными и редкими.
|
|
||||||
|
|
||||||
2. **Исправление Механики Товара "Уменьшить HP врага"**:
|
|
||||||
- Теперь при покупке этого бонуса у всех фигур противника вычитается **3 HP**, но фигуры не умирают.
|
|
||||||
- Если HP противника меньше 3, устанавливается **1 HP**.
|
|
||||||
|
|
||||||
3. **Улучшение Баланса и Механик**:
|
|
||||||
- **Шанс генерации монет** снижен до **0.1%**, уменьшая их количество и повышая ценность.
|
|
||||||
- **Удалено отображение вероятностей** генерации бонусов и монет из интерфейса игры, упрощая визуальное восприятие.
|
|
||||||
|
|
||||||
4. **Прочие Улучшения**:
|
|
||||||
- Оптимизация интерфейса магазина для улучшения взаимодействия с пользователем.
|
|
||||||
- Устранение багов, связанных с переходом ходов после применения бонусов.
|
|
||||||
|
|
||||||
Спасибо за внимание и приятной игры в **Шахматы с Открытым Миром 1.1.1**!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
@ -5,52 +5,39 @@ import time
|
|||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
|
|
||||||
# --- ПАРАМЕТРЫ ПОЛЯ ---
|
# ========= Глобальные настройки по умолчанию =========
|
||||||
CELL_SIZE = 32
|
CELL_SIZE = 32
|
||||||
GRID_WIDTH = 30
|
PANEL_WIDTH = 200
|
||||||
GRID_HEIGHT = 30
|
|
||||||
PANEL_WIDTH = 200 # Ширина правой панели
|
|
||||||
WINDOW_WIDTH = CELL_SIZE * GRID_WIDTH + PANEL_WIDTH
|
|
||||||
WINDOW_HEIGHT = CELL_SIZE * GRID_HEIGHT
|
|
||||||
|
|
||||||
# --- ЦВЕТА ---
|
# Значения по умолчанию для поля и шанса бонусов (меню позволит их изменять)
|
||||||
|
default_GRID_WIDTH = 30
|
||||||
|
default_GRID_HEIGHT = 30
|
||||||
|
default_BONUS_GENERATION_CHANCE = 0.10 # 10%
|
||||||
|
|
||||||
|
# ========= Цвета =========
|
||||||
COLOR_BG = (30, 30, 30)
|
COLOR_BG = (30, 30, 30)
|
||||||
COLOR_GRID = (50, 50, 50)
|
COLOR_GRID = (50, 50, 50)
|
||||||
COLOR_FOG = (20, 20, 20)
|
COLOR_FOG = (20, 20, 20)
|
||||||
COLOR_SELECTED = (255, 0, 0)
|
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) # Розовый
|
|
||||||
|
|
||||||
# Цвет текущего хода
|
COLOR_TURN_TEXT = (255, 255, 255)
|
||||||
COLOR_TURN_TEXT = (255, 255, 255) # Белый
|
COLOR_TIMER_NORMAL = (255, 255, 255)
|
||||||
|
COLOR_TIMER_WARNING = (255, 0, 0)
|
||||||
|
COLOR_MERCHANT = (139, 69, 19)
|
||||||
|
COLOR_COIN = (255, 165, 0)
|
||||||
|
|
||||||
# Новый цвет для пометки клеток, которые скоро уйдут в туман войны
|
# ========= Шрифты =========
|
||||||
COLOR_PRE_FOG = (169, 169, 169) # Светло-серый
|
|
||||||
|
|
||||||
# Цвет таймера
|
|
||||||
COLOR_TIMER_NORMAL = (255, 255, 255) # Белый
|
|
||||||
COLOR_TIMER_WARNING = (255, 0, 0) # Красный
|
|
||||||
|
|
||||||
# Цвет торговца
|
|
||||||
COLOR_MERCHANT = (139, 69, 19) # Коричневый
|
|
||||||
|
|
||||||
# Цвет монеты
|
|
||||||
COLOR_COIN = (255, 165, 0) # Оранжевый
|
|
||||||
|
|
||||||
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
|
|
||||||
pygame.display.set_caption("Шахматы с Открытым Миром 1.1.1")
|
|
||||||
|
|
||||||
# --- Шрифты ---
|
|
||||||
font = pygame.font.SysFont(None, CELL_SIZE // 2)
|
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)
|
||||||
@ -58,59 +45,126 @@ 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)
|
shop_font = pygame.font.SysFont(None, 24)
|
||||||
|
|
||||||
# --- Глобальное множество открытых клеток (туман войны) ---
|
# ========= Меню настроек =========
|
||||||
global_revealed = set()
|
def main_menu():
|
||||||
|
menu_running = True
|
||||||
|
menu_width, menu_height = 600, 400
|
||||||
|
menu_screen = pygame.display.set_mode((menu_width, menu_height))
|
||||||
|
pygame.display.set_caption("Настройки игры")
|
||||||
|
|
||||||
|
# Начальные значения из настроек по умолчанию
|
||||||
|
grid_width = default_GRID_WIDTH
|
||||||
|
grid_height = default_GRID_HEIGHT
|
||||||
|
bonus_chance = default_BONUS_GENERATION_CHANCE # 0.10 = 10%
|
||||||
|
|
||||||
|
# Подготавливаем тексты для кнопок
|
||||||
|
minus_text = shop_font.render("-", True, (0, 0, 0))
|
||||||
|
plus_text = shop_font.render("+", True, (0, 0, 0))
|
||||||
|
|
||||||
|
while menu_running:
|
||||||
|
menu_screen.fill((50, 50, 50))
|
||||||
|
|
||||||
|
# Заголовок
|
||||||
|
title_text = shop_font.render("Настройки игры", True, (255,255,255))
|
||||||
|
title_rect = title_text.get_rect(center=(menu_width//2, 40))
|
||||||
|
menu_screen.blit(title_text, title_rect)
|
||||||
|
|
||||||
|
# Размер поля
|
||||||
|
field_text = shop_font.render("Размер поля:", True, (255,255,255))
|
||||||
|
menu_screen.blit(field_text, (50, 80))
|
||||||
|
|
||||||
|
# Ширина поля
|
||||||
|
width_text = shop_font.render(f"Ширина: {grid_width}", True, (255,255,255))
|
||||||
|
menu_screen.blit(width_text, (100, 130))
|
||||||
|
width_minus_rect = pygame.Rect(50, 130, 40, 30)
|
||||||
|
width_plus_rect = pygame.Rect(250, 130, 40, 30)
|
||||||
|
pygame.draw.rect(menu_screen, (200,200,200), width_minus_rect)
|
||||||
|
pygame.draw.rect(menu_screen, (200,200,200), width_plus_rect)
|
||||||
|
menu_screen.blit(minus_text, minus_text.get_rect(center=width_minus_rect.center))
|
||||||
|
menu_screen.blit(plus_text, plus_text.get_rect(center=width_plus_rect.center))
|
||||||
|
|
||||||
|
# Высота поля
|
||||||
|
height_text = shop_font.render(f"Высота: {grid_height}", True, (255,255,255))
|
||||||
|
menu_screen.blit(height_text, (100, 180))
|
||||||
|
height_minus_rect = pygame.Rect(50, 180, 40, 30)
|
||||||
|
height_plus_rect = pygame.Rect(250, 180, 40, 30)
|
||||||
|
pygame.draw.rect(menu_screen, (200,200,200), height_minus_rect)
|
||||||
|
pygame.draw.rect(menu_screen, (200,200,200), height_plus_rect)
|
||||||
|
menu_screen.blit(minus_text, minus_text.get_rect(center=height_minus_rect.center))
|
||||||
|
menu_screen.blit(plus_text, plus_text.get_rect(center=height_plus_rect.center))
|
||||||
|
|
||||||
|
# Шанс бонусов (выводим значение в процентах)
|
||||||
|
bonus_label = shop_font.render("Шанс бонусов (%):", True, (255,255,255))
|
||||||
|
menu_screen.blit(bonus_label, (100, 230))
|
||||||
|
bonus_value = shop_font.render(f"{int(bonus_chance*100)}", True, (255,255,255))
|
||||||
|
menu_screen.blit(bonus_value, (300, 230))
|
||||||
|
bonus_minus_rect = pygame.Rect(350, 225, 40, 30)
|
||||||
|
bonus_plus_rect = pygame.Rect(450, 225, 40, 30)
|
||||||
|
pygame.draw.rect(menu_screen, (200,200,200), bonus_minus_rect)
|
||||||
|
pygame.draw.rect(menu_screen, (200,200,200), bonus_plus_rect)
|
||||||
|
menu_screen.blit(minus_text, minus_text.get_rect(center=bonus_minus_rect.center))
|
||||||
|
menu_screen.blit(plus_text, plus_text.get_rect(center=bonus_plus_rect.center))
|
||||||
|
|
||||||
|
# Кнопка "Начать игру"
|
||||||
|
start_rect = pygame.Rect(menu_width//2 - 75, 300, 150, 50)
|
||||||
|
pygame.draw.rect(menu_screen, (0,255,0), start_rect)
|
||||||
|
start_text = shop_font.render("Начать игру", True, (0,0,0))
|
||||||
|
menu_screen.blit(start_text, start_text.get_rect(center=start_rect.center))
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
pygame.quit()
|
||||||
|
sys.exit()
|
||||||
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
|
mx, my = pygame.mouse.get_pos()
|
||||||
|
if width_minus_rect.collidepoint(mx, my):
|
||||||
|
grid_width = max(8, grid_width - 1)
|
||||||
|
elif width_plus_rect.collidepoint(mx, my):
|
||||||
|
grid_width = min(100, grid_width + 1)
|
||||||
|
elif height_minus_rect.collidepoint(mx, my):
|
||||||
|
grid_height = max(8, grid_height - 1)
|
||||||
|
elif height_plus_rect.collidepoint(mx, my):
|
||||||
|
grid_height = min(100, grid_height + 1)
|
||||||
|
elif bonus_minus_rect.collidepoint(mx, my):
|
||||||
|
bonus_chance = max(0.0, bonus_chance - 0.01)
|
||||||
|
elif bonus_plus_rect.collidepoint(mx, my):
|
||||||
|
bonus_chance = min(1.0, bonus_chance + 0.01)
|
||||||
|
elif start_rect.collidepoint(mx, my):
|
||||||
|
menu_running = False
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
return grid_width, grid_height, bonus_chance
|
||||||
|
|
||||||
# --- Словарь символов (фолбэк, если спрайт не найден) ---
|
# ========= Вызываем меню настроек и обновляем глобальные параметры =========
|
||||||
|
GRID_WIDTH, GRID_HEIGHT, BONUS_GENERATION_CHANCE = main_menu()
|
||||||
|
WINDOW_WIDTH = CELL_SIZE * GRID_WIDTH + PANEL_WIDTH
|
||||||
|
WINDOW_HEIGHT = CELL_SIZE * GRID_HEIGHT
|
||||||
|
|
||||||
|
# Теперь устанавливаем главное окно игры
|
||||||
|
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
|
||||||
|
pygame.display.set_caption("Шахматы с Открытым Миром 1.1.1")
|
||||||
|
|
||||||
|
# ========= Глобальные переменные =========
|
||||||
|
global_revealed = set()
|
||||||
|
bonus_cells = {}
|
||||||
|
base_bonus_probabilities = {
|
||||||
|
'regen': 0.42,
|
||||||
|
'hp_upgrade': 0.32,
|
||||||
|
'damage': 0.2,
|
||||||
|
'king_hp_upgrade': 0.05,
|
||||||
|
'add_piece': 0.01
|
||||||
|
}
|
||||||
|
bonus_probabilities = base_bonus_probabilities.copy()
|
||||||
piece_symbols = {
|
piece_symbols = {
|
||||||
'rook': 'Л',
|
'rook': 'Л',
|
||||||
'knight': 'К',
|
'knight': 'К',
|
||||||
'bishop': 'С',
|
'bishop': 'С',
|
||||||
'queen': 'Ф',
|
'queen': 'Ф',
|
||||||
'king': 'К', # Король
|
'king': 'К',
|
||||||
'pawn': 'П'
|
'pawn': 'П'
|
||||||
}
|
}
|
||||||
|
|
||||||
# === МЕХАНИКА БОНУСОВ ===
|
# ========= Класс фигур =========
|
||||||
bonus_cells = {}
|
|
||||||
|
|
||||||
base_bonus_probabilities = {
|
|
||||||
'regen': 0.42, # 42%
|
|
||||||
'hp_upgrade': 0.32, # 32%
|
|
||||||
'damage': 0.2, # 20%
|
|
||||||
'king_hp_upgrade': 0.05, # 5%
|
|
||||||
'add_piece': 0.01 # 1%
|
|
||||||
}
|
|
||||||
|
|
||||||
bonus_probabilities = base_bonus_probabilities.copy()
|
|
||||||
|
|
||||||
def choose_bonus_type():
|
|
||||||
rand = random.random()
|
|
||||||
cumulative = 0
|
|
||||||
for bonus_type, prob in bonus_probabilities.items():
|
|
||||||
cumulative += prob
|
|
||||||
if rand < cumulative:
|
|
||||||
return bonus_type
|
|
||||||
return 'regen' # По умолчанию
|
|
||||||
|
|
||||||
# === МЕХАНИКА БОЯ ===
|
|
||||||
def resolve_combat(attacker, defender):
|
|
||||||
a = attacker.hp
|
|
||||||
b = defender.hp
|
|
||||||
if a < b:
|
|
||||||
defender.hp = b - a
|
|
||||||
attacker.hp = 0
|
|
||||||
return (False, True)
|
|
||||||
elif a > b:
|
|
||||||
attacker.hp = a - b
|
|
||||||
defender.hp = 0
|
|
||||||
return (True, False)
|
|
||||||
else:
|
|
||||||
attacker.hp = 1
|
|
||||||
defender.hp = 0
|
|
||||||
return (True, False)
|
|
||||||
|
|
||||||
# === КЛАСС ФИГУРЫ ===
|
|
||||||
class Piece:
|
class Piece:
|
||||||
def __init__(self, name, color, x, y):
|
def __init__(self, name, color, x, y):
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -118,9 +172,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 # Для ограничения повторного хода за один ход
|
self.has_moved = False
|
||||||
|
|
||||||
# Инициализация HP
|
|
||||||
if self.name == 'king':
|
if self.name == 'king':
|
||||||
self.max_hp = 5
|
self.max_hp = 5
|
||||||
self.hp = 5
|
self.hp = 5
|
||||||
@ -145,7 +197,7 @@ class Piece:
|
|||||||
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']:
|
||||||
max_steps = self.move_range if hasattr(self, 'move_range') else 3
|
max_steps = getattr(self, 'move_range', 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':
|
||||||
@ -184,51 +236,124 @@ class Piece:
|
|||||||
moves.append((nx, ny))
|
moves.append((nx, ny))
|
||||||
return moves
|
return moves
|
||||||
|
|
||||||
# --- Инициализация фигур ---
|
# ========= Функция разрешения боя =========
|
||||||
|
def resolve_combat(attacker, defender):
|
||||||
|
a = attacker.hp
|
||||||
|
b = defender.hp
|
||||||
|
if a < b:
|
||||||
|
defender.hp = b - a
|
||||||
|
attacker.hp = 0
|
||||||
|
return (False, True)
|
||||||
|
elif a > b:
|
||||||
|
attacker.hp = a - b
|
||||||
|
defender.hp = 0
|
||||||
|
return (True, False)
|
||||||
|
else:
|
||||||
|
attacker.hp = 1
|
||||||
|
defender.hp = 0
|
||||||
|
return (True, False)
|
||||||
|
|
||||||
|
# ========= Функция инициализации фигур =========
|
||||||
def initialize_pieces():
|
def initialize_pieces():
|
||||||
"""Чёрные: y=2 (back), y=3 (pawns). Белые: y=27 (back), y=26 (pawns)."""
|
|
||||||
pieces = []
|
pieces = []
|
||||||
cx = GRID_WIDTH // 2
|
cx = GRID_WIDTH // 2
|
||||||
# Чёрные
|
offset = 3 # для базовой линии – 8 фигур
|
||||||
|
# Вычисляем вертикальные позиции пропорционально высоте поля:
|
||||||
|
black_back_y = max(0, int(GRID_HEIGHT * 0.1))
|
||||||
|
black_pawn_y = min(GRID_HEIGHT - 1, black_back_y + 1)
|
||||||
|
white_back_y = GRID_HEIGHT - 1 - int(GRID_HEIGHT * 0.1)
|
||||||
|
white_pawn_y = max(0, white_back_y - 1)
|
||||||
|
|
||||||
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 - offset + i
|
||||||
y = 2
|
if 0 <= x < GRID_WIDTH:
|
||||||
pieces.append(Piece(pname, 'black', x, y))
|
pieces.append(Piece(pname, 'black', x, black_back_y))
|
||||||
for i in range(8):
|
for i in range(8):
|
||||||
x = cx - 3 + i
|
x = cx - offset + i
|
||||||
y = 3
|
if 0 <= x < GRID_WIDTH:
|
||||||
pieces.append(Piece('pawn', 'black', x, y))
|
pieces.append(Piece('pawn', 'black', x, black_pawn_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 - offset + i
|
||||||
y = 27
|
if 0 <= x < GRID_WIDTH:
|
||||||
pieces.append(Piece(pname, 'white', x, y))
|
pieces.append(Piece(pname, 'white', x, white_back_y))
|
||||||
for i in range(8):
|
for i in range(8):
|
||||||
x = cx - 3 + i
|
x = cx - offset + i
|
||||||
y = 26
|
if 0 <= x < GRID_WIDTH:
|
||||||
pieces.append(Piece('pawn', 'white', x, y))
|
pieces.append(Piece('pawn', 'white', x, white_pawn_y))
|
||||||
return pieces
|
return pieces
|
||||||
|
|
||||||
pieces = initialize_pieces()
|
# ========= Спрайты для фигур =========
|
||||||
|
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
|
||||||
|
|
||||||
selected_piece = None
|
load_piece_sprites()
|
||||||
possible_moves = []
|
|
||||||
game_over = False
|
|
||||||
winner = None
|
|
||||||
|
|
||||||
# --- Туман войны ---
|
# ========= Спрайты для окружающей среды =========
|
||||||
cell_counters = {} # {(x,y): turns_since_last_revealed}
|
try:
|
||||||
# Каждая новая клетка имеет 10% шанс стать бонусной.
|
open_cell_sprite = pygame.image.load("sprites/open_cell.png").convert_alpha()
|
||||||
BONUS_GENERATION_CHANCE = 0.10
|
open_cell_sprite = pygame.transform.scale(open_cell_sprite, (CELL_SIZE, CELL_SIZE))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка загрузки open_cell.png: {e}")
|
||||||
|
open_cell_sprite = None
|
||||||
|
|
||||||
# --- Система ходов и многоперемещений ---
|
try:
|
||||||
current_turn = 'white' # Начинаем с белых
|
fog_sprite = pygame.image.load("sprites/fog.png").convert_alpha()
|
||||||
|
fog_sprite = pygame.transform.scale(fog_sprite, (CELL_SIZE, CELL_SIZE))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка загрузки fog.png: {e}")
|
||||||
|
fog_sprite = None
|
||||||
|
|
||||||
|
# ========= Механика бонусов =========
|
||||||
|
def choose_bonus_type():
|
||||||
|
rand = random.random()
|
||||||
|
cumulative = 0
|
||||||
|
for bonus_type, prob in bonus_probabilities.items():
|
||||||
|
cumulative += prob
|
||||||
|
if rand < cumulative:
|
||||||
|
return bonus_type
|
||||||
|
return 'regen'
|
||||||
|
|
||||||
|
# ========= Торговцы, монеты и магазин =========
|
||||||
|
merchant_positions = [(0, GRID_HEIGHT // 2), (GRID_WIDTH - 1, GRID_HEIGHT // 2)]
|
||||||
|
global_revealed.update(merchant_positions)
|
||||||
|
coin_cells = set()
|
||||||
|
player_coins = {'white': 0, 'black': 0}
|
||||||
|
shop_open = False
|
||||||
|
shop_items = {
|
||||||
|
'extend_move': {'name': 'Расширить ход', 'cost': 6},
|
||||||
|
'increase_hp': {'name': 'Увеличить HP', 'cost': 6},
|
||||||
|
'damage_enemy': {'name': 'Уменьшить HP врага', 'cost': 6},
|
||||||
|
'curse': {'name': 'Проклятие', 'cost': 6}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ========= Система ходов =========
|
||||||
|
current_turn = 'white'
|
||||||
turn_count = 0
|
turn_count = 0
|
||||||
moves_remaining = 3 # За один ход игрок может сделать 3 перемещения
|
moves_remaining = 3
|
||||||
|
|
||||||
# --- Механика повышения вероятности красных полей ---
|
|
||||||
def update_bonus_probabilities():
|
def update_bonus_probabilities():
|
||||||
global bonus_probabilities
|
global bonus_probabilities
|
||||||
if turn_count <= 100:
|
if turn_count <= 100:
|
||||||
@ -247,8 +372,8 @@ def update_bonus_probabilities():
|
|||||||
else:
|
else:
|
||||||
bonus_probabilities[bt] = prob / total_other_probs * remaining_prob
|
bonus_probabilities[bt] = prob / total_other_probs * remaining_prob
|
||||||
|
|
||||||
# --- Механика времени на ход ---
|
# ========= Время на ход =========
|
||||||
TURN_TIME_LIMIT = 120 # seconds
|
TURN_TIME_LIMIT = 120 # секунд
|
||||||
timer_start_time = time.time()
|
timer_start_time = time.time()
|
||||||
timer_expired = False
|
timer_expired = False
|
||||||
player_timeouts = {'white': 0, 'black': 0}
|
player_timeouts = {'white': 0, 'black': 0}
|
||||||
@ -274,7 +399,6 @@ def check_timer():
|
|||||||
else:
|
else:
|
||||||
switch_turn()
|
switch_turn()
|
||||||
|
|
||||||
# При переключении хода сбрасываем число оставшихся ходов и флаг has_moved у фигур текущего игрока.
|
|
||||||
def switch_turn():
|
def switch_turn():
|
||||||
global current_turn, turn_count, timer_expired, moves_remaining
|
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'
|
||||||
@ -287,17 +411,17 @@ def switch_turn():
|
|||||||
if p.color == current_turn:
|
if p.color == current_turn:
|
||||||
p.has_moved = False
|
p.has_moved = False
|
||||||
|
|
||||||
# После успешного перемещения фигуры вызываем эту функцию.
|
|
||||||
def end_move(moved_piece):
|
def end_move(moved_piece):
|
||||||
global moves_remaining
|
global moves_remaining
|
||||||
moved_piece.has_moved = True
|
# Если фигура погибла, этот ход всё равно расходуется
|
||||||
moves_remaining -= 1
|
moves_remaining -= 1
|
||||||
if moves_remaining <= 0:
|
if moves_remaining <= 0:
|
||||||
switch_turn()
|
switch_turn()
|
||||||
|
|
||||||
# --- Обновление тумана войны и генерация бонусов/монет ---
|
# ========= Обновление тумана (с удалением монет при старении) =========
|
||||||
|
cell_counters = {} # {(x,y): turns_since_last_revealed}
|
||||||
def update_fog():
|
def update_fog():
|
||||||
global global_revealed, bonus_cells
|
global global_revealed, bonus_cells, coin_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:
|
||||||
@ -326,9 +450,12 @@ def update_fog():
|
|||||||
global_revealed.remove(pos)
|
global_revealed.remove(pos)
|
||||||
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]
|
||||||
|
if pos in coin_cells:
|
||||||
|
del coin_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:
|
||||||
if random.random() < 0.90:
|
# 95% шанс бонус, 5% монета
|
||||||
|
if random.random() < 0.95:
|
||||||
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}
|
||||||
@ -336,76 +463,38 @@ def update_fog():
|
|||||||
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
|
|
||||||
merchant_positions = [(0, central_line_y), (GRID_WIDTH - 1, central_line_y)]
|
|
||||||
global_revealed.update(merchant_positions)
|
|
||||||
|
|
||||||
# --- Монеты на поле ---
|
|
||||||
coin_cells = set()
|
|
||||||
|
|
||||||
# --- Счет монет для игроков ---
|
|
||||||
player_coins = {'white': 0, 'black': 0}
|
|
||||||
|
|
||||||
# --- Механика магазина ---
|
|
||||||
# Повышенные цены: 6 монет за покупку
|
|
||||||
shop_open = False
|
|
||||||
shop_items = {
|
|
||||||
'extend_move': {'name': 'Расширить ход', 'cost': 6},
|
|
||||||
'increase_hp': {'name': 'Увеличить HP', 'cost': 6},
|
|
||||||
'damage_enemy': {'name': 'Уменьшить HP врага', 'cost': 6},
|
|
||||||
'curse': {'name': 'Проклятие', 'cost': 6}
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Реализация спрайтов фигур ---
|
|
||||||
# Ожидается, что в папке "sprites" находятся файлы: wK.png, wQ.png, wR.png, wB.png, wN.png, wP.png для белых,
|
|
||||||
# и bK.png, bQ.png, bR.png, bB.png, bN.png, bP.png для чёрных.
|
|
||||||
piece_sprites = {}
|
|
||||||
|
|
||||||
def load_piece_sprites():
|
|
||||||
piece_names_map = {
|
|
||||||
'king': 'K',
|
|
||||||
'queen': 'Q',
|
|
||||||
'rook': 'R',
|
|
||||||
'bishop': 'B',
|
|
||||||
'knight': 'N',
|
|
||||||
'pawn': 'P'
|
|
||||||
}
|
|
||||||
colors = ['white', 'black']
|
|
||||||
for color in colors:
|
|
||||||
letter = 'w' if color == 'white' else 'b'
|
|
||||||
for name in piece_names_map:
|
|
||||||
filename = f"sprites/{letter}{piece_names_map[name]}.png"
|
|
||||||
try:
|
|
||||||
image = pygame.image.load(filename).convert_alpha()
|
|
||||||
image = pygame.transform.scale(image, (CELL_SIZE, CELL_SIZE))
|
|
||||||
piece_sprites[(color, name)] = image
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Ошибка загрузки спрайта {filename}: {e}")
|
|
||||||
piece_sprites[(color, name)] = None
|
|
||||||
|
|
||||||
load_piece_sprites()
|
|
||||||
|
|
||||||
# --- Функции отрисовки ---
|
|
||||||
def draw_grid():
|
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):
|
||||||
pygame.draw.line(screen, COLOR_GRID, (x, 0), (x, CELL_SIZE * GRID_HEIGHT))
|
pygame.draw.line(screen, COLOR_GRID, (x, 0), (x, CELL_SIZE * GRID_HEIGHT))
|
||||||
for y in range(0, CELL_SIZE * GRID_HEIGHT, CELL_SIZE):
|
for y in range(0, CELL_SIZE * GRID_HEIGHT, CELL_SIZE):
|
||||||
pygame.draw.line(screen, COLOR_GRID, (0, y), (CELL_SIZE * GRID_WIDTH, y))
|
pygame.draw.line(screen, COLOR_GRID, (0, y), (CELL_SIZE * GRID_WIDTH, y))
|
||||||
|
|
||||||
def draw_fog():
|
def draw_environment():
|
||||||
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 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)
|
if pos in global_revealed:
|
||||||
pygame.draw.rect(screen, COLOR_FOG, rect)
|
if open_cell_sprite:
|
||||||
|
screen.blit(open_cell_sprite, rect)
|
||||||
|
else:
|
||||||
|
pygame.draw.rect(screen, (30, 30, 30), rect)
|
||||||
|
if cell_counters.get(pos, 0) == 4 and (pos not in bonus_cells) and (pos not in coin_cells):
|
||||||
|
if fog_sprite:
|
||||||
|
overlay = fog_sprite.copy()
|
||||||
|
overlay.set_alpha(128)
|
||||||
|
screen.blit(overlay, rect)
|
||||||
|
else:
|
||||||
|
overlay = pygame.Surface((CELL_SIZE, CELL_SIZE))
|
||||||
|
overlay.set_alpha(128)
|
||||||
|
overlay.fill((169, 169, 169))
|
||||||
|
screen.blit(overlay, rect)
|
||||||
else:
|
else:
|
||||||
if pos in merchant_positions:
|
if fog_sprite:
|
||||||
continue
|
screen.blit(fog_sprite, rect)
|
||||||
if cell_counters.get(pos, 0) == 4:
|
else:
|
||||||
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_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():
|
||||||
@ -485,10 +574,7 @@ def draw_pieces(pieces):
|
|||||||
text = font.render(symbol, True, (255, 0, 0))
|
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_str = f"{p.hp}/{p.max_hp}"
|
hp_str = f"{p.hp}/{p.max_hp}"
|
||||||
# Определяем цвета: для белых фигур текст будет черным, для черных – белым;
|
|
||||||
# фон выбираем противоположный
|
|
||||||
if p.color == 'white':
|
if p.color == 'white':
|
||||||
text_color = (0, 0, 0)
|
text_color = (0, 0, 0)
|
||||||
bg_color = (255, 255, 255)
|
bg_color = (255, 255, 255)
|
||||||
@ -497,9 +583,8 @@ def draw_pieces(pieces):
|
|||||||
bg_color = (0, 0, 0)
|
bg_color = (0, 0, 0)
|
||||||
hp_text = font.render(hp_str, True, text_color)
|
hp_text = font.render(hp_str, True, text_color)
|
||||||
text_rect = hp_text.get_rect(topleft=(x + 2, y + 2))
|
text_rect = hp_text.get_rect(topleft=(x + 2, y + 2))
|
||||||
# Создаем временную поверхность для фона с альфа-каналом
|
|
||||||
bg_surf = pygame.Surface(text_rect.size)
|
bg_surf = pygame.Surface(text_rect.size)
|
||||||
bg_surf.set_alpha(200) # степень прозрачности (0-255)
|
bg_surf.set_alpha(200)
|
||||||
bg_surf.fill(bg_color)
|
bg_surf.fill(bg_color)
|
||||||
screen.blit(bg_surf, text_rect.topleft)
|
screen.blit(bg_surf, text_rect.topleft)
|
||||||
screen.blit(hp_text, text_rect.topleft)
|
screen.blit(hp_text, text_rect.topleft)
|
||||||
@ -554,6 +639,7 @@ def draw_victory(winner):
|
|||||||
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):
|
||||||
if bonus_type == 'regen':
|
if bonus_type == 'regen':
|
||||||
if piece.name != 'king':
|
if piece.name != 'king':
|
||||||
@ -572,7 +658,15 @@ def apply_bonus(piece, bonus_type, pieces):
|
|||||||
else:
|
else:
|
||||||
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':
|
||||||
pass
|
damage = random.randint(1, 5)
|
||||||
|
piece.hp -= damage
|
||||||
|
print(f"Бонус DAMAGE: Фигура получила {damage} урона, осталось {piece.hp} HP")
|
||||||
|
if piece.hp <= 0:
|
||||||
|
try:
|
||||||
|
pieces.remove(piece)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return 'dead'
|
||||||
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)]
|
||||||
@ -586,43 +680,18 @@ def apply_bonus(piece, bonus_type, pieces):
|
|||||||
pieces.append(new_piece)
|
pieces.append(new_piece)
|
||||||
break
|
break
|
||||||
|
|
||||||
def handle_shop_purchase(key):
|
# ========= Инициализация фигур =========
|
||||||
if player_coins[current_turn] < shop_items[key]['cost']:
|
pieces = initialize_pieces()
|
||||||
return
|
selected_piece = None
|
||||||
player_coins[current_turn] -= shop_items[key]['cost']
|
possible_moves = []
|
||||||
if key == 'extend_move':
|
game_over = False
|
||||||
for p in pieces:
|
winner = None
|
||||||
if p.color == current_turn:
|
|
||||||
if p.name in ['rook', 'bishop', 'queen']:
|
|
||||||
setattr(p, 'move_range', getattr(p, 'move_range', 3) + 2)
|
|
||||||
elif p.name == 'pawn':
|
|
||||||
setattr(p, 'move_range', getattr(p, 'move_range', 1) + 1)
|
|
||||||
elif key == 'increase_hp':
|
|
||||||
for p in pieces:
|
|
||||||
if p.color == current_turn and p.name != 'king':
|
|
||||||
p.max_hp += 3
|
|
||||||
p.hp = min(p.hp + 3, p.max_hp)
|
|
||||||
elif key == 'damage_enemy':
|
|
||||||
enemy_color = 'black' if current_turn == 'white' else 'white'
|
|
||||||
for p in pieces:
|
|
||||||
if p.color == enemy_color:
|
|
||||||
if p.hp > 3:
|
|
||||||
p.hp -= 3
|
|
||||||
elif p.hp > 1:
|
|
||||||
p.hp = 1
|
|
||||||
else:
|
|
||||||
p.hp = 1
|
|
||||||
elif key == 'curse':
|
|
||||||
bonus_probabilities['regen'] = max(bonus_probabilities['regen'] - 0.10, 0)
|
|
||||||
bonus_probabilities['hp_upgrade'] = max(bonus_probabilities['hp_upgrade'] - 0.10, 0)
|
|
||||||
total_prob = sum(bonus_probabilities.values())
|
|
||||||
for bt in bonus_probabilities:
|
|
||||||
bonus_probabilities[bt] /= total_prob if total_prob > 0 else 1
|
|
||||||
|
|
||||||
# --- Инициализируем туман войны в начале игры ---
|
# ========= Инициализация тумана =========
|
||||||
|
cell_counters = {} # {(x,y): turns_since_last_revealed}
|
||||||
update_fog()
|
update_fog()
|
||||||
|
|
||||||
# --- Основной цикл игры ---
|
# ========= Основной игровой цикл =========
|
||||||
running = True
|
running = True
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
@ -640,10 +709,52 @@ while running:
|
|||||||
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:
|
||||||
|
# Если на клетке есть бонус damage по пути
|
||||||
|
if selected_piece.name in ['rook', 'bishop', 'queen']:
|
||||||
|
# Если фигура двигается на несколько клеток, проверяем промежуточный путь
|
||||||
|
path = []
|
||||||
|
dx = gx - selected_piece.x
|
||||||
|
dy = gy - selected_piece.y
|
||||||
|
if dx != 0:
|
||||||
|
dx //= abs(dx)
|
||||||
|
if dy != 0:
|
||||||
|
dy //= abs(dy)
|
||||||
|
for step in range(1, max(abs(gx - selected_piece.x), abs(gy - selected_piece.y))):
|
||||||
|
path_x = selected_piece.x + dx * step
|
||||||
|
path_y = selected_piece.y + dy * step
|
||||||
|
path.append((path_x, path_y))
|
||||||
|
survived = True
|
||||||
|
for pos in path:
|
||||||
|
if pos in bonus_cells and bonus_cells[pos]['type'] == 'damage':
|
||||||
|
damage = random.randint(1, 5)
|
||||||
|
selected_piece.hp = max(selected_piece.hp - damage, 0)
|
||||||
|
del bonus_cells[pos]
|
||||||
|
if selected_piece.hp == 0:
|
||||||
|
# — модификация для обработки смерти на пути:
|
||||||
|
pieces.remove(selected_piece)
|
||||||
|
selected_piece = None
|
||||||
|
possible_moves.clear()
|
||||||
|
survived = False
|
||||||
|
# Считаем, что ход потрачен, уменьшаем moves_remaining
|
||||||
|
moves_remaining -= 1
|
||||||
|
break
|
||||||
|
if not survived:
|
||||||
|
if moves_remaining <= 0:
|
||||||
|
switch_turn()
|
||||||
|
continue
|
||||||
|
# Если бонус (damage или другой) находится непосредственно на целевой клетке
|
||||||
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)
|
result = apply_bonus(selected_piece, bonus_type, pieces)
|
||||||
del bonus_cells[(gx, gy)]
|
del bonus_cells[(gx, gy)]
|
||||||
|
if result == 'dead':
|
||||||
|
# Если фигура погибла, засчитываем ход, уменьшаем счетчик и остаёмся на том же игроке
|
||||||
|
moves_remaining -= 1
|
||||||
|
if moves_remaining <= 0:
|
||||||
|
switch_turn()
|
||||||
|
selected_piece = None
|
||||||
|
possible_moves.clear()
|
||||||
|
continue
|
||||||
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))
|
||||||
@ -662,8 +773,9 @@ while running:
|
|||||||
pieces.remove(attacker)
|
pieces.remove(attacker)
|
||||||
selected_piece = None
|
selected_piece = None
|
||||||
possible_moves.clear()
|
possible_moves.clear()
|
||||||
update_fog()
|
moves_remaining -= 1
|
||||||
switch_turn()
|
if moves_remaining <= 0:
|
||||||
|
switch_turn()
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
path = []
|
path = []
|
||||||
@ -689,15 +801,18 @@ while running:
|
|||||||
selected_piece = None
|
selected_piece = None
|
||||||
possible_moves.clear()
|
possible_moves.clear()
|
||||||
survived = False
|
survived = False
|
||||||
switch_turn()
|
moves_remaining -= 1
|
||||||
break
|
break
|
||||||
if survived:
|
if not survived:
|
||||||
attacker.x = gx
|
if moves_remaining <= 0:
|
||||||
attacker.y = gy
|
switch_turn()
|
||||||
attacker.selected = False
|
continue
|
||||||
end_move(attacker)
|
attacker.x = gx
|
||||||
selected_piece = None
|
attacker.y = gy
|
||||||
possible_moves.clear()
|
attacker.selected = False
|
||||||
|
end_move(attacker)
|
||||||
|
selected_piece = None
|
||||||
|
possible_moves.clear()
|
||||||
else:
|
else:
|
||||||
path = []
|
path = []
|
||||||
if selected_piece.name in ['rook', 'bishop', 'queen']:
|
if selected_piece.name in ['rook', 'bishop', 'queen']:
|
||||||
@ -722,15 +837,18 @@ while running:
|
|||||||
selected_piece = None
|
selected_piece = None
|
||||||
possible_moves.clear()
|
possible_moves.clear()
|
||||||
survived = False
|
survived = False
|
||||||
switch_turn()
|
moves_remaining -= 1
|
||||||
break
|
break
|
||||||
if survived:
|
if not survived:
|
||||||
selected_piece.x = gx
|
if moves_remaining <= 0:
|
||||||
selected_piece.y = gy
|
switch_turn()
|
||||||
selected_piece.selected = False
|
continue
|
||||||
end_move(selected_piece)
|
selected_piece.x = gx
|
||||||
selected_piece = None
|
selected_piece.y = gy
|
||||||
possible_moves.clear()
|
selected_piece.selected = False
|
||||||
|
end_move(selected_piece)
|
||||||
|
selected_piece = None
|
||||||
|
possible_moves.clear()
|
||||||
update_fog()
|
update_fog()
|
||||||
else:
|
else:
|
||||||
selected_piece.selected = False
|
selected_piece.selected = False
|
||||||
@ -746,13 +864,14 @@ while running:
|
|||||||
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)
|
# Здесь можно добавить логику покупки
|
||||||
|
pass
|
||||||
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_environment()
|
||||||
draw_grid()
|
draw_grid()
|
||||||
draw_fog()
|
|
||||||
draw_merchants()
|
draw_merchants()
|
||||||
draw_bonus_cells()
|
draw_bonus_cells()
|
||||||
draw_coin_cells()
|
draw_coin_cells()
|
||||||
|
BIN
sprites/fog.png
Normal file
BIN
sprites/fog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
sprites/open_cell.png
Normal file
BIN
sprites/open_cell.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 MiB |
BIN
sprites/pre_fog.png
Normal file
BIN
sprites/pre_fog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Loading…
x
Reference in New Issue
Block a user