상세 컨텐츠

본문 제목

0923 TIL - Re: C#으로 Text RPG 만들기 2

스파르타 코딩캠프/'24 Today I Learned

by lucar 2024. 9. 24. 08:48

본문

좀 많이 작성했다.

 

while (p.hp > 0)
{
    string input = Console.ReadLine();
    string[] commands = input.Split(' ');
    try
    {
        if (commands[0] == "show")
        {
            if (commands[1] == "inventory" || commands[1] == "i")
            {
                p.ShowInventory();
            }
            else if (commands[1] == "stat" || commands[1] == "s" || commands[1] == "stats" || commands[1] == "status")
            {
                p.Status();
            }
            else if (place == Place.Shop && (commands[1] == "buy" || commands[1] == "b"))
            {
                int i = 1;
                foreach (Item item in shop.sellList)
                {
                    Console.Write($"{i}. ");
                    item.Info();
                    i++;
                }
            }
            else if (place == Place.Shop && (commands[1] == "sell" || commands[1] == "s"))
            {
                int i = 1;
                foreach (Item item in p.Inventory)
                {
                    Console.Write($"{i}. ");
                    item.Info();
                    i++;
                }
            }
            else
            {
                Console.WriteLine("명령어를 다시 입력해주세요.");
            }

        }
        else if (commands[0] == "move")
        {
            if (commands[1] == "town" || commands[1] == "t")
            {
                place = Place.Town;
                Action.Town(weather, time);
            }
            else if (commands[1] == "shop" || commands[1] == "s")
            {
                place = Place.Shop;

                Action.Shop();
            }
            else if (commands[1] == "inn" || commands[1] == "i")
            {
                place = Place.Inn;
                Action.Inn(p, weather, ref time);
            }
            else if (commands[1] == "dungeon" || commands[1] == "d")
            {
                place = Place.Dungeon;
                Action.Dungeon();
            }
            else
            {
                Console.WriteLine("명령어를 다시 입력해주세요.");
            }
        }
        else if (commands[0] == "?" || commands[0] == "/?")
        {
            Console.WriteLine("show 명령어를 통해 스테이터스와 인벤토리를 확인 가능합니다.");
            Console.WriteLine("show inventory show status");
            Console.WriteLine("move 명령어를 통해 이동 가능합니다.");
            Console.WriteLine("move town, move shop, move inn, move dungeon");
            Console.WriteLine("모든 하위 명령어는 약자로 실행 가능합니다.");
            Console.WriteLine("show i, show s, move t, move s 등");
            Console.WriteLine("인벤토리 내에서 equip (물품번호)를 통해서 아이템을 장착 가능합니다.");
            if(place == Place.Shop)
            {
                Console.WriteLine("show buy를 입력해 구매 목록을, show sell을 입력해 판매 목록을 볼 수 있습니다.");
                Console.WriteLine("구매목록에서는 buy (물품번호), 판매 목록에서는 sell (물품번호)를 입력하여 사거나 팔 수 있습니다.");
            }
            else if(place == Place.Dungeon)
            {

            }
        }
        else if (commands[0] == "buy" && place == Place.Shop)
        {
            if (int.TryParse(commands[1], out int output) == true)
            {
                shop.BuyItem(output, p);
                Console.WriteLine($"현재 소지금 : {p.gold} G");
            }
        }
        else if (commands[0] == "sell" && place == Place.Shop)
        {
            if (int.TryParse(commands[1], out int output) == true)
            {
                shop.SellItem(output, p);
                Console.WriteLine($"현재 소지금 : {p.gold} G");
            }
        }
        else if (commands[0] == "equip")
        {
            if(int.TryParse(commands[1],out int output) == true)
            {
                p.EquipItem(output);
            }
        }
        else if (commands[0] == "enter" && place == Place.Dungeon)
        {
            if (int.TryParse(commands[1], out int output) == true)
            {
                dun.EnterDungeon(output, p);
            }
        }
        else
        {
            Console.WriteLine("명령어를 다시 입력해주세요.");
        }
    }
    catch (IndexOutOfRangeException)
    {
        commands = new string[2];
        Console.WriteLine("명령어가 너무 짧습니다.");
    }

1,2,3을 입력받는 것 보다 명령어로 접근하는게 좀 더 게임하는 느낌이 날 것 같아서 이렇게 만들어봤다.

 

show, move 등의 상위 명령어와

i, s, 등의 하위 명령어의 조합으로 게임을 진행할 수 있게 만들었다.

 

Action 이라는 새로운 클래스를 만들어서 동작에 따라 상호작용이 가능하게 만들긴 했는데

public static class Action
{
    public static void Intro(Player p)
    {
        Console.WriteLine("Sparta 던전에 오신 것을 환영합니다.");
        Console.WriteLine("당신의 이름을 알려주세요.");
        p.name = Console.ReadLine();
        Console.WriteLine("당신의 직업은?");
        Console.WriteLine("1. 전사, 2. 도적, 3. 수강생");
        switch (IsInt(Console.ReadLine(), 4))
        {
            case 1: p.job = "전사"; p.defense = 5; break;
            case 2: p.job = "도적"; p.damage = 15; break;
            case 3: p.job = "수강생"; p.gold = 2000; break;
            case 4: p.job = "튜터"; p.damage = 30; p.defense = 20; p.gold = 5000; break;
        }
        Console.WriteLine("튜토리얼을 보시겠습니까? (y/n)");
        string a = Console.ReadLine();
        if (a == "y")
        {
            Console.WriteLine("이 게임은 던전에 진입으로 골드를 벌어서 그 골드로 아이템을 구입해 강해지는 성장형 RPG입니다.");
            Console.WriteLine("방어력에 비례해 던전의 성공확률이 증가하며 공격력에 비례해 추가적인 보상을 얻을 수 있습니다.");
            Console.WriteLine("게임의 진행은 명령어로 진행되며 자세한 내용은 플레이 중 ?를 입력하는 것으로 확인 할 수 있습니다.");
            Console.WriteLine($"{p.name}님의 순탄한 여정을 빕니다.");
        }
        else Console.WriteLine($"{p.name}님의 순탄한 여정을 빕니다.");

    }
    public static void Town(Weather w, Time t) //마을에서 날씨 확인가능
    {
        if (w == Weather.Sunny)
        {
            if(t == Time.Day)
            {
                Console.WriteLine("\r\n힘세고 강한 아침");
                Console.WriteLine("눈부신 햇볕이 마을을 비춘다.");
            }
            else
            {
                Console.WriteLine("\r\n구름 한 점 없는 밤이다.");
                Console.WriteLine("밤 하늘은 수 많은 별 들로 반짝인다.");
            }
        }
        else if (w == Weather.Overcast)
        {
            if (t == Time.Day)
            {
                Console.WriteLine("\r\n온 세상이 하얀 구름으로 뒤덮인 것 같다.");
                Console.WriteLine("그렇게 어둡지는 않다.");
            }
            else
            {
                Console.WriteLine("\r\n새까만 밤이다.");
                Console.WriteLine("빛이 없으면 한 치 앞이 보이지 않는다.");
            }
        }
        else if (w == Weather.Rain)
        {
            if (t == Time.Day)
            {
                Console.WriteLine("\r\n비 냄새가 온 사방에 가득하다.");
                Console.WriteLine("습하고 축축하다.");
            }
            else
            {
                Console.WriteLine("\r\n어둡고 습하고 축축하다.");
                Console.WriteLine("빗소리와 개구리 소리가 가득하다.");
            }
        }
        else if (w == Weather.Mist)
        {
            if (t == Time.Day)
            {
                Console.WriteLine("\r\n짙은 안개가 끼였다.");
                Console.WriteLine("앞이 잘 안보인다. 여기가 어디지?");
            }
            else
            {
                Console.WriteLine("\r\n금방이라도 뭐가 튀어나올 듯한 으스스한 느낌이다.");
                Console.WriteLine("이런 날에 굳이 밖으로 나가야할까?");
            }
        }
        Console.WriteLine("\r\n그럼 이제 뭘 할까?");
        Console.WriteLine("명령어 확인은 '?'를 입력");
    }
    public static Weather Inn(Player p, Weather w, ref Time t) //휴식을 취하면 체력이 모두 회복되고 날씨가 바뀜
    {
        Console.WriteLine("모험자 여관에 온 것을 환영하네, 낯선 이여");
        Console.WriteLine("방 값은 500 G라네, 잠시 쉬고 가겠는가? (y/n)");
        Console.WriteLine($"현재 소지금 : {p.gold} G");
        bool isChar = char.TryParse(Console.ReadLine(), out char output);
        if(isChar == true)
        {
            if(output == 'y')
            {
                if (p.gold >= 500)
                {
                    p.gold -= 500;
                    Console.WriteLine("500G 치고 초라한 방이다.");
                    Console.WriteLine("체력이 모두 회복되었다.");
                    p.hp = p.maxHp;
                    if (t == Time.Day) t = Time.Night;
                    else t = Time.Day;
                    w = GetRandomWeather();
                    Town(w, t);
                    return w;
                }
                else
                {
                    Console.WriteLine("돈이 모자란 손님은 받지 않는다!");
                    Console.WriteLine("당신은 매몰차게 쫒겨났다.");
                    Town(w, t);
                    return w;
                }
            }
            else if(output == 'n')
            {
                Console.WriteLine("바쁜데 시간낭비 하지 말고 나가!");
                Town(w, t);
                return w;
            }
            else
            {
                Console.WriteLine("뭐라고 하는지 모르겠군 y/n으로 답하게");
                return Inn(p, w,ref t);
            }
        }
        else
        {
            Console.WriteLine("뭐라고 하는지 모르겠군 y/n으로 답하게");
            return Inn(p, w,ref t);
        }
        Weather GetRandomWeather()
        {
            Random random = new Random();
            var a = Enum.GetValues(enumType: typeof(Weather));
            return (Weather)a.GetValue(random.Next(0, a.Length));
        }
    }

    public static void Shop() //상점에서 물건 구매와 판매 가능
    {
        Console.WriteLine("어서 오게나, 돈만 충분하다면 언제든지 환영일세");
        Console.WriteLine("무엇을 하겠는가?");
        Console.WriteLine("show buy를 입력해 구매목록을, show sell을 입력해 판매목록을 볼 수 있습니다. 다시 보려면 ?를 입력해주세요.");
    }

    public static void Dungeon() //던전은 3단계로 나뉨
    {
        Console.WriteLine("눈 앞에 3가지의 입구가 있다.");
        Console.WriteLine("차례대로 쉬움, 보통, 어려움이다.");
        Console.WriteLine("1. 쉬움 (권장 방어력 5)");
        Console.WriteLine("2. 보통 (권장 방어력 10)");
        Console.WriteLine("3. 어려움 (권장 방어력 20)");
        Console.WriteLine("던전에 입장하려면 enter (번호)를 입력하자.");
    }

    public static int IsInt(string input, int maxnum = 100)
    {
        bool isInt;
        int numb;
        isInt = int.TryParse(input, out numb);
        if (isInt == false)
        {
            Console.WriteLine("숫자로 다시 입력해주세요.");
            return IsInt(Console.ReadLine(), maxnum);
        }
        else if (numb > maxnum)
        {
            Console.WriteLine("범위 내의 숫자로 다시 입력해주세요.");
            return IsInt(Console.ReadLine(), maxnum);
        }
        else return numb;
    }

}

시간과 날씨에 따라 상호작용은 아직 구현이 안되어있는 상태이다. 그냥 기분 내기로 추가했다.

 

오늘 작성하면서 가장 애를 먹였던게 바로

public void EquipItem(int numb)
{
    //인벤토리를 열고
    //equip 번호 를 통해 해당 아이템을 장착
    if (EquipItems[0] == null && Inventory[numb-1].place == Item.EquipPlace.Weapon)
    {
        EquipItems[0] = Inventory[numb-1];
        Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
    }
    else if (EquipItems[0] != null && Inventory[numb-1].place == Item.EquipPlace.Weapon)
    {
        Console.WriteLine("이미 장착된 슬롯 입니다. 교체하시겠습니까? (y/n)");
        string input = Console.ReadLine();
        if (input == "y")
        {
            EquipItems[0] = Inventory[numb-1];
            Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
        }
    }
    if (EquipItems[1] == null && Inventory[numb-1].place == Item.EquipPlace.Helmet)
    {
        EquipItems[1] = Inventory[numb-1];
        Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
    }
    else if (EquipItems[1] != null && Inventory[numb-1].place == Item.EquipPlace.Helmet)
    {
        Console.WriteLine("이미 장착된 슬롯 입니다. 교체하시겠습니까? (y/n)");
        string input = Console.ReadLine();
        if (input == "y")
        {
            EquipItems[1] = Inventory[numb-1];
            Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
        }
    }
    if (EquipItems[2] == null && Inventory[numb-1].place == Item.EquipPlace.Armor)
    {
        EquipItems[2] = Inventory[numb-1];
        Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
    }
    else if (EquipItems[2] != null && Inventory[numb-1].place == Item.EquipPlace.Armor)
    {
        Console.WriteLine("이미 장착된 슬롯 입니다. 교체하시겠습니까? (y/n)");
        string input = Console.ReadLine();
        if (input == "y")
        {
            EquipItems[2] = Inventory[numb-1];
            Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
        }
    }
    if (EquipItems[3] == null && Inventory[numb-1].place == Item.EquipPlace.Accessory)
    {
        EquipItems[3] = Inventory[numb-1];
        Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
    }
    else if (EquipItems[3] != null && Inventory[numb-1].place == Item.EquipPlace.Accessory)
    {
        Console.WriteLine("이미 장착된 슬롯 입니다. 교체하시겠습니까? (y/n)");
        string input = Console.ReadLine();
        if (input == "y")
        {
            EquipItems[3] = Inventory[numb-1];
            Console.WriteLine($"{Inventory[numb - 1].name}을(를) 장착했습니다.");
        }
    }
    //아이템 수치 만큼 능력치 상승
    try
    {
        for (int i = 0; i < EquipItems.Length; i++)
        {
            if (EquipItems[i] == null) break;
            addDamage += EquipItems[i].damage;
            addDefense += EquipItems[i].defense;
        }
    }
    catch(NullReferenceException)
    {
        for(int i = 0; i < EquipItems.Length; i++)
        {
            addDamage += EquipItems[i].damage;
            addDefense += EquipItems[i].defense;
        }
    }

아이템 장착에 관한 거였는데

생각하면 할 수록 뭔가 하나씩 에러가 있어서 하드 코딩으로 일단 흩뿌려놨다.

 

문제는 이거였다

 

아이템을 착용하고 교체하고 해제할 때에 어떻게 설정할 것인가?

 

처음에는 Item 클래스에 isEquip이라는 부울 필드를 추가하여 조절해보려했다.

이 경우에는 장착된 장비에 [E]표식을 붙이는데에는 큰 문제가 없었지만

동일한 장비칸에 장착하는 경우 이 전 장비에 접근하기가 쉽지 않았다.

 

다음에는 Item클래스가 아닌 Player클래스에 isEquip 필드를 추가해보았다.

마찬가지로 어떤 장비가 장착되었고 그 장비에 직접적으로 접근할 수가 없어서 유기

 

마지막으로 장착 칸을 따로 만들어주었다.

인벤토리 내부에서 장착을 하면 4칸짜리 배열에 추가하고 Item클래스의 place에 따라서 장착될 수 있는

칸을 제한하였다. 이 방법이 제일 무난하고 교체 및 해제의 구현도 간단했지만

 

장착된 아이템에 [E] 표식을 남기는데에는 많은 어려움이 따랐다.

 

for문을 통해 인벤토리 리스트와 장착 배열을 비교하여 같은 애들에게 [E]표식을 달아주려고 했는데

NullReferenceException에러가 나를 괴롭힌다.

 

자꾸 널 값을 참조할 수 없다고 하는데 

예외처리 부분도 아직 공부가 부족해서 당장은 해결하기 힘들어 보인다.

 

그래서 아예 null이면 break를 쳐버리는 방식을 사용했는데

아마 반복문 내부에 continue를 통해서 해결할 수 있을 것 같다.

 

라고 하자마자 성공했다.

 

역시 생각을 정리할 노트 같은게 필요하다.

 

성공한 줄 알았는데 하나를 장착하면 다른 건 장착을 안해도 전부 [E] 마크가 찍힌다.

응 실패야

 

밥먹고 와서 보니까 당연히 null이 아니라고 찍어버리면 다 찍히는게 맞지 않나?

인벤토리안에 있는 것 중에 이게 있다면 표식을 찍어야 하니까 수정해주자.

 

수정하니까 하나만 찍히긴 한다.

진짜 하나만 찍힌다

 

하나만 장착해도 모든 슬롯이 전부 장비 중으로 찍히는 버그가 발생했다.

무기 부분만 수정해서 확인해보자.

 

장착아이템 슬롯을 부울값으로 만들면 해결 될 것처럼 보인다.

그런데 부울 값으로 변경하면 위에 언급했듯이

장비 교체 시 해당 값 만큼 능력치를 빼주고 다시 더해줘야하는데 그 값에 접근하기가 어렵다.

부울 값으로 만들고

능력치 계산을 따로 빼서 연산시켜야하나

 

Dictionary로 따로 빼서 키값으로 연산을 한다면 이전 데이터 값에도 접근하기가 쉬울것 같다.

 

오늘 강의를 하나도 못 들은 관계로 내일 계속

관련글 더보기