[TIL] 23-12-29

Lev·2023년 12월 29일
0

📓TIL Archive

목록 보기
14/33

💡 '뭘 만들어야 하지?' 생각이 들 땐...

  1. 클래스 만들기
    • 무기, 몬스터, 맵 등 개념을 만들어야 할 때
    • 클래스 만들었으면 객체화 시키기!
  2. 로직 만들기
    • 메모리맵을 머릿속에 그리기
    • n회 반복 ⇒ 반복문
    • 만약 ~ ⇒ 조건문

클래스 (이어서)

가상함수와 다형성 Virtual Function

class FightUnit
{
public:
	// [Virtual]
	// 자식클래스가 부모클래스와 완전히 동일한 함수를 구현할 경우
	// 자식의 함수를 우선적으로 실행한다는 뜻
	virtual int GetAtt()
	{
		return Att;
	}

private:
	int Att = 10;
};

class Monster : public FightUnit
{

};

class Player : public FightUnit
{
protected:
	// [Override]
	// 재구현한 함수에는 (필수는 아니지만) override를 붙여주자
	int GetAtt() override
	{
		return FightUnit::GetAtt() + WeaponAtt;	// (O)
		// return GetAtt() + WeaponAtt;	// (X)
		// 명시해주지 않으면 재귀가 되어 무한루프에 갇히기 때문
	}

private:
	int WeaponAtt = 10;
};

void Fight(FightUnit* Left, FightUnit* Right)
// [다형성]
// 겉으로 보기에는 같은 FightUnit이지만, 동작할 때에는 다르게 동작하는 것
// Fight는 인자가 Player인지 Monster인지 알지 못하고, 알 필요도 없다
// 그래서 형변환하여 인자로 받는 것이다
{
	Left->GetAtt();	// 20
	Right->GetAtt();	// 10
}

int main()
{
	Player NewPlayer;
	Monster NewMonster;

	Fight(&NewPlayer, &NewMonster);
}

Text RPG

  • 다형성 적용
  • Weapon 추가
  • 치료 구현
  • 무기 강화 구현
// [TextRPG.cpp]
#include <iostream>
#include "Player.h"
#include "FightZone.h"
#include "Town.h"
#include <conio.h>

int main()
{

	Player NewPlayer = Player();
	NewPlayer.SetName("Player");
	
	FightZone NewFightZone;
	Town NewTownZone;

	// 메인 메뉴
	while (true)
	{
		NewPlayer.StatusRender();
		printf_s("어디로 가시겠습니까?\n");
		printf_s("1. 마을.\n");
		printf_s("2. 사냥터.\n");
		int Select = _getch();

		switch (Select)
		{
		case '1':
			NewTownZone.In(NewPlayer);	// 플레이어를 마을에 넣기
			break;
		case '2':
			NewFightZone.In(NewPlayer);	// 플레이어를 전투공간에 넣기
			break;
		default:
			break;
		}

		system("cls");
	}
}
// [Player.h]
#pragma once
#include "FightUnit.h"
#include "Weapon.h"

class Player : public FightUnit
{
public:
	Player();

	// 다른 클래스를 값형의 멤버로 두고 있는 경우, public으로 하는 편이다
	// 어짜피 Weapon 클래스의 멤버는 알아서 보호되고 있기 때문
	Weapon Weapon;

	// 사실은... 동적할당을 배운 후, 포인터로 받는 것이 좋다
	// 값형일 경우 => 플레이어가 생성될 때부터 손에 들려 있음
	// 포인터일 경우 => 없다가 생기는 것이 가능

	// 절대로 사라지지 않는다면 값형
	// 존재하거나 존재하지 않을 가능성이 있다면 포인터

	void StatusRender() override;	// Monster와 달리 소지금을 화면에 표시해주기 위해

protected:
	int GetRAtt() override;	// Monster와 달리 무기의 공격력을 더해주기 위해
};
// [Player.cpp]
#include "Player.h"
#include <iostream>

Player::Player()
{
	Weapon.SetName("Steel Sword");
	Weapon.SetAtt(10);
}

void Player::StatusRender()
{
	int Size = printf_s("%s", Name);

	for (int i = 0; i < 50 - Size; i++)
	{
		int a = 0;
		printf_s("-");
	}

	printf_s("\n");
	printf_s("공격력 %d ~ %d\n", MinAtt, MaxAtt);
	printf_s("체력 %d\n", Hp);
	printf_s("소지금 %d\n", Gold);

	for (int i = 0; i < 50; i++)
	{
		printf_s("-");
	}

	printf_s("\n");
}

int Player::GetRAtt()
{
	CurAtt = FightUnit::GetRAtt() + Weapon.GetAtt() + Weapon.GetEquipUp();
	return CurAtt;
}
// [Weapon.h]
#pragma once
#include "NameUnit.h"

