본문 바로가기

숙제

C++ 2번 과제(전직 시스템과 전투 시스템)

필수 기능

  • Player라는 기본 클래스를 정의 합니다.
    • Player 클래스에는 attack()이라는 순수 가상 함수를 포함합니다.
      • attack() 함수는 무기를 휘두르기만 하는 기본적인 공격 함수입니다.
  • Player 클래스를 상속받아 다양한 직업 클래스를 생성합니다.
    • 예) Warrior, Magician, Thief, Archer
      • 각 직업 클래스에서 attack()함수를 재정의(오버라이딩)하여 해당 직업의 공격을 출력하면 됩니다!
  • 메인 함수에서 Player 타입의 포인터 배열을 선언하고, 해당하는 번호를 입력하면 (1. 전사 2. 마법사 3. 도적 4. 궁수) Warrior, Magician, Thief, Archer를 각각 배열의 원소로 선언합니다. → 이후 Player는 attack() 함수를 호출하여 공격합니다.
  • 전체적인 구조는 아래와 같습니다.

도전 기능

필수 기능 가이드에 있는 요구사항을 만족하는 코드를 구현했다면, 아래 코드스니펫을 보고 요구사항대로 Monster클래스를 정의해주세요!

class Monster {
public:
		// Monster 생성자
		// - 몬스터의 이름을 매개변수로 입력 받습니다.
		// - 모든 몬스터는 HP 10, 공격력 30, 방어력 10, 스피드 10의 능력치를 가집니다.
    Monster(string name);
    
    // 몬스터의 공격 함수
    // - 플레이어 객체의 포인터를 매개변수로 입력 받습니다.
    // - 몬스터의 공격력-플레이어의 방어력을 데미지로 정의합니다.
    // - 만약 위에서 계산한 데미지가 0 이하라면, 데미지를 1로 정의합니다.
    // - 플레이어에게 얼마나 데미지를 입혔는지 출력합니다.
    // - setHP 함수를 실행하여 HP-데미지 계산 결과를 매개변수로 전달합니다.
    // - setHP에서 리턴 받은 생존 여부를 기준으로 분기문이 실행됩니다.
    // - 생존했을 경우, 플레이어의 남은 HP만 출력합니다.
    // - 생존하지 못했을 경우, 플레이어의 남은 HP와 몬스터의 승리 문구를 출력합니다.
    void attack(Player* player);
    
    // 몬스터의 속성값을 리턴하는 get 함수
    string getName();
    int getHP();
    int getPower();
    int getDefence();
    int getSpeed();
		
		// 몬스터의 속성값을 정의하는 set 함수
		// setHP의 경우 배틀 시스템에 직결되는 함수이므로, 생존 여부를 리턴합니다.
		// HP가 1 이상일 때 true, HP가 0 이하가 되었을 때 false를 리턴합니다.
		// HP가 1 이상일 때만 새로운 HP값 정의를 진행합니다.
		// 0 이하일 경우 HP를 0으로 정의합니다.
    void setName(string name);
    bool setHP(int HP);
    void setPower(int power);
    void setDefence(int defence);
    void setSpeed(int speed);

protected:
    string name; // 몬스터의 이름
    int HP; // 몬스터의 HP
    int power; // 몬스터의 공격력
    int defence; // 몬스터의 방어력
    int speed; // 몬스터의 스피드
};

Player의 attack() 함수를 오버로딩하여 attack(Monster* monster) 함수를 구현하세요.

자세한 사항은 아래 코드 스니펫을 참조하시면 됩니다!

class Monster;

class Player {
public:
    Player(string nickname);

    virtual void attack() = 0;
    virtual void attack(Monster* monster) = 0;
    void printPlayerStatus();

    // getter
    // ...
    
    // setter(setHP, setMP는 리턴 타입 bool로 설정)
    // ...

protected:
    // attributes
};

