// [TextRPG.cpp]
#include <iostream>
#include "Player.h"
#include "FightZone.h"
#include "Town.h"
#include <conio.h>
int main()
{
Player NewPlayer = Player();
NewPlayer.SetName("Player");
/*
<과제1>
난이도별 FightZone 3개 만들기
- 초급 사냥터 => 몬스터 Min 5 max 15 Hp 100
- 중급 사냥터 => 몬스터 Min 10 max 20 Hp 200
- 고급 사냥터 => 몬스터 Min 20 max 40 Hp 300
"1. 마을.\n";
"2. 초급 사냥터.\n";
"3. 중급 사냥터.\n";
"4. 고급 사냥터.\n";
*/
Town NewTownZone;
FightZone NewFightZone0(5, 15, 100);
FightZone NewFightZone1(10, 20, 200);
FightZone NewFightZone2(20, 40, 300);
// 메인 메뉴
while (true)
{
NewPlayer.StatusRender();
printf_s("어디로 가시겠습니까?\n");
printf_s("1. 마을.\n");
printf_s("2. 초급 사냥터.\n");
printf_s("3. 중급 사냥터.\n");
printf_s("4. 고급 사냥터.\n");
int Select = _getch();
switch (Select)
{
case '1':
NewTownZone.In(NewPlayer); // 플레이어를 마을에 넣기
break;
case '2':
NewFightZone0.In(NewPlayer); // 플레이어를 초급 사냥터에 넣기
break;
case '3':
NewFightZone1.In(NewPlayer); // 플레이어를 중급 사냥터에 넣기
break;
case '4':
NewFightZone2.In(NewPlayer); // 플레이어를 고급 사냥터에 넣기
break;
default:
break;
}
/*
[StatusUnit.h]
void StatusInit(int _Hp, int _MinAtt, int _MaxAtt)
{
SetMaxHp(_Hp);
SetHp(_Hp);
SetMinAtt(_MinAtt);
SetMaxAtt(_MaxAtt);
}
[TextRPG.cpp]
FightZone NewFightZone[3];
NewFightZone[0].NewMonster.StatusInit(100, 5, 15);
NewFightZone[1].NewMonster.StatusInit(200, 10, 20);
NewFightZone[2].NewMonster.StatusInit(300, 20, 40);
switch (Select)
{
case '2':
case '3':
case '4':
{
int FightSelect = Select - '2';
NewFightZone[FightSelect].In(NewPlayer);
break;
}
=> 이런 식으로 배열을 이용해 구현해도 된다
*/
system("cls");
}
}
// [FightZone.h]
#pragma once
#include "Monster.h"
#include "ZoneBase.h"
class FightZone : public ZoneBase
{
public:
FightZone();
FightZone(const int _MinAtt, const int _MaxAtt, const int _Hp); // <1>
// 조건이 다른 FightZone들을 초기화(실제론 대입에 가깝지만)하기 위해 새로운 생성자를 만들었다
void In(class Player& _Player);
protected:
Monster NewMonster = Monster();
bool FightLogic(FightUnit& _First, FightUnit& _Last, FightUnit& _Top, FightUnit& _Bot);
};
// [FightZone.cpp]
#include "FightZone.h"
#include <conio.h>
#include <iostream>
#include "Player.h"
FightZone::FightZone()
{
NewMonster.SetName("Monster");
}
FightZone::FightZone(const int _MinAtt, const int _MaxAtt, const int _Hp) // <1>
{
NewMonster.SetName("Monster");
NewMonster.SetMinAtt(_MinAtt);
NewMonster.SetMaxAtt(_MaxAtt);
NewMonster.SetHp(_Hp);
NewMonster.SetMaxHp(_Hp);
}
void FightZone::In(Player& _Player)
{
system("cls");
NewMonster.FightStart(_Player); // <3>
_Player.FightStart(NewMonster); // <3>
while (true)
{
_Player.StatusRender();
NewMonster.StatusRender();
bool IsEnd = false;
if (_Player.GetRSpeed() >= NewMonster.GetRSpeed())
{
printf_s("%s의 선공\n", _Player.GetName());
IsEnd = FightLogic(_Player, NewMonster, _Player, NewMonster);
}
else {
printf_s("%s의 선공\n", NewMonster.GetName());
IsEnd = FightLogic(NewMonster, _Player, _Player, NewMonster);
}
if (IsEnd == true)
{
if (NewMonster.IsDeath() == true)
{
NewMonster.FightEnd(_Player); // <3>
_Player.FightEnd(NewMonster); // <3>
int Input = _getch();
}
return;
}
}
}
bool FightZone::FightLogic(FightUnit& _First, FightUnit& _Second, FightUnit& _Top, FightUnit& _Bot)
{
{
int Input = _getch();
}
system("cls");
_Second.DamageLogic(_First);
_Top.StatusRender();
_Bot.StatusRender();
_First.DamageRender();
if (_Second.IsDeath() == true)
{
printf_s("게임 종료\n");
int Input = _getch();
return true;
}
{
int Input = _getch();
}
system("cls");
_First.DamageLogic(_Second);
_Top.StatusRender();
_Bot.StatusRender();
_First.DamageRender();
_Second.DamageRender();
if (_First.IsDeath() == true)
{
printf_s("게임 종료\n");
int Input = _getch();
return true;
}
{
int Input = _getch();
}
system("cls");
return false;
};
// [StatusUnit.h]
#pragma once
#include "NameUnit.h"
class StatusUnit : public NameUnit
{
public:
// Get 함수
inline int GetHp() const
{
return Hp;
}
inline int GetMaxHp() const
{
return MaxHp;
}
inline int GetGold() const
{
return Gold;
}
inline int GetExp() const // <2>
{
return Exp;
}
// Set? 함수
inline void SetHp(int _Hp)
{
if (_Hp > MaxHp)
{
_Hp = MaxHp;
}
Hp = _Hp;
}
inline void SetMaxHp(const int _MaxHp)
{
MaxHp = _MaxHp;
}
inline void HpReset()
{
Hp = MaxHp;
}
inline void SetMinAtt(const int _MinAtt)
{
MinAtt = _MinAtt;
}
inline void SetMaxAtt(const int _MaxAtt)
{
MaxAtt = _MaxAtt;
}
inline void AddGold(const int _Gold)
{
Gold += _Gold;
}
inline void AddExp(const int _Exp) // <2>
{
Exp += _Exp;
}
// 난수를 계산해주는 함수 (Gold와 Exp 계산에 사용)
int Random(int _Min, int _Max); // <2>
/*
<과제4>
StatusRender()를 세 부분으로 나누고, 다형성 적용하기
- Player는 레벨과 경험치를 추가로 보여줘야 한다
*/
void StatusRender();
virtual void StatusRenderStart(); // <4>
virtual void StatusRenderBase(); // <4>
virtual void StatusRenderEnd(); // <4>
protected:
int Hp = 100;
int MaxHp = 100;
int MinAtt = 10;
int MaxAtt = 20;
int Speed = 20;
int Gold = 0;
int Exp = 0; // <2>
};
// [StatusUnit.cpp]
#include "StatusUnit.h"
#include <iostream>
#include <random>
int StatusUnit::Random(int _Min, int _Max)
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(_Min, _Max);
return dist(gen);
}
void StatusUnit::StatusRender()
{
int Size = printf_s("%s", Name);
for (int i = 0; i < 50 - Size; i++)
{
int a = 0;
printf_s("-");
}
printf_s("\n");
StatusRenderStart(); // <4>
StatusRenderBase(); // <4>
StatusRenderEnd(); // <4>
for (int i = 0; i < 50; i++)
{
printf_s("-");
}
printf_s("\n");
}
void StatusUnit::StatusRenderStart() // <4>
{
}
void StatusUnit::StatusRenderBase() // <4>
{
printf_s("공격력 %d ~ %d\n", MinAtt, MaxAtt);
printf_s("체력 %d / %d\n", Hp, MaxHp);
printf_s("소지금 %d\n", Gold);
}
void StatusUnit::StatusRenderEnd() // <4>
{
}
// [FightUnit.h]
#pragma once
#include "StatusUnit.h"
class FightUnit : public StatusUnit
{
public:
// Get 함수
inline int GetMinAtt() const
{
return MinAtt;
}
inline int GetMaxAtt() const
{
return MaxAtt;
}
inline bool IsDeath()
{
return Hp == 0;
}
// 난수로 공격값과 선공을 정하기 위한 함수
virtual int GetRAtt();
int GetRSpeed() const;
// Fight용 함수
void DamageLogic(FightUnit& _Unit);
void DamageRender();
/*
<과제3>
Player와 Monster의 전투 전후로 진행되는 요소를 함수로 만들고, 다형성 적용하기
- Player는 전투 후에 골드와 경험치를 얻고, 레벨업을 해야할지 결정한다
- Monster는 전투 전에 본인이 소지할 골드와 경험치를 계산한다
*/
virtual void FightStart(FightUnit& _Other) // <3>
{
}
virtual void FightEnd(FightUnit& _Ohter) // <3>
{
}
protected:
int CurAtt = 0;
};
// [Player.h]
#pragma once
#include "FightUnit.h"
#include "Weapon.h"
class Player : public FightUnit
{
public:
Player();
Weapon Weapon;
void FightEnd(FightUnit& _Other) override; // <3>
void StatusRenderStart() override; // <4>
/*
<과제2>
Player의 레벨과 경험치 구현하기
- Exp가 LevelUpExp를 넘어설 경우 레벨업
- 레벨업 시 공격력, 체력 상승
*/
void LevelUp(); // <2>
protected:
int GetRAtt() override;
private:
int Level = 1; // <2>
int LevelUpExp = 100; // <2>
};
// [Player.cpp]
#include "Player.h"
#include <iostream>
#include <conio.h>
Player::Player()
{
Weapon.SetName("Steel Sword");
Weapon.SetAtt(10);
}
void Player::FightEnd(FightUnit& _Other) // <3>
{
int Exp = _Other.GetExp(); // 몬스터가 준 경험치
AddExp(Exp);
printf_s("%d만큼의 경험치를 얻었다!\n", Exp);
int Input = _getch();
if (GetExp() >= LevelUpExp) // 가지고 있는 경험치와 요구 경험치 비교
{
LevelUp(); // <2>
}
int Gold = _Other.GetGold(); // 몬스터가 준 골드
AddGold(Gold);
printf_s("%d만큼의 골드를 벌었다!\n", Gold);
}
void Player::StatusRenderStart() // <4>
{
printf_s("레벨 %d (%d / %d)\n", Level, Exp, LevelUpExp);
// Monster와 달리 Level과 Exp 표시
}
void Player::LevelUp() // <2>
{
while (GetExp() >= LevelUpExp) // 한꺼번에 여러번 레벨업 할 경우 대비
{
// 레벨 업
++Level;
// 경험치 소모, 요구 경험치 늘리기
Exp -= LevelUpExp;
LevelUpExp += 100;
// 체력 증가
MaxHp += 100;
HpReset();
// 공격력 증가
MinAtt += 10;
MaxAtt += 10;
printf_s("레벨 업! %d -> %d\n", Level - 1, Level);
int Input = _getch();
}
}
int Player::GetRAtt()
{
CurAtt = FightUnit::GetRAtt() + Weapon.GetAtt() + Weapon.GetEquipUp();
return CurAtt;
}
// [Monster.h]
#pragma once
#include "FightUnit.h"
class Monster : public FightUnit
{
public:
void FightStart(FightUnit& _Other) override; // <3>
};
// [Monster.cpp]
#include "Monster.h"
void Monster::FightStart(/*Monster* const this, */FightUnit& _Player) // <3>
{
/*this->*/HpReset(/*this*/);
// Player에게 얼마만큼의 골드와 경험치를 줄 지 미리 난수로 계산해둔다
int Gold = Random(1000, 10000);
AddGold(Gold);
int Exp = Random(50, 100);
AddExp(Exp);
}
💡 잊지 말자!
프로그램의 모든 것은 위치, 크기, 형태, 값을 가지고 있다.
함수도 마찬가지이다.
#include <iostream>
int Test()
{
return 10;
}
void PlayerAttack()
{
printf_s("플레이어가 공격합니다.");
}
void PlayerMove()
{
printf_s("플레이어가 이동합니다.");
}
class Button
{
public:
void (*Function)() = nullptr;
int Image;
int Color;
int ScreenPos;
virtual void Click()
{
Function(); // 콜백 방식
}
};
class MoveButton : public Button
{
public:
void Click() override
{
printf_s("플레이어가 이동합니다.");
}
};
class AttackButton : public Button
{
public:
void Click() override
{
printf_s("플레이어가 공격합니다.");
}
};
int main()
{
{
int (*Ptr)() = Test; // [함수 포인터] 선언 => (자료형) (*변수명)()
// 포인터의 자료형이 본래 함수의 자료형과 같아야 한다
Ptr();
// 함수의 포인터이기 때문에 함수 사용하는 방법 그대로 사용 가능하다
sizeof(void(*)()); // 8byte
// 변수명을 삭제하면 그대로 함수포인터자료형이 된다
}
// 함수포인터 문법은 UI 등지에서 가장 많이 사용된다
{
// 모든 버튼마다 새로운 클래스를 만들어주는 방식 (X)
// - 버튼마다 클래스를 만드는 것이 번거로움
// - 일부만 다른 클래스가 너무 많이 생성됨
MoveButton MButton;
AttackButton AButton;
MButton.Click();
AButton.Click();
}
{
// 버튼의 기능별로 클릭했을 때 실행되는 함수를
// 포인터가 각각 다르게 가리키고 있는 방식 (O)
Button MoveButton;
Button AttButton;
MoveButton.Function = PlayerMove;
AttButton.Function = PlayerAttack;
MoveButton.Click();
AttButton.Click();
}
}