오늘은 어제 밤에 작성한 DungeonManager.cs 코드를 피드백을 받았다.
피드백 결과, ShowBattle() 함수에서는 Attack(), TakeDamage() 메서드만 남겨놓고 나머지 조건문이나 출력문은 Attack(), TakeDamage() 메서드 안에서 처리를 하라고 하셨다.
괜히 변수를 일일이 참조하는 것보다 해당 클래스 내에서 인터페이스를 상속 받아 그 안에서 처리하는 것이 좋다는 말씀이신 것 같다. 생각해보니 그 편이 더 좋을 것 같아 수정을 하기로 했다.
어제의 경우, 인터페이스를 IAttack과 ITakeDamage로 나누어 선언했었는데, 하나로 묶어 IObject로 선언하라고 피드백을 주셔서 수정했다.
interface IObject
{
int Attack();
void TakeDamage(int damage);
bool IsDead();
}
IObject를 상속받는 Monster나 Player 클래스에서 해당 인터페이스를 통해 공격, 데미지를 입을 경우를 처리할 수 있다.
public int Attack()
{
int damage = 0;
double getDamage;
getDamage = this.Atk / 100.0 * 10;
damage = new Random().Next(this.Atk - (int)Math.Ceiling(getDamage), this.Atk + (int)Math.Ceiling(getDamage) + 1);
Console.WriteLine($"Lv.{this.Lv} {this.Name} 의 공격!");
return damage;
}
위의 메서드는 Monster 클래스에서 IObject를 상속받아 정의한 Attack()이다.
가하는 데미지 뿐 아니라 출력문까지 한 번에 실행하도록 작성했다.
public void TakeDamage(int damage)
{
int criticalDamage = damage;
int r = new Random().Next(0, 101);
// 공격 미스. 10%의 확률로 공격이 적중하지 않음
if(r > 90)
{
Console.Write($"Lv.{this.Lv} {this.Name} 을(를) 공격했지만 아무일도 일어나지 않았습니다.\n");
return;
}
Console.Write($"Lv.{this.Lv} {this.Name} 을(를) 맞췄습니다. [데미지 : ");
// 치명타 공격
if (r <= 15)
{
criticalDamage += (damage * 60 / 100);
Utilities.TextColorWithNoNewLine($"{criticalDamage}", ConsoleColor.DarkRed);
Console.Write("]");
Utilities.TextColorWithNoNewLine(" -", ConsoleColor.Yellow);
Console.Write(" 치명타 공격");
Utilities.TextColorWithNoNewLine("!!", ConsoleColor.Yellow);
}
else
{
Utilities.TextColorWithNoNewLine($"{damage}", ConsoleColor.DarkRed);
Console.Write("]");
}
Console.WriteLine($"\n\nLv.{this.Lv} {this.Name}");
Console.Write($"{this.Hp} -> ");
if (r <= 15)
this.Hp -= criticalDamage;
else
this.Hp -= damage;
if (this.Hp <= 0) this.isDead = true;
Console.WriteLine($"{(this.isDead ? "Dead" : this.Hp)}");
}
위의 메서드는 마찬가지로 Monster 클래스에서 선언한 TakeDamage()이다.
Player의 Attack()에서 damage를 매개변수로 넘겨주면 확률에 따라 공격을 회피할 지, 치명타 공격을 받을지 정해 Monster의 Hp 값을 수정한다. 마찬가지로 출력문의 처리 또한 한번에 하도록 수정을 했다.
public void ShowBattle(Monster monster, bool isPlayerTurn)
{
if(isPlayerTurn)
monster.TakeDamage(Player.Attack());
else
player.TakeDamage(monster.Attack());
...
}
덕분에 ShowBattle()에서는 코드를 확연하게 줄일 수 있었다.
DungeonManager.cs에 던전 클래스를 만들어 던전 난이도별 생성 가능한 몬스터 종류나 경험치, 드랍 아이템 등을 관리할 수 있도록 클래스를 생성했다.
public class Dungeon
{
public int dungeonStage { get; set; }
// 난이도별 생성 가능한 몬스터 목록
public int dungeonMonsterType { get; set; }
public Dungeon(int dungeonStage, int dungeonMonsterType)
{
this.dungeonStage = dungeonStage;
this.dungeonMonsterType = dungeonMonsterType;
}
}
아직은 몬스터 종류만 구현을 했지만, 앞으로 다른 기능들을 추가하기 위해 미리 만들어 두었다.
이후, 던전 클래스에 들어갈 데이터인 Dungeon_Data.json 파일을 생성해서 읽도록 만들었다.
public DungeonManager(Player player)
{
EventManager.Instance.AddListener(EventType.eSetMonsters, this);
List<Dungeon>? d = (List<Dungeon>?)Utilities.LoadFile(LoadType.Dungeon);
dungeons = d;
// 플레이어 정보 받아오기
this.player = player; // Player 완성 시 new Player() 지우고 다시 설정하기
}
public List<Dungeon> dungeons = [];
DungeonManager 생성자에서 파일을 읽는 코드이다.
case LoadType.Dungeon:
{
path = Directory.GetParent(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location))
.Parent.Parent.Parent.FullName + @"\Dungeon_Data.json";
if (File.Exists(path) == false)
return null;
StreamReader? file = File.OpenText(path);
if (file != null)
{
JsonTextReader reader = new JsonTextReader(file);
JArray json = (JArray)JToken.ReadFrom(reader);
string? str = JsonConvert.SerializeObject(json);
file.Close();
return JsonConvert.DeserializeObject<List<Dungeon>>(str);
}
break;
}
Utilities.cs의 LoadFile() 메서드. 열거형 LoadType을 선언해 Switch문을 통해 일치하는 LoadType의 파일을 읽어올 수 있다.
으악, 정말 힘들었다. 객체 지향 프로그래밍을 하는 것이 너무 어렵다... 아직 익숙하지 않아서 더욱 그런 것 같다.
오늘은 팀원분들과 코드를 병합하며 소통을 했다. 다행히 크게 문제 없이 잘 넘어갈 수 있었다.
오늘부터 코드카타를 시작했다. 아직은 쉬운 문제이긴 한데, 흠... 아무튼 꾸준히 알고리즘 문제를 풀도록 하자. 정말 신기한 점은 백준허브를 통해 프로그래머스에서 문제를 풀면 자동으로 Github에 연동이 된다는 점이다!
백준허브 ▼
https://github.com/BaekjoonHub/BaekjoonHub
세상엔 정말 천재들이 많은 것 같다.