void JobName::attack(Monster* monster) {
    // 플레이어의 공격력-몬스터의 방어력을 계산하여 데미지로 정의합니다.
		// 계산된 데미지가 0 이하일 경우, 데미지를 1로 정의합니다.
		
		// 공격 문장을 출력합니다.
		// - 전사, 마법사: 계산된 데미지로 1회 공격
		// - 궁수: 계산된 데미지/3으로 3회 공격 (소수점 생략)
		// - 도적: 계산된 데미지/5으로 5회 공격 (소수점 생략)
		// - 궁수와 도적의 경우, 3과 5로 나눈 결과가 0이라면 1로 정의합니다.
		// ex) * 슬라임에게 화살로 3의 피해를 입혔다! 3번 출력
		
		// setHP를 호출하여 몬스터의 HP-데미지를 계산한 값을 매개변수로 전달합니다.
		// 리턴된 생존 여부를 기준으로 분기문이 실행됩니다.
		// 생존했을 경우 몬스터의 남은 HP 출력
		// 생존하지 못했을 경우 몬스터의 남은 HP와 플레이어의 승리 문구 출력
}

필수 기능

Player

// Player.h

#pragma once
#include <iostream>
#include <string>

class Player
{
protected:
	//필요한 캐릭터의 속성들을 변수 선언한다.
	std::string job_name;
	std::string nickname;
	int Level;
	int HP;
	int MP;
	int Power;
	int Defence;
	int Accuracy;
	int Speed;

public:

	virtual void initStats() = 0; // 직업별 초기 스탯을 위한 순수 가상 함수
	virtual void attack() = 0; // 직업별 공격을 위한 순수 가상 함수

	void printPlayerStatus();
	void setjob_name(std::string job);
	void setnickname(std::string name);
	void setHP(int hp);
	void setMP(int mp);
	void setpower(int power);
	void setdefence(int defence);
	void setaccuracy(int accuracy);
	void setspeed(int speed);
	void setlevel(int level);
};
// Player.cpp

#include "Player.h"

using namespace std;

void Player::printPlayerStatus()
{
    cout << "------------------------------------" << endl;
    cout << "* 현재 능력치" << endl;
    cout << "닉네임: " << nickname << endl;
    cout << "직업: " << job_name << endl;
    cout << "Lv. " << Level << endl;
    cout << "HP: " << HP << endl;
    cout << "MP: " << MP << endl;
    cout << "공격력: " << Power << endl;
    cout << "방어력: " << Defence << endl;
    cout << "정확도: " << Accuracy << endl;
    cout << "속도: " << Speed << endl;
    cout << "------------------------------------" << endl;
}
void Player::setjob_name(string job)
{
    job_name = job;
}
void Player::setnickname(string name)
{
    nickname = name;
}
void Player::setHP(int hp)
{
    HP = hp;
}
void Player::setMP(int mp)
{
    MP = mp;
}
void Player::setpower(int power)
{
    Power = power;
}
void Player::setdefence(int defence)
{
    Defence = defence;
}
void Player::setaccuracy(int accuracy)
{
    Accuracy = accuracy;
}
void Player::setspeed(int speed)
{
    Speed = speed;
}
void Player::setlevel(int level)
{
    Level = level;
}

Warrior, Archer, Magician, Thief

//Warrior.h

#pragma once
#include "Player.h"

class Warrior:public Player
{
public:

	virtual void attack() override;
	virtual void initStats() override;
};
//Warrior.cpp

#include "Warrior.h"

using namespace std;

void Warrior::initStats() // 직업별 초기 스탯 설정
{
    setjob_name("전사");
    setlevel(1);
    setnickname(nickname); // nickname은 main에서 입력받음
    setHP(300);
    setMP(50);
    setpower(70);
    setdefence(50);
    setaccuracy(60);
    setspeed(50);
}
void Warrior::attack()
{
	cout << "Slash attack!";
}

Archer, Magician, Thief의 헤더파일에 클래스명만 다르게했음.
소스파일에서는 초기 스탯을 다 다르게 설정하고 attack함수의 출력 메세지만 변경함.

main

//main.cpp

#include "Player.h"
#include "Warrior.h"
#include "Magician.h"
#include "Thief.h"
#include "Archer.h"