// Player에게 무기를 들려주기 위해 생성
class Weapon : public NameUnit
{
public:
	// Get 함수
	inline int GetAtt() const
	{
		return Att;
	}

	inline int GetEquipUp() const
	{
		return EquipUp;
	}

	// Set 함수
	inline void SetAtt(int _Att)
	{
		Att = _Att;
	}

	inline void SetEquipUp(int _EquipUp)
	{
		EquipUp= _EquipUp;
	}

private:
	int Att;
	int EquipUp = 0;
};
// [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;
	}

	// Set? 함수
	inline void HpReset()
	{
		Hp = MaxHp;
	}

	inline void AddGold(int _Gold)
	{
		Gold += _Gold;
	}

	// 추가로 얻는 Gold를 난수로 계산해주는 함수
	int RandomGold(int _Min, int _Max);

	// 화면에 Status를 띄워주는 함수
	virtual void StatusRender();

protected:
	int Hp = 100;
	int MaxHp = 100;
	int MinAtt = 10;
	int MaxAtt = 20;
	int Speed = 20;
	int Gold = 0;
};
// [StatusUnit.cpp]
#include "StatusUnit.h"
#include <iostream>
#include <random>

int StatusUnit::RandomGold(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");
	printf_s("공격력 %d ~ %d\n", MinAtt, MaxAtt);
	printf_s("체력 %d\n", Hp);

	for (int i = 0; i < 50; i++)
	{
		printf_s("-");
	}

	printf_s("\n");
}
// [NameUnit.h]
#pragma once

// 다른 Status들과 구분하기 위해 분리
class NameUnit
{
public:
	inline char* GetName()
	{
		return Name;
	}

	void SetName(const char* _Name);

protected:
	char Name[100] = "None";
};
// [NameUnit.cpp]
#include "NameUnit.h"

void NameUnit::SetName(const char* _Name)
{
	int Cnt = 0;
	while (_Name[Cnt])
	{
		++Cnt;
	}

	for (int i = 0; i < Cnt + 1; i++)
	{
		Name[i] = _Name[i];
	}
}
// [FightUnit.h]
#pragma once
#include "StatusUnit.h"

class FightUnit : public StatusUnit
{
public:
	// Get 함수
	inline int GetMinAtt()
	{
		return MinAtt;
	}

	inline int GetMaxAtt()
	{
		return MaxAtt;
	}

	inline bool IsDeath()
	{
		return Hp == 0;
	}

	// 난수로 공격값과 선공을 정하기 위한 함수
	virtual int GetRAtt();	// Monster와 Player에게 다르게 적용된다

	int GetRSpeed() const;

	// Fight용 함수
	void DamageLogic(FightUnit& _Unit);

	void DamageRender();

protected:
	int CurAtt = 0;
};
// [FightUnit.cpp]
#include "FightUnit.h"
#include <iostream>
#include <random>

int FightUnit::GetRAtt()
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<> dist(MinAtt, MaxAtt);
	CurAtt = dist(gen);
	// DamageRender 함수에서 주체를 공격하는 사람으로 하기 위해
	// CurAtt를 DamageLogic 함수가 아닌, GetRAtt 함수에서 정해준다

	return CurAtt;
}

int FightUnit::GetRSpeed() const
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<> dist(MinAtt, MaxAtt);

	return dist(gen);
}

void FightUnit::DamageLogic(FightUnit& _Unit)
{
	Hp -= _Unit.GetRAtt();

	if (Hp < 0)
	{
		Hp = 0;
	}
}

void FightUnit::DamageRender()
{
	printf_s("%s 가 %d의 공격력으로 공격했습니다.\n", Name, CurAtt);
}
// [ZoneBase.h]
#pragma once

// 지역들의 틀을 만들어주기 위해
class ZoneBase
{
public:
	virtual void In(class Player& _Player)	// 플레이어가 들어갈 수 있는 공간이라는 뜻
	// FightZone과 Town용이 다르다
	{

	}
};
// [FightZone.h]
#pragma once
#include "Monster.h"
#include "ZoneBase.h"

class FightZone : public ZoneBase
{
public:
	FightZone();

	// 승패가 갈릴 때까지 전투하는 함수
	void In(class Player& _Player) override;	// 전방선언 => 헤더 파일을 추가하지 않아도 된다

protected:
	Monster NewMonster = Monster();

	// 1회 합을 주고 받는 함수
	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");
}

void FightZone::In(Player& _Player)
{
	system("cls");

	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)
		{
			// <과제 4. 몬스터가 죽었을 때 플레이어가 골드를 얻게 하기>
			// => 골드의 범위는 100 ~ 500
			if (NewMonster.IsDeath() == true)
			{
				int RGold = _Player.RandomGold(100, 500);
				_Player.AddGold(RGold);
				printf_s("%d만큼의 골드를 벌었다!\n", RGold);

				int Input = _getch();
			}

			// <과제 3. 전투가 끝난 후에 다시 몬스터의 Hp가 차게 만들기>
			NewMonster.HpReset();
			return;
		}
	}
}

