ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 열일곱번째 포켓몬, 트리토돈 구현 (하품, 잠듦)
    포켓몬 배틀 구현 2022. 3. 25. 07:03

    어느덧 스무번째를 앞두고있는 지금,

    17번째 포켓몬, 트리토돈 구현이다.

     

    귀여운 생김새와 달리 막이(탱커)로써 쓰이는건 알고있었다만...

    생각보다 순위가 높아 놀랐다.

    찾아보니 특성 마중물(물타입 공격의 데미지를 무시하고 특공 1랭크 상승)덕분에

    미쳐 날뛰는 가이오가 카운터로 좋다고...

     

    그리고 지금까지 꽤 많은 막이들을 구현했는데,

    트리토돈은 지금까지 구현하지않은, 마지막 상태이상 "잠듦"을 유발하는 하품을 대부분 채용한다.

    물론 주 용도는 잠듦 상태이상보다는 교체 유도긴 하다만...

     

    쨌든 드디어 화상, 독, 마비, 혼란, 얼음, 맹독을 넘어 마지막 상태이상 잠듦을 구현한다.

     

    하품의 특이한 점은 기술을 맞은 "다음 턴"에 잠든다.

    즉 이번 턴에 하품을 맞은 상대방은,

    다음 턴에 공격을 하고 잠들지, 아니면 교체를 할지 선택하게 된다.

    보통은 잠드는 것보다 교체하는게 이득이라 교체를 하게 되는데,

    이 교체 역시 상대방의 1턴을 낭비시키는것이고

    이때 상대방이 교체할것같은 포켓몬에게 유리한 포켓몬으로 맞교체해

    기점을 잡는 등

    많은 서포터, 탱커 포켓몬들이 채용하는 기술이다.

     

    쨌든 이 "다음턴"에 잠든다는 점 때문에 구현이 어렵지 않을까 걱정했는데,

    예상외로 쉽게 구현했다.

     

    일단 하품을 구현하려면

    1.상대를 "하품" 카운트를 2로 만드는 파트

    2.매 턴 하품 카운트를 1씩 깎는 파트

    3.이때 하품 카운트가 0이 되면 상대방이 "잠듦" 상태이상에 걸리는 파트

    를 구현해야하며

     

    이 잠듦 상태이상은

    1.공격을 시도할때마다 "잠듦" 카운트를 1씩 깎는 파트

    2.잠듦으로 인해 공격이 실패하는 파트

    3.잠듦 카운트가 0이 되면 잠에서 깨어나는 파트

    3가지를 구현해야한다.

     

    쓰고 보니 많아보이네

    그럼 빨리 구현 시작.

     

     if (skillname == "하품") {
            //하품은 상대에게 영향을 미치는 변화기이지만
            //고스트 타입에게 무효되지않는다.
            if (bt[1] == 145) {
                if (k == 1) { cout << "상대 "; }
                cout << s[sn][0][11] << s1(s, sn) << "공격으로부터 몸을 지켰다!\n";
            }
            //상대가 방어를 썼을경우 막힌다
            else {
                if (k == 1) { cout << "상대 "; }
                cout << s[sn][0][11] << "의 졸음을 유도했다!\n";
                if (s[sn][5][10] == "1" || s[sn][5][0] == "y");
                else {
                    s[sn][5][10] = "2";
                }
                //[5][10]값이 1인 경우 = 상대방이 전턴에도 하품을 맞은 경우
                //[5][0]값이 y인 경우 = 상대방에게 이미 상태이상이 걸려있는 경우
            }
        }

    일단 "변화스킬"함수에 하품을 추가하고,

    이 하품도 상대방을 대상으로 하는 변화기라 "방어"에 막힌다. 이를 추가해준다.

    그 외엔 "졸음을 유도했다"라는 텍스트와 함께 

    상대방의 "하품" 카운트를 2로 만든다.

     

    이 카운트는 매 턴 깎여 0이 되면 잠듦상태로 만들게 한다.

    이미 상대방이 하품을 맞아 카운트가 1인 경우와

    상대방이 이미 상태이상에 걸린 경우는 제외한다.

     

     

     if (plife[pp] > 0) {
            p[pp][5][10] = to_string(stoi(p[pp][5][10]) - 1);
            //매 턴 하품 카운트 값을 1씩 깎는다
            //기절하였을 때에는 스킵한다.
            if (p[pp][5][10] == "0" && p[pp][5][0] == "n") {
                //해당 값이 0이 됐을때 상태이상에 걸려있지 않으면
                if (k == 0) cout << "상대 ";
                cout << p[pp][0][11] << s1(p, pp) << "잠들어 버렸다!\n";
                p[pp][5][9] = "y";
                //잠듦 처리
                p[pp][5][0] = "y";
                //상태이상 처리
                int sleep = 245;
                sleep = rand() % 3 + 2;
                //잠듦 상태이상은 1~3턴동안 유지된다
                //랜덤 값으로 2에서 4의 값을 뽑고, 적이 행동할때마다 1씩 깎아 0이 되면 풀린다.
                p[pp][5][11] = to_string(sleep);
                //남은 상태이상 턴
    
            }
        }

    그리고 "후처리" 함수에서 필드 위 모든 포켓몬의 하품 카운트를 1씩 깎고,

    이때 값이 0이 되면 잠듦 상태이상을 건다.

     

    하품을 맞았는지 안맞았는지는 확인하지 않는데,

    하품 카운트의 기본값은 -1이고 교체할때마다 -1로 초기화되기에,

    하품을 맞았을때만 양수가 되어 이후 0이 될수있기에

    이를 따로 조건문에 추가하지 않는다.

     

    잠듦 상태이상에 걸리면

    잠듦 여부 [5][9]와 상태이상 여부 [5][0]의 값을 "y"로 바꿔주고,

    잠듦 상태이상이 유지될 턴을 정한다.

     

    잠듦은 1~3턴동안 유지되는데,

    적이 행동할때마다 1씩 까여 0이 되면 풀리므로,

    잠듦 카운트는 2에서 4의 값중 랜덤으로 뽑는다.

    이 값을 [5][11]에 저장하면 하품은 구현 끝!

     

    이제 잠듦 상태이상을 구현해야한다.

     

    if( p[pn][5][9]=="y")
            p[pn][5][11] = to_string( stoi(p[pn][5][11]) - 1 );
        if (p[pn][5][9]=="y" && p[pn][5][11] != "0") {
            if (k == 0) cout << "상대 ";
            cout << p[pn][0][11] << s1(p, pn) << "쿨쿨 잠들어있다.\n";
            //스킬 사용 불가
        }
        else {
            if (p[pn][5][9] == "y" && p[pn][5][11] == "0") {
                if (k == 0) cout << "상대 ";
                cout << p[pn][0][11] << s1(p, pn) << "눈을 떴다!\n";
                p[pn][5][0] = "n";
                p[pn][5][9] = "n";
    
            }

    이는 마비, 혼란, 얼음 등 다른 상태이상들과 같이 "명중"함수에서 구현한다.

    잠듦 상태일떄 ([5][9]=="y")

    잠듦 카운트를 1 깎고

    이 값이 0이 아니면 잠듦 유지 = 공격 생략

    0이면 "눈을 떴다!"라는 텍스트와 함께 공격을 진행한다.

     

    써놓고 보니 뭔가 많았는데 구현은 금방 끝났다.

    하나 남은 상태이상 언제 구현하나 기다렸는데,

    드디어 처리하게되어 약간 뿌듯하다.

     

     

    하품 외에 다른 스킬은 hp회복, 열탕, 대지의 힘

    전부 구현 했던것들이라 더 구현할건 없어서 패스

     

     

     

     

    다음은 트리토돈의 특성 "마중물"

    위에서 말했듯 물타입 스킬을 맞으면 데미지를 받지않고 특공이 1랭크 상승하는 특성이다.

    마중물 특성이 발동되면 공격이 취소되니,

    당연히 해당 특성은 공격 전에 처리해야한다.

    그렇기에 이는 "위력증감" 함수에서 구현하게 된다

    int samsung = 1;
        for (int i = 6; i < 11; i++) { //공격한 스킬의 타입과
            for (int j = 0; j < two - 1; j++) {//맞은 포켓몬의 방어 상성을 비교하여
                if (s[ss][i][j] == skilltype) { samsung = i; }//동일한 경우가 있는지 체크한다.
            }
        }
    if (s[ss][0][12] == "마중물" && p[pp][number][1] == "물") samsung = 10;

    위력증감 함수에선 공격 기술의 타입과, 방어측 포켓몬의 방어 상성을 대조해

    위력 몇배의 공격인지 확인하는데,

    이때 무효, 즉 0배인 공격은 samsung 값이 10이 나온다.

    마중물도 이와 같으므로 위 조건에 해당하면 해당 값을 10으로 수정하게 한다.

     

    이러면 마중물로 인해 데미지를 받지 않는것은 구현됐고...

    if (mh == 0 && attack == 1) {
                                //공격기를 썼는데 무효 상성일 경우
                                //변화기의 무효 상성은 해당 함수에서 따로 처리 
                                if (p[pn][number][0] == "무릎차기") {//사용한 스킬이 무릎차기일 경우
                                    printf("하지만 실패했다!\n");
                                    무릎차기실패(p, k, pn, plife, ps, s, sn, bt);
                                }
                                else if (p[pn][number][1] == "물" && s[sn][0][12] == "마중물") {
                                    printf("[특성 마중물]\n");
                                    int mj = 321;
                                    if (k == 1) mj = 0;
                                    else if (k == 0) mj = 1;
                                    랭크(s, sn, "특공", 1, mj);
                                }
                                else
                                    printf("하지만 효과가 없는 듯하다...\n");

    이러한 무효로 인해 스킬이 실패한것은 "명중" 함수에서 처리하므로

    마중물 텍스트와 특공 상승은 "명중"함수에 추가해준다.

     

        if (p[pp][0][13] == "검은진흙") {
            if (plife[pp] < stoi(p[pp][0][3])) {
                cout << "[아이템 검은진흙]\n";
                if (k == 0) cout << "상대 ";
                cout << p[pp][0][11] << s1(p, pp) << "검은진흙으로 인해 조금 회복했다. (" << plife[pp];
                int hill = stoi(p[pp][0][3]) / 16; //회복량은 최대 체력의 1/16
    
                if ((plife[pp] + hill) > stoi(p[pp][0][3])) {
                    hill = stoi(p[pp][0][3]) - plife[pp];
                }
                //회복후 체력이 최대체력 이상이면 최대체력만큼만 회복
                plife[pp] += hill;//회복 처리
                cout << "+" << hill << "=" << plife[pp] << ")\n";//회복량 프린트
            }
        }
        if (p[pp][0][13] == "먹밥") {
            if (plife[pp] < stoi(p[pp][0][3])) {
                cout << "[아이템 먹다남은음식]\n";
                    if (k == 0) cout << "상대 ";
                cout << p[pp][0][11] << s1(p, pp) << "먹다남은음식으로 인해 조금 회복했다. (" << plife[pp];
                int hill = stoi(p[pp][0][3]) / 16; //회복량은 최대 체력의 1/16
    
                if ((plife[pp] + hill) > stoi(p[pp][0][3])) {
                    hill = stoi(p[pp][0][3]) - plife[pp];
                }
                //회복후 체력이 최대체력 이상이면 최대체력만큼만 회복
                plife[pp] += hill;//회복 처리
                cout << "+" << hill << "=" << plife[pp] << ")\n";//회복량 프린트
            }
        }

    마지막으로 아이템 먹다남은 음식 먹밥은

    매 턴 1/16만큼 체력을 회복해주는 아이템으로

    지난 글 무한다이노에서 구현한 검은진흙과 동일한 아이템이다.

    정확히는 검은진흙은 독타입 포켓몬만 회복된다는 차이점이 있지만

    이는 나중에 기술 "트릭"을 구현할때 설명하기로 하고,

    그냥 코드 복붙해서 이름만 수정해주면 끝난다.

     

     

    구현 완료된 모습

Designed by Tistory.