using namespace std;
// 메인 함수
int main()
{
	Warrior wa;
	Archer ar;
	Magician ma;
	Thief th;

	Player* player[4] = {&wa, &ar, &ma, &th}; // Player 타입의 포인터 배열 선언

	Player* job = nullptr;

	int job_choice;
	string name;

	cout << "이름을 입력하세요." << endl;
	cin >> name;

	cout << "1.전사 2.궁수 3.마법사 4.도적" << endl << "직업 선택: ";

	cin >> job_choice;
	
	if (job_choice >= 1 && job_choice <= 4)
	{
		job = player[job_choice - 1];
	}
	else
	{
		cout << "잘못된 입력입니다.";
		return 0;
	}
    
	

	if (job != nullptr)
	{
    		job->setnickname(name); // nickname에 입력한 name값 넣어주기
    		job->initStats(); // 직업이 정해졌으니 초기 스탯 넣어주기
    		job->printPlayerStatus(); // 현재 직업 상태 메세지 출력
    		job->attack();
	}
}

결과


도전 기능

필수 기능에서 작성했던 코드들을 도전 기능을 작성하다보니 좀 많이 수정이 됐다.(10시간 걸렸음..)

Player

// Player.h

#pragma once
#include <iostream>
#include <string>

class Player
{
protected:

	std::string job_name;
	std::string nickname;
	int Level;
	int HP;
	int MP;
	int Power;
	int Defence;
	int Accuracy;
	int Speed;
	int Damage; // 데미지 추가됨

public:

	virtual void initStats() {}; // 순수 가상 함수에서 가상 함수로 수정했음.
	virtual void attack() = 0;
    virtual void givedamage(Player* player) {}; // damage를 주기 위한 매개변수 있는 가상 함수 선언
	
    //게터 함수들이 추가됨.
	int getpower();
	int getdefence();
	int getHP();
	int getdamage();
	std::string getname();

	virtual void printPlayerStatus();
    
	virtual void setjob_name(std::string job);
	virtual void setnickname(std::string name);
	virtual bool setHP(int hp); // setHP함수가 요구사항에서 bool로 하라해서 변경함
	virtual void setHP2(int hp); // 원래 setHP의 기능이 setHP2로 이름만 바꿈
	virtual void setMP(int mp);
	virtual void setpower(int power);
	virtual void setdefence(int defence);
	virtual void setaccuracy(int accuracy);
	virtual void setspeed(int speed);
	virtual void setlevel(int level);

	
};

Player.h의 특이점은 initStats가 원래 순수 가상 함수였는데 현재 직업, 몬스터 클래스가 Player를 상속받는 클래스다 보니 하나라도 initStats를 구현하지 않으면 빌드 오류가 발생 하여 일반 가상 함수로 변경했음.

// Player.cpp

#include "Player.h"

using namespace std;

void Player::printPlayerStatus()
{
    cout << "------------------------------------" << endl;
    cout << "* 현재 능력치" << endl;
    cout << "닉네임: " << nickname << endl;
    cout << "직업: " << job_name << endl;
    cout << "Lv. " << Level << endl;
    cout << "HP: " << HP << endl;
    cout << "MP: " << MP << endl;
    cout << "공격력: " << Power << endl;
    cout << "방어력: " << Defence << endl;
    cout << "정확도: " << Accuracy << endl;
    cout << "속도: " << Speed << endl;
    cout << "------------------------------------" << endl;
}

void Player::setjob_name(string job)
{
    job_name = job;
}

void Player::setnickname(string name)
{
    nickname = name;
}

bool Player::setHP(int hp) // setHP 함수가 bool로 변경
{
    return hp > 0;
}

void Player::setMP(int mp)
{
    MP = mp;
}

void Player::setpower(int power)
{
    Power = power;
}

void Player::setdefence(int defence)
{
    Defence = defence;
}

void Player::setaccuracy(int accuracy)
{
    Accuracy = accuracy;
}

void Player::setspeed(int speed)
{
    Speed = speed;
}

