Консольная RPG на TypeScript с турнирной системой, тремя классами персонажей, системой навыков, арен и прокачки. Игроки сражаются в турнире на выбывание — каждый бой в случайной арене, победители получают опыт и повышают уровень.
- Node.js ≥ 20
- npm ≥ 9
npm install
npm run devnpm test- Docker Desktop (Windows / macOS) или Docker Engine (Linux)
docker build -t fantasy-tournament .Используется двухэтапная сборка: первый контейнер компилирует TypeScript, второй — содержит только скомпилированный JS без dev-зависимостей. Итоговый образ весит ~100 МБ вместо ~400 МБ.
docker run -it fantasy-tournamentФлаг
-itобязателен — без негоreadlineне получитstdinи игра зависнет на первом вопросе.
В PowerShell и Windows Terminal команда работает без изменений:
docker run -it fantasy-tournamentВ старом cmd.exe нужна обёртка winpty:
winpty docker run -it fantasy-tournamentdocker build -t fantasy-tournament . && docker run -it fantasy-tournamentФабричный метод (Factory Method)
Каждый тип сущностей создаётся через изолированную фабрику.
| Фабрика | Ответственность |
|---|---|
PlayerFabric |
Точка входа — делегирует в нужную фабрику класса |
KnightFabric / ArcherFabric / WizardFabric |
Создают конкретный класс с дефолтными навыками |
WeaponFabric |
Создаёт оружие по типу со случайным уроном |
SkillFabric |
Создаёт навыки из шаблонов или с нуля |
ArenaFabric |
Выбирает арену по имени или случайно |
Шаблонный метод (Template Method)
Player реализует общую логику боя (attack, takeDamage, useSkill, gainExperience). Подклассы переопределяют только специфическое поведение — например, Knight может переопределить takeDamage.
Принцип единственной ответственности (SRP)
Logger— весь консольный вывод в одном местеGame— только игровой процесс (турнир, бои, опыт)readAnswer— изолирует работу сreadline- Утилиты рандомизации — чистые функции без состояния
index.ts
└── createGame()
├── Game
│ ├── PlayerFabric → KnightFabric / ArcherFabric / WizardFabric
│ │ └── SkillFabric, WeaponFabric
│ ├── ArenaFabric → TrainingGround / VolcanicCrater / AncientRuins
│ └── Logger
└── createCharacter()
Ввод числа игроков (кратно 4)
→ Создание персонажа (опционально)
→ Генерация остальных случайных игроков
→ Турнир на выбывание
→ Каждый бой — в случайной арене
→ Победитель получает опыт → возможный levep-up
→ Победитель сбрасывает состояние и идёт дальше
→ Финал → Победитель
Бой ведётся пошагово, ходы чередуются. На каждом ходу:
- Если у атакующего есть активные баффы — они применяются к урону
- Арена модифицирует итоговый урон через
modifyDamage() - С вероятностью 40% атакующий использует случайный навык
- Бой заканчивается, когда
health ≤ 0у одного из участников
Если у игрока активен стан (countOfSkipingTurns > 0), его атака пропускается и счётчик уменьшается.
Каждый бой проходит в случайно выбранной арене. Арена влияет на урон и количество опыта после победы.
| Арена | Модификатор урона | Бонус опыта |
|---|---|---|
| Training Ground | ×0.9 (мягче) | +25 |
| Volcanic Crater | ×1.15 (жёстче) | +40 |
| Ancient Ruins | ×1.0 (нейтрально) | +55 |
После победы в бою победитель получает опыт:
опыт = experienceBonus_арены + уровень_побеждённого × 10
При достижении порога опыта игрок повышает уровень:
- Начальный порог: 100 XP
- После каждого повышения порог умножается на ×1.35
- За каждый уровень: +10 HP, +2 к силе
- При повышении уровня HP и сила полностью восстанавливаются
reset() после боя сбрасывает только боевые состояния (стан, кулдауны навыков, здоровье до текущего initialHealth). Уровень и накопленный опыт сохраняются между боями.
| Навык | Тип | Механика |
|---|---|---|
| Огненные стрелы | Бафф (постоянный) | +2 к силе, без ограничения по ходам, 1 использование |
| Ледяные стрелы | Бафф (временный) | +3 к силе на 3 атаки, у лучника 2 использования |
| Удар возмездия | Эффект (урон) | Наносит сила × 1.3 + урон оружия напрямую |
| Заворожение | Эффект (стан) | Противник пропускает 1 ход |
Баффы без turns (огненные стрелы) остаются активными до reset(). Баффы с turns (ледяные стрелы) применяются каждую атаку и истекают по счётчику.
| Класс | Оружие | Дефолтные навыки | Особенность |
|---|---|---|---|
| Knight | Меч (sword) | Удар возмездия, Ледяные стрелы | — |
| Archer | Лук (bow) | Ледяные стрелы ×2, Огненные стрелы | Ледяные стрелы можно использовать дважды |
| Wizard | Посох (stick) | Заворожение, Ледяные стрелы | — |
| Параметр | Диапазон |
|---|---|
| Здоровье | 125–150 |
| Сила | 10–15 |
| Урон оружия | 2–5 |
src/
├── index.ts # Точка входа
├── abstract/
│ └── Player.ts # Базовый класс игрока с логикой боя и прокачки
├── classes/
│ ├── Knight.ts
│ ├── Archer.ts
│ ├── Wizard.ts
│ └── index.ts
├── arenas/
│ ├── IArena.ts # Интерфейс арены
│ ├── TrainingGround.ts
│ ├── VolcanicCrater.ts
│ └── AncientRuins.ts
├── fabrics/
│ ├── arenasFabric/
│ │ └── ArenaFabric.ts
│ ├── playersFabrics/
│ │ ├── PlayerFabric.ts # Главная фабрика + уникальные имена (shuffle)
│ │ ├── KnightFabric.ts
│ │ ├── ArcherFabric.ts
│ │ ├── WizardFabric.ts
│ │ └── index.ts
│ ├── skillFabric/
│ │ └── SkillFabric.ts # Шаблоны навыков + deep copy buff
│ └── weaponsFabric/
│ └── WeaponFabric.ts
├── gameplay/
│ └── Game.ts # Турнир, бой, начисление опыта
├── skills/
│ └── ISkill.ts
├── weapon/
│ └── IWeapon.ts
└── utils/
├── input/
│ ├── createGame.ts # Диалог запуска игры
│ └── createCharacter.ts # Диалог создания персонажа
├── output/
│ └── Logger.ts # Весь консольный вывод
├── question/
│ └── readAnswer.ts # Обёртка над readline
└── randomization/
├── getRandomNumber.ts
├── getRandomArrayElement.ts
└── index.ts
tests/
├── Archer.spec.ts
├── ArcherFabric.spec.ts
├── ArenaFabric.spec.ts
├── Game.spec.ts
├── ISkill.spec.ts
├── Knight.spec.ts
├── KnightFabric.spec.ts
├── PlayerFabric.spec.ts
├── PlayerProgression.spec.ts
├── SkillFabric.spec.ts
├── WeaponFabric.spec.ts
├── Wizard.spec.ts
├── WizardFabric.spec.ts
├── getRandomArrayElement.spec.ts
└── getRandomNumber.spec.ts