걸릴 확률이 존재하지 않는 버프, 디버프 마법은 제외하고 확률로 걸리는
마법들을 설명합니다.
존, 참, 슬립, 뮤트 등이 있습니다. 참의 경우에는 제 설명에 오류가 있을 가능성이 매우 큽니다.
코드가 굉장히 복잡하기 때문입니다. 어떤진영인지 원래 어떤 진영인지 이런 정보들이 마구잡이로
들어있기 때문에 매우 복잡합니다.
첫번째 포스트에서도 사용했던, 0x40ffd3 Apply_Spell_To_Target이라 명명한, 함수에서
switch(마법타입) case문을 통해서 관찰하였습니다.
case Mute:
먼저 올바른 타겟 포스인지 체크합니다. 이 체크가, 진영을 보여주는건지 뭘 리턴하는건지 모르겠네요.
일단 0을 리턴해야지만 마법이 걸립니다.
그리고 Army Number가 0또는 7이어야 합니다. 강제로 1-6으로 용병 설정시 뮤트를 걸 수 없습니다.
해당 유닛의 ArmyFlag의 MuteFlag 0x10이 0인지 체크합니다.
마법 실행 플레그가 1이면 , 가장 먼저 마법이 성공할지를 계산하는 함수를 호출합니다.
해당 함수는 0x0040f54d에 위치했습니다.
이 함수는 먼저 마법방어력이 100인지 체크한 후 100이면, 마법 실패를 리턴합니다.
다음 rng값을 구합니다.
다음 100 - ((공격자_LV - 방어자_LV) << 2) ) 를 수행합니다.
음수 처리가 어떻게 될지는 잘 모르겠지만, 다시 쓰면
MDF - (공격자 - 방어자) X 4 의 값을 얻습니다. 편의상, 레벨차 보정 마방치라고 부르겠습니다.
rng값에 %101을 취해 0-100의 난수를 얻습니다.
난수 < 레벨차_보정 마방치 이면, 마법실패를 리턴하고, 아닌 경우 마법 성공 로직을 돌립니다.
동일 레벨일 때, 마방치가 100이 아닌 99일 때, 0-98은 실패, 99와 100은 성공이므로,
확률은 1/100이 아닌 2/101입니다. 일단 마법 면역만 아니면 잘 걸립니다.
잘 모르는 상태로 기대했던 것보다, 2배가량 높게 걸립니다.
표로 보여드리면 다음과 같습니다.
실패확률 |
| 공격자 방어자 레벨차 |
마방 | -8 | -4 | 0 | 4 | 8 |
20 | 0.515 | 0.356 | 0.198 | 0.040 | 0.000 |
40 | 0.713 | 0.554 | 0.396 | 0.238 | 0.079 |
60 | 0.911 | 0.752 | 0.594 | 0.436 | 0.277 |
80 | 1.000 | 0.950 | 0.792 | 0.634 | 0.475 |
99 | 1.000 | 1.000 | 0.980 | 0.822 | 0.663 |
| | | | | |
성공확률 |
| 공격자 방어자 레벨차 |
마방 | -8 | -4 | 0 | 4 | 8 |
20 | 0.485 | 0.644 | 0.802 | 0.960 | 1.000 |
40 | 0.287 | 0.446 | 0.604 | 0.762 | 0.921 |
60 | 0.089 | 0.248 | 0.406 | 0.564 | 0.723 |
80 | 0.000 | 0.050 | 0.208 | 0.366 | 0.525 |
99 | 0.000 | 0.000 | 0.020 | 0.178 | 0.337 |
계산이 틀려보이신다면, 소수점 3번째 까지 나와서 그렇습니다. 99/101계산기로 해보시기를.
실패확률 부분을 보시면, 마방이 높을 수록 실패확률이 높고, 공격자의 레벨이 올라갈수록(오른쪽)
실패확률이 내려가는 것을 볼 수 있습니다.
성공확률은 그 반대입니다.
마방이 20이어도, 레벨만 높으면 50%는 저항을 합니다.
마방이 99여도, 레벨이 낮으면 1/3은 마법이 걸립니다.
case zone:
타겟이 맞는지를 체크한 후, 존플레그를 확인해서 안걸려 있으면 , 거는 로직으로 갑니다.
실행 플레그가 없으면, 레벨마방보정치를 어딘가에 저장하고, 타겟수와 효과수에 1을 더합니다.
실행플레그가 있으면, 마법이 성공하면, Zone_flag를 1로 해줍니다. 0x08
마법이 실패하면 다른 함수를 호출합니다. (왜????)
여기서 요상한 짓을 하는데, 마법 때마다, 어떤 메모리 공간에 올라와 있는 메모리 값을
다른데다가 이동을 (시키고 위치x *48 +4, 위치y *48 -4) 값을 저장합니다.
case sleep:
슬립도 시작은 타겟이 적합한지 먼저 체크를 하고, army타입이 불사가 아닌지를 체크합니다.
불사는 슬립에 면역이도록 하드코딩 되어 있습니다. 종족값을 바꿀 때는 이걸 고려해야합니다.
실행플레그가 없을 때는, 존과 동일합니다.
실행플레그가 있으면 성공일 때는 슬립 플레그를 1로 합니다. 슬립 플레그를 쓰는 비트필트는 ,
부대원별로 따로 존재합니다.
슬립은 성공시에도, 다른 메모리 공간에 접근해서 위치를 저장합니다.
case charm:
마지막으로 참입니다.
참마법은 sleep과 동일합니다. 타겟체크, 불사체크 실행플레그 없을 때 동일입니다.
참은 참이 걸려있던지 안걸려있던지를 확인을 안합니다.
마법이 성공할지 체크를 먼저 합니다.
실패시 실패 벡터공간에 데이터를 저장합니다. (다 동일 함수 사용)
성공시에 어떤 byte값이(b1) 0인지 체크합니다. 0이면,
다른 바이트 값(b2)를 b1에 씁니다. if(b1==0) b1 = b2;
이어서 조건 없이 b2에 마법사용자의 b2값을 씁니다.
추측을 해봅시다. b1은 원래진영번호, 0이면 변화없음이지 않을까.
b2는 현재 진영번호라고 생각해보자.
이어서 b1과 b2모두 1이 아닐 때, b2 = 3을 쓴다.
b1==b2면 b1 = 0 이다.
그 다음 어떤 함수를 작동시키는데, 모든 타일에 있는 유닛들에 대해서, 뭔가를 건드린다.
ArmyFlag 0x40이 참과 관련이 있는 것 같은데 아직 정보가 부족하며,
적으로 바뀌면, 적들이 사용하는, force 정보공간으로 주소가 이전이 되는지,
첫번째 hex값과 두번째 hex 값으로 force 넘버를 변조한다.
근데 계산 과정에서 앞에서 바꾼 camp 번호값이 안 쓰인다. (??)
왜 재 계산을 하는건지 알 수 없다.
참에 대해 조금더 들여다 보자(자가치유코드 다시보기)
루프를 돕니다. 먼저 원래의 캠프 값을 가져옵니다. 그 값이 0이면, 현재 캠프 값을
다시 가져옵니다.
얻어온 캠프값을 가지고 비트앤드 연산을 취한 뒤, 0이 아니어야 합니다.
캠프값에 대한 정보가 아직 필요하지만, 현재 턴이오는 캠프가 아니면 자동회복 되지 않습니다.
어차피 한바퀴돌면, 자기턴이 올거라 무슨의미가 있을지는 알 수 없습니다.
처음에 20개의 포스에 대해서, 루프를 돕니다.
이 20개가 전체 전투가능한 지휘관 숫자면, 우리에게 참이걸린 적도 작동을 할테지만, 아니라면, 이상해 집니다.
참이 걸려 우리편이 된 친구들은, 우리턴 시작에 자가회복이 된다고 일단 합시다.
이 친구들은 인접위치의 부대원에게 체력 회복을 하고 가장 먼저, 참을 해제 합니다.
확률 계산을 한 후, 원래캠프 번호가 0이 아니며, 참마법( ??) 플레그가 0(??)이면서,
아미 플레그가 0x20이 없어야 합니다. 그리고 지휘관의 마법 방어력의 확률로
마법을 해제합니다.
마법해제 코드는
current_camp = initial_camp;
initial_camp = 0;
입니다. 캠프 번호 두개가 참마법의 플레그로 보입니다.
부대원 플레그 0x20, 포스 플레그 0x20은 미궁으로 빠집니다. 이름을 다시 지어야 겠습니다.
일단은 둘다 Absolute_Camp(enum값)로 이름을 바꾸고 사용처를 확인해야겠습니다.