void Player::setlevel(int level)
{
    Level = level;
}
// 아래부터 도전 기능에서 추가됨.
int Player::getdefence()
{
    return Defence;
}

int Player::getpower()
{
    return Power;
}

int Player::getHP()
{
    return HP;
}

void Player::setHP2(int hp)
{
    HP = hp;
}

int Player::getdamage()
{
    return Damage;
}

string Player::getname()
{
    return nickname;
}

Player.cpp에서는 게터 함수가 좀 추가되고 setHP가 bool로 변하고 setHP2에 원래 setHP의 기능을 똑같이 구현했다.

각 직업(Warrior, Archer, Magician, Thief)

// Warrior.h

#pragma once
#include "Player.h"

class Warrior:public Player
{
public:

	virtual void attack() override; // Player의 attack 함수를 재정의
	virtual void initStats() override; // Player의 initStats 함수를 재정의
	virtual void givedamage(Player* player) override;
};

각 직업의 헤더파일은 클래스 명만 다르고 다른건 다 똑같다.

// Warrior.cpp

#include "Warrior.h"

using namespace std;

void Warrior::initStats()
{
    setjob_name("전사");
    setlevel(1);
    setnickname(nickname);
    setHP2(50);
    setMP(50);
    setpower(50);
    setdefence(20);
    setaccuracy(5);
    setspeed(5);
}
void Warrior::attack()
{
    cout << "Slash attack!" << endl;
    cout << getdamage() << "의 피해를 주었다." << endl;
}

void Warrior::givedamage(Player* player) 
{ // 플레이어의 Power에서 몬스터의 defence를 빼서 Damage에 값을 대입함.
    Damage = Power - player->getdefence();

    if (Damage <= 0) // 만약 Damage가 0이하라면 1로 설정함.
    {
        Damage = 1;
    }
}

소스파일은 전사, 마법사는 거의 비슷하지만 궁수, 도적은 공격 횟수를 추가하여 다르게 구현했다.

// Archer.cpp

#include "Archer.h"

using namespace std;

void Archer::initStats()
{
    setjob_name("궁수");
    setlevel(1);
    setnickname(nickname);
    setHP2(100);
    setMP(80);
    setpower(45);
    setdefence(5);
    setaccuracy(90);
    setspeed(25);
}

void Archer::attack()
{
	cout << "Arrow attack!";
    	cout << getdamage() << "의 피해를 주었다." << endl;
}

void Archer::givedamage(Player* player)
{ // 궁수는 공격 횟수 3번으로 데미지 나누기 3을함
    Damage = (Power - player->getdefence()) / 3; 

    if (Damage <= 0)
    {
        Damage = 1;
    }
}
// Thief.cpp

#include "Thief.h"

using namespace std;

void Thief::initStats()
{
    setjob_name("도적");
    setlevel(1);
    setnickname(nickname);
    setHP2(100);
    setMP(80);
    setpower(50);
    setdefence(5);
    setaccuracy(80);
    setspeed(35);
}

void Thief::attack()
{
	cout << "speed attack!";
    	cout << getdamage() << "의 피해를 주었다." << endl;
}

void Thief::givedamage(Player* player)
{ // 도적은 공격 횟수 5번으로 데미지 나누기 5를 함.
    Damage = (Power - player->getdefence()) / 5;

    if (Damage <= 0)
    {
        Damage = 1;
    }
}

몬스터(Monster)

// Monster.h

#pragma once
#include "Player.h"

class Monster:public Player
{
public:

	Monster(std::string name);

	virtual void attack() override; // Player의 attack을 재정의
	virtual void givedamage(Player* player) override;
};
//Monster.cpp

#include "Monster.h"

using namespace std;

Monster::Monster(std::string name) // 몬스터의 이름 매개변수로 입력, 기초 능력치
{
	nickname = name;
	setHP2(50);
	setpower(30);
	setdefence(10);
	setspeed(10);
}

void Monster::attack()
{
	cout << "몬스터가 공격했다." << endl;
	cout << "플레이어에게" << getdamage() << "의 피해를 주었다." << endl; // 데미지 출력
}