bool FightZone::FightLogic(FightUnit& _First, FightUnit& _Second, FightUnit& _Top, FightUnit& _Bot)
{
	{
		int Input = _getch();
	}
	system("cls");

	// First가 선공
	_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");

	// Second가 후공
	_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;
};
// [Town.h]
#pragma once
#include "ZoneBase.h"

// 마을이라는 새로운 지역 생성
class Town : public ZoneBase
{
public:
	void In(class Player& _Player) override;

	void WeaponUp(Player& _Player);
	void Heal(Player& _Player);
};
// [Town.cpp]
#include "Town.h"
#include "Player.h"
#include <iostream>
#include <conio.h>
#include <random>

void Town::In(Player& _Player)
{
	while (true)
	{
        system("cls");
        _Player.StatusRender();
        printf_s("마을에서 무엇을 하시겠습니까?\n");
        printf_s("1. 강화.\n");
        printf_s("2. 치료.\n");
        printf_s("3. 나간다.\n");
        int Select = _getch();

        switch (Select)
        {
        case '1':
            WeaponUp(_Player);
            break;
        case '2':
            Heal(_Player);
            break;
        case '3':
            return;
        default:
            break;
        }
	}
}

/*
<과제 1. 무기 강화하기>
=> 플레이어의 무기 강화 상태를 보여주기
=> "1. 강화한다, 2. 나간다."의 선택지 보여주기
=> 실제로 무기 강화수치 올리기
    0 ~ 9강 : 실패해도 강화수치가 떨어지지 않는다
    10 ~ 19강 : 실패하면 강화수치가 5씩 떨어진다
    20 ~ 29강 : 실패하면 강화수치가 0이 된다
    30강보다 강화할 수는 없다
*/
void Town::WeaponUp(Player& _Player)
{
    while (true)
    {
        int EquipUp = _Player.Weapon.GetEquipUp();

        system("cls");
        _Player.StatusRender();
        printf_s("강화상점에서 무엇을 하시겠습니까?\n");
        printf_s("현재 무기 강화수치 : %d\n", EquipUp);
        printf_s("1. 강화한다.\n");
        printf_s("2. 나간다.\n");
        int Select = _getch();

        system("cls");
        _Player.StatusRender();

        switch (Select)
        {
        case '1':
        {
            // <과제 5. 무기 강화 1회당 골드를 소모하게 만들기>
            // => 강화 수치마다 강화비용이 증가되도록
            int EquipUpGold = (EquipUp + 1) * 100;
            
            if (_Player.GetGold() < EquipUpGold)
            {
                printf_s("소지금이 부족하여 강화할 수 없습니다.\n");
                break;
            }

            if (EquipUp == 30)
            {
                printf_s("더이상 강화할 수 없습니다.\n");
                break;
            }

            _Player.AddGold(-EquipUpGold);

            std::random_device rd;
            std::mt19937 gen(rd());
            std::uniform_int_distribution<> dist(0, 1);

            system("cls");
            _Player.StatusRender();

            if (dist(gen) == 1)
            {
                _Player.Weapon.SetEquipUp(EquipUp + 1);
                printf_s("강화에 성공했습니다!\n");
            }
            else
            {
                printf_s("강화에 실패했습니다!\n");

                if (EquipUp >= 20)
                {
                    _Player.Weapon.SetEquipUp(0);
                }
                else if (EquipUp >= 10)
                {
                    _Player.Weapon.SetEquipUp(EquipUp - 1);
                }
            }

            printf_s("무기 강화수치 : %d\n", _Player.Weapon.GetEquipUp());
            break;
        }
        case '2':
            return;
        default:
            break;
        }

        int Input = _getch();
    }
}

// <과제 2. 플레이어가 치료되게 만들기>
void Town::Heal(Player& _Player)
{
    system("cls");
    _Player.StatusRender();

    if (_Player.GetHp() == _Player.GetMaxHp())
    {
        printf_s("체력이 가득 차 있습니다.\n");
        int Input = _getch();

        return;
    }

    int HealAmount = _Player.GetMaxHp() - _Player.GetHp();

    if (_Player.GetGold() < HealAmount)
    {
        printf_s("소지금이 부족하여 치료할 수 없습니다.\n");
        int Input = _getch();

        return;
    }

    _Player.AddGold(-HealAmount);
    _Player.HpReset();

    system("cls");
    _Player.StatusRender();
    printf_s("%s가 치료되었습니다.", _Player.GetName());

    int Input = _getch();
}
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글