AutoBattler — это консольная пошаговая RPG, где игрок создаёт героя, выбирает класс и наблюдает за автоматическими боями против монстров.
Ты не управляешь сражением напрямую — но именно твои решения при создании персонажа и выборе повышения определяют исход каждой битвы.
Игра написана на C++17, без сторонних игровых движков. Всё отображение выполнено средствами ASCII-графики и управлением курсора в терминале.
- CMake 3.20+
- C++17 (MSVC, GCC, Clang)
- Поддержка: Windows
https://github.com/Makintosh365/AutoBattler.git
cd AutoBattler
mkdir build
cd build
cmake ..
cmake --build .
./autobattler.exe
- Полностью консольный интерфейс с динамической отрисовкой и эффектами вывода.
- Пошаговые сражения: расчёт попаданий, промахов и урона с учётом характеристик.
- Система классов: Воин, Разбойник, Варвар — каждый со своими пассивными способностями.
- Повышение уровня и мультикласс (можно развивать разные ветви).
- Таблица монстров со своими слабостями и типами урона.
- Поддержка модульной архитектуры и простого расширения новых типов оружия/событий.
Ожидание: после нажатия кнопки «ПРОВЕСТИ» на экране появляются 4 прямоугольника:
- внешняя рамка экрана,
- центральное поле
mainBox, - левый блок
leftBox, - правый блок
rightBox.
Ничто не должно перекрывать рамки и текст, прямоугольники не пересекаются.
| Класс | Здоровье | Оружие | 1-й уровень | 2-й уровень | 3-й уровень |
|---|---|---|---|---|---|
| Разбойник | +4 HP | Кинжал | +1 к урону при ловкости > цели | +1 к ловкости | Яд (+1/+2 урон на ход) |
| Воин | +5 HP | Меч | Двойной урон в первый ход | Щит (−3 урона при защите) | +1 к силе |
| Варвар | +6 HP | Дубина | Ярость (+2 урона первые 3 хода) | Каменная кожа (−урон по выносливости) | +1 к выносливости |
| Противник | HP | Урон | Сила | Ловкость | Выносливность | Особенность | Награда |
|---|---|---|---|---|---|---|---|
| Гоблин | 5 | 2 | 1 | 1 | 1 | — | Кинжал |
| Скелет | 10 | 2 | 2 | 2 | 1 | Уязвим к дробящему | Дубина |
| Слайм | 8 | 1 | 3 | 1 | 2 | Иммунитет к рубящему | Копьё |
| Призрак | 6 | 3 | 1 | 3 | 1 | Скрытая атака (как у разбойника) | Меч |
| Голем | 10 | 1 | 3 | 1 | 3 | Каменная кожа (как у варвара) | Топор |
| Дракон | 20 | 4 | 3 | 3 | 3 | Каждые 3 хода дыхание огнём (+3 урона) | Легендарный меч |
| Название | Урон | Тип урона |
|---|---|---|
| Меч | 3 | Рубящий |
| Дубина | 3 | Дробящий |
| Кинжал | 2 | Колющий |
| Топор | 4 | Рубящий |
| Копьё | 3 | Колющий |
| Легендарный меч | 10 | Рубящий |
Архитектура игры позволяет легко добавлять новых монстров, классы, способности и типы оружия,
не изменяя основную логику.
Ниже — краткий гайд, как это делается.
Чтобы добавить нового противника, нужно внести его в перечисление и фабрику.
- Чтобы добавить монстра нужно:
- 1) Внести его в enum class
enum class MonsterType
{
SLIME, // слизень
GOBLIN, // гоблин
SKELETON, // скелет
GHOST, // призрак
GOLEM, // голем
DRAGON // дракон
VAMPIRE // <--- новый монстр
};- 2) Внести его в фабричный метод
Monster Monster::create(MonsterType type)
{
switch (type)
{
case MonsterType::SKELETON:
return Monster("Скелет", type,
10, 2, 2, 2, 1,
Weapon::create("Дубина"),
{ std::make_shared<VulnerableToBluntAbility>() });
// Новый монстр — вампир
case MonsterType::VAMPIRE:
return Monster("Вампир", type,
10,2, 2, 2, 2,
Weapon::create("Кинжал"),
{ std::make_shared<LifeStealAbility>() });- **3) Добавить способность в AbilityImpl.h **
//------------------------------------------------------------------------------
// Вампир: Вампиризм
//------------------------------------------------------------------------------
class LifeStealAbility : public Ability
{
public:
std::string name() const override { return "Вампиризм"; }
std::string description() const override { return "Восстанавливает половину урона"; }
int onAttack(const Entity* attacker,
const Monster* defender,
int baseDamage,
int turnCount) const override;
};
- 4) Добавить способность в AbilityImpl.cpp
//------------------------------------------------------------------------------
// Вампир: Вампиризм
//------------------------------------------------------------------------------
int LifeStealAbility::onAttackMonster(const Monster* attacker,
const Entity* defender,
int baseDamage,
int turnCount) const
{
// Реализация способности
}
Чтобы добавить нового персонажа, нужно внести его в перечисление и фабрику.
- Чтобы добавить персонажа нужно:
- 1) Внести его в enum class
// source/Class.h
enum class ClassType
{
WARRIOR, // воин
ROGUE, // разбойник
BARBARIAN, // варвар
MAGE // <--- новый класс
};
- 2) Внести его в фабричный метод в файле Class.cpp
CharacterClass CharacterClass::create(ClassType type)
{
switch (type)
{
case ClassType::ROGUE:
return CharacterClass(
type, "Разбойник", 3,
{
{}, // [0] пустой уровень (заглушка)
LevelBonus{4, 0, 0, 0, { std::make_shared<SneakAttackAbility>() }}, // lvl 1
LevelBonus{4, 0, 1, 0, {}}, // lvl 2
LevelBonus{4, 0, 0, 0, { std::make_shared<PoisonAbility>() }} // lvl 3
});
case ClassType::MAGE:
return CharacterClass(
type, "Маг", 3,
{
{}, // [0] пустой уровень (заглушка)
LevelBonus{3, 0, 0, 0, { std::make_shared<FireBallAbility>() }}, // lvl 1
LevelBonus{3, 0, 0, 0, {}}, // lvl 2
LevelBonus{3, 0, 0, 0, { std::make_shared<MagicShieldAbility>() }} // lvl 3
});- 3) Добавить способность в AbilityImpl.h
//------------------------------------------------------------------------------
// Маг: Огненный шар
//------------------------------------------------------------------------------
class FireBallAbility : public Ability
{
public:
std::string name() const override { return "Огненный шар "; }
std::string description() const override { return "+6 к атаке на чётном ходу"; }
int onAttack(const Entity* attacker,
const Monster* defender,
int baseDamage,
int turnCount) const override;
};
//------------------------------------------------------------------------------
// Маг: Магический щит
//------------------------------------------------------------------------------
class MagicShieldAbility : public Ability
{
public:
int onDefense(const Entity* defender,
const Monster* attacker,
int incomingDamage,
int turnCount) const override;
};
- 4) Добавить способность в AbilityImpl.cpp
//------------------------------------------------------------------------------
// Маг: Огненный шар
//------------------------------------------------------------------------------
int FireBallAbility::onAttack(const Entity* attacker,
const Monster* defender,
int baseDamage,
int turnCount) const
{
// Реализация способности
}
//------------------------------------------------------------------------------
// Маг: Магический щит
//------------------------------------------------------------------------------
int MagicShieldAbility::onDefense(const Entity* defender,
const Monster* attacker,
int incomingDamage,
int turnCount) const
{
// Реализация способности
}
Чтобы добавить новое оружие, нужно внести его в перечисление и фабрику.
- Чтобы добавить оружие нужно:
- 1) При необходимости добавить новый тип урона
// source/Weapon.h
enum class WeaponType
{
SLASHING, // рубящий
BLUNT, // дробящий
PIERCING, // колющий
MAGICAL // <--- новый тип урона
};
- 1.2) Также нужно будет добавить новый тип урона в toString
//------------------------------------------------------------------------------
// возвращает строковое описание оружия.
//------------------------------------------------------------------------------
std::string Weapon::toString() const
{
std::ostringstream oss;
oss << name_ << " (урон: " << damage_ << ", тип: ";
switch (type_)
{
case WeaponType::SLASHING:
oss << "рубящее";
break;
case WeaponType::PIERCING:
oss << "колющее";
break;
case WeaponType::BLUNT:
oss << "дробящее";
break;
case WeaponType::MAGICAL:
oss << "Магический"; // <--- новый тип урона
break;
}
oss << ")";
return oss.str();
}
- 2) Внести в фабрику Weapon::create()
Weapon Weapon::create(const std::string& name)
{
if (name == "Кинжал")
return Weapon("Кинжал", 2, WeaponType::PIERCING);
if (name == "Меч")
return Weapon("Меч", 3, WeaponType::SLASHING);
if (name == "Дубина")
return Weapon("Дубина", 3, WeaponType::BLUNT);
if (name == "Топор")
return Weapon("Топор", 4, WeaponType::SLASHING);
if (name == "Копьё")
return Weapon("Копьё", 3, WeaponType::PIERCING);
if (name == "Кулак")
return Weapon("Кулак", 1, WeaponType::BLUNT);
if (name == "Легендарный меч")
return Weapon("Легендарный меч", 10, WeaponType::SLASHING);
if (name == "Посох мага")
return Weapon("Посох мага", 5, WeaponType::MAGICAL);
throw std::invalid_argument("Неизвестное оружие: " + name);
}- 1) Размеры и позиция окон терминала задаются в main.cpp при создании ConsoleBox:
// main.cpp
ConsoleBox mainBox(90, 15, 5, 6);
ConsoleBox leftBox(40, 15, 20, 6);
ConsoleBox rightBox(40, 15, 20, 56);
- 2) Формат конструктора:
ConsoleBox(width, height, top, left);|Аргумент |Описание| |width |ширина окна (в символах)| |height |высота окна| |top |отступ сверху| |left |отступ слева|
- 1) Начальные характеристики задаются при создании персонаж
//Player.cpp
//--------------------------------------------------------------------------
// Генерация случайных характеристик 1 - 3
//--------------------------------------------------------------------------
int strength = Utils::randomInt(1, 3);
int agility = Utils::randomInt(1, 3);
int endurance = Utils::randomInt(1, 3);