void Monster::givedamage(Player* player) // 플레이어 객체의 포인터 매개변수로 입력
{
	Damage = Power - player->getdefence(); // 몬스터의 공격력 - 플레이어의 방어력 = 데미지
	if (Damage <= 0) // 데미지 0 이하라면 데미지 1로 정의
	{
		Damage = 1;
	}
}

 

main

#include "Player.h"
#include "Warrior.h"
#include "Magician.h"
#include "Thief.h"
#include "Archer.h"
#include "Monster.h"

using namespace std;
// 메인 함수
int main()
{
	Warrior wa;
	Archer ar;
	Magician ma;
	Thief th;

	Player* player[4] = {&wa, &ar, &ma, &th}; // Player 타입의 포인터 배열 선언

	Player* job = nullptr;
	Player* monster = new Monster("슬라임"); // 몬스터의 이름은 슬라임

	int job_choice;
	string name;

	cout << "이름을 입력하세요." << endl;
	cin >> name;

	cout << "1.전사 2.궁수 3.마법사 4.도적" << endl << "직업 선택: ";

	cin >> job_choice;
	
	if (job_choice >= 1 && job_choice <= 4)
	{
		job = player[job_choice - 1];
	}
	else
	{
		cout << "잘못된 입력입니다.";
		return 0;
	}
	
	if (job != nullptr)
	{
		job->setnickname(name);
		job->initStats();
		job->printPlayerStatus();
	}

	int choice = 0;

	int Player_hp = job->getHP(); //setHP의 매개변수에 넣기위한 변수
	int Monster_hp = monster->getHP(); //setHP의 매개변수에 넣기위한 변수

	while (choice < 3)
	{
		cout << "행동을 고르세요. 1. 플레이어가 공격 2. 슬라임이 공격" << endl;

		cin >> choice;

		switch (choice)
		{

		case 1: // 플레이어가 몬스터를 공격했을 때

			if (job == player[1]) // 직업이 궁수라면
			{
				for (int i = 0; i < 3; i++)
				{
					job->givedamage(monster);
					Monster_hp = monster->getHP() - job->getdamage();
					monster->setHP2(Monster_hp);

					job->attack();
				}
			}
			else if (job == player[3]) // 직업이 도적이라면
			{
				for (int i = 0; i < 5; i++)
				{
					job->givedamage(monster);
					Monster_hp = monster->getHP() - job->getdamage();
					monster->setHP2(Monster_hp);

					job->attack();
				}
			}
			else if (job == player[0] || job == player[2]) // 직업이 전사, 마법사라면
			{
				job->givedamage(monster);
				Monster_hp = monster->getHP() - job->getdamage();
				monster->setHP2(Monster_hp);

				job->attack();
			}
			if (monster->setHP(Monster_hp)) // 몬스터 생존 여부 판단
				{
					cout << monster->getname() <<"의 남은 HP: " << Monster_hp << endl;
				}
				else
				{
					cout << monster->getname() << "을(를) 처치했습니다. 플레이어의 승리입니다." << endl;
					return 0;
				}
				
			break;

		case 2: // 슬라임이 플레이어를 공격했을 때
			
			monster->givedamage(job);

			if (job->setHP(Player_hp))
			{ // 플레이어가 피
				Player_hp = job->getHP() - monster->getdamage();
				job->setHP2(Player_hp);

				if (job->setHP(Player_hp)) // 플레이어 생존 여부 판단
				{
					monster->attack();
					cout << "플레이어의 남은 HP: " << Player_hp << endl;
				}
				else
				{ // 플레이어 사망시 플레이어의 남은 HP와 몬스터의 승리문구 출력
					Player_hp = 0;
					cout << "플레이어의 HP: " << Player_hp << endl;
					cout << monster->getname() << "의 승리입니다." << endl;
					return 0;
				}
			}
			break;
		}
	}
}

결과

몬스터의 방어력은 10이다.

 

전사(법사 동일)

궁수

도적