텍스트 기반 RPG 게임 제작 battlemanager

끄적끄적·2025년 1월 15일

배틀매니저

BattleManager.h

#ifndef BATTLEMANAGER_H
#define BATTLEMANAGER_H
#include"Character.h"
#include"Battle.h"
#include"Monster.h"
#include <string>
#include <map>
#include <vector>
#include"EnumCollection.h"

class BattleManager
{
	
private:

	BaseMonster* monster;
	
	EnumPrintLog MonsterImage;
	static BattleManager* Battleinstance;

	BattleManager() = default;
	~BattleManager() = delete;
	BattleManager(const BattleManager&) = delete;
	BattleManager& operator=(const BattleManager&) = delete;
public:

	static BattleManager* GetInstance();
	BaseMonster* getMonster() { return monster; }
	void setMonster(BaseMonster* monster);
	void BeginBattle(Character* player, int stage);
	void CreateMonster(bool isNamde, int stage);
	EnumPrintLog getMonsterImage() { return MonsterImage; }
	void setMonsterImage(EnumPrintLog imageData) { this->MonsterImage = imageData; }

};



#endif

BattleManager.cpp

#include "BattleManager.h"


BattleManager* BattleManager::Battleinstance = nullptr;

BattleManager* BattleManager::GetInstance()
{

	if (Battleinstance == nullptr)
		Battleinstance = new BattleManager();

	return Battleinstance;

}


void BattleManager::setMonster(BaseMonster* monster)
{
	this->monster = monster;

}

void BattleManager::BeginBattle(Character* player, int stage)
{
	
	if (stage % 5 == 0)
	{
		CreateMonster(true, stage);

	}
	else
	{
		CreateMonster(false, stage);

	}
	Battle battle;
	battle.Fight(player, monster, stage);

}

void BattleManager::CreateMonster(bool isNamde,int stage)
{
	monster = nullptr;
	std::random_device rd;
	std::mt19937 gen(rd());
	Log* logger = Log::GetInstance();
	int range;
	if (stage < 20)
	{
		if (isNamde)
		{
			range = 3;
			
			std::uniform_int_distribution<int> dist(0, range-1);

			switch (dist(gen))
			{
			case 0:
				setMonster(new GoblinRider());
				setMonsterImage(EGoblinRider);
				MonsterImage = EGoblinRider;
				break;
			case 1:
				setMonster(new TwinHeadTroll());
				setMonsterImage(ETwinHeadTroll);


				break;
			case 2:
				setMonster(new Treant());
				setMonsterImage(ETreant);



				break;
			default:
				break;
			}
		}
		else
		{
			range = 5;

			std::uniform_int_distribution<int> dist(0, range-1);

			switch (dist(gen))
			{
			case 0:
				setMonster(new Goblin());
				setMonsterImage(EGoblin);


				break;
			case 1:
				setMonster(new Orc());
				setMonsterImage(EOrc);


				break;
			case 2:
				setMonster(new Troll());
				setMonsterImage(ETroll);


				break;
			case 3:
				setMonster(new Wolf());
				setMonsterImage(EWolf);


				break;
			case 4:
				setMonster(new Slime());
				setMonsterImage(ESlime);

				break;
			default:
				break;
			}
		}
	}
	else
	{
		setMonster(new Dragon());
		setMonsterImage(EDragon);

	}


}


배틀매니저가 하는 일

  • 스테이지에 맞게 몬스터 랜덤 생성
  • 매칭된 몬스터와 배틀 시스템 동작

배틀

Battle.h

#pragma once
#include "Character.h"
#include "Monster.h"
#include"Log.h"
#include<algorithm>
#include"EnumCollection.h"
#include"BattleManager.h"
#include<format>
#include"consumableItem.h"
#include"ItemFactory.h"
#include"Item.h"
class Battle
{
	struct saveStatus {
		int Power;
		int CurrentHP;
		int MaxHP;
	};

	MonsterState state;
	saveStatus CharacterData;
	bool endBattle = false;
	bool myTurn = true;
	bool isWin = false;
	BaseMonster* battleMonster;
	bool nextTurn;
public:
	
	void restoreCharacterState(Character* player);
	void saveCharacterState(Character* player);
	void StageOfDifficulty(int stage);
	void NextTurn();
	int Input(int min,int max);
	void Fight(Character* Player,BaseMonster* monster, int stage);
	void PlayerAction(Character* Player);
	void AttackSystem(Character* Player);
	void isEndBattle(Character* Player);
	void MonsterAction(Character* Player);
	void LootAction(Character* Player);
	bool RandomSuccess(int probability);
	void MonsterSkill(Character* Player);
	void UseItem(Character* Player);
};

Battle.cpp

#include "Battle.h"
//Common




void Battle::restoreCharacterState(Character* player)
{
	player->SetAttackPower(CharacterData.Power);
	player->SetMaxHP(CharacterData.MaxHP);
}

void Battle::saveCharacterState(Character* player)
{
	CharacterData.Power = player->GetAttackPower();
	CharacterData.CurrentHP = player->GetCurrentHP();
	CharacterData.MaxHP = player->GetMaxHP();
}

