Идея выучить C появилась у меня довольно давно. Я пробовал писать в Arduino IDE, но мне не хватало чего-то более масштабного — такого проекта, где можно наделать кучу ошибок, но при этом видеть результат и двигаться дальше.
Я наткнулся в Play Market на ASCII-RPG под названием Stone Story. Сам формат меня зацепил: минимализм, но при этом ощущение полноценной игры. Поэтому я решил сделать нечто похожее, но со своими механиками.
Мне показалось, что сочетание моего ника и RPG звучит вполне нормально. Так и появилось название MerRPG.
Структуры
Первым делом я сразу решил использовать структуры, а именно:
typedef struct { char Name[15]; int Hp; int Attack; } Player; typedef struct { char Name[15]; int Hp; int min_attack; int max_attack; int type; } Monster; typedef struct { int min_heal; int max_heal; } Heal;
Структура игрока
Структура монстров
Структура лечения
Функции
С этими аспектами мне понравилось рабоать больше всего. Я использовал void функции (за исключением главной функции main)
Разделю на несколько аспектов:
Инициализация игрока и монстров:
void init_player(Player* player) { printf("Send Nickname: "); scanf("%14s", player->Name); player->Hp = 20; }
Сделал ограничение для имени персонажа
Сделал ограничение хп игрока
void init_monster(Monster* monster) { Monster monster_list[3] = { {"Amogus",30,3,6,0}, {"Slime",20,1,3,1}, {"Spider",25,2,4,2} }; int id = rand() %3; *monster = monster_list[id]; }
Добавил несколько монстров
Сделал случайный выбор одного из трех
2. Вывод модельки игрока и монстра:
void print_status(Player player,Monster monster){ printf("\n--- MerRPG ---\n"); if (monster.type == 0) { printf(" 0 (AMOGUS)\n"); printf("/|\\ (00)\n"); printf("/ \\ /__\\\n"); } else if (monster.type == 1) { printf(" 0 (SLIME)\n"); printf("/|\\ (00)\n"); printf("/ \\ /~~~\\\n"); } else if (monster.type == 2) { printf(" 0 (SPIDER)\n"); printf("/|\\ //\\(oo)/\\\\\n"); printf("/ \\ // \\\\\n"); } printf("%s HP: %d\n",player.Name, player.Hp); printf("%s HP: %d\n", monster.Name,monster.Hp); }
Почему здесь я не использовал указатель? Т.к программа лишь выводит значения, но не изменяет их.
3 Атака игрока и монстра:
void player_attack(Player* player,Monster* monster){ int damage_player= rand() % 5 +1; monster ->Hp -= damage_player; printf("You caused %d damage\n",damage_player); if (monster ->Hp < 0) { monster ->Hp =0; } }
На этом моменте я решил отказаться от взятия атаки из структуры игрока и сделал отдельную переменную в самой функции.
void monster_attack(Player* player,Monster* monster){ int damage_monster = rand() % (monster ->max_attack - monster ->min_attack +1) + monster ->min_attack; player ->Hp -= damage_monster; printf("Emeny caused %d damage\n",damage_monster); if (player ->Hp < 0) { player ->Hp =0; } }
По сути две эти функции не отличаются функционалом, однако специально для монстров я сделал и использовал понятия минимального и максимального урона.
4 Функция лечения игрока
void player_heal(Player* player, Heal* heal) { int heal_player = rand() % (heal ->max_heal - heal ->min_heal + 1) + heal->min_heal; player ->Hp += heal_player; printf("You have restored %d Hp\n", heal_player); if (player ->Hp > 20) { player ->Hp = 20; } }
Здесь я добавил ограничение, чтобы лечение не превышало максимальное HP.
5 Самая большая функция - функция старта игры:
void start_game() { Player player; Monster monster; Heal heal = {1,6}; init_player(&player); init_monster(&monster); while(player.Hp > 0 && monster.Hp > 0 ) { print_status(player,monster); printf("[0] - Exit\n"); printf("[1] - Attack\n"); printf("[2] - Healing\n"); scanf("%d", &choice); if (choice == 1) { player_attack(&player,&monster); if ( monster.Hp > 0) { monster_attack(&player,&monster); } } else if (choice == 2) { player_heal(&player,&heal); monster_attack(&player,&monster); } else if (choice == 0) { printf("You are out of the game\n"); break; } else { break; } } if (player.Hp == 0) { printf("You Lose...\n"); } else if (monster.Hp == 0){ printf("You Win!\n"); } printf("Press any button to return to menu\n"); char tmp; getchar(); scanf("%s",&tmp); }
Основной цикл боя
Обработка действий игрока
Проверка победы или поражения
6 Уже в отдельном файле была перенесена функция начального меню игры:
void menu_game(void) { while (1) { printf(" __ __ ____ ____ ____ \n"); printf("| \\/ | ___ _ __| _ \\| _ \\ / ___| \n"); printf("| |\\/| |/ _ \\ '__| |_) | |_) | | _ \n"); printf("| | | | __/ | | _ <| __/| |_| |\n"); printf("|_| |_|\\___|_| |_| \\_\\_| \\____|\n"); printf("=== MerRPG ===\n"); printf("1 - Start game\n"); printf("2 - Exit\n"); scanf("%d", &choice); if (choice == 1) { start_game(); } else if (choice == 2) { break; } } }
В будущем планирую сюда добавить настройки.
7 Ну и последняя, но немало важная функция - главная функция main:
int main() { srand(time(NULL)); menu_game(); return 0; }
srand() в данном случае делает так, что бы при каждом запуске игры:
Монстры
Урон
Лечение
Были случайными.
Костыли:
Хочу вынести парочку интересных для меня костлкй.. А именно:
printf("Press any button to return to menu\n"); char tmp; getchar(); scanf("%s",&tmp); }
Я хотел, чтобы игрок мог нажать любую кнопку для возврата в меню. Но при использовании кириллицы программа начинала вести себя странно и могла зациклиться. Насколько я понял, это связано с тем, что %c читает один байт, а кириллица занимает больше. В итоге я заменил %c на %s.
Еще один костыль в этом же куске... Без getchar() игра просто не даст пользователю нажать на любую кнопку и сразу же перекинет его в главное меню. Поэтому пришлось вручную очищать буфер ;)
Костыль с ограничением количество хп, которое дает лечение:
void start_game() { Player player; Monster monster; Heal heal = {1,6};
Мне не удалось сделать это по аналогии с минимальным и максимальным уроном монстров. Поэтому я нашел более "легкий" способ.
Файлы с расширением .h
Как только мне понадобилось вынести меню в отдельный файл, так сразу этот файл мне помог это сделать.. А точнее 2 файла. Т.к в меню используется данный кусок, вызывающий функцию start_game():
if (choice == 1) { start_game(); }
Нужно было создать два файла .h - game.h и menu.h
#ifndef GAME_H #define GAME_H void start_game(void); #endif
#ifndef MENU_H #define MENU_H void menu_game(void); #endif
В каждом из которых нужно было вызывать требуемые функции.
В целом мне нравится заниматься данными вещами. Игра будет развиваться, будут появляться новые фичи ( и костыли).
Я буду вам очень признателен за помощь в исправлении моих ошибок. Выслушаю любую критику.
