ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 세번째 포켓몬, 썬더 구현 (변화기, 생구)
    포켓몬 배틀 구현 2022. 3. 16. 21:53

    처음 목표로 한 3마리,

    에이스번, 자시안 그리고 마지막 썬더 구현 차례이다.

    썬더 구현까지 마치면,

    포켓몬 3마리의 구현이 완료되었으므로

    드디어 3:3 배틀을 할 최소한의 조건이 달성된다.

     

    자 이제 썬더는 무엇을 구현해야하냐면

     

    1.10만볼트 - 상태이상 "마비"

    2.변화스킬 "날개쉬기"

    3.폭풍 - 상태이상 "혼란"

    4.특성 "정전기"

    5.아이템 "생명의 구슬"

     

    이렇게 다섯가지이다.

    그럼 일단은, 구현하기 쉬울것같은 마비부터 구현해보자.

     

     

    if (a[aa][skillnumber][6] == "마비" && b[bb][5][0] == "n"
                && rd < stoi(a[aa][skillnumber][7]) && b[bb][0][1] != "전기" && b[bb][0][2] != "전기") {
                //마비는 상대가 전기타입이 아닐때 발동
                if (s == 1) cout << "상대 ";
                cout << b[bb][0][11] << "은 마비되어 기술이 나오기 어려워졌다!\n";
                int spd = stoi(b[bb][0][8]);
                spd /= 2;
                string ssppdd = to_string(spd); // 마비는 상대의 스피드를 절반으로 깎음
                b[bb][0][8] = ssppdd;
                b[bb][5][3] = "y";
                b[bb][5][0] = "y";
            }

     

    마비는 다른 상태이상과 같이 "부가효과" 함수에서 마비를 거는것과 스피드 감소를 구현한다.

    다른 점이 있다면

    화상과 독은 "후처리" 함수에서 상태이상 데미지 계산을 했지만,

    마비의 경우 "명중" 함수에서 일정 확률에 따라 스킬을 실패하게 만든다.

     

    일단 "부가효과"에서의 구현의 경우 화상 코드를 복붙해서 수정하면 금방 끝난다.

    상태이상에 걸리지 않는 타입을 "불꽃"에서 "전기"로 변경하고

    깎이는 대상을 공격력에서 스피드로 변경하고,

    이런저런 값들을 수정하면 끝.

     

    까먹고 안고쳐서 스피드 대신 공격력이 깎이고 있었는데

    한참 뒤에 랭크 변화 파드 디버깅하다 문제점을 찾은건 안비밀(...)

     

    if (p[pn][5][3] == "y" && mabi < 25) { 
                if (k == 0) cout << "상대 "; //마비 상태이상일때 마비 변수가 25 미만이면 스킬 사용 불가
                cout << p[pn][0][11] << s1(p, pn) << "몸이 저려서 움직일 수 없다!\n"; 
            }

    이후 "명중" 함수에서

    마비 상태이상이 발동되면 바로 함수를 끝내 공격이 취소되게 한다.

     

     

    if (a[aa][skillnumber][6] == "혼란" && b[bb][5][4] == "n" && rd < stoi(a[aa][skillnumber][7])) {
                //혼란은 다른 상태이상과 중첩되므로 조건이 약간 다름.
                if (s == 1) cout << "상대 ";
                cout << b[bb][0][11] << "은 혼란에 빠졌다!\n";
                b[bb][5][4] = "y";
                int cr = rand() % 4 + 2;
                b[bb][5][5] = to_string(cr);
                //혼돈이 걸릴 턴 수. 2부터 5까지 랜덤 값.
    }

     

    다음은 스킬 "폭풍"의 상태이상 "혼란"

    혼란은 마비, 화상, 독, 얼음, 수면 다섯 상태이상과 달리

    다른 상태이상과 중첩되며, 교체시 사라진다.

    그렇기에 조건문이 살짝 다르다.

     

    또한 혼란은 1~4턴동안 지속되므로

    2~5의 랜덤 값을 추가로 뽑고

    매 턴마다 이를 1씩 깎아 0이 되면 혼란이 풀리게 한다.

     

    혼란을 거는 파트는 "부가효과" 함수에서,

    혼란으로 인한 자해는 "명중" 함수에서,

    교체시 혼란 상태이상이 풀리는것은 "교체" 함수에서 따로 구현해준다.

     

    부가효과 파트는 다른 상태이상 코드를 복붙해서 살짝 수정해주면 끝나고

     

    if (p[pn][5][4] == "y") {//혼란 상태이상에 걸린 상태일 경우
                    int chaoscounter = stoi(p[pn][5][5]) - 1;;
                    p[pn][5][5] = to_string(chaoscounter);//혼란 상태이상의 남은 턴 수 - 1
                    if (chaoscounter == 0) {//해당 값이 0이면
                        if (k == 0) cout << "상대 ";
                        cout << p[pn][0][11] << s1(p, pn) << "혼란이 풀렸다!\n";
                        p[pn][5][4] = "n";//혼란 해제
                    }
                    else { 
                        if (k == 0) cout << "상대 ";
                        cout << p[pn][0][11] << s1(p, pn) << "혼란에 빠져있다!\n"; 
                    } //아닐 경우 혼란 유지
                }
                if (p[pn][5][4] == "y" && chaos < 33) {//혼란 상태이상일때 혼란 변수가 33 미만이면
                    cout << "영문도 모른채 자신을 공격했다!\n";
                    int randomnum = (rand() % 16) + 85;
                    int cdamage = (22 * 40 * stoi(p[pn][0][4]) / 50 / stoi(p[pn][0][6]));
                    cdamage = (cdamage + 2) * randomnum / 100;
                    //일정 데미지를 자신에게 입힘. 
                    //위력은( 22 * 40 * 본인 공격력 /50 / 본인 방어력 * 랜덤수 /100
                    if (cdamage >= plife[pn])
                        cdamage = plife[pn];
                    //데미지가 현재 체력 이상이면 현재 체력 만큼만 데미지
                    if (k == 0) cout << "상대 ";
                    cout << p[pn][0][11] << s1(p, pn) << cdamage << "의 데미지를 입었다! (";
                    cout << plife[pn] << "-" << cdamage << "=";
                    plife[pn] -= cdamage;
                    cout << plife[pn] << ")\n";
                    if (plife[pn] <= 0) {//라이프가 0이되면 텍스트 출력
                        if (k == 0) cout << "상대 ";
                        cout << p[pn][0][11] << s1(p, pn) << "쓰러졌다!\n";
                        bt[1] = -1;//공격한 쪽이 쓰러졌으므로 bt[1]=-1
                    }
                }

    "명중"파트에서의 혼란은 위의 마비파틀르 복붙...했다만 추가해야할게 좀 많다.

    그냥 공격이 스킵되는 "마비"와 달리,

    "혼란"은 자신에게 자해 데미지를 입히고 공격이 취소된다.

     

    일단 혼란 턴수가 끝나면 혼란이 끝나는걸 가장 먼저 구현하고,

    아닐경우 "혼란에 빠져있다"라는 텍스트는 공통으로 출력한다.

     

    이때 1/3확률로 자해 데미지를 입는데,

    해당 데미지의 위력은 40, 그리고 때린쪽의 공격력과 방어력으로 데미지가 계산된다.

     

    이후 자해 데미지가 현재 체력 이상이면 해제 체력 만큼만 데미지를 주고,

    이로 인해 체력이 0이되면 "쓰러졌다" 텍스트를 출력,

    선공측이 기절했을때 후공측의 반격이 실패하도록 bt[1] 값을 -1로 바꿔 전달한다.

     

     

    //교체한 포켓몬의 "혼란" 상태이상 초기화
        p[pp][5][4] = "n";

    그리고 "교체이벤트" 함수의 혼란 초기화

    혼란이 걸려있든 말든 혼란 상태를 "n"으로 초기화한다.

     

     

    그리고 하나더, 이는 나중에 구현한건데...

    int mj = rand() % 100;
        //명중 관련 랜덤 수.
        //해당 숫자가 스킬의 명중률보다 낮으면 명중, 높으면 빗맞음.
        if ((p[pn][number][0] == "폭풍" || p[pn][number][0] == "번개") && ps[2] == 1) {
            mj = 0; //날씨가 비 상태면 폭풍과 번개의 명중률은 100%가 된다.
        }

    "폭풍" 스킬은 날씨 상태가 "비"이면 명중률이 100%가 된다.

    "명중" 함수에서 0부터 99의 값 mj가 명중률보다 낮으면 명중, 아니면 빗맞는데

    설명 그대로 명중률을 100으로 만드는것보단

    그냥 mj를 0으로 바꾸는게 훨씬 간단하고 쉬워서 

    (어차피 100% 명중하게 되는건 똑같고)

    위와같이 구현했다.

     

    처음엔 "비"라는 날씨가 구현이 안되어있어서 스킵했는데

    이후 "가이오가"를 구현하며 "비" 날씨를 구현할때 이를 추가로 구현하였다.

     

     

     

     

    이제 썬더의 스킬 폭풍, 10만볼튼느 구현 완료,

    열풍은 부가효과가 화상인데 이는 에이스번때 구현했으니 스킵.

    마지막 남은 기술은 "날개쉬기"로, 첫 변화기이다.

     

     

     

    날개쉬기는 체력의 1/2만큼 회복하는 회복기이다.

    이는 지금까지 써왔던 "공격스킬" 함수가 아닌

    "변화스킬" 함수에서 구현한다.

     

    void 변화스킬(string p[][one][two], string s[][one][two],
        int pn, int sn, int number, int plife[], int slife[], int k, int ps[], int bt[]) {
        if (skillname == "날개쉬기" || skillname =="HP회복" || skillname == "아침햇살") {
            //날개쉬기 : 체력을 절반 회복한다.
            if (plife[pn] == stoi(p[pn][0][3])) {
                printf("더이상 회복할 수 없다!\n");
            }//이미 풀피면 회복할 수 없다.
            else {
                if (k == 0)cout << "상대 ";
                cout << p[pn][0][11] << "의 체력이 회복되었다! (";
                cout << plife[pn];
                int hill = stoi(p[pn][0][3]) / 2; //회복량은 최대 체력의 절반
                if (skillname == "아침햇살" && ps[2] == 1)
                    hill /= 2;
                if ((plife[pn] + hill) > stoi(p[pn][0][3])) { hill = stoi(p[pn][0][3]) - plife[pn]; }
                //단 풀피 이상은 회복 불가능하므로, 체력이 절반 이상이면 회복량은 풀피 - 현재체력
                plife[pn] += hill;//회복 처리
                cout<<"+"<< hill<<"="<<plife[pn]<<")\n";//회복량 프린트
            }
        }

    변화스킬 함수는 변화기를 모아놓은 함수이다.

    모든 스킬들에 데미지 계산 과정이 존재하는 공격기들과 달리

    변화 스킬은 모든 스킬들이 공유하는 공통점이 존재하지 않는다.

    즉 사용하느 타이밍이 동일하여 모아놓았을뿐,

    서로간의 공통점이 없어 "변화스킬" 함수는 별도의 설명을 하지 않았다.

    각 스킬을 추가할때 개별적으로 설명하면 되니까.

     

    쨌든 날개쉬기 구현 파트를 보자면,

    지금은 날개쉬기와 비슷한 회복기 hp회복과 아침햇살도 추가로 if문으로 확인하고

    체력을 절반 회복, 회복 이후 체력이 풀피 이상이면 풀피 만큼만 회복시킨다.

     

    "아침햇살"의 경우 날씨에 따라 회복량이 변하는데 이는 

    아침햇살을 사용하는 "솔가레오"때 추가로 설명하도록 하고,

     

    if (skillname == "날개쉬기") {
                    if (k == 0)cout << "상대 ";
                    cout << p[pn][0][11] << "의 비행타입이 사라졌다!\n";
                    for (int i = 6; i < 11; i++) {
                        for (int j = 0; j < two - 1; j++) {
                            p[pn][i][j] = "null";
                        }
                    } 
                    if (p[pn][0][11] == "썬더") {
                        p[pn][0][2] = "null";
                        p[pn][7][0] = "땅";
                        p[pn][8][0] = "전기";
                        p[pn][8][1] = "비행";
                        p[pn][8][2] = "강철";
    
                    }
                }

    또한 날개쉬기의 숨은 효과로 시전 포켓몬의 비행 타입을 한턴동안 없애준다.

    이게 왜 중요하냐면,

     

    하필 줄기차게 나오는게 썬더고

    하필 너도 나도 1순위로 채용하는 기술이 지진이고

    하필 썬더는 평소에 지진 0배, 날개쉬기시 지진에 2배 데미지를 입는다.

    썬더의 날개쉬기 타이밍에 맞춰 지진을 날리는 심리전은

    생각 이상으로 빈번하게 발생하기에 꼭 구현해야하는 파트이다.

     

    if (skillname == "날개쉬기") {
                    if (k == 0)cout << "상대 ";
                    cout << p[pn][0][11] << "의 비행타입이 사라졌다!\n";
                    for (int i = 6; i < 11; i++) {
                        for (int j = 0; j < two - 1; j++) {
                            p[pn][i][j] = "null";
                        }
                    } 
                    if (p[pn][0][11] == "썬더") {
                        p[pn][0][2] = "null";
                        p[pn][7][0] = "땅";
                        p[pn][8][0] = "전기";
                        p[pn][8][1] = "비행";
                        p[pn][8][2] = "강철";
    
                    }
                }

    일단 날개쉬기 파트에서,

    날개쉬기를 사용한 포켓몬의 방어상성을 전부 초기화해준다.

    그리고 사용한 포켓몬에 따라 방어상성을 새로 추가해준다.

    썬더의 경우 전기 비행타입이고

    날개쉬기를 사용하면 단일 전기타입이 되므로

    썬더의 두번째 타입 비행을 null로 바꾸고

    방어상성을 단일 전기타입 상성으로 바꿔준다.

     

    그럼 이제 턴이 끝나면 이를 원상태로 복귀시켜놔야하는데,

    이는 "후처리" 함수에서 처리한다.

     

       //날개쉬기를 사용한 썬더의 타입 초기화
        for (int i = 1; i < 4; i++) {
            if (p[i][0][11] == "썬더") {
                for (int k = 6; k < 11; k++) {
                    for (int j = 0; j < two - 1; j++) {
                        p[i][k][j] = "null";
                    }
                }
                p[i][0][2] = "비행";
                p[i][7][0] = "얼음";
                p[i][7][1] = "바위";
                p[i][8][0] = "강철";
                p[i][8][1] = "비행";
                p[i][8][2] = "격투";
                p[i][8][3] = "벌레";
                p[i][8][4] = "풀";
                p[i][10][0] = "땅";
            }
            if (s[i][0][11] == "썬더") {
                for (int k = 6; k < 11; k++) {
                    for (int j = 0; j < two - 1; j++) {
                        s[i][k][j] = "null";
                    }
                }
                s[i][0][2] = "비행";
                s[i][7][0] = "얼음";
                s[i][7][1] = "바위";
                s[i][8][0] = "강철";
                s[i][8][1] = "비행";
                s[i][8][2] = "격투";
                s[i][8][3] = "벌레";
                s[i][8][4] = "풀";
                s[i][10][0] = "땅";
            }
        }

    썬더가 날개쉬기를 사용했는지 안했는지는 확인할 필요 없고,

    그냥 모든 썬더의 두번째 타입을 비행으로,

    방어 상성을 전기&비행 타입의 방어 상성으로 수정해버리면 된다.

    날개쉬기를 썼다면 원래 값으로 돌아올것이고,

    안썼다면 값이 바뀌지 않을것이다.

     

    이러면 썬더의 4가지 스킬은 전부 구현 완료!

    다음은 구현해야 하는것은 썬더의 특성 "정전기"

    상대방이 접촉기로 썬더를 공격하면 33% 확률로 마비 상태이상에 걸린다.

     

    이러한 특성은 상대방이 썬더를 공격한 "다음" 발생하는 이벤트이므로

    부가효과는 아니지만 "부가효과" 함수에서 처리한다.

     //썬더의 특성 정전기는 공격 당할때 발동하므로 "즉발특성"함수가 아닌 "부가효과" 함수에서 따로 처리함
        if (b[bb][0][12] == "정전기" && a[aa][skillnumber][11] == "y" && a[aa][5][0] == "n"
            && a[aa][0][1] != "전기" && a[aa][0][2] != "전기" && trd < 33) {
            //맞은쪽의 특성이 정전기이고, 상대가 사용한 기술이 접촉기이고, 상대가 상태이상에 걸리지 않았고
            //상대가 전기 타입이 아니면 33% 확률로 상대를 마비시킴
            printf("[특성 정전기]\n");
            if (s == 0) cout << "상대 ";
            cout << a[aa][0][11] << s1(a, aa) << "마비되어 기술이 나오기 어려워졌다!\n";
            int spd = stoi(a[aa][0][8]);
            spd /= 2;
            string ssppdd = to_string(spd); // 마비는 상대의 스피드를 절반으로 깎음
            a[aa][0][8] = ssppdd;
            a[aa][5][0] = "y";
            a[aa][5][3] = "y";
        }

    이때 필요한 조건은 5가지인데

     

    1.맞은쪽의 특성이 정전기이고

    b[bb][0][12] == "정전기"

     

    2.때린쪽의 사용한 기술이 접촉기이고

     a[aa][skillnumber][11] == "y"

    *모든 스킬의 접촉 여부는 스킬의 [11]칸에 "n" 또는 "y"로 저장되어있다.

     

    3.때린쪽이 상태이상에 걸려있지 않고

    a[aa][5][0] == "n"

     

    4.때린쪽의 타입이 전기타입이 아니고

    a[aa][0][1] != "전기" && a[aa][0][2] != "전기"

     

    5.33% 확률로 발생한다.

    trd < 33

    지금까지 구현한 모든 부가효과는 랜덤 카운터로 int 변수 rd를 사용하였는데,

    왜냐하면 해당 부가효과들은 절대로 동시에 발동될 수 없기 떄문이었다.

    다만 해당 부가효과는 때릴때가 아닌 맞을때 발동되므로 다른 부가효과 동시에 발동 가능하다.

    그러므로 두 부가효과가 독립적으로 작용하도록, 랜덤 카운터를 trd로 새로 선언하여 사용하였다.

     

    쨌든 위 다섯가지 조건이 전부 부합하면

    공격측은 마비 상태이상에 걸린다.

    해당 코드는 맨 위의 "마비" 상태이상 부가효과 코드와 완전히 동일하므로 설명은 생략.

     

     

     

     

    이제 마지막, 아이템 생명의 구슬만 구현하면 끝난다.

    생명의 구슬은 주는 데미지를 1.3배로 늘리는 대신

    공격할때마다 최대 체력의 10%만큼 데미지를 입는다.

     

    즉 데미지를 1.3배로 늘리는 파트와

    공격할때마다 최대 체력의 10% 데미지를 받는 파트 2가지를 구현하면 된다.

     

    전자는 "위력증감" 함수에서,

    후자는 "부가효과" 함수에석 구현한다.

     

    if (p[pp][0][13] == "생구") {
                damage = damage * 13 / 10;
                //지닌 물건이 생명의 구슬인 경우 데미지가 1.3배가 된다.
            }

    데미지 1.3배는 정말 쉽다.

    그냥 아이템이 생구면 데미지를 1.3배하면 끝

     

    if (a[aa][0][13] == "생구") {
            //공격한 쪽의 도구가 생명의 구슬인 경우
            cout << "[아이템 생명의구슬]\n";
            if (s == 0) cout << "상대 ";
            cout << a[aa][0][0] << "의 생명력이 조금 깎였다! ";
            bandong = (stoi(a[aa][0][3]) / 10);
            //생명의 구슬로 인한 데미지는 전체 체력의 1/10
            cout << "(" << alife[aa];
            alife[aa] = alife[aa] - bandong;
            cout << "-" << bandong << "=" << alife[aa] << ")\n";
            //체력이 얼마나 깎였는지 출력
            if (alife[aa] <= 0) {
                cout << alife[aa] << s1(a, aa) << "쓰러졌다!";
                bt[1] = -1;
                return;
            }
        }

     

    10% 데미지를 입는 파트는

    이전에 구현한 "반동" 부가효과를 복붙하여 수정하면 된다.

    변수값만 수정하면 구조는 100% 동일하기 때문에 설명은 생략!

     

     

    구현 완료한 모습.

Designed by Tistory.