void Battle::StageOfDifficulty(int stage)
{
	float multiple = 0.05*stage;
	multiple = multiple + 1.0f;
	battleMonster->SetHealth(battleMonster->GetHealth() * multiple);
	battleMonster->SetExperience(battleMonster->GetExperience() * multiple);
	battleMonster->SetDamage(battleMonster->GetDamage() * multiple);
	battleMonster->SetGold(battleMonster->GetGold() * multiple);
}

void Battle::NextTurn()
{
	if (nextTurn)
	{
		myTurn = !myTurn;
	}
}

int Battle::Input(int min,int max)
{
	Log* logger = Log::GetInstance();
	int input;
	cin >> input;
	if (min > input || max < input)
	{
		input = 0;
		logger->PrintLog("입력 범위 오류 다시 선택하세요.\n", false);

	}

	return input;
}
void Battle::Fight(Character* Player, BaseMonster* monster, int stage) // 전투
{
	Log* logger = Log::GetInstance();
	BattleManager* BM = BattleManager::GetInstance();
	this->battleMonster = monster;
	StageOfDifficulty(stage);
	saveCharacterState(Player);
	logger->PrintLog(battleMonster->GetName() + "가(이) 등장했다!\n", BM->getMonsterImage(), false);
	Sleep(1000);
	while (!endBattle)
	{
		//logger->PrintLog();
		logger->PrintLog("", BM->getMonsterImage(), true);
		logger->PrintLog("플레이어 체력 :" + to_string(Player->GetCurrentHP()) + " : " + to_string(Player->GetMaxHP()) + " 플레이어 공격력 : " + to_string(Player->GetAttackPower())+"\n",false);
		logger->PrintLog(battleMonster->GetName() + " 체력 : " + to_string(battleMonster->GetHealth()) + " 공격력 : " + to_string(battleMonster->GetDamage())+"\n",false);
		if (myTurn)
		{
			PlayerAction(Player);
			Sleep(2000);

			//플레이어턴
		}
		else
		{
			Sleep(1500);

			MonsterAction(Player);
			//몬스터 턴
			Sleep(2500);
		}

		NextTurn();
		isEndBattle(Player);
		


	}

	if (isWin)
	{
		LootAction(Player);
	}


}



void Battle::PlayerAction(Character* Player)
{
	Log* logger = Log::GetInstance();
	ItemFactory& IFactory = ItemFactory::GetInstance();
	Item* item = IFactory.GenerateItem("LowHPPotion");
	Player->TakeItem(item);
	vector<Inventory> cosumableItems = Player->GetInventoryItems(ItemType::Consumable);

	
	logger->PrintLog("무엇을 해야할까?\n1.공격\t2.아이템사용\n",false);
	bool flag = false;
	
	while (!flag)
	{
		int choice = Input(1, 2);
		switch (choice)
		{
		case 1:
			AttackSystem(Player);
			nextTurn = true;
			flag = true;
			break;

		case 2:
			if (cosumableItems.size() > 0)
			{
				UseItem(Player);
				flag = true;
			}
			else
			{
				logger->PrintLog("사용 아이템이 없습니다.\n", false);
			}
			nextTurn = false;
			//아이템

			break;

		default:
			break;
		}
	}
	
}

void Battle::MonsterAction(Character* Player)
{
	Log* logger = Log::GetInstance();
	
	
	//logger->PrintLog(battleMonster->GetName() + "가(이) 공격했다!\n",false);
	int Damage = 0;
	state = (MonsterState)battleMonster->TakeAction();
	switch (state)
	{
	case Enums::EAttack:
		Damage = battleMonster->GetDamage();

		Player->TakeDamage(Damage);
		logger->PrintLog("플레이어가 " + to_string(Damage) + "의 피해를 입었다.\n", false);
		state = EAttack;
		break;
	case Enums::EDefence:
		state = EDefence;
		break;

	case Enums::EMonsterSkill:
		MonsterSkill(Player);
		break;

	default:
		break;
	}
	//logger->PrintLog();

	nextTurn = true;

}

void Battle::MonsterSkill(Character* Player) // 특수패턴
{
	Log* logger = Log::GetInstance();
	int choice = Input(1, 3);
	while (choice==0)
	{
		choice = Input(1, 3);
	}

	int result = battleMonster->GetResponseScore(choice);
	if (result==1)
	{
		battleMonster->TakeDamage(Player->GetAttackPower() * 3);
		logger->PrintLog("좋은 선택이었다!\n", false);
		logger->PrintLog(battleMonster->GetName() + "가(이)" + to_string(Player->GetAttackPower() * 3) + "의 치명적인 피해를 입었다!\n", false);

	}
	else if (result == 2)
	{
		logger->PrintLog("나쁘지 않은 선택이었다!\n", false);
		logger->PrintLog(battleMonster->GetName() + " 에게 피해를 입지 않았다.\n", false);

	}
	else
	{
		Player->TakeDamage(battleMonster->GetDamage()*2);
		logger->PrintLog("나쁜 선택이었다.\n", false);
		logger->PrintLog("플레이어가 " +battleMonster->GetName()+" "+ to_string(battleMonster->GetDamage()*2) + "의 치명적인 피해를 입었다.\n", false);

	}

	
}

