강께르의 개발일지
[C++] 몬스터와 1대3 턴제 전투 게임 본문
1. 이것은 무엇인가?
- 이 게임은 몬스터 3마리와 동시에 전투를 벌이는데 턴제 방식으로 서로의 동작을 처리하여 서로의 체력을 감소시켜 살아남는 게임이다.
- 클래스를 배우고 있는 중인 만큼, 클래스 중심으로 프로그래밍하는 법을 익히는 게임이 될 것이다.
2. 요구 사항
- 플레이어와 몬스터 3마리와 동시에 싸우는 게임이다. 싸우는 방식은 플레이어 - 몬스터1 - 플레이어 - 몬스터2 ... 이런 식으로 싸우게 할 것이다.
- 같은 몬스터 3마리로 하는 것이 아니라 다양한 몬스터가 무작위로 결정될 수 있도록 한다. 이 게임 같은 경우엔 본인이 구상한 몬스터로 하피와 큰턱괴물이라는 이름의 몬스터가 무작위로 결정될 것이다.
- 플레이어와 각 몬스터는 기본기 하나와 스킬 3개로 구성될 것이다. 기본기는 부모 클래스에 구현해 상속해 기능을 공유할 것이고 각 클래스마다 함수를 이용해 고유한 스킬 3개를 가질 것이다.
- 턴제 방식을 계속 주고 받다가 한 쪽의 체력이 모두 바닥이 나면, 게임 오버 관련 함수를 호출하고 게임을 종료할 것이다.
3. 새로 알게 된 점
- 저번에 객체 지향 프로그래밍한다는 것의 단점 중 하나가 객체가 처리하려는 것을 이해하는 데에 오래 걸려서 개발 속도가 느리다고 했다. 과제를 처음 받아봤을 때, 이전에 했던 몬스터와 1대1 턴제 전투 게임을 연상이 되어 매우 빨리 개발할 수 있을 것이라는 생각을 했지만, 개발 중에 가장 많이 느꼈던 것은 하드 코딩하다가 내가 프로그램한테 먹히겠다는 생각이었다. 가이드라인 없이 하드코딩하다가 떠오르는 영감을 가지고 살을 붙이려고 하니까 내가 무엇을 하고 있는지, 하고 있는게 맞는지, 내가 써놓았던 그 기능은 어디에 있는지 찾아다니면서 이해하느라 시간이 많이 갔다. 그래서 이번 프로그래밍 글은 쓰지 말까... 라는 생각을 할 정도로 스스로 생각하기에 스파게티 소스 같은 게임이 완성되었다.
- 그래서 어지간히 머리로 커버하려는 미련한 짓인 하드코딩을 그만두고 처음 시작할 때, 요구사항이나 개발 과정을 간략하게 그려볼 가이드라인을 생각해보는 시간을 가져야겠다.
4. 실행 과정
- 주된 프로그램의 흐름을 담은 함수는 MainGame 클래스의 Update() 함수이다. 그렇기에 Update() 함수에 대한 설명을 주로 하겠다.
- 시작하기에 앞서 MainGame 클래스로부터 객체 mainGame을 생성한다.
- mainGame의 생성으로 생성자가 호출된다. 생성자는 멤버변수에 대한 초기화를 진행하는데 멤버변수로 플레이어와 몬스터의 초기화가 진행된다. 몬스터는 단일 객체가 아닌 객체 배열로 선언되어 있다. 그래서 3번 돌아가는 반복문이 돌아가는 동안 무작위 값으로 일정한 값에 조건이 만족하면 그 만족한 조건에 따라 몬스터를 무작위로 선택해 배열에 저장할 것이다.
- Init()을 통해 콘솔 세팅을 먼저 해준다. 창 크기 조절과 타이틀 설정이 주요 기능이다.
- Update() 함수가 실행되면 시간값으로 랜덤값 시드를 설정하고 플레이어의 생존 여부와 몬스터들의 생존 여부를 bool값으로 저장해 while문의 조건으로 사용한다
- Rander() 함수를 통해 콘솔을 한번 비우고 화면 속 타이틀과 몬스터의 정보, 플레이어의 정보를 간략하게 출력한다.
- RanderAtkMenu() 함수를 호출해 플레이어에게 공격, 스킬 메뉴를 제공하고 입력을 받아 저장한다. 이 때, 메뉴 이외의 값은 허용하지 않는 예외 처리를 포함한다.
- 3번 스킬이 아니면 Rander() 함수를 한번 더 호출해 콘솔을 비워 정보를 출력하고 RanderHitMonsterMenu() 함수를 호출한다. RanderHitMonsterMenu()함수는 RanderAtkMenu()함수와 같은 방식으로 공격할 대상을 메뉴로 제공하고 입력을 받아 저장한다. 체력이 없거나 올바르지 않은 메뉴를 허용하지 않는 예외 처리도 포함한다.
- 다시 Rander()함수를 호출해 콘솔을 비우고 정보를 출력한 다음, 사용자의 입력을 저장한 변수를 조건으로 사용해 그에 따른 함수를 호출한다.
- Character 클래스에 정의된 BasicAttack()함수는 다음과 같이 정의되어 있다. 먼저 회피율 판정에 대한 bool 값을 저장한다. 100 이하의 무작위 값을 정하고 그 값이 공격할 객체의 회피율 변수 값보다 작으면 회피했다고 판단할 것이다.
- 치명타율도 이와 동일하다. 무작위 값을 정하고 객체의 치명타율보다 작으면 치명타가 터졌다고 판단할 것이다.
- 만약 회피하지 않았고, 치명타가 터졌다면 치명타 공격력만큼 공격받은 객체의 체력 변수를 수정한다. 이 때, 직접 접근하는 것이 아닌 Set~()함수를 이용해 수정한다.
- 치명타가 터지지 않을 때도 똑같다. 일반 공격력만큼 체력을 Set함수로 수정한다.
- 회피했다면 어떤 Set함수를 호출하지 않고 문구 출력만 한다.
- 그리고 공격당한 객체가 죽었는지 확인하는 IsDead()함수를 호출한다. 체력이 1 미만이면 멤버변수인 m_isAlive를 false로 대입한다.
- 이와 같은 매커니즘을 가진 함수들이 플레이어의 스킬, 몬스터의 스킬 함수들이다. 회피율에 의해 명중 판정을 하고 치명타율에 의해 치명타 데미지를 줄 것인지 판단하는 것은 모두 똑같다. 아래는 Player 클래스에 선언된 플레이어의 스킬 함수에 대한 짧은 설명이다.
- PowerAttack()함수는 적의 회피율이 올라가서 명중하기 힘들지만 맞추기만 하면 기본 공격력과 치명타 공격력이 두 배로 들어가는 스킬을 구현해놓은 것이다.
- GettingNewMind()함수는 회피율 판정에 의해 회피가 되지 않았다는 판정이 되면 회피율과 치명타율을 올리는 버프 스킬이다. 회피율 판정이기에 회피율이 오르면 더더욱 확률이 떨어지겠지만 무작정 버프만 하는 플레이를 지양하도록 만든 것이다.
- DangerClose()함수는 플레이어의 체력을 깎고 막대한 데미지를 입히는 스킬이다.
- 플레이어의 선택에 따라 함수를 호출하고 나서는 컴퓨터의 순서이다. 컴퓨터는 1~4 사이의 무작위 숫자를 저장한다.
- 그리고 컴퓨터의 순서를 정하는 IsMonsterTurn()함수를 호출한다. 이는 정적변수로 선언된 computerTurnCnt가 있는데 이를 이용해 턴이 진행될 수록 1씩 증가시켜 Monster 객체 배열의 인덱스로 활용해 차례대로 몬스터가 순서가 돌아와 공격하는 시스템이 되도록 구현을 했다.
- 하지만 체력이 다한 몬스터는 공격하지 않도록 IsMonsterTurn()함수 내에 while문을 이용해 죽은 몬스터가 있는지 확인한다. 만약 있으면 computerTurnCnt 값을 증가시켜 다른 인덱스를 가리키도록 한다. computerTurnCnt가 배열 인덱스 이상의 값을 가지지 않도록 나머지 연산자로 최대 값 제한을 걸어둔다. 만약 죽은 몬스터의 수가 3이 된다면 무한 루프에 빠질 수 있으니 break문으로 나올 수 있도록 한다.
- 컴퓨터가 정한 무작위값에 의해 조건문을 실행된다. 조건문에 의해 몬스터 스킬 함수가 실행될 것이다. 몬스터의 종류에 따라 다른 함수를 호출해야하는데 그것은 Monster 객체 내 멤버변수로 있는 m_name의 문자열을 비교하여 알맞은 함수를 호출할 수 있도록 도울 것이다.
- 다음은 Monster 객체 내에 구현된 스킬 함수에 대한 내용이다.
- HarpySkill_1()함수는 3연속으로 기본 공격력보다 약한 공격을 가하는 스킬이다.
- HarpySkill_2()함수는 깃털을 날려 기본 공격력만큼 공격을 가하며 회피율을 떨어뜨리는 디버프형 스킬이다.
- HarpySkill_3()함수는 깃털을 정리하여 치명타율을 올리게 하는 버프형 스킬이다.
- BigJawSkill_1()함수는 잠을 자서 체력을 회복하는 회복스킬이다.
- BigJawSkill_2()함수는 모든 체력을 소모해서 큰 피해를 주는 자폭 스킬이다.
- BigJawSkill_3()함수는 턱의 힘을 꽉 쥐어서 공격력을 증가시키는 버프 스킬이다.
- 컴퓨터의 순서를 마치고나서 몬스터 혹은 플레이어가 살아있는지 확인하고 중괄호를 만난다.
- 이후로 조건이 불만족하지 않는 이상, 계속 반복문을 수행한다.
- 조건이 불만족한다면 Rander()함수로 콘솔을 지우고 정보를 출력한 다음, RanderGameOver()함수로 종료 출력문을 보여주고 끝낸다.
5. 코드
5-1. Header.h
#ifndef __HEADER_H__
#define __HEADER_H__
#include<iostream>
#include<ctime>
#include<Windows.h>
#include<conio.h>
using namespace std;
enum class SKILL
{
BASIC = 1,
SKILL_1,
SKILL_2,
SKILL_3
};
#endif
5-2. Character.h
#ifndef __CHARA_H__
#define __CHARA_H__
#include"Header.h"
class Character
{
private:
int m_healthPoint;
int m_manaPoint;
int m_attackPoint;
int m_criticalRate;
int m_criticalDamage;
int m_evadeRate;
bool m_isAlive;
string m_name;
public:
Character(int hp, int mp, int atk, int criRate, int criDmg, int evdRate, const char* name)
: m_healthPoint(hp), m_manaPoint(mp), m_attackPoint(atk),
m_criticalRate(criRate), m_criticalDamage(criDmg), m_evadeRate(evdRate),
m_name(name), m_isAlive(true)
{ }
~Character() { }
int GetHealthPoint() { return m_healthPoint; }
int GetManaPoint() { return m_manaPoint; }
int GetEvadeRate() { return m_evadeRate; }
int GetCriticalDmg() { return m_criticalDamage; }
int GetCriticalRate() { return m_criticalRate; }
int GetAttackPoint() { return m_attackPoint; }
string GetName() { return m_name; }
bool GetIsAlive() { return m_isAlive; }
void SetHealthPoint(int hp) { m_healthPoint = hp; }
void SetManaPoint(int mp) { m_manaPoint = mp; }
void SetAttackPoint(int atk) { m_attackPoint = atk; }
void SetCriticalRate(int criRate) { m_criticalRate = criRate; }
void SetEvadeRate(int evdRate) { m_evadeRate = evdRate; }
void BasicAttack(Character&);
void IsDead();
};
#endif
5-3. Character.cpp
#include "Character.h"
void Character::BasicAttack(Character& enemy)
{
int enemyHP = enemy.GetHealthPoint();
bool isEvd = (rand() % 100) < enemy.GetEvadeRate();
cout << m_name << "이/가 ";
if (!isEvd)
{
bool isCri = (rand() % 100) < m_criticalRate;
if (isCri)
{
if (m_criticalDamage > enemyHP)
enemyHP = 0;
else
enemyHP -= m_criticalDamage;
cout << enemy.GetName() << "에게 " << m_criticalDamage <<
"만큼 치명적인 피해를 줬습니다." << endl;
}
else
{
if (m_attackPoint > enemyHP)
enemyHP = 0;
else
enemyHP -= m_attackPoint;
cout << enemy.GetName() << "에게 " << m_attackPoint <<
"만큼 피해를 줬습니다." << endl;
}
}
else
cout << enemy.GetName() << "에게 공격하려 했으나, 실패했습니다." << endl;
enemy.SetHealthPoint(enemyHP);
enemy.IsDead();
Sleep(1000);
}
void Character::IsDead()
{
if (this->GetHealthPoint() < 1)
m_isAlive = false;
}
5-4. Player.h
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include"Character.h"
class Player : public Character
{
private:
const string m_skill[4] = { "베기", "강타", "심기일전", "동귀어진" };
public:
Player(int hp, int mp, int atk, int criRate, int criDmg, int evdRate, const char* name)
: Character(hp, mp, atk, criRate, criDmg, evdRate, name)
{ }
const string& GetSkill(int idx) { return m_skill[idx]; }
void PowerAttack(Character&);
void GettingNewMind();
void DangerClose(Character&);
};
#endif
5-5. Player.cpp
#include "Player.h"
void Player::PowerAttack(Character& enemy)
{
int enemyHP = enemy.GetHealthPoint();
bool isEvd = (rand() % 100) < (enemy.GetEvadeRate() + 10);
cout << GetName() << "이/가 마나 5를 소모하여 ";
if (!isEvd)
{
bool isCri = (rand() % 100) < GetCriticalRate();
if (isCri)
{
if (GetCriticalDmg() * 2 > enemyHP)
enemyHP = 0;
else
enemyHP -= GetCriticalDmg() * 2;
cout << enemy.GetName() << "에게 " << GetCriticalDmg() * 2 <<
"만큼 치명적인 강타를 때렸습니다." << endl;
}
else
{
if (GetAttackPoint() * 2 > enemyHP)
enemyHP = 0;
else
enemyHP -= GetAttackPoint() * 2;
cout << enemy.GetName() << "에게 " << GetAttackPoint() * 2 <<
"만큼 강타를 했습니다." << endl;
}
}
else
cout << enemy.GetName() << "에게 공격하려 했으나, 실패했습니다." << endl;
enemy.SetHealthPoint(enemyHP);
SetManaPoint(GetManaPoint() - 5);
enemy.IsDead();
Sleep(1000);
}
void Player::GettingNewMind()
{
bool isEvd = (rand() % 100) < (GetEvadeRate());
const int increaseStat = 5;
cout << GetName() << "이/가 마나 5를 소모하여 ";
if (!isEvd)
{
SetCriticalRate(GetCriticalRate() + increaseStat);
SetEvadeRate(GetEvadeRate() + increaseStat);
cout << "스스로의 마음을 가다듬어 " << increaseStat <<
"만큼 치명타율과 회피율이 증가했습니다." << endl;
}
else
cout << "마음을 가다듬으려 했으나, 실패했습니다." << endl;
SetManaPoint(GetManaPoint() - 5);
Sleep(1000);
}
void Player::DangerClose(Character& enemy)
{
int enemyHP = enemy.GetHealthPoint();
bool isEvd = (rand() % 100) < (enemy.GetEvadeRate() + 30);
cout << GetName() << "이/가 마나 10과 체력 10을 소모하여 ";
if (!isEvd)
{
bool isCri = (rand() % 100) < GetCriticalRate();
if (isCri)
{
if (GetCriticalDmg() * 4 > enemyHP)
enemyHP = 0;
else
enemyHP -= GetCriticalDmg() * 4;
cout << enemy.GetName() << "에게 " << GetCriticalDmg() * 4 <<
"만큼 치명적인 동귀어진을 가했습니다." << endl;
}
else
{
if (GetAttackPoint() * 4 > enemyHP)
enemyHP = 0;
else
enemyHP -= GetAttackPoint() * 4;
cout << enemy.GetName() << "에게 " << GetAttackPoint() * 4 <<
"만큼 동귀어진을 가했습니다." << endl;
}
}
else
cout << enemy.GetName() << "에게 공격하려 했으나, 실패했습니다." << endl;
enemy.SetHealthPoint(enemyHP);
SetHealthPoint(GetHealthPoint() - 10);
SetManaPoint(GetManaPoint() - 5);
enemy.IsDead();
Sleep(1000);
}
5-6. Monster.h
#ifndef __MONSTER_H__
#define __MONSTER_H__
#include"Character.h"
class Monster : public Character
{
private:
public:
Monster(int hp, int mp, int atk, int criRate, int criDmg, int evdRate, const char* name)
: Character(hp, mp, atk, criRate, criDmg, evdRate, name)
{ }
void HarpySkill_1(Character&);
void HarpySkill_2(Character&);
void HarpySkill_3();
void BigJawSkill_1();
void BigJawSkill_2(Character&);
void BigJawSkill_3();
};
#endif
5-7. Monster.cpp
#include "Monster.h"
void Monster::HarpySkill_1(Character& enemy)
{
int enemyHP = enemy.GetHealthPoint();
cout << GetName() << "이/가 마나 5를 소모하여 ";
for (int i = 0; i < 3; i++)
{
bool isEvd = (rand() % 100) < (enemy.GetEvadeRate());
if (!isEvd)
{
if (GetAttackPoint() > enemyHP)
enemyHP = 0;
else
enemyHP -= GetAttackPoint();
cout << enemy.GetName() << "에게 " << GetAttackPoint() / 2 <<
"만큼 난도질을 가했습니다." << endl;
}
else
cout << enemy.GetName() << "에게 난도질하려 했으나, 실패했습니다." << endl;
Sleep(1500);
}
enemy.SetHealthPoint(enemyHP);
SetManaPoint(GetManaPoint() - 5);
enemy.IsDead();
Sleep(1000);
}
void Monster::HarpySkill_2(Character& enemy)
{
int enemyHP = enemy.GetHealthPoint();
int enemyEvd = enemy.GetEvadeRate();
cout << GetName() << "이/가 마나 5를 소모하여 ";
for (int i = 0; i < 3; i++)
{
bool isEvd = (rand() % 100) < (enemy.GetEvadeRate() + 5);
if (!isEvd)
{
bool isCri = (rand() % 100) < GetCriticalRate();
if (isCri)
{
if (GetCriticalDmg() * 2 > enemyHP)
enemyHP = 0;
else
{
enemyHP -= GetCriticalDmg();
enemyEvd -= GetCriticalDmg();
}
cout << enemy.GetName() << "에게 " << GetCriticalDmg() <<
"만큼 치명적인 깃털을 날려 상처를 입히고 " <<
enemyEvd << "%로 떨어졌습니다." << endl;
}
else
{
if (GetAttackPoint() > enemyHP)
enemyHP = 0;
else {
enemyHP -= GetAttackPoint();
enemyEvd -= GetAttackPoint();
}
cout << enemy.GetName() << "에게 " << GetAttackPoint() <<
"만큼 깃털을 날려 상처를 입혔습니다." <<
enemyEvd << "%로 떨어졌습니다." << endl;
}
}
else
cout << enemy.GetName() << "에게 깃털을 날리려 했으나, 실패했습니다." << endl;
}
enemy.SetHealthPoint(enemyHP);
enemy.SetEvadeRate(enemyEvd);
SetManaPoint(GetManaPoint() - 5);
enemy.IsDead();
Sleep(1500);
}
void Monster::HarpySkill_3()
{
bool isEvd = (rand() % 100) < (GetEvadeRate());
const int increaseStat = 5;
cout << GetName() << "이/가 마나 5를 소모하여 ";
if (!isEvd)
{
SetCriticalRate(GetCriticalRate() + increaseStat);
cout << "깃털을 정리하여 " << increaseStat <<
"만큼 치명타율이 증가했습니다." << endl;
}
else
cout << "깃털을 정리하려 했으나, 실패했습니다." << endl;
SetManaPoint(GetManaPoint() - 5);
Sleep(1500);
}
void Monster::BigJawSkill_1()
{
bool isEvd = (rand() % 100) < (GetEvadeRate());
const int increaseStat = 5;
cout << GetName() << "이/가 마나 5를 소모하여 ";
if (!isEvd)
{
SetHealthPoint(GetHealthPoint() + increaseStat);
cout << "꿀잠을 자서 " << increaseStat <<
"만큼 체력이 회복했습니다." << endl;
}
else
cout << "잠을 잤으나, 아무 일도 일어나지 않았습니다." << endl;
SetManaPoint(GetManaPoint() - 5);
Sleep(1500);
}
void Monster::BigJawSkill_2(Character& enemy)
{
int enemyHP = enemy.GetHealthPoint();
bool isEvd = (rand() % 100) < (enemy.GetEvadeRate() + 60);
cout << GetName() << "이/가 모든 체력을 소모하여 ";
if (!isEvd)
{
if (GetAttackPoint() + 50 > enemyHP)
enemyHP = 0;
else
enemyHP -= GetAttackPoint() + 50;
cout << enemy.GetName() << "에게 " << GetAttackPoint() + 50 <<
"만큼 자폭 공격을 가했습니다" << endl;
}
else
cout << enemy.GetName() << "에게 자폭해보려 했으나, 실패했습니다." << endl;
enemy.SetHealthPoint(enemyHP);
SetHealthPoint(0);
enemy.IsDead();
Sleep(1500);
}
void Monster::BigJawSkill_3()
{
bool isEvd = (rand() % 100) < (GetEvadeRate());
const int increaseStat = 5;
cout << GetName() << "이/가 마나 5를 소모하여 ";
if (!isEvd)
{
SetAttackPoint(GetAttackPoint() + increaseStat);
cout << "큰 턱의 힘을 힘껏 쥐며 " << increaseStat <<
"만큼 공격력이 증가했습니다." << endl;
}
else
cout << "턱의 힘을 쥐어봤지만, 실패했습니다." << endl;
SetManaPoint(GetManaPoint() - 5);
Sleep(1500);
}
5-8. MainGame.h
#ifndef __MAINGAME_H__
#define __MAINGAME_H__
#include"Monster.h"
#include"Player.h"
class MainGame
{
private:
Player* player;
Monster* monster[3];
int m_key;
public:
MainGame();
~MainGame();
void Init();
void Update();
void Rander();
void RanderAtkMenu();
void RanderHitMonsterMenu();
void RanderGameover(bool);
void IsMonsterTurn(int&);
};
#endif
5-9. MainGame.cpp
#include "MainGame.h"
MainGame::MainGame() : m_key(0)
{
player = new Player(50, 30, 15, 10, 30, 10, "전사");
int randNum;
srand((unsigned int)time(NULL));
for (int i = 0; i < 3; i++)
{
randNum = rand() % 100;
if(randNum < 50)
monster[i] = new Monster(30, 20, 2, 10, 10, 10, "하피");
else
monster[i] = new Monster(50, 20, 4, 10, 10, 10, "큰턱괴물");
}
}
MainGame::~MainGame()
{
delete player;
for(int i = 0; i < 3; i++)
delete monster[i];
}
void MainGame::Init()
{
system("mode con:cols=47 lines=30");
system("title Player VS Three Monsters");
}
void MainGame::Update()
{
srand((unsigned int)time(NULL));
int atkCommand, computerCommandNum;
static int computerTurnCnt = 0;
bool isAllMonsterAlive = monster[0]->GetIsAlive() == true ||
monster[1]->GetIsAlive() == true ||
monster[2]->GetIsAlive() == true;
bool isPlayerAlive = player->GetIsAlive() == true;
while (isAllMonsterAlive && isPlayerAlive)
{
Rander();
RanderAtkMenu();
atkCommand = m_key;
if (atkCommand != 3)
{
Rander();
RanderHitMonsterMenu();
}
Rander();
switch ((SKILL)atkCommand)
{
case SKILL::BASIC:
player->BasicAttack(*monster[m_key - 1]);
break;
case SKILL::SKILL_1:
player->PowerAttack(*monster[m_key - 1]);
break;
case SKILL::SKILL_2:
player->GettingNewMind();
break;
case SKILL::SKILL_3:
player->DangerClose(*monster[m_key - 1]);
break;
}
computerCommandNum = rand() % 4 + 1;
IsMonsterTurn(computerTurnCnt);
switch ((SKILL)computerCommandNum)
{
case SKILL::BASIC:
cout << computerTurnCnt + 1 << "번째 몬스터인 ";
monster[computerTurnCnt]->BasicAttack(*player);
break;
case SKILL::SKILL_1:
cout << computerTurnCnt + 1 << "번째 몬스터인 ";
if (monster[computerTurnCnt]->GetName() == "하피")
monster[computerTurnCnt]->HarpySkill_1(*player);
else if (monster[computerTurnCnt]->GetName() == "큰턱괴물")
monster[computerTurnCnt]->BigJawSkill_1();
break;
case SKILL::SKILL_2:
cout << computerTurnCnt + 1 << "번째 몬스터인 ";
if (monster[computerTurnCnt]->GetName() == "하피")
monster[computerTurnCnt]->HarpySkill_2(*player);
else if (monster[computerTurnCnt]->GetName() == "큰턱괴물")
monster[computerTurnCnt]->BigJawSkill_2(*player);
break;
case SKILL::SKILL_3:
cout << computerTurnCnt + 1 << "번째 몬스터인 ";
if (monster[computerTurnCnt]->GetName() == "하피")
monster[computerTurnCnt]->HarpySkill_3();
else if (monster[computerTurnCnt]->GetName() == "큰턱괴물")
monster[computerTurnCnt]->BigJawSkill_3();
break;
}
computerTurnCnt = (computerTurnCnt + 1) % 3;
isAllMonsterAlive = monster[0]->GetIsAlive() == true ||
monster[1]->GetIsAlive() == true ||
monster[2]->GetIsAlive() == true;
isPlayerAlive = player->GetIsAlive() == true;
}
Rander();
RanderGameover(isAllMonsterAlive);
}
void MainGame::Rander()
{
system("cls");
cout << "==============================================" << endl;
cout << "==T==H==R==E==E=======M==O==N==S==T==E==R==S==" << endl;
cout << "==============================================" << endl;
for (int i = 0; i < 3; i++)
{
cout << i + 1 << "번째 몬스터" << endl;
cout << "이름 : " << monster[i]->GetName() << endl;
cout << "HP : " << monster[i]->GetHealthPoint() << endl;
cout << "----------------------------------------------" << endl;
}
cout << "이름 : " << player->GetName() << endl;
cout << "HP : " << player->GetHealthPoint() << endl;
cout << "MP : " << player->GetManaPoint() << endl;
cout << "----------------------------------------------" << endl;
}
void MainGame::RanderAtkMenu()
{
cout << player->GetName() << "여, 당신의 행동을 선택하라." << endl;
for (int i = 0; i < 4; i++)
{
cout << i + 1 << "." << player->GetSkill(i);
if (i < 3)
cout << " ";
}
cout << endl << "입력 : ";
cin >> m_key;
while (m_key < 0 || m_key > 4)
{
Rander();
cout << player->GetName() << "여, 다시 선택하라." << endl;
for (int i = 0; i < 4; i++)
{
cout << i + 1 << "." << player->GetSkill(i);
if (i < 3)
cout << " ";
}
cout << endl << "입력 : ";
cin >> m_key;
}
cin.get();
}
void MainGame::RanderHitMonsterMenu()
{
cout << player->GetName() << "여, 누구를 공격할 건가." << endl;
for (int i = 0; i < 3; i++)
{
cout << i + 1 << "." << monster[i]->GetName();
if (i < 2)
cout << " ";
}
cout << endl << "입력 : ";
cin >> m_key;
bool isAlive = monster[m_key - 1]->GetIsAlive() == false;
bool isRightKey = (m_key < 0) || (m_key > 3);
while (isAlive || isRightKey)
{
Rander();
cout << player->GetName() << "여, 다시 선택하라." << endl;
for (int i = 0; i < 3; i++)
{
cout << i + 1 << "." << monster[i]->GetName();
if (i < 2)
cout << " ";
}
cout << endl << "입력 : ";
cin >> m_key;
isAlive = monster[m_key - 1]->GetIsAlive() == false;
isRightKey = (m_key < 0) || (m_key > 3);
}
cin.get();
}
void MainGame::RanderGameover(bool isAllMonsterAlive)
{
cout << player->GetName();
if (isAllMonsterAlive == true)
cout << "은/는 몬스터에게 패배했습니다." << endl;
else
cout << "은/는 몬스터를 모두 죽였습니다" << endl;
}
void MainGame::IsMonsterTurn(int& cnt)
{
int monsterDeathCnt = 0;
while (monster[cnt]->GetIsAlive() == false)
{
cnt++;
cnt = cnt % 3;
monsterDeathCnt++;
if (monsterDeathCnt == 3)
break;
}
}
5-10. Main.cpp
#include"MainGame.h"
int main(void)
{
MainGame mainGame;
mainGame.Init();
mainGame.Update();
}
6. 결과
7. 아쉬운 점
- 스파게티 소스인 듯한 코드인 점, 돌릴 때마다 하자 있는 게임이라는 것을 다 작성하고 나서 알게 된 점...
'연습' 카테고리의 다른 글
[C++] 상점 기능 구현 (1) | 2021.06.22 |
---|---|
[C++] 월남뽕 게임 (0) | 2021.06.16 |
[C++] 슬라이드 퍼즐 게임 (0) | 2021.06.14 |
[C++] 빙고 게임_미완성 (0) | 2021.06.13 |
[C++] 몬스터와 일대일 턴제 전투 게임_구조체로 수정 (0) | 2021.06.12 |