-
열번째 포켓몬, 누오 구현 (방어)포켓몬 배틀 구현 2022. 3. 23. 03:07
10번째 포켓몬, "누오" 구현이다.
누오 전체 포켓몬중 채용율 9위이며,
이는 3위인 메타몽, 이 메타몽도 전설 포켓몬을 복사하기 위해 쓰는것을 감안하면
비전설 포켓몬중 가장 채용률이 좋다 할수있다.
그리고 지금까지 딜탱 포지션의 랜드로스, 썬더는 있었지만
순수한 탱커 포지션, 일명 "막이" 포켓몬은 첫 구현이다.
어태커들은 대부분 구현할게 거기서 거기다보니 슬슬 구현할게 없었는데
탱커는 처음이라 오랜만에 구현해야할게 많았다.
https://pikalytics.com/pokedex/homebss/quagsire
Quagsire Series 12 Battle Stadium Singles Stats Pokédex | Pokémon | Pikalytics
Rocky Helmet 71.800% Leftovers 22.400% Sitrus Berry 1.800% Maranga Berry 1.500% Kee Berry 1.000% Figy Berry 0.600% Iapapa Berry 0.200% Mystic Water 0.200% Metronome 0.100% Rindo Berry 0.100%
pikalytics.com
스킬 채용율을 보면
1등은 hp회복, 2등은 지진, 3등은 하품, 4등은 방어, 5등은 맹독이다.
일단 hp 회복은 필수.
지진도 적당한 견제기로써 대부분 채용한다.
그리고 이제 스킬을 정해야하는데, 같은 "막이"라도 용도가 다르다.
"하품"은 상대방을 한턴 "뒤" 잠듦 상태로 만드는 기술이다.
잠듦 상태이상은 기존에 구현했던 "얼음"과 비슷하다.
상태이상동안 아무 행동도 할수없고,
다른 상태이상과 겹치지 않으며 일정 턴이 지나면 풀린다.
그리고 무엇보다, 한턴 "뒤", 즉 상대방이 다음턴에 교체하지 않았을떄 발동되기에
"잠듦" 상태이상보단 상대방의 교체를 유도하는 스킬이다.
이는 전에 말한, 3턴동안 매우 강해지는 "다이맥스"를 견제하기에 좋다.
다이맥스는 교체하면 풀리고 이후 다시 쓸수없으니까.
이렇게 상대방의 한턴을 버리게해, 자신에게 상황을 유리하게 이끌어가는것을 "기점을 잡는다" 라고 한다.
이러한 "기점잡이" 형태가 있는 반면,
상대방에게 상태이상을 걸고, 상대방의 공격을 맞으면서 버티며
상태이상 데미지로만 적을 잡는 일명 "깔짝" 형태도 존재한다.
일단 나는 다맥을 구현할 생각이 없기도하고,
무엇보다 우라오스의 특성은 "방어" 무시인데
방어를 쓰는 포켓몬이 없어서 구현을 못했다.
하품 기점잡이 형태는 "방어"를 잘 채용하지 않으므로
거의 대부분 "방어"를 채용하는 깔짝 형으로 가기로 했다.
그렇기에 채용할 다른 두 스킬은 "맹독"과 "방어"
이제 무엇을 구현해냐햐면
1.hp회복 - 썬더에서 구현 완료
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; //회복량은 최대 체력의 절반
날개쉬기와 이름만 다른 hp 절반 회복 스킬이므로
조건문에 이름만 추가하면 끝
2.지진 - 부가효과 없음
3.방어
방어는 상대방의 공격을 막는다.
다만 연속으로 사용하면 성공률이 계속 떨어지므로,
무한 방어로 적을 공격을 계속 막는것은 불가능하다.
이 방어를 구현하려면
1.사용 성공시 상대방을 공격을 막는 파트
2.연속 사용시 성공률이 떨어지는 파트
3.방어 실패, 교체, 다른 스킬 사용시 성공률이 복구되는 파트
3개를 구현해야 한다.
일단 1번파트.
방어는 변화기이므로 "변화스킬"함수에서 처리한다.
if (skillname == "방어") { int brd = rand() % 100; if (brd < stoi(p[pn][number][15])) { if (k == 0)cout << "상대 "; cout << p[pn][0][11] <<s1(p,pn)<< "방어 태세에 들어갔다!\n"; bt[1] = 145; p[pn][number][15] = to_string(stoi(p[pn][number][15]) / 3); } else { cout << "하지만 실패했다!\n"; p[pn][number][15] = "100"; } }
방어의 성공률은, [15]번 칸을 따로 배정해서 사용한다.
스킬의 명중률 값은 이미 [3]번 칸에 존재하지만,
이 명중률을 위 3가지 경우에 매번 초기화 해줘야하는데,
스킬의 이름을 if문으로 확인하고
스킬명이 방어일때 명중률을 복구하는것보다
그냥 방어 스킬에만 [15]번칸으로 명중률을 추가 할당하고
방어실패, 교체, 다른 스킬 사용시마다 모든 스킬의 [15]번 값을 100으로 초기화해버리면
훨씬 간단하게 구현 가능하다.
rand()로 임의의 값을 뽑아
해당 값이 방어의 현재 명중률보다 작을때만 방어에 성공,
아니면 방어에 실패한다.
방어 성공시엔 "방어 태세에 들어갔다"라는 텍스트와 함께
배틀 트리거 값을 145로 수정하고,
명중률도 1/3으로 깎아 2번 파트도 구현해준다.
명중률이 초기화되지 않는다면 == 즉 방어를 연속으로 사용한다면
다음 성공률은 33%, 다음은 11%다.
else if (bt[1] == 145 && p[pn][0][12] != "보이지않는주먹" && attack==1) { //공격했는데 상대가 방어했고, 공격측 특성이 방어무시가 아닌 경우 if (k == 1) { cout << "상대 "; } cout << s[sn][0][11] << s1(s, sn) << "공격으로부터 몸을 지켰다!\n"; if (p[pn][number][0] == "무릎차기") {//무릎차기 실패. 무릎차기실패(p, k, pn, plife, ps, s, sn, bt); } }
방어는 우선도가 +4이기에 대부분 선공으로 사용하게되고,
이후 후공측의 "명중" 함수에서
bt[1]값이 145 == 선공 측이 "방어"를 썼고
후공측의 특성이 "보이지않는주먹" == 방어무시가 아니면 -우라오스의 특성-
"ㅇㅇ는 공격으로부터 몸을 지켰다!"라는 텍스트와 함꼐 명중 함수를 종료한다.
무릎차기가 방어로 막힌 경우에도 실패로 취급하므로
무릎차기실패 함수를 호출한다.
else { if (bt[1] == 145 && p[pn][0][12] == "보이지않는주먹" && attack==1) { //상대가 방어했는데 공격 측 특성이 방어 무시인 경우 cout << "[특성 보이지않는주먹]\n"; cout << "해당 공격은 막을 수 없다!\n"; }
그 외에, 방어했는데 공격측 특성이
우라오스의 방어 무시 특성, "보이지않는주먹"인 경우
막을수없다! 라는 텍스트와 함께 명중 함수를 계속 실행, 즉 공격이 진행된다.
이렇게 "방어"를 구현하면서 방어 무시 특성인 "보이지않는 주먹"도 구현 완료.
1번, 2번 파트는 구현 완료했고,
이제 방어 실패, 교체, 다른 스킬 사용시 성공률이 복구되는 3번 파트를 구현해야 한다.
if (skillname == "방어") { int brd = rand() % 100; if (brd < stoi(p[pn][number][15])) { if (k == 0)cout << "상대 "; cout << p[pn][0][11] <<s1(p,pn)<< "방어 태세에 들어갔다!\n"; bt[1] = 145; p[pn][number][15] = to_string(stoi(p[pn][number][15]) / 3); } else { cout << "하지만 실패했다!\n"; p[pn][number][15] = "100"; } }
첫번째 경우는 간단하다.
"변화스킬" 함수의 방어파트에서
방어가 성공했는지 if로 확인했으므로
그 외의 경우를 else로 실패 처리하고
그때 성공률을 다시 100으로 변경하면 된다.
//교체한 포켓몬의 "방어" 성공률 초기화 p[pp][4][15] = "100";
교체시 성공률 초기화는 "교체"함수에서,
교체한 포켓몬의 4번 스킬의 [15] 값을 100으로 변경한다.
[15] 칸은 "방어" 스킬만 사용하므로
다른 스킬은 [15]번 값이 변경되든 말든 아무 영향이 없고,
추가로 방어 스킬은 "4"번 칸에만 할당하므로 스킬 번호는 4로 고정해준다.
스킬 순서를 스킬 설명 텍스트 순으로 정렬해놓는데,
방어가 설명이 길어서 왠만하면 4번 스킬이된다.
if로 4개의 스킬 이름을 확인해서 방어인 경우에만
성공률을 100으로 되돌려도 되지만,
이쪽이 간단하니까...
if (number != 4) {//다른 스킬 사용시 방어 성공확률 초기화 p[ps[0]][4][15] = "100"; } if (snumber != 4) { s[ps[1]][4][15] = "100"; }
마지막으로 다른 스킬 사용시 스킬 확률 초기화는 메인 함수에서.
배틀이 끝났을때 사용한 스킬이 4번 스킬이 아니면 해당 스킬의 [15]값을 100으로 한다.
모든 방어 스킬의 번호는 4번이므로, 4번 스킬을 쓰지 않았다면 방어는 무조건 쓰지 않았을것이고,
위와 같이 [15]번 칸은 방어 스킬만 사용하는 값이므로 다른 스킬은 값이 변경되든 말든 아무 변화가 없다.
이렇게 방어는 구현 완료했고.
마지막 남은 맹독 스킬은
상대방을 맹독 상태로 만든다.
맹독 "상태이상"은 구현했지만,
상대방을 맹독 상태로 만드는 맹독 "스킬"은 구현하지 않았으므로
간단하게 구현해준다.
이 역시 변화기이므로 "변화스킬" 함수에서 처리해준다.
if (skillname == "맹독") { if (s[sn][0][1] == "독" || s[sn][0][2] == "독" || s[sn][0][1] == "강철" || s[sn][0][2] == "강철") { cout << "하지만 효과가 없는 것 같다...\n"; //상대가 독이나 강철 타입이면 무효 }
일단 맹독은 독과 강철타입에게는 통하지 않으므로 따로 처리해주고
else if (bt[1] == 145) { if (k == 1) { cout << "상대 "; } cout << s[sn][0][11] << s1(s, sn) << "공격으로부터 몸을 지켰다!\n"; }
대부분의 변화기는 자신의 랭크를 올리거나 자신의 체력을 회복하는 스킬이라 방어에 막히지 않지만,
맹독은 상대방에게 영향을 끼치는 몇안되는 변화기이므로
상대방이 "방어"시 막히도록 조건문을 추가해준다.
else if (s[sn][5][1] == "y" || s[sn][5][3] == "y" || s[sn][5][6] == "y") { cout << "상대에겐 더이상 상태이상을 걸 수 없다!\n"; //상대가 이미 상태이상에 걸려있으면 무효 }
상대방이 화상, 마비, 얼음 상태이상에 걸려있을땐 걸리지 않는다.
메인 상태이상들은 중복되지 않으니까.
else { if (s[sn][5][2] == "y") {//상대가 이미 "독" 상태이상에 걸린 경우 if (k == 1)cout << "상대 "; cout << s[sn][0][0] << "의 독이 더 강해졌다!\n";//맹독으로 업그레이드 s[sn][5][7] = "y";//맹독 여부를 y로 변경 s[sn][5][8] = "0";//맹독 카운트. 매 턴 맹독이 데미지가 더 강해지게 한다. s[sn][5][2] = "n";//독 여부를 n으로 변경 cout << s[sn][5][7]; }
다만 상대방이 "독" 상태이상에 걸린 경우엔
이를 "맹독"으로 강화한다.
독은 매턴 최대 체력의 1/8 데미지,
맹독은 첫 턴에 1/16 데미지이지만 이후 2/16, 3/16, 4/16... 턴이 지날수록 점차 강해진다.
이것들은 전부 옛날에 구현해놨으므로
적의 독 여부를 나타내는 s[sn][5][2]값을 "n"으로
맹독 여부를 나타내는 s[sn][5][7]값을 "y"로 바꿔주기만 하면 된다.
맹독은 카운트도 필요하므로 따로 추가해준다.
else {//상대가 상태이상에 걸리지 않은 경우 if (k == 1)cout << "상대 "; cout << s[sn][0][0] << s1(s, sn) << "맹독에 걸렸다!\n"; s[sn][5][7] = "y"; s[sn][5][8] = "0"; s[sn][5][0] = "y"; //상태이상 여부를 y로 변경 } }
상대방이 상태이상에 걸리지 않아있는 경우엔
그냥 맹독에 걸리게 하면 끝!
이렇게 누오의 기술은 전부 구현을 완료했고.
누오가 지닌 아이템은 울퉁불퉁멧.
상대방이 접촉기로 공격시 상대방 체력의 1/6만큼 데미지를 준다.
접촉기로 공격한 다음 발생하는 이벤트이므로
역시나 "부가효과"함수에서 처리한다.
if (b[bb][0][13] == "울멧" && a[aa][skillnumber][11] == "y") { //맞은쪽의 지닌 도구가 울퉁불퉁멧이고 상대가 사용한 기술이 접촉기이면 cout << "[아이템 울퉁불퉁멧]\n"; if (s == 0) cout << "상대 "; cout << a[aa][0][11] << s1(a, aa) << "울퉁불퉁멧 때문에 데미지를 입었다! ("; bandong = stoi(a[aa][0][3]) / 6; //울퉁불퉁멧으로 입는 반동 데미지는 전체 체력의 1/6 if (bandong >= alife[aa]) { bandong = alife[aa]; } //반동 데미지가 현재 체력보다 높을경우 현재 체력만큼 데미지 cout << alife[aa] << "-" << bandong << "="; alife[aa] -= bandong; cout << alife[aa] << ")\n"; if (alife[aa] <= 0) { cout << alife[aa] << s1(a, aa) << "쓰러졌다!"; bt[1] = -1; return; } }
부가효과 함수에 간단하게 구현 완료
맞은 쪽의 조건에 의해, 그리고 접촉기에만 발동하는것은
썬더의 특성 정전기와 비슷하다.
그리고 이 데미지로 공격측이 기절했을경우
공격측에 발동하는 다른 부가효과는 발동하지 않으므로
바로 return한다.
이제 마지막으로 구현해야할것은 누오의 특성, "천진"이다.
천진은 상대방의 랭크변화를 무시하는 특성으로,
등장시 일단 1랭업 하고 나오는 "자시안"을 비롯,
랭크업 스위퍼
-랭크업으로 능력치를 올린뒤 상대방을 전부 쓰러뜨리는 메인 딜러 포지션-
들을 전부 막아내게 해주는 특성으로
애매한 능력치의 누오를 픽률 1위 탱커로 있게 해준 특성이다.
구현은 생각보다 쉬운데,
if (b[bb][0][12] == "천진" && a[aa][number][6] != "틀깨기") atk = stoi(a[aa][11][tm + 6]); //맞는쪽의 특성이 "천진"일경우 공격측의 랭크변화를 무시한다 = 현재 공격력이 아닌 원래 공격력을 참조한다. //단 공격쪽의 특성이 "틀깨기"=방어 특성 무시면 제외한다.
데미지 계산 함수에서,
보통은 랭크 변화가 적용된 현재 공격력 a[aa][0][4]값을 참고하지만
방어측의 특성이 "천진"일 경우
랭크 변환가 적용되지 않은, 원래 공격력 값인 a[aa][11][10]을 데미지 계산에 넣는다.
틀깨기는 방어측의 방어적 특성을 무시하는 특성으로
천진 역시 무시하므로 조건문에 추가해놨지만
일단 추가만해놨을뿐
아직 특성이 틀깨기기인 포켓몬이 없어서
사실상 무쓸모다(...)
쨌든 그렇게 누오 구현 완료!
첫 탱커라 여러모로 구현할게 많았고,
특히 "방어"는 구현하기 생각보다 빡쎘다.
아무래도 방어 성공률이 초기화되는 경우도 많고,
신경써야할게 많다보니...
구현 완료된 모습
'포켓몬 배틀 구현' 카테고리의 다른 글
열두번째 포켓몬, 따라큐 구현 (특성 탈) (0) 2022.03.23 열한번째 포켓몬, 솔가레오 구현 (틀깨기, 약점보험) (0) 2022.03.23 여덟, 아홉번째 포켓몬 이벨타르 & 흑마렉스 구현 (흡혈) (0) 2022.03.23 일곱번째 포켓몬, 우라오스 구현 (카운터) (0) 2022.03.17 여섯번째 포켓몬, 가이오가 구현 (스카프, 얼음) (0) 2022.03.17