void Battle::UseItem(Character* Player)
{
	Log* logger = Log::GetInstance();

	vector<Inventory> cosumableItems = Player->GetInventoryItems(ItemType::Consumable);
	logger->PrintLog("====== Item List =====\n", false);

	int i = 1;
	for (auto inventory : cosumableItems)
	{
		
		logger->PrintLog(format("{}. 아이템명 :{}, 수량 :{}\n",i, inventory.item->GetName(), inventory.Count), false);
		i++;
	}

	int choice = Input(1, i);
	if (choice > 0)
	{
		auto selectedItem = cosumableItems[choice-1];
		//dynamic_cast<ConsumableItem*>(selectedItem.item)->ConsumeEffect(*Player);
		Player->UseItem(selectedItem.item->GetName());
	}

	

}

void Battle::LootAction(Character* Player)
{
	Log* logger = Log::GetInstance();
	ItemFactory &IFactory = ItemFactory::GetInstance();
	Player->TakeExp(battleMonster->GetExperience());
	Player->TakeGold(battleMonster->GetGold());
	string itemName = battleMonster->GetRandomItem();
	
	Item* item = IFactory.GenerateItem(itemName);
	Player->TakeItem(item);

	logger->PrintLog(format("{} 에게 {}경험치를 얻었다!\n", battleMonster->GetName(), battleMonster->GetExperience()), false);
	logger->PrintLog(format("{} 에게 {}골드를 얻었다!\n", battleMonster->GetName(), battleMonster->GetGold()), false);
	logger->PrintLog(format("{} 에게 {}을(를) 얻었다\n", battleMonster->GetName(), itemName), false);
	Sleep(2000);
	logger->PrintLog("");
	//Player->TakeItem()
	//battleMonster.
}

bool Battle::RandomSuccess(int probability)
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<int> dist(1, 100);
	if (dist(gen) <= probability)
	{
		return true;
	}
	else
	{
		return false;
	}
}



void Battle::AttackSystem(Character* Player)
{
	Log* logger = Log::GetInstance();
	logger->PrintLog("어느곳을 공격할까?\n1.머리(40%)\t2.몸통(80%)\n",false);
	bool flag = false;
	bool isHit;
	int Damage = 0;
	while (!flag)
	{
		int choice = Input(1, 2);
		switch (choice)
		{
		case 1:
			isHit = RandomSuccess(40);
			if (isHit)
			{
				logger->PrintLog("머리 공격이 성공했다!\n", false);
				Damage = Player->GetAttackPower() * 2;
				if (state == EDefence)
				{
					logger->PrintLog("몬스터가 방어태세를 취했습니다. 감소된 피해를 입힙니다.\n", false);

					Damage *= 0.6;
				}
				battleMonster->TakeDamage(Damage);

				logger->PrintLog(battleMonster->GetName() + "가(이)"+ to_string(Damage)+"의 피해를 입었다!\n",false);
			}
			else
			{
				logger->PrintLog("공격이 빗나갔다.\n",false);
				logger->PrintLog(battleMonster->GetName() + "가(이) 피해를 입지 않았다.\n",false);
			}
			
			flag = true;
			//log
			break;
		case 2:
			isHit = RandomSuccess(80);
			if (isHit)
			{
				logger->PrintLog("공격이 성공했다!\n", false);
				Damage = Player->GetAttackPower();
				if (state == EDefence)
				{
					logger->PrintLog("몬스터가 방어태세를 취했습니다. 감소된 피해를 입힙니다.\n", false);
					Damage *= 0.6;
				}
				battleMonster->TakeDamage(Damage);

				logger->PrintLog(battleMonster->GetName() + "가(이)" + to_string(Damage) + "의 피해를 입었다!\n", false);

			}
			else
			{
				logger->PrintLog("공격이 빗나갔다.\n",false);
				logger->PrintLog(battleMonster->GetName() + "가(이) 피해를 입지 않았다.\n",false);
			}
			flag = true;
			//log
			break;
		default:
			break;
		}
	}
	

	
}

void Battle::isEndBattle(Character* Player)
{
	Log* logger = Log::GetInstance();
	if (Player->GetCurrentHP() < 1 || battleMonster->GetHealth()<1)
	{
		endBattle = true;
	}

	if (endBattle)
	{
		if (Player->GetCurrentHP() > 0)//승리
		{
			logger->PrintLog("전투에서 승리했다!\n", false);
			isWin = true;
			Sleep(2000);

		}
		else //패배
		{
			logger->PrintLog("플레이어 사망.\n",false);
			isWin = false;
			Player = nullptr;
			Sleep(2000);

		}
	}
}

배틀 클래스가 하는일

  • 매칭된 몬스터와 전투
  • 스테이지에 맞게 몬스터 난이도 스펙 조정
  • 전투 승패 결과에 따라 몬스터 루팅 or 플레이어 사망 결정
  • 몬스터 클래스 안에 있는 몬스터 패턴을 읽어와서 그에 맞게 전투 실행

0개